From d6460118e43033c396053d3a9b1635c2cea01c57 Mon Sep 17 00:00:00 2001 From: JamesEMcClure Date: Tue, 15 May 2018 09:36:58 -0400 Subject: [PATCH 01/54] Initial commit --- LICENSE | 674 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/LICENSE @@ -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 +. From 4dd489dbc6142ae4a9dfafc7327b4972483ff3f2 Mon Sep 17 00:00:00 2001 From: James E McClure Date: Fri, 25 May 2018 07:57:14 -0400 Subject: [PATCH 02/54] concatenating licenses --- LICENSE | 23 +++++++++++++++++++++++ LICENSE.bsd2 | 23 ----------------------- 2 files changed, 23 insertions(+), 23 deletions(-) delete mode 100644 LICENSE.bsd2 diff --git a/LICENSE b/LICENSE index 94a9ed02..72128316 100644 --- a/LICENSE +++ b/LICENSE @@ -672,3 +672,26 @@ 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 . +Copyright (c) 2015, JamesEMcClure +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSE.bsd2 b/LICENSE.bsd2 deleted file mode 100644 index d06c05d5..00000000 --- a/LICENSE.bsd2 +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) 2015, JamesEMcClure -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 2e8382cceb4cf80b58227080a2a103501d54d9c9 Mon Sep 17 00:00:00 2001 From: James E McClure Date: Mon, 11 Jun 2018 15:19:05 -0400 Subject: [PATCH 03/54] Added OPM headers --- IO/IOHelpers.h | 15 +++++++++++++++ IO/Mesh.cpp | 30 ++++++++++++++++++++++++++++++ IO/Mesh.h | 15 +++++++++++++++ IO/MeshDatabase.cpp | 30 ++++++++++++++++++++++++++++++ IO/MeshDatabase.h | 15 +++++++++++++++ IO/PIO.cpp | 15 +++++++++++++++ IO/PIO.h | 15 +++++++++++++++ IO/PIO.hpp | 30 ++++++++++++++++++++++++++++++ IO/Reader.cpp | 15 +++++++++++++++ IO/Reader.h | 15 +++++++++++++++ IO/Writer.cpp | 15 +++++++++++++++ IO/Writer.h | 15 +++++++++++++++ IO/netcdf.cpp | 15 +++++++++++++++ IO/netcdf.h | 15 +++++++++++++++ IO/silo.cpp | 15 +++++++++++++++ IO/silo.h | 15 +++++++++++++++ IO/silo.hpp | 30 ++++++++++++++++++++++++++++++ analysis/Minkowski.cpp | 15 +++++++++++++++ analysis/Minkowski.h | 15 +++++++++++++++ analysis/PointList.h | 15 +++++++++++++++ analysis/TwoPhase.cpp | 30 ++++++++++++++++++++++++++++++ analysis/TwoPhase.h | 15 +++++++++++++++ analysis/analysis.cpp | 15 +++++++++++++++ analysis/analysis.h | 15 +++++++++++++++ analysis/distance.cpp | 15 +++++++++++++++ analysis/distance.h | 15 +++++++++++++++ analysis/filters.cpp | 15 +++++++++++++++ analysis/filters.h | 15 +++++++++++++++ analysis/histogram.h | 15 +++++++++++++++ analysis/imfilter.h | 15 +++++++++++++++ analysis/imfilter.hpp | 30 ++++++++++++++++++++++++++++++ analysis/pmmc.h | 15 +++++++++++++++ analysis/runAnalysis.cpp | 15 +++++++++++++++ analysis/runAnalysis.h | 15 +++++++++++++++ analysis/uCT.cpp | 15 +++++++++++++++ analysis/uCT.h | 15 +++++++++++++++ common/Array.h | 15 +++++++++++++++ common/Array.hpp | 30 ++++++++++++++++++++++++++++++ common/Communication.cpp | 15 +++++++++++++++ common/Communication.h | 15 +++++++++++++++ common/Communication.hpp | 30 ++++++++++++++++++++++++++++++ common/Database.cpp | 15 +++++++++++++++ common/Database.h | 15 +++++++++++++++ common/Database.hpp | 30 ++++++++++++++++++++++++++++++ common/Domain.cpp | 15 +++++++++++++++ common/Domain.h | 15 +++++++++++++++ common/FunctionTable.h | 15 +++++++++++++++ common/FunctionTable.hpp | 30 ++++++++++++++++++++++++++++++ common/MPI_Helpers.cpp | 15 +++++++++++++++ common/MPI_Helpers.h | 15 +++++++++++++++ common/MPI_Helpers.hpp | 30 ++++++++++++++++++++++++++++++ common/ScaLBL.cpp | 15 +++++++++++++++ common/ScaLBL.h | 15 +++++++++++++++ common/SpherePack.cpp | 30 ++++++++++++++++++++++++++++++ common/SpherePack.h | 15 +++++++++++++++ common/StackTrace.cpp | 15 +++++++++++++++ common/StackTrace.h | 15 +++++++++++++++ common/UnitTest.cpp | 15 +++++++++++++++ common/UnitTest.h | 15 +++++++++++++++ common/Units.cpp | 15 +++++++++++++++ common/Units.h | 15 +++++++++++++++ common/Utilities.cpp | 15 +++++++++++++++ common/Utilities.h | 15 +++++++++++++++ common/UtilityMacros.h | 15 +++++++++++++++ cpu/BGK.cpp | 15 +++++++++++++++ cpu/Color.cpp | 15 +++++++++++++++ cpu/D3Q19.cpp | 15 +++++++++++++++ cpu/D3Q7.cpp | 15 +++++++++++++++ cpu/Extras.cpp | 15 +++++++++++++++ cpu/MRT.cpp | 15 +++++++++++++++ cpu/dfh.cpp | 30 ++++++++++++++++++++++++++++++ cpu/thermal.cpp | 30 ++++++++++++++++++++++++++++++ gpu/BGK.cu | 15 +++++++++++++++ gpu/Color.cu | 15 +++++++++++++++ gpu/CudaExtras.cu | 15 +++++++++++++++ gpu/D3Q19.cu | 15 +++++++++++++++ gpu/D3Q7.cu | 15 +++++++++++++++ gpu/Extras.cu | 15 +++++++++++++++ gpu/MRT.cu | 15 +++++++++++++++ gpu/dfh.cu | 30 ++++++++++++++++++++++++++++++ models/ColorModel.cpp | 15 +++++++++++++++ models/ColorModel.h | 15 +++++++++++++++ models/MRTModel.cpp | 15 +++++++++++++++ models/MRTModel.h | 15 +++++++++++++++ threadpool/atomic_helpers.cpp | 30 ++++++++++++++++++++++++++++++ threadpool/atomic_helpers.h | 15 +++++++++++++++ threadpool/atomic_list.h | 15 +++++++++++++++ threadpool/atomic_list.hpp | 30 ++++++++++++++++++++++++++++++ threadpool/thread_pool.cpp | 30 ++++++++++++++++++++++++++++++ threadpool/thread_pool.h | 15 +++++++++++++++ threadpool/thread_pool.hpp | 30 ++++++++++++++++++++++++++++++ 91 files changed, 1650 insertions(+) mode change 100755 => 100644 common/Domain.h mode change 100755 => 100644 common/UnitTest.cpp mode change 100755 => 100644 common/UnitTest.h diff --git a/IO/IOHelpers.h b/IO/IOHelpers.h index 2e9b06e0..eca4b0db 100644 --- a/IO/IOHelpers.h +++ b/IO/IOHelpers.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef IO_HELPERS_INC #define IO_HELPERS_INC diff --git a/IO/Mesh.cpp b/IO/Mesh.cpp index 742dac85..45e3a7d3 100644 --- a/IO/Mesh.cpp +++ b/IO/Mesh.cpp @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "Mesh.h" #include "common/Utilities.h" #include "shared_ptr.h" diff --git a/IO/Mesh.h b/IO/Mesh.h index 604dddfd..45de8df0 100644 --- a/IO/Mesh.h +++ b/IO/Mesh.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef MESH_INC #define MESH_INC diff --git a/IO/MeshDatabase.cpp b/IO/MeshDatabase.cpp index 1fad9231..602a93dc 100644 --- a/IO/MeshDatabase.cpp +++ b/IO/MeshDatabase.cpp @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "IO/MeshDatabase.h" #include "IO/Mesh.h" #include "IO/IOHelpers.h" diff --git a/IO/MeshDatabase.h b/IO/MeshDatabase.h index ad696260..88347f2d 100644 --- a/IO/MeshDatabase.h +++ b/IO/MeshDatabase.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef MeshDatabase_INC #define MeshDatabase_INC diff --git a/IO/PIO.cpp b/IO/PIO.cpp index 6c6ece2d..6554bf15 100644 --- a/IO/PIO.cpp +++ b/IO/PIO.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "IO/PIO.h" #include "common/Utilities.h" #include "common/MPI_Helpers.h" diff --git a/IO/PIO.h b/IO/PIO.h index b6d8b103..99a9b3be 100644 --- a/IO/PIO.h +++ b/IO/PIO.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef included_PIO #define included_PIO diff --git a/IO/PIO.hpp b/IO/PIO.hpp index 67b32cdb..add86db4 100644 --- a/IO/PIO.hpp +++ b/IO/PIO.hpp @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef included_PIO_hpp #define included_PIO_hpp diff --git a/IO/Reader.cpp b/IO/Reader.cpp index dedb9b82..4c9074ae 100644 --- a/IO/Reader.cpp +++ b/IO/Reader.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "IO/Reader.h" #include "IO/Mesh.h" #include "IO/MeshDatabase.h" diff --git a/IO/Reader.h b/IO/Reader.h index ce8dba22..f1c310d5 100644 --- a/IO/Reader.h +++ b/IO/Reader.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef READER_INC #define READER_INC diff --git a/IO/Writer.cpp b/IO/Writer.cpp index bb522cf6..404a45e5 100644 --- a/IO/Writer.cpp +++ b/IO/Writer.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "IO/Writer.h" #include "IO/MeshDatabase.h" #include "IO/IOHelpers.h" diff --git a/IO/Writer.h b/IO/Writer.h index 08ac0775..a007cb81 100644 --- a/IO/Writer.h +++ b/IO/Writer.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef WRITER_INC #define WRITER_INC diff --git a/IO/netcdf.cpp b/IO/netcdf.cpp index e355c344..ec113297 100644 --- a/IO/netcdf.cpp +++ b/IO/netcdf.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "IO/netcdf.h" #include "common/Utilities.h" #include "common/MPI_Helpers.h" diff --git a/IO/netcdf.h b/IO/netcdf.h index 657747bf..289a228f 100644 --- a/IO/netcdf.h +++ b/IO/netcdf.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef NETCDF_READER #define NETCDF_READER diff --git a/IO/silo.cpp b/IO/silo.cpp index eece8583..8051c5a5 100644 --- a/IO/silo.cpp +++ b/IO/silo.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "IO/silo.h" #include "common/Utilities.h" #include "common/MPI_Helpers.h" diff --git a/IO/silo.h b/IO/silo.h index 4c7081e5..178958bc 100644 --- a/IO/silo.h +++ b/IO/silo.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef SILO_INTERFACE #define SILO_INTERFACE diff --git a/IO/silo.hpp b/IO/silo.hpp index 312f32d8..938baec3 100644 --- a/IO/silo.hpp +++ b/IO/silo.hpp @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef SILO_INTERFACE_HPP #define SILO_INTERFACE_HPP diff --git a/analysis/Minkowski.cpp b/analysis/Minkowski.cpp index 7be0ec95..7e91d103 100644 --- a/analysis/Minkowski.cpp +++ b/analysis/Minkowski.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "analysis/Minkowski.h" #include "analysis/pmmc.h" #include "common/Domain.h" diff --git a/analysis/Minkowski.h b/analysis/Minkowski.h index f30d8357..425c2967 100644 --- a/analysis/Minkowski.h +++ b/analysis/Minkowski.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ // Header file for two-phase averaging class #ifndef Minkowski_INC #define Minkowski_INC diff --git a/analysis/PointList.h b/analysis/PointList.h index 2ea39345..a5d0fa40 100644 --- a/analysis/PointList.h +++ b/analysis/PointList.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef PointList_INC #define PointList_INC diff --git a/analysis/TwoPhase.cpp b/analysis/TwoPhase.cpp index e6b441f9..f7adc8cc 100644 --- a/analysis/TwoPhase.cpp +++ b/analysis/TwoPhase.cpp @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "analysis/TwoPhase.h" #include "analysis/pmmc.h" diff --git a/analysis/TwoPhase.h b/analysis/TwoPhase.h index 5213246d..256c2e2b 100644 --- a/analysis/TwoPhase.h +++ b/analysis/TwoPhase.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ // Header file for two-phase averaging class #ifndef TwoPhase_INC #define TwoPhase_INC diff --git a/analysis/analysis.cpp b/analysis/analysis.cpp index 35675a34..fcea766c 100644 --- a/analysis/analysis.cpp +++ b/analysis/analysis.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "analysis/analysis.h" #include "ProfilerApp.h" #include diff --git a/analysis/analysis.h b/analysis/analysis.h index 2ce531b1..ea5fab68 100644 --- a/analysis/analysis.h +++ b/analysis/analysis.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef Analysis_H_INC #define Analysis_H_INC diff --git a/analysis/distance.cpp b/analysis/distance.cpp index f539aebf..6fc1aab7 100644 --- a/analysis/distance.cpp +++ b/analysis/distance.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "analysis/distance.h" diff --git a/analysis/distance.h b/analysis/distance.h index b84a93f8..846403bb 100644 --- a/analysis/distance.h +++ b/analysis/distance.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef Distance_H_INC #define Distance_H_INC diff --git a/analysis/filters.cpp b/analysis/filters.cpp index c5858560..8f60d42f 100644 --- a/analysis/filters.cpp +++ b/analysis/filters.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "analysis/filters.h" #include "math.h" #include "ProfilerApp.h" diff --git a/analysis/filters.h b/analysis/filters.h index 131b2b9f..a1fe7927 100644 --- a/analysis/filters.h +++ b/analysis/filters.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef Filters_H_INC #define Filters_H_INC diff --git a/analysis/histogram.h b/analysis/histogram.h index 3d8b5ff2..62e8c279 100644 --- a/analysis/histogram.h +++ b/analysis/histogram.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ /* * Generate a histogram for volumetric, interfacial and common curve properties * copyright 2014, James E. McClure diff --git a/analysis/imfilter.h b/analysis/imfilter.h index b1c4040a..3f7f9301 100644 --- a/analysis/imfilter.h +++ b/analysis/imfilter.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ // These functions mimic the behavior of imfilter in MATLAB #ifndef included_imfilter #define included_imfilter diff --git a/analysis/imfilter.hpp b/analysis/imfilter.hpp index 9816beca..7d13d54d 100644 --- a/analysis/imfilter.hpp +++ b/analysis/imfilter.hpp @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "analysis/imfilter.h" #include "ProfilerApp.h" #include diff --git a/analysis/pmmc.h b/analysis/pmmc.h index b56a2796..436420e3 100644 --- a/analysis/pmmc.h +++ b/analysis/pmmc.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef pmmc_INC #define pmmc_INC diff --git a/analysis/runAnalysis.cpp b/analysis/runAnalysis.cpp index 38f9bdce..3e6cf300 100644 --- a/analysis/runAnalysis.cpp +++ b/analysis/runAnalysis.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ // Run the analysis, blob identification, and write restart files #include "analysis/runAnalysis.h" #include "analysis/analysis.h" diff --git a/analysis/runAnalysis.h b/analysis/runAnalysis.h index c5644fe2..956724ce 100644 --- a/analysis/runAnalysis.h +++ b/analysis/runAnalysis.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef RunAnalysis_H_INC #define RunAnalysis_H_INC diff --git a/analysis/uCT.cpp b/analysis/uCT.cpp index 315f441f..b14cf0b5 100644 --- a/analysis/uCT.cpp +++ b/analysis/uCT.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "analysis/uCT.h" #include "analysis/analysis.h" #include "analysis/distance.h" diff --git a/analysis/uCT.h b/analysis/uCT.h index 284aac61..63be1a56 100644 --- a/analysis/uCT.h +++ b/analysis/uCT.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef uCT_H_INC #define uCT_H_INC diff --git a/common/Array.h b/common/Array.h index edfa687a..2eda3446 100644 --- a/common/Array.h +++ b/common/Array.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef included_ArrayClass #define included_ArrayClass diff --git a/common/Array.hpp b/common/Array.hpp index 60a8987c..7062c44a 100644 --- a/common/Array.hpp +++ b/common/Array.hpp @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef included_ArrayClass_hpp #define included_ArrayClass_hpp diff --git a/common/Communication.cpp b/common/Communication.cpp index b75919cd..96c604d5 100644 --- a/common/Communication.cpp +++ b/common/Communication.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "common/Communication.h" diff --git a/common/Communication.h b/common/Communication.h index 17a6f042..4556c217 100644 --- a/common/Communication.h +++ b/common/Communication.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef COMMUNICATION_H_INC #define COMMUNICATION_H_INC diff --git a/common/Communication.hpp b/common/Communication.hpp index a8eff9ee..a374f988 100644 --- a/common/Communication.hpp +++ b/common/Communication.hpp @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef COMMUNICATION_HPP_INC #define COMMUNICATION_HPP_INC diff --git a/common/Database.cpp b/common/Database.cpp index d318cc03..ab01ef3b 100644 --- a/common/Database.cpp +++ b/common/Database.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "common/Database.h" #include "common/Utilities.h" diff --git a/common/Database.h b/common/Database.h index dc2f846c..6aaa0dd1 100644 --- a/common/Database.h +++ b/common/Database.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef included_Database #define included_Database diff --git a/common/Database.hpp b/common/Database.hpp index 37a89af0..1a906736 100644 --- a/common/Database.hpp +++ b/common/Database.hpp @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef included_Database_hpp #define included_Database_hpp diff --git a/common/Domain.cpp b/common/Domain.cpp index ee338d92..1bb05733 100644 --- a/common/Domain.cpp +++ b/common/Domain.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ // Created by James McClure // Copyright 2008-2013 #include diff --git a/common/Domain.h b/common/Domain.h old mode 100755 new mode 100644 index 0b92c3d4..500800fc --- a/common/Domain.h +++ b/common/Domain.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef Domain_INC #define Domain_INC diff --git a/common/FunctionTable.h b/common/FunctionTable.h index e2bdcb67..47918aef 100644 --- a/common/FunctionTable.h +++ b/common/FunctionTable.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef included_FunctionTable #define included_FunctionTable diff --git a/common/FunctionTable.hpp b/common/FunctionTable.hpp index 52897d5c..5a35bc6b 100644 --- a/common/FunctionTable.hpp +++ b/common/FunctionTable.hpp @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef included_FunctionTable_hpp #define included_FunctionTable_hpp diff --git a/common/MPI_Helpers.cpp b/common/MPI_Helpers.cpp index 23924f21..fbf9158d 100644 --- a/common/MPI_Helpers.cpp +++ b/common/MPI_Helpers.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "common/MPI_Helpers.h" #include "common/Utilities.h" diff --git a/common/MPI_Helpers.h b/common/MPI_Helpers.h index 0cae743b..dcee5f49 100644 --- a/common/MPI_Helpers.h +++ b/common/MPI_Helpers.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ // This file contains wrappers for MPI routines and functions to pack/unpack data structures #ifndef MPI_WRAPPERS_INC #define MPI_WRAPPERS_INC diff --git a/common/MPI_Helpers.hpp b/common/MPI_Helpers.hpp index 85261cf1..2a82f90c 100644 --- a/common/MPI_Helpers.hpp +++ b/common/MPI_Helpers.hpp @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ // This file contains wrappers for MPI routines and functions to pack/unpack data structures #ifndef MPI_WRAPPERS_HPP #define MPI_WRAPPERS_HPP diff --git a/common/ScaLBL.cpp b/common/ScaLBL.cpp index ef25636c..847ad3f8 100644 --- a/common/ScaLBL.cpp +++ b/common/ScaLBL.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "common/ScaLBL.h" ScaLBL_Communicator::ScaLBL_Communicator(std::shared_ptr Dm){ diff --git a/common/ScaLBL.h b/common/ScaLBL.h index 6f925053..9fb001d7 100644 --- a/common/ScaLBL.h +++ b/common/ScaLBL.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ /* ScaLBL.h * Header file for Scalable Lattice Boltzmann Library * Separate implementations for GPU and CPU must both follow the conventions defined in this header diff --git a/common/SpherePack.cpp b/common/SpherePack.cpp index a7246b72..907c4c74 100644 --- a/common/SpherePack.cpp +++ b/common/SpherePack.cpp @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include #include #include diff --git a/common/SpherePack.h b/common/SpherePack.h index 5075b289..59a46c9c 100644 --- a/common/SpherePack.h +++ b/common/SpherePack.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef SpherePack_INC #define SpherePack_INC diff --git a/common/StackTrace.cpp b/common/StackTrace.cpp index 8b9e4015..e120e992 100644 --- a/common/StackTrace.cpp +++ b/common/StackTrace.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "common/StackTrace.h" #include diff --git a/common/StackTrace.h b/common/StackTrace.h index 8d436bf7..6c1ae2ae 100644 --- a/common/StackTrace.h +++ b/common/StackTrace.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef included_StackTrace #define included_StackTrace diff --git a/common/UnitTest.cpp b/common/UnitTest.cpp old mode 100755 new mode 100644 index b995fa68..ce671e87 --- a/common/UnitTest.cpp +++ b/common/UnitTest.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "common/UnitTest.h" #include "common/Utilities.h" #include diff --git a/common/UnitTest.h b/common/UnitTest.h old mode 100755 new mode 100644 index 80503d19..92af309d --- a/common/UnitTest.h +++ b/common/UnitTest.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef included_UnitTest #define included_UnitTest diff --git a/common/Units.cpp b/common/Units.cpp index 8cef9832..9c374bdc 100644 --- a/common/Units.cpp +++ b/common/Units.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "common/Units.h" #include "common/Utilities.h" diff --git a/common/Units.h b/common/Units.h index a3496ee1..441f1275 100644 --- a/common/Units.h +++ b/common/Units.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef included_Units #define included_Units diff --git a/common/Utilities.cpp b/common/Utilities.cpp index f227b8e0..69596495 100644 --- a/common/Utilities.cpp +++ b/common/Utilities.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "common/Utilities.h" #include "common/StackTrace.h" diff --git a/common/Utilities.h b/common/Utilities.h index e6db4279..4cca25d0 100644 --- a/common/Utilities.h +++ b/common/Utilities.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef included_Utilities #define included_Utilities diff --git a/common/UtilityMacros.h b/common/UtilityMacros.h index bfac172f..fb501c1d 100644 --- a/common/UtilityMacros.h +++ b/common/UtilityMacros.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ // This file contains useful macros including ERROR, WARNING, INSIST, ASSERT, etc. #ifndef included_UtilityMacros #define included_UtilityMacros diff --git a/cpu/BGK.cpp b/cpu/BGK.cpp index 436ab381..d0249ead 100644 --- a/cpu/BGK.cpp +++ b/cpu/BGK.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ extern "C" void ScaLBL_D3Q19_AAeven_BGK(double *dist, int start, int finish, int Np, double rlx, double Fx, double Fy, double Fz){ int n; // conserved momemnts diff --git a/cpu/Color.cpp b/cpu/Color.cpp index af80d689..f4158fd3 100644 --- a/cpu/Color.cpp +++ b/cpu/Color.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include #define STOKES diff --git a/cpu/D3Q19.cpp b/cpu/D3Q19.cpp index ac027399..062a5e82 100644 --- a/cpu/D3Q19.cpp +++ b/cpu/D3Q19.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include extern "C" void ScaLBL_D3Q19_Pack(int q, int *list, int start, int count, double *sendbuf, double *dist, int N){ diff --git a/cpu/D3Q7.cpp b/cpu/D3Q7.cpp index 344e6851..d99d4f01 100644 --- a/cpu/D3Q7.cpp +++ b/cpu/D3Q7.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ // CPU Functions for D3Q7 Lattice Boltzmann Methods extern "C" void ScaLBL_Scalar_Pack(int *list, int count, double *sendbuf, double *Data, int N){ diff --git a/cpu/Extras.cpp b/cpu/Extras.cpp index 71f5c04a..e0d19976 100644 --- a/cpu/Extras.cpp +++ b/cpu/Extras.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ // Basic cuda functions callable from C/C++ code #include #include diff --git a/cpu/MRT.cpp b/cpu/MRT.cpp index b3ddb47d..f513c6c2 100644 --- a/cpu/MRT.cpp +++ b/cpu/MRT.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ extern "C" void INITIALIZE(char *ID, double *f_even, double *f_odd, int Nx, int Ny, int Nz) { diff --git a/cpu/dfh.cpp b/cpu/dfh.cpp index 850c38f8..3f9324ff 100644 --- a/cpu/dfh.cpp +++ b/cpu/dfh.cpp @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include #include diff --git a/cpu/thermal.cpp b/cpu/thermal.cpp index 24a7de81..9e1f81fb 100644 --- a/cpu/thermal.cpp +++ b/cpu/thermal.cpp @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ // cpu implementation for thermal lattice boltzmann methods // copyright James McClure, 2014 diff --git a/gpu/BGK.cu b/gpu/BGK.cu index b1da88bb..d89e1298 100644 --- a/gpu/BGK.cu +++ b/gpu/BGK.cu @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include #define NBLOCKS 1024 diff --git a/gpu/Color.cu b/gpu/Color.cu index 6bd76534..e9921cf9 100644 --- a/gpu/Color.cu +++ b/gpu/Color.cu @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include #include #include diff --git a/gpu/CudaExtras.cu b/gpu/CudaExtras.cu index 86ec713d..8e3ef955 100644 --- a/gpu/CudaExtras.cu +++ b/gpu/CudaExtras.cu @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ // Basic cuda functions callable from C/C++ code #include diff --git a/gpu/D3Q19.cu b/gpu/D3Q19.cu index d4c7e65c..c58c76c8 100644 --- a/gpu/D3Q19.cu +++ b/gpu/D3Q19.cu @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include #include diff --git a/gpu/D3Q7.cu b/gpu/D3Q7.cu index 16863fec..b93493fa 100644 --- a/gpu/D3Q7.cu +++ b/gpu/D3Q7.cu @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ // GPU Functions for D3Q7 Lattice Boltzmann Methods #define NBLOCKS 560 diff --git a/gpu/Extras.cu b/gpu/Extras.cu index 9ea1d77d..b23f3f63 100644 --- a/gpu/Extras.cu +++ b/gpu/Extras.cu @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ // Basic cuda functions callable from C/C++ code #include #include diff --git a/gpu/MRT.cu b/gpu/MRT.cu index e79a41d1..c2cd664e 100644 --- a/gpu/MRT.cu +++ b/gpu/MRT.cu @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ //************************************************************************* // CUDA kernels for single-phase ScaLBL_D3Q19_MRT code // James McClure diff --git a/gpu/dfh.cu b/gpu/dfh.cu index e0e49151..881a7353 100644 --- a/gpu/dfh.cu +++ b/gpu/dfh.cu @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include #include #include diff --git a/models/ColorModel.cpp b/models/ColorModel.cpp index 51543410..def9fec0 100644 --- a/models/ColorModel.cpp +++ b/models/ColorModel.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ /* color lattice boltzmann model */ diff --git a/models/ColorModel.h b/models/ColorModel.h index d5549f31..9cf4268d 100644 --- a/models/ColorModel.h +++ b/models/ColorModel.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ /* Implementation of color lattice boltzmann model */ diff --git a/models/MRTModel.cpp b/models/MRTModel.cpp index 58982f44..bb098fbb 100644 --- a/models/MRTModel.cpp +++ b/models/MRTModel.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ /* * Multi-relaxation time LBM Model */ diff --git a/models/MRTModel.h b/models/MRTModel.h index ff9db633..9aaee346 100644 --- a/models/MRTModel.h +++ b/models/MRTModel.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ /* * Multi-relaxation time LBM Model */ diff --git a/threadpool/atomic_helpers.cpp b/threadpool/atomic_helpers.cpp index 574cd30e..57f602f5 100644 --- a/threadpool/atomic_helpers.cpp +++ b/threadpool/atomic_helpers.cpp @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include "threadpool/atomic_helpers.h" #include diff --git a/threadpool/atomic_helpers.h b/threadpool/atomic_helpers.h index 178c1af1..ed2032c7 100644 --- a/threadpool/atomic_helpers.h +++ b/threadpool/atomic_helpers.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ // Copyright © 2004 Mark Berrill. All Rights Reserved. This work is distributed with permission, // but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. #ifndef included_ThreadPoolAtomicHelpers diff --git a/threadpool/atomic_list.h b/threadpool/atomic_list.h index 5da8cc85..db9c6832 100644 --- a/threadpool/atomic_list.h +++ b/threadpool/atomic_list.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef included_AtomicModelAtomicList #define included_AtomicModelAtomicList diff --git a/threadpool/atomic_list.hpp b/threadpool/atomic_list.hpp index a0850971..0867fc71 100644 --- a/threadpool/atomic_list.hpp +++ b/threadpool/atomic_list.hpp @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #ifndef included_AtomicList_hpp #define included_AtomicList_hpp diff --git a/threadpool/thread_pool.cpp b/threadpool/thread_pool.cpp index 9b0ff4fd..1d5e1c98 100644 --- a/threadpool/thread_pool.cpp +++ b/threadpool/thread_pool.cpp @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #define _CRT_NONSTDC_NO_DEPRECATE #include "threadpool/thread_pool.h" #include "common/Utilities.h" diff --git a/threadpool/thread_pool.h b/threadpool/thread_pool.h index eff12433..21791dad 100644 --- a/threadpool/thread_pool.h +++ b/threadpool/thread_pool.h @@ -1,3 +1,18 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ // Copyright © 2004 Mark Berrill. All Rights Reserved. This work is distributed with permission, // but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A // PARTICULAR PURPOSE. diff --git a/threadpool/thread_pool.hpp b/threadpool/thread_pool.hpp index a87860b3..cdcf03bf 100644 --- a/threadpool/thread_pool.hpp +++ b/threadpool/thread_pool.hpp @@ -1,3 +1,33 @@ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ +/* + Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ // This file contains the template functions for the thread pool #ifndef included_ThreadPoolTmpl #define included_ThreadPoolTmpl From 29aa949cb3081883a59ae8e6b4200962302f5a05 Mon Sep 17 00:00:00 2001 From: JamesEMcclure Date: Thu, 8 Aug 2019 14:45:44 -0400 Subject: [PATCH 04/54] clean up configure for hu --- sample_scripts/configure_huckleberry | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sample_scripts/configure_huckleberry b/sample_scripts/configure_huckleberry index 03d35824..8c4499b0 100755 --- a/sample_scripts/configure_huckleberry +++ b/sample_scripts/configure_huckleberry @@ -7,6 +7,8 @@ module list module load hdf5/1.8.12 module load silo +export LBPM_SOURCE_DIR=$HOME/LBPM + cmake \ -D CMAKE_C_COMPILER:PATH=mpicc \ -D CMAKE_CXX_COMPILER:PATH=mpicxx \ @@ -24,7 +26,7 @@ cmake \ -D USE_SILO=1 \ -D SILO_DIRECTORY=$SILO_DIR \ -D USE_TIMER=0 \ - ~/LBPM-WIA + $LBPM_SOURCE_DIR make VERBOSE=1 -j1 && make install From 1c7956faebff3f969846acc093d03e899213f379 Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Sun, 15 Sep 2019 14:00:20 +0200 Subject: [PATCH 05/54] Fixed typo Changes to be committed: modified: analysis/runAnalysis.cpp --- analysis/runAnalysis.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analysis/runAnalysis.cpp b/analysis/runAnalysis.cpp index 67d29701..e1907a21 100644 --- a/analysis/runAnalysis.cpp +++ b/analysis/runAnalysis.cpp @@ -231,7 +231,7 @@ public: ~IOWorkItem() { } virtual void run() { auto vis_db = input_db->getDatabase( "Visualization" ); - auto db = input_db->getDatabase( "Colr" ); + auto db = input_db->getDatabase( "Color" ); int timestep = db->getWithDefault( "timestep", 0 ); From 383c967731341935c2b67547209a7e7dbf48281b Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Tue, 17 Sep 2019 11:55:55 +0200 Subject: [PATCH 06/54] New python file with grid utilities --- workflows/Util/gridUtil.py | 183 +++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 workflows/Util/gridUtil.py diff --git a/workflows/Util/gridUtil.py b/workflows/Util/gridUtil.py new file mode 100644 index 00000000..16b4e954 --- /dev/null +++ b/workflows/Util/gridUtil.py @@ -0,0 +1,183 @@ +#!/bin/env python +# +# 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 . +# +#################################################################### + + +import numpy as np + +def segmentGrid32bit(inputFileName, outputFileName): + inputFile=inputFileName + value=np.fromfile(inputFile,dtype=np.float32) + value_new = np.copy(value) + + value_new[value < 0.1] = 1 + value_new[value > -0.01 and value < 0.01] = 0 + value_new[value > 0.1] = 2 + + + value_new.tofile(outputFileName) + +def segmentGrid8bit(inputFileName, outputFileName): + inputFile=inputFileName + value=np.fromfile(inputFile,dtype=np.uint8) + value_new = np.copy(value) + + value_new[value == 0] = 0 + value_new[value == 127] = 1 + value_new[value == 255] = 0 + + value_new.tofile(outputFileName) + + +def segmentGrid8bitRedPhase(path): + import os + for inputFile in os.listdir(path): + if os.path.isfile(inputFile) and inputFile.endswith('.raw') and inputFile.startswith('id_t'): + value=np.fromfile(inputFile,dtype=np.uint8) + value_new = np.copy(value) + + value_new[value == 0] = 0 + value_new[value == 1] = 255 + value_new[value >= 2] = 0 + + value_new.tofile('Red_'+inputFile) + +def segmentGrid8bitRockPhase(inputFileName): + inputFile=inputFileName + value=np.fromfile(inputFile,dtype=np.uint8) + value_new = np.copy(value) + + value_new[value == 0] = 255 + value_new[value == 1] = 0 + value_new[value >= 2] = 0 + + value_new.tofile('Rock_'+inputFile) + + + +def unSegmentGrid8bit(inputFileName, outputFileName): + inputFile=inputFileName + value=np.fromfile(inputFile,dtype=np.uint8) + value_new = np.copy(value) + + value_new[value == 0] = 0 + value_new[value == 1] = 127 + value_new[value == 2] = 255 + + value_new.tofile(outputFileName) + + + +def segmentGrid32to8bit(inputFileName, outputFileName): +## Something wrong with this... + inputFile=inputFileName + value=np.fromfile(inputFile,dtype=np.float32) + value_new = np.copy(value) + value_new.astype(np.uint8) + + + value_new[value < -0.2] = 1 + value_new[value > -0.1, value < 0.1] = 0 + value_new[value > 0.1] = 2 + + value_new.tofile(outputFileName) + +def addVoidLayers(inputFileName, outputFileName, nx, ny, nz, layers): + inputFile=open(inputFileName, 'r') + value=np.fromfile(inputFile,dtype=np.uint8) + inputFile.close() + value_3D = np.reshape(value, (nz,ny,nx)) + value_3D = np.pad(value_3D, ((0,0), (0,0), (layers,layers)), + 'constant', constant_values=((1,1))) + + value_3D = np.pad(value_3D, ((1,1), (1,1), (0,0)), + 'constant', constant_values=((0,0))) + + value_3D.tofile(outputFileName) + + +def crop(inputFileName, outputFileName, nx, ny, nz, x1, x2, y1, y2, z1, z2): + inputFile=open(inputFileName, 'r') + value=np.fromfile(inputFile,dtype=np.uint8) + inputFile.close() + value_3D = np.reshape(value, (nz,ny,nx)) + value_3D_crop = value_3D[z1-1:z2, y1-1:y2, x1-1:x2] + value_3D_crop.tofile(outputFileName) + + +def circleTube(outputFileName,nx,ny,nz): + + tube_3D_new = np.zeros((nz, ny, nx), dtype=np.uint8) + centerZ = nz/2 + centerY = ny/2 + radius = ny/2 + + + # outer loop to handle number of rows + for i in range(0, nx): + + # inner loop to handle number spaces + # values changing acc. to requirement + for j in range(0, ny): + for k in range(0, nz): + if (((j-centerY)**2 + (k-centerZ)**2) < radius**2): + tube_3D_new[k,j,i] = tube_3D_new[k,j,i] + 1 + + tube_3D_new.tofile(outputFileName) + +def squareTube(outputFileName,nx,ny,nz): + + tube_3D_new = np.zeros((nz, ny, nx), dtype=np.uint8) + 1 + tube_3D_new = np.pad(tube_3D_new, ((1,1), (1,1), (0,0)), + 'constant', constant_values=((0,0))) + + + tube_3D_new.tofile(outputFileName) + +def makeVtk(headerVTK, path) : + import os + for f in os.listdir(path): + if os.path.isfile(f) and f.endswith('.raw') and f.startswith('Red_id_t'): + infile = open(f, 'rb') + header = open(headerVTK, 'r') + outFileName = f.split('.')[0]+'.vtk' + outFile = open( outFileName, 'w') + outFile.write(header.read()) + outFile.write(infile.read()) + outFile.close() + infile.close() + header.close() + + + + + + +# Driver Code +#segmentGrid8bitRedPhase('path') +#segmentGrid8bitRockPhase('id_t100008.raw') + + +#makeVtk('headerVTK.txt', 'path') + +#circleTube('Tube.raw',100,101,101) + +#squareTube('SquareTube.raw',100,100,100) + +#crop('BentheimerSegmWW8bit2.raw', 'Outout2.raw', 1432, 461, 457, 100, 699, 5, 404, 5, 404) +#addVoidLayers('Outout2.raw', 'BentheimerSegmWW8bit2CropCoated600x400x400.raw', 600, 400, 400, 0) + + From 7023f91e4aa8f8d826614bd3dc6355361c7e2145 Mon Sep 17 00:00:00 2001 From: James E McClure Date: Tue, 24 Mar 2020 09:23:39 -0400 Subject: [PATCH 07/54] provide control of vis in permeabilty sim --- models/MRTModel.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/models/MRTModel.cpp b/models/MRTModel.cpp index d617380d..acd368f6 100644 --- a/models/MRTModel.cpp +++ b/models/MRTModel.cpp @@ -383,7 +383,9 @@ void ScaLBL_MRTModel::VelocityField(){ if (rank==0) printf("%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g\n",Fx, Fy, Fz, mu, Morphology.V(),Morphology.A(),Morphology.J(),Morphology.X(),vax,vay,vaz); */ - + vis_db = db->getDatabase( "Visualization" ); + if (vis_db->getWithDefault( "write_silo", false )){ + std::vector visData; fillHalo fillData(Dm->Comm,Dm->rank_info,{Dm->Nx-2,Dm->Ny-2,Dm->Nz-2},{1,1,1},0,1); @@ -435,5 +437,5 @@ void ScaLBL_MRTModel::VelocityField(){ fillData.copy(Velocity_z,VelzData); IO::writeData( timestep, visData, Dm->Comm ); - + } } From e3eb37f67d6097dc2356b473d4573d8c8cb898bb Mon Sep 17 00:00:00 2001 From: JamesEMcclure Date: Tue, 24 Mar 2020 09:37:22 -0400 Subject: [PATCH 08/54] added vis_Db to MRT model --- models/MRTModel.h | 1 + 1 file changed, 1 insertion(+) diff --git a/models/MRTModel.h b/models/MRTModel.h index d3803593..9be72339 100644 --- a/models/MRTModel.h +++ b/models/MRTModel.h @@ -64,6 +64,7 @@ public: std::shared_ptr db; std::shared_ptr domain_db; std::shared_ptr mrt_db; + std::shared_ptr vis_db; IntArray Map; DoubleArray Distance; From 5af88489452fbe4fa0ae0b8781ecee36de5ca56a Mon Sep 17 00:00:00 2001 From: JamesEMcclure Date: Thu, 7 May 2020 14:24:04 -0400 Subject: [PATCH 09/54] add copyright header to greyscale LBM --- cpu/Greyscale.cpp | 15 +++++++++++++++ gpu/Greyscale.cu | 16 ++++++++++++++++ models/GreyscaleModel.cpp | 16 +++++++++++++++- models/GreyscaleModel.h | 16 +++++++++++++++- 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/cpu/Greyscale.cpp b/cpu/Greyscale.cpp index b4b017c8..c0fa1ae0 100644 --- a/cpu/Greyscale.cpp +++ b/cpu/Greyscale.cpp @@ -1,3 +1,18 @@ +/* + Copyright 2020 Equinor ASA + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ #include extern "C" void ScaLBL_D3Q19_AAeven_Greyscale(double *dist, int start, int finish, int Np, double rlx, double rlx_eff, double Gx, double Gy, double Gz, diff --git a/gpu/Greyscale.cu b/gpu/Greyscale.cu index 0a9a63e0..7aa9fff9 100644 --- a/gpu/Greyscale.cu +++ b/gpu/Greyscale.cu @@ -1,3 +1,19 @@ +/* + Copyright 2020 Equinor ASA + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . +*/ + #include #define NBLOCKS 1024 diff --git a/models/GreyscaleModel.cpp b/models/GreyscaleModel.cpp index c28c88c5..4966d618 100644 --- a/models/GreyscaleModel.cpp +++ b/models/GreyscaleModel.cpp @@ -1,5 +1,19 @@ /* -Greyscale lattice boltzmann model + Copyright 2020 Equinor ASA + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . + + Greyscale lattice boltzmann model */ #include "models/GreyscaleModel.h" #include "analysis/distance.h" diff --git a/models/GreyscaleModel.h b/models/GreyscaleModel.h index c670239f..9ac31be4 100644 --- a/models/GreyscaleModel.h +++ b/models/GreyscaleModel.h @@ -1,5 +1,19 @@ /* -Implementation of color lattice boltzmann model + Copyright 2020 Equinor ASA + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM 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 OPM. If not, see . + + greyscale lattice boltzmann model */ #include #include From 4ca5d971d3a9fe0ce9d51dbe3cb9b127b3e62fdd Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Tue, 23 Jun 2020 13:51:14 +0200 Subject: [PATCH 10/54] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index acad2c4b..fc758bc9 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Configure, build & install procedure * edit configure script from sample_scripts directory and configure (e.g.) - `/path/to/LBPM-WIA/sample_scripts/configure_desktop` + `/path/to/LBPM/sample_scripts/configure_desktop` * compile and install From 56dc55a14d00973350920515e64891723cd3f08d Mon Sep 17 00:00:00 2001 From: JamesEMcclure Date: Mon, 12 Oct 2020 06:08:29 -0400 Subject: [PATCH 11/54] added equinor copyright statement --- analysis/Minkowski.cpp | 1 + analysis/Minkowski.h | 1 + analysis/PointList.h | 1 + analysis/TwoPhase.cpp | 2 ++ analysis/TwoPhase.h | 1 + analysis/analysis.cpp | 1 + analysis/analysis.h | 1 + analysis/distance.cpp | 1 + analysis/distance.h | 1 + analysis/filters.cpp | 1 + analysis/filters.h | 1 + analysis/histogram.h | 1 + analysis/imfilter.h | 1 + analysis/imfilter.hpp | 2 ++ analysis/pmmc.h | 1 + analysis/runAnalysis.cpp | 1 + analysis/runAnalysis.h | 1 + analysis/uCT.cpp | 1 + analysis/uCT.h | 1 + common/Array.h | 1 + common/Array.hpp | 2 ++ common/Communication.cpp | 1 + common/Communication.h | 1 + common/Communication.hpp | 2 ++ common/Database.cpp | 1 + common/Database.h | 1 + common/Database.hpp | 2 ++ common/Domain.cpp | 2 ++ common/Domain.h | 1 + common/FunctionTable.h | 1 + common/FunctionTable.hpp | 2 ++ common/MPI_Helpers.cpp | 1 + common/MPI_Helpers.h | 1 + common/MPI_Helpers.hpp | 2 ++ common/ScaLBL.cpp | 1 + common/ScaLBL.h | 1 + common/SpherePack.cpp | 2 ++ common/SpherePack.h | 1 + common/UnitTest.cpp | 1 + common/UnitTest.h | 1 + common/Units.cpp | 1 + common/Units.h | 1 + common/Utilities.cpp | 1 + common/Utilities.h | 1 + common/UtilityMacros.h | 1 + cpu/BGK.cpp | 1 + cpu/Color.cpp | 1 + cpu/D3Q19.cpp | 1 + cpu/D3Q7.cpp | 1 + cpu/dfh.cpp | 2 ++ gpu/BGK.cu | 1 + gpu/Color.cu | 1 + gpu/CudaExtras.cu | 1 + gpu/D3Q19.cu | 1 + gpu/D3Q7.cu | 1 + gpu/dfh.cu | 2 ++ models/ColorModel.cpp | 1 + models/ColorModel.h | 1 + models/GreyscaleModel.cpp | 1 + models/GreyscaleModel.h | 1 + models/MRTModel.cpp | 1 + models/MRTModel.h | 1 + 62 files changed, 73 insertions(+) diff --git a/analysis/Minkowski.cpp b/analysis/Minkowski.cpp index e64b2c70..f994ca37 100644 --- a/analysis/Minkowski.cpp +++ b/analysis/Minkowski.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/analysis/Minkowski.h b/analysis/Minkowski.h index b1e3b2a0..c349aba9 100644 --- a/analysis/Minkowski.h +++ b/analysis/Minkowski.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/analysis/PointList.h b/analysis/PointList.h index a5d0fa40..1fd6a351 100644 --- a/analysis/PointList.h +++ b/analysis/PointList.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/analysis/TwoPhase.cpp b/analysis/TwoPhase.cpp index 0bc9a5fd..72cb53c9 100644 --- a/analysis/TwoPhase.cpp +++ b/analysis/TwoPhase.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify @@ -15,6 +16,7 @@ */ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/analysis/TwoPhase.h b/analysis/TwoPhase.h index 20a11c34..9cca8bcf 100644 --- a/analysis/TwoPhase.h +++ b/analysis/TwoPhase.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/analysis/analysis.cpp b/analysis/analysis.cpp index f81fdda5..d1661eac 100644 --- a/analysis/analysis.cpp +++ b/analysis/analysis.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/analysis/analysis.h b/analysis/analysis.h index ea5fab68..b1d9de12 100644 --- a/analysis/analysis.h +++ b/analysis/analysis.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/analysis/distance.cpp b/analysis/distance.cpp index 4cce3cb5..37b5e46e 100644 --- a/analysis/distance.cpp +++ b/analysis/distance.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/analysis/distance.h b/analysis/distance.h index 26d7dfe7..933938b6 100644 --- a/analysis/distance.h +++ b/analysis/distance.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/analysis/filters.cpp b/analysis/filters.cpp index 5e06cdda..d234dde4 100644 --- a/analysis/filters.cpp +++ b/analysis/filters.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/analysis/filters.h b/analysis/filters.h index fb436c55..05b5620d 100644 --- a/analysis/filters.h +++ b/analysis/filters.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/analysis/histogram.h b/analysis/histogram.h index 62e8c279..cc12b226 100644 --- a/analysis/histogram.h +++ b/analysis/histogram.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/analysis/imfilter.h b/analysis/imfilter.h index 3f7f9301..670d2514 100644 --- a/analysis/imfilter.h +++ b/analysis/imfilter.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/analysis/imfilter.hpp b/analysis/imfilter.hpp index 7d13d54d..75402df9 100644 --- a/analysis/imfilter.hpp +++ b/analysis/imfilter.hpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify @@ -15,6 +16,7 @@ */ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/analysis/pmmc.h b/analysis/pmmc.h index 436420e3..f8b22c6d 100644 --- a/analysis/pmmc.h +++ b/analysis/pmmc.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/analysis/runAnalysis.cpp b/analysis/runAnalysis.cpp index 4eabd1f7..fd8f9a47 100644 --- a/analysis/runAnalysis.cpp +++ b/analysis/runAnalysis.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/analysis/runAnalysis.h b/analysis/runAnalysis.h index 2a39d25e..96cc1182 100644 --- a/analysis/runAnalysis.h +++ b/analysis/runAnalysis.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/analysis/uCT.cpp b/analysis/uCT.cpp index ea3b3cd6..7a4b4e31 100644 --- a/analysis/uCT.cpp +++ b/analysis/uCT.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/analysis/uCT.h b/analysis/uCT.h index 63be1a56..940b3d38 100644 --- a/analysis/uCT.h +++ b/analysis/uCT.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/Array.h b/common/Array.h index af4274b0..1e15695f 100644 --- a/common/Array.h +++ b/common/Array.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/Array.hpp b/common/Array.hpp index e62ad247..4e6a2913 100644 --- a/common/Array.hpp +++ b/common/Array.hpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify @@ -15,6 +16,7 @@ */ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/Communication.cpp b/common/Communication.cpp index 3f669c3f..3d383d3d 100644 --- a/common/Communication.cpp +++ b/common/Communication.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/Communication.h b/common/Communication.h index dc121c3f..d22b09a5 100644 --- a/common/Communication.h +++ b/common/Communication.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/Communication.hpp b/common/Communication.hpp index b1f8ecc4..a33c8843 100644 --- a/common/Communication.hpp +++ b/common/Communication.hpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify @@ -15,6 +16,7 @@ */ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/Database.cpp b/common/Database.cpp index ab01ef3b..9d73d5ef 100644 --- a/common/Database.cpp +++ b/common/Database.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/Database.h b/common/Database.h index 6aaa0dd1..016868cf 100644 --- a/common/Database.h +++ b/common/Database.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/Database.hpp b/common/Database.hpp index 1a906736..cc3899aa 100644 --- a/common/Database.hpp +++ b/common/Database.hpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify @@ -15,6 +16,7 @@ */ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/Domain.cpp b/common/Domain.cpp index 796ae447..c6e0887f 100644 --- a/common/Domain.cpp +++ b/common/Domain.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify @@ -15,6 +16,7 @@ */ // Created by James McClure // Copyright 2008-2013 + Copyright Equnior ASA #include #include #include diff --git a/common/Domain.h b/common/Domain.h index 16409c13..112aeb05 100644 --- a/common/Domain.h +++ b/common/Domain.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/FunctionTable.h b/common/FunctionTable.h index c1c4d10c..bc881b4b 100644 --- a/common/FunctionTable.h +++ b/common/FunctionTable.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/FunctionTable.hpp b/common/FunctionTable.hpp index 1b1689d8..118e7734 100644 --- a/common/FunctionTable.hpp +++ b/common/FunctionTable.hpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify @@ -15,6 +16,7 @@ */ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/MPI_Helpers.cpp b/common/MPI_Helpers.cpp index 5b4e2457..9fadc726 100644 --- a/common/MPI_Helpers.cpp +++ b/common/MPI_Helpers.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/MPI_Helpers.h b/common/MPI_Helpers.h index 2d33e499..dedfc63c 100644 --- a/common/MPI_Helpers.h +++ b/common/MPI_Helpers.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/MPI_Helpers.hpp b/common/MPI_Helpers.hpp index 2a82f90c..74fa9ca8 100644 --- a/common/MPI_Helpers.hpp +++ b/common/MPI_Helpers.hpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify @@ -15,6 +16,7 @@ */ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/ScaLBL.cpp b/common/ScaLBL.cpp index 816098a7..79a0f190 100644 --- a/common/ScaLBL.cpp +++ b/common/ScaLBL.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/ScaLBL.h b/common/ScaLBL.h index e379bfad..cc5bdcc7 100644 --- a/common/ScaLBL.h +++ b/common/ScaLBL.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/SpherePack.cpp b/common/SpherePack.cpp index 907c4c74..fc47525d 100644 --- a/common/SpherePack.cpp +++ b/common/SpherePack.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify @@ -15,6 +16,7 @@ */ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/SpherePack.h b/common/SpherePack.h index 59a46c9c..6c6905f6 100644 --- a/common/SpherePack.h +++ b/common/SpherePack.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/UnitTest.cpp b/common/UnitTest.cpp index ce671e87..bb7c849b 100644 --- a/common/UnitTest.cpp +++ b/common/UnitTest.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/UnitTest.h b/common/UnitTest.h index 92af309d..339bd65e 100644 --- a/common/UnitTest.h +++ b/common/UnitTest.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/Units.cpp b/common/Units.cpp index 9c374bdc..f6e9b79a 100644 --- a/common/Units.cpp +++ b/common/Units.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/Units.h b/common/Units.h index 441f1275..2ae503cd 100644 --- a/common/Units.h +++ b/common/Units.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/Utilities.cpp b/common/Utilities.cpp index 6082f7dd..e67b2fb1 100644 --- a/common/Utilities.cpp +++ b/common/Utilities.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/Utilities.h b/common/Utilities.h index fc3f3e4b..dfe180a5 100644 --- a/common/Utilities.h +++ b/common/Utilities.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/common/UtilityMacros.h b/common/UtilityMacros.h index fb501c1d..185acbc5 100644 --- a/common/UtilityMacros.h +++ b/common/UtilityMacros.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/cpu/BGK.cpp b/cpu/BGK.cpp index d0249ead..0df5b1b0 100644 --- a/cpu/BGK.cpp +++ b/cpu/BGK.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/cpu/Color.cpp b/cpu/Color.cpp index 48578fc0..44e86780 100644 --- a/cpu/Color.cpp +++ b/cpu/Color.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/cpu/D3Q19.cpp b/cpu/D3Q19.cpp index 8d897e84..5a36880a 100644 --- a/cpu/D3Q19.cpp +++ b/cpu/D3Q19.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/cpu/D3Q7.cpp b/cpu/D3Q7.cpp index 6cde5433..9941e7bc 100644 --- a/cpu/D3Q7.cpp +++ b/cpu/D3Q7.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/cpu/dfh.cpp b/cpu/dfh.cpp index 1cbc2db1..419af282 100644 --- a/cpu/dfh.cpp +++ b/cpu/dfh.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify @@ -15,6 +16,7 @@ */ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/gpu/BGK.cu b/gpu/BGK.cu index d89e1298..c117afd6 100644 --- a/gpu/BGK.cu +++ b/gpu/BGK.cu @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/gpu/Color.cu b/gpu/Color.cu index 2f70a60d..c25e0f08 100644 --- a/gpu/Color.cu +++ b/gpu/Color.cu @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/gpu/CudaExtras.cu b/gpu/CudaExtras.cu index 8e3ef955..3351131c 100644 --- a/gpu/CudaExtras.cu +++ b/gpu/CudaExtras.cu @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/gpu/D3Q19.cu b/gpu/D3Q19.cu index d31712b1..c1bfdbc3 100644 --- a/gpu/D3Q19.cu +++ b/gpu/D3Q19.cu @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/gpu/D3Q7.cu b/gpu/D3Q7.cu index 2ac58245..5e99b681 100644 --- a/gpu/D3Q7.cu +++ b/gpu/D3Q7.cu @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/gpu/dfh.cu b/gpu/dfh.cu index f4dbe299..239b9828 100644 --- a/gpu/dfh.cu +++ b/gpu/dfh.cu @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify @@ -15,6 +16,7 @@ */ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/models/ColorModel.cpp b/models/ColorModel.cpp index c02a268c..d8c5edc3 100644 --- a/models/ColorModel.cpp +++ b/models/ColorModel.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/models/ColorModel.h b/models/ColorModel.h index 11d0bca4..4e0880bd 100644 --- a/models/ColorModel.h +++ b/models/ColorModel.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/models/GreyscaleModel.cpp b/models/GreyscaleModel.cpp index 39ee7554..eb78d47f 100644 --- a/models/GreyscaleModel.cpp +++ b/models/GreyscaleModel.cpp @@ -1,5 +1,6 @@ /* Copyright 2020 Equinor ASA + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/models/GreyscaleModel.h b/models/GreyscaleModel.h index f525eccd..82cf3b0f 100644 --- a/models/GreyscaleModel.h +++ b/models/GreyscaleModel.h @@ -1,5 +1,6 @@ /* Copyright 2020 Equinor ASA + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/models/MRTModel.cpp b/models/MRTModel.cpp index 418bd073..b37675e1 100644 --- a/models/MRTModel.cpp +++ b/models/MRTModel.cpp @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify diff --git a/models/MRTModel.h b/models/MRTModel.h index 9be72339..1fc04bc2 100644 --- a/models/MRTModel.h +++ b/models/MRTModel.h @@ -1,5 +1,6 @@ /* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University + Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify From 1c4f4801b32ce4e507f9ea50cce0fdc1a30f99c6 Mon Sep 17 00:00:00 2001 From: JamesEMcclure Date: Mon, 12 Oct 2020 09:35:55 -0400 Subject: [PATCH 12/54] fix uncommented copyright --- common/Domain.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/common/Domain.cpp b/common/Domain.cpp index c6e0887f..01d7cbb0 100644 --- a/common/Domain.cpp +++ b/common/Domain.cpp @@ -16,7 +16,6 @@ */ // Created by James McClure // Copyright 2008-2013 - Copyright Equnior ASA #include #include #include From 170bec519efe1b129eaa09df1f4ecb42bf22e55c Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Mon, 21 Jun 2021 20:49:53 +0200 Subject: [PATCH 13/54] Update README.md Add code blocks --- docs/README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index bd31ba05..7d22422a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,19 +1,27 @@ Dependencies for LBPM documentation # install sphinx +```python pip install Sphinx - +``` # foamatting requires sphinx read-the-docs-theme +```python pip install sphinx-rtd-theme +``` + # equation rendering requires latex and dvipng command +``` sudo apt-get install dvipng sudo apt-get install texlive texstudio sudo apt-get install texlive-latex-recommended texlive-pictures texlive-latex-extra +``` # To build the docs +``` Step 1) install dependencies listed above Step 2) type 'make html' from the docs/ directory Step 3) point your browser at ~/local/doc/build/html/index.html -# \ No newline at end of file +``` +# From 0676e61f92a166057f5deafd789a29a9b9fee9f7 Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Mon, 21 Jun 2021 20:51:42 +0200 Subject: [PATCH 14/54] Update visit.rst --- docs/source/userGuide/visualization/visit.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/userGuide/visualization/visit.rst b/docs/source/userGuide/visualization/visit.rst index f9ff2c8a..4d9e1158 100644 --- a/docs/source/userGuide/visualization/visit.rst +++ b/docs/source/userGuide/visualization/visit.rst @@ -3,3 +3,5 @@ Visualizing simulation data with visit ====================================== placeholder for visit + +Paraview > v 5.6 also works From 9f1f785450c234c7cb39d66683ca4275b48a7c57 Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Mon, 30 Aug 2021 10:07:53 +0200 Subject: [PATCH 15/54] Update grid utility script --- workflows/Util/gridUtil.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/workflows/Util/gridUtil.py b/workflows/Util/gridUtil.py index 16b4e954..35427c59 100644 --- a/workflows/Util/gridUtil.py +++ b/workflows/Util/gridUtil.py @@ -95,6 +95,7 @@ def segmentGrid32to8bit(inputFileName, outputFileName): value_new.tofile(outputFileName) + def addVoidLayers(inputFileName, outputFileName, nx, ny, nz, layers): inputFile=open(inputFileName, 'r') value=np.fromfile(inputFile,dtype=np.uint8) @@ -108,7 +109,7 @@ def addVoidLayers(inputFileName, outputFileName, nx, ny, nz, layers): value_3D.tofile(outputFileName) - +# from and including -- to and including def crop(inputFileName, outputFileName, nx, ny, nz, x1, x2, y1, y2, z1, z2): inputFile=open(inputFileName, 'r') value=np.fromfile(inputFile,dtype=np.uint8) From f3397c9d09f3f2fda86e3d93a75c037c7a0f522a Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Mon, 30 Aug 2021 10:59:52 +0200 Subject: [PATCH 16/54] Update relperm.csv file Added low and high limits for effective permeability based on the effective porosity seen by the decoupled phase. Acts as lower limit --- models/ColorModel.cpp | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/models/ColorModel.cpp b/models/ColorModel.cpp index a71ce0c9..be9ea7a0 100644 --- a/models/ColorModel.cpp +++ b/models/ColorModel.cpp @@ -825,16 +825,29 @@ double ScaLBL_ColorModel::Run(int returntime){ double kAeff_connected = h*h*muA*flow_rate_A_connected/(force_mag); double kBeff_connected = h*h*muB*flow_rate_B_connected/(force_mag); + // Saturation normalized effective permeability to account for decoupled phases and + // effective porosity. + double kAeff_connected_low = (1.0 - current_saturation)*h*h*muA*flow_rate_A_connected/(force_mag); + double kBeff_connected_low = current_saturation*h*h*muB*flow_rate_B_connected/(force_mag); + + double kAeff_disconnected = h*h*muA*flow_rate_A_disconnected/(force_mag); double kBeff_disconnected = h*h*muB*flow_rate_B_disconnected/(force_mag); double kAeff = h*h*muA*(flow_rate_A)/(force_mag); double kBeff = h*h*muB*(flow_rate_B)/(force_mag); + // Saturation normalized effective permeability to account for decoupled phases and + // effective porosity. + double kAeff_low = (1.0 - current_saturation)*h*h*muA*(flow_rate_A)/(force_mag); + double kBeff_low = current_saturation*h*h*muB*(flow_rate_B)/(force_mag); + /* flow rate = [volume of fluid] x [momentum of fluid] / [mass of fluid] */ /* fluid A eats the films */ - double flow_rate_A_filmA = (flow_rate_A*Averages->gnb.M + volA*(vfn_x*dir_x + vfn_y*dir_y + vfn_z*dir_z))/(Averages->gnb.M + Mfn); - double flow_rate_B_filmA = (flow_rate_B*Averages->gwb.M - volB*(vfn_x*dir_x + vfn_y*dir_y + vfn_z*dir_z))/(Averages->gwb.M - (rhoB/rhoA)*Mfn); + double flow_rate_A_filmA = (flow_rate_A*Averages->gnb.M + + volA*(vfn_x*dir_x + vfn_y*dir_y + vfn_z*dir_z))/(Averages->gnb.M + Mfn); + double flow_rate_B_filmA = (flow_rate_B*Averages->gwb.M - + volB*(vfn_x*dir_x + vfn_y*dir_y + vfn_z*dir_z))/(Averages->gwb.M - (rhoB/rhoA)*Mfn); /* fluid B eats the films */ double flow_rate_A_filmB = (flow_rate_A*Averages->gnb.M - volA*(vfw_x*dir_x + vfw_y*dir_y + vfw_z*dir_z))/(Averages->gnb.M - (rhoA/rhoB)*Mfw); double flow_rate_B_filmB = (flow_rate_B*Averages->gwb.M + volB*(vfw_x*dir_x + vfw_y*dir_y + vfw_z*dir_z))/(Averages->gwb.M + Mfw); @@ -855,13 +868,22 @@ double ScaLBL_ColorModel::Run(int returntime){ WriteHeader=true; kr_log_file = fopen("relperm.csv","a"); if (WriteHeader){ - fprintf(kr_log_file,"timesteps sat.water eff.perm.oil eff.perm.water eff.perm.oil.connected eff.perm.water.connected eff.perm.oil.disconnected eff.perm.water.disconnected "); - fprintf(kr_log_file,"eff.perm.oil.upper.bound eff.perm.water.lower.bound eff.perm.oil.lower.bound eff.perm.water.upper.bound "); - fprintf(kr_log_file,"cap.pressure cap.pressure.connected pressure.drop Ca M\n"); + fprintf(kr_log_file, "timesteps sat.water "); + fprintf(kr_log_file, "eff.perm.oil.upper.bound eff.perm.water.upper.bound "); + fprintf(kr_log_file, "eff.perm.oil.lower.bound eff.perm.water.lower.bound "); + fprintf(kr_log_file, "eff.perm.oil.connected.upper.bound eff.perm.water.connected.upper.bound "); + fprintf(kr_log_file, "eff.perm.oil.connected.lower.bound eff.perm.water.connected.lower.bound "); + fprintf(kr_log_file, "eff.perm.oil.disconnected eff.perm.water.disconnected "); + fprintf(kr_log_file, "cap.pressure cap.pressure.connected pressure.drop Ca M\n"); + } - fprintf(kr_log_file,"%i %.5g %.5g %.5g %.5g %.5g %.5g %.5g ",CURRENT_TIMESTEP,current_saturation,kAeff,kBeff,kAeff_connected,kBeff_connected,kAeff_disconnected,kBeff_disconnected); - fprintf(kr_log_file,"%.5g %.5g %.5g %.5g ",kAeff_filmA, kBeff_filmA, kAeff_filmB,kBeff_filmB); - fprintf(kr_log_file,"%.5g %.5g %.5g %.5g %.5g\n",pAB,pAB_connected,viscous_pressure_drop,Ca,Mobility); + fprintf(kr_log_file,"%i %.5g ", CURRENT_TIMESTEP,current_saturation); + fprintf(kr_log_file,"%.5g %.5g ", kAeff, kBeff); + fprintf(kr_log_file,"%.5g %.5g ", kAeff_low, kBeff_low); + fprintf(kr_log_file,"%.5g %.5g ", kAeff_connected, kBeff_connected); + fprintf(kr_log_file,"%.5g %.5g ", kAeff_connected_low, kBeff_connected_low); + fprintf(kr_log_file,"%.5g %.5g ", kAeff_disconnected, kBeff_disconnected); + fprintf(kr_log_file,"%.5g %.5g %.5g %.5g %.5g\n", pAB, pAB_connected, viscous_pressure_drop, Ca, Mobility); fclose(kr_log_file); printf(" Measured capillary number %f \n ",Ca); From 0be38be1396f587699962298bcf0ebba8602b3e6 Mon Sep 17 00:00:00 2001 From: JamesEMcclure Date: Wed, 1 Sep 2021 10:07:27 -0400 Subject: [PATCH 17/54] remove warnings for color --- models/ColorModel.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/models/ColorModel.cpp b/models/ColorModel.cpp index 63fe76a7..b34713c6 100644 --- a/models/ColorModel.cpp +++ b/models/ColorModel.cpp @@ -845,18 +845,18 @@ double ScaLBL_ColorModel::Run(int returntime){ /* flow rate = [volume of fluid] x [momentum of fluid] / [mass of fluid] */ /* fluid A eats the films */ - double flow_rate_A_filmA = (flow_rate_A*Averages->gnb.M + - volA*(vfn_x*dir_x + vfn_y*dir_y + vfn_z*dir_z))/(Averages->gnb.M + Mfn); - double flow_rate_B_filmA = (flow_rate_B*Averages->gwb.M - - volB*(vfn_x*dir_x + vfn_y*dir_y + vfn_z*dir_z))/(Averages->gwb.M - (rhoB/rhoA)*Mfn); + //double flow_rate_A_filmA = (flow_rate_A*Averages->gnb.M + + // volA*(vfn_x*dir_x + vfn_y*dir_y + vfn_z*dir_z))/(Averages->gnb.M + Mfn); + //double flow_rate_B_filmA = (flow_rate_B*Averages->gwb.M - + // volB*(vfn_x*dir_x + vfn_y*dir_y + vfn_z*dir_z))/(Averages->gwb.M - (rhoB/rhoA)*Mfn); /* fluid B eats the films */ - double flow_rate_A_filmB = (flow_rate_A*Averages->gnb.M - volA*(vfw_x*dir_x + vfw_y*dir_y + vfw_z*dir_z))/(Averages->gnb.M - (rhoA/rhoB)*Mfw); - double flow_rate_B_filmB = (flow_rate_B*Averages->gwb.M + volB*(vfw_x*dir_x + vfw_y*dir_y + vfw_z*dir_z))/(Averages->gwb.M + Mfw); + //double flow_rate_A_filmB = (flow_rate_A*Averages->gnb.M - volA*(vfw_x*dir_x + vfw_y*dir_y + vfw_z*dir_z))/(Averages->gnb.M - (rhoA/rhoB)*Mfw); + //double flow_rate_B_filmB = (flow_rate_B*Averages->gwb.M + volB*(vfw_x*dir_x + vfw_y*dir_y + vfw_z*dir_z))/(Averages->gwb.M + Mfw); /* effective permeability uncertainty limits */ - double kAeff_filmA = h*h*muA*(flow_rate_A_filmA)/(force_mag); - double kBeff_filmA = h*h*muB*(flow_rate_B_filmA)/(force_mag); - double kAeff_filmB = h*h*muA*(flow_rate_A_filmB)/(force_mag); - double kBeff_filmB = h*h*muB*(flow_rate_B_filmB)/(force_mag); + //double kAeff_filmA = h*h*muA*(flow_rate_A_filmA)/(force_mag); + //double kBeff_filmA = h*h*muB*(flow_rate_B_filmA)/(force_mag); + //double kAeff_filmB = h*h*muA*(flow_rate_A_filmB)/(force_mag); + //double kBeff_filmB = h*h*muB*(flow_rate_B_filmB)/(force_mag); double viscous_pressure_drop = (rhoA*volA + rhoB*volB)*force_mag; double Mobility = muA/muB; From abc1c09214d648539a8c4dd488ee33b11fcbd40e Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Mon, 13 Sep 2021 13:34:18 +0200 Subject: [PATCH 18/54] Update install.rst Install path to MPI --- docs/source/install.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/install.rst b/docs/source/install.rst index 635c92d0..418d42ef 100644 --- a/docs/source/install.rst +++ b/docs/source/install.rst @@ -29,7 +29,7 @@ Building Dependencies .. code-block:: bash - export MPI_DIR=/path/to/mpi/ + export MPI_DIR=/path/to/mpi export LBPM_ZLIB_DIR=/path/to/zlib export LBPM_HDF5_DIR=/path/to/hdf5 export LBPM_SILO_DIR=/path/to/silo From d6e90e23cd9b899bf31cb192a7ca324361aef5c4 Mon Sep 17 00:00:00 2001 From: JamesEMcclure Date: Mon, 20 Sep 2021 09:28:18 -0400 Subject: [PATCH 19/54] remove unused film terms --- models/ColorModel.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/models/ColorModel.cpp b/models/ColorModel.cpp index 38cd5764..9a308254 100644 --- a/models/ColorModel.cpp +++ b/models/ColorModel.cpp @@ -833,15 +833,6 @@ double ScaLBL_ColorModel::Run(int returntime){ double vBd_x = Averages->gwd.Px/Mass_w; double vBd_y = Averages->gwd.Py/Mass_w; double vBd_z = Averages->gwd.Pz/Mass_w; - // film contribution - double Mfn = Averages->gifs.Mn; - double Mfw = Averages->gifs.Mw; - double vfn_x = Averages->gifs.Pnx; - double vfn_y = Averages->gifs.Pny; - double vfn_z = Averages->gifs.Pnz; - double vfw_x = Averages->gifs.Pwx; - double vfw_y = Averages->gifs.Pwy; - double vfw_z = Averages->gifs.Pwz; double flow_rate_A_connected = Vol_nc*(vAc_x*dir_x + vAc_y*dir_y + vAc_z*dir_z); double flow_rate_B_connected = Vol_wc*(vBc_x*dir_x + vBc_y*dir_y + vBc_z*dir_z); From 44426848c4407a260bb24eb3743980c59c63f44e Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Mon, 4 Oct 2021 14:32:09 +0200 Subject: [PATCH 20/54] Add effective pressure Added effective pressure 1 / k_eff to the output. To be used in history matching of core flooding. --- analysis/SubPhase.cpp | 11 +++++++---- models/ColorModel.cpp | 8 +++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/analysis/SubPhase.cpp b/analysis/SubPhase.cpp index 42ea8d7c..ba28f899 100644 --- a/analysis/SubPhase.cpp +++ b/analysis/SubPhase.cpp @@ -96,7 +96,7 @@ SubPhase::SubPhase(std::shared_ptr dm): { // If timelog is empty, write a short header to list the averages //fprintf(TIMELOG,"--------------------------------------------------------------------------------------\n"); - fprintf(TIMELOG,"sw krw krn krwf krnf vw vn force pw pn wet\n"); + fprintf(TIMELOG,"sw krw krn krwf krnf vw vn force pw pn wet peff\n"); } } } @@ -378,15 +378,18 @@ void SubPhase::Basic(){ double not_water_film_flow_rate=gnb.V*(giwn.Pnx*dir_x + giwn.Pny*dir_y + giwn.Pnz*dir_z)/gnb.M / Dm->Volume; //double total_flow_rate = water_flow_rate + not_water_flow_rate; //double fractional_flow = water_flow_rate / total_flow_rate; - double h = Dm->voxel_length; double krn = h*h*nu_n*not_water_flow_rate / force_mag ; double krw = h*h*nu_w*water_flow_rate / force_mag; /* not counting films */ double krnf = krn - h*h*nu_n*not_water_film_flow_rate / force_mag ; double krwf = krw - h*h*nu_w*water_film_flow_rate / force_mag; - //printf(" water saturation = %f, fractional flow =%f \n",saturation,fractional_flow); - fprintf(TIMELOG,"%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g\n",saturation,krw,krn,krwf,krnf,h*water_flow_rate,h*not_water_flow_rate, force_mag, gwb.p, gnb.p, total_wetting_interaction_global); + double eff_pressure = 1.0 / (krn + krw); // effective pressure drop + + fprintf(TIMELOG,"%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g\n", + saturation, krw, krn, krwf, krnf, h*water_flow_rate, + h*not_water_flow_rate, force_mag, + gwb.p, gnb.p, total_wetting_interaction_global, eff_pressure); fflush(TIMELOG); } if (err==true){ diff --git a/models/ColorModel.cpp b/models/ColorModel.cpp index 9a308254..2c214db3 100644 --- a/models/ColorModel.cpp +++ b/models/ColorModel.cpp @@ -875,7 +875,8 @@ double ScaLBL_ColorModel::Run(int returntime){ //double kBeff_filmB = h*h*muB*(flow_rate_B_filmB)/(force_mag); double viscous_pressure_drop = (rhoA*volA + rhoB*volB)*force_mag; - double Mobility = muA/muB; + double Mobility = muA/muB; // visc contrast + double eff_pres = 1.0 / (kAeff + kBeff); // effective pressure drop bool WriteHeader=false; FILE * kr_log_file = fopen("relperm.csv","r"); @@ -891,7 +892,7 @@ double ScaLBL_ColorModel::Run(int returntime){ fprintf(kr_log_file, "eff.perm.oil.connected.upper.bound eff.perm.water.connected.upper.bound "); fprintf(kr_log_file, "eff.perm.oil.connected.lower.bound eff.perm.water.connected.lower.bound "); fprintf(kr_log_file, "eff.perm.oil.disconnected eff.perm.water.disconnected "); - fprintf(kr_log_file, "cap.pressure cap.pressure.connected pressure.drop Ca M\n"); + fprintf(kr_log_file, "cap.pressure cap.pressure.connected pressure.drop Ca M eff.pressure\n"); } fprintf(kr_log_file,"%i %.5g ", CURRENT_TIMESTEP,current_saturation); @@ -900,7 +901,8 @@ double ScaLBL_ColorModel::Run(int returntime){ fprintf(kr_log_file,"%.5g %.5g ", kAeff_connected, kBeff_connected); fprintf(kr_log_file,"%.5g %.5g ", kAeff_connected_low, kBeff_connected_low); fprintf(kr_log_file,"%.5g %.5g ", kAeff_disconnected, kBeff_disconnected); - fprintf(kr_log_file,"%.5g %.5g %.5g %.5g %.5g\n", pAB, pAB_connected, viscous_pressure_drop, Ca, Mobility); + fprintf(kr_log_file,"%.5g %.5g %.5g %.5g %.5g ", pAB, pAB_connected, viscous_pressure_drop, Ca, Mobility); + fprintf(kr_log_file,"%.5g\n", eff_pres); fclose(kr_log_file); printf(" Measured capillary number %f \n ",Ca); From e2f18425fc265d6d2f5345ae2b08f994a591f947 Mon Sep 17 00:00:00 2001 From: James McClure Date: Mon, 25 Oct 2021 12:49:09 -0400 Subject: [PATCH 21/54] add static images for devloper docs --- docs/source/_static/images/AA-stream.png | Bin 0 -> 340055 bytes .../_static/images/data-structures-v0.png | Bin 0 -> 169203 bytes docs/source/_static/images/domain-decomp.png | Bin 0 -> 278163 bytes .../designOverview/dataStructures.rst | 119 +++++++++++++++++- 4 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 docs/source/_static/images/AA-stream.png create mode 100644 docs/source/_static/images/data-structures-v0.png create mode 100644 docs/source/_static/images/domain-decomp.png diff --git a/docs/source/_static/images/AA-stream.png b/docs/source/_static/images/AA-stream.png new file mode 100644 index 0000000000000000000000000000000000000000..2068ed9067a62f997f155f9d352660c4aac9c63d GIT binary patch literal 340055 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

)Cxn#*<3G#tBSfi7$+410U!vm4EDM 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 z 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%0DxpmlvrI-Oa*zlB3-p$~KkClUFM2}`*4q>~ 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

1uSKL%7CdFH(?jBV*eduQ$lw)j% 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⪻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 literal 0 HcmV?d00001 diff --git a/docs/source/developerGuide/designOverview/dataStructures.rst b/docs/source/developerGuide/designOverview/dataStructures.rst index bea4f91b..dc791601 100644 --- a/docs/source/developerGuide/designOverview/dataStructures.rst +++ b/docs/source/developerGuide/designOverview/dataStructures.rst @@ -3,4 +3,121 @@ Data Structures =============== LBPM includes a variety of generalized data structures to facilitate the implementation -of different lattice Boltzmann models. +of different lattice Boltzmann models. These core data structures are designed so that +they are agnostic to the particular physics of a model. Many different lattice Boltzmann +schemes can be constructed using the same basic data structures. The core data structures +are primarily designed to + +1) prepare 3D image data for distributed memory simulation based on MPI +2) develop common data structures to facilitate the local streaming step +3) generate and store information needed to exchange data between MPI processes +4) develop common routines for parallel communication for lattice Boltzmann methods. + +By using the core data structures, it is possible to develop new physical models +without having to revisit the data structures or parallel algorithms on which a model is +built. Understanding and using the core data structures is very useful if you intend +to build new physical models within LBPM. In most cases, only the collision step +will need to be developed to implement a particular physical model. + + +------------------ +Domain +------------------ + +The ``Domain`` data structure includes information needed to carry out generic +parallel computations on 3D image data. LBPM is designed to support parallel +simulation where the image data is distributed over a potentially large number of +processors. Each processor recieves an equal size chunk of the input image, +which is specified based on the input database file in the form ``n = nx, ny, nz``. +The total image size is specified as ``N = Nx, Ny, Nz``, which is needed to read +the input image. In the typical case, the input image data will be read only by MPI +rank 0, and sub-domains will be assigned and distributed to each MPI process. +The regular process grid is specified as ``nproc = Px, Py, Pz``. If the entire image +is to be distributed across the processors, the values must be chosen such that +``Nx = nx*Px``, ``Ny = ny*Py`` and ``Nz = nz*Pz``. The basic purpose for the +data structures contained in the ``Domain`` class is to retain basic information +about the domain structure to support parallel computations in MPI. Code within the +``analysis/`` directory will generally rely on these data structures to determine +how to perform computations in parallel. For GPU-based application, information +contained within the ``Domain`` structure is usually retained only the CPU. +Lattice Boltzmann algorithms rely on dedicated data structures within the ``ScaLBL`` +class to perform MPI computations, which are designed for these applications +specifically. + +.. figure:: ../../_static/images/domain-decomp.png + :width: 600 + :alt: domain decomposition + + Domain decomposition used by LBPM to distribute 3D image data across processors. + Each processor recieves an equal size block of the input image. The Domain class + organizes MPI communications with adjacent blocks based on the processor layout. + + +------------------ +ScaLBL +------------------ + + +While the ``Domain`` data structures retain essential information needed to +carry out generic types of computations based on regular 3D image files, +``ScaLBL`` data structures are designed specifically for lattice Boltzmann methods. +The main purposes for these data structures are to: + +1) allow for overlap between computations that do not depend on MPI communication and the + MPI communications required by LBMs (e.g. to carry out the streaming step, perform + halo exchanges, etc.) +2) reduce the computational and memory requirements by excluding image sites where flow + does not occur; and +3) to facilitate efficient data access patterns based on the memory layout used by + data structures. + +These are critical steps to provide good parallel performance and make efficient use +of GPUs. + + +In many cases flow will occur only in a fraction of the voxels from the original 3D input +image. Since LBM schemes store a lot of data for each active voxels (e.g. 19 double precision +values for a single D3Q19 model), significant memory savings can be realized by excluding +immobile voxels from the data structure. Furthermore, it is useful to distiguish between +the interior lattice sites (which do not require MPI communications to update) and the exterior +lattice sites (which must wait for MPI communications to complete before the local update can +be completed). Within LBPM it is almost always advantageous to overlap interior computations with +MPI communications, so that the latter costs can be hidden behind the computational density +of the main lattice Boltzmann kernels. As the local sub-domain size increases, the number of +interior sites will tend to be significantly larger than the number of exterior sites. +The data layout is summarized in the image below. + + +.. figure:: ../../_static/images/data-structures-v0.png + :width: 600 + :alt: ScaLBL data structure layout + + Data structure used by ScaLBL to support lattice Boltzmann methods. + Regions of the input image that are occupied by solid are excluded. + Interior and exterior lattice sites are stored separately so that + computations can efficiently overlap with MPI communication. + +Another factor in the data structure design are the underlying algorithms used by the LBM +to carry out the streaming step. There are at least a half dozen distinct algorithms that +can be used to perform streaming for lattice Boltzmann methods, each with their own advantages +and disadvantages. Generally, the choice of streaming algorithm should reduce the overall memory +footprint while also avoiding unnecessary memory accesses. LBPM uses the AA algorithm so +that a single array is needed to perform the streaming step. The AA algorithm relies on the symmetry +of the discrete velocity set used by LBMs to implement an efficient algorithm. Conceptually, +we can easily see that for each distribution that needs to be accessed to perform streaming, +there is an opposite distribution. The AA algorithm proceeds by identifying these distributions +and exchanging their storage locations in memory, as depicted in the image below. The data +access pattern will therefore alternate between even and odd timesteps. Within LBPM, a ``NeighborList`` +is constructed to store the memory location for the accompanying pair for each distribution. +If the neighboring site is in the solid, the neighbor is specified such that the halfway +bounceback rule will applied automatically. In this way, the streaming step can be performed +very efficiently on either GPU or CPU. + + +.. figure:: ../../_static/images/AA-stream.png + :width: 600 + :alt: AA streaming pattern + + Data access pattern for AA streaming algorithm used by LBPM. The location to read + and write distributions alternates between even and odd timesteps. A solid bounceback + rule is built into the data structure. From 58b34fd3657b72dba6fbc20491959e5410f56041 Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Wed, 27 Oct 2021 22:23:37 +0200 Subject: [PATCH 22/54] Create test_install_openmpi.yml Test file in GitHub actions. --- .github/workflows/test_install_openmpi.yml | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/test_install_openmpi.yml diff --git a/.github/workflows/test_install_openmpi.yml b/.github/workflows/test_install_openmpi.yml new file mode 100644 index 00000000..82a9db47 --- /dev/null +++ b/.github/workflows/test_install_openmpi.yml @@ -0,0 +1,25 @@ +name: Install OpenMPI test + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + install-openmpi: + + runs-on: ubuntu-latest + + steps: + - name: download-openmpi + run: wget https://download.open-mpi.org/release/open-mpi/v4.0/openmpi-4.0.2.tar.gz + - name: extract-openmpi + run: tar -xvf ./openmpi-4.0.2.tar.gz + - name: configure-openmpi + run: ./openmpi-4.0.2/configure --prefix="/home/${USER}/.openmpi" + - name: install-openmpi + run: | + make -j + sudo make install + From 2c8ff644adb511e79a4021070a24b95c5e4d31b7 Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Wed, 27 Oct 2021 22:36:02 +0200 Subject: [PATCH 23/54] Update test_install_openmpi.yml --- .github/workflows/test_install_openmpi.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test_install_openmpi.yml b/.github/workflows/test_install_openmpi.yml index 82a9db47..1ce440e4 100644 --- a/.github/workflows/test_install_openmpi.yml +++ b/.github/workflows/test_install_openmpi.yml @@ -22,4 +22,9 @@ jobs: run: | make -j sudo make install - + - name: setting path + run: echo "/home/${USER}/.openmpi/bin" >> $GITHUB_PATH + + - name: checking version + run: mpirun --version + From 9ae67fe337cef01825633a40e19ffdddeb5c5b67 Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Wed, 27 Oct 2021 22:39:44 +0200 Subject: [PATCH 24/54] Update test_install_openmpi.yml --- .github/workflows/test_install_openmpi.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/test_install_openmpi.yml b/.github/workflows/test_install_openmpi.yml index 1ce440e4..b70b7dda 100644 --- a/.github/workflows/test_install_openmpi.yml +++ b/.github/workflows/test_install_openmpi.yml @@ -12,6 +12,11 @@ jobs: runs-on: ubuntu-latest steps: + - name: check path + run: | + echo $PATH + echo $GITHUB_PATH + - name: download-openmpi run: wget https://download.open-mpi.org/release/open-mpi/v4.0/openmpi-4.0.2.tar.gz - name: extract-openmpi From 867e31de6ba107edca78d63fb7ae56d4b8ee8dd6 Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Wed, 27 Oct 2021 22:45:51 +0200 Subject: [PATCH 25/54] Update test_install_openmpi.yml --- .github/workflows/test_install_openmpi.yml | 31 +++++++++++----------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test_install_openmpi.yml b/.github/workflows/test_install_openmpi.yml index b70b7dda..44190f91 100644 --- a/.github/workflows/test_install_openmpi.yml +++ b/.github/workflows/test_install_openmpi.yml @@ -16,20 +16,21 @@ jobs: run: | echo $PATH echo $GITHUB_PATH + cmake --version - - name: download-openmpi - run: wget https://download.open-mpi.org/release/open-mpi/v4.0/openmpi-4.0.2.tar.gz - - name: extract-openmpi - run: tar -xvf ./openmpi-4.0.2.tar.gz - - name: configure-openmpi - run: ./openmpi-4.0.2/configure --prefix="/home/${USER}/.openmpi" - - name: install-openmpi - run: | - make -j - sudo make install - - name: setting path - run: echo "/home/${USER}/.openmpi/bin" >> $GITHUB_PATH - - - name: checking version - run: mpirun --version + #- name: download-openmpi + # run: wget https://download.open-mpi.org/release/open-mpi/v4.0/openmpi-4.0.2.tar.gz + #- name: extract-openmpi + # run: tar -xvf ./openmpi-4.0.2.tar.gz + #- name: configure-openmpi + # run: ./openmpi-4.0.2/configure --prefix="/home/${USER}/.openmpi" + #- name: install-openmpi + # run: | + # make -j + # sudo make install + #- name: setting path + # run: echo "/home/${USER}/.openmpi/bin" >> $GITHUB_PATH + # + #- name: checking version + # run: mpirun --version From e99860f3474386b8ca9cbf1cf8fd518d03a5a8c7 Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Wed, 27 Oct 2021 23:12:24 +0200 Subject: [PATCH 26/54] Update test_install_openmpi.yml --- .github/workflows/test_install_openmpi.yml | 35 ++++++++++++---------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test_install_openmpi.yml b/.github/workflows/test_install_openmpi.yml index 44190f91..b193a6e6 100644 --- a/.github/workflows/test_install_openmpi.yml +++ b/.github/workflows/test_install_openmpi.yml @@ -18,19 +18,24 @@ jobs: echo $GITHUB_PATH cmake --version - #- name: download-openmpi - # run: wget https://download.open-mpi.org/release/open-mpi/v4.0/openmpi-4.0.2.tar.gz - #- name: extract-openmpi - # run: tar -xvf ./openmpi-4.0.2.tar.gz - #- name: configure-openmpi - # run: ./openmpi-4.0.2/configure --prefix="/home/${USER}/.openmpi" - #- name: install-openmpi - # run: | - # make -j - # sudo make install - #- name: setting path - # run: echo "/home/${USER}/.openmpi/bin" >> $GITHUB_PATH - # - #- name: checking version - # run: mpirun --version + - name: download-openmpi + run: wget https://download.open-mpi.org/release/open-mpi/v4.0/openmpi-4.0.2.tar.gz + + - name: extract-openmpi + run: tar -xvf ./openmpi-4.0.2.tar.gz + + - name: configure-openmpi + run: ./openmpi-4.0.2/configure --prefix="/home/${USER}/.openmpi" + + - name: install-openmpi + run: | + make -j + sudo make install + + - name: setting path + run: | + echo "/home/${USER}/.openmpi/bin" >> $GITHUB_PATH + #echo "/home/${USER}/.openmpi/bin" >> $PATH + - name: checking version + run: mpirun --version From ebf15d16befbf9a65cbdca1aab21a78fad8581d3 Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Tue, 2 Nov 2021 10:08:31 +0100 Subject: [PATCH 27/54] Remove ; in line 277 Get error in compilation --- IO/HDF5Writer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IO/HDF5Writer.cpp b/IO/HDF5Writer.cpp index 3fca33bd..577e59c5 100644 --- a/IO/HDF5Writer.cpp +++ b/IO/HDF5Writer.cpp @@ -274,7 +274,7 @@ std::vector writeMeshesHDF5( const std::vector writeMeshesHDF5( - const std::vector &, const std::string &, IO::FileFormat, int ); + const std::vector &, const std::string &, IO::FileFormat, int ) { return std::vector(); } From 4e28da8278c34822db4aa0443fda6e777f3bebb1 Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Tue, 2 Nov 2021 22:54:55 +0100 Subject: [PATCH 28/54] Create build and test workflow (#51) * Create build and test workflow --- .github/workflows/c-cpp.yml | 147 ++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/c-cpp.yml diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml new file mode 100644 index 00000000..c3e86225 --- /dev/null +++ b/.github/workflows/c-cpp.yml @@ -0,0 +1,147 @@ +name: LBPM CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build-and-test: + + runs-on: ubuntu-latest + env: + LBPM_ZLIB_DIR: /home/runner/extlib/zlib + LBPM_HDF5_DIR: /home/runner/extlib/hdf5 + LBPM_SILO_DIR: /home/runner/extlib/silo + MPI_DIR: /home/runner/.openmpi + + steps: + + - name: download dependencies + run: | + echo $LBPM_ZLIB_DIR + echo $LBPM_HDF5_DIR + echo $LBPM_SILO_DIR + echo $GITHUB_PATH + echo $GITHUB_WORKSPACE + + sudo apt-get update -y + #sudo apt-get install -y libtool-ltdl + #sudo apt-get install -y libtool-ltdl-devel + + + #curl "https://wci.llnl.gov/sites/wci/files/2021-01/silo-4.10.2.tgz" -o "silo-4.10.2.tar.gz" + wget https://bitbucket.org/AdvancedMultiPhysics/tpl-builder/downloads/silo-4.10.2.tar.gz + wget https://www.zlib.net/zlib-1.2.11.tar.gz + wget https://support.hdfgroup.org/ftp/HDF5/releases/hdf5-1.8/hdf5-1.8.12/src/hdf5-1.8.12.tar.gz + #wget https://support.hdfgroup.org/ftp/HDF5/releases/hdf5-1.8/hdf5-1.8.10/src/hdf5-1.8.10.tar.gz + + tar -xzvf zlib-1.2.11.tar.gz + tar -xzvf hdf5-1.8.12.tar.gz + tar -xzvf silo-4.10.2.tar.gz + + + + - name: check out commit + uses: actions/checkout@v2 + with: + path: LBPM + + + + - name: install-openmpi + run: | + wget https://download.open-mpi.org/release/open-mpi/v3.1/openmpi-3.1.2.tar.gz + tar -xvf ./openmpi-3.1.2.tar.gz + ./openmpi-3.1.2/configure --prefix="$HOME/.openmpi" + make -j + sudo make install + echo "$HOME/.openmpi/bin" >> $GITHUB_PATH + + + + - name: install zlib dependencies + run: | + cd zlib-1.2.11 + ./configure --prefix=$LBPM_ZLIB_DIR + make + sudo make install + cd .. + + + - name: install hdf5 dependencies + run: | + cd hdf5-1.8.12 + CC=/home/runner/.openmpi/bin/mpicc CXX=/home/runner/.openmpi/bin/mpicxx CXXFLAGS="-fPIC -O3 -std=c++14" \ + ./configure --prefix=$LBPM_HDF5_DIR --enable-parallel --enable-shared --with-zlib=$LBPM_ZLIB_DIR + make + sudo make install + cd .. + + + - name: install silo dependencies + run: | + cd silo-4.10.2 + CC=$MPI_DIR/bin/mpicc CXX=$MPI_DIR/bin/mpicxx CXXFLAGS="-fPIC -O3 -std=c++14" \ + ./configure --prefix=$LBPM_SILO_DIR -with-hdf5="$LBPM_HDF5_DIR/include,$LBPM_HDF5_DIR/lib" --enable-static + make + sudo make install + cd .. + + + - name: configure cmake + run: | + mkdir build + cd build + rm -rf CMake* + cmake \ + -D CMAKE_BUILD_TYPE:STRING=Release \ + -D CMAKE_C_COMPILER:PATH=$MPI_DIR/bin/mpicc \ + -D CMAKE_CXX_COMPILER:PATH=$MPI_DIR/bin/mpicxx \ + -D MPI_CXX_COMPILER=$MPI_DIR/bin/mpicxx \ + -D CMAKE_C_FLAGS="-fPIC" \ + -D CMAKE_CXX_FLAGS="-fPIC" \ + -D CMAKE_CXX_STD=14 \ + -D USE_TIMER=0 \ + -D TIMER_DIRECTORY=$LBPM_TIMER_DIR \ + -D USE_NETCDF=0 \ + -D NETCDF_DIRECTORY=$LBPM_NETCDF_DIR \ + -D USE_SILO=1 \ + -D HDF5_DIRECTORY=$LBPM_HDF5_DIR \ + -D SILO_DIRECTORY=$LBPM_SILO_DIR \ + -D USE_CUDA=0 \ + $GITHUB_WORKSPACE/LBPM + + #-DCMAKE_C_COMPILER:PATH=$MPI_DIR/bin/mpicc \ + #-DCMAKE_CXX_COMPILER:PATH=$MPI_DIR/bin/mpicxx \ + #-DCMAKE_C_FLAGS="-O3 -fPIC" \ + #-DCMAKE_CXX_FLAGS="-O3 -fPIC " \ + #-DCMAKE_CXX_STANDARD=14 \ + #-DMPIEXEC=$MPI_DIR/mpirun \ + #-DUSE_EXT_MPI_FOR_SERIAL_TESTS:BOOL=TRUE \ + #-DCMAKE_BUILD_TYPE:STRING=Release \ + #-DHDF5_DIRECTORY=$LBPM_HDF5_DIR \ + #-DHDF5_LIB=$LBPM_HDF5_DIR/lib/libhdf5.a \ + #-DUSE_SILO=1 \ + #-DSILO_LIB=$LBPM_SILO_DIR/lib/libsiloh5.a \ + #-DSILO_DIRECTORY=$LBPM_SILO_DIR \ + #-DUSE_NETCDF=0 \ + #-DUSE_CUDA=0 \ + #-DUSE_TIMER=0 \ + #$GITHUB_WORKSPACE/LBPM + cd .. + + + + - name: build and make + run: | + cd build + make + sudo make install + cd .. + + - name: tests + run: | + cd build + ctest From e3cb19f022e793b361382585154f8e709f4e559e Mon Sep 17 00:00:00 2001 From: Mark Berrill Date: Wed, 3 Nov 2021 12:11:27 -0400 Subject: [PATCH 29/54] Fixing bug compiling without HDF5 --- IO/HDF5Writer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IO/HDF5Writer.cpp b/IO/HDF5Writer.cpp index 577e59c5..ca5c5baf 100644 --- a/IO/HDF5Writer.cpp +++ b/IO/HDF5Writer.cpp @@ -274,7 +274,7 @@ std::vector writeMeshesHDF5( const std::vector writeMeshesHDF5( - const std::vector &, const std::string &, IO::FileFormat, int ) + const std::vector &, const std::string &, IO::FileFormat, int, Xdmf & ) { return std::vector(); } From b9d46f2865b95e22374df0a2566b8f5954434c67 Mon Sep 17 00:00:00 2001 From: Mark Berrill Date: Wed, 3 Nov 2021 12:27:11 -0400 Subject: [PATCH 30/54] Fixing minor bug in CMake with TEST_MAX_PROCS --- CMakeLists.txt | 1 - cmake/libraries.cmake | 2 +- tests/CMakeLists.txt | 4 +++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a55c9f1..507b10b5 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,6 @@ MESSAGE("====================") SET( PROJ LBPM ) # Set the project name for CMake SET( LBPM_LIB lbpm-wia ) # Set the final library name SET( LBPM_INC ) # Set an optional subfolder for includes (e.g. include/name/...) -SET( TEST_MAX_PROCS 16 ) # Initialize the project diff --git a/cmake/libraries.cmake b/cmake/libraries.cmake index 567385ee..1273ee70 100644 --- a/cmake/libraries.cmake +++ b/cmake/libraries.cmake @@ -263,7 +263,7 @@ ENDMACRO () # Macro to configure LBPM specific options MACRO ( CONFIGURE_LBPM ) # Set the maximum number of processors for the tests - IF ( NOT TEST_MAX_PROCS ) + IF ( NOT DEFINED TEST_MAX_PROCS ) SET( TEST_MAX_PROCS 32 ) ENDIF() # Add the correct paths to rpath in case we build shared libraries diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 188cb34b..6129d7fe 100755 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -102,8 +102,10 @@ ADD_LBPM_TEST( TestColorSquareTube ../example/Bubble/input.db) #ADD_LBPM_TEST_1_2_4( TestColorSquareTube ../example/Bubble/input.db) SET_TESTS_PROPERTIES( hello_world PROPERTIES ENVIRONMENT "MPICH_RDMA_ENABLED_CUDA=0") -IF ( USE_MPI ) +IF ( USE_MPI AND TEST_MAX_PROCS GREATER 1 ) SET_TESTS_PROPERTIES( hello_world_2procs PROPERTIES ENVIRONMENT "MPICH_RDMA_ENABLED_CUDA=0") +ENDIF() +IF ( USE_MPI AND TEST_MAX_PROCS GREATER 3 ) SET_TESTS_PROPERTIES( hello_world_4procs PROPERTIES ENVIRONMENT "MPICH_RDMA_ENABLED_CUDA=0") ENDIF() From 23491b53d94163c0c51d75aff13c00b08b082e56 Mon Sep 17 00:00:00 2001 From: Mark Berrill Date: Thu, 4 Nov 2021 16:34:18 -0400 Subject: [PATCH 31/54] Fixing failing test without Silo --- tests/TestWriter.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/TestWriter.cpp b/tests/TestWriter.cpp index 76518ac0..2b9e9698 100644 --- a/tests/TestWriter.cpp +++ b/tests/TestWriter.cpp @@ -402,10 +402,14 @@ int main( int argc, char **argv ) // Run the tests testWriter( "old", meshData, ut ); testWriter( "new", meshData, ut ); +#ifdef USE_SILO testWriter( "silo-double", meshData, ut ); testWriter( "silo-float", meshData, ut ); +#endif +#ifdef USE_HDF5 testWriter( "hdf5-double", meshData, ut ); testWriter( "hdf5-float", meshData, ut ); +#endif // Finished ut.report(); From 16440d09fc496116b8fe1d81995c56ce4deace16 Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Fri, 5 Nov 2021 15:17:15 +0100 Subject: [PATCH 32/54] Add clang script and .clang example (#52) Basic usage: clang-format-all src/ Advanced usage: clang-format-all project1/ project2/ project3/ --- .clang-format-2 | 13 ++++++++ clang-format-all | 84 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 .clang-format-2 create mode 100755 clang-format-all diff --git a/.clang-format-2 b/.clang-format-2 new file mode 100644 index 00000000..85c4e21c --- /dev/null +++ b/.clang-format-2 @@ -0,0 +1,13 @@ +# clang-format +--- +Language: Cpp +BasedOnStyle: LLVM +AccessModifierOffset: -4 +IndentWidth: 4 + +# Our includes are not order-agnostic +SortIncludes: false + +# Some of our comments include insightful insight +ReflowComments: false +... diff --git a/clang-format-all b/clang-format-all new file mode 100755 index 00000000..f2744b0c --- /dev/null +++ b/clang-format-all @@ -0,0 +1,84 @@ +#!/bin/bash +# +# clang-format-all: a tool to run clang-format on an entire project +# Copyright (C) 2016 Evan Klitzke +# +# 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 . + +function usage { + echo "Usage: $0 DIR..." + exit 1 +} + +if [ $# -eq 0 ]; then + usage +fi + +# Variable that will hold the name of the clang-format command +FMT="" + +# Some distros just call it clang-format. Others (e.g. Ubuntu) are insistent +# that the version number be part of the command. We prefer clang-format if +# that's present, otherwise we work backwards from highest version to lowest +# version. +for clangfmt in clang-format{,-{4,3}.{9,8,7,6,5,4,3,2,1,0}}; do + if which "$clangfmt" &>/dev/null; then + FMT="$clangfmt" + break + fi +done + +# Check if we found a working clang-format +if [ -z "$FMT" ]; then + echo "failed to find clang-format" + exit 1 +fi + +# Check all of the arguments first to make sure they're all directories +for dir in "$@"; do + if [ ! -d "${dir}" ]; then + echo "${dir} is not a directory" + usage + fi +done + +# Find a dominating file, starting from a given directory and going up. +find-dominating-file() { + if [ -r "$1"/"$2" ]; then + return 0 + fi + if [ "$1" = "/" ]; then + return 1 + fi + find-dominating-file "$(realpath "$1"/..)" "$2" + return $? +} + +# Run clang-format -i on all of the things +for dir in "$@"; do + pushd "${dir}" &>/dev/null + if ! find-dominating-file . .clang-format; then + echo "Failed to find dominating .clang-format starting at $PWD" + continue + fi + find . \ + \( -name '*.c' \ + -o -name '*.cc' \ + -o -name '*.cpp' \ + -o -name '*.h' \ + -o -name '*.hh' \ + -o -name '*.hpp' \) \ + -exec "${FMT}" -i '{}' \; + popd &>/dev/null +done \ No newline at end of file From d926202d7fcf243c86a6f7de8d809860ff69622e Mon Sep 17 00:00:00 2001 From: James McClure Date: Sat, 6 Nov 2021 08:06:08 -0400 Subject: [PATCH 33/54] update I/O format options to include hdf5 --- analysis/runAnalysis.cpp | 13 ++- analysis/runAnalysis.h | 2 + models/ColorModel.cpp | 213 --------------------------------------- models/MRTModel.cpp | 48 +-------- models/MRTModel.h | 1 + 5 files changed, 17 insertions(+), 260 deletions(-) diff --git a/analysis/runAnalysis.cpp b/analysis/runAnalysis.cpp index 4c82b377..c7bad7f3 100644 --- a/analysis/runAnalysis.cpp +++ b/analysis/runAnalysis.cpp @@ -607,6 +607,9 @@ runAnalysis::runAnalysis( std::shared_ptr input_db, const RankInfoStru auto db = input_db->getDatabase( "Analysis" ); auto vis_db = input_db->getDatabase( "Visualization" ); + + /* set the I/O format */ + format = vis_db->getWithDefault( "format", "silo" ); // Ids of work items to use for dependencies ThreadPool::thread_id_t d_wait_blobID; @@ -645,8 +648,10 @@ runAnalysis::runAnalysis( std::shared_ptr input_db, const RankInfoStru d_rank = d_comm.getRank(); writeIDMap( ID_map_struct(), 0, id_map_filename ); + // Initialize IO for silo - IO::initialize( "", "silo", "false" ); + //std::string format = "silo"; + IO::initialize( "", format, "false" ); // Create the MeshDataStruct d_meshData.resize( 1 ); @@ -786,7 +791,11 @@ runAnalysis::runAnalysis( ScaLBL_ColorModel &ColorModel) d_rank = d_comm.getRank(); writeIDMap( ID_map_struct(), 0, id_map_filename ); // Initialize IO for silo - IO::initialize( "", "silo", "false" ); + //std::string format = "silo"; + + format = vis_db->getWithDefault( "format", "silo" ); + + IO::initialize( "", format, "false" ); // Create the MeshDataStruct d_meshData.resize( 1 ); diff --git a/analysis/runAnalysis.h b/analysis/runAnalysis.h index c7c4ce71..9600a297 100644 --- a/analysis/runAnalysis.h +++ b/analysis/runAnalysis.h @@ -100,6 +100,8 @@ private: int d_subphase_analysis_interval; double d_beta; bool d_regular; + std::string format; // IO format string "silo" or "hdf5" + ThreadPool d_tpool; RankInfoStruct d_rank_info; IntArray d_Map; diff --git a/models/ColorModel.cpp b/models/ColorModel.cpp index b94b4b1f..8f924aa2 100644 --- a/models/ColorModel.cpp +++ b/models/ColorModel.cpp @@ -754,7 +754,6 @@ double ScaLBL_ColorModel::Run(int returntime){ timestep = INITIAL_TIMESTEP; TRIGGER_FORCE_RESCALE = true; if (rank == 0) printf(" Capillary number missed target value = %f (measured value was Ca = %f) \n ",capillary_number, Ca); - } if (RESCALE_FORCE == true && SET_CAPILLARY_NUMBER == true && CURRENT_TIMESTEP > RESCALE_FORCE_AFTER_TIMESTEP){ @@ -925,198 +924,7 @@ void ScaLBL_ColorModel::Run(){ if (analysis_db->keyExists( "analysis_interval" )){ analysis_interval = analysis_db->getScalar( "analysis_interval" ); } - /* - int IMAGE_INDEX = 0; - int IMAGE_COUNT = 0; - std::vector ImageList; - bool SET_CAPILLARY_NUMBER = false; - bool RESCALE_FORCE = false; - bool MORPH_ADAPT = false; - bool USE_MORPH = false; - bool USE_SEED = false; - bool USE_DIRECT = false; - bool USE_MORPHOPEN_OIL = false; - int MAX_MORPH_TIMESTEPS = 50000; // maximum number of LBM timesteps to spend in morphological adaptation routine - int MIN_STEADY_TIMESTEPS = 100000; - int MAX_STEADY_TIMESTEPS = 200000; - int RESCALE_FORCE_AFTER_TIMESTEP = 0; - int RAMP_TIMESTEPS = 0;//50000; // number of timesteps to run initially (to get a reasonable velocity field before other pieces kick in) - int CURRENT_MORPH_TIMESTEPS=0; // counter for number of timesteps spent in morphological adaptation routine (reset each time) - int CURRENT_STEADY_TIMESTEPS=0; // counter for number of timesteps spent in morphological adaptation routine (reset each time) - int morph_interval = 100000; - int morph_timesteps = 0; - double morph_delta = 0.0; - double seed_water = 0.0; - double capillary_number = 0.0; - double tolerance = 0.01; - double Ca_previous = 0.f; - double initial_volume = 0.0; - double delta_volume = 0.0; - double delta_volume_target = 0.0; - - double KRA_MORPH_FACTOR=0.5; - double volA_prev = 0.0; - double log_krA_prev = 1.0; - double log_krA_target = 1.0; - double log_krA = 1.0; - double slope_krA_volume = 0.0; - if (color_db->keyExists( "vol_A_previous" )){ - volA_prev = color_db->getScalar( "vol_A_previous" ); - } - if (color_db->keyExists( "log_krA_previous" )){ - log_krA_prev = color_db->getScalar( "log_krA_previous" ); - } - if (color_db->keyExists( "krA_morph_factor" )){ - KRA_MORPH_FACTOR = color_db->getScalar( "krA_morph_factor" ); - } - if (color_db->keyExists( "capillary_number" )){ - capillary_number = color_db->getScalar( "capillary_number" ); - SET_CAPILLARY_NUMBER=true; - } - if (color_db->keyExists( "rescale_force_after_timestep" )){ - RESCALE_FORCE_AFTER_TIMESTEP = color_db->getScalar( "rescale_force_after_timestep" ); - RESCALE_FORCE = true; - } - if (color_db->keyExists( "timestep" )){ - timestep = color_db->getScalar( "timestep" ); - } - if (BoundaryCondition != 0 && BoundaryCondition != 5 && SET_CAPILLARY_NUMBER==true){ - if (rank == 0) printf("WARINING: capillary number target only supported for BC = 0 or 5 \n"); - SET_CAPILLARY_NUMBER=false; - } - if (analysis_db->keyExists( "seed_water" )){ - seed_water = analysis_db->getScalar( "seed_water" ); - if (rank == 0) printf("Seed water in oil %f (seed_water) \n",seed_water); - } - if (analysis_db->keyExists( "morph_delta" )){ - morph_delta = analysis_db->getScalar( "morph_delta" ); - if (rank == 0) printf("Target volume change %f (morph_delta) \n",morph_delta); - } - if (analysis_db->keyExists( "morph_interval" )){ - morph_interval = analysis_db->getScalar( "morph_interval" ); - USE_MORPH = true; - } - if (analysis_db->keyExists( "use_morphopen_oil" )){ - USE_MORPHOPEN_OIL = analysis_db->getScalar( "use_morphopen_oil" ); - if (rank == 0 && USE_MORPHOPEN_OIL) printf("Volume change by morphological opening \n"); - USE_MORPH = true; - } - if (analysis_db->keyExists( "tolerance" )){ - tolerance = analysis_db->getScalar( "tolerance" ); - } - - if (analysis_db->keyExists( "min_steady_timesteps" )){ - MIN_STEADY_TIMESTEPS = analysis_db->getScalar( "min_steady_timesteps" ); - } - if (analysis_db->keyExists( "max_steady_timesteps" )){ - MAX_STEADY_TIMESTEPS = analysis_db->getScalar( "max_steady_timesteps" ); - } - if (analysis_db->keyExists( "max_morph_timesteps" )){ - MAX_MORPH_TIMESTEPS = analysis_db->getScalar( "max_morph_timesteps" ); - } - - auto protocol = color_db->getWithDefault( "protocol", "none" ); - if (protocol == "image sequence"){ - // Get the list of images - USE_DIRECT = true; - ImageList = color_db->getVector( "image_sequence"); - IMAGE_INDEX = color_db->getWithDefault( "image_index", 0 ); - IMAGE_COUNT = ImageList.size(); - morph_interval = 10000; - USE_MORPH = true; - USE_SEED = false; - } - else if (protocol == "seed water"){ - morph_delta = -0.05; - seed_water = 0.01; - USE_SEED = true; - USE_MORPH = true; - } - else if (protocol == "open connected oil"){ - morph_delta = -0.05; - USE_SEED = false; - USE_MORPH = true; - USE_MORPHOPEN_OIL = true; - } - else if (protocol == "shell aggregation"){ - morph_delta = -0.05; - USE_MORPH = true; - USE_SEED = false; - } - else if (protocol == "fractional flow"){ - USE_MORPH = false; - USE_SEED = false; - } - else if (protocol == "centrifuge"){ - USE_MORPH = false; - USE_SEED = false; - } - else if (protocol == "core flooding"){ - USE_MORPH = false; - USE_SEED = false; - if (SET_CAPILLARY_NUMBER){ - double MuB = rhoB*(tauB - 0.5)/3.0; - double IFT = 6.0*alpha; - double CrossSectionalArea = (double) (nprocx*(Nx-2)*nprocy*(Ny-2)); - flux = Dm->Porosity()*CrossSectionalArea*IFT*capillary_number/MuB; - } - } if (rank==0){ - printf("********************************************************\n"); - if (protocol == "image sequence"){ - printf(" using protocol = image sequence \n"); - printf(" min_steady_timesteps = %i \n",MIN_STEADY_TIMESTEPS); - printf(" max_steady_timesteps = %i \n",MAX_STEADY_TIMESTEPS); - printf(" tolerance = %f \n",tolerance); - std::string first_image = ImageList[IMAGE_INDEX]; - printf(" first image in sequence: %s ***\n", first_image.c_str()); - } - else if (protocol == "seed water"){ - printf(" using protocol = seed water \n"); - printf(" min_steady_timesteps = %i \n",MIN_STEADY_TIMESTEPS); - printf(" max_steady_timesteps = %i \n",MAX_STEADY_TIMESTEPS); - printf(" tolerance = %f \n",tolerance); - printf(" morph_delta = %f \n",morph_delta); - printf(" seed_water = %f \n",seed_water); - } - else if (protocol == "open connected oil"){ - printf(" using protocol = open connected oil \n"); - printf(" min_steady_timesteps = %i \n",MIN_STEADY_TIMESTEPS); - printf(" max_steady_timesteps = %i \n",MAX_STEADY_TIMESTEPS); - printf(" tolerance = %f \n",tolerance); - printf(" morph_delta = %f \n",morph_delta); - } - else if (protocol == "shell aggregation"){ - printf(" using protocol = shell aggregation \n"); - printf(" min_steady_timesteps = %i \n",MIN_STEADY_TIMESTEPS); - printf(" max_steady_timesteps = %i \n",MAX_STEADY_TIMESTEPS); - printf(" tolerance = %f \n",tolerance); - printf(" morph_delta = %f \n",morph_delta); - } - else if (protocol == "fractional flow"){ - printf(" using protocol = fractional flow \n"); - printf(" min_steady_timesteps = %i \n",MIN_STEADY_TIMESTEPS); - printf(" max_steady_timesteps = %i \n",MAX_STEADY_TIMESTEPS); - printf(" tolerance = %f \n",tolerance); - } - else if (protocol == "centrifuge"){ - printf(" using protocol = centrifuge \n"); - printf(" driving force = %f \n",Fz); - if (Fz < 0){ - printf(" Component B displacing component A \n"); - } - else if (Fz > 0){ - printf(" Component A displacing component B \n"); - } - } - else if (protocol == "core flooding"){ - printf(" using protocol = core flooding \n"); - printf(" capillary number = %f \n", capillary_number); - } - printf("No. of timesteps: %i \n", timestepMax); - fflush(stdout); - } - */ //************ MAIN ITERATION LOOP ***************************************/ comm.barrier(); PROFILE_START("Loop"); @@ -1293,26 +1101,5 @@ void ScaLBL_ColorModel::WriteDebug(){ fwrite(PhaseField.data(),8,N,VELZ_FILE); fclose(VELZ_FILE); -/* ScaLBL_Comm->RegularLayout(Map,&ColorGrad[0],PhaseField); - FILE *CGX_FILE; - sprintf(LocalRankFilename,"Gradient_X.%05i.raw",rank); - CGX_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,CGX_FILE); - fclose(CGX_FILE); - - ScaLBL_Comm->RegularLayout(Map,&ColorGrad[Np],PhaseField); - FILE *CGY_FILE; - sprintf(LocalRankFilename,"Gradient_Y.%05i.raw",rank); - CGY_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,CGY_FILE); - fclose(CGY_FILE); - - ScaLBL_Comm->RegularLayout(Map,&ColorGrad[2*Np],PhaseField); - FILE *CGZ_FILE; - sprintf(LocalRankFilename,"Gradient_Z.%05i.raw",rank); - CGZ_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,CGZ_FILE); - fclose(CGZ_FILE); -*/ } diff --git a/models/MRTModel.cpp b/models/MRTModel.cpp index e1a451e2..2cbcd224 100644 --- a/models/MRTModel.cpp +++ b/models/MRTModel.cpp @@ -20,6 +20,7 @@ void ScaLBL_MRTModel::ReadParams(string filename){ db = std::make_shared( filename ); domain_db = db->getDatabase( "Domain" ); mrt_db = db->getDatabase( "MRT" ); + vis_db = db->getDatabase( "Visualization" ); tau = 1.0; timestepMax = 100000; @@ -371,51 +372,8 @@ void ScaLBL_MRTModel::Run(){ void ScaLBL_MRTModel::VelocityField(){ -/* Minkowski Morphology(Mask); - int SIZE=Np*sizeof(double); - ScaLBL_D3Q19_Momentum(fq,Velocity, Np); - ScaLBL_DeviceBarrier(); comm.barrier(); - ScaLBL_CopyToHost(&VELOCITY[0],&Velocity[0],3*SIZE); + auto format = vis_db->getWithDefault( "format", "silo" ); - memcpy(Morphology.SDn.data(), Distance.data(), Nx*Ny*Nz*sizeof(double)); - Morphology.Initialize(); - Morphology.UpdateMeshValues(); - Morphology.ComputeLocal(); - Morphology.Reduce(); - - double count_loc=0; - double count; - double vax,vay,vaz; - double vax_loc,vay_loc,vaz_loc; - vax_loc = vay_loc = vaz_loc = 0.f; - for (int n=0; nLastExterior(); n++){ - vax_loc += VELOCITY[n]; - vay_loc += VELOCITY[Np+n]; - vaz_loc += VELOCITY[2*Np+n]; - count_loc+=1.0; - } - - for (int n=ScaLBL_Comm->FirstInterior(); nLastInterior(); n++){ - vax_loc += VELOCITY[n]; - vay_loc += VELOCITY[Np+n]; - vaz_loc += VELOCITY[2*Np+n]; - count_loc+=1.0; - } - MPI_Allreduce(&vax_loc,&vax,1,MPI_DOUBLE,MPI_SUM,Mask->Comm); - MPI_Allreduce(&vay_loc,&vay,1,MPI_DOUBLE,MPI_SUM,Mask->Comm); - MPI_Allreduce(&vaz_loc,&vaz,1,MPI_DOUBLE,MPI_SUM,Mask->Comm); - MPI_Allreduce(&count_loc,&count,1,MPI_DOUBLE,MPI_SUM,Mask->Comm); - - vax /= count; - vay /= count; - vaz /= count; - - double mu = (tau-0.5)/3.f; - if (rank==0) printf("Fx Fy Fz mu Vs As Js Xs vx vy vz\n"); - if (rank==0) printf("%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g\n",Fx, Fy, Fz, mu, - Morphology.V(),Morphology.A(),Morphology.J(),Morphology.X(),vax,vay,vaz); - */ - std::vector visData; fillHalo fillData(Dm->Comm,Dm->rank_info,{Dm->Nx-2,Dm->Ny-2,Dm->Nz-2},{1,1,1},0,1); @@ -424,7 +382,7 @@ void ScaLBL_MRTModel::VelocityField(){ auto VzVar = std::make_shared(); auto SignDistVar = std::make_shared(); - IO::initialize("","silo","false"); + IO::initialize("",format,"false"); // Create the MeshDataStruct visData.resize(1); visData[0].meshName = "domain"; diff --git a/models/MRTModel.h b/models/MRTModel.h index 4c41b746..f44ccf8a 100644 --- a/models/MRTModel.h +++ b/models/MRTModel.h @@ -49,6 +49,7 @@ public: std::shared_ptr db; std::shared_ptr domain_db; std::shared_ptr mrt_db; + std::shared_ptr vis_db; IntArray Map; DoubleArray Distance; From 11a62b4a0bdf51fa036d80381f1f87b95ba1a4de Mon Sep 17 00:00:00 2001 From: James McClure Date: Sat, 6 Nov 2021 09:49:21 -0400 Subject: [PATCH 34/54] merge writer MRT --- models/MRTModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/MRTModel.cpp b/models/MRTModel.cpp index 0d9c5cc4..19105ce6 100644 --- a/models/MRTModel.cpp +++ b/models/MRTModel.cpp @@ -390,7 +390,7 @@ void ScaLBL_MRTModel::VelocityField(){ auto format = vis_db->getWithDefault( "format", "silo" ); - memcpy(Morphology.SDn.data(), Distance.data(), Nx*Ny*Nz*sizeof(double)); + /* memcpy(Morphology.SDn.data(), Distance.data(), Nx*Ny*Nz*sizeof(double)); Morphology.Initialize(); Morphology.UpdateMeshValues(); Morphology.ComputeLocal(); From 8e29cc4a17b4e5c89e09a5eb96d44db4a2f46bb5 Mon Sep 17 00:00:00 2001 From: James McClure Date: Sun, 7 Nov 2021 07:09:09 -0500 Subject: [PATCH 35/54] fix bug in writer mode --- analysis/runAnalysis.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/analysis/runAnalysis.cpp b/analysis/runAnalysis.cpp index c7bad7f3..284d721f 100644 --- a/analysis/runAnalysis.cpp +++ b/analysis/runAnalysis.cpp @@ -609,7 +609,7 @@ runAnalysis::runAnalysis( std::shared_ptr input_db, const RankInfoStru auto vis_db = input_db->getDatabase( "Visualization" ); /* set the I/O format */ - format = vis_db->getWithDefault( "format", "silo" ); + format = vis_db->getWithDefault( "format", "silo" ); // Ids of work items to use for dependencies ThreadPool::thread_id_t d_wait_blobID; @@ -651,6 +651,8 @@ runAnalysis::runAnalysis( std::shared_ptr input_db, const RankInfoStru // Initialize IO for silo //std::string format = "silo"; + format = vis_db->getWithDefault( "format", "silo" ); + IO::initialize( "", format, "false" ); // Create the MeshDataStruct d_meshData.resize( 1 ); @@ -793,7 +795,7 @@ runAnalysis::runAnalysis( ScaLBL_ColorModel &ColorModel) // Initialize IO for silo //std::string format = "silo"; - format = vis_db->getWithDefault( "format", "silo" ); + format = vis_db->getWithDefault( "format", "silo" ); IO::initialize( "", format, "false" ); // Create the MeshDataStruct From ce57491bce577fdf2ff8c7c27c5cb0000b266842 Mon Sep 17 00:00:00 2001 From: James McClure Date: Sun, 7 Nov 2021 07:55:15 -0500 Subject: [PATCH 36/54] never rescale unsteady cases --- models/ColorModel.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/models/ColorModel.cpp b/models/ColorModel.cpp index 8f924aa2..7b5024f3 100644 --- a/models/ColorModel.cpp +++ b/models/ColorModel.cpp @@ -745,6 +745,8 @@ double ScaLBL_ColorModel::Run(int returntime){ if (CURRENT_TIMESTEP >= MAX_STEADY_TIMESTEPS) isSteady = true; + if (BC != 0 ) isSteady = false; // Never rescale unsteady simulation cases + if (isSteady && (Ca > maxCa || Ca < minCa) && SET_CAPILLARY_NUMBER ){ /* re-run the point if the actual Ca is too far from the target Ca */ isSteady = false; From 1fe39b82231670e840e1250cda404175b28849fa Mon Sep 17 00:00:00 2001 From: James McClure Date: Sun, 7 Nov 2021 07:56:18 -0500 Subject: [PATCH 37/54] never rescale unsteady cases --- models/ColorModel.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/models/ColorModel.cpp b/models/ColorModel.cpp index 7b5024f3..df27a8f2 100644 --- a/models/ColorModel.cpp +++ b/models/ColorModel.cpp @@ -707,7 +707,7 @@ double ScaLBL_ColorModel::Run(int returntime){ analysis.basic(timestep, current_db, *Averages, Phi, Pressure, Velocity, fq, Den ); // allow initial ramp-up to get closer to steady state CURRENT_TIMESTEP += 2; - if (CURRENT_TIMESTEP > MIN_STEADY_TIMESTEPS){ + if (CURRENT_TIMESTEP > MIN_STEADY_TIMESTEPS && BC == 0){ analysis.finish(); double volB = Averages->gwb.V; @@ -744,9 +744,7 @@ double ScaLBL_ColorModel::Run(int returntime){ isSteady = true; if (CURRENT_TIMESTEP >= MAX_STEADY_TIMESTEPS) isSteady = true; - - if (BC != 0 ) isSteady = false; // Never rescale unsteady simulation cases - + if (isSteady && (Ca > maxCa || Ca < minCa) && SET_CAPILLARY_NUMBER ){ /* re-run the point if the actual Ca is too far from the target Ca */ isSteady = false; From 97721a9a100673388c8fba37332470014f675a62 Mon Sep 17 00:00:00 2001 From: James McClure Date: Sun, 7 Nov 2021 07:57:54 -0500 Subject: [PATCH 38/54] never rescale unsteady cases --- models/ColorModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/ColorModel.cpp b/models/ColorModel.cpp index df27a8f2..c7caa292 100644 --- a/models/ColorModel.cpp +++ b/models/ColorModel.cpp @@ -707,7 +707,7 @@ double ScaLBL_ColorModel::Run(int returntime){ analysis.basic(timestep, current_db, *Averages, Phi, Pressure, Velocity, fq, Den ); // allow initial ramp-up to get closer to steady state CURRENT_TIMESTEP += 2; - if (CURRENT_TIMESTEP > MIN_STEADY_TIMESTEPS && BC == 0){ + if (CURRENT_TIMESTEP > MIN_STEADY_TIMESTEPS && BoundaryCondition == 0){ analysis.finish(); double volB = Averages->gwb.V; From f29ae0b0bc8be050162fe9732d3dcb191c1fb370 Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Sun, 7 Nov 2021 20:38:36 +0100 Subject: [PATCH 39/54] Update c-cpp.yml (#54) --- .github/workflows/c-cpp.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index c3e86225..6ab68898 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -103,6 +103,7 @@ jobs: -D CMAKE_C_FLAGS="-fPIC" \ -D CMAKE_CXX_FLAGS="-fPIC" \ -D CMAKE_CXX_STD=14 \ + -D TEST_MAX_PROCS=1 \ -D USE_TIMER=0 \ -D TIMER_DIRECTORY=$LBPM_TIMER_DIR \ -D USE_NETCDF=0 \ From 3d251e1f4edcd9cbb426abc39498c226297e0fe5 Mon Sep 17 00:00:00 2001 From: James McClure Date: Mon, 8 Nov 2021 14:28:05 -0500 Subject: [PATCH 40/54] add I/O format string for electrochem --- analysis/ElectroChemistry.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/analysis/ElectroChemistry.cpp b/analysis/ElectroChemistry.cpp index 9eb56315..c4ebe840 100644 --- a/analysis/ElectroChemistry.cpp +++ b/analysis/ElectroChemistry.cpp @@ -162,10 +162,12 @@ void ElectroChemistryAnalyzer::WriteVis( ScaLBL_IonModel &Ion, ScaLBL_Poisson &P auto vis_db = input_db->getDatabase( "Visualization" ); char VisName[40]; + auto format = vis_db->getWithDefault( "format", "hdf5" ); + std::vector visData; fillHalo fillData(Dm->Comm,Dm->rank_info,{Dm->Nx-2,Dm->Ny-2,Dm->Nz-2},{1,1,1},0,1); - IO::initialize("","silo","false"); + IO::initialize("",format,"false"); // Create the MeshDataStruct visData.resize(1); From 23189f55777623a43760bcd1a249b802ffa6c71a Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Mon, 8 Nov 2021 22:58:37 +0100 Subject: [PATCH 41/54] Clang format (#55) Run clang-format on modules of code --- .clang-format | 115 +- analysis/ElectroChemistry.cpp | 783 ++- analysis/ElectroChemistry.h | 58 +- analysis/FlowAdaptor.cpp | 1068 +-- analysis/FlowAdaptor.h | 45 +- analysis/FreeEnergy.cpp | 234 +- analysis/FreeEnergy.h | 50 +- analysis/GreyPhase.cpp | 390 +- analysis/GreyPhase.h | 87 +- analysis/Minkowski.cpp | 442 +- analysis/Minkowski.h | 99 +- analysis/PointList.h | 162 +- analysis/SubPhase.cpp | 1711 ++--- analysis/SubPhase.h | 151 +- analysis/TwoPhase.cpp | 2568 +++---- analysis/TwoPhase.h | 307 +- analysis/analysis.cpp | 940 +-- analysis/analysis.h | 80 +- analysis/dcel.cpp | 822 +-- analysis/dcel.h | 94 +- analysis/distance.cpp | 200 +- analysis/distance.h | 27 +- analysis/filters.cpp | 304 +- analysis/filters.h | 11 +- analysis/histogram.h | 78 +- analysis/imfilter.h | 60 +- analysis/imfilter.hpp | 441 +- analysis/morphology.cpp | 1732 ++--- analysis/morphology.h | 108 +- analysis/pmmc.h | 7589 +++++++++++---------- analysis/runAnalysis.cpp | 1252 ++-- analysis/runAnalysis.h | 70 +- analysis/uCT.cpp | 499 +- analysis/uCT.h | 46 +- common/Array.h | 449 +- common/Array.hpp | 1544 +++-- common/ArraySize.h | 316 +- common/Communication.cpp | 120 +- common/Communication.h | 546 +- common/Communication.hpp | 423 +- common/Database.cpp | 671 +- common/Database.h | 139 +- common/Database.hpp | 160 +- common/Domain.cpp | 2689 ++++---- common/Domain.h | 174 +- common/FunctionTable.cpp | 54 +- common/FunctionTable.h | 121 +- common/FunctionTable.hpp | 388 +- common/MPI.cpp | 4058 ++++++------ common/MPI.h | 359 +- common/ReadMicroCT.cpp | 128 +- common/ReadMicroCT.h | 7 +- common/ScaLBL.cpp | 4947 ++++++++------ common/ScaLBL.h | 888 ++- common/SpherePack.cpp | 484 +- common/SpherePack.h | 19 +- common/UnitTest.cpp | 326 +- common/UnitTest.h | 33 +- common/Units.cpp | 168 +- common/Units.h | 85 +- common/Utilities.cpp | 139 +- common/Utilities.h | 37 +- common/Utilities.hpp | 217 +- common/UtilityMacros.h | 95 +- common/WideHalo.cpp | 859 ++- common/WideHalo.h | 220 +- cpu/BGK.cpp | 533 +- cpu/Color.cpp | 5554 ++++++++-------- cpu/D3Q19.cpp | 3530 +++++----- cpu/D3Q7.cpp | 368 +- cpu/D3Q7BC.cpp | 1196 ++-- cpu/Extras.cpp | 69 +- cpu/FreeLee.cpp | 9211 ++++++++++++++++---------- cpu/Greyscale.cpp | 5093 +++++++------- cpu/GreyscaleColor.cpp | 5322 +++++++-------- cpu/Ion.cpp | 482 +- cpu/MRT.cpp | 561 +- cpu/MixedGradient.cpp | 93 +- cpu/Poisson.cpp | 506 +- cpu/Stokes.cpp | 1741 ++--- cpu/dfh.cpp | 2573 +++---- cpu/exe/lb2_Color_mpi.cpp | 3177 +++++---- cpu/exe/lb2_Color_wia_mpi_bubble.cpp | 5310 ++++++++------- cpu/thermal.cpp | 3 - models/ColorModel.cpp | 2178 +++--- models/ColorModel.h | 116 +- models/DFHModel.cpp | 1051 +-- models/DFHModel.h | 74 +- models/FreeLeeModel.cpp | 2259 ++++--- models/FreeLeeModel.h | 121 +- models/GreyscaleColorModel.cpp | 2661 ++++---- models/GreyscaleColorModel.h | 121 +- models/GreyscaleModel.cpp | 1608 ++--- models/GreyscaleModel.h | 105 +- models/IonModel.cpp | 2096 +++--- models/IonModel.h | 105 +- models/MRTModel.cpp | 819 +-- models/MRTModel.h | 69 +- models/MultiPhysController.cpp | 176 +- models/MultiPhysController.h | 37 +- models/PoissonSolver.cpp | 1496 +++-- models/PoissonSolver.h | 97 +- models/StokesModel.cpp | 1859 +++--- models/StokesModel.h | 86 +- 104 files changed, 56746 insertions(+), 49196 deletions(-) diff --git a/.clang-format b/.clang-format index b930c24c..85c4e21c 100644 --- a/.clang-format +++ b/.clang-format @@ -1,108 +1,13 @@ -# To run clang tools: -# cd to root directory -# To update format only: -# find . -name "*.cpp" -or -name "*.cc" -or -name "*.h" -or -name "*.hpp" -or -name "*.I" | xargs -I{} clang-format -i {} -# git status -s . | sed s/^...// | grep -E "(\.cpp|\.h|\.cc|\.hpp|\.I)" | xargs -I{} clang-format -i {} - -# To run modernize -# export CLANG_PATH=/packages/llvm/build/llvm-60 -# export PATH=${CLANG_PATH}/bin:${CLANG_PATH}/share/clang:$PATH -# find src -name "*.cpp" -or -name "*.cc" | xargs -I{} clang-tidy -checks=modernize* -p=/projects/AtomicModel/build/debug -fix {} -# find src -name "*.cpp" -or -name "*.cc" -or -name "*.h" -or -name "*.hpp" -or -name "*.I" | xargs -I{} clang-format -i {} - - - - - +# clang-format --- -Language: Cpp -# BasedOnStyle: LLVM +Language: Cpp +BasedOnStyle: LLVM AccessModifierOffset: -4 -AlignAfterOpenBracket: DontAlign -AlignConsecutiveAssignments: true -AlignConsecutiveDeclarations: false -AlignEscapedNewlinesLeft: true -AlignOperands: true -AlignTrailingComments: true -AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: true -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: All -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: true -BinPackArguments: true -BinPackParameters: true -BraceWrapping: - AfterClass: true - AfterControlStatement: false - AfterEnum: false - AfterFunction: true - AfterNamespace: false - AfterObjCDeclaration: true - AfterStruct: false - AfterUnion: false - BeforeCatch: false - BeforeElse: false - IndentBraces: false -BreakBeforeBinaryOperators: None -#BreakBeforeBraces: Stroustrup -BreakBeforeBraces: Custom -BreakBeforeTernaryOperators: false -BreakConstructorInitializersBeforeComma: false -ColumnLimit: 100 -CommentPragmas: '^ IWYU pragma:' -ConstructorInitializerAllOnOneLineOrOnePerLine: true -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: false -DerivePointerAlignment: false -DisableFormat: false -ExperimentalAutoDetectBinPacking: false -ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] -IncludeCategories: - - Regex: '^"(llvm|llvm-c|clang|clang-c)/' - Priority: 2 - - Regex: '^(<|"(gtest|isl|json)/)' - Priority: 3 - - Regex: '.*' - Priority: 1 -IndentCaseLabels: false -IndentWidth: 4 -IndentWrappedFunctionNames: false -KeepEmptyLinesAtTheStartOfBlocks: true -MacroBlockBegin: '' -MacroBlockEnd: '' -MaxEmptyLinesToKeep: 2 -NamespaceIndentation: None -ObjCBlockIndentWidth: 4 -ObjCSpaceAfterProperty: false -ObjCSpaceBeforeProtocolList: true -PenaltyBreakBeforeFirstCallParameter: 19 -PenaltyBreakComment: 300 -PenaltyBreakFirstLessLess: 120 -PenaltyBreakString: 1000 -PenaltyExcessCharacter: 1000 -PenaltyReturnTypeOnItsOwnLine: 60 -PointerAlignment: Right -ReflowComments: true -SortIncludes: true -SortUsingDeclarations: true -SpaceAfterCStyleCast: true -SpaceAfterTemplateKeyword: false -SpaceBeforeAssignmentOperators: true -SpaceBeforeParens: ControlStatements -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -SpacesInAngles: false -SpacesInContainerLiterals: true -SpacesInCStyleCastParentheses: false -SpacesInParentheses: true -SpacesInSquareBrackets: false -Standard: Cpp11 -TabWidth: 4 -UseTab: Never -... +IndentWidth: 4 +# Our includes are not order-agnostic +SortIncludes: false + +# Some of our comments include insightful insight +ReflowComments: false +... diff --git a/analysis/ElectroChemistry.cpp b/analysis/ElectroChemistry.cpp index 9eb56315..2ddd80d6 100644 --- a/analysis/ElectroChemistry.cpp +++ b/analysis/ElectroChemistry.cpp @@ -1,150 +1,177 @@ #include "analysis/ElectroChemistry.h" +ElectroChemistryAnalyzer::ElectroChemistryAnalyzer(std::shared_ptr dm) + : Dm(dm) { -ElectroChemistryAnalyzer::ElectroChemistryAnalyzer(std::shared_ptr dm): - Dm(dm) -{ - - Nx=dm->Nx; Ny=dm->Ny; Nz=dm->Nz; - Volume=(Nx-2)*(Ny-2)*(Nz-2)*Dm->nprocx()*Dm->nprocy()*Dm->nprocz()*1.0; - - ChemicalPotential.resize(Nx,Ny,Nz); ChemicalPotential.fill(0); - ElectricalPotential.resize(Nx,Ny,Nz); ElectricalPotential.fill(0); - ElectricalField_x.resize(Nx,Ny,Nz); ElectricalField_x.fill(0); - ElectricalField_y.resize(Nx,Ny,Nz); ElectricalField_y.fill(0); - ElectricalField_z.resize(Nx,Ny,Nz); ElectricalField_z.fill(0); - Pressure.resize(Nx,Ny,Nz); Pressure.fill(0); - Rho.resize(Nx,Ny,Nz); Rho.fill(0); - Vel_x.resize(Nx,Ny,Nz); Vel_x.fill(0); // Gradient of the phase indicator field - Vel_y.resize(Nx,Ny,Nz); Vel_y.fill(0); - Vel_z.resize(Nx,Ny,Nz); Vel_z.fill(0); - SDs.resize(Nx,Ny,Nz); SDs.fill(0); - IonFluxDiffusive_x.resize(Nx,Ny,Nz); IonFluxDiffusive_x.fill(0); - IonFluxDiffusive_y.resize(Nx,Ny,Nz); IonFluxDiffusive_y.fill(0); - IonFluxDiffusive_z.resize(Nx,Ny,Nz); IonFluxDiffusive_z.fill(0); - IonFluxAdvective_x.resize(Nx,Ny,Nz); IonFluxAdvective_x.fill(0); - IonFluxAdvective_y.resize(Nx,Ny,Nz); IonFluxAdvective_y.fill(0); - IonFluxAdvective_z.resize(Nx,Ny,Nz); IonFluxAdvective_z.fill(0); - IonFluxElectrical_x.resize(Nx,Ny,Nz); IonFluxElectrical_x.fill(0); - IonFluxElectrical_y.resize(Nx,Ny,Nz); IonFluxElectrical_y.fill(0); - IonFluxElectrical_z.resize(Nx,Ny,Nz); IonFluxElectrical_z.fill(0); + Nx = dm->Nx; + Ny = dm->Ny; + Nz = dm->Nz; + Volume = (Nx - 2) * (Ny - 2) * (Nz - 2) * Dm->nprocx() * Dm->nprocy() * + Dm->nprocz() * 1.0; - if (Dm->rank()==0){ - bool WriteHeader=false; - TIMELOG = fopen("electrokinetic.csv","r"); - if (TIMELOG != NULL) - fclose(TIMELOG); - else - WriteHeader=true; + ChemicalPotential.resize(Nx, Ny, Nz); + ChemicalPotential.fill(0); + ElectricalPotential.resize(Nx, Ny, Nz); + ElectricalPotential.fill(0); + ElectricalField_x.resize(Nx, Ny, Nz); + ElectricalField_x.fill(0); + ElectricalField_y.resize(Nx, Ny, Nz); + ElectricalField_y.fill(0); + ElectricalField_z.resize(Nx, Ny, Nz); + ElectricalField_z.fill(0); + Pressure.resize(Nx, Ny, Nz); + Pressure.fill(0); + Rho.resize(Nx, Ny, Nz); + Rho.fill(0); + Vel_x.resize(Nx, Ny, Nz); + Vel_x.fill(0); // Gradient of the phase indicator field + Vel_y.resize(Nx, Ny, Nz); + Vel_y.fill(0); + Vel_z.resize(Nx, Ny, Nz); + Vel_z.fill(0); + SDs.resize(Nx, Ny, Nz); + SDs.fill(0); + IonFluxDiffusive_x.resize(Nx, Ny, Nz); + IonFluxDiffusive_x.fill(0); + IonFluxDiffusive_y.resize(Nx, Ny, Nz); + IonFluxDiffusive_y.fill(0); + IonFluxDiffusive_z.resize(Nx, Ny, Nz); + IonFluxDiffusive_z.fill(0); + IonFluxAdvective_x.resize(Nx, Ny, Nz); + IonFluxAdvective_x.fill(0); + IonFluxAdvective_y.resize(Nx, Ny, Nz); + IonFluxAdvective_y.fill(0); + IonFluxAdvective_z.resize(Nx, Ny, Nz); + IonFluxAdvective_z.fill(0); + IonFluxElectrical_x.resize(Nx, Ny, Nz); + IonFluxElectrical_x.fill(0); + IonFluxElectrical_y.resize(Nx, Ny, Nz); + IonFluxElectrical_y.fill(0); + IonFluxElectrical_z.resize(Nx, Ny, Nz); + IonFluxElectrical_z.fill(0); - TIMELOG = fopen("electrokinetic.csv","a+"); - if (WriteHeader) - { - // If timelog is empty, write a short header to list the averages - //fprintf(TIMELOG,"--------------------------------------------------------------------------------------\n"); - fprintf(TIMELOG,"TBD TBD\n"); - } - } + if (Dm->rank() == 0) { + bool WriteHeader = false; + TIMELOG = fopen("electrokinetic.csv", "r"); + if (TIMELOG != NULL) + fclose(TIMELOG); + else + WriteHeader = true; + TIMELOG = fopen("electrokinetic.csv", "a+"); + if (WriteHeader) { + // If timelog is empty, write a short header to list the averages + //fprintf(TIMELOG,"--------------------------------------------------------------------------------------\n"); + fprintf(TIMELOG, "TBD TBD\n"); + } + } } -ElectroChemistryAnalyzer::~ElectroChemistryAnalyzer(){ - if (Dm->rank()==0){ - fclose(TIMELOG); - } +ElectroChemistryAnalyzer::~ElectroChemistryAnalyzer() { + if (Dm->rank() == 0) { + fclose(TIMELOG); + } } -void ElectroChemistryAnalyzer::SetParams(){ - -} +void ElectroChemistryAnalyzer::SetParams() {} -void ElectroChemistryAnalyzer::Basic(ScaLBL_IonModel &Ion, ScaLBL_Poisson &Poisson, ScaLBL_StokesModel &Stokes, int timestep){ +void ElectroChemistryAnalyzer::Basic(ScaLBL_IonModel &Ion, + ScaLBL_Poisson &Poisson, + ScaLBL_StokesModel &Stokes, int timestep) { - int i,j,k; - double Vin=0.0; - double Vout=0.0; - Poisson.getElectricPotential(ElectricalPotential); - - /* local sub-domain averages */ - double *rho_avg_local; - double *rho_mu_avg_local; - double *rho_mu_fluctuation_local; - double *rho_psi_avg_local; - double *rho_psi_fluctuation_local; - /* global averages */ - double *rho_avg_global; - double *rho_mu_avg_global; - double *rho_mu_fluctuation_global; - double *rho_psi_avg_global; - double *rho_psi_fluctuation_global; - - /* local sub-domain averages */ - rho_avg_local = new double [Ion.number_ion_species]; - rho_mu_avg_local = new double [Ion.number_ion_species]; - rho_mu_fluctuation_local = new double [Ion.number_ion_species]; - rho_psi_avg_local = new double [Ion.number_ion_species]; - rho_psi_fluctuation_local = new double [Ion.number_ion_species]; - /* global averages */ - rho_avg_global = new double [Ion.number_ion_species]; - rho_mu_avg_global = new double [Ion.number_ion_species]; - rho_mu_fluctuation_global = new double [Ion.number_ion_species]; - rho_psi_avg_global = new double [Ion.number_ion_species]; - rho_psi_fluctuation_global = new double [Ion.number_ion_species]; - - for (size_t ion=0; ionComm.sumReduce( rho_avg_local[ion]) / Volume; - rho_mu_avg_global[ion]=Dm->Comm.sumReduce( rho_mu_avg_local[ion]) / Volume; - rho_psi_avg_global[ion]=Dm->Comm.sumReduce( rho_psi_avg_local[ion]) / Volume; + int i, j, k; + double Vin = 0.0; + double Vout = 0.0; + Poisson.getElectricPotential(ElectricalPotential); - if (rho_avg_global[ion] > 0.0){ - rho_mu_avg_global[ion] /= rho_avg_global[ion]; - rho_psi_avg_global[ion] /= rho_avg_global[ion]; - } - } - - for (size_t ion=0; ionComm.sumReduce( rho_mu_fluctuation_local[ion]); - rho_psi_fluctuation_global[ion]=Dm->Comm.sumReduce( rho_psi_fluctuation_local[ion]); - } - - if (Dm->rank()==0){ - fprintf(TIMELOG,"%i ",timestep); - for (size_t ion=0; ionComm.sumReduce(rho_avg_local[ion]) / Volume; + rho_mu_avg_global[ion] = + Dm->Comm.sumReduce(rho_mu_avg_local[ion]) / Volume; + rho_psi_avg_global[ion] = + Dm->Comm.sumReduce(rho_psi_avg_local[ion]) / Volume; + + if (rho_avg_global[ion] > 0.0) { + rho_mu_avg_global[ion] /= rho_avg_global[ion]; + rho_psi_avg_global[ion] /= rho_avg_global[ion]; + } + } + + for (size_t ion = 0; ion < Ion.number_ion_species; ion++) { + rho_mu_fluctuation_local[ion] = 0.0; + rho_psi_fluctuation_local[ion] = 0.0; + /* Compute averages for each ion */ + for (k = 1; k < Nz; k++) { + for (j = 1; j < Ny; j++) { + for (i = 1; i < Nx; i++) { + rho_mu_fluctuation_local[ion] += + (Rho(i, j, k) * Rho(i, j, k) - rho_mu_avg_global[ion]); + rho_psi_fluctuation_local[ion] += + (Rho(i, j, k) * ElectricalPotential(i, j, k) - + rho_psi_avg_global[ion]); + } + } + } + rho_mu_fluctuation_global[ion] = + Dm->Comm.sumReduce(rho_mu_fluctuation_local[ion]); + rho_psi_fluctuation_global[ion] = + Dm->Comm.sumReduce(rho_psi_fluctuation_local[ion]); + } + + if (Dm->rank() == 0) { + fprintf(TIMELOG, "%i ", timestep); + for (size_t ion = 0; ion < Ion.number_ion_species; ion++) { + fprintf(TIMELOG, "%.8g ", rho_avg_global[ion]); + fprintf(TIMELOG, "%.8g ", rho_mu_avg_global[ion]); + fprintf(TIMELOG, "%.8g ", rho_psi_avg_global[ion]); + fprintf(TIMELOG, "%.8g ", rho_mu_fluctuation_global[ion]); + fprintf(TIMELOG, "%.8g ", rho_psi_fluctuation_global[ion]); + } + fprintf(TIMELOG, "%.8g %.8g\n", Vin, Vout); + fflush(TIMELOG); + } + /* else{ fprintf(TIMELOG,"%i ",timestep); for (int ion=0; ion input_db, int timestep){ - - auto vis_db = input_db->getDatabase( "Visualization" ); - char VisName[40]; - - std::vector visData; - fillHalo fillData(Dm->Comm,Dm->rank_info,{Dm->Nx-2,Dm->Ny-2,Dm->Nz-2},{1,1,1},0,1); +void ElectroChemistryAnalyzer::WriteVis(ScaLBL_IonModel &Ion, + ScaLBL_Poisson &Poisson, + ScaLBL_StokesModel &Stokes, + std::shared_ptr input_db, + int timestep) { - IO::initialize("","silo","false"); - // Create the MeshDataStruct + auto vis_db = input_db->getDatabase("Visualization"); + char VisName[40]; + + std::vector visData; + fillHalo fillData(Dm->Comm, Dm->rank_info, + {Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2}, {1, 1, 1}, + 0, 1); + + IO::initialize("", "silo", "false"); + // Create the MeshDataStruct visData.resize(1); visData[0].meshName = "domain"; - visData[0].mesh = std::make_shared( Dm->rank_info,Dm->Nx-2,Dm->Ny-2,Dm->Nz-2,Dm->Lx,Dm->Ly,Dm->Lz ); + visData[0].mesh = + std::make_shared(Dm->rank_info, Dm->Nx - 2, Dm->Ny - 2, + Dm->Nz - 2, Dm->Lx, Dm->Ly, Dm->Lz); //electric potential auto ElectricPotentialVar = std::make_shared(); //electric field @@ -180,7 +215,7 @@ void ElectroChemistryAnalyzer::WriteVis( ScaLBL_IonModel &Ion, ScaLBL_Poisson &P //ion concentration std::vector> IonConcentration; - for (size_t ion=0; ion()); } //fluid velocity @@ -189,7 +224,7 @@ void ElectroChemistryAnalyzer::WriteVis( ScaLBL_IonModel &Ion, ScaLBL_Poisson &P auto VzVar = std::make_shared(); // diffusive ion flux std::vector> IonFluxDiffusive; - for (size_t ion=0; ion()); IonFluxDiffusive.push_back(std::make_shared()); @@ -197,7 +232,7 @@ void ElectroChemistryAnalyzer::WriteVis( ScaLBL_IonModel &Ion, ScaLBL_Poisson &P } // advective ion flux std::vector> IonFluxAdvective; - for (size_t ion=0; ion()); IonFluxAdvective.push_back(std::make_shared()); @@ -205,7 +240,7 @@ void ElectroChemistryAnalyzer::WriteVis( ScaLBL_IonModel &Ion, ScaLBL_Poisson &P } // electro-migrational ion flux std::vector> IonFluxElectrical; - for (size_t ion=0; ion()); IonFluxElectrical.push_back(std::make_shared()); @@ -214,270 +249,348 @@ void ElectroChemistryAnalyzer::WriteVis( ScaLBL_IonModel &Ion, ScaLBL_Poisson &P //-------------------------------------------------------------------------------------------------------------------- //-------------------------------------Create Names for Variables------------------------------------------------------ - if (vis_db->getWithDefault( "save_electric_potential", true )){ - ElectricPotentialVar->name = "ElectricPotential"; - ElectricPotentialVar->type = IO::VariableType::VolumeVariable; - ElectricPotentialVar->dim = 1; - ElectricPotentialVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + if (vis_db->getWithDefault("save_electric_potential", true)) { + ElectricPotentialVar->name = "ElectricPotential"; + ElectricPotentialVar->type = IO::VariableType::VolumeVariable; + ElectricPotentialVar->dim = 1; + ElectricPotentialVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(ElectricPotentialVar); } - if (vis_db->getWithDefault( "save_concentration", true )){ - for (size_t ion=0; ionname = VisName; - IonConcentration[ion]->type = IO::VariableType::VolumeVariable; - IonConcentration[ion]->dim = 1; - IonConcentration[ion]->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(IonConcentration[ion]); - } + if (vis_db->getWithDefault("save_concentration", true)) { + for (size_t ion = 0; ion < Ion.number_ion_species; ion++) { + sprintf(VisName, "IonConcentration_%zu", ion + 1); + IonConcentration[ion]->name = VisName; + IonConcentration[ion]->type = IO::VariableType::VolumeVariable; + IonConcentration[ion]->dim = 1; + IonConcentration[ion]->data.resize(Dm->Nx - 2, Dm->Ny - 2, + Dm->Nz - 2); + visData[0].vars.push_back(IonConcentration[ion]); + } } - if (vis_db->getWithDefault( "save_velocity", false )){ + if (vis_db->getWithDefault("save_velocity", false)) { VxVar->name = "Velocity_x"; VxVar->type = IO::VariableType::VolumeVariable; VxVar->dim = 1; - VxVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + VxVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(VxVar); VyVar->name = "Velocity_y"; VyVar->type = IO::VariableType::VolumeVariable; VyVar->dim = 1; - VyVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + VyVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(VyVar); VzVar->name = "Velocity_z"; VzVar->type = IO::VariableType::VolumeVariable; VzVar->dim = 1; - VzVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + VzVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(VzVar); } - if (vis_db->getWithDefault( "save_ion_flux_diffusive", false )){ - for (size_t ion=0; iongetWithDefault("save_ion_flux_diffusive", false)) { + for (size_t ion = 0; ion < Ion.number_ion_species; ion++) { // x-component of diffusive flux - sprintf(VisName,"Ion%zu_FluxDiffusive_x",ion+1); - IonFluxDiffusive[3*ion+0]->name = VisName; - IonFluxDiffusive[3*ion+0]->type = IO::VariableType::VolumeVariable; - IonFluxDiffusive[3*ion+0]->dim = 1; - IonFluxDiffusive[3*ion+0]->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(IonFluxDiffusive[3*ion+0]); + sprintf(VisName, "Ion%zu_FluxDiffusive_x", ion + 1); + IonFluxDiffusive[3 * ion + 0]->name = VisName; + IonFluxDiffusive[3 * ion + 0]->type = + IO::VariableType::VolumeVariable; + IonFluxDiffusive[3 * ion + 0]->dim = 1; + IonFluxDiffusive[3 * ion + 0]->data.resize(Dm->Nx - 2, Dm->Ny - 2, + Dm->Nz - 2); + visData[0].vars.push_back(IonFluxDiffusive[3 * ion + 0]); // y-component of diffusive flux - sprintf(VisName,"Ion%zu_FluxDiffusive_y",ion+1); - IonFluxDiffusive[3*ion+1]->name = VisName; - IonFluxDiffusive[3*ion+1]->type = IO::VariableType::VolumeVariable; - IonFluxDiffusive[3*ion+1]->dim = 1; - IonFluxDiffusive[3*ion+1]->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(IonFluxDiffusive[3*ion+1]); + sprintf(VisName, "Ion%zu_FluxDiffusive_y", ion + 1); + IonFluxDiffusive[3 * ion + 1]->name = VisName; + IonFluxDiffusive[3 * ion + 1]->type = + IO::VariableType::VolumeVariable; + IonFluxDiffusive[3 * ion + 1]->dim = 1; + IonFluxDiffusive[3 * ion + 1]->data.resize(Dm->Nx - 2, Dm->Ny - 2, + Dm->Nz - 2); + visData[0].vars.push_back(IonFluxDiffusive[3 * ion + 1]); // z-component of diffusive flux - sprintf(VisName,"Ion%zu_FluxDiffusive_z",ion+1); - IonFluxDiffusive[3*ion+2]->name = VisName; - IonFluxDiffusive[3*ion+2]->type = IO::VariableType::VolumeVariable; - IonFluxDiffusive[3*ion+2]->dim = 1; - IonFluxDiffusive[3*ion+2]->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(IonFluxDiffusive[3*ion+2]); - } + sprintf(VisName, "Ion%zu_FluxDiffusive_z", ion + 1); + IonFluxDiffusive[3 * ion + 2]->name = VisName; + IonFluxDiffusive[3 * ion + 2]->type = + IO::VariableType::VolumeVariable; + IonFluxDiffusive[3 * ion + 2]->dim = 1; + IonFluxDiffusive[3 * ion + 2]->data.resize(Dm->Nx - 2, Dm->Ny - 2, + Dm->Nz - 2); + visData[0].vars.push_back(IonFluxDiffusive[3 * ion + 2]); + } } - if (vis_db->getWithDefault( "save_ion_flux_advective", false )){ - for (size_t ion=0; iongetWithDefault("save_ion_flux_advective", false)) { + for (size_t ion = 0; ion < Ion.number_ion_species; ion++) { // x-component of advective flux - sprintf(VisName,"Ion%zu_FluxAdvective_x",ion+1); - IonFluxAdvective[3*ion+0]->name = VisName; - IonFluxAdvective[3*ion+0]->type = IO::VariableType::VolumeVariable; - IonFluxAdvective[3*ion+0]->dim = 1; - IonFluxAdvective[3*ion+0]->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(IonFluxAdvective[3*ion+0]); + sprintf(VisName, "Ion%zu_FluxAdvective_x", ion + 1); + IonFluxAdvective[3 * ion + 0]->name = VisName; + IonFluxAdvective[3 * ion + 0]->type = + IO::VariableType::VolumeVariable; + IonFluxAdvective[3 * ion + 0]->dim = 1; + IonFluxAdvective[3 * ion + 0]->data.resize(Dm->Nx - 2, Dm->Ny - 2, + Dm->Nz - 2); + visData[0].vars.push_back(IonFluxAdvective[3 * ion + 0]); // y-component of advective flux - sprintf(VisName,"Ion%zu_FluxAdvective_y",ion+1); - IonFluxAdvective[3*ion+1]->name = VisName; - IonFluxAdvective[3*ion+1]->type = IO::VariableType::VolumeVariable; - IonFluxAdvective[3*ion+1]->dim = 1; - IonFluxAdvective[3*ion+1]->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(IonFluxAdvective[3*ion+1]); + sprintf(VisName, "Ion%zu_FluxAdvective_y", ion + 1); + IonFluxAdvective[3 * ion + 1]->name = VisName; + IonFluxAdvective[3 * ion + 1]->type = + IO::VariableType::VolumeVariable; + IonFluxAdvective[3 * ion + 1]->dim = 1; + IonFluxAdvective[3 * ion + 1]->data.resize(Dm->Nx - 2, Dm->Ny - 2, + Dm->Nz - 2); + visData[0].vars.push_back(IonFluxAdvective[3 * ion + 1]); // z-component of advective flux - sprintf(VisName,"Ion%zu_FluxAdvective_z",ion+1); - IonFluxAdvective[3*ion+2]->name = VisName; - IonFluxAdvective[3*ion+2]->type = IO::VariableType::VolumeVariable; - IonFluxAdvective[3*ion+2]->dim = 1; - IonFluxAdvective[3*ion+2]->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(IonFluxAdvective[3*ion+2]); - } + sprintf(VisName, "Ion%zu_FluxAdvective_z", ion + 1); + IonFluxAdvective[3 * ion + 2]->name = VisName; + IonFluxAdvective[3 * ion + 2]->type = + IO::VariableType::VolumeVariable; + IonFluxAdvective[3 * ion + 2]->dim = 1; + IonFluxAdvective[3 * ion + 2]->data.resize(Dm->Nx - 2, Dm->Ny - 2, + Dm->Nz - 2); + visData[0].vars.push_back(IonFluxAdvective[3 * ion + 2]); + } } - if (vis_db->getWithDefault( "save_ion_flux_electrical", false )){ - for (size_t ion=0; iongetWithDefault("save_ion_flux_electrical", false)) { + for (size_t ion = 0; ion < Ion.number_ion_species; ion++) { // x-component of electro-migrational flux - sprintf(VisName,"Ion%zu_FluxElectrical_x",ion+1); - IonFluxElectrical[3*ion+0]->name = VisName; - IonFluxElectrical[3*ion+0]->type = IO::VariableType::VolumeVariable; - IonFluxElectrical[3*ion+0]->dim = 1; - IonFluxElectrical[3*ion+0]->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(IonFluxElectrical[3*ion+0]); + sprintf(VisName, "Ion%zu_FluxElectrical_x", ion + 1); + IonFluxElectrical[3 * ion + 0]->name = VisName; + IonFluxElectrical[3 * ion + 0]->type = + IO::VariableType::VolumeVariable; + IonFluxElectrical[3 * ion + 0]->dim = 1; + IonFluxElectrical[3 * ion + 0]->data.resize(Dm->Nx - 2, Dm->Ny - 2, + Dm->Nz - 2); + visData[0].vars.push_back(IonFluxElectrical[3 * ion + 0]); // y-component of electro-migrational flux - sprintf(VisName,"Ion%zu_FluxElectrical_y",ion+1); - IonFluxElectrical[3*ion+1]->name = VisName; - IonFluxElectrical[3*ion+1]->type = IO::VariableType::VolumeVariable; - IonFluxElectrical[3*ion+1]->dim = 1; - IonFluxElectrical[3*ion+1]->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(IonFluxElectrical[3*ion+1]); + sprintf(VisName, "Ion%zu_FluxElectrical_y", ion + 1); + IonFluxElectrical[3 * ion + 1]->name = VisName; + IonFluxElectrical[3 * ion + 1]->type = + IO::VariableType::VolumeVariable; + IonFluxElectrical[3 * ion + 1]->dim = 1; + IonFluxElectrical[3 * ion + 1]->data.resize(Dm->Nx - 2, Dm->Ny - 2, + Dm->Nz - 2); + visData[0].vars.push_back(IonFluxElectrical[3 * ion + 1]); // z-component of electro-migrational flux - sprintf(VisName,"Ion%zu_FluxElectrical_z",ion+1); - IonFluxElectrical[3*ion+2]->name = VisName; - IonFluxElectrical[3*ion+2]->type = IO::VariableType::VolumeVariable; - IonFluxElectrical[3*ion+2]->dim = 1; - IonFluxElectrical[3*ion+2]->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(IonFluxElectrical[3*ion+2]); - } + sprintf(VisName, "Ion%zu_FluxElectrical_z", ion + 1); + IonFluxElectrical[3 * ion + 2]->name = VisName; + IonFluxElectrical[3 * ion + 2]->type = + IO::VariableType::VolumeVariable; + IonFluxElectrical[3 * ion + 2]->dim = 1; + IonFluxElectrical[3 * ion + 2]->data.resize(Dm->Nx - 2, Dm->Ny - 2, + Dm->Nz - 2); + visData[0].vars.push_back(IonFluxElectrical[3 * ion + 2]); + } } - if (vis_db->getWithDefault( "save_electric_field", false )){ + if (vis_db->getWithDefault("save_electric_field", false)) { ElectricFieldVar_x->name = "ElectricField_x"; ElectricFieldVar_x->type = IO::VariableType::VolumeVariable; ElectricFieldVar_x->dim = 1; - ElectricFieldVar_x->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + ElectricFieldVar_x->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(ElectricFieldVar_x); ElectricFieldVar_y->name = "ElectricField_y"; ElectricFieldVar_y->type = IO::VariableType::VolumeVariable; ElectricFieldVar_y->dim = 1; - ElectricFieldVar_y->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + ElectricFieldVar_y->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(ElectricFieldVar_y); ElectricFieldVar_z->name = "ElectricField_z"; ElectricFieldVar_z->type = IO::VariableType::VolumeVariable; ElectricFieldVar_z->dim = 1; - ElectricFieldVar_z->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + ElectricFieldVar_z->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(ElectricFieldVar_z); } //-------------------------------------------------------------------------------------------------------------------- - + //------------------------------------Save All Variables-------------------------------------------------------------- - if (vis_db->getWithDefault( "save_electric_potential", true )){ - ASSERT(visData[0].vars[0]->name=="ElectricPotential"); - Poisson.getElectricPotential(ElectricalPotential); - Array& ElectricPotentialData = visData[0].vars[0]->data; - fillData.copy(ElectricalPotential,ElectricPotentialData); + if (vis_db->getWithDefault("save_electric_potential", true)) { + ASSERT(visData[0].vars[0]->name == "ElectricPotential"); + Poisson.getElectricPotential(ElectricalPotential); + Array &ElectricPotentialData = visData[0].vars[0]->data; + fillData.copy(ElectricalPotential, ElectricPotentialData); } - if (vis_db->getWithDefault( "save_concentration", true )){ - for (size_t ion=0; ionname = VisName; - ASSERT(visData[0].vars[1+ion]->name==VisName); - Array& IonConcentrationData = visData[0].vars[1+ion]->data; - Ion.getIonConcentration(Rho,ion); - fillData.copy(Rho,IonConcentrationData); - } + if (vis_db->getWithDefault("save_concentration", true)) { + for (size_t ion = 0; ion < Ion.number_ion_species; ion++) { + sprintf(VisName, "IonConcentration_%zu", ion + 1); + //IonConcentration[ion]->name = VisName; + ASSERT(visData[0].vars[1 + ion]->name == VisName); + Array &IonConcentrationData = + visData[0].vars[1 + ion]->data; + Ion.getIonConcentration(Rho, ion); + fillData.copy(Rho, IonConcentrationData); + } } - if (vis_db->getWithDefault( "save_velocity", false )){ - ASSERT(visData[0].vars[1+Ion.number_ion_species+0]->name=="Velocity_x"); - ASSERT(visData[0].vars[1+Ion.number_ion_species+1]->name=="Velocity_y"); - ASSERT(visData[0].vars[1+Ion.number_ion_species+2]->name=="Velocity_z"); - Stokes.getVelocity(Vel_x,Vel_y,Vel_z); - Array& VelxData = visData[0].vars[1+Ion.number_ion_species+0]->data; - Array& VelyData = visData[0].vars[1+Ion.number_ion_species+1]->data; - Array& VelzData = visData[0].vars[1+Ion.number_ion_species+2]->data; - fillData.copy(Vel_x,VelxData); - fillData.copy(Vel_y,VelyData); - fillData.copy(Vel_z,VelzData); + if (vis_db->getWithDefault("save_velocity", false)) { + ASSERT(visData[0].vars[1 + Ion.number_ion_species + 0]->name == + "Velocity_x"); + ASSERT(visData[0].vars[1 + Ion.number_ion_species + 1]->name == + "Velocity_y"); + ASSERT(visData[0].vars[1 + Ion.number_ion_species + 2]->name == + "Velocity_z"); + Stokes.getVelocity(Vel_x, Vel_y, Vel_z); + Array &VelxData = + visData[0].vars[1 + Ion.number_ion_species + 0]->data; + Array &VelyData = + visData[0].vars[1 + Ion.number_ion_species + 1]->data; + Array &VelzData = + visData[0].vars[1 + Ion.number_ion_species + 2]->data; + fillData.copy(Vel_x, VelxData); + fillData.copy(Vel_y, VelyData); + fillData.copy(Vel_z, VelzData); } - if (vis_db->getWithDefault( "save_ion_flux_diffusive", false )){ - for (size_t ion=0; iongetWithDefault("save_ion_flux_diffusive", false)) { + for (size_t ion = 0; ion < Ion.number_ion_species; ion++) { + // x-component of diffusive flux - sprintf(VisName,"Ion%zu_FluxDiffusive_x",ion+1); - //IonFluxDiffusive[3*ion+0]->name = VisName; - ASSERT(visData[0].vars[4+Ion.number_ion_species+3*ion+0]->name==VisName); + sprintf(VisName, "Ion%zu_FluxDiffusive_x", ion + 1); + //IonFluxDiffusive[3*ion+0]->name = VisName; + ASSERT(visData[0] + .vars[4 + Ion.number_ion_species + 3 * ion + 0] + ->name == VisName); // y-component of diffusive flux - sprintf(VisName,"Ion%zu_FluxDiffusive_y",ion+1); - //IonFluxDiffusive[3*ion+1]->name = VisName; - ASSERT(visData[0].vars[4+Ion.number_ion_species+3*ion+1]->name==VisName); + sprintf(VisName, "Ion%zu_FluxDiffusive_y", ion + 1); + //IonFluxDiffusive[3*ion+1]->name = VisName; + ASSERT(visData[0] + .vars[4 + Ion.number_ion_species + 3 * ion + 1] + ->name == VisName); // z-component of diffusive flux - sprintf(VisName,"Ion%zu_FluxDiffusive_z",ion+1); - //IonFluxDiffusive[3*ion+2]->name = VisName; - ASSERT(visData[0].vars[4+Ion.number_ion_species+3*ion+2]->name==VisName); + sprintf(VisName, "Ion%zu_FluxDiffusive_z", ion + 1); + //IonFluxDiffusive[3*ion+2]->name = VisName; + ASSERT(visData[0] + .vars[4 + Ion.number_ion_species + 3 * ion + 2] + ->name == VisName); - Array& IonFluxData_x = visData[0].vars[4+Ion.number_ion_species+3*ion+0]->data; - Array& IonFluxData_y = visData[0].vars[4+Ion.number_ion_species+3*ion+1]->data; - Array& IonFluxData_z = visData[0].vars[4+Ion.number_ion_species+3*ion+2]->data; - Ion.getIonFluxDiffusive(IonFluxDiffusive_x,IonFluxDiffusive_y,IonFluxDiffusive_z,ion); - fillData.copy(IonFluxDiffusive_x,IonFluxData_x); - fillData.copy(IonFluxDiffusive_y,IonFluxData_y); - fillData.copy(IonFluxDiffusive_z,IonFluxData_z); - } + Array &IonFluxData_x = + visData[0].vars[4 + Ion.number_ion_species + 3 * ion + 0]->data; + Array &IonFluxData_y = + visData[0].vars[4 + Ion.number_ion_species + 3 * ion + 1]->data; + Array &IonFluxData_z = + visData[0].vars[4 + Ion.number_ion_species + 3 * ion + 2]->data; + Ion.getIonFluxDiffusive(IonFluxDiffusive_x, IonFluxDiffusive_y, + IonFluxDiffusive_z, ion); + fillData.copy(IonFluxDiffusive_x, IonFluxData_x); + fillData.copy(IonFluxDiffusive_y, IonFluxData_y); + fillData.copy(IonFluxDiffusive_z, IonFluxData_z); + } } - if (vis_db->getWithDefault( "save_ion_flux_advective", false )){ - for (size_t ion=0; iongetWithDefault("save_ion_flux_advective", false)) { + for (size_t ion = 0; ion < Ion.number_ion_species; ion++) { + // x-component of diffusive flux - sprintf(VisName,"Ion%zu_FluxAdvective_x",ion+1); - //IonFluxDiffusive[3*ion+0]->name = VisName; - ASSERT(visData[0].vars[4+Ion.number_ion_species*(1+3)+3*ion+0]->name==VisName); + sprintf(VisName, "Ion%zu_FluxAdvective_x", ion + 1); + //IonFluxDiffusive[3*ion+0]->name = VisName; + ASSERT(visData[0] + .vars[4 + Ion.number_ion_species * (1 + 3) + 3 * ion + 0] + ->name == VisName); // y-component of diffusive flux - sprintf(VisName,"Ion%zu_FluxAdvective_y",ion+1); - //IonFluxDiffusive[3*ion+1]->name = VisName; - ASSERT(visData[0].vars[4+Ion.number_ion_species*(1+3)+3*ion+1]->name==VisName); + sprintf(VisName, "Ion%zu_FluxAdvective_y", ion + 1); + //IonFluxDiffusive[3*ion+1]->name = VisName; + ASSERT(visData[0] + .vars[4 + Ion.number_ion_species * (1 + 3) + 3 * ion + 1] + ->name == VisName); // z-component of diffusive flux - sprintf(VisName,"Ion%zu_FluxAdvective_z",ion+1); - //IonFluxDiffusive[3*ion+2]->name = VisName; - ASSERT(visData[0].vars[4+Ion.number_ion_species*(1+3)+3*ion+2]->name==VisName); + sprintf(VisName, "Ion%zu_FluxAdvective_z", ion + 1); + //IonFluxDiffusive[3*ion+2]->name = VisName; + ASSERT(visData[0] + .vars[4 + Ion.number_ion_species * (1 + 3) + 3 * ion + 2] + ->name == VisName); - Array& IonFluxData_x = visData[0].vars[4+Ion.number_ion_species*(1+3)+3*ion+0]->data; - Array& IonFluxData_y = visData[0].vars[4+Ion.number_ion_species*(1+3)+3*ion+1]->data; - Array& IonFluxData_z = visData[0].vars[4+Ion.number_ion_species*(1+3)+3*ion+2]->data; - Ion.getIonFluxAdvective(IonFluxAdvective_x,IonFluxAdvective_y,IonFluxAdvective_z,ion); - fillData.copy(IonFluxAdvective_x,IonFluxData_x); - fillData.copy(IonFluxAdvective_y,IonFluxData_y); - fillData.copy(IonFluxAdvective_z,IonFluxData_z); - } + Array &IonFluxData_x = + visData[0] + .vars[4 + Ion.number_ion_species * (1 + 3) + 3 * ion + 0] + ->data; + Array &IonFluxData_y = + visData[0] + .vars[4 + Ion.number_ion_species * (1 + 3) + 3 * ion + 1] + ->data; + Array &IonFluxData_z = + visData[0] + .vars[4 + Ion.number_ion_species * (1 + 3) + 3 * ion + 2] + ->data; + Ion.getIonFluxAdvective(IonFluxAdvective_x, IonFluxAdvective_y, + IonFluxAdvective_z, ion); + fillData.copy(IonFluxAdvective_x, IonFluxData_x); + fillData.copy(IonFluxAdvective_y, IonFluxData_y); + fillData.copy(IonFluxAdvective_z, IonFluxData_z); + } } - - if (vis_db->getWithDefault( "save_ion_flux_electrical", false )){ - for (size_t ion=0; iongetWithDefault("save_ion_flux_electrical", false)) { + for (size_t ion = 0; ion < Ion.number_ion_species; ion++) { + // x-component of diffusive flux - sprintf(VisName,"Ion%zu_FluxElectrical_x",ion+1); - //IonFluxDiffusive[3*ion+0]->name = VisName; - ASSERT(visData[0].vars[4+Ion.number_ion_species*(1+6)+3*ion+0]->name==VisName); + sprintf(VisName, "Ion%zu_FluxElectrical_x", ion + 1); + //IonFluxDiffusive[3*ion+0]->name = VisName; + ASSERT(visData[0] + .vars[4 + Ion.number_ion_species * (1 + 6) + 3 * ion + 0] + ->name == VisName); // y-component of diffusive flux - sprintf(VisName,"Ion%zu_FluxElectrical_y",ion+1); - //IonFluxDiffusive[3*ion+1]->name = VisName; - ASSERT(visData[0].vars[4+Ion.number_ion_species*(1+6)+3*ion+1]->name==VisName); + sprintf(VisName, "Ion%zu_FluxElectrical_y", ion + 1); + //IonFluxDiffusive[3*ion+1]->name = VisName; + ASSERT(visData[0] + .vars[4 + Ion.number_ion_species * (1 + 6) + 3 * ion + 1] + ->name == VisName); // z-component of diffusive flux - sprintf(VisName,"Ion%zu_FluxElectrical_z",ion+1); - //IonFluxDiffusive[3*ion+2]->name = VisName; - ASSERT(visData[0].vars[4+Ion.number_ion_species*(1+6)+3*ion+2]->name==VisName); + sprintf(VisName, "Ion%zu_FluxElectrical_z", ion + 1); + //IonFluxDiffusive[3*ion+2]->name = VisName; + ASSERT(visData[0] + .vars[4 + Ion.number_ion_species * (1 + 6) + 3 * ion + 2] + ->name == VisName); - Array& IonFluxData_x = visData[0].vars[4+Ion.number_ion_species*(1+6)+3*ion+0]->data; - Array& IonFluxData_y = visData[0].vars[4+Ion.number_ion_species*(1+6)+3*ion+1]->data; - Array& IonFluxData_z = visData[0].vars[4+Ion.number_ion_species*(1+6)+3*ion+2]->data; - Ion.getIonFluxElectrical(IonFluxElectrical_x,IonFluxElectrical_y,IonFluxElectrical_z,ion); - fillData.copy(IonFluxElectrical_x,IonFluxData_x); - fillData.copy(IonFluxElectrical_y,IonFluxData_y); - fillData.copy(IonFluxElectrical_z,IonFluxData_z); - } + Array &IonFluxData_x = + visData[0] + .vars[4 + Ion.number_ion_species * (1 + 6) + 3 * ion + 0] + ->data; + Array &IonFluxData_y = + visData[0] + .vars[4 + Ion.number_ion_species * (1 + 6) + 3 * ion + 1] + ->data; + Array &IonFluxData_z = + visData[0] + .vars[4 + Ion.number_ion_species * (1 + 6) + 3 * ion + 2] + ->data; + Ion.getIonFluxElectrical(IonFluxElectrical_x, IonFluxElectrical_y, + IonFluxElectrical_z, ion); + fillData.copy(IonFluxElectrical_x, IonFluxData_x); + fillData.copy(IonFluxElectrical_y, IonFluxData_y); + fillData.copy(IonFluxElectrical_z, IonFluxData_z); + } } - if (vis_db->getWithDefault( "save_electric_field", false )){ - ASSERT(visData[0].vars[4+Ion.number_ion_species*(1+9)+0]->name=="ElectricField_x"); - ASSERT(visData[0].vars[4+Ion.number_ion_species*(1+9)+1]->name=="ElectricField_y"); - ASSERT(visData[0].vars[4+Ion.number_ion_species*(1+9)+2]->name=="ElectricField_z"); - Poisson.getElectricField(ElectricalField_x, ElectricalField_y, ElectricalField_z); - Array& ElectricalFieldxData = visData[0].vars[4+Ion.number_ion_species*(1+9)+0]->data; - Array& ElectricalFieldyData = visData[0].vars[4+Ion.number_ion_species*(1+9)+1]->data; - Array& ElectricalFieldzData = visData[0].vars[4+Ion.number_ion_species*(1+9)+2]->data; - fillData.copy(ElectricalField_x,ElectricalFieldxData); - fillData.copy(ElectricalField_y,ElectricalFieldyData); - fillData.copy(ElectricalField_z,ElectricalFieldzData); + if (vis_db->getWithDefault("save_electric_field", false)) { + ASSERT( + visData[0].vars[4 + Ion.number_ion_species * (1 + 9) + 0]->name == + "ElectricField_x"); + ASSERT( + visData[0].vars[4 + Ion.number_ion_species * (1 + 9) + 1]->name == + "ElectricField_y"); + ASSERT( + visData[0].vars[4 + Ion.number_ion_species * (1 + 9) + 2]->name == + "ElectricField_z"); + Poisson.getElectricField(ElectricalField_x, ElectricalField_y, + ElectricalField_z); + Array &ElectricalFieldxData = + visData[0].vars[4 + Ion.number_ion_species * (1 + 9) + 0]->data; + Array &ElectricalFieldyData = + visData[0].vars[4 + Ion.number_ion_species * (1 + 9) + 1]->data; + Array &ElectricalFieldzData = + visData[0].vars[4 + Ion.number_ion_species * (1 + 9) + 2]->data; + fillData.copy(ElectricalField_x, ElectricalFieldxData); + fillData.copy(ElectricalField_y, ElectricalFieldyData); + fillData.copy(ElectricalField_z, ElectricalFieldzData); } - if (vis_db->getWithDefault( "write_silo", true )) - IO::writeData( timestep, visData, Dm->Comm ); + if (vis_db->getWithDefault("write_silo", true)) + IO::writeData(timestep, visData, Dm->Comm); //-------------------------------------------------------------------------------------------------------------------- -/* if (vis_db->getWithDefault( "save_8bit_raw", true )){ + /* if (vis_db->getWithDefault( "save_8bit_raw", true )){ char CurrentIDFilename[40]; sprintf(CurrentIDFilename,"id_t%d.raw",timestep); Averages.AggregateLabels(CurrentIDFilename); } -*/ +*/ } diff --git a/analysis/ElectroChemistry.h b/analysis/ElectroChemistry.h index 50e44b3f..1b739611 100644 --- a/analysis/ElectroChemistry.h +++ b/analysis/ElectroChemistry.h @@ -20,29 +20,29 @@ #include "models/PoissonSolver.h" #include "models/StokesModel.h" -class ElectroChemistryAnalyzer{ +class ElectroChemistryAnalyzer { public: - std::shared_ptr Dm; - double Volume; - // input variables - double rho_n, rho_w; - double nu_n, nu_w; - double gamma_wn, beta; - double Fx, Fy, Fz; + std::shared_ptr Dm; + double Volume; + // input variables + double rho_n, rho_w; + double nu_n, nu_w; + double gamma_wn, beta; + double Fx, Fy, Fz; //........................................................................... - int Nx,Ny,Nz; - DoubleArray Rho; // density field - DoubleArray ChemicalPotential; // density field - DoubleArray ElectricalPotential; // density field - DoubleArray ElectricalField_x; // density field - DoubleArray ElectricalField_y; // density field - DoubleArray ElectricalField_z; // density field - DoubleArray Pressure; // pressure field - DoubleArray Vel_x; // velocity field - DoubleArray Vel_y; - DoubleArray Vel_z; - DoubleArray SDs; + int Nx, Ny, Nz; + DoubleArray Rho; // density field + DoubleArray ChemicalPotential; // density field + DoubleArray ElectricalPotential; // density field + DoubleArray ElectricalField_x; // density field + DoubleArray ElectricalField_y; // density field + DoubleArray ElectricalField_z; // density field + DoubleArray Pressure; // pressure field + DoubleArray Vel_x; // velocity field + DoubleArray Vel_y; + DoubleArray Vel_z; + DoubleArray SDs; DoubleArray IonFluxDiffusive_x; //ion diffusive flux components DoubleArray IonFluxDiffusive_y; DoubleArray IonFluxDiffusive_z; @@ -53,15 +53,17 @@ public: DoubleArray IonFluxElectrical_y; DoubleArray IonFluxElectrical_z; - ElectroChemistryAnalyzer(std::shared_ptr Dm); - ~ElectroChemistryAnalyzer(); - - void SetParams(); - void Basic( ScaLBL_IonModel &Ion, ScaLBL_Poisson &Poisson, ScaLBL_StokesModel &Stokes, int timestep); - void WriteVis( ScaLBL_IonModel &Ion, ScaLBL_Poisson &Poisson, ScaLBL_StokesModel &Stokes, std::shared_ptr input_db, int timestep); + ElectroChemistryAnalyzer(std::shared_ptr Dm); + ~ElectroChemistryAnalyzer(); + + void SetParams(); + void Basic(ScaLBL_IonModel &Ion, ScaLBL_Poisson &Poisson, + ScaLBL_StokesModel &Stokes, int timestep); + void WriteVis(ScaLBL_IonModel &Ion, ScaLBL_Poisson &Poisson, + ScaLBL_StokesModel &Stokes, + std::shared_ptr input_db, int timestep); private: - FILE *TIMELOG; + FILE *TIMELOG; }; #endif - diff --git a/analysis/FlowAdaptor.cpp b/analysis/FlowAdaptor.cpp index bbd1efbd..ad98be77 100644 --- a/analysis/FlowAdaptor.cpp +++ b/analysis/FlowAdaptor.cpp @@ -4,511 +4,587 @@ #include "analysis/distance.h" #include "analysis/morphology.h" -FlowAdaptor::FlowAdaptor(ScaLBL_ColorModel &M){ - Nx = M.Dm->Nx; - Ny = M.Dm->Ny; - Nz = M.Dm->Nz; - timestep=-1; - timestep_previous=-1; +FlowAdaptor::FlowAdaptor(ScaLBL_ColorModel &M) { + Nx = M.Dm->Nx; + Ny = M.Dm->Ny; + Nz = M.Dm->Nz; + timestep = -1; + timestep_previous = -1; - phi.resize(Nx,Ny,Nz); phi.fill(0); // phase indicator field - phi_t.resize(Nx,Ny,Nz); phi_t.fill(0); // time derivative for the phase indicator field + phi.resize(Nx, Ny, Nz); + phi.fill(0); // phase indicator field + phi_t.resize(Nx, Ny, Nz); + phi_t.fill(0); // time derivative for the phase indicator field } -FlowAdaptor::~FlowAdaptor(){ +FlowAdaptor::~FlowAdaptor() {} -} +double FlowAdaptor::ImageInit(ScaLBL_ColorModel &M, std::string Filename) { + int rank = M.rank; + int Nx = M.Nx; + int Ny = M.Ny; + int Nz = M.Nz; + if (rank == 0) + printf("Re-initializing fluids from file: %s \n", Filename.c_str()); + M.Mask->Decomp(Filename); + for (int i = 0; i < Nx * Ny * Nz; i++) + M.id[i] = M.Mask->id[i]; // save what was read + for (int i = 0; i < Nx * Ny * Nz; i++) + M.Dm->id[i] = M.Mask->id[i]; // save what was read -double FlowAdaptor::ImageInit(ScaLBL_ColorModel &M, std::string Filename){ - int rank = M.rank; - int Nx = M.Nx; int Ny = M.Ny; int Nz = M.Nz; - if (rank==0) printf("Re-initializing fluids from file: %s \n", Filename.c_str()); - M.Mask->Decomp(Filename); - for (int i=0; iid[i]; // save what was read - for (int i=0; iid[i] = M.Mask->id[i]; // save what was read + double *PhaseLabel; + PhaseLabel = new double[Nx * Ny * Nz]; + M.AssignComponentLabels(PhaseLabel); - double *PhaseLabel; - PhaseLabel = new double[Nx*Ny*Nz]; - M.AssignComponentLabels(PhaseLabel); - - double Count = 0.0; - double PoreCount = 0.0; - for (int k=1; kComm.sumReduce( Count); - PoreCount=M.Dm->Comm.sumReduce( PoreCount); - - if (rank==0) printf(" new saturation: %f (%f / %f) \n", Count / PoreCount, Count, PoreCount); - ScaLBL_CopyToDevice(M.Phi, PhaseLabel, Nx*Ny*Nz*sizeof(double)); - M.Dm->Comm.barrier(); - - ScaLBL_D3Q19_Init(M.fq, M.Np); - ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, 0, M.ScaLBL_Comm->LastExterior(), M.Np); - ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, M.ScaLBL_Comm->FirstInterior(), M.ScaLBL_Comm->LastInterior(), M.Np); - M.Dm->Comm.barrier(); - - ScaLBL_CopyToHost(M.Averages->Phi.data(),M.Phi,Nx*Ny*Nz*sizeof(double)); - - double saturation = Count/PoreCount; - return saturation; -} - - -double FlowAdaptor::UpdateFractionalFlow(ScaLBL_ColorModel &M){ - - double MASS_FRACTION_CHANGE = 0.006; - double FRACTIONAL_FLOW_EPSILON = 5e-6; - if (M.db->keyExists( "FlowAdaptor" )){ - auto flow_db = M.db->getDatabase( "FlowAdaptor" ); - MASS_FRACTION_CHANGE = flow_db->getWithDefault( "mass_fraction_factor", 0.006); - FRACTIONAL_FLOW_EPSILON = flow_db->getWithDefault( "fractional_flow_epsilon", 5e-6); - } - int Np = M.Np; - double dA, dB, phi; - double vx,vy,vz; - double mass_a, mass_b, mass_a_global, mass_b_global; - - double *Aq_tmp, *Bq_tmp; - double *Vel_x, *Vel_y, *Vel_z, *Phase; - - Aq_tmp = new double [7*Np]; - Bq_tmp = new double [7*Np]; - Phase = new double [Np]; - Vel_x = new double [Np]; - Vel_y = new double [Np]; - Vel_z = new double [Np]; - - ScaLBL_CopyToHost(Aq_tmp, M.Aq, 7*Np*sizeof(double)); - ScaLBL_CopyToHost(Bq_tmp, M.Bq, 7*Np*sizeof(double)); - ScaLBL_CopyToHost(Vel_x, &M.Velocity[0], Np*sizeof(double)); - ScaLBL_CopyToHost(Vel_y, &M.Velocity[Np], Np*sizeof(double)); - ScaLBL_CopyToHost(Vel_z, &M.Velocity[2*Np], Np*sizeof(double)); - - int Nx = M.Nx; int Ny = M.Ny; int Nz = M.Nz; - - mass_a = mass_b = 0.0; - double maxSpeed = 0.0; - double localMaxSpeed = 0.0; - /* compute mass change based on weights */ - double sum_weights_A = 0.0; - double sum_weights_B = 0.0; - for (int k=1; kSDs(i,j,k); - if (!(n<0) ){ - dA = Aq_tmp[n] + Aq_tmp[n+Np] + Aq_tmp[n+2*Np] + Aq_tmp[n+3*Np] + Aq_tmp[n+4*Np] + Aq_tmp[n+5*Np] + Aq_tmp[n+6*Np]; - dB = Bq_tmp[n] + Bq_tmp[n+Np] + Bq_tmp[n+2*Np] + Bq_tmp[n+3*Np] + Bq_tmp[n+4*Np] + Bq_tmp[n+5*Np] + Bq_tmp[n+6*Np]; - phi = (dA - dB) / (dA + dB); - Phase[n] = phi; - mass_a += dA; - mass_b += dB; - vx = Vel_x[n]; - vy = Vel_y[n]; - vz = Vel_z[n]; - double local_momentum = sqrt(vx*vx+vy*vy+vz*vz); - double local_weight = (FRACTIONAL_FLOW_EPSILON + local_momentum); - if (phi > 0.0){ - sum_weights_A += local_weight*dA; - } - else { - sum_weights_B += local_weight*dB; - } - if ( local_momentum > localMaxSpeed){ - localMaxSpeed = local_momentum; - } - } - } - } - } - maxSpeed = M.Dm->Comm.maxReduce(localMaxSpeed); - mass_a_global = M.Dm->Comm.sumReduce(mass_a); - mass_b_global = M.Dm->Comm.sumReduce(mass_b); - double sum_weights_A_global = M.Dm->Comm.sumReduce(sum_weights_A); - double sum_weights_B_global = M.Dm->Comm.sumReduce(sum_weights_B); - sum_weights_A_global /= (FRACTIONAL_FLOW_EPSILON + maxSpeed); - sum_weights_B_global /= (FRACTIONAL_FLOW_EPSILON + maxSpeed); - - //double total_momentum_A = sqrt(vax_global*vax_global+vay_global*vay_global+vaz_global*vaz_global); - //double total_momentum_B = sqrt(vbx_global*vbx_global+vby_global*vby_global+vbz_global*vbz_global); - /* compute the total mass change */ - double TOTAL_MASS_CHANGE = MASS_FRACTION_CHANGE*(mass_a_global + mass_b_global); - if (fabs(TOTAL_MASS_CHANGE) > 0.1*mass_a_global ) - TOTAL_MASS_CHANGE = 0.1*mass_a_global; - if (fabs(TOTAL_MASS_CHANGE) > 0.1*mass_b_global ) - TOTAL_MASS_CHANGE = 0.1*mass_b_global; - - double MASS_FACTOR_A = TOTAL_MASS_CHANGE / sum_weights_A_global; - double MASS_FACTOR_B = TOTAL_MASS_CHANGE / sum_weights_B_global; - - double LOCAL_MASS_CHANGE = 0.0; - for (int k=1; k maxSpeed) local_momentum = maxSpeed; - if (phi > 0.0){ - LOCAL_MASS_CHANGE = MASS_FACTOR_A*local_weight; - Aq_tmp[n] -= 0.3333333333333333*LOCAL_MASS_CHANGE; - Aq_tmp[n+Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE; - Aq_tmp[n+2*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE; - Aq_tmp[n+3*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE; - Aq_tmp[n+4*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE; - Aq_tmp[n+5*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE; - Aq_tmp[n+6*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE; - //DebugMassA[n] = (-1.0)*LOCAL_MASS_CHANGE; - } - else{ - LOCAL_MASS_CHANGE = MASS_FACTOR_B*local_weight; - Bq_tmp[n] += 0.3333333333333333*LOCAL_MASS_CHANGE; - Bq_tmp[n+Np] += 0.1111111111111111*LOCAL_MASS_CHANGE; - Bq_tmp[n+2*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE; - Bq_tmp[n+3*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE; - Bq_tmp[n+4*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE; - Bq_tmp[n+5*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE; - Bq_tmp[n+6*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE; - //DebugMassB[n] = LOCAL_MASS_CHANGE; - } - } - } - } - } - - if (M.rank == 0) printf("Update Fractional Flow: change mass of fluid B by %f \n",TOTAL_MASS_CHANGE/mass_b_global); - - // Need to initialize Aq, Bq, Den, Phi directly - //ScaLBL_CopyToDevice(Phi,phase.data(),7*Np*sizeof(double)); - ScaLBL_CopyToDevice(M.Aq, Aq_tmp, 7*Np*sizeof(double)); - ScaLBL_CopyToDevice(M.Bq, Bq_tmp, 7*Np*sizeof(double)); - - return(TOTAL_MASS_CHANGE); -} - -void FlowAdaptor::Flatten(ScaLBL_ColorModel &M){ - - ScaLBL_D3Q19_Init(M.fq, M.Np); - ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, 0, M.ScaLBL_Comm->LastExterior(), M.Np); - ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, M.ScaLBL_Comm->FirstInterior(), M.ScaLBL_Comm->LastInterior(), M.Np); -} - -double FlowAdaptor::MoveInterface(ScaLBL_ColorModel &M){ - - double INTERFACE_CUTOFF = M.color_db->getWithDefault( "move_interface_cutoff", 0.1 ); - double MOVE_INTERFACE_FACTOR = M.color_db->getWithDefault( "move_interface_factor", 10.0 ); - - ScaLBL_CopyToHost( phi.data(), M.Phi, Nx*Ny*Nz* sizeof( double ) ); - /* compute the local derivative of phase indicator field */ - double beta = M.beta; - double factor = 0.5/beta; - double total_interface_displacement = 0.0; - double total_interface_sites = 0.0; - for (int n=0; nPhi(n); - double dist1 = factor*log((1.0+value1)/(1.0-value1)); - double value2 = phi(n); - double dist2 = factor*log((1.0+value2)/(1.0-value2)); - phi_t(n) = value2; - if (value1 < INTERFACE_CUTOFF && value1 > -1*INTERFACE_CUTOFF && value2 < INTERFACE_CUTOFF && value2 > -1*INTERFACE_CUTOFF ){ - /* time derivative of distance */ - double dxdt = 0.125*(dist2-dist1); - /* extrapolate to move the distance further */ - double dist3 = dist2 + MOVE_INTERFACE_FACTOR*dxdt; - /* compute the new phase interface */ - phi_t(n) = (2.f*(exp(-2.f*beta*(dist3)))/(1.f+exp(-2.f*beta*(dist3))) - 1.f); - total_interface_displacement += fabs(MOVE_INTERFACE_FACTOR*dxdt); - total_interface_sites += 1.0; - } - } - ScaLBL_CopyToDevice( M.Phi, phi_t.data(), Nx*Ny*Nz* sizeof( double ) ); - return total_interface_sites; -} - -double FlowAdaptor::ShellAggregation(ScaLBL_ColorModel &M, const double target_delta_volume){ - - const RankInfoStruct rank_info(M.rank,M.nprocx,M.nprocy,M.nprocz); - auto rank = M.rank; - auto Nx = M.Nx; auto Ny = M.Ny; auto Nz = M.Nz; - auto N = Nx*Ny*Nz; - double vF = 0.f; - double vS = 0.f; - double delta_volume; - double WallFactor = 1.0; - bool USE_CONNECTED_NWP = false; - - DoubleArray phase(Nx,Ny,Nz); - IntArray phase_label(Nx,Ny,Nz);; - DoubleArray phase_distance(Nx,Ny,Nz); - Array phase_id(Nx,Ny,Nz); - fillHalo fillDouble(M.Dm->Comm,M.Dm->rank_info,{Nx-2,Ny-2,Nz-2},{1,1,1},0,1); - - // Basic algorithm to - // 1. Copy phase field to CPU - ScaLBL_CopyToHost(phase.data(), M.Phi, N*sizeof(double)); - - double count = 0.f; - for (int k=1; k 0.f && M.Averages->SDs(i,j,k) > 0.f) count+=1.f; - } - } - } - double volume_initial = M.Dm->Comm.sumReduce( count); - double PoreVolume = M.Dm->Volume*M.Dm->Porosity(); - /*ensure target isn't an absurdly small fraction of pore volume */ - if (volume_initial < target_delta_volume*PoreVolume){ - volume_initial = target_delta_volume*PoreVolume; - } - - // 2. Identify connected components of phase field -> phase_label - - double volume_connected = 0.0; - double second_biggest = 0.0; - if (USE_CONNECTED_NWP){ - ComputeGlobalBlobIDs(Nx-2,Ny-2,Nz-2,rank_info,phase,M.Averages->SDs,vF,vS,phase_label,M.Dm->Comm); - M.Dm->Comm.barrier(); - - // only operate on component "0"ScaLBL_ColorModel &M, - count = 0.0; - - for (int k=0; kComm.sumReduce( count); - second_biggest = M.Dm->Comm.sumReduce( second_biggest); - } - else { - // use the whole NWP - for (int k=0; kSDs(i,j,k) > 0.f){ - if (phase(i,j,k) > 0.f ){ - phase_id(i,j,k) = 0; - } - else { - phase_id(i,j,k) = 1; - } - } - else { - phase_id(i,j,k) = 1; - } - } - } - } - } - - // 3. Generate a distance map to the largest object -> phase_distance - CalcDist(phase_distance,phase_id,*M.Dm); - - double temp,value; - double factor=0.5/M.beta; - for (int k=0; k 1.f) value=1.f; - if (value < -1.f) value=-1.f; - // temp -- distance based on analytical form McClure, Prins et al, Comp. Phys. Comm. - temp = -factor*log((1.0+value)/(1.0-value)); - /// use this approximation close to the object - if (fabs(value) < 0.8 && M.Averages->SDs(i,j,k) > 1.f ){ - phase_distance(i,j,k) = temp; - } - // erase the original object - phase(i,j,k) = -1.0; - } - } - } - } - - - if (rank==0) printf("Pathway volume / next largest ganglion %f \n",volume_connected/second_biggest ); - - if (rank==0) printf("MorphGrow with target volume fraction change %f \n", target_delta_volume/volume_initial); - double target_delta_volume_incremental = target_delta_volume; - if (fabs(target_delta_volume) > 0.01*volume_initial) - target_delta_volume_incremental = 0.01*volume_initial*target_delta_volume/fabs(target_delta_volume); - - delta_volume = MorphGrow(M.Averages->SDs,phase_distance,phase_id,M.Averages->Dm, target_delta_volume_incremental, WallFactor); - - for (int k=0; kSDs(i,j,k) > 0.f){ - if (d < 3.f){ - //phase(i,j,k) = -1.0; - phase(i,j,k) = (2.f*(exp(-2.f*M.beta*d))/(1.f+exp(-2.f*M.beta*d))-1.f); - } - } - } - } - } - fillDouble.fill(phase); - - count = 0.f; - for (int k=1; k 0.f && M.Averages->SDs(i,j,k) > 0.f){ - count+=1.f; - } - } - } - } - double volume_final= M.Dm->Comm.sumReduce( count); - - delta_volume = (volume_final-volume_initial); - if (rank == 0) printf("Shell Aggregation: change fluid volume fraction by %f \n", delta_volume/volume_initial); - if (rank == 0) printf(" new saturation = %f \n", volume_final/(M.Mask->Porosity()*double((Nx-2)*(Ny-2)*(Nz-2)*M.nprocs))); - - // 6. copy back to the device - //if (rank==0) printf("MorphInit: copy data back to device\n"); - ScaLBL_CopyToDevice(M.Phi,phase.data(),N*sizeof(double)); - - // 7. Re-initialize phase field and density - ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, 0, M.ScaLBL_Comm->LastExterior(), M.Np); - ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, M.ScaLBL_Comm->FirstInterior(), M.ScaLBL_Comm->LastInterior(), M.Np); - auto BoundaryCondition = M.BoundaryCondition; - if (BoundaryCondition == 1 || BoundaryCondition == 2 || BoundaryCondition == 3 || BoundaryCondition == 4){ - if (M.Dm->kproc()==0){ - ScaLBL_SetSlice_z(M.Phi,1.0,Nx,Ny,Nz,0); - ScaLBL_SetSlice_z(M.Phi,1.0,Nx,Ny,Nz,1); - ScaLBL_SetSlice_z(M.Phi,1.0,Nx,Ny,Nz,2); - } - if (M.Dm->kproc() == M.nprocz-1){ - ScaLBL_SetSlice_z(M.Phi,-1.0,Nx,Ny,Nz,Nz-1); - ScaLBL_SetSlice_z(M.Phi,-1.0,Nx,Ny,Nz,Nz-2); - ScaLBL_SetSlice_z(M.Phi,-1.0,Nx,Ny,Nz,Nz-3); - } - } - return delta_volume; -} - - -double FlowAdaptor::SeedPhaseField(ScaLBL_ColorModel &M, const double seed_water_in_oil){ - srand(time(NULL)); - auto rank = M.rank; - auto Np = M.Np; - double mass_loss =0.f; - double count =0.f; - double *Aq_tmp, *Bq_tmp; - - Aq_tmp = new double [7*Np]; - Bq_tmp = new double [7*Np]; - - ScaLBL_CopyToHost(Aq_tmp, M.Aq, 7*Np*sizeof(double)); - ScaLBL_CopyToHost(Bq_tmp, M.Bq, 7*Np*sizeof(double)); - - - for (int n=0; n < M.ScaLBL_Comm->LastExterior(); n++){ - double random_value = seed_water_in_oil*double(rand())/ RAND_MAX; - double dA = Aq_tmp[n] + Aq_tmp[n+Np] + Aq_tmp[n+2*Np] + Aq_tmp[n+3*Np] + Aq_tmp[n+4*Np] + Aq_tmp[n+5*Np] + Aq_tmp[n+6*Np]; - double dB = Bq_tmp[n] + Bq_tmp[n+Np] + Bq_tmp[n+2*Np] + Bq_tmp[n+3*Np] + Bq_tmp[n+4*Np] + Bq_tmp[n+5*Np] + Bq_tmp[n+6*Np]; - double phase_id = (dA - dB) / (dA + dB); - if (phase_id > 0.0){ - Aq_tmp[n] -= 0.3333333333333333*random_value; - Aq_tmp[n+Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+2*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+3*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+4*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+5*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+6*Np] -= 0.1111111111111111*random_value; - - Bq_tmp[n] += 0.3333333333333333*random_value; - Bq_tmp[n+Np] += 0.1111111111111111*random_value; - Bq_tmp[n+2*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+3*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+4*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+5*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+6*Np] += 0.1111111111111111*random_value; + double Count = 0.0; + double PoreCount = 0.0; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + if (M.id[Nx * Ny * k + Nx * j + i] == 2) { + PoreCount++; + Count++; + } else if (M.id[Nx * Ny * k + Nx * j + i] == 1) { + PoreCount++; + } + } + } } - mass_loss += random_value*seed_water_in_oil; - } - for (int n=M.ScaLBL_Comm->FirstInterior(); n < M.ScaLBL_Comm->LastInterior(); n++){ - double random_value = seed_water_in_oil*double(rand())/ RAND_MAX; - double dA = Aq_tmp[n] + Aq_tmp[n+Np] + Aq_tmp[n+2*Np] + Aq_tmp[n+3*Np] + Aq_tmp[n+4*Np] + Aq_tmp[n+5*Np] + Aq_tmp[n+6*Np]; - double dB = Bq_tmp[n] + Bq_tmp[n+Np] + Bq_tmp[n+2*Np] + Bq_tmp[n+3*Np] + Bq_tmp[n+4*Np] + Bq_tmp[n+5*Np] + Bq_tmp[n+6*Np]; - double phase_id = (dA - dB) / (dA + dB); - if (phase_id > 0.0){ - Aq_tmp[n] -= 0.3333333333333333*random_value; - Aq_tmp[n+Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+2*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+3*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+4*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+5*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+6*Np] -= 0.1111111111111111*random_value; - - Bq_tmp[n] += 0.3333333333333333*random_value; - Bq_tmp[n+Np] += 0.1111111111111111*random_value; - Bq_tmp[n+2*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+3*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+4*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+5*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+6*Np] += 0.1111111111111111*random_value; - } - mass_loss += random_value*seed_water_in_oil; - } + Count = M.Dm->Comm.sumReduce(Count); + PoreCount = M.Dm->Comm.sumReduce(PoreCount); - count= M.Dm->Comm.sumReduce( count); - mass_loss= M.Dm->Comm.sumReduce( mass_loss); - if (rank == 0) printf("Remove mass %f from %f voxels \n",mass_loss,count); + if (rank == 0) + printf(" new saturation: %f (%f / %f) \n", Count / PoreCount, Count, + PoreCount); + ScaLBL_CopyToDevice(M.Phi, PhaseLabel, Nx * Ny * Nz * sizeof(double)); + M.Dm->Comm.barrier(); - // Need to initialize Aq, Bq, Den, Phi directly - //ScaLBL_CopyToDevice(Phi,phase.data(),7*Np*sizeof(double)); - ScaLBL_CopyToDevice(M.Aq, Aq_tmp, 7*Np*sizeof(double)); - ScaLBL_CopyToDevice(M.Bq, Bq_tmp, 7*Np*sizeof(double)); + ScaLBL_D3Q19_Init(M.fq, M.Np); + ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, 0, + M.ScaLBL_Comm->LastExterior(), M.Np); + ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, + M.ScaLBL_Comm->FirstInterior(), + M.ScaLBL_Comm->LastInterior(), M.Np); + M.Dm->Comm.barrier(); - return(mass_loss); + ScaLBL_CopyToHost(M.Averages->Phi.data(), M.Phi, + Nx * Ny * Nz * sizeof(double)); + + double saturation = Count / PoreCount; + return saturation; +} + +double FlowAdaptor::UpdateFractionalFlow(ScaLBL_ColorModel &M) { + + double MASS_FRACTION_CHANGE = 0.006; + double FRACTIONAL_FLOW_EPSILON = 5e-6; + if (M.db->keyExists("FlowAdaptor")) { + auto flow_db = M.db->getDatabase("FlowAdaptor"); + MASS_FRACTION_CHANGE = + flow_db->getWithDefault("mass_fraction_factor", 0.006); + FRACTIONAL_FLOW_EPSILON = + flow_db->getWithDefault("fractional_flow_epsilon", 5e-6); + } + int Np = M.Np; + double dA, dB, phi; + double vx, vy, vz; + double mass_a, mass_b, mass_a_global, mass_b_global; + + double *Aq_tmp, *Bq_tmp; + double *Vel_x, *Vel_y, *Vel_z, *Phase; + + Aq_tmp = new double[7 * Np]; + Bq_tmp = new double[7 * Np]; + Phase = new double[Np]; + Vel_x = new double[Np]; + Vel_y = new double[Np]; + Vel_z = new double[Np]; + + ScaLBL_CopyToHost(Aq_tmp, M.Aq, 7 * Np * sizeof(double)); + ScaLBL_CopyToHost(Bq_tmp, M.Bq, 7 * Np * sizeof(double)); + ScaLBL_CopyToHost(Vel_x, &M.Velocity[0], Np * sizeof(double)); + ScaLBL_CopyToHost(Vel_y, &M.Velocity[Np], Np * sizeof(double)); + ScaLBL_CopyToHost(Vel_z, &M.Velocity[2 * Np], Np * sizeof(double)); + + int Nx = M.Nx; + int Ny = M.Ny; + int Nz = M.Nz; + + mass_a = mass_b = 0.0; + double maxSpeed = 0.0; + double localMaxSpeed = 0.0; + /* compute mass change based on weights */ + double sum_weights_A = 0.0; + double sum_weights_B = 0.0; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + int n = M.Map(i, j, k); + //double distance = M.Averages->SDs(i,j,k); + if (!(n < 0)) { + dA = Aq_tmp[n] + Aq_tmp[n + Np] + Aq_tmp[n + 2 * Np] + + Aq_tmp[n + 3 * Np] + Aq_tmp[n + 4 * Np] + + Aq_tmp[n + 5 * Np] + Aq_tmp[n + 6 * Np]; + dB = Bq_tmp[n] + Bq_tmp[n + Np] + Bq_tmp[n + 2 * Np] + + Bq_tmp[n + 3 * Np] + Bq_tmp[n + 4 * Np] + + Bq_tmp[n + 5 * Np] + Bq_tmp[n + 6 * Np]; + phi = (dA - dB) / (dA + dB); + Phase[n] = phi; + mass_a += dA; + mass_b += dB; + vx = Vel_x[n]; + vy = Vel_y[n]; + vz = Vel_z[n]; + double local_momentum = sqrt(vx * vx + vy * vy + vz * vz); + double local_weight = + (FRACTIONAL_FLOW_EPSILON + local_momentum); + if (phi > 0.0) { + sum_weights_A += local_weight * dA; + } else { + sum_weights_B += local_weight * dB; + } + if (local_momentum > localMaxSpeed) { + localMaxSpeed = local_momentum; + } + } + } + } + } + maxSpeed = M.Dm->Comm.maxReduce(localMaxSpeed); + mass_a_global = M.Dm->Comm.sumReduce(mass_a); + mass_b_global = M.Dm->Comm.sumReduce(mass_b); + double sum_weights_A_global = M.Dm->Comm.sumReduce(sum_weights_A); + double sum_weights_B_global = M.Dm->Comm.sumReduce(sum_weights_B); + sum_weights_A_global /= (FRACTIONAL_FLOW_EPSILON + maxSpeed); + sum_weights_B_global /= (FRACTIONAL_FLOW_EPSILON + maxSpeed); + + //double total_momentum_A = sqrt(vax_global*vax_global+vay_global*vay_global+vaz_global*vaz_global); + //double total_momentum_B = sqrt(vbx_global*vbx_global+vby_global*vby_global+vbz_global*vbz_global); + /* compute the total mass change */ + double TOTAL_MASS_CHANGE = + MASS_FRACTION_CHANGE * (mass_a_global + mass_b_global); + if (fabs(TOTAL_MASS_CHANGE) > 0.1 * mass_a_global) + TOTAL_MASS_CHANGE = 0.1 * mass_a_global; + if (fabs(TOTAL_MASS_CHANGE) > 0.1 * mass_b_global) + TOTAL_MASS_CHANGE = 0.1 * mass_b_global; + + double MASS_FACTOR_A = TOTAL_MASS_CHANGE / sum_weights_A_global; + double MASS_FACTOR_B = TOTAL_MASS_CHANGE / sum_weights_B_global; + + double LOCAL_MASS_CHANGE = 0.0; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + int n = M.Map(i, j, k); + if (!(n < 0)) { + phi = Phase[n]; + vx = Vel_x[n]; + vy = Vel_y[n]; + vz = Vel_z[n]; + double local_momentum = sqrt(vx * vx + vy * vy + vz * vz); + double local_weight = + (FRACTIONAL_FLOW_EPSILON + local_momentum) / + (FRACTIONAL_FLOW_EPSILON + maxSpeed); + /* impose ceiling for spurious currents */ + //if (local_momentum > maxSpeed) local_momentum = maxSpeed; + if (phi > 0.0) { + LOCAL_MASS_CHANGE = MASS_FACTOR_A * local_weight; + Aq_tmp[n] -= 0.3333333333333333 * LOCAL_MASS_CHANGE; + Aq_tmp[n + Np] -= + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Aq_tmp[n + 2 * Np] -= + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Aq_tmp[n + 3 * Np] -= + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Aq_tmp[n + 4 * Np] -= + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Aq_tmp[n + 5 * Np] -= + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Aq_tmp[n + 6 * Np] -= + 0.1111111111111111 * LOCAL_MASS_CHANGE; + //DebugMassA[n] = (-1.0)*LOCAL_MASS_CHANGE; + } else { + LOCAL_MASS_CHANGE = MASS_FACTOR_B * local_weight; + Bq_tmp[n] += 0.3333333333333333 * LOCAL_MASS_CHANGE; + Bq_tmp[n + Np] += + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Bq_tmp[n + 2 * Np] += + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Bq_tmp[n + 3 * Np] += + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Bq_tmp[n + 4 * Np] += + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Bq_tmp[n + 5 * Np] += + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Bq_tmp[n + 6 * Np] += + 0.1111111111111111 * LOCAL_MASS_CHANGE; + //DebugMassB[n] = LOCAL_MASS_CHANGE; + } + } + } + } + } + + if (M.rank == 0) + printf("Update Fractional Flow: change mass of fluid B by %f \n", + TOTAL_MASS_CHANGE / mass_b_global); + + // Need to initialize Aq, Bq, Den, Phi directly + //ScaLBL_CopyToDevice(Phi,phase.data(),7*Np*sizeof(double)); + ScaLBL_CopyToDevice(M.Aq, Aq_tmp, 7 * Np * sizeof(double)); + ScaLBL_CopyToDevice(M.Bq, Bq_tmp, 7 * Np * sizeof(double)); + + return (TOTAL_MASS_CHANGE); +} + +void FlowAdaptor::Flatten(ScaLBL_ColorModel &M) { + + ScaLBL_D3Q19_Init(M.fq, M.Np); + ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, 0, + M.ScaLBL_Comm->LastExterior(), M.Np); + ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, + M.ScaLBL_Comm->FirstInterior(), + M.ScaLBL_Comm->LastInterior(), M.Np); +} + +double FlowAdaptor::MoveInterface(ScaLBL_ColorModel &M) { + + double INTERFACE_CUTOFF = + M.color_db->getWithDefault("move_interface_cutoff", 0.1); + double MOVE_INTERFACE_FACTOR = + M.color_db->getWithDefault("move_interface_factor", 10.0); + + ScaLBL_CopyToHost(phi.data(), M.Phi, Nx * Ny * Nz * sizeof(double)); + /* compute the local derivative of phase indicator field */ + double beta = M.beta; + double factor = 0.5 / beta; + double total_interface_displacement = 0.0; + double total_interface_sites = 0.0; + for (int n = 0; n < Nx * Ny * Nz; n++) { + /* compute the distance to the interface */ + double value1 = M.Averages->Phi(n); + double dist1 = factor * log((1.0 + value1) / (1.0 - value1)); + double value2 = phi(n); + double dist2 = factor * log((1.0 + value2) / (1.0 - value2)); + phi_t(n) = value2; + if (value1 < INTERFACE_CUTOFF && value1 > -1 * INTERFACE_CUTOFF && + value2 < INTERFACE_CUTOFF && value2 > -1 * INTERFACE_CUTOFF) { + /* time derivative of distance */ + double dxdt = 0.125 * (dist2 - dist1); + /* extrapolate to move the distance further */ + double dist3 = dist2 + MOVE_INTERFACE_FACTOR * dxdt; + /* compute the new phase interface */ + phi_t(n) = (2.f * (exp(-2.f * beta * (dist3))) / + (1.f + exp(-2.f * beta * (dist3))) - + 1.f); + total_interface_displacement += fabs(MOVE_INTERFACE_FACTOR * dxdt); + total_interface_sites += 1.0; + } + } + ScaLBL_CopyToDevice(M.Phi, phi_t.data(), Nx * Ny * Nz * sizeof(double)); + return total_interface_sites; +} + +double FlowAdaptor::ShellAggregation(ScaLBL_ColorModel &M, + const double target_delta_volume) { + + const RankInfoStruct rank_info(M.rank, M.nprocx, M.nprocy, M.nprocz); + auto rank = M.rank; + auto Nx = M.Nx; + auto Ny = M.Ny; + auto Nz = M.Nz; + auto N = Nx * Ny * Nz; + double vF = 0.f; + double vS = 0.f; + double delta_volume; + double WallFactor = 1.0; + bool USE_CONNECTED_NWP = false; + + DoubleArray phase(Nx, Ny, Nz); + IntArray phase_label(Nx, Ny, Nz); + ; + DoubleArray phase_distance(Nx, Ny, Nz); + Array phase_id(Nx, Ny, Nz); + fillHalo fillDouble(M.Dm->Comm, M.Dm->rank_info, + {Nx - 2, Ny - 2, Nz - 2}, {1, 1, 1}, 0, 1); + + // Basic algorithm to + // 1. Copy phase field to CPU + ScaLBL_CopyToHost(phase.data(), M.Phi, N * sizeof(double)); + + double count = 0.f; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + if (phase(i, j, k) > 0.f && M.Averages->SDs(i, j, k) > 0.f) + count += 1.f; + } + } + } + double volume_initial = M.Dm->Comm.sumReduce(count); + double PoreVolume = M.Dm->Volume * M.Dm->Porosity(); + /*ensure target isn't an absurdly small fraction of pore volume */ + if (volume_initial < target_delta_volume * PoreVolume) { + volume_initial = target_delta_volume * PoreVolume; + } + + // 2. Identify connected components of phase field -> phase_label + + double volume_connected = 0.0; + double second_biggest = 0.0; + if (USE_CONNECTED_NWP) { + ComputeGlobalBlobIDs(Nx - 2, Ny - 2, Nz - 2, rank_info, phase, + M.Averages->SDs, vF, vS, phase_label, M.Dm->Comm); + M.Dm->Comm.barrier(); + + // only operate on component "0"ScaLBL_ColorModel &M, + count = 0.0; + + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int label = phase_label(i, j, k); + if (label == 0) { + phase_id(i, j, k) = 0; + count += 1.0; + } else + phase_id(i, j, k) = 1; + if (label == 1) { + second_biggest += 1.0; + } + } + } + } + volume_connected = M.Dm->Comm.sumReduce(count); + second_biggest = M.Dm->Comm.sumReduce(second_biggest); + } else { + // use the whole NWP + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + if (M.Averages->SDs(i, j, k) > 0.f) { + if (phase(i, j, k) > 0.f) { + phase_id(i, j, k) = 0; + } else { + phase_id(i, j, k) = 1; + } + } else { + phase_id(i, j, k) = 1; + } + } + } + } + } + + // 3. Generate a distance map to the largest object -> phase_distance + CalcDist(phase_distance, phase_id, *M.Dm); + + double temp, value; + double factor = 0.5 / M.beta; + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + if (phase_distance(i, j, k) < 3.f) { + value = phase(i, j, k); + if (value > 1.f) + value = 1.f; + if (value < -1.f) + value = -1.f; + // temp -- distance based on analytical form McClure, Prins et al, Comp. Phys. Comm. + temp = -factor * log((1.0 + value) / (1.0 - value)); + /// use this approximation close to the object + if (fabs(value) < 0.8 && M.Averages->SDs(i, j, k) > 1.f) { + phase_distance(i, j, k) = temp; + } + // erase the original object + phase(i, j, k) = -1.0; + } + } + } + } + + if (rank == 0) + printf("Pathway volume / next largest ganglion %f \n", + volume_connected / second_biggest); + + if (rank == 0) + printf("MorphGrow with target volume fraction change %f \n", + target_delta_volume / volume_initial); + double target_delta_volume_incremental = target_delta_volume; + if (fabs(target_delta_volume) > 0.01 * volume_initial) + target_delta_volume_incremental = 0.01 * volume_initial * + target_delta_volume / + fabs(target_delta_volume); + + delta_volume = + MorphGrow(M.Averages->SDs, phase_distance, phase_id, M.Averages->Dm, + target_delta_volume_incremental, WallFactor); + + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + if (phase_distance(i, j, k) < 0.0) + phase_id(i, j, k) = 0; + else + phase_id(i, j, k) = 1; + //if (phase_distance(i,j,k) < 0.0 ) phase(i,j,k) = 1.0; + } + } + } + + CalcDist(phase_distance, phase_id, *M.Dm); // re-calculate distance + + // 5. Update phase indicator field based on new distnace + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + double d = phase_distance(i, j, k); + if (M.Averages->SDs(i, j, k) > 0.f) { + if (d < 3.f) { + //phase(i,j,k) = -1.0; + phase(i, j, k) = (2.f * (exp(-2.f * M.beta * d)) / + (1.f + exp(-2.f * M.beta * d)) - + 1.f); + } + } + } + } + } + fillDouble.fill(phase); + + count = 0.f; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + if (phase(i, j, k) > 0.f && M.Averages->SDs(i, j, k) > 0.f) { + count += 1.f; + } + } + } + } + double volume_final = M.Dm->Comm.sumReduce(count); + + delta_volume = (volume_final - volume_initial); + if (rank == 0) + printf("Shell Aggregation: change fluid volume fraction by %f \n", + delta_volume / volume_initial); + if (rank == 0) + printf(" new saturation = %f \n", + volume_final / + (M.Mask->Porosity() * + double((Nx - 2) * (Ny - 2) * (Nz - 2) * M.nprocs))); + + // 6. copy back to the device + //if (rank==0) printf("MorphInit: copy data back to device\n"); + ScaLBL_CopyToDevice(M.Phi, phase.data(), N * sizeof(double)); + + // 7. Re-initialize phase field and density + ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, 0, + M.ScaLBL_Comm->LastExterior(), M.Np); + ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, + M.ScaLBL_Comm->FirstInterior(), + M.ScaLBL_Comm->LastInterior(), M.Np); + auto BoundaryCondition = M.BoundaryCondition; + if (BoundaryCondition == 1 || BoundaryCondition == 2 || + BoundaryCondition == 3 || BoundaryCondition == 4) { + if (M.Dm->kproc() == 0) { + ScaLBL_SetSlice_z(M.Phi, 1.0, Nx, Ny, Nz, 0); + ScaLBL_SetSlice_z(M.Phi, 1.0, Nx, Ny, Nz, 1); + ScaLBL_SetSlice_z(M.Phi, 1.0, Nx, Ny, Nz, 2); + } + if (M.Dm->kproc() == M.nprocz - 1) { + ScaLBL_SetSlice_z(M.Phi, -1.0, Nx, Ny, Nz, Nz - 1); + ScaLBL_SetSlice_z(M.Phi, -1.0, Nx, Ny, Nz, Nz - 2); + ScaLBL_SetSlice_z(M.Phi, -1.0, Nx, Ny, Nz, Nz - 3); + } + } + return delta_volume; +} + +double FlowAdaptor::SeedPhaseField(ScaLBL_ColorModel &M, + const double seed_water_in_oil) { + srand(time(NULL)); + auto rank = M.rank; + auto Np = M.Np; + double mass_loss = 0.f; + double count = 0.f; + double *Aq_tmp, *Bq_tmp; + + Aq_tmp = new double[7 * Np]; + Bq_tmp = new double[7 * Np]; + + ScaLBL_CopyToHost(Aq_tmp, M.Aq, 7 * Np * sizeof(double)); + ScaLBL_CopyToHost(Bq_tmp, M.Bq, 7 * Np * sizeof(double)); + + for (int n = 0; n < M.ScaLBL_Comm->LastExterior(); n++) { + double random_value = seed_water_in_oil * double(rand()) / RAND_MAX; + double dA = Aq_tmp[n] + Aq_tmp[n + Np] + Aq_tmp[n + 2 * Np] + + Aq_tmp[n + 3 * Np] + Aq_tmp[n + 4 * Np] + + Aq_tmp[n + 5 * Np] + Aq_tmp[n + 6 * Np]; + double dB = Bq_tmp[n] + Bq_tmp[n + Np] + Bq_tmp[n + 2 * Np] + + Bq_tmp[n + 3 * Np] + Bq_tmp[n + 4 * Np] + + Bq_tmp[n + 5 * Np] + Bq_tmp[n + 6 * Np]; + double phase_id = (dA - dB) / (dA + dB); + if (phase_id > 0.0) { + Aq_tmp[n] -= 0.3333333333333333 * random_value; + Aq_tmp[n + Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 2 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 3 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 4 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 5 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 6 * Np] -= 0.1111111111111111 * random_value; + + Bq_tmp[n] += 0.3333333333333333 * random_value; + Bq_tmp[n + Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 2 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 3 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 4 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 5 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 6 * Np] += 0.1111111111111111 * random_value; + } + mass_loss += random_value * seed_water_in_oil; + } + + for (int n = M.ScaLBL_Comm->FirstInterior(); + n < M.ScaLBL_Comm->LastInterior(); n++) { + double random_value = seed_water_in_oil * double(rand()) / RAND_MAX; + double dA = Aq_tmp[n] + Aq_tmp[n + Np] + Aq_tmp[n + 2 * Np] + + Aq_tmp[n + 3 * Np] + Aq_tmp[n + 4 * Np] + + Aq_tmp[n + 5 * Np] + Aq_tmp[n + 6 * Np]; + double dB = Bq_tmp[n] + Bq_tmp[n + Np] + Bq_tmp[n + 2 * Np] + + Bq_tmp[n + 3 * Np] + Bq_tmp[n + 4 * Np] + + Bq_tmp[n + 5 * Np] + Bq_tmp[n + 6 * Np]; + double phase_id = (dA - dB) / (dA + dB); + if (phase_id > 0.0) { + Aq_tmp[n] -= 0.3333333333333333 * random_value; + Aq_tmp[n + Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 2 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 3 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 4 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 5 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 6 * Np] -= 0.1111111111111111 * random_value; + + Bq_tmp[n] += 0.3333333333333333 * random_value; + Bq_tmp[n + Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 2 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 3 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 4 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 5 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 6 * Np] += 0.1111111111111111 * random_value; + } + mass_loss += random_value * seed_water_in_oil; + } + + count = M.Dm->Comm.sumReduce(count); + mass_loss = M.Dm->Comm.sumReduce(mass_loss); + if (rank == 0) + printf("Remove mass %f from %f voxels \n", mass_loss, count); + + // Need to initialize Aq, Bq, Den, Phi directly + //ScaLBL_CopyToDevice(Phi,phase.data(),7*Np*sizeof(double)); + ScaLBL_CopyToDevice(M.Aq, Aq_tmp, 7 * Np * sizeof(double)); + ScaLBL_CopyToDevice(M.Bq, Bq_tmp, 7 * Np * sizeof(double)); + + return (mass_loss); } diff --git a/analysis/FlowAdaptor.h b/analysis/FlowAdaptor.h index 172a90b4..37addd68 100644 --- a/analysis/FlowAdaptor.h +++ b/analysis/FlowAdaptor.h @@ -12,7 +12,6 @@ #include "models/ColorModel.h" - /** * \class FlowAdaptor * @brief @@ -20,21 +19,19 @@ * */ -class FlowAdaptor{ +class FlowAdaptor { public: - - /** * \brief Create a flow adaptor to operate on the LB model * @param M ScaLBL_ColorModel */ - FlowAdaptor(ScaLBL_ColorModel &M); - + FlowAdaptor(ScaLBL_ColorModel &M); + /** * \brief Destructor */ - ~FlowAdaptor(); - + ~FlowAdaptor(); + /** * \brief Fast-forward interface motion * \details Accelerate the movement of interfaces based on the time derivative @@ -43,29 +40,30 @@ public: * move_interface_factor -- determines how much to ``fast forward" * @param M ScaLBL_ColorModel */ - double MoveInterface(ScaLBL_ColorModel &M); - + double MoveInterface(ScaLBL_ColorModel &M); + /** * \brief Image re-initialization * \details Re-initialize LB simulation from image data * @param M ScaLBL_ColorModel * @param Filename name of input file to be used to read image */ - double ImageInit(ScaLBL_ColorModel &M, std::string Filename); - + double ImageInit(ScaLBL_ColorModel &M, std::string Filename); + /** * \details Update volume fraction based on morphological algorithm. Dilation / erosion algorithm will be applied to * grow / shrink the phase regions * @param M ScaLBL_ColorModel * @param delta_volume target change in volume fraction */ - double ShellAggregation(ScaLBL_ColorModel &M, const double delta_volume); - + double ShellAggregation(ScaLBL_ColorModel &M, const double delta_volume); + /** * \details Update fractional flow condition. Mass will be preferentially added or removed from * phase regions based on where flow is occurring * @param M ScaLBL_ColorModel - */ double UpdateFractionalFlow(ScaLBL_ColorModel &M); + */ + double UpdateFractionalFlow(ScaLBL_ColorModel &M); /** * \brief image re-initialization @@ -73,18 +71,19 @@ public: * @param M ScaLBL_ColorModel * @param seed_water_in_oil controls amount of mass to randomly seed into fluids */ - double SeedPhaseField(ScaLBL_ColorModel &M, const double seed_water_in_oil); - + double SeedPhaseField(ScaLBL_ColorModel &M, const double seed_water_in_oil); + /** * \brief Re-initialize LB simulation * @param M ScaLBL_ColorModel */ - void Flatten(ScaLBL_ColorModel &M); - DoubleArray phi; - DoubleArray phi_t; + void Flatten(ScaLBL_ColorModel &M); + DoubleArray phi; + DoubleArray phi_t; + private: - int Nx, Ny, Nz; - int timestep; - int timestep_previous; + int Nx, Ny, Nz; + int timestep; + int timestep_previous; }; #endif \ No newline at end of file diff --git a/analysis/FreeEnergy.cpp b/analysis/FreeEnergy.cpp index b49f2ec5..567c2f92 100644 --- a/analysis/FreeEnergy.cpp +++ b/analysis/FreeEnergy.cpp @@ -1,55 +1,60 @@ #include "analysis/FreeEnergy.h" -FreeEnergyAnalyzer::FreeEnergyAnalyzer(std::shared_ptr dm): - Dm(dm) -{ - - Nx=dm->Nx; Ny=dm->Ny; Nz=dm->Nz; - Volume=(Nx-2)*(Ny-2)*(Nz-2)*Dm->nprocx()*Dm->nprocy()*Dm->nprocz()*1.0; - - ChemicalPotential.resize(Nx,Ny,Nz); ChemicalPotential.fill(0); - Phi.resize(Nx,Ny,Nz); Phi.fill(0); - Pressure.resize(Nx,Ny,Nz); Pressure.fill(0); - Rho.resize(Nx,Ny,Nz); Rho.fill(0); - Vel_x.resize(Nx,Ny,Nz); Vel_x.fill(0); // Gradient of the phase indicator field - Vel_y.resize(Nx,Ny,Nz); Vel_y.fill(0); - Vel_z.resize(Nx,Ny,Nz); Vel_z.fill(0); - SDs.resize(Nx,Ny,Nz); SDs.fill(0); - - if (Dm->rank()==0){ - bool WriteHeader=false; - TIMELOG = fopen("free.csv","r"); - if (TIMELOG != NULL) - fclose(TIMELOG); - else - WriteHeader=true; +FreeEnergyAnalyzer::FreeEnergyAnalyzer(std::shared_ptr dm) : Dm(dm) { - TIMELOG = fopen("free.csv","a+"); - if (WriteHeader) - { - // If timelog is empty, write a short header to list the averages - //fprintf(TIMELOG,"--------------------------------------------------------------------------------------\n"); - fprintf(TIMELOG,"timestep\n"); - } - } + Nx = dm->Nx; + Ny = dm->Ny; + Nz = dm->Nz; + Volume = (Nx - 2) * (Ny - 2) * (Nz - 2) * Dm->nprocx() * Dm->nprocy() * + Dm->nprocz() * 1.0; + ChemicalPotential.resize(Nx, Ny, Nz); + ChemicalPotential.fill(0); + Phi.resize(Nx, Ny, Nz); + Phi.fill(0); + Pressure.resize(Nx, Ny, Nz); + Pressure.fill(0); + Rho.resize(Nx, Ny, Nz); + Rho.fill(0); + Vel_x.resize(Nx, Ny, Nz); + Vel_x.fill(0); // Gradient of the phase indicator field + Vel_y.resize(Nx, Ny, Nz); + Vel_y.fill(0); + Vel_z.resize(Nx, Ny, Nz); + Vel_z.fill(0); + SDs.resize(Nx, Ny, Nz); + SDs.fill(0); + + if (Dm->rank() == 0) { + bool WriteHeader = false; + TIMELOG = fopen("free.csv", "r"); + if (TIMELOG != NULL) + fclose(TIMELOG); + else + WriteHeader = true; + + TIMELOG = fopen("free.csv", "a+"); + if (WriteHeader) { + // If timelog is empty, write a short header to list the averages + //fprintf(TIMELOG,"--------------------------------------------------------------------------------------\n"); + fprintf(TIMELOG, "timestep\n"); + } + } } -FreeEnergyAnalyzer::~FreeEnergyAnalyzer(){ - if (Dm->rank()==0){ - fclose(TIMELOG); - } +FreeEnergyAnalyzer::~FreeEnergyAnalyzer() { + if (Dm->rank() == 0) { + fclose(TIMELOG); + } } -void FreeEnergyAnalyzer::SetParams(){ - -} +void FreeEnergyAnalyzer::SetParams() {} -void FreeEnergyAnalyzer::Basic(ScaLBL_FreeLeeModel &LeeModel, int timestep){ +void FreeEnergyAnalyzer::Basic(ScaLBL_FreeLeeModel &LeeModel, int timestep) { - if (Dm->rank()==0){ - fprintf(TIMELOG,"%i ",timestep); - /*for (int ion=0; ionrank() == 0) { + fprintf(TIMELOG, "%i ", timestep); + /*for (int ion=0; ion input_db, int timestep){ - - auto vis_db = input_db->getDatabase( "Visualization" ); - - std::vector visData; - fillHalo fillData(Dm->Comm,Dm->rank_info,{Dm->Nx-2,Dm->Ny-2,Dm->Nz-2},{1,1,1},0,1); +void FreeEnergyAnalyzer::WriteVis(ScaLBL_FreeLeeModel &LeeModel, + std::shared_ptr input_db, + int timestep) { - IO::initialize("","silo","false"); - // Create the MeshDataStruct + auto vis_db = input_db->getDatabase("Visualization"); + + std::vector visData; + fillHalo fillData(Dm->Comm, Dm->rank_info, + {Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2}, {1, 1, 1}, + 0, 1); + + IO::initialize("", "silo", "false"); + // Create the MeshDataStruct visData.resize(1); visData[0].meshName = "domain"; - visData[0].mesh = std::make_shared( Dm->rank_info,Dm->Nx-2,Dm->Ny-2,Dm->Nz-2,Dm->Lx,Dm->Ly,Dm->Lz ); + visData[0].mesh = + std::make_shared(Dm->rank_info, Dm->Nx - 2, Dm->Ny - 2, + Dm->Nz - 2, Dm->Lx, Dm->Ly, Dm->Lz); auto VisPhase = std::make_shared(); auto VisPressure = std::make_shared(); auto VisChemicalPotential = std::make_shared(); auto VxVar = std::make_shared(); auto VyVar = std::make_shared(); auto VzVar = std::make_shared(); - - if (vis_db->getWithDefault( "save_phase_field", true )){ - VisPhase->name = "Phase"; - VisPhase->type = IO::VariableType::VolumeVariable; - VisPhase->dim = 1; - VisPhase->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + if (vis_db->getWithDefault("save_phase_field", true)) { + VisPhase->name = "Phase"; + VisPhase->type = IO::VariableType::VolumeVariable; + VisPhase->dim = 1; + VisPhase->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(VisPhase); - } - - if (vis_db->getWithDefault( "save_potential", true )){ - - VisPressure->name = "Pressure"; - VisPressure->type = IO::VariableType::VolumeVariable; - VisPressure->dim = 1; - VisPressure->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + } + + if (vis_db->getWithDefault("save_potential", true)) { + + VisPressure->name = "Pressure"; + VisPressure->type = IO::VariableType::VolumeVariable; + VisPressure->dim = 1; + VisPressure->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(VisPressure); - - VisChemicalPotential->name = "ChemicalPotential"; - VisChemicalPotential->type = IO::VariableType::VolumeVariable; - VisChemicalPotential->dim = 1; - VisChemicalPotential->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + + VisChemicalPotential->name = "ChemicalPotential"; + VisChemicalPotential->type = IO::VariableType::VolumeVariable; + VisChemicalPotential->dim = 1; + VisChemicalPotential->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(VisChemicalPotential); } - if (vis_db->getWithDefault( "save_velocity", false )){ + if (vis_db->getWithDefault("save_velocity", false)) { VxVar->name = "Velocity_x"; VxVar->type = IO::VariableType::VolumeVariable; VxVar->dim = 1; - VxVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + VxVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(VxVar); VyVar->name = "Velocity_y"; VyVar->type = IO::VariableType::VolumeVariable; VyVar->dim = 1; - VyVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + VyVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(VyVar); VzVar->name = "Velocity_z"; VzVar->type = IO::VariableType::VolumeVariable; VzVar->dim = 1; - VzVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + VzVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(VzVar); } - - if (vis_db->getWithDefault( "save_phase", true )){ - ASSERT(visData[0].vars[0]->name=="Phase"); - LeeModel.getPhase(Phi); - Array& PhaseData = visData[0].vars[0]->data; - fillData.copy(Phi,PhaseData); + + if (vis_db->getWithDefault("save_phase", true)) { + ASSERT(visData[0].vars[0]->name == "Phase"); + LeeModel.getPhase(Phi); + Array &PhaseData = visData[0].vars[0]->data; + fillData.copy(Phi, PhaseData); } - if (vis_db->getWithDefault( "save_potential", true )){ - ASSERT(visData[0].vars[1]->name=="Pressure"); - LeeModel.getPotential(Pressure, ChemicalPotential); - Array& PressureData = visData[0].vars[1]->data; - fillData.copy(Pressure,PressureData); - - ASSERT(visData[0].vars[2]->name=="ChemicalPotential"); - Array& ChemicalPotentialData = visData[0].vars[2]->data; - fillData.copy(ChemicalPotential,ChemicalPotentialData); - } - - if (vis_db->getWithDefault( "save_velocity", false )){ - ASSERT(visData[0].vars[3]->name=="Velocity_x"); - ASSERT(visData[0].vars[4]->name=="Velocity_y"); - ASSERT(visData[0].vars[5]->name=="Velocity_z"); - LeeModel.getVelocity(Vel_x,Vel_y,Vel_z); - Array& VelxData = visData[0].vars[3]->data; - Array& VelyData = visData[0].vars[4]->data; - Array& VelzData = visData[0].vars[5]->data; - fillData.copy(Vel_x,VelxData); - fillData.copy(Vel_y,VelyData); - fillData.copy(Vel_z,VelzData); - } - - if (vis_db->getWithDefault( "write_silo", true )) - IO::writeData( timestep, visData, Dm->Comm ); + if (vis_db->getWithDefault("save_potential", true)) { + ASSERT(visData[0].vars[1]->name == "Pressure"); + LeeModel.getPotential(Pressure, ChemicalPotential); + Array &PressureData = visData[0].vars[1]->data; + fillData.copy(Pressure, PressureData); -/* if (vis_db->getWithDefault( "save_8bit_raw", true )){ + ASSERT(visData[0].vars[2]->name == "ChemicalPotential"); + Array &ChemicalPotentialData = visData[0].vars[2]->data; + fillData.copy(ChemicalPotential, ChemicalPotentialData); + } + + if (vis_db->getWithDefault("save_velocity", false)) { + ASSERT(visData[0].vars[3]->name == "Velocity_x"); + ASSERT(visData[0].vars[4]->name == "Velocity_y"); + ASSERT(visData[0].vars[5]->name == "Velocity_z"); + LeeModel.getVelocity(Vel_x, Vel_y, Vel_z); + Array &VelxData = visData[0].vars[3]->data; + Array &VelyData = visData[0].vars[4]->data; + Array &VelzData = visData[0].vars[5]->data; + fillData.copy(Vel_x, VelxData); + fillData.copy(Vel_y, VelyData); + fillData.copy(Vel_z, VelzData); + } + + if (vis_db->getWithDefault("write_silo", true)) + IO::writeData(timestep, visData, Dm->Comm); + + /* if (vis_db->getWithDefault( "save_8bit_raw", true )){ char CurrentIDFilename[40]; sprintf(CurrentIDFilename,"id_t%d.raw",timestep); Averages.AggregateLabels(CurrentIDFilename); } -*/ +*/ } diff --git a/analysis/FreeEnergy.h b/analysis/FreeEnergy.h index 75e6aef6..bd394358 100644 --- a/analysis/FreeEnergy.h +++ b/analysis/FreeEnergy.h @@ -27,36 +27,36 @@ * */ -class FreeEnergyAnalyzer{ +class FreeEnergyAnalyzer { public: - std::shared_ptr Dm; - double Volume; - // input variables - double rho_n, rho_w; - double nu_n, nu_w; - double gamma_wn, beta; - double Fx, Fy, Fz; + std::shared_ptr Dm; + double Volume; + // input variables + double rho_n, rho_w; + double nu_n, nu_w; + double gamma_wn, beta; + double Fx, Fy, Fz; //........................................................................... - int Nx,Ny,Nz; - DoubleArray Rho; - DoubleArray Phi; - DoubleArray ChemicalPotential; - DoubleArray Pressure; - DoubleArray Vel_x; - DoubleArray Vel_y; - DoubleArray Vel_z; - DoubleArray SDs; + int Nx, Ny, Nz; + DoubleArray Rho; + DoubleArray Phi; + DoubleArray ChemicalPotential; + DoubleArray Pressure; + DoubleArray Vel_x; + DoubleArray Vel_y; + DoubleArray Vel_z; + DoubleArray SDs; - FreeEnergyAnalyzer(std::shared_ptr Dm); - ~FreeEnergyAnalyzer(); - - void SetParams(); - void Basic( ScaLBL_FreeLeeModel &LeeModel, int timestep); - void WriteVis( ScaLBL_FreeLeeModel &LeeModel, std::shared_ptr input_db, int timestep); + FreeEnergyAnalyzer(std::shared_ptr Dm); + ~FreeEnergyAnalyzer(); + + void SetParams(); + void Basic(ScaLBL_FreeLeeModel &LeeModel, int timestep); + void WriteVis(ScaLBL_FreeLeeModel &LeeModel, + std::shared_ptr input_db, int timestep); private: - FILE *TIMELOG; + FILE *TIMELOG; }; #endif - diff --git a/analysis/GreyPhase.cpp b/analysis/GreyPhase.cpp index 011d9b6a..8c1d66b3 100644 --- a/analysis/GreyPhase.cpp +++ b/analysis/GreyPhase.cpp @@ -1,206 +1,234 @@ #include "analysis/GreyPhase.h" // Constructor -GreyPhaseAnalysis::GreyPhaseAnalysis(std::shared_ptr dm): - Dm(dm) -{ - Nx=dm->Nx; Ny=dm->Ny; Nz=dm->Nz; - Volume=(Nx-2)*(Ny-2)*(Nz-2)*Dm->nprocx()*Dm->nprocy()*Dm->nprocz()*1.0; - - // Global arrays - SDs.resize(Nx,Ny,Nz); SDs.fill(0); - Porosity.resize(Nx,Ny,Nz); Porosity.fill(0); - //PhaseID.resize(Nx,Ny,Nz); PhaseID.fill(0); - Rho_n.resize(Nx,Ny,Nz); Rho_n.fill(0); - Rho_w.resize(Nx,Ny,Nz); Rho_w.fill(0); - Pressure.resize(Nx,Ny,Nz); Pressure.fill(0); - //Phi.resize(Nx,Ny,Nz); Phi.fill(0); - //DelPhi.resize(Nx,Ny,Nz); DelPhi.fill(0); - Vel_x.resize(Nx,Ny,Nz); Vel_x.fill(0); // Gradient of the phase indicator field - Vel_y.resize(Nx,Ny,Nz); Vel_y.fill(0); - Vel_z.resize(Nx,Ny,Nz); Vel_z.fill(0); - MobilityRatio.resize(Nx,Ny,Nz); MobilityRatio.fill(0); - //......................................... - - if (Dm->rank()==0){ - bool WriteHeader=false; - TIMELOG = fopen("timelog.csv","r"); - if (TIMELOG != NULL) - fclose(TIMELOG); - else - WriteHeader=true; +GreyPhaseAnalysis::GreyPhaseAnalysis(std::shared_ptr dm) : Dm(dm) { + Nx = dm->Nx; + Ny = dm->Ny; + Nz = dm->Nz; + Volume = (Nx - 2) * (Ny - 2) * (Nz - 2) * Dm->nprocx() * Dm->nprocy() * + Dm->nprocz() * 1.0; - TIMELOG = fopen("timelog.csv","a+"); - if (WriteHeader) - { - // If timelog is empty, write a short header to list the averages - //fprintf(TIMELOG,"--------------------------------------------------------------------------------------\n"); - fprintf(TIMELOG,"sw krw krn vw vn pw pn\n"); - } - } + // Global arrays + SDs.resize(Nx, Ny, Nz); + SDs.fill(0); + Porosity.resize(Nx, Ny, Nz); + Porosity.fill(0); + //PhaseID.resize(Nx,Ny,Nz); PhaseID.fill(0); + Rho_n.resize(Nx, Ny, Nz); + Rho_n.fill(0); + Rho_w.resize(Nx, Ny, Nz); + Rho_w.fill(0); + Pressure.resize(Nx, Ny, Nz); + Pressure.fill(0); + //Phi.resize(Nx,Ny,Nz); Phi.fill(0); + //DelPhi.resize(Nx,Ny,Nz); DelPhi.fill(0); + Vel_x.resize(Nx, Ny, Nz); + Vel_x.fill(0); // Gradient of the phase indicator field + Vel_y.resize(Nx, Ny, Nz); + Vel_y.fill(0); + Vel_z.resize(Nx, Ny, Nz); + Vel_z.fill(0); + MobilityRatio.resize(Nx, Ny, Nz); + MobilityRatio.fill(0); + //......................................... + + if (Dm->rank() == 0) { + bool WriteHeader = false; + TIMELOG = fopen("timelog.csv", "r"); + if (TIMELOG != NULL) + fclose(TIMELOG); + else + WriteHeader = true; + + TIMELOG = fopen("timelog.csv", "a+"); + if (WriteHeader) { + // If timelog is empty, write a short header to list the averages + //fprintf(TIMELOG,"--------------------------------------------------------------------------------------\n"); + fprintf(TIMELOG, "sw krw krn vw vn pw pn\n"); + } + } } - // Destructor -GreyPhaseAnalysis::~GreyPhaseAnalysis() -{ +GreyPhaseAnalysis::~GreyPhaseAnalysis() {} -} +void GreyPhaseAnalysis::Write(int timestep) {} -void GreyPhaseAnalysis::Write(int timestep) -{ - -} - -void GreyPhaseAnalysis::SetParams(double rhoA, double rhoB, double tauA, double tauB, double force_x, double force_y, double force_z, double alpha, double B, double GreyPorosity) -{ - Fx = force_x; - Fy = force_y; - Fz = force_z; - rho_n = rhoA; - rho_w = rhoB; - nu_n = (tauA-0.5)/3.f; - nu_w = (tauB-0.5)/3.f; - gamma_wn = 6.0*alpha; - beta = B; +void GreyPhaseAnalysis::SetParams(double rhoA, double rhoB, double tauA, + double tauB, double force_x, double force_y, + double force_z, double alpha, double B, + double GreyPorosity) { + Fx = force_x; + Fy = force_y; + Fz = force_z; + rho_n = rhoA; + rho_w = rhoB; + nu_n = (tauA - 0.5) / 3.f; + nu_w = (tauB - 0.5) / 3.f; + gamma_wn = 6.0 * alpha; + beta = B; grey_porosity = GreyPorosity; } -void GreyPhaseAnalysis::Basic(){ - int i,j,k,n,imin,jmin,kmin,kmax; +void GreyPhaseAnalysis::Basic() { + int i, j, k, n, imin, jmin, kmin, kmax; - // If external boundary conditions are set, do not average over the inlet - kmin=1; kmax=Nz-1; - imin=jmin=1; - if (Dm->inlet_layers_z > 0 && Dm->kproc() == 0) kmin += Dm->inlet_layers_z; - if (Dm->outlet_layers_z > 0 && Dm->kproc() == Dm->nprocz()-1) kmax -= Dm->outlet_layers_z; + // If external boundary conditions are set, do not average over the inlet + kmin = 1; + kmax = Nz - 1; + imin = jmin = 1; + if (Dm->inlet_layers_z > 0 && Dm->kproc() == 0) + kmin += Dm->inlet_layers_z; + if (Dm->outlet_layers_z > 0 && Dm->kproc() == Dm->nprocz() - 1) + kmax -= Dm->outlet_layers_z; - Water_local.reset(); - Oil_local.reset(); - double count_w = 0.0; - double count_n = 0.0; - for (k=kmin; kid[n] > 0 ){ - // compute density - double nA = Rho_n(n); - double nB = Rho_w(n); - double phi = (nA-nB)/(nA+nB); - double porosity = Porosity(n); - double mobility_ratio = MobilityRatio(n); - - Water_local.M += nB*porosity; - Water_local.Px += porosity*(nA+nB)*Vel_x(n)*0.5*(1.0-mobility_ratio); - Water_local.Py += porosity*(nA+nB)*Vel_y(n)*0.5*(1.0-mobility_ratio); - Water_local.Pz += porosity*(nA+nB)*Vel_z(n)*0.5*(1.0-mobility_ratio); - - Oil_local.M += nA*porosity; - Oil_local.Px += porosity*(nA+nB)*Vel_x(n)*0.5*(1.0+mobility_ratio); - Oil_local.Py += porosity*(nA+nB)*Vel_y(n)*0.5*(1.0+mobility_ratio); - Oil_local.Pz += porosity*(nA+nB)*Vel_z(n)*0.5*(1.0+mobility_ratio); + Water_local.reset(); + Oil_local.reset(); + double count_w = 0.0; + double count_n = 0.0; + for (k = kmin; k < kmax; k++) { + for (j = jmin; j < Ny - 1; j++) { + for (i = imin; i < Nx - 1; i++) { + n = k * Nx * Ny + j * Nx + i; + // Compute volume averages + if (Dm->id[n] > 0) { + // compute density + double nA = Rho_n(n); + double nB = Rho_w(n); + double phi = (nA - nB) / (nA + nB); + double porosity = Porosity(n); + double mobility_ratio = MobilityRatio(n); - if ( phi > 0.99 ){ - Oil_local.p += Pressure(n); - //Oil_local.p += pressure*(rho_n*nA)/(rho_n*nA+rho_w*nB); - count_n += 1.0; - } - else if ( phi < -0.99 ){ - Water_local.p += Pressure(n); - //Water_local.p += pressure*(rho_w*nB)/(rho_n*nA+rho_w*nB); - count_w += 1.0; - } - } - } - } - } - Oil.M=Dm->Comm.sumReduce( Oil_local.M); - Oil.Px=Dm->Comm.sumReduce( Oil_local.Px); - Oil.Py=Dm->Comm.sumReduce( Oil_local.Py); - Oil.Pz=Dm->Comm.sumReduce( Oil_local.Pz); - - Water.M=Dm->Comm.sumReduce( Water_local.M); - Water.Px=Dm->Comm.sumReduce( Water_local.Px); - Water.Py=Dm->Comm.sumReduce( Water_local.Py); - Water.Pz=Dm->Comm.sumReduce( Water_local.Pz); + Water_local.M += nB * porosity; + Water_local.Px += porosity * (nA + nB) * Vel_x(n) * 0.5 * + (1.0 - mobility_ratio); + Water_local.Py += porosity * (nA + nB) * Vel_y(n) * 0.5 * + (1.0 - mobility_ratio); + Water_local.Pz += porosity * (nA + nB) * Vel_z(n) * 0.5 * + (1.0 - mobility_ratio); + Oil_local.M += nA * porosity; + Oil_local.Px += porosity * (nA + nB) * Vel_x(n) * 0.5 * + (1.0 + mobility_ratio); + Oil_local.Py += porosity * (nA + nB) * Vel_y(n) * 0.5 * + (1.0 + mobility_ratio); + Oil_local.Pz += porosity * (nA + nB) * Vel_z(n) * 0.5 * + (1.0 + mobility_ratio); - //Oil.p /= Oil.M; - //Water.p /= Water.M; - count_w=Dm->Comm.sumReduce( count_w); - count_n=Dm->Comm.sumReduce( count_n); - if (count_w > 0.0) - Water.p=Dm->Comm.sumReduce( Water_local.p) / count_w; - else - Water.p = 0.0; - if (count_n > 0.0) - Oil.p=Dm->Comm.sumReduce( Oil_local.p) / count_n; - else - Oil.p = 0.0; + if (phi > 0.99) { + Oil_local.p += Pressure(n); + //Oil_local.p += pressure*(rho_n*nA)/(rho_n*nA+rho_w*nB); + count_n += 1.0; + } else if (phi < -0.99) { + Water_local.p += Pressure(n); + //Water_local.p += pressure*(rho_w*nB)/(rho_n*nA+rho_w*nB); + count_w += 1.0; + } + } + } + } + } + Oil.M = Dm->Comm.sumReduce(Oil_local.M); + Oil.Px = Dm->Comm.sumReduce(Oil_local.Px); + Oil.Py = Dm->Comm.sumReduce(Oil_local.Py); + Oil.Pz = Dm->Comm.sumReduce(Oil_local.Pz); - // check for NaN - bool err=false; - if (Water.M != Water.M) err=true; - if (Water.p != Water.p) err=true; - if (Water.Px != Water.Px) err=true; - if (Water.Py != Water.Py) err=true; - if (Water.Pz != Water.Pz) err=true; + Water.M = Dm->Comm.sumReduce(Water_local.M); + Water.Px = Dm->Comm.sumReduce(Water_local.Px); + Water.Py = Dm->Comm.sumReduce(Water_local.Py); + Water.Pz = Dm->Comm.sumReduce(Water_local.Pz); - if (Oil.M != Oil.M) err=true; - if (Oil.p != Oil.p) err=true; - if (Oil.Px != Oil.Px) err=true; - if (Oil.Py != Oil.Py) err=true; - if (Oil.Pz != Oil.Pz) err=true; - - if (Dm->rank() == 0){ - double force_mag = sqrt(Fx*Fx+Fy*Fy+Fz*Fz); - double dir_x = 0.0; - double dir_y = 0.0; - double dir_z = 0.0; - if (force_mag > 0.0){ - dir_x = Fx/force_mag; - dir_y = Fy/force_mag; - dir_z = Fz/force_mag; - } - else { - // default to z direction - dir_x = 0.0; - dir_y = 0.0; - dir_z = 1.0; - } - if (Dm->BoundaryCondition == 1 || Dm->BoundaryCondition == 2 || Dm->BoundaryCondition == 3 || Dm->BoundaryCondition == 4 ){ - // compute the pressure drop - double pressure_drop = (Pressure(Nx*Ny + Nx + 1) - 1.0) / 3.0; - double length = ((Nz-2)*Dm->nprocz()); - force_mag -= pressure_drop/length; - } - if (force_mag == 0.0){ - // default to z direction - dir_x = 0.0; - dir_y = 0.0; - dir_z = 1.0; - force_mag = 1.0; - } - saturation=Water.M/(Water.M + Oil.M); // assume constant density - water_flow_rate=grey_porosity*saturation*(Water.Px*dir_x + Water.Py*dir_y + Water.Pz*dir_z)/Water.M; - oil_flow_rate =grey_porosity*(1.0-saturation)*(Oil.Px*dir_x + Oil.Py*dir_y + Oil.Pz*dir_z)/Oil.M; + //Oil.p /= Oil.M; + //Water.p /= Water.M; + count_w = Dm->Comm.sumReduce(count_w); + count_n = Dm->Comm.sumReduce(count_n); + if (count_w > 0.0) + Water.p = Dm->Comm.sumReduce(Water_local.p) / count_w; + else + Water.p = 0.0; + if (count_n > 0.0) + Oil.p = Dm->Comm.sumReduce(Oil_local.p) / count_n; + else + Oil.p = 0.0; - double h = Dm->voxel_length; + // check for NaN + bool err = false; + if (Water.M != Water.M) + err = true; + if (Water.p != Water.p) + err = true; + if (Water.Px != Water.Px) + err = true; + if (Water.Py != Water.Py) + err = true; + if (Water.Pz != Water.Pz) + err = true; + + if (Oil.M != Oil.M) + err = true; + if (Oil.p != Oil.p) + err = true; + if (Oil.Px != Oil.Px) + err = true; + if (Oil.Py != Oil.Py) + err = true; + if (Oil.Pz != Oil.Pz) + err = true; + + if (Dm->rank() == 0) { + double force_mag = sqrt(Fx * Fx + Fy * Fy + Fz * Fz); + double dir_x = 0.0; + double dir_y = 0.0; + double dir_z = 0.0; + if (force_mag > 0.0) { + dir_x = Fx / force_mag; + dir_y = Fy / force_mag; + dir_z = Fz / force_mag; + } else { + // default to z direction + dir_x = 0.0; + dir_y = 0.0; + dir_z = 1.0; + } + if (Dm->BoundaryCondition == 1 || Dm->BoundaryCondition == 2 || + Dm->BoundaryCondition == 3 || Dm->BoundaryCondition == 4) { + // compute the pressure drop + double pressure_drop = (Pressure(Nx * Ny + Nx + 1) - 1.0) / 3.0; + double length = ((Nz - 2) * Dm->nprocz()); + force_mag -= pressure_drop / length; + } + if (force_mag == 0.0) { + // default to z direction + dir_x = 0.0; + dir_y = 0.0; + dir_z = 1.0; + force_mag = 1.0; + } + saturation = Water.M / (Water.M + Oil.M); // assume constant density + water_flow_rate = + grey_porosity * saturation * + (Water.Px * dir_x + Water.Py * dir_y + Water.Pz * dir_z) / Water.M; + oil_flow_rate = grey_porosity * (1.0 - saturation) * + (Oil.Px * dir_x + Oil.Py * dir_y + Oil.Pz * dir_z) / + Oil.M; + + double h = Dm->voxel_length; //TODO check if need greyporosity or domain porosity ? - compare to analytical solution - double krn = h*h*nu_n*oil_flow_rate / force_mag ; - double krw = h*h*nu_w*water_flow_rate / force_mag; - //printf(" water saturation = %f, fractional flow =%f \n",saturation,fractional_flow); - fprintf(TIMELOG,"%.5g %.5g %.5g %.5g %.5g %.5g %.5g\n",saturation,krw,krn,h*water_flow_rate,h*oil_flow_rate, Water.p, Oil.p); - fflush(TIMELOG); - } + double krn = h * h * nu_n * oil_flow_rate / force_mag; + double krw = h * h * nu_w * water_flow_rate / force_mag; + //printf(" water saturation = %f, fractional flow =%f \n",saturation,fractional_flow); + fprintf(TIMELOG, "%.5g %.5g %.5g %.5g %.5g %.5g %.5g\n", saturation, + krw, krn, h * water_flow_rate, h * oil_flow_rate, Water.p, + Oil.p); + fflush(TIMELOG); + } - if (err==true){ - // exception if simulation produceds NaN - printf("GreyPhaseAnalysis.cpp: NaN encountered, may need to check simulation parameters \n"); - } - ASSERT(err==false); + if (err == true) { + // exception if simulation produceds NaN + printf("GreyPhaseAnalysis.cpp: NaN encountered, may need to check " + "simulation parameters \n"); + } + ASSERT(err == false); } /* inline void InterfaceTransportMeasures( double beta, double rA, double rB, double nA, double nB, diff --git a/analysis/GreyPhase.h b/analysis/GreyPhase.h index eb8724fb..a9a72723 100644 --- a/analysis/GreyPhase.h +++ b/analysis/GreyPhase.h @@ -15,7 +15,6 @@ #include "IO/Reader.h" #include "IO/Writer.h" - /** * \class GreyPhase * @@ -23,18 +22,15 @@ * The GreyPhase class tracks pressure, mass and momentum within a grey phase * */ -class GreyPhase{ - public: - double p; - double M,Px,Py,Pz; - void reset(){ - p=M=Px=Py=Pz=0.0; - } +class GreyPhase { +public: + double p; + double M, Px, Py, Pz; + void reset() { p = M = Px = Py = Pz = 0.0; } - private: +private: }; - /** * \class GreyPhaseAnalysis * @@ -42,47 +38,48 @@ class GreyPhase{ * The GreyPhaseAnalysis class is constructed to analyze the LBPM greyscale model * */ -class GreyPhaseAnalysis{ +class GreyPhaseAnalysis { public: - std::shared_ptr Dm; - double Volume; - // input variables - double rho_n, rho_w; - double nu_n, nu_w; - double gamma_wn, beta; - double Fx, Fy, Fz; + std::shared_ptr Dm; + double Volume; + // input variables + double rho_n, rho_w; + double nu_n, nu_w; + double gamma_wn, beta; + double Fx, Fy, Fz; double grey_porosity; - // outputs - double saturation,water_flow_rate, oil_flow_rate; + // outputs + double saturation, water_flow_rate, oil_flow_rate; - //simulation outputs (averaged values) - GreyPhase Water, Oil; - GreyPhase Water_local, Oil_local; - //........................................................................... - int Nx,Ny,Nz; - //IntArray PhaseID; // Phase ID array - DoubleArray SDs; // contains porosity map - DoubleArray Porosity; // contains porosity map - DoubleArray Rho_n; // density field - DoubleArray Rho_w; // density field - //DoubleArray Phi; // phase indicator field - //DoubleArray DelPhi; // Magnitude of Gradient of the phase indicator field - DoubleArray Pressure; // pressure field - DoubleArray Vel_x; // velocity field - DoubleArray Vel_y; - DoubleArray Vel_z; - DoubleArray MobilityRatio; + //simulation outputs (averaged values) + GreyPhase Water, Oil; + GreyPhase Water_local, Oil_local; + //........................................................................... + int Nx, Ny, Nz; + //IntArray PhaseID; // Phase ID array + DoubleArray SDs; // contains porosity map + DoubleArray Porosity; // contains porosity map + DoubleArray Rho_n; // density field + DoubleArray Rho_w; // density field + //DoubleArray Phi; // phase indicator field + //DoubleArray DelPhi; // Magnitude of Gradient of the phase indicator field + DoubleArray Pressure; // pressure field + DoubleArray Vel_x; // velocity field + DoubleArray Vel_y; + DoubleArray Vel_z; + DoubleArray MobilityRatio; - GreyPhaseAnalysis(std::shared_ptr Dm); - ~GreyPhaseAnalysis(); - - void SetParams(double rhoA, double rhoB, double tauA, double tauB, double force_x, double force_y, double force_z, double alpha, double beta, double GreyPorosity); - void Basic(); - void Write(int time); + GreyPhaseAnalysis(std::shared_ptr Dm); + ~GreyPhaseAnalysis(); + + void SetParams(double rhoA, double rhoB, double tauA, double tauB, + double force_x, double force_y, double force_z, double alpha, + double beta, double GreyPorosity); + void Basic(); + void Write(int time); private: - FILE *TIMELOG; + FILE *TIMELOG; }; #endif - diff --git a/analysis/Minkowski.cpp b/analysis/Minkowski.cpp index aca2620d..938828f8 100644 --- a/analysis/Minkowski.cpp +++ b/analysis/Minkowski.cpp @@ -29,282 +29,278 @@ #include - #define PI 3.14159265359 // Constructor -Minkowski::Minkowski(std::shared_ptr dm): - kstart(0), kfinish(0), isovalue(0), Volume(0), - LOGFILE(NULL), Dm(dm), Vi(0), Vi_global(0) -{ - Nx=dm->Nx; Ny=dm->Ny; Nz=dm->Nz; - Volume=double((Nx-2)*(Ny-2)*(Nz-2))*double(Dm->nprocx()*Dm->nprocy()*Dm->nprocz()); - - id.resize(Nx,Ny,Nz); id.fill(0); - label.resize(Nx,Ny,Nz); label.fill(0); - distance.resize(Nx,Ny,Nz); distance.fill(0); +Minkowski::Minkowski(std::shared_ptr dm) + : kstart(0), kfinish(0), isovalue(0), Volume(0), LOGFILE(NULL), Dm(dm), + Vi(0), Vi_global(0) { + Nx = dm->Nx; + Ny = dm->Ny; + Nz = dm->Nz; + Volume = double((Nx - 2) * (Ny - 2) * (Nz - 2)) * + double(Dm->nprocx() * Dm->nprocy() * Dm->nprocz()); - if (Dm->rank()==0){ - LOGFILE = fopen("minkowski.csv","a+"); - if (fseek(LOGFILE,0,SEEK_SET) == fseek(LOGFILE,0,SEEK_CUR)) - { - // If LOGFILE is empty, write a short header to list the averages - //fprintf(LOGFILE,"--------------------------------------------------------------------------------------\n"); - fprintf(LOGFILE,"Vn An Jn Xn\n"); //miknowski measures, - } - } + id.resize(Nx, Ny, Nz); + id.fill(0); + label.resize(Nx, Ny, Nz); + label.fill(0); + distance.resize(Nx, Ny, Nz); + distance.fill(0); + + if (Dm->rank() == 0) { + LOGFILE = fopen("minkowski.csv", "a+"); + if (fseek(LOGFILE, 0, SEEK_SET) == fseek(LOGFILE, 0, SEEK_CUR)) { + // If LOGFILE is empty, write a short header to list the averages + //fprintf(LOGFILE,"--------------------------------------------------------------------------------------\n"); + fprintf(LOGFILE, "Vn An Jn Xn\n"); //miknowski measures, + } + } } - // Destructor -Minkowski::~Minkowski() -{ - if ( LOGFILE!=NULL ) { fclose(LOGFILE); } +Minkowski::~Minkowski() { + if (LOGFILE != NULL) { + fclose(LOGFILE); + } } -void Minkowski::ComputeScalar(const DoubleArray& Field, const double isovalue) -{ +void Minkowski::ComputeScalar(const DoubleArray &Field, const double isovalue) { PROFILE_START("ComputeScalar"); - Xi = Ji = Ai = 0.0; - DCEL object; - int e1,e2,e3; - double s,s1,s2,s3; - double a1,a2,a3; - //double Vx,Vy,Vz,Wx,Wy,Wz,nx,ny,nz,norm; - //int Nx = Field.size(0); - //int Ny = Field.size(1); - //int Nz = Field.size(2); - for (int k=1; k 0) EulerChar = (0.25*nvert - nside_intern - 0.5*nside_extern + nface); */ - } - } - } - // Voxel counting for volume fraction - Vi = 0.f; - for (int k=1; kComm.barrier(); - // Phase averages - Vi_global = Dm->Comm.sumReduce( Vi ); - Xi_global = Dm->Comm.sumReduce( Xi ); - Ai_global = Dm->Comm.sumReduce( Ai ); - Ji_global = Dm->Comm.sumReduce( Ji ); - Dm->Comm.barrier(); + } + } + } + // Voxel counting for volume fraction + Vi = 0.f; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + if (Field(i, j, k) < isovalue) { + Vi += 1.0; + } + } + } + } + // convert X for 2D manifold to 3D object + Xi *= 0.5; + + Dm->Comm.barrier(); + // Phase averages + Vi_global = Dm->Comm.sumReduce(Vi); + Xi_global = Dm->Comm.sumReduce(Xi); + Ai_global = Dm->Comm.sumReduce(Ai); + Ji_global = Dm->Comm.sumReduce(Ji); + Dm->Comm.barrier(); PROFILE_STOP("ComputeScalar"); } - -void Minkowski::MeasureObject(){ - /* +void Minkowski::MeasureObject() { + /* * compute the distance to an object * * THIS ALGORITHM ASSUMES THAT id() is populated with phase id to distinguish objects * 0 - labels the object * 1 - labels the rest of the */ - //DoubleArray smooth_distance(Nx,Ny,Nz); - for (int k=0; k -2.5) { - double new_distance = factor*log((1.0+value)/(1.0-value)); - if (dist_value*new_distance < 0.0 ) - new_distance = (-1.0)*new_distance; - distance(i,j,k) = new_distance; - } - } - } - } - - ComputeScalar(distance,0.0); + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + distance(i, j, k) = 2.0 * double(id(i, j, k)) - 1.0; + } + } + } + CalcDist(distance, id, *Dm); + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + double value = Phi(i, j, k); + double dist_value = distance(i, j, k); + if (dist_value < 2.5 && dist_value > -2.5) { + double new_distance = + factor * log((1.0 + value) / (1.0 - value)); + if (dist_value * new_distance < 0.0) + new_distance = (-1.0) * new_distance; + distance(i, j, k) = new_distance; + } + } + } + } + + ComputeScalar(distance, 0.0); } - -int Minkowski::MeasureConnectedPathway(){ - /* +int Minkowski::MeasureConnectedPathway() { + /* * compute the connected pathway for object with LABEL in id field * compute the labels for connected components * compute the distance to the connected pathway * * THIS ALGORITHM ASSUMES THAT id() is populated with phase id to distinguish objects */ - - char LABEL = 0; - for (int k=0; krank_info,distance,distance,vF,vF,label,Dm->Comm); -// int n_connected_components = ComputeGlobalPhaseComponent(Nx-2,Ny-2,Nz-2,Dm->rank_info,const IntArray &PhaseID, int &VALUE, BlobIDArray &GlobalBlobID, Dm->Comm ) - Dm->Comm.barrier(); - - for (int k=0; krank_info, distance, + distance, vF, vF, label, Dm->Comm); + // int n_connected_components = ComputeGlobalPhaseComponent(Nx-2,Ny-2,Nz-2,Dm->rank_info,const IntArray &PhaseID, int &VALUE, BlobIDArray &GlobalBlobID, Dm->Comm ) + Dm->Comm.barrier(); + + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + if (label(i, j, k) == 0) { + id(i, j, k) = 0; + } else { + id(i, j, k) = 1; + } + } + } + } + MeasureObject(); + return n_connected_components; } -int Minkowski::MeasureConnectedPathway(double factor, const DoubleArray &Phi){ - /* +int Minkowski::MeasureConnectedPathway(double factor, const DoubleArray &Phi) { + /* * compute the connected pathway for object with LABEL in id field * compute the labels for connected components * compute the distance to the connected pathway * * THIS ALGORITHM ASSUMES THAT id() is populated with phase id to distinguish objects */ - - char LABEL = 0; - for (int k=0; krank_info,distance,distance,vF,vF,label,Dm->Comm); -// int n_connected_components = ComputeGlobalPhaseComponent(Nx-2,Ny-2,Nz-2,Dm->rank_info,const IntArray &PhaseID, int &VALUE, BlobIDArray &GlobalBlobID, Dm->Comm ) - Dm->Comm.barrier(); - - for (int k=0; krank_info, distance, + distance, vF, vF, label, Dm->Comm); + // int n_connected_components = ComputeGlobalPhaseComponent(Nx-2,Ny-2,Nz-2,Dm->rank_info,const IntArray &PhaseID, int &VALUE, BlobIDArray &GlobalBlobID, Dm->Comm ) + Dm->Comm.barrier(); + + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + if (label(i, j, k) == 0) { + id(i, j, k) = 0; + } else { + id(i, j, k) = 1; + } + } + } + } + MeasureObject(factor, Phi); + return n_connected_components; } - -void Minkowski::PrintAll() -{ - if (Dm->rank()==0){ - fprintf(LOGFILE,"%.5g %.5g %.5g %.5g\n",Vi_global, Ai_global, Ji_global, Xi_global); // minkowski measures - fflush(LOGFILE); - } +void Minkowski::PrintAll() { + if (Dm->rank() == 0) { + fprintf(LOGFILE, "%.5g %.5g %.5g %.5g\n", Vi_global, Ai_global, + Ji_global, Xi_global); // minkowski measures + fflush(LOGFILE); + } } - diff --git a/analysis/Minkowski.h b/analysis/Minkowski.h index 0909f4e1..9ecd324a 100644 --- a/analysis/Minkowski.h +++ b/analysis/Minkowski.h @@ -42,60 +42,51 @@ * */ +class Minkowski { + //........................................................................... + int kstart, kfinish; -class Minkowski{ - //........................................................................... - int kstart,kfinish; + double isovalue; + double Volume; - double isovalue; - double Volume; - - // CSV / text file where time history of averages is saved - FILE *LOGFILE; + // CSV / text file where time history of averages is saved + FILE *LOGFILE; public: - //........................................................................... - std::shared_ptr Dm; - Array id; - Array label; - Array distance; - //........................................................................... - // Averaging variables - //........................................................................... - // local averages (to each MPI process) - double Ai,Ji,Xi,Vi; - // Global averages (all processes) - double Ai_global,Ji_global,Xi_global,Vi_global; - int n_connected_components; - //........................................................................... - int Nx,Ny,Nz; + //........................................................................... + std::shared_ptr Dm; + Array id; + Array label; + Array distance; + //........................................................................... + // Averaging variables + //........................................................................... + // local averages (to each MPI process) + double Ai, Ji, Xi, Vi; + // Global averages (all processes) + double Ai_global, Ji_global, Xi_global, Vi_global; + int n_connected_components; + //........................................................................... + int Nx, Ny, Nz; - double V(){ - return Vi; - } - double A(){ - return Ai; - } - double H(){ - return Ji; - } - double X(){ - return Xi; - } - - //.......................................................................... + double V() { return Vi; } + double A() { return Ai; } + double H() { return Ji; } + double X() { return Xi; } + + //.......................................................................... /** * \brief Null constructor */ - Minkowski(){};//NULL CONSTRUCTOR - + Minkowski(){}; //NULL CONSTRUCTOR + /** * \brief Constructor based on an existing Domain * @param Dm - Domain structure */ - Minkowski(std::shared_ptr Dm); - ~Minkowski(); - + Minkowski(std::shared_ptr Dm); + ~Minkowski(); + /** * \brief Compute scalar minkowski functionals * step 1. compute the distance to an object @@ -104,11 +95,11 @@ public: * THIS ALGORITHM ASSUMES THAT id() is populated with phase id to distinguish objects * 0 - labels the object * 1 - labels everything else - */ - void MeasureObject(); - - void MeasureObject(double factor, const DoubleArray &Phi); - + */ + void MeasureObject(); + + void MeasureObject(double factor, const DoubleArray &Phi); + /** * \details Compute scalar minkowski functionals for connected part of a structure * step 1. compute connected components and extract largest region by volume @@ -118,25 +109,23 @@ public: * THIS ALGORITHM ASSUMES THAT id() is populated with phase id to distinguish objects * 0 - labels the object * 1 - labels everything else - */ - int MeasureConnectedPathway(); + */ + int MeasureConnectedPathway(); + + int MeasureConnectedPathway(double factor, const DoubleArray &Phi); - int MeasureConnectedPathway(double factor, const DoubleArray &Phi); - /** * \brief Compute scalar minkowski functionals * \details Construct an isosurface and return the geometric invariants based on the triangulated list * @param isovalue - threshold value to use to determine iso-surface * @param Field - DoubleArray containing the field to threshold */ - void ComputeScalar(const DoubleArray& Field, const double isovalue); + void ComputeScalar(const DoubleArray &Field, const double isovalue); /** * \brief print the scalar invariants */ - void PrintAll(); - + void PrintAll(); }; #endif - diff --git a/analysis/PointList.h b/analysis/PointList.h index 89bd7107..21a813ac 100644 --- a/analysis/PointList.h +++ b/analysis/PointList.h @@ -21,25 +21,49 @@ struct LBPM_Point { LBPM_Point() : x(0.0), y(0.0), z(0.0) {} - LBPM_Point(double xv,double yv,double zv) : x(xv), y(yv), z(zv) {} - double x,y,z; + LBPM_Point(double xv, double yv, double zv) : x(xv), y(yv), z(zv) {} + double x, y, z; }; typedef LBPM_Point Point; -inline Point operator+(const Point &A,const Point &B) {return Point(A.x+B.x,A.y+B.y,A.z+B.z);} -inline Point operator-(const Point &A,const Point &B) {return Point(A.x-B.x,A.y-B.y,A.z-B.z);} -inline Point operator*(const Point &A,double v) {return Point(A.x*v,A.y*v,A.z*v);} -inline Point operator*(double v,const Point &A) {return Point(A.x*v,A.y*v,A.z*v);} -inline Point operator/(const Point &A,double v) {return Point(A.x/v,A.y/v,A.z/v);} -inline Point operator-(const Point &A) {return Point(-A.x,-A.y,-A.z);} +inline Point operator+(const Point &A, const Point &B) { + return Point(A.x + B.x, A.y + B.y, A.z + B.z); +} +inline Point operator-(const Point &A, const Point &B) { + return Point(A.x - B.x, A.y - B.y, A.z - B.z); +} +inline Point operator*(const Point &A, double v) { + return Point(A.x * v, A.y * v, A.z * v); +} +inline Point operator*(double v, const Point &A) { + return Point(A.x * v, A.y * v, A.z * v); +} +inline Point operator/(const Point &A, double v) { + return Point(A.x / v, A.y / v, A.z / v); +} +inline Point operator-(const Point &A) { return Point(-A.x, -A.y, -A.z); } -inline bool operator==(const Point &A,const Point &B) {return (A.x==B.x && A.y==B.y && A.z==B.z);} -inline bool operator!=(const Point &A,const Point &B) {return (A.x!=B.x || A.y!=B.y || A.z!=B.z);} +inline bool operator==(const Point &A, const Point &B) { + return (A.x == B.x && A.y == B.y && A.z == B.z); +} +inline bool operator!=(const Point &A, const Point &B) { + return (A.x != B.x || A.y != B.y || A.z != B.z); +} -inline double Norm(const Point &A) {return sqrt(A.x*A.x+A.y*A.y+A.z*A.z);} -inline Point Cross(const Point &A,const Point &B) {return Point(A.y*B.z-A.z*B.y,B.x*A.z-A.x*B.z,A.x*B.y-A.y*B.x);} -inline double Dot(const Point &A,const Point &B) {return (A.x*B.x+A.y*B.y+A.z*B.z);} -inline double Distance(const Point &A,const Point &B) {return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y)+(A.z-B.z)*(A.z-B.z));} +inline double Norm(const Point &A) { + return sqrt(A.x * A.x + A.y * A.y + A.z * A.z); +} +inline Point Cross(const Point &A, const Point &B) { + return Point(A.y * B.z - A.z * B.y, B.x * A.z - A.x * B.z, + A.x * B.y - A.y * B.x); +} +inline double Dot(const Point &A, const Point &B) { + return (A.x * B.x + A.y * B.y + A.z * B.z); +} +inline double Distance(const Point &A, const Point &B) { + return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + + (A.z - B.z) * (A.z - B.z)); +} /* class PointList{ @@ -104,25 +128,38 @@ PointList::~PointList() delete data; } */ -template -class DTList { +template class DTList { public: DTList() : Data(0), length(0), refCount(new size_t(1)), outOfRange() {} - DTList(const DTList &A) : Data(A.Data), length(A.length), refCount(A.refCount), outOfRange() {++(*refCount);} -protected: - DTList(size_t len) : Data(len<=0 ? 0 : new T[len]), length(len<=0 ? 0 : len), refCount(new size_t(1)), outOfRange() {} -public: - - virtual ~DTList() { - --(*refCount); - if (*refCount==0) {delete [] Data; delete refCount;} - Data = 0; refCount = 0; length=0; + DTList(const DTList &A) + : Data(A.Data), length(A.length), refCount(A.refCount), outOfRange() { + ++(*refCount); } - + +protected: + DTList(size_t len) + : Data(len <= 0 ? 0 : new T[len]), length(len <= 0 ? 0 : len), + refCount(new size_t(1)), outOfRange() {} + +public: + virtual ~DTList() { + --(*refCount); + if (*refCount == 0) { + delete[] Data; + delete refCount; + } + Data = 0; + refCount = 0; + length = 0; + } + DTList &operator=(const DTList &A) { - if (A.refCount!=refCount) { // Otherwise doing A=A. + if (A.refCount != refCount) { // Otherwise doing A=A. --(*refCount); - if (*refCount==0) {delete [] Data; delete refCount;} + if (*refCount == 0) { + delete[] Data; + delete refCount; + } refCount = A.refCount; ++(*refCount); length = A.length; @@ -130,62 +167,69 @@ public: } return *this; } - - size_t MemoryUsed(void) const {return length*sizeof(T);} - - const T *Pointer(void) const {return Data;} - - size_t IsEmpty(void) const {return (Data==0);} - size_t Length(void) const {return length;} - const T operator()(size_t i) const {return Data[i];} - + size_t MemoryUsed(void) const { return length * sizeof(T); } + + const T *Pointer(void) const { return Data; } + + size_t IsEmpty(void) const { return (Data == 0); } + size_t Length(void) const { return length; } + + const T operator()(size_t i) const { return Data[i]; } + protected: T *Data; size_t length; size_t *refCount; - + // Should be static. T outOfRange; }; -template -class DTMutableList : public DTList { +template class DTMutableList : public DTList { public: DTMutableList() : DTList() {} DTMutableList(size_t len) : DTList(len) {} DTMutableList(const DTMutableList &A) : DTList(A) {} - - DTMutableList &operator=(const DTMutableList &A) {DTList::operator=(A); return *this;} - - T *Pointer(void) {return DTList::Data;} - const T *Pointer(void) const {return DTList::Data;} - T &operator()(size_t i) {return DTList::Data[i];} - T operator()(size_t i) const {return DTList::Data[i];} - - DTMutableList &operator=(T v) {for (size_t i=0;i::length;i++) DTList::Data[i] = v; return *this;} + + DTMutableList &operator=(const DTMutableList &A) { + DTList::operator=(A); + return *this; + } + + T *Pointer(void) { return DTList::Data; } + const T *Pointer(void) const { return DTList::Data; } + T &operator()(size_t i) { return DTList::Data[i]; } + T operator()(size_t i) const { return DTList::Data[i]; } + + DTMutableList &operator=(T v) { + for (size_t i = 0; i < DTList::length; i++) + DTList::Data[i] = v; + return *this; + } }; -template DTMutableList TruncateSize(const DTList &A,size_t length) -{ - if (length>A.Length()) length = A.Length(); +template +DTMutableList TruncateSize(const DTList &A, size_t length) { + if (length > A.Length()) + length = A.Length(); DTMutableList toReturn(length); const T *fromP = A.Pointer(); T *toP = toReturn.Pointer(); - for (size_t i=0;i DTMutableList IncreaseSize(const DTList &A,size_t addLength) -{ - DTMutableList toReturn(A.Length()+(addLength>=0 ? addLength : 0)); +template +DTMutableList IncreaseSize(const DTList &A, size_t addLength) { + DTMutableList toReturn(A.Length() + (addLength >= 0 ? addLength : 0)); size_t len = A.Length(); const T *fromP = A.Pointer(); T *toP = toReturn.Pointer(); - for (size_t i=0;i dm): - Dm(dm) -{ - Nx=dm->Nx; Ny=dm->Ny; Nz=dm->Nz; - Volume=(Nx-2)*(Ny-2)*(Nz-2)*Dm->nprocx()*Dm->nprocy()*Dm->nprocz()*1.0; - - morph_w = std::shared_ptr(new Minkowski(Dm)); - morph_n = std::shared_ptr(new Minkowski(Dm)); - morph_i = std::shared_ptr(new Minkowski(Dm)); +SubPhase::SubPhase(std::shared_ptr dm) : Dm(dm) { + Nx = dm->Nx; + Ny = dm->Ny; + Nz = dm->Nz; + Volume = (Nx - 2) * (Ny - 2) * (Nz - 2) * Dm->nprocx() * Dm->nprocy() * + Dm->nprocz() * 1.0; - // Global arrays - PhaseID.resize(Nx,Ny,Nz); PhaseID.fill(0); - Label_WP.resize(Nx,Ny,Nz); Label_WP.fill(0); - Label_NWP.resize(Nx,Ny,Nz); Label_NWP.fill(0); - Rho_n.resize(Nx,Ny,Nz); Rho_n.fill(0); - Rho_w.resize(Nx,Ny,Nz); Rho_w.fill(0); - Pressure.resize(Nx,Ny,Nz); Pressure.fill(0); - Phi.resize(Nx,Ny,Nz); Phi.fill(0); - DelPhi.resize(Nx,Ny,Nz); DelPhi.fill(0); - Vel_x.resize(Nx,Ny,Nz); Vel_x.fill(0); // Gradient of the phase indicator field - Vel_y.resize(Nx,Ny,Nz); Vel_y.fill(0); - Vel_z.resize(Nx,Ny,Nz); Vel_z.fill(0); - Dissipation.resize(Nx,Ny,Nz); Dissipation.fill(0); - SDs.resize(Nx,Ny,Nz); SDs.fill(0); - //......................................... + morph_w = std::shared_ptr(new Minkowski(Dm)); + morph_n = std::shared_ptr(new Minkowski(Dm)); + morph_i = std::shared_ptr(new Minkowski(Dm)); - //......................................... - if (Dm->rank()==0){ - bool WriteHeader=false; - SUBPHASE = fopen("subphase.csv","r"); - if (SUBPHASE != NULL) - fclose(SUBPHASE); - else - WriteHeader=true; + // Global arrays + PhaseID.resize(Nx, Ny, Nz); + PhaseID.fill(0); + Label_WP.resize(Nx, Ny, Nz); + Label_WP.fill(0); + Label_NWP.resize(Nx, Ny, Nz); + Label_NWP.fill(0); + Rho_n.resize(Nx, Ny, Nz); + Rho_n.fill(0); + Rho_w.resize(Nx, Ny, Nz); + Rho_w.fill(0); + Pressure.resize(Nx, Ny, Nz); + Pressure.fill(0); + Phi.resize(Nx, Ny, Nz); + Phi.fill(0); + DelPhi.resize(Nx, Ny, Nz); + DelPhi.fill(0); + Vel_x.resize(Nx, Ny, Nz); + Vel_x.fill(0); // Gradient of the phase indicator field + Vel_y.resize(Nx, Ny, Nz); + Vel_y.fill(0); + Vel_z.resize(Nx, Ny, Nz); + Vel_z.fill(0); + Dissipation.resize(Nx, Ny, Nz); + Dissipation.fill(0); + SDs.resize(Nx, Ny, Nz); + SDs.fill(0); + //......................................... - SUBPHASE = fopen("subphase.csv","a+"); - if (WriteHeader) - { - // If timelog is empty, write a short header to list the averages - //fprintf(SUBPHASE,"--------------------------------------------------------------------------------------\n"); - fprintf(SUBPHASE,"time rn rw nun nuw Fx Fy Fz iftwn wet "); - fprintf(SUBPHASE,"pwc pwd pnc pnd "); // pressures - fprintf(SUBPHASE,"Mwc Mwd Mwi Mnc Mnd Mni Msw Msn "); // mass - fprintf(SUBPHASE,"Pwc_x Pwd_x Pwi_x Pnc_x Pnd_x Pni_x Psw_x Psn_x "); // momentum - fprintf(SUBPHASE,"Pwc_y Pwd_y Pwi_y Pnc_y Pnd_y Pni_y Psw_y Psn_y "); - fprintf(SUBPHASE,"Pwc_z Pwd_z Pwi_z Pnc_z Pnd_z Pni_z Psw_z Psn_z "); - fprintf(SUBPHASE,"Kwc Kwd Kwi Knc Knd Kni "); // kinetic energy - fprintf(SUBPHASE,"Dwc Dwd Dnc Dnd "); // viscous dissipation - fprintf(SUBPHASE,"Vwc Awc Hwc Xwc "); // wc region - fprintf(SUBPHASE,"Vwd Awd Hwd Xwd Nwd "); // wd region - fprintf(SUBPHASE,"Vnc Anc Hnc Xnc "); // nc region - fprintf(SUBPHASE,"Vnd And Hnd Xnd Nnd "); // nd regionin - fprintf(SUBPHASE,"Vi Ai Hi Xi "); // interface region - fprintf(SUBPHASE,"Vic Aic Hic Xic Nic\n"); // interface region + //......................................... + if (Dm->rank() == 0) { + bool WriteHeader = false; + SUBPHASE = fopen("subphase.csv", "r"); + if (SUBPHASE != NULL) + fclose(SUBPHASE); + else + WriteHeader = true; - // stress tensor? - } + SUBPHASE = fopen("subphase.csv", "a+"); + if (WriteHeader) { + // If timelog is empty, write a short header to list the averages + fprintf(SUBPHASE, "time rn rw nun nuw Fx Fy Fz iftwn wet "); + fprintf(SUBPHASE, "pwc pwd pnc pnd "); // pressures + fprintf(SUBPHASE, "Mwc Mwd Mwi Mnc Mnd Mni Msw Msn "); // mass + fprintf( + SUBPHASE, + "Pwc_x Pwd_x Pwi_x Pnc_x Pnd_x Pni_x Psw_x Psn_x "); // momentum + fprintf(SUBPHASE, + "Pwc_y Pwd_y Pwi_y Pnc_y Pnd_y Pni_y Psw_y Psn_y "); + fprintf(SUBPHASE, + "Pwc_z Pwd_z Pwi_z Pnc_z Pnd_z Pni_z Psw_z Psn_z "); + fprintf(SUBPHASE, "Kwc Kwd Kwi Knc Knd Kni "); // kinetic energy + fprintf(SUBPHASE, "Dwc Dwd Dnc Dnd "); // viscous dissipation + fprintf(SUBPHASE, "Vwc Awc Hwc Xwc "); // wc region + fprintf(SUBPHASE, "Vwd Awd Hwd Xwd Nwd "); // wd region + fprintf(SUBPHASE, "Vnc Anc Hnc Xnc "); // nc region + fprintf(SUBPHASE, "Vnd And Hnd Xnd Nnd "); // nd regionin + fprintf(SUBPHASE, "Vi Ai Hi Xi "); // interface region + fprintf(SUBPHASE, "Vic Aic Hic Xic Nic\n"); // interface region - } - else{ - char LocalRankString[8]; - sprintf(LocalRankString,"%05d",Dm->rank()); - char LocalRankFilename[40]; - sprintf(LocalRankFilename,"%s%s","subphase.csv.",LocalRankString); - SUBPHASE = fopen(LocalRankFilename,"a+"); - //fprintf(SUBPHASE,"--------------------------------------------------------------------------------------\n"); - fprintf(SUBPHASE,"time rn rw nun nuw Fx Fy Fz iftwn wet "); - fprintf(SUBPHASE,"pwc pwd pnc pnd "); // pressures - fprintf(SUBPHASE,"Mwc Mwd Mwi Mnc Mnd Mni Msw Msn "); // mass - fprintf(SUBPHASE,"Pwc_x Pwd_x Pwi_x Pnc_x Pnd_x Pni_x Psw_x Psn_x "); // momentum - fprintf(SUBPHASE,"Pwc_y Pwd_y Pwi_y Pnc_y Pnd_y Pni_y Psw_y Psn_y "); - fprintf(SUBPHASE,"Pwc_z Pwd_z Pwi_z Pnc_z Pnd_z Pni_z Psw_z Psn_z "); - fprintf(SUBPHASE,"Kwc Kwd Kwi Knc Knd Kni "); // kinetic energy - fprintf(SUBPHASE,"Dwc Dwd Dnc Dnd "); // viscous dissipation - fprintf(SUBPHASE,"Vwc Awc Hwc Xwc "); // wc region - fprintf(SUBPHASE,"Vwd Awd Hwd Xwd Nwd "); // wd region - fprintf(SUBPHASE,"Vnc Anc Hnc Xnc "); // nc region - fprintf(SUBPHASE,"Vnd And Hnd Xnd Nnd "); // nd region - fprintf(SUBPHASE,"Vi Ai Hi Xi "); // interface region - fprintf(SUBPHASE,"Vic Aic Hic Xic Nic\n"); // interface region - } - - if (Dm->rank()==0){ - bool WriteHeader=false; - TIMELOG = fopen("timelog.csv","r"); - if (TIMELOG != NULL) - fclose(TIMELOG); - else - WriteHeader=true; + // stress tensor? + } - TIMELOG = fopen("timelog.csv","a+"); - if (WriteHeader) - { - // If timelog is empty, write a short header to list the averages - //fprintf(TIMELOG,"--------------------------------------------------------------------------------------\n"); - fprintf(TIMELOG,"sw krw krn krwf krnf vw vn force pw pn wet peff\n"); - } - } + } else { + char LocalRankString[8]; + sprintf(LocalRankString, "%05d", Dm->rank()); + char LocalRankFilename[40]; + sprintf(LocalRankFilename, "%s%s", "subphase.csv.", LocalRankString); + SUBPHASE = fopen(LocalRankFilename, "a+"); + + fprintf(SUBPHASE, "time rn rw nun nuw Fx Fy Fz iftwn wet "); + fprintf(SUBPHASE, "pwc pwd pnc pnd "); // pressures + fprintf(SUBPHASE, "Mwc Mwd Mwi Mnc Mnd Mni Msw Msn "); // mass + fprintf(SUBPHASE, + "Pwc_x Pwd_x Pwi_x Pnc_x Pnd_x Pni_x Psw_x Psn_x "); // momentum + fprintf(SUBPHASE, "Pwc_y Pwd_y Pwi_y Pnc_y Pnd_y Pni_y Psw_y Psn_y "); + fprintf(SUBPHASE, "Pwc_z Pwd_z Pwi_z Pnc_z Pnd_z Pni_z Psw_z Psn_z "); + fprintf(SUBPHASE, "Kwc Kwd Kwi Knc Knd Kni "); // kinetic energy + fprintf(SUBPHASE, "Dwc Dwd Dnc Dnd "); // viscous dissipation + fprintf(SUBPHASE, "Vwc Awc Hwc Xwc "); // wc region + fprintf(SUBPHASE, "Vwd Awd Hwd Xwd Nwd "); // wd region + fprintf(SUBPHASE, "Vnc Anc Hnc Xnc "); // nc region + fprintf(SUBPHASE, "Vnd And Hnd Xnd Nnd "); // nd region + fprintf(SUBPHASE, "Vi Ai Hi Xi "); // interface region + fprintf(SUBPHASE, "Vic Aic Hic Xic Nic\n"); // interface region + } + + if (Dm->rank() == 0) { + bool WriteHeader = false; + TIMELOG = fopen("timelog.csv", "r"); + if (TIMELOG != NULL) + fclose(TIMELOG); + else + WriteHeader = true; + + TIMELOG = fopen("timelog.csv", "a+"); + if (WriteHeader) { + // If timelog is empty, write a short header to list the averages + fprintf(TIMELOG, + "sw krw krn krwf krnf vw vn force pw pn wet peff\n"); + } + } } - // Destructor -SubPhase::~SubPhase() -{ - if ( SUBPHASE!=NULL ) { fclose(SUBPHASE); } - +SubPhase::~SubPhase() { + if (SUBPHASE != NULL) { + fclose(SUBPHASE); + } } -void SubPhase::Write(int timestep) -{ - if (Dm->rank()==0){ - fprintf(SUBPHASE,"%i %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ",timestep,rho_n,rho_w,nu_n,nu_w,Fx,Fy,Fz,gamma_wn,total_wetting_interaction_global); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g ",gwc.p, gwd.p, gnc.p, gnd.p); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ",gwc.M, gwd.M, giwn.Mw, gnc.M, gnd.M, giwn.Mn, gifs.Mw, gifs.Mn); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ",gwc.Px, gwd.Px, giwn.Pwx, gnc.Px, gnd.Px, giwn.Pnx, gifs.Pwx, gifs.Pnx); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ",gwc.Py, gwd.Py, giwn.Pwy, gnc.Py, gnd.Py, giwn.Pny, gifs.Pwy, gifs.Pny); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ",gwc.Pz, gwd.Pz, giwn.Pwz, gnc.Pz, gnd.Pz, giwn.Pnz, gifs.Pwz, gifs.Pnz); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g %.8g %.8g ",gwc.K, gwd.K, giwn.Kw, gnc.K, gnd.K, giwn.Kn); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g ",gwc.visc, gwd.visc, gnc.visc, gnd.visc); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g ",gwc.V, gwc.A, gwc.H, gwc.X); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g %i ",gwd.V, gwd.A, gwd.H, gwd.X, gwd.Nc); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g ",gnc.V, gnc.A, gnc.H, gnc.X); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g %i ",gnd.V, gnd.A, gnd.H, gnd.X, gnd.Nc); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g ",giwn.V, giwn.A, giwn.H, giwn.X); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g %i\n",giwnc.V, giwnc.A, giwnc.H, giwnc.X, giwnc.Nc); - fflush(SUBPHASE); - } - else{ - fprintf(SUBPHASE,"%i %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ",timestep,rho_n,rho_w,nu_n,nu_w,Fx,Fy,Fz,gamma_wn,total_wetting_interaction); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g ",wc.p, wd.p, nc.p, nd.p); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ",wc.M, wd.M, iwn.Mw, nc.M, nd.M, iwn.Mn, ifs.Mw, ifs.Mn); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ",wc.Px, wd.Px, iwn.Pwx, nc.Px, nd.Px, iwn.Pnx, ifs.Pwx, ifs.Pnx); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ",wc.Py, wd.Py, iwn.Pwy, nc.Py, nd.Py, iwn.Pny, ifs.Pwy, ifs.Pny); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ",wc.Pz, wd.Pz, iwn.Pwz, nc.Pz, nd.Pz, iwn.Pnz, ifs.Pwz, ifs.Pnz); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g %.8g %.8g ",wc.K, wd.K, iwn.Kw, nc.K, nd.K, iwn.Kn); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g ",wc.visc, wd.visc, nc.visc, nd.visc); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g ",wc.V, wc.A, wc.H, wc.X); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g %i ",wd.V, wd.A, wd.H, wd.X, wd.Nc); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g ",nc.V, nc.A, nc.H, nc.X); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g %i ",nd.V, nd.A, nd.H, nd.X, nd.Nc); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g ",iwn.V, iwn.A, iwn.H, iwn.X); - fprintf(SUBPHASE,"%.8g %.8g %.8g %.8g\n",iwnc.V, iwnc.A, iwnc.H, iwnc.X); - } - +void SubPhase::Write(int timestep) { + if (Dm->rank() == 0) { + fprintf(SUBPHASE, "%i %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ", + timestep, rho_n, rho_w, nu_n, nu_w, Fx, Fy, Fz, gamma_wn, + total_wetting_interaction_global); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g ", gwc.p, gwd.p, gnc.p, gnd.p); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ", gwc.M, + gwd.M, giwn.Mw, gnc.M, gnd.M, giwn.Mn, gifs.Mw, gifs.Mn); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ", gwc.Px, + gwd.Px, giwn.Pwx, gnc.Px, gnd.Px, giwn.Pnx, gifs.Pwx, gifs.Pnx); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ", gwc.Py, + gwd.Py, giwn.Pwy, gnc.Py, gnd.Py, giwn.Pny, gifs.Pwy, gifs.Pny); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ", gwc.Pz, + gwd.Pz, giwn.Pwz, gnc.Pz, gnd.Pz, giwn.Pnz, gifs.Pwz, gifs.Pnz); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g %.8g %.8g ", gwc.K, gwd.K, + giwn.Kw, gnc.K, gnd.K, giwn.Kn); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g ", gwc.visc, gwd.visc, gnc.visc, + gnd.visc); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g ", gwc.V, gwc.A, gwc.H, gwc.X); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g %i ", gwd.V, gwd.A, gwd.H, gwd.X, + gwd.Nc); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g ", gnc.V, gnc.A, gnc.H, gnc.X); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g %i ", gnd.V, gnd.A, gnd.H, gnd.X, + gnd.Nc); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g ", giwn.V, giwn.A, giwn.H, + giwn.X); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g %i\n", giwnc.V, giwnc.A, giwnc.H, + giwnc.X, giwnc.Nc); + fflush(SUBPHASE); + } else { + fprintf(SUBPHASE, "%i %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ", + timestep, rho_n, rho_w, nu_n, nu_w, Fx, Fy, Fz, gamma_wn, + total_wetting_interaction); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g ", wc.p, wd.p, nc.p, nd.p); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ", wc.M, + wd.M, iwn.Mw, nc.M, nd.M, iwn.Mn, ifs.Mw, ifs.Mn); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ", wc.Px, + wd.Px, iwn.Pwx, nc.Px, nd.Px, iwn.Pnx, ifs.Pwx, ifs.Pnx); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ", wc.Py, + wd.Py, iwn.Pwy, nc.Py, nd.Py, iwn.Pny, ifs.Pwy, ifs.Pny); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g ", wc.Pz, + wd.Pz, iwn.Pwz, nc.Pz, nd.Pz, iwn.Pnz, ifs.Pwz, ifs.Pnz); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g %.8g %.8g ", wc.K, wd.K, iwn.Kw, + nc.K, nd.K, iwn.Kn); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g ", wc.visc, wd.visc, nc.visc, + nd.visc); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g ", wc.V, wc.A, wc.H, wc.X); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g %i ", wd.V, wd.A, wd.H, wd.X, + wd.Nc); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g ", nc.V, nc.A, nc.H, nc.X); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g %i ", nd.V, nd.A, nd.H, nd.X, + nd.Nc); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g ", iwn.V, iwn.A, iwn.H, iwn.X); + fprintf(SUBPHASE, "%.8g %.8g %.8g %.8g\n", iwnc.V, iwnc.A, iwnc.H, + iwnc.X); + } } -void SubPhase::SetParams(double rhoA, double rhoB, double tauA, double tauB, double force_x, double force_y, double force_z, double alpha, double B) -{ - Fx = force_x; - Fy = force_y; - Fz = force_z; - rho_n = rhoA; - rho_w = rhoB; - nu_n = (tauA-0.5)/3.f; - nu_w = (tauB-0.5)/3.f; - gamma_wn = 5.796*alpha; - beta = B; +void SubPhase::SetParams(double rhoA, double rhoB, double tauA, double tauB, + double force_x, double force_y, double force_z, + double alpha, double B) { + Fx = force_x; + Fy = force_y; + Fz = force_z; + rho_n = rhoA; + rho_w = rhoB; + nu_n = (tauA - 0.5) / 3.f; + nu_w = (tauB - 0.5) / 3.f; + gamma_wn = 5.796 * alpha; + beta = B; } -void SubPhase::Basic(){ - int i,j,k,n,imin,jmin,kmin,kmax; +void SubPhase::Basic() { + int i, j, k, n, imin, jmin, kmin, kmax; - // If external boundary conditions are set, do not average over the inlet - kmin=1; kmax=Nz-1; - imin=jmin=1; - /*// If inlet/outlet layers exist use these as default - if (Dm->inlet_layers_x > 0) imin = Dm->inlet_layers_x; - if (Dm->inlet_layers_y > 0) jmin = Dm->inlet_layers_y; - if (Dm->inlet_layers_z > 0 && Dm->kproc() == 0) kmin += Dm->inlet_layers_z; - if (Dm->outlet_layers_z > 0 && Dm->kproc() == Dm->nprocz()-1) kmax -= Dm->outlet_layers_z; - */ - nb.reset(); wb.reset(); iwn.reset(); + // If external boundary conditions are set, do not average over the inlet + kmin = 1; + kmax = Nz - 1; + imin = jmin = 1; - double count_w = 0.0; - double count_n = 0.0; - - /* compute the laplacian */ - Dm->CommunicateMeshHalo(Phi); - for (int k=1; kCommunicateMeshHalo(DelPhi); - - for (k=0; kid[n] > 0 ){ - // compute density - double nA = Rho_n(n); - double nB = Rho_w(n); - double phi = (nA-nB)/(nA+nB); - Phi(n) = phi; - } - if (Phi(n) != Phi(n)){ - // check for NaN - Phi(n) = 0.0; - //printf("Nan at %i %i %i \n",i,j,k); - } - } - } - } - - for (k=kmin; kid[n] > 0 ){ - // compute density - double nA = Rho_n(n); - double nB = Rho_w(n); - double phi = (nA-nB)/(nA+nB); - if ( phi > 0.0 ){ - nA = 1.0; - nb.V += 1.0; - nb.M += nA*rho_n; - // velocity - nb.Px += rho_n*nA*Vel_x(n); - nb.Py += rho_n*nA*Vel_y(n); - nb.Pz += rho_n*nA*Vel_z(n); - } - else{ - nB = 1.0; - wb.M += nB*rho_w; - wb.V += 1.0; + nb.reset(); + wb.reset(); + iwn.reset(); - // velocity - wb.Px += rho_w*nB*Vel_x(n); - wb.Py += rho_w*nB*Vel_y(n); - wb.Pz += rho_w*nB*Vel_z(n); - } - if ( phi > 0.99 ){ - nb.p += Pressure(n); - count_n += 1.0; - } - else if ( phi < -0.99 ){ - wb.p += Pressure(n); - count_w += 1.0; - } - /* compute the film contribution */ - else if (SDs(i,j,k) < 2.0){ - if ( phi > 0.0 ){ - nA = 1.0; - iwn.V += 1.0; - iwn.Mn += nA*rho_n; - // velocity - iwn.Pnx += rho_n*nA*Vel_x(n); - iwn.Pny += rho_n*nA*Vel_y(n); - iwn.Pnz += rho_n*nA*Vel_z(n); - } - else{ - nB = 1.0; - iwn.Mw += nB*rho_w; - iwn.V += 1.0; + double count_w = 0.0; + double count_n = 0.0; - iwn.Pwx += rho_w*nB*Vel_x(n); - iwn.Pwy += rho_w*nB*Vel_y(n); - iwn.Pwz += rho_w*nB*Vel_z(n); - } - } - } - } - } - } - - total_wetting_interaction = count_wetting_interaction = 0.0; - total_wetting_interaction_global = count_wetting_interaction_global=0.0; - for (k=kmin; kid[n] > 0 && SDs(i,j,k) < 2.0 ){ - count_wetting_interaction += 1.0; - total_wetting_interaction += DelPhi(i,j,k); - } - } - } - } - //printf("wetting interaction = %f, count = %f\n",total_wetting_interaction,count_wetting_interaction); - total_wetting_interaction_global=Dm->Comm.sumReduce( total_wetting_interaction); - count_wetting_interaction_global=Dm->Comm.sumReduce( count_wetting_interaction); - - gwb.V=Dm->Comm.sumReduce( wb.V); - gnb.V=Dm->Comm.sumReduce( nb.V); - gwb.M=Dm->Comm.sumReduce( wb.M); - gnb.M=Dm->Comm.sumReduce( nb.M); - gwb.Px=Dm->Comm.sumReduce( wb.Px); - gwb.Py=Dm->Comm.sumReduce( wb.Py); - gwb.Pz=Dm->Comm.sumReduce( wb.Pz); - gnb.Px=Dm->Comm.sumReduce( nb.Px); - gnb.Py=Dm->Comm.sumReduce( nb.Py); - gnb.Pz=Dm->Comm.sumReduce( nb.Pz); - - giwn.Mw=Dm->Comm.sumReduce( iwn.Mw); - giwn.Pwx=Dm->Comm.sumReduce( iwn.Pwx); - giwn.Pwy=Dm->Comm.sumReduce( iwn.Pwy); - giwn.Pwz=Dm->Comm.sumReduce( iwn.Pwz); - - giwn.Mn=Dm->Comm.sumReduce( iwn.Mn); - giwn.Pnx=Dm->Comm.sumReduce( iwn.Pnx); - giwn.Pny=Dm->Comm.sumReduce( iwn.Pny); - giwn.Pnz=Dm->Comm.sumReduce( iwn.Pnz); - - count_w=Dm->Comm.sumReduce( count_w); - count_n=Dm->Comm.sumReduce( count_n); - if (count_w > 0.0) - gwb.p=Dm->Comm.sumReduce( wb.p) / count_w; - else - gwb.p = 0.0; - if (count_n > 0.0) - gnb.p=Dm->Comm.sumReduce( nb.p) / count_n; - else - gnb.p = 0.0; + /* compute the laplacian */ + Dm->CommunicateMeshHalo(Phi); + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + // Compute all of the derivatives using finite differences + double fx = 0.5 * (Phi(i + 1, j, k) - Phi(i - 1, j, k)); + double fy = 0.5 * (Phi(i, j + 1, k) - Phi(i, j - 1, k)); + double fz = 0.5 * (Phi(i, j, k + 1) - Phi(i, j, k - 1)); + DelPhi(i, j, k) = sqrt(fx * fx + fy * fy + fz * fz); + } + } + } + Dm->CommunicateMeshHalo(DelPhi); - // check for NaN - bool err=false; - if (gwb.V != gwb.V) err=true; - if (gnb.V != gnb.V) err=true; - if (gwb.p != gwb.p) err=true; - if (gnb.p != gnb.p) err=true; - if (gwb.Px != gwb.Px) err=true; - if (gwb.Py != gwb.Py) err=true; - if (gwb.Pz != gwb.Pz) err=true; - if (gnb.Px != gnb.Px) err=true; - if (gnb.Py != gnb.Py) err=true; - if (gnb.Pz != gnb.Pz) err=true; - - if (Dm->rank() == 0){ - /* align flow direction based on total mass flux */ - double dir_x = gwb.Px + gnb.Px; - double dir_y = gwb.Py + gnb.Py; - double dir_z = gwb.Pz + gnb.Pz; - double flow_magnitude = dir_x*dir_x + dir_y*dir_y + dir_z*dir_z; - double force_mag = sqrt(Fx*Fx+Fy*Fy+Fz*Fz); - if (force_mag > 0.0){ - dir_x = Fx/force_mag; - dir_y = Fy/force_mag; - dir_z = Fz/force_mag; - } - else { - dir_x /= flow_magnitude; - dir_y /= flow_magnitude; - dir_z /= flow_magnitude; - } - if (Dm->BoundaryCondition == 1 || Dm->BoundaryCondition == 2 || Dm->BoundaryCondition == 3 || Dm->BoundaryCondition == 4 ){ - // compute the pressure drop - double pressure_drop = (Pressure(Nx*Ny + Nx + 1) - 1.0) / 3.0; - double length = ((Nz-2)*Dm->nprocz()); - force_mag -= pressure_drop/length; - } - if (force_mag == 0.0 && flow_magnitude == 0.0){ - // default to z direction - dir_x = 0.0; - dir_y = 0.0; - dir_z = 1.0; - force_mag = 1.0; - } - double saturation=gwb.V/(gwb.V + gnb.V); - double water_flow_rate=gwb.V*(gwb.Px*dir_x + gwb.Py*dir_y + gwb.Pz*dir_z)/gwb.M / Dm->Volume; - double not_water_flow_rate=gnb.V*(gnb.Px*dir_x + gnb.Py*dir_y + gnb.Pz*dir_z)/gnb.M/ Dm->Volume; - - /* contribution from water films */ - double water_film_flow_rate=gwb.V*(giwn.Pwx*dir_x + giwn.Pwy*dir_y + giwn.Pwz*dir_z)/gwb.M / Dm->Volume; - double not_water_film_flow_rate=gnb.V*(giwn.Pnx*dir_x + giwn.Pny*dir_y + giwn.Pnz*dir_z)/gnb.M / Dm->Volume; - //double total_flow_rate = water_flow_rate + not_water_flow_rate; - //double fractional_flow = water_flow_rate / total_flow_rate; - double h = Dm->voxel_length; - double krn = h*h*nu_n*not_water_flow_rate / force_mag ; - double krw = h*h*nu_w*water_flow_rate / force_mag; - /* not counting films */ - double krnf = krn - h*h*nu_n*not_water_film_flow_rate / force_mag ; - double krwf = krw - h*h*nu_w*water_film_flow_rate / force_mag; - double eff_pressure = 1.0 / (krn + krw); // effective pressure drop + for (k = 0; k < Nz; k++) { + for (j = 0; j < Ny; j++) { + for (i = 0; i < Nx; i++) { + n = k * Nx * Ny + j * Nx + i; + // Compute volume averages + if (Dm->id[n] > 0) { + // compute density + double nA = Rho_n(n); + double nB = Rho_w(n); + double phi = (nA - nB) / (nA + nB); + Phi(n) = phi; + } + if (Phi(n) != Phi(n)) { + // check for NaN + Phi(n) = 0.0; + //printf("Nan at %i %i %i \n",i,j,k); + } + } + } + } - fprintf(TIMELOG,"%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g\n", - saturation, krw, krn, krwf, krnf, h*water_flow_rate, - h*not_water_flow_rate, force_mag, - gwb.p, gnb.p, total_wetting_interaction_global, eff_pressure); - fflush(TIMELOG); - } - if (err==true){ - // exception if simulation produceds NaN - printf("SubPhase.cpp: NaN encountered, may need to check simulation parameters \n"); - } - ASSERT(err==false); + for (k = kmin; k < kmax; k++) { + for (j = jmin; j < Ny - 1; j++) { + for (i = imin; i < Nx - 1; i++) { + n = k * Nx * Ny + j * Nx + i; + // Compute volume averages + if (Dm->id[n] > 0) { + // compute density + double nA = Rho_n(n); + double nB = Rho_w(n); + double phi = (nA - nB) / (nA + nB); + if (phi > 0.0) { + nA = 1.0; + nb.V += 1.0; + nb.M += nA * rho_n; + // velocity + nb.Px += rho_n * nA * Vel_x(n); + nb.Py += rho_n * nA * Vel_y(n); + nb.Pz += rho_n * nA * Vel_z(n); + } else { + nB = 1.0; + wb.M += nB * rho_w; + wb.V += 1.0; + + // velocity + wb.Px += rho_w * nB * Vel_x(n); + wb.Py += rho_w * nB * Vel_y(n); + wb.Pz += rho_w * nB * Vel_z(n); + } + if (phi > 0.99) { + nb.p += Pressure(n); + count_n += 1.0; + } else if (phi < -0.99) { + wb.p += Pressure(n); + count_w += 1.0; + } + /* compute the film contribution */ + else if (SDs(i, j, k) < 2.0) { + if (phi > 0.0) { + nA = 1.0; + iwn.V += 1.0; + iwn.Mn += nA * rho_n; + // velocity + iwn.Pnx += rho_n * nA * Vel_x(n); + iwn.Pny += rho_n * nA * Vel_y(n); + iwn.Pnz += rho_n * nA * Vel_z(n); + } else { + nB = 1.0; + iwn.Mw += nB * rho_w; + iwn.V += 1.0; + + iwn.Pwx += rho_w * nB * Vel_x(n); + iwn.Pwy += rho_w * nB * Vel_y(n); + iwn.Pwz += rho_w * nB * Vel_z(n); + } + } + } + } + } + } + + total_wetting_interaction = count_wetting_interaction = 0.0; + total_wetting_interaction_global = count_wetting_interaction_global = 0.0; + for (k = kmin; k < kmax; k++) { + for (j = jmin; j < Ny - 1; j++) { + for (i = imin; i < Nx - 1; i++) { + n = k * Nx * Ny + j * Nx + i; + // compute contribution of wetting terms (within two voxels of solid) + if (Dm->id[n] > 0 && SDs(i, j, k) < 2.0) { + count_wetting_interaction += 1.0; + total_wetting_interaction += DelPhi(i, j, k); + } + } + } + } + + total_wetting_interaction_global = + Dm->Comm.sumReduce(total_wetting_interaction); + count_wetting_interaction_global = + Dm->Comm.sumReduce(count_wetting_interaction); + + gwb.V = Dm->Comm.sumReduce(wb.V); + gnb.V = Dm->Comm.sumReduce(nb.V); + gwb.M = Dm->Comm.sumReduce(wb.M); + gnb.M = Dm->Comm.sumReduce(nb.M); + gwb.Px = Dm->Comm.sumReduce(wb.Px); + gwb.Py = Dm->Comm.sumReduce(wb.Py); + gwb.Pz = Dm->Comm.sumReduce(wb.Pz); + gnb.Px = Dm->Comm.sumReduce(nb.Px); + gnb.Py = Dm->Comm.sumReduce(nb.Py); + gnb.Pz = Dm->Comm.sumReduce(nb.Pz); + + giwn.Mw = Dm->Comm.sumReduce(iwn.Mw); + giwn.Pwx = Dm->Comm.sumReduce(iwn.Pwx); + giwn.Pwy = Dm->Comm.sumReduce(iwn.Pwy); + giwn.Pwz = Dm->Comm.sumReduce(iwn.Pwz); + + giwn.Mn = Dm->Comm.sumReduce(iwn.Mn); + giwn.Pnx = Dm->Comm.sumReduce(iwn.Pnx); + giwn.Pny = Dm->Comm.sumReduce(iwn.Pny); + giwn.Pnz = Dm->Comm.sumReduce(iwn.Pnz); + + count_w = Dm->Comm.sumReduce(count_w); + count_n = Dm->Comm.sumReduce(count_n); + if (count_w > 0.0) + gwb.p = Dm->Comm.sumReduce(wb.p) / count_w; + else + gwb.p = 0.0; + if (count_n > 0.0) + gnb.p = Dm->Comm.sumReduce(nb.p) / count_n; + else + gnb.p = 0.0; + + // check for NaN + bool err = false; + if (gwb.V != gwb.V) + err = true; + if (gnb.V != gnb.V) + err = true; + if (gwb.p != gwb.p) + err = true; + if (gnb.p != gnb.p) + err = true; + if (gwb.Px != gwb.Px) + err = true; + if (gwb.Py != gwb.Py) + err = true; + if (gwb.Pz != gwb.Pz) + err = true; + if (gnb.Px != gnb.Px) + err = true; + if (gnb.Py != gnb.Py) + err = true; + if (gnb.Pz != gnb.Pz) + err = true; + + if (Dm->rank() == 0) { + /* align flow direction based on total mass flux */ + double dir_x = gwb.Px + gnb.Px; + double dir_y = gwb.Py + gnb.Py; + double dir_z = gwb.Pz + gnb.Pz; + double flow_magnitude = dir_x * dir_x + dir_y * dir_y + dir_z * dir_z; + double force_mag = sqrt(Fx * Fx + Fy * Fy + Fz * Fz); + if (force_mag > 0.0) { + dir_x = Fx / force_mag; + dir_y = Fy / force_mag; + dir_z = Fz / force_mag; + } else { + dir_x /= flow_magnitude; + dir_y /= flow_magnitude; + dir_z /= flow_magnitude; + } + if (Dm->BoundaryCondition == 1 || Dm->BoundaryCondition == 2 || + Dm->BoundaryCondition == 3 || Dm->BoundaryCondition == 4) { + // compute the pressure drop + double pressure_drop = (Pressure(Nx * Ny + Nx + 1) - 1.0) / 3.0; + double length = ((Nz - 2) * Dm->nprocz()); + force_mag -= pressure_drop / length; + } + if (force_mag == 0.0 && flow_magnitude == 0.0) { + // default to z direction + dir_x = 0.0; + dir_y = 0.0; + dir_z = 1.0; + force_mag = 1.0; + } + double saturation = gwb.V / (gwb.V + gnb.V); + double water_flow_rate = + gwb.V * (gwb.Px * dir_x + gwb.Py * dir_y + gwb.Pz * dir_z) / gwb.M / + Dm->Volume; + double not_water_flow_rate = + gnb.V * (gnb.Px * dir_x + gnb.Py * dir_y + gnb.Pz * dir_z) / gnb.M / + Dm->Volume; + + /* contribution from water films */ + double water_film_flow_rate = + gwb.V * (giwn.Pwx * dir_x + giwn.Pwy * dir_y + giwn.Pwz * dir_z) / + gwb.M / Dm->Volume; + double not_water_film_flow_rate = + gnb.V * (giwn.Pnx * dir_x + giwn.Pny * dir_y + giwn.Pnz * dir_z) / + gnb.M / Dm->Volume; + //double total_flow_rate = water_flow_rate + not_water_flow_rate; + //double fractional_flow = water_flow_rate / total_flow_rate; + double h = Dm->voxel_length; + double krn = h * h * nu_n * not_water_flow_rate / force_mag; + double krw = h * h * nu_w * water_flow_rate / force_mag; + /* not counting films */ + double krnf = krn - h * h * nu_n * not_water_film_flow_rate / force_mag; + double krwf = krw - h * h * nu_w * water_film_flow_rate / force_mag; + double eff_pressure = 1.0 / (krn + krw); // effective pressure drop + + fprintf(TIMELOG, + "%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g\n", + saturation, krw, krn, krwf, krnf, h * water_flow_rate, + h * not_water_flow_rate, force_mag, gwb.p, gnb.p, + total_wetting_interaction_global, eff_pressure); + fflush(TIMELOG); + } + if (err == true) { + // exception if simulation produceds NaN + printf("SubPhase.cpp: NaN encountered, may need to check simulation " + "parameters \n"); + } + ASSERT(err == false); } -inline void InterfaceTransportMeasures( double beta, double rA, double rB, double nA, double nB, - double nx, double ny, double nz, double ux, double uy, double uz, interface &I){ - - double A1,A2,A3,A4,A5,A6; - double B1,B2,B3,B4,B5,B6; - double nAB,delta; - double phi = (nA-nB)/(nA+nB); - // Instantiate mass transport distributions - // Stationary value - distribution 0 - nAB = 1.0/(nA+nB); - //............................................... - // q = 0,2,4 - // Cq = {1,0,0}, {0,1,0}, {0,0,1} - delta = beta*nA*nB*nAB*0.1111111111111111*nx; - if (!(nA*nB*nAB>0)) delta=0; - A1 = nA*(0.1111111111111111*(1+4.5*ux))+delta; - B1 = nB*(0.1111111111111111*(1+4.5*ux))-delta; - A2 = nA*(0.1111111111111111*(1-4.5*ux))-delta; - B2 = nB*(0.1111111111111111*(1-4.5*ux))+delta; +inline void InterfaceTransportMeasures(double beta, double rA, double rB, + double nA, double nB, double nx, + double ny, double nz, double ux, + double uy, double uz, interface &I) { - //............................................... - // Cq = {0,1,0} - delta = beta*nA*nB*nAB*0.1111111111111111*ny; - if (!(nA*nB*nAB>0)) delta=0; - A3 = nA*(0.1111111111111111*(1+4.5*uy))+delta; - B3 = nB*(0.1111111111111111*(1+4.5*uy))-delta; - A4 = nA*(0.1111111111111111*(1-4.5*uy))-delta; - B4 = nB*(0.1111111111111111*(1-4.5*uy))+delta; + double A1, A2, A3, A4, A5, A6; + double B1, B2, B3, B4, B5, B6; + double nAB, delta; + double phi = (nA - nB) / (nA + nB); + // Instantiate mass transport distributions + // Stationary value - distribution 0 + nAB = 1.0 / (nA + nB); + //............................................... + // q = 0,2,4 + // Cq = {1,0,0}, {0,1,0}, {0,0,1} + delta = beta * nA * nB * nAB * 0.1111111111111111 * nx; + if (!(nA * nB * nAB > 0)) + delta = 0; + A1 = nA * (0.1111111111111111 * (1 + 4.5 * ux)) + delta; + B1 = nB * (0.1111111111111111 * (1 + 4.5 * ux)) - delta; + A2 = nA * (0.1111111111111111 * (1 - 4.5 * ux)) - delta; + B2 = nB * (0.1111111111111111 * (1 - 4.5 * ux)) + delta; - //............................................... - // q = 4 - // Cq = {0,0,1} - delta = beta*nA*nB*nAB*0.1111111111111111*nz; - if (!(nA*nB*nAB>0)) delta=0; - A5 = nA*(0.1111111111111111*(1+4.5*uz))+delta; - B5 = nB*(0.1111111111111111*(1+4.5*uz))-delta; - A6 = nA*(0.1111111111111111*(1-4.5*uz))-delta; - B6 = nB*(0.1111111111111111*(1-4.5*uz))+delta; - - double unx = (A1-A2); - double uny = (A3-A4); - double unz = (A5-A6); - double uwx = (B1-B2); - double uwy = (B3-B4); - double uwz = (B5-B6); - /* + //............................................... + // Cq = {0,1,0} + delta = beta * nA * nB * nAB * 0.1111111111111111 * ny; + if (!(nA * nB * nAB > 0)) + delta = 0; + A3 = nA * (0.1111111111111111 * (1 + 4.5 * uy)) + delta; + B3 = nB * (0.1111111111111111 * (1 + 4.5 * uy)) - delta; + A4 = nA * (0.1111111111111111 * (1 - 4.5 * uy)) - delta; + B4 = nB * (0.1111111111111111 * (1 - 4.5 * uy)) + delta; + + //............................................... + // q = 4 + // Cq = {0,0,1} + delta = beta * nA * nB * nAB * 0.1111111111111111 * nz; + if (!(nA * nB * nAB > 0)) + delta = 0; + A5 = nA * (0.1111111111111111 * (1 + 4.5 * uz)) + delta; + B5 = nB * (0.1111111111111111 * (1 + 4.5 * uz)) - delta; + A6 = nA * (0.1111111111111111 * (1 - 4.5 * uz)) - delta; + B6 = nB * (0.1111111111111111 * (1 - 4.5 * uz)) + delta; + + double unx = (A1 - A2); + double uny = (A3 - A4); + double unz = (A5 - A6); + double uwx = (B1 - B2); + double uwy = (B3 - B4); + double uwz = (B5 - B6); + /* I.Mn += rA*nA; I.Mw += rB*nB; I.Pnx += rA*nA*unx; @@ -456,441 +513,423 @@ inline void InterfaceTransportMeasures( double beta, double rA, double rB, doubl I.Kn += rA*nA*(unx*unx + uny*uny + unz*unz); I.Kw += rB*nB*(uwx*uwx + uwy*uwy + uwz*uwz); */ - if (phi > 0.0){ - I.Mn += rA; - I.Pnx += rA*ux; - I.Pny += rA*uy; - I.Pnz += rA*uz; - } - else { - I.Mw += rB; - I.Pwx += rB*ux; - I.Pwy += rB*uy; - I.Pwz += rB*uz; - } - I.Kn += rA*nA*(unx*unx + uny*uny + unz*unz); - I.Kw += rB*nB*(uwx*uwx + uwy*uwy + uwz*uwz); - + if (phi > 0.0) { + I.Mn += rA; + I.Pnx += rA * ux; + I.Pny += rA * uy; + I.Pnz += rA * uz; + } else { + I.Mw += rB; + I.Pwx += rB * ux; + I.Pwy += rB * uy; + I.Pwz += rB * uz; + } + I.Kn += rA * nA * (unx * unx + uny * uny + unz * unz); + I.Kw += rB * nB * (uwx * uwx + uwy * uwy + uwz * uwz); } -void SubPhase::Full(){ - int i,j,k,n,imin,jmin,kmin,kmax; +void SubPhase::Full() { + int i, j, k, n, imin, jmin, kmin, kmax; - // If external boundary conditions are set, do not average over the inlet - kmin=1; kmax=Nz-1; - /*if (Dm->BoundaryCondition > 0 && Dm->BoundaryCondition != 5 && Dm->kproc() == 0) kmin=4; - if (Dm->BoundaryCondition > 0 && Dm->BoundaryCondition != 5 && Dm->kproc() == Dm->nprocz()-1) kmax=Nz-4; - */ - imin=jmin=1; - /*// If inlet layers exist use these as default - * NOTE -- excluding inlet / outlet will screw up topological averages!!! - if (Dm->inlet_layers_x > 0) imin = Dm->inlet_layers_x; - if (Dm->inlet_layers_y > 0) jmin = Dm->inlet_layers_y; - if (Dm->inlet_layers_z > 0 && Dm->kproc() == 0) kmin += Dm->inlet_layers_z; - if (Dm->outlet_layers_z > 0 && Dm->kproc() == Dm->nprocz()-1) kmax -= Dm->outlet_layers_z; - */ - nd.reset(); nc.reset(); wd.reset(); wc.reset(); iwn.reset(); iwnc.reset(); ifs.reset(); + // If external boundary conditions are set, do not average over the inlet + kmin = 1; + kmax = Nz - 1; + imin = jmin = 1; - Dm->CommunicateMeshHalo(Phi); - for (int k=1; kCommunicateMeshHalo(DelPhi); - - - Dm->CommunicateMeshHalo(Vel_x); - Dm->CommunicateMeshHalo(Vel_y); - Dm->CommunicateMeshHalo(Vel_z); - for (int k=1; k 2.0){ - Dissipation(i,j,k) = 2*rho*nu*( ux*ux + vy*vy + wz*wz + 0.5*(vx + uy)*(vx + uy)+ 0.5*(vz + wy)*(vz + wy)+ 0.5*(uz + wx)*(uz + wx)); - } - } - } - } + nd.reset(); + nc.reset(); + wd.reset(); + wc.reset(); + iwn.reset(); + iwnc.reset(); + ifs.reset(); - /* Set up geometric analysis of each region */ - - // non-wetting - for (k=0; kid[n] > 0)){ - // Solid phase - morph_n->id(i,j,k) = 1; + Dm->CommunicateMeshHalo(Phi); + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + // Compute all of the derivatives using finite differences + double fx = 0.5 * (Phi(i + 1, j, k) - Phi(i - 1, j, k)); + double fy = 0.5 * (Phi(i, j + 1, k) - Phi(i, j - 1, k)); + double fz = 0.5 * (Phi(i, j, k + 1) - Phi(i, j, k - 1)); + DelPhi(i, j, k) = sqrt(fx * fx + fy * fy + fz * fz); + } + } + } + Dm->CommunicateMeshHalo(DelPhi); - } - else if (Phi(n) > 0.0){ - // non-wetting phase - morph_n->id(i,j,k) = 0; - } - else { - // wetting phase - morph_n->id(i,j,k) = 1; - } - } - } - } - // measure the whole object - morph_n->MeasureObject();//0.5/beta,Phi); - nd.V = morph_n->V(); - nd.A = morph_n->A(); - nd.H = morph_n->H(); - nd.X = morph_n->X(); - // measure only the connected part - nd.Nc = morph_n->MeasureConnectedPathway();//0.5/beta,Phi); - nc.V = morph_n->V(); - nc.A = morph_n->A(); - nc.H = morph_n->H(); - nc.X = morph_n->X(); - // update disconnected part - nd.V -= nc.V; - nd.A -= nc.A; - nd.H -= nc.H; - nd.X -= nc.X; + Dm->CommunicateMeshHalo(Vel_x); + Dm->CommunicateMeshHalo(Vel_y); + Dm->CommunicateMeshHalo(Vel_z); + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + // Compute velocity gradients using finite differences + double phi = Phi(i, j, k); + double nu = nu_n + 0.5 * (1.0 - phi) * (nu_w - nu_n); + double rho = rho_n + 0.5 * (1.0 - phi) * (rho_w - rho_n); + double ux = 0.5 * (Vel_x(i + 1, j, k) - Vel_x(i - 1, j, k)); + double uy = 0.5 * (Vel_x(i, j + 1, k) - Vel_x(i, j - 1, k)); + double uz = 0.5 * (Vel_x(i, j, k + 1) - Vel_x(i, j, k - 1)); + double vx = 0.5 * (Vel_y(i + 1, j, k) - Vel_y(i - 1, j, k)); + double vy = 0.5 * (Vel_y(i, j + 1, k) - Vel_y(i, j - 1, k)); + double vz = 0.5 * (Vel_y(i, j, k + 1) - Vel_y(i, j, k - 1)); + double wx = 0.5 * (Vel_z(i + 1, j, k) - Vel_z(i - 1, j, k)); + double wy = 0.5 * (Vel_z(i, j + 1, k) - Vel_z(i, j - 1, k)); + double wz = 0.5 * (Vel_z(i, j, k + 1) - Vel_z(i, j, k - 1)); + if (SDs(i, j, k) > 2.0) { + Dissipation(i, j, k) = 2 * rho * nu * + (ux * ux + vy * vy + wz * wz + + 0.5 * (vx + uy) * (vx + uy) + + 0.5 * (vz + wy) * (vz + wy) + + 0.5 * (uz + wx) * (uz + wx)); + } + } + } + } - // compute global entities - gnc.V=Dm->Comm.sumReduce( nc.V); - gnc.A=Dm->Comm.sumReduce( nc.A); - gnc.H=Dm->Comm.sumReduce( nc.H); - gnc.X=Dm->Comm.sumReduce( nc.X); - gnd.V=Dm->Comm.sumReduce( nd.V); - gnd.A=Dm->Comm.sumReduce( nd.A); - gnd.H=Dm->Comm.sumReduce( nd.H); - gnd.X=Dm->Comm.sumReduce( nd.X); - gnd.Nc = nd.Nc; - // wetting - for (k=0; kid[n] > 0)){ - // Solid phase - morph_w->id(i,j,k) = 1; + /* Set up geometric analysis of each region */ - } - else if (Phi(n) < 0.0){ - // wetting phase - morph_w->id(i,j,k) = 0; - } - else { - // non-wetting phase - morph_w->id(i,j,k) = 1; - } - } - } - } - morph_w->MeasureObject();//-0.5/beta,Phi); - wd.V = morph_w->V(); - wd.A = morph_w->A(); - wd.H = morph_w->H(); - wd.X = morph_w->X(); - // measure only the connected part - wd.Nc = morph_w->MeasureConnectedPathway();//-0.5/beta,Phi); - wc.V = morph_w->V(); - wc.A = morph_w->A(); - wc.H = morph_w->H(); - wc.X = morph_w->X(); - // update disconnected part - wd.V -= wc.V; - wd.A -= wc.A; - wd.H -= wc.H; - wd.X -= wc.X; - // compute global entities - gwc.V=Dm->Comm.sumReduce( wc.V); - gwc.A=Dm->Comm.sumReduce( wc.A); - gwc.H=Dm->Comm.sumReduce( wc.H); - gwc.X=Dm->Comm.sumReduce( wc.X); - gwd.V=Dm->Comm.sumReduce( wd.V); - gwd.A=Dm->Comm.sumReduce( wd.A); - gwd.H=Dm->Comm.sumReduce( wd.H); - gwd.X=Dm->Comm.sumReduce( wd.X); - gwd.Nc = wd.Nc; - - /* Set up geometric analysis of interface region */ - for (k=0; kid[n] > 0)){ - // Solid phase - morph_i->id(i,j,k) = 1; - } - else if (DelPhi(n) > 1e-4){ - // interface - morph_i->id(i,j,k) = 0; - } - else { - // not interface - morph_i->id(i,j,k) = 1; - } - } - } - } - morph_i->MeasureObject(); - iwn.V = morph_i->V(); - iwn.A = morph_i->A(); - iwn.H = morph_i->H(); - iwn.X = morph_i->X(); - giwn.V=Dm->Comm.sumReduce( iwn.V); - giwn.A=Dm->Comm.sumReduce( iwn.A); - giwn.H=Dm->Comm.sumReduce( iwn.H); - giwn.X=Dm->Comm.sumReduce( iwn.X); - // measure only the connected part - iwnc.Nc = morph_i->MeasureConnectedPathway(); - iwnc.V = morph_i->V(); - iwnc.A = morph_i->A(); - iwnc.H = morph_i->H(); - iwnc.X = morph_i->X(); - giwnc.V=Dm->Comm.sumReduce( iwnc.V); - giwnc.A=Dm->Comm.sumReduce( iwnc.A); - giwnc.H=Dm->Comm.sumReduce( iwnc.H); - giwnc.X=Dm->Comm.sumReduce( iwnc.X); - giwnc.Nc = iwnc.Nc; + // non-wetting + for (k = 0; k < Nz; k++) { + for (j = 0; j < Ny; j++) { + for (i = 0; i < Nx; i++) { + n = k * Nx * Ny + j * Nx + i; + if (!(Dm->id[n] > 0)) { + // Solid phase + morph_n->id(i, j, k) = 1; - double vol_nc_bulk = 0.0; - double vol_wc_bulk = 0.0; - double vol_nd_bulk = 0.0; - double vol_wd_bulk = 0.0; - for (k=kmin; kid[n] > 0 ){ - // compute density - double nA = Rho_n(n); - double nB = Rho_w(n); - double phi = (nA-nB)/(nA+nB); - double ux = Vel_x(n); - double uy = Vel_y(n); - double uz = Vel_z(n); - double visc = Dissipation(n); - - if (DelPhi(n) > 1e-3 ){ - // get the normal vector - double nx = 0.5*(Phi(i+1,j,k)-Phi(i-1,j,k)); - double ny = 0.5*(Phi(i,j+1,k)-Phi(i,j-1,k)); - double nz = 0.5*(Phi(i,j,k+1)-Phi(i,j,k-1)); - if (SDs(n) > 2.5){ - // not a film region - InterfaceTransportMeasures( beta, rho_w, rho_n, nA, nB, nx, ny, nz, ux, uy, uz, iwn); - } - else{ - // films that are close to the wetting fluid - if ( morph_w->distance(i,j,k) < 2.5 && phi > 0.0){ - ifs.Mw += rho_w; - ifs.Pwx += rho_w*ux; - ifs.Pwy += rho_w*uy; - ifs.Pwz += rho_w*uz; - } - // films that are close to the NWP - if ( morph_n->distance(i,j,k) < 2.5 && phi < 0.0){ - ifs.Mn += rho_n; - ifs.Pnx += rho_n*ux; - ifs.Pny += rho_n*uy; - ifs.Pnz += rho_n*uz; - } - } - } - else if ( phi > 0.0){ - if (morph_n->label(i,j,k) > 0 ){ - vol_nd_bulk += 1.0; - nd.p += Pressure(n); - } - else{ - vol_nc_bulk += 1.0; - nc.p += Pressure(n); - } - } - else{ - // water region - if (morph_w->label(i,j,k) > 0 ){ - vol_wd_bulk += 1.0; - wd.p += Pressure(n); - } - else{ - vol_wc_bulk += 1.0; - wc.p += Pressure(n); - } - } - if ( phi > 0.0){ - if (morph_n->label(i,j,k) > 0 ){ - nA = 1.0; - nd.M += nA*rho_n; - nd.Px += nA*rho_n*ux; - nd.Py += nA*rho_n*uy; - nd.Pz += nA*rho_n*uz; - nd.K += nA*rho_n*(ux*ux + uy*uy + uz*uz); - nd.visc += visc; - } - else{ - nA = 1.0; - nc.M += nA*rho_n; - nc.Px += nA*rho_n*ux; - nc.Py += nA*rho_n*uy; - nc.Pz += nA*rho_n*uz; - nc.K += nA*rho_n*(ux*ux + uy*uy + uz*uz); - nc.visc += visc; - } - } - else{ - // water region - if (morph_w->label(i,j,k) > 0 ){ - nB = 1.0; - wd.M += nB*rho_w; - wd.Px += nB*rho_w*ux; - wd.Py += nB*rho_w*uy; - wd.Pz += nB*rho_w*uz; - wd.K += nB*rho_w*(ux*ux + uy*uy + uz*uz); - wd.visc += visc; - } - else{ - nB = 1.0; - wc.M += nB*rho_w; - wc.Px += nB*rho_w*ux; - wc.Py += nB*rho_w*uy; - wc.Pz += nB*rho_w*uz; - wc.K += nB*rho_w*(ux*ux + uy*uy + uz*uz); - wc.visc += visc; - } - } - } - } - } - } + } else if (Phi(n) > 0.0) { + // non-wetting phase + morph_n->id(i, j, k) = 0; + } else { + // wetting phase + morph_n->id(i, j, k) = 1; + } + } + } + } + // measure the whole object + morph_n->MeasureObject(); //0.5/beta,Phi); + nd.V = morph_n->V(); + nd.A = morph_n->A(); + nd.H = morph_n->H(); + nd.X = morph_n->X(); + // measure only the connected part + nd.Nc = morph_n->MeasureConnectedPathway(); //0.5/beta,Phi); + nc.V = morph_n->V(); + nc.A = morph_n->A(); + nc.H = morph_n->H(); + nc.X = morph_n->X(); + // update disconnected part + nd.V -= nc.V; + nd.A -= nc.A; + nd.H -= nc.H; + nd.X -= nc.X; - gnd.M=Dm->Comm.sumReduce( nd.M); - gnd.Px=Dm->Comm.sumReduce( nd.Px); - gnd.Py=Dm->Comm.sumReduce( nd.Py); - gnd.Pz=Dm->Comm.sumReduce( nd.Pz); - gnd.K=Dm->Comm.sumReduce( nd.K); - gnd.visc=Dm->Comm.sumReduce( nd.visc); + // compute global entities + gnc.V = Dm->Comm.sumReduce(nc.V); + gnc.A = Dm->Comm.sumReduce(nc.A); + gnc.H = Dm->Comm.sumReduce(nc.H); + gnc.X = Dm->Comm.sumReduce(nc.X); + gnd.V = Dm->Comm.sumReduce(nd.V); + gnd.A = Dm->Comm.sumReduce(nd.A); + gnd.H = Dm->Comm.sumReduce(nd.H); + gnd.X = Dm->Comm.sumReduce(nd.X); + gnd.Nc = nd.Nc; + // wetting + for (k = 0; k < Nz; k++) { + for (j = 0; j < Ny; j++) { + for (i = 0; i < Nx; i++) { + n = k * Nx * Ny + j * Nx + i; + if (!(Dm->id[n] > 0)) { + // Solid phase + morph_w->id(i, j, k) = 1; - gwd.M=Dm->Comm.sumReduce( wd.M); - gwd.Px=Dm->Comm.sumReduce( wd.Px); - gwd.Py=Dm->Comm.sumReduce( wd.Py); - gwd.Pz=Dm->Comm.sumReduce( wd.Pz); - gwd.K=Dm->Comm.sumReduce( wd.K); - gwd.visc=Dm->Comm.sumReduce( wd.visc); - - gnc.M=Dm->Comm.sumReduce( nc.M); - gnc.Px=Dm->Comm.sumReduce( nc.Px); - gnc.Py=Dm->Comm.sumReduce( nc.Py); - gnc.Pz=Dm->Comm.sumReduce( nc.Pz); - gnc.K=Dm->Comm.sumReduce( nc.K); - gnc.visc=Dm->Comm.sumReduce( nc.visc); + } else if (Phi(n) < 0.0) { + // wetting phase + morph_w->id(i, j, k) = 0; + } else { + // non-wetting phase + morph_w->id(i, j, k) = 1; + } + } + } + } + morph_w->MeasureObject(); //-0.5/beta,Phi); + wd.V = morph_w->V(); + wd.A = morph_w->A(); + wd.H = morph_w->H(); + wd.X = morph_w->X(); + // measure only the connected part + wd.Nc = morph_w->MeasureConnectedPathway(); //-0.5/beta,Phi); + wc.V = morph_w->V(); + wc.A = morph_w->A(); + wc.H = morph_w->H(); + wc.X = morph_w->X(); + // update disconnected part + wd.V -= wc.V; + wd.A -= wc.A; + wd.H -= wc.H; + wd.X -= wc.X; + // compute global entities + gwc.V = Dm->Comm.sumReduce(wc.V); + gwc.A = Dm->Comm.sumReduce(wc.A); + gwc.H = Dm->Comm.sumReduce(wc.H); + gwc.X = Dm->Comm.sumReduce(wc.X); + gwd.V = Dm->Comm.sumReduce(wd.V); + gwd.A = Dm->Comm.sumReduce(wd.A); + gwd.H = Dm->Comm.sumReduce(wd.H); + gwd.X = Dm->Comm.sumReduce(wd.X); + gwd.Nc = wd.Nc; - gwc.M=Dm->Comm.sumReduce( wc.M); - gwc.Px=Dm->Comm.sumReduce( wc.Px); - gwc.Py=Dm->Comm.sumReduce( wc.Py); - gwc.Pz=Dm->Comm.sumReduce( wc.Pz); - gwc.K=Dm->Comm.sumReduce( wc.K); - gwc.visc=Dm->Comm.sumReduce( wc.visc); + /* Set up geometric analysis of interface region */ + for (k = 0; k < Nz; k++) { + for (j = 0; j < Ny; j++) { + for (i = 0; i < Nx; i++) { + n = k * Nx * Ny + j * Nx + i; + if (!(Dm->id[n] > 0)) { + // Solid phase + morph_i->id(i, j, k) = 1; + } else if (DelPhi(n) > 1e-4) { + // interface + morph_i->id(i, j, k) = 0; + } else { + // not interface + morph_i->id(i, j, k) = 1; + } + } + } + } + morph_i->MeasureObject(); + iwn.V = morph_i->V(); + iwn.A = morph_i->A(); + iwn.H = morph_i->H(); + iwn.X = morph_i->X(); + giwn.V = Dm->Comm.sumReduce(iwn.V); + giwn.A = Dm->Comm.sumReduce(iwn.A); + giwn.H = Dm->Comm.sumReduce(iwn.H); + giwn.X = Dm->Comm.sumReduce(iwn.X); + // measure only the connected part + iwnc.Nc = morph_i->MeasureConnectedPathway(); + iwnc.V = morph_i->V(); + iwnc.A = morph_i->A(); + iwnc.H = morph_i->H(); + iwnc.X = morph_i->X(); + giwnc.V = Dm->Comm.sumReduce(iwnc.V); + giwnc.A = Dm->Comm.sumReduce(iwnc.A); + giwnc.H = Dm->Comm.sumReduce(iwnc.H); + giwnc.X = Dm->Comm.sumReduce(iwnc.X); + giwnc.Nc = iwnc.Nc; - giwn.Mn=Dm->Comm.sumReduce( iwn.Mn); - giwn.Pnx=Dm->Comm.sumReduce( iwn.Pnx); - giwn.Pny=Dm->Comm.sumReduce( iwn.Pny); - giwn.Pnz=Dm->Comm.sumReduce( iwn.Pnz); - giwn.Kn=Dm->Comm.sumReduce( iwn.Kn); - giwn.Mw=Dm->Comm.sumReduce( iwn.Mw); - giwn.Pwx=Dm->Comm.sumReduce( iwn.Pwx); - giwn.Pwy=Dm->Comm.sumReduce( iwn.Pwy); - giwn.Pwz=Dm->Comm.sumReduce( iwn.Pwz); - giwn.Kw=Dm->Comm.sumReduce( iwn.Kw); - - gifs.Mn= Dm->Comm.sumReduce( ifs.Mn); - gifs.Pnx=Dm->Comm.sumReduce( ifs.Pnx); - gifs.Pny=Dm->Comm.sumReduce( ifs.Pny); - gifs.Pnz=Dm->Comm.sumReduce( ifs.Pnz); - gifs.Mw= Dm->Comm.sumReduce( ifs.Mw); - gifs.Pwx=Dm->Comm.sumReduce( ifs.Pwx); - gifs.Pwy=Dm->Comm.sumReduce( ifs.Pwy); - gifs.Pwz=Dm->Comm.sumReduce( ifs.Pwz); - - // pressure averaging - gnc.p=Dm->Comm.sumReduce( nc.p); - gnd.p=Dm->Comm.sumReduce( nd.p); - gwc.p=Dm->Comm.sumReduce( wc.p); - gwd.p=Dm->Comm.sumReduce( wd.p); + double vol_nc_bulk = 0.0; + double vol_wc_bulk = 0.0; + double vol_nd_bulk = 0.0; + double vol_wd_bulk = 0.0; + for (k = kmin; k < kmax; k++) { + for (j = jmin; j < Ny - 1; j++) { + for (i = imin; i < Nx - 1; i++) { + n = k * Nx * Ny + j * Nx + i; + // Compute volume averages + if (Dm->id[n] > 0) { + // compute density + double nA = Rho_n(n); + double nB = Rho_w(n); + double phi = (nA - nB) / (nA + nB); + double ux = Vel_x(n); + double uy = Vel_y(n); + double uz = Vel_z(n); + double visc = Dissipation(n); - if (vol_wc_bulk > 0.0) - wc.p = wc.p /vol_wc_bulk; - if (vol_nc_bulk > 0.0) - nc.p = nc.p /vol_nc_bulk; - if (vol_wd_bulk > 0.0) - wd.p = wd.p /vol_wd_bulk; - if (vol_nd_bulk > 0.0) - nd.p = nd.p /vol_nd_bulk; + if (DelPhi(n) > 1e-3) { + // get the normal vector + double nx = 0.5 * (Phi(i + 1, j, k) - Phi(i - 1, j, k)); + double ny = 0.5 * (Phi(i, j + 1, k) - Phi(i, j - 1, k)); + double nz = 0.5 * (Phi(i, j, k + 1) - Phi(i, j, k - 1)); + if (SDs(n) > 2.5) { + // not a film region + InterfaceTransportMeasures(beta, rho_w, rho_n, nA, + nB, nx, ny, nz, ux, uy, + uz, iwn); + } else { + // films that are close to the wetting fluid + if (morph_w->distance(i, j, k) < 2.5 && phi > 0.0) { + ifs.Mw += rho_w; + ifs.Pwx += rho_w * ux; + ifs.Pwy += rho_w * uy; + ifs.Pwz += rho_w * uz; + } + // films that are close to the NWP + if (morph_n->distance(i, j, k) < 2.5 && phi < 0.0) { + ifs.Mn += rho_n; + ifs.Pnx += rho_n * ux; + ifs.Pny += rho_n * uy; + ifs.Pnz += rho_n * uz; + } + } + } else if (phi > 0.0) { + if (morph_n->label(i, j, k) > 0) { + vol_nd_bulk += 1.0; + nd.p += Pressure(n); + } else { + vol_nc_bulk += 1.0; + nc.p += Pressure(n); + } + } else { + // water region + if (morph_w->label(i, j, k) > 0) { + vol_wd_bulk += 1.0; + wd.p += Pressure(n); + } else { + vol_wc_bulk += 1.0; + wc.p += Pressure(n); + } + } + if (phi > 0.0) { + if (morph_n->label(i, j, k) > 0) { + nA = 1.0; + nd.M += nA * rho_n; + nd.Px += nA * rho_n * ux; + nd.Py += nA * rho_n * uy; + nd.Pz += nA * rho_n * uz; + nd.K += nA * rho_n * (ux * ux + uy * uy + uz * uz); + nd.visc += visc; + } else { + nA = 1.0; + nc.M += nA * rho_n; + nc.Px += nA * rho_n * ux; + nc.Py += nA * rho_n * uy; + nc.Pz += nA * rho_n * uz; + nc.K += nA * rho_n * (ux * ux + uy * uy + uz * uz); + nc.visc += visc; + } + } else { + // water region + if (morph_w->label(i, j, k) > 0) { + nB = 1.0; + wd.M += nB * rho_w; + wd.Px += nB * rho_w * ux; + wd.Py += nB * rho_w * uy; + wd.Pz += nB * rho_w * uz; + wd.K += nB * rho_w * (ux * ux + uy * uy + uz * uz); + wd.visc += visc; + } else { + nB = 1.0; + wc.M += nB * rho_w; + wc.Px += nB * rho_w * ux; + wc.Py += nB * rho_w * uy; + wc.Pz += nB * rho_w * uz; + wc.K += nB * rho_w * (ux * ux + uy * uy + uz * uz); + wc.visc += visc; + } + } + } + } + } + } - vol_wc_bulk=Dm->Comm.sumReduce( vol_wc_bulk); - vol_wd_bulk=Dm->Comm.sumReduce( vol_wd_bulk); - vol_nc_bulk=Dm->Comm.sumReduce( vol_nc_bulk); - vol_nd_bulk=Dm->Comm.sumReduce( vol_nd_bulk); - - if (vol_wc_bulk > 0.0) - gwc.p = gwc.p /vol_wc_bulk; - if (vol_nc_bulk > 0.0) - gnc.p = gnc.p /vol_nc_bulk; - if (vol_wd_bulk > 0.0) - gwd.p = gwd.p /vol_wd_bulk; - if (vol_nd_bulk > 0.0) - gnd.p = gnd.p /vol_nd_bulk; + gnd.M = Dm->Comm.sumReduce(nd.M); + gnd.Px = Dm->Comm.sumReduce(nd.Px); + gnd.Py = Dm->Comm.sumReduce(nd.Py); + gnd.Pz = Dm->Comm.sumReduce(nd.Pz); + gnd.K = Dm->Comm.sumReduce(nd.K); + gnd.visc = Dm->Comm.sumReduce(nd.visc); + + gwd.M = Dm->Comm.sumReduce(wd.M); + gwd.Px = Dm->Comm.sumReduce(wd.Px); + gwd.Py = Dm->Comm.sumReduce(wd.Py); + gwd.Pz = Dm->Comm.sumReduce(wd.Pz); + gwd.K = Dm->Comm.sumReduce(wd.K); + gwd.visc = Dm->Comm.sumReduce(wd.visc); + + gnc.M = Dm->Comm.sumReduce(nc.M); + gnc.Px = Dm->Comm.sumReduce(nc.Px); + gnc.Py = Dm->Comm.sumReduce(nc.Py); + gnc.Pz = Dm->Comm.sumReduce(nc.Pz); + gnc.K = Dm->Comm.sumReduce(nc.K); + gnc.visc = Dm->Comm.sumReduce(nc.visc); + + gwc.M = Dm->Comm.sumReduce(wc.M); + gwc.Px = Dm->Comm.sumReduce(wc.Px); + gwc.Py = Dm->Comm.sumReduce(wc.Py); + gwc.Pz = Dm->Comm.sumReduce(wc.Pz); + gwc.K = Dm->Comm.sumReduce(wc.K); + gwc.visc = Dm->Comm.sumReduce(wc.visc); + + giwn.Mn = Dm->Comm.sumReduce(iwn.Mn); + giwn.Pnx = Dm->Comm.sumReduce(iwn.Pnx); + giwn.Pny = Dm->Comm.sumReduce(iwn.Pny); + giwn.Pnz = Dm->Comm.sumReduce(iwn.Pnz); + giwn.Kn = Dm->Comm.sumReduce(iwn.Kn); + giwn.Mw = Dm->Comm.sumReduce(iwn.Mw); + giwn.Pwx = Dm->Comm.sumReduce(iwn.Pwx); + giwn.Pwy = Dm->Comm.sumReduce(iwn.Pwy); + giwn.Pwz = Dm->Comm.sumReduce(iwn.Pwz); + giwn.Kw = Dm->Comm.sumReduce(iwn.Kw); + + gifs.Mn = Dm->Comm.sumReduce(ifs.Mn); + gifs.Pnx = Dm->Comm.sumReduce(ifs.Pnx); + gifs.Pny = Dm->Comm.sumReduce(ifs.Pny); + gifs.Pnz = Dm->Comm.sumReduce(ifs.Pnz); + gifs.Mw = Dm->Comm.sumReduce(ifs.Mw); + gifs.Pwx = Dm->Comm.sumReduce(ifs.Pwx); + gifs.Pwy = Dm->Comm.sumReduce(ifs.Pwy); + gifs.Pwz = Dm->Comm.sumReduce(ifs.Pwz); + + // pressure averaging + gnc.p = Dm->Comm.sumReduce(nc.p); + gnd.p = Dm->Comm.sumReduce(nd.p); + gwc.p = Dm->Comm.sumReduce(wc.p); + gwd.p = Dm->Comm.sumReduce(wd.p); + + if (vol_wc_bulk > 0.0) + wc.p = wc.p / vol_wc_bulk; + if (vol_nc_bulk > 0.0) + nc.p = nc.p / vol_nc_bulk; + if (vol_wd_bulk > 0.0) + wd.p = wd.p / vol_wd_bulk; + if (vol_nd_bulk > 0.0) + nd.p = nd.p / vol_nd_bulk; + + vol_wc_bulk = Dm->Comm.sumReduce(vol_wc_bulk); + vol_wd_bulk = Dm->Comm.sumReduce(vol_wd_bulk); + vol_nc_bulk = Dm->Comm.sumReduce(vol_nc_bulk); + vol_nd_bulk = Dm->Comm.sumReduce(vol_nd_bulk); + + if (vol_wc_bulk > 0.0) + gwc.p = gwc.p / vol_wc_bulk; + if (vol_nc_bulk > 0.0) + gnc.p = gnc.p / vol_nc_bulk; + if (vol_wd_bulk > 0.0) + gwd.p = gwd.p / vol_wd_bulk; + if (vol_nd_bulk > 0.0) + gnd.p = gnd.p / vol_nd_bulk; } +void SubPhase::AggregateLabels(const std::string &filename) { -void SubPhase::AggregateLabels( const std::string& filename ) -{ - - int nx = Dm->Nx; - int ny = Dm->Ny; - int nz = Dm->Nz; - - //printf("aggregate labels: local size=%i, global size = %i",local_size, full_size); - // assign the ID from the phase indicator field - for (int k=0; kid[n]; - if (local_id_val > 0){ - double value = Phi(i,j,k); - if (value > 0.0) local_id_val = 1; - else local_id_val = 2; - } - Dm->id[n] = local_id_val; - } - } - } - Dm->Comm.barrier(); + int nx = Dm->Nx; + int ny = Dm->Ny; + int nz = Dm->Nz; - Dm->AggregateLabels( filename ); + // assign the ID from the phase indicator field + for (int k = 0; k < nz; k++) { + for (int j = 0; j < ny; j++) { + for (int i = 0; i < nx; i++) { + int n = k * nx * ny + j * nx + i; + signed char local_id_val = Dm->id[n]; + if (local_id_val > 0) { + double value = Phi(i, j, k); + if (value > 0.0) + local_id_val = 1; + else + local_id_val = 2; + } + Dm->id[n] = local_id_val; + } + } + } + Dm->Comm.barrier(); + Dm->AggregateLabels(filename); } - - - diff --git a/analysis/SubPhase.h b/analysis/SubPhase.h index c00f00de..ed7b8f20 100644 --- a/analysis/SubPhase.h +++ b/analysis/SubPhase.h @@ -17,49 +17,48 @@ #include "IO/Reader.h" #include "IO/Writer.h" - -class phase{ +class phase { public: - int Nc; - double p; - double M,Px,Py,Pz,K,visc; - double V,A,H,X; - void reset(){ - p=M=Px=Py=Pz=K=0.0; - visc=0.0; - V=A=H=X=0.0; - Nc=1; - } + int Nc; + double p; + double M, Px, Py, Pz, K, visc; + double V, A, H, X; + void reset() { + p = M = Px = Py = Pz = K = 0.0; + visc = 0.0; + V = A = H = X = 0.0; + Nc = 1; + } private: }; -class interface{ +class interface { public: - int Nc; - double M,Px,Py,Pz,K; - double Mw,Mn,Pnx,Pny,Pnz,Pwx,Pwy,Pwz,Kw,Kn; - double V,A,H,X; - void reset(){ - Nc = 0; - M=Px=Py=Pz=K=0.0; - V=A=H=X=0.0; - Mw=Mn=Pnx=Pny=Pnz=Pwx=Pwy=Pwz=Kw=Kn=0.0; - } + int Nc; + double M, Px, Py, Pz, K; + double Mw, Mn, Pnx, Pny, Pnz, Pwx, Pwy, Pwz, Kw, Kn; + double V, A, H, X; + void reset() { + Nc = 0; + M = Px = Py = Pz = K = 0.0; + V = A = H = X = 0.0; + Mw = Mn = Pnx = Pny = Pnz = Pwx = Pwy = Pwz = Kw = Kn = 0.0; + } private: }; -class SubPhase{ +class SubPhase { public: - std::shared_ptr Dm; - double Volume; - // input variables - double rho_n, rho_w; - double nu_n, nu_w; - double gamma_wn, beta; - double Fx, Fy, Fz; - /* + std::shared_ptr Dm; + double Volume; + // input variables + double rho_n, rho_w; + double nu_n, nu_w; + double gamma_wn, beta; + double Fx, Fy, Fz; + /* * indices * w - water phase * n - not water phase @@ -68,53 +67,55 @@ public: * i - interface region * b - bulk (total) */ - // local entities - phase wc,wd,wb,nc,nd,nb,solid; - interface iwn,iwnc; - interface ifs; - - // global entities - phase gwc,gwd,gwb,gnc,gnd,gnb,gsolid; - interface giwn,giwnc; - interface gifs; - /* fluid-solid wetting interaction */ - double total_wetting_interaction, count_wetting_interaction; - double total_wetting_interaction_global, count_wetting_interaction_global; - - //........................................................................... - int Nx,Ny,Nz; - IntArray PhaseID; // Phase ID array (solid=0, non-wetting=1, wetting=2) - BlobIDArray Label_WP; // Wetting phase label - BlobIDArray Label_NWP; // Non-wetting phase label index (0:nblobs-1) - std::vector Label_NWP_map; // Non-wetting phase label for each index - DoubleArray Rho_n; // density field - DoubleArray Rho_w; // density field - DoubleArray Phi; // phase indicator field - DoubleArray DelPhi; // Magnitude of Gradient of the phase indicator field - DoubleArray Pressure; // pressure field - DoubleArray Vel_x; // velocity field - DoubleArray Vel_y; - DoubleArray Vel_z; - DoubleArray Dissipation; - DoubleArray SDs; + // local entities + phase wc, wd, wb, nc, nd, nb, solid; + interface iwn, iwnc; + interface ifs; - std::shared_ptr morph_w; - std::shared_ptr morph_n; - std::shared_ptr morph_i; + // global entities + phase gwc, gwd, gwb, gnc, gnd, gnb, gsolid; + interface giwn, giwnc; + interface gifs; + /* fluid-solid wetting interaction */ + double total_wetting_interaction, count_wetting_interaction; + double total_wetting_interaction_global, count_wetting_interaction_global; - SubPhase(std::shared_ptr Dm); - ~SubPhase(); - - void SetParams(double rhoA, double rhoB, double tauA, double tauB, double force_x, double force_y, double force_z, double alpha, double beta); - void Basic(); - void Full(); - void Write(int time); - void AggregateLabels( const std::string& filename ); + //........................................................................... + int Nx, Ny, Nz; + IntArray PhaseID; // Phase ID array (solid=0, non-wetting=1, wetting=2) + BlobIDArray Label_WP; // Wetting phase label + BlobIDArray Label_NWP; // Non-wetting phase label index (0:nblobs-1) + std::vector + Label_NWP_map; // Non-wetting phase label for each index + DoubleArray Rho_n; // density field + DoubleArray Rho_w; // density field + DoubleArray Phi; // phase indicator field + DoubleArray DelPhi; // Magnitude of Gradient of the phase indicator field + DoubleArray Pressure; // pressure field + DoubleArray Vel_x; // velocity field + DoubleArray Vel_y; + DoubleArray Vel_z; + DoubleArray Dissipation; + DoubleArray SDs; + + std::shared_ptr morph_w; + std::shared_ptr morph_n; + std::shared_ptr morph_i; + + SubPhase(std::shared_ptr Dm); + ~SubPhase(); + + void SetParams(double rhoA, double rhoB, double tauA, double tauB, + double force_x, double force_y, double force_z, double alpha, + double beta); + void Basic(); + void Full(); + void Write(int time); + void AggregateLabels(const std::string &filename); private: - FILE *TIMELOG; - FILE *SUBPHASE; + FILE *TIMELOG; + FILE *SUBPHASE; }; #endif - diff --git a/analysis/TwoPhase.cpp b/analysis/TwoPhase.cpp index d0f63f76..447670a5 100644 --- a/analysis/TwoPhase.cpp +++ b/analysis/TwoPhase.cpp @@ -44,7 +44,6 @@ #include - #define BLOB_AVG_COUNT 35 // Array access for averages defined by the following @@ -87,157 +86,197 @@ #define PI 3.14159265359 // Constructor -TwoPhase::TwoPhase(std::shared_ptr dm): - n_nw_pts(0), n_ns_pts(0), n_ws_pts(0), n_nws_pts(0), n_local_sol_pts(0), n_local_nws_pts(0), - n_nw_tris(0), n_ns_tris(0), n_ws_tris(0), n_nws_seg(0), n_local_sol_tris(0), - nc(0), kstart(0), kfinish(0), fluid_isovalue(0), solid_isovalue(0), Volume(0), - TIMELOG(NULL), NWPLOG(NULL), WPLOG(NULL), - Dm(dm), NumberComponents_WP(0), NumberComponents_NWP(0), trimdist(0), - porosity(0), poreVol(0), awn(0), ans(0), aws(0), lwns(0), wp_volume(0), nwp_volume(0), - As(0), dummy(0), vol_w(0), vol_n(0), sat_w(0), sat_w_previous(0), - pan(0), paw(0), pan_global(0), paw_global(0), vol_w_global(0), vol_n_global(0), - awn_global(0), ans_global(0),aws_global(0), lwns_global(0), efawns(0), efawns_global(0), - Jwn(0), Jwn_global(0), Kwn(0), Kwn_global(0), KNwns(0), KNwns_global(0), - KGwns(0), KGwns_global(0), trawn(0), trawn_global(0), trJwn(0), trJwn_global(0), - trRwn(0), trRwn_global(0), nwp_volume_global(0), wp_volume_global(0), - As_global(0), wwndnw_global(0), wwnsdnwn_global(0), Jwnwwndnw_global(0), dEs(0), dAwn(0), dAns(0) -{ - Nx=dm->Nx; Ny=dm->Ny; Nz=dm->Nz; - Volume=(Nx-2)*(Ny-2)*(Nz-2)*Dm->nprocx()*Dm->nprocy()*Dm->nprocz()*1.0; +TwoPhase::TwoPhase(std::shared_ptr dm) + : n_nw_pts(0), n_ns_pts(0), n_ws_pts(0), n_nws_pts(0), n_local_sol_pts(0), + n_local_nws_pts(0), n_nw_tris(0), n_ns_tris(0), n_ws_tris(0), + n_nws_seg(0), n_local_sol_tris(0), nc(0), kstart(0), kfinish(0), + fluid_isovalue(0), solid_isovalue(0), Volume(0), TIMELOG(NULL), + NWPLOG(NULL), WPLOG(NULL), Dm(dm), NumberComponents_WP(0), + NumberComponents_NWP(0), trimdist(0), porosity(0), poreVol(0), awn(0), + ans(0), aws(0), lwns(0), wp_volume(0), nwp_volume(0), As(0), dummy(0), + vol_w(0), vol_n(0), sat_w(0), sat_w_previous(0), pan(0), paw(0), + pan_global(0), paw_global(0), vol_w_global(0), vol_n_global(0), + awn_global(0), ans_global(0), aws_global(0), lwns_global(0), efawns(0), + efawns_global(0), Jwn(0), Jwn_global(0), Kwn(0), Kwn_global(0), KNwns(0), + KNwns_global(0), KGwns(0), KGwns_global(0), trawn(0), trawn_global(0), + trJwn(0), trJwn_global(0), trRwn(0), trRwn_global(0), + nwp_volume_global(0), wp_volume_global(0), As_global(0), wwndnw_global(0), + wwnsdnwn_global(0), Jwnwwndnw_global(0), dEs(0), dAwn(0), dAns(0) { + Nx = dm->Nx; + Ny = dm->Ny; + Nz = dm->Nz; + Volume = (Nx - 2) * (Ny - 2) * (Nz - 2) * Dm->nprocx() * Dm->nprocy() * + Dm->nprocz() * 1.0; - TempID = new char[Nx*Ny*Nz]; - - wet_morph = std::shared_ptr(new Minkowski(Dm)); - nonwet_morph = std::shared_ptr(new Minkowski(Dm)); + TempID = new char[Nx * Ny * Nz]; - // Global arrays - PhaseID.resize(Nx,Ny,Nz); PhaseID.fill(0); - Label_WP.resize(Nx,Ny,Nz); Label_WP.fill(0); - Label_NWP.resize(Nx,Ny,Nz); Label_NWP.fill(0); - SDn.resize(Nx,Ny,Nz); SDn.fill(0); - SDs.resize(Nx,Ny,Nz); SDs.fill(0); - Phase.resize(Nx,Ny,Nz); Phase.fill(0); - Press.resize(Nx,Ny,Nz); Press.fill(0); - dPdt.resize(Nx,Ny,Nz); dPdt.fill(0); - MeanCurvature.resize(Nx,Ny,Nz); MeanCurvature.fill(0); - GaussCurvature.resize(Nx,Ny,Nz); GaussCurvature.fill(0); - SDs_x.resize(Nx,Ny,Nz); SDs_x.fill(0); // Gradient of the signed distance - SDs_y.resize(Nx,Ny,Nz); SDs_y.fill(0); - SDs_z.resize(Nx,Ny,Nz); SDs_z.fill(0); - SDn_x.resize(Nx,Ny,Nz); SDn_x.fill(0); // Gradient of the signed distance - SDn_y.resize(Nx,Ny,Nz); SDn_y.fill(0); - SDn_z.resize(Nx,Ny,Nz); SDn_z.fill(0); - DelPhi.resize(Nx,Ny,Nz); DelPhi.fill(0); - Phase_tplus.resize(Nx,Ny,Nz); Phase_tplus.fill(0); - Phase_tminus.resize(Nx,Ny,Nz); Phase_tminus.fill(0); - Vel_x.resize(Nx,Ny,Nz); Vel_x.fill(0); // Gradient of the phase indicator field - Vel_y.resize(Nx,Ny,Nz); Vel_y.fill(0); - Vel_z.resize(Nx,Ny,Nz); Vel_z.fill(0); - //......................................... - // Allocate cube storage space - CubeValues.resize(2,2,2); - nw_tris.resize(3,20); - ns_tris.resize(3,20); - ws_tris.resize(3,20); - nws_seg.resize(2,20); - local_sol_tris.resize(3,18); - nw_pts=DTMutableList(20); - ns_pts=DTMutableList(20); - ws_pts=DTMutableList(20); - nws_pts=DTMutableList(20); - local_nws_pts=DTMutableList(20); - local_sol_pts=DTMutableList(20); - tmp=DTMutableList(20); - //......................................... - Values.resize(20); - DistanceValues.resize(20); - KGwns_values.resize(20); - KNwns_values.resize(20); - InterfaceSpeed.resize(20); - NormalVector.resize(60); - //......................................... - van.resize(3); - vaw.resize(3); - vawn.resize(3); - vawns.resize(3); - Gwn.resize(6); - Gns.resize(6); - Gws.resize(6); - van_global.resize(3); - vaw_global.resize(3); - vawn_global.resize(3); - vawns_global.resize(3); - Gwn_global.resize(6); - Gns_global.resize(6); - Gws_global.resize(6); - //......................................... - if (Dm->rank()==0){ - TIMELOG = fopen("timelog.tcat","a+"); - if (fseek(TIMELOG,0,SEEK_SET) == fseek(TIMELOG,0,SEEK_CUR)) - { - // If timelog is empty, write a short header to list the averages - //fprintf(TIMELOG,"--------------------------------------------------------------------------------------\n"); - fprintf(TIMELOG,"time rn rw nun nuw Fx Fy Fz iftwn "); // Timestep, Change in Surface Energy - fprintf(TIMELOG,"sw pw pn awn ans aws Jwn Kwn lwns cwns KNwns KGwns "); // Scalar averages - fprintf(TIMELOG,"vawx vawy vawz vanx vany vanz "); // Velocity averages - fprintf(TIMELOG,"vawnx vawny vawnz vawnsx vawnsy vawnsz "); - fprintf(TIMELOG,"Gwnxx Gwnyy Gwnzz Gwnxy Gwnxz Gwnyz "); // Orientation tensors - fprintf(TIMELOG,"Gwsxx Gwsyy Gwszz Gwsxy Gwsxz Gwsyz "); - fprintf(TIMELOG,"Gnsxx Gnsyy Gnszz Gnsxy Gnsxz Gnsyz "); - fprintf(TIMELOG,"trawn trJwn trRwn "); //trimmed curvature, - fprintf(TIMELOG,"wwndnw wwnsdnwn Jwnwwndnw "); //kinematic quantities, - fprintf(TIMELOG,"Vw Aw Jw Xw "); //miknowski measures, - fprintf(TIMELOG,"Vn An Jn Xn\n"); //miknowski measures, -// fprintf(TIMELOG,"Euler Kn Jn An\n"); //miknowski measures, - } + wet_morph = std::shared_ptr(new Minkowski(Dm)); + nonwet_morph = std::shared_ptr(new Minkowski(Dm)); - NWPLOG = fopen("components.NWP.tcat","a+"); - fprintf(NWPLOG,"time label vol pn awn ans Jwn Kwn lwns cwns "); - fprintf(NWPLOG,"vx vy vz vwnx vwny vwnz vwnsx vwnsy vwnsz vsq "); - fprintf(NWPLOG,"Gwnxx Gwnyy Gwnzz Gwnxy Gwnxz Gwnyz Cx Cy Cz trawn trJwn Kn Euler\n"); + // Global arrays + PhaseID.resize(Nx, Ny, Nz); + PhaseID.fill(0); + Label_WP.resize(Nx, Ny, Nz); + Label_WP.fill(0); + Label_NWP.resize(Nx, Ny, Nz); + Label_NWP.fill(0); + SDn.resize(Nx, Ny, Nz); + SDn.fill(0); + SDs.resize(Nx, Ny, Nz); + SDs.fill(0); + Phase.resize(Nx, Ny, Nz); + Phase.fill(0); + Press.resize(Nx, Ny, Nz); + Press.fill(0); + dPdt.resize(Nx, Ny, Nz); + dPdt.fill(0); + MeanCurvature.resize(Nx, Ny, Nz); + MeanCurvature.fill(0); + GaussCurvature.resize(Nx, Ny, Nz); + GaussCurvature.fill(0); + SDs_x.resize(Nx, Ny, Nz); + SDs_x.fill(0); // Gradient of the signed distance + SDs_y.resize(Nx, Ny, Nz); + SDs_y.fill(0); + SDs_z.resize(Nx, Ny, Nz); + SDs_z.fill(0); + SDn_x.resize(Nx, Ny, Nz); + SDn_x.fill(0); // Gradient of the signed distance + SDn_y.resize(Nx, Ny, Nz); + SDn_y.fill(0); + SDn_z.resize(Nx, Ny, Nz); + SDn_z.fill(0); + DelPhi.resize(Nx, Ny, Nz); + DelPhi.fill(0); + Phase_tplus.resize(Nx, Ny, Nz); + Phase_tplus.fill(0); + Phase_tminus.resize(Nx, Ny, Nz); + Phase_tminus.fill(0); + Vel_x.resize(Nx, Ny, Nz); + Vel_x.fill(0); // Gradient of the phase indicator field + Vel_y.resize(Nx, Ny, Nz); + Vel_y.fill(0); + Vel_z.resize(Nx, Ny, Nz); + Vel_z.fill(0); + //......................................... + // Allocate cube storage space + CubeValues.resize(2, 2, 2); + nw_tris.resize(3, 20); + ns_tris.resize(3, 20); + ws_tris.resize(3, 20); + nws_seg.resize(2, 20); + local_sol_tris.resize(3, 18); + nw_pts = DTMutableList(20); + ns_pts = DTMutableList(20); + ws_pts = DTMutableList(20); + nws_pts = DTMutableList(20); + local_nws_pts = DTMutableList(20); + local_sol_pts = DTMutableList(20); + tmp = DTMutableList(20); + //......................................... + Values.resize(20); + DistanceValues.resize(20); + KGwns_values.resize(20); + KNwns_values.resize(20); + InterfaceSpeed.resize(20); + NormalVector.resize(60); + //......................................... + van.resize(3); + vaw.resize(3); + vawn.resize(3); + vawns.resize(3); + Gwn.resize(6); + Gns.resize(6); + Gws.resize(6); + van_global.resize(3); + vaw_global.resize(3); + vawn_global.resize(3); + vawns_global.resize(3); + Gwn_global.resize(6); + Gns_global.resize(6); + Gws_global.resize(6); + //......................................... + if (Dm->rank() == 0) { + TIMELOG = fopen("timelog.tcat", "a+"); + if (fseek(TIMELOG, 0, SEEK_SET) == fseek(TIMELOG, 0, SEEK_CUR)) { + // If timelog is empty, write a short header to list the averages + //fprintf(TIMELOG,"--------------------------------------------------------------------------------------\n"); + fprintf( + TIMELOG, + "time rn rw nun nuw Fx Fy Fz iftwn "); // Timestep, Change in Surface Energy + fprintf(TIMELOG, "sw pw pn awn ans aws Jwn Kwn lwns cwns KNwns " + "KGwns "); // Scalar averages + fprintf(TIMELOG, + "vawx vawy vawz vanx vany vanz "); // Velocity averages + fprintf(TIMELOG, "vawnx vawny vawnz vawnsx vawnsy vawnsz "); + fprintf( + TIMELOG, + "Gwnxx Gwnyy Gwnzz Gwnxy Gwnxz Gwnyz "); // Orientation tensors + fprintf(TIMELOG, "Gwsxx Gwsyy Gwszz Gwsxy Gwsxz Gwsyz "); + fprintf(TIMELOG, "Gnsxx Gnsyy Gnszz Gnsxy Gnsxz Gnsyz "); + fprintf(TIMELOG, "trawn trJwn trRwn "); //trimmed curvature, + fprintf(TIMELOG, + "wwndnw wwnsdnwn Jwnwwndnw "); //kinematic quantities, + fprintf(TIMELOG, "Vw Aw Jw Xw "); //miknowski measures, + fprintf(TIMELOG, "Vn An Jn Xn\n"); //miknowski measures, + // fprintf(TIMELOG,"Euler Kn Jn An\n"); //miknowski measures, + } - WPLOG = fopen("components.WP.tcat","a+"); - fprintf(WPLOG,"time label vol pw awn ans Jwn Kwn lwns cwns "); - fprintf(WPLOG,"vx vy vz vwnx vwny vwnz vwnsx vwnsy vwnsz vsq "); - fprintf(WPLOG,"Gwnxx Gwnyy Gwnzz Gwnxy Gwnxz Gwnyz Cx Cy Cz trawn trJwn\n"); - } - else{ - char LocalRankString[8]; - sprintf(LocalRankString,"%05d",Dm->rank()); - char LocalRankFilename[40]; - sprintf(LocalRankFilename,"%s%s","timelog.tcat.",LocalRankString); - TIMELOG = fopen(LocalRankFilename,"a+"); - //fprintf(TIMELOG,"--------------------------------------------------------------------------------------\n"); - fprintf(TIMELOG,"time rn rw nun nuw Fx Fy Fz iftwn ");; // Timestep, - fprintf(TIMELOG,"sw pw pn awn ans aws Jwn Kwn lwns cwns KNwns KGwns "); // Scalar averages - fprintf(TIMELOG,"vawx vawy vawz vanx vany vanz "); // Velocity averages - fprintf(TIMELOG,"vawnx vawny vawnz vawnsx vawnsy vawnsz "); - fprintf(TIMELOG,"Gwnxx Gwnyy Gwnzz Gwnxy Gwnxz Gwnyz "); // Orientation tensors - fprintf(TIMELOG,"Gwsxx Gwsyy Gwszz Gwsxy Gwsxz Gwsyz "); - fprintf(TIMELOG,"Gnsxx Gnsyy Gnszz Gnsxy Gnsxz Gnsyz "); - fprintf(TIMELOG,"trawn trJwn trRwn "); //trimmed curvature, - fprintf(TIMELOG,"wwndnw wwnsdnwn Jwnwwndnw "); //kinematic quantities, - fprintf(TIMELOG,"Vw Aw Jw Xw "); //miknowski measures, - fprintf(TIMELOG,"Vn An Jn Xn\n"); //miknowski measures, - // fprintf(TIMELOG,"Euler Kn Jn An\n"); //miknowski measures, - } + NWPLOG = fopen("components.NWP.tcat", "a+"); + fprintf(NWPLOG, "time label vol pn awn ans Jwn Kwn lwns cwns "); + fprintf(NWPLOG, "vx vy vz vwnx vwny vwnz vwnsx vwnsy vwnsz vsq "); + fprintf(NWPLOG, "Gwnxx Gwnyy Gwnzz Gwnxy Gwnxz Gwnyz Cx Cy Cz trawn " + "trJwn Kn Euler\n"); + + WPLOG = fopen("components.WP.tcat", "a+"); + fprintf(WPLOG, "time label vol pw awn ans Jwn Kwn lwns cwns "); + fprintf(WPLOG, "vx vy vz vwnx vwny vwnz vwnsx vwnsy vwnsz vsq "); + fprintf(WPLOG, + "Gwnxx Gwnyy Gwnzz Gwnxy Gwnxz Gwnyz Cx Cy Cz trawn trJwn\n"); + } else { + char LocalRankString[8]; + sprintf(LocalRankString, "%05d", Dm->rank()); + char LocalRankFilename[40]; + sprintf(LocalRankFilename, "%s%s", "timelog.tcat.", LocalRankString); + TIMELOG = fopen(LocalRankFilename, "a+"); + //fprintf(TIMELOG,"--------------------------------------------------------------------------------------\n"); + fprintf(TIMELOG, "time rn rw nun nuw Fx Fy Fz iftwn "); + ; // Timestep, + fprintf( + TIMELOG, + "sw pw pn awn ans aws Jwn Kwn lwns cwns KNwns KGwns "); // Scalar averages + fprintf(TIMELOG, "vawx vawy vawz vanx vany vanz "); // Velocity averages + fprintf(TIMELOG, "vawnx vawny vawnz vawnsx vawnsy vawnsz "); + fprintf(TIMELOG, + "Gwnxx Gwnyy Gwnzz Gwnxy Gwnxz Gwnyz "); // Orientation tensors + fprintf(TIMELOG, "Gwsxx Gwsyy Gwszz Gwsxy Gwsxz Gwsyz "); + fprintf(TIMELOG, "Gnsxx Gnsyy Gnszz Gnsxy Gnsxz Gnsyz "); + fprintf(TIMELOG, "trawn trJwn trRwn "); //trimmed curvature, + fprintf(TIMELOG, "wwndnw wwnsdnwn Jwnwwndnw "); //kinematic quantities, + fprintf(TIMELOG, "Vw Aw Jw Xw "); //miknowski measures, + fprintf(TIMELOG, "Vn An Jn Xn\n"); //miknowski measures, + // fprintf(TIMELOG,"Euler Kn Jn An\n"); //miknowski measures, + } } - // Destructor -TwoPhase::~TwoPhase() -{ - delete [] TempID; - if ( TIMELOG!=NULL ) { fclose(TIMELOG); } - if ( NWPLOG!=NULL ) { fclose(NWPLOG); } - if ( WPLOG!=NULL ) { fclose(WPLOG); } +TwoPhase::~TwoPhase() { + delete[] TempID; + if (TIMELOG != NULL) { + fclose(TIMELOG); + } + if (NWPLOG != NULL) { + fclose(NWPLOG); + } + if (WPLOG != NULL) { + fclose(WPLOG); + } } - -void TwoPhase::ColorToSignedDistance(double Beta, DoubleArray &ColorData, DoubleArray &DistData) -{ - NULL_USE( Beta ); - /*double factor,temp,value; +void TwoPhase::ColorToSignedDistance(double Beta, DoubleArray &ColorData, + DoubleArray &DistData) { + NULL_USE(Beta); + /*double factor,temp,value; factor=0.5/Beta; // Initialize to -1,1 (segmentation) for (int k=0; kCommunicateMeshHalo(Phase); - for (k=1; kCommunicateMeshHalo(Phase); + for (k = 1; k < Nz - 1; k++) { + for (j = 1; j < Ny - 1; j++) { + for (i = 1; i < Nx - 1; i++) { + // Compute all of the derivatives using finite differences + fx = 0.5 * (Phase(i + 1, j, k) - Phase(i - 1, j, k)); + fy = 0.5 * (Phase(i, j + 1, k) - Phase(i, j - 1, k)); + fz = 0.5 * (Phase(i, j, k + 1) - Phase(i, j, k - 1)); + DelPhi(i, j, k) = sqrt(fx * fx + fy * fy + fz * fz); + } + } + } } - -void TwoPhase::Initialize() -{ - trimdist=1.0; - fluid_isovalue=solid_isovalue=0.0; - // Initialize the averaged quantities - awn = aws = ans = lwns = 0.0; - nwp_volume = wp_volume = 0.0; - As = 0.0; - pan = paw = 0.0; - vaw(0) = vaw(1) = vaw(2) = 0.0; - van(0) = van(1) = van(2) = 0.0; - vawn(0) = vawn(1) = vawn(2) = 0.0; - vawns(0) = vawns(1) = vawns(2) = 0.0; - Gwn(0) = Gwn(1) = Gwn(2) = 0.0; - Gwn(3) = Gwn(4) = Gwn(5) = 0.0; - Gws(0) = Gws(1) = Gws(2) = 0.0; - Gws(3) = Gws(4) = Gws(5) = 0.0; - Gns(0) = Gns(1) = Gns(2) = 0.0; - Gns(3) = Gns(4) = Gns(5) = 0.0; - vol_w = vol_n =0.0; - KGwns = KNwns = 0.0; - Jwn = Kwn = efawns = 0.0; - trJwn = trawn = trRwn = 0.0; - euler = Jn = An = Kn = 0.0; - wwndnw = 0.0; wwnsdnwn = 0.0; Jwnwwndnw=0.0; +void TwoPhase::Initialize() { + trimdist = 1.0; + fluid_isovalue = solid_isovalue = 0.0; + // Initialize the averaged quantities + awn = aws = ans = lwns = 0.0; + nwp_volume = wp_volume = 0.0; + As = 0.0; + pan = paw = 0.0; + vaw(0) = vaw(1) = vaw(2) = 0.0; + van(0) = van(1) = van(2) = 0.0; + vawn(0) = vawn(1) = vawn(2) = 0.0; + vawns(0) = vawns(1) = vawns(2) = 0.0; + Gwn(0) = Gwn(1) = Gwn(2) = 0.0; + Gwn(3) = Gwn(4) = Gwn(5) = 0.0; + Gws(0) = Gws(1) = Gws(2) = 0.0; + Gws(3) = Gws(4) = Gws(5) = 0.0; + Gns(0) = Gns(1) = Gns(2) = 0.0; + Gns(3) = Gns(4) = Gns(5) = 0.0; + vol_w = vol_n = 0.0; + KGwns = KNwns = 0.0; + Jwn = Kwn = efawns = 0.0; + trJwn = trawn = trRwn = 0.0; + euler = Jn = An = Kn = 0.0; + wwndnw = 0.0; + wwnsdnwn = 0.0; + Jwnwwndnw = 0.0; } -void TwoPhase::SetParams(double rhoA, double rhoB, double tauA, double tauB, double force_x, double force_y, double force_z, double alpha) -{ - Fx = force_x; - Fy = force_y; - Fz = force_z; - rho_n = rhoA; - rho_w = rhoB; - nu_n = (tauA-0.5)/3.f; - nu_w = (tauB-0.5)/3.f; - gamma_wn = 5.796*alpha; - +void TwoPhase::SetParams(double rhoA, double rhoB, double tauA, double tauB, + double force_x, double force_y, double force_z, + double alpha) { + Fx = force_x; + Fy = force_y; + Fz = force_z; + rho_n = rhoA; + rho_w = rhoB; + nu_n = (tauA - 0.5) / 3.f; + nu_w = (tauB - 0.5) / 3.f; + gamma_wn = 5.796 * alpha; } /* @@ -378,1121 +413,1212 @@ void TwoPhase::SetupCubes(Domain &Dm) } */ - -void TwoPhase::UpdateSolid() -{ - Dm->CommunicateMeshHalo(SDs); - //........................................................................... - // Gradient of the Signed Distance function - //........................................................................... - pmmc_MeshGradient(SDs,SDs_x,SDs_y,SDs_z,Nx,Ny,Nz); - //........................................................................... - Dm->CommunicateMeshHalo(SDs_x); - //........................................................................... - Dm->CommunicateMeshHalo(SDs_y); - //........................................................................... - Dm->CommunicateMeshHalo(SDs_z); - //........................................................................... +void TwoPhase::UpdateSolid() { + Dm->CommunicateMeshHalo(SDs); + //........................................................................... + // Gradient of the Signed Distance function + //........................................................................... + pmmc_MeshGradient(SDs, SDs_x, SDs_y, SDs_z, Nx, Ny, Nz); + //........................................................................... + Dm->CommunicateMeshHalo(SDs_x); + //........................................................................... + Dm->CommunicateMeshHalo(SDs_y); + //........................................................................... + Dm->CommunicateMeshHalo(SDs_z); + //........................................................................... } - -void TwoPhase::UpdateMeshValues() -{ - int i,j,k,n; - //........................................................................... - Dm->CommunicateMeshHalo(SDn); - //........................................................................... - // Compute the gradients of the phase indicator and signed distance fields - pmmc_MeshGradient(SDn,SDn_x,SDn_y,SDn_z,Nx,Ny,Nz); - //........................................................................... - // Gradient of the phase indicator field - //........................................................................... - Dm->CommunicateMeshHalo(SDn_x); - //........................................................................... - Dm->CommunicateMeshHalo(SDn_y); - //........................................................................... - Dm->CommunicateMeshHalo(SDn_z); - //........................................................................... - Dm->CommunicateMeshHalo(SDs); - pmmc_MeshGradient(SDs,SDs_x,SDs_y,SDs_z,Nx,Ny,Nz); - //........................................................................... - Dm->CommunicateMeshHalo(SDs_x); - //........................................................................... - Dm->CommunicateMeshHalo(SDs_y); - //........................................................................... - Dm->CommunicateMeshHalo(SDs_z); - //........................................................................... - // Compute the mesh curvature of the phase indicator field - pmmc_MeshCurvature(SDn, MeanCurvature, GaussCurvature, Nx, Ny, Nz); - //........................................................................... - // Update the time derivative of non-dimensional density field - // Map Phase_tplus and Phase_tminus - for (int n=0; nCommunicateMeshHalo(Press); - //........................................................................... - Dm->CommunicateMeshHalo(Vel_x); - //........................................................................... - Dm->CommunicateMeshHalo(Vel_y); - //........................................................................... - Dm->CommunicateMeshHalo(Vel_z); - //........................................................................... - Dm->CommunicateMeshHalo(MeanCurvature); - //........................................................................... - Dm->CommunicateMeshHalo(GaussCurvature); - //........................................................................... - Dm->CommunicateMeshHalo(DelPhi); - //........................................................................... - // Initializing the blob ID - for (k=0; kid[n] > 0)){ - // Solid phase - PhaseID(i,j,k) = 0; - } - else if (SDn(i,j,k) < 0.0){ - // wetting phase - PhaseID(i,j,k) = 2; - } - else { - // non-wetting phase - PhaseID(i,j,k) = 1; - } - } - } - } +void TwoPhase::UpdateMeshValues() { + int i, j, k, n; + //........................................................................... + Dm->CommunicateMeshHalo(SDn); + //........................................................................... + // Compute the gradients of the phase indicator and signed distance fields + pmmc_MeshGradient(SDn, SDn_x, SDn_y, SDn_z, Nx, Ny, Nz); + //........................................................................... + // Gradient of the phase indicator field + //........................................................................... + Dm->CommunicateMeshHalo(SDn_x); + //........................................................................... + Dm->CommunicateMeshHalo(SDn_y); + //........................................................................... + Dm->CommunicateMeshHalo(SDn_z); + //........................................................................... + Dm->CommunicateMeshHalo(SDs); + pmmc_MeshGradient(SDs, SDs_x, SDs_y, SDs_z, Nx, Ny, Nz); + //........................................................................... + Dm->CommunicateMeshHalo(SDs_x); + //........................................................................... + Dm->CommunicateMeshHalo(SDs_y); + //........................................................................... + Dm->CommunicateMeshHalo(SDs_z); + //........................................................................... + // Compute the mesh curvature of the phase indicator field + pmmc_MeshCurvature(SDn, MeanCurvature, GaussCurvature, Nx, Ny, Nz); + //........................................................................... + // Update the time derivative of non-dimensional density field + // Map Phase_tplus and Phase_tminus + for (int n = 0; n < Nx * Ny * Nz; n++) + dPdt(n) = 0.125 * (Phase_tplus(n) - Phase_tminus(n)); + //........................................................................... + Dm->CommunicateMeshHalo(Press); + //........................................................................... + Dm->CommunicateMeshHalo(Vel_x); + //........................................................................... + Dm->CommunicateMeshHalo(Vel_y); + //........................................................................... + Dm->CommunicateMeshHalo(Vel_z); + //........................................................................... + Dm->CommunicateMeshHalo(MeanCurvature); + //........................................................................... + Dm->CommunicateMeshHalo(GaussCurvature); + //........................................................................... + Dm->CommunicateMeshHalo(DelPhi); + //........................................................................... + // Initializing the blob ID + for (k = 0; k < Nz; k++) { + for (j = 0; j < Ny; j++) { + for (i = 0; i < Nx; i++) { + n = k * Nx * Ny + j * Nx + i; + if (!(Dm->id[n] > 0)) { + // Solid phase + PhaseID(i, j, k) = 0; + } else if (SDn(i, j, k) < 0.0) { + // wetting phase + PhaseID(i, j, k) = 2; + } else { + // non-wetting phase + PhaseID(i, j, k) = 1; + } + } + } + } } -void TwoPhase::ComputeLocal() -{ - int i,j,k,n,imin,jmin,kmin,kmax; - int cube[8][3] = {{0,0,0},{1,0,0},{0,1,0},{1,1,0},{0,0,1},{1,0,1},{0,1,1},{1,1,1}}; +void TwoPhase::ComputeLocal() { + int i, j, k, n, imin, jmin, kmin, kmax; + int cube[8][3] = {{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {1, 1, 0}, + {0, 0, 1}, {1, 0, 1}, {0, 1, 1}, {1, 1, 1}}; - // If external boundary conditions are set, do not average over the inlet - kmin=1; kmax=Nz-1; - if (Dm->BoundaryCondition > 0 && Dm->kproc() == 0) kmin=4; - if (Dm->BoundaryCondition > 0 && Dm->kproc() == Dm->nprocz()-1) kmax=Nz-4; + // If external boundary conditions are set, do not average over the inlet + kmin = 1; + kmax = Nz - 1; + if (Dm->BoundaryCondition > 0 && Dm->kproc() == 0) + kmin = 4; + if (Dm->BoundaryCondition > 0 && Dm->kproc() == Dm->nprocz() - 1) + kmax = Nz - 4; - imin=jmin=1; - // If inlet layers exist use these as default - if (Dm->inlet_layers_x > 0) imin = Dm->inlet_layers_x; - if (Dm->inlet_layers_y > 0) jmin = Dm->inlet_layers_y; - if (Dm->inlet_layers_z > 0) kmin = Dm->inlet_layers_z; - - for (k=kmin; kid[n] > 0 ){ - // 1-D index for this cube corner - // compute the norm of the gradient of the phase indicator field - // Compute the non-wetting phase volume contribution - if ( Phase(i+cube[p][0],j+cube[p][1],k+cube[p][2]) > 0 ){ - nwp_volume += 0.125; - // velocity - van(0) += 0.125*Vel_x(n); - van(1) += 0.125*Vel_y(n); - van(2) += 0.125*Vel_z(n); - // volume the excludes the interfacial region - if (DelPhi(n) < 1e-4){ - vol_n += 0.125; - // pressure - pan += 0.125*Press(n); + imin = jmin = 1; + // If inlet layers exist use these as default + if (Dm->inlet_layers_x > 0) + imin = Dm->inlet_layers_x; + if (Dm->inlet_layers_y > 0) + jmin = Dm->inlet_layers_y; + if (Dm->inlet_layers_z > 0) + kmin = Dm->inlet_layers_z; - } - } - else{ - wp_volume += 0.125; - // velocity - vaw(0) += 0.125*Vel_x(n); - vaw(1) += 0.125*Vel_y(n); - vaw(2) += 0.125*Vel_z(n); - if (DelPhi(n) < 1e-4){ - // volume the excludes the interfacial region - vol_w += 0.125; - // pressure - paw += 0.125*Press(n); + for (k = kmin; k < kmax; k++) { + for (j = jmin; j < Ny - 1; j++) { + for (i = imin; i < Nx - 1; i++) { + //........................................................................... + n_nw_pts = n_ns_pts = n_ws_pts = n_nws_pts = n_local_sol_pts = + n_local_nws_pts = 0; + n_nw_tris = n_ns_tris = n_ws_tris = n_nws_seg = + n_local_sol_tris = 0; + //........................................................................... + // Compute volume averages + for (int p = 0; p < 8; p++) { + n = i + cube[p][0] + (j + cube[p][1]) * Nx + + (k + cube[p][2]) * Nx * Ny; + if (Dm->id[n] > 0) { + // 1-D index for this cube corner + // compute the norm of the gradient of the phase indicator field + // Compute the non-wetting phase volume contribution + if (Phase(i + cube[p][0], j + cube[p][1], + k + cube[p][2]) > 0) { + nwp_volume += 0.125; + // velocity + van(0) += 0.125 * Vel_x(n); + van(1) += 0.125 * Vel_y(n); + van(2) += 0.125 * Vel_z(n); + // volume the excludes the interfacial region + if (DelPhi(n) < 1e-4) { + vol_n += 0.125; + // pressure + pan += 0.125 * Press(n); + } + } else { + wp_volume += 0.125; + // velocity + vaw(0) += 0.125 * Vel_x(n); + vaw(1) += 0.125 * Vel_y(n); + vaw(2) += 0.125 * Vel_z(n); + if (DelPhi(n) < 1e-4) { + // volume the excludes the interfacial region + vol_w += 0.125; + // pressure + paw += 0.125 * Press(n); + } + } + } + } - } - } - } - } + //........................................................................... + // Construct the interfaces and common curve + pmmc_ConstructLocalCube( + SDs, SDn, solid_isovalue, fluid_isovalue, nw_pts, nw_tris, + Values, ns_pts, ns_tris, ws_pts, ws_tris, local_nws_pts, + nws_pts, nws_seg, local_sol_pts, local_sol_tris, + n_local_sol_tris, n_local_sol_pts, n_nw_pts, n_nw_tris, + n_ws_pts, n_ws_tris, n_ns_tris, n_ns_pts, n_local_nws_pts, + n_nws_pts, n_nws_seg, i, j, k, Nx, Ny, Nz); - //........................................................................... - // Construct the interfaces and common curve - pmmc_ConstructLocalCube(SDs, SDn, solid_isovalue, fluid_isovalue, - nw_pts, nw_tris, Values, ns_pts, ns_tris, ws_pts, ws_tris, - local_nws_pts, nws_pts, nws_seg, local_sol_pts, local_sol_tris, - n_local_sol_tris, n_local_sol_pts, n_nw_pts, n_nw_tris, - n_ws_pts, n_ws_tris, n_ns_tris, n_ns_pts, n_local_nws_pts, n_nws_pts, n_nws_seg, - i, j, k, Nx, Ny, Nz); + // wn interface averages + if (n_nw_pts > 0) { + awn += pmmc_CubeSurfaceOrientation(Gwn, nw_pts, nw_tris, + n_nw_tris); + Kwn += pmmc_CubeSurfaceInterpValue( + CubeValues, GaussCurvature, nw_pts, nw_tris, Values, i, + j, k, n_nw_pts, n_nw_tris); + Jwn += pmmc_CubeSurfaceInterpValue( + CubeValues, MeanCurvature, nw_pts, nw_tris, Values, i, + j, k, n_nw_pts, n_nw_tris); - // wn interface averages - if (n_nw_pts > 0){ - awn += pmmc_CubeSurfaceOrientation(Gwn,nw_pts,nw_tris,n_nw_tris); - Kwn += pmmc_CubeSurfaceInterpValue(CubeValues,GaussCurvature,nw_pts,nw_tris,Values,i,j,k,n_nw_pts,n_nw_tris); - Jwn += pmmc_CubeSurfaceInterpValue(CubeValues,MeanCurvature,nw_pts,nw_tris,Values,i,j,k,n_nw_pts,n_nw_tris); + // Compute the normal speed of the interface + wwndnw += pmmc_InterfaceSpeed( + dPdt, SDn_x, SDn_y, SDn_z, CubeValues, nw_pts, nw_tris, + NormalVector, InterfaceSpeed, vawn, i, j, k, n_nw_pts, + n_nw_tris); - // Compute the normal speed of the interface - wwndnw += pmmc_InterfaceSpeed(dPdt, SDn_x, SDn_y, SDn_z, CubeValues, nw_pts, nw_tris, - NormalVector, InterfaceSpeed, vawn, i, j, k, n_nw_pts, n_nw_tris); + //for (int p=0; p 0) { + efawns += pmmc_CubeContactAngle( + CubeValues, Values, SDn_x, SDn_y, SDn_z, SDs_x, SDs_y, + SDs_z, local_nws_pts, i, j, k, n_local_nws_pts); - pmmc_CubeTrimSurfaceInterpInverseValues(CubeValues,MeanCurvature,SDs,nw_pts,nw_tris,Values,DistanceValues, - i,j,k,n_nw_pts,n_nw_tris,trimdist,dummy,trRwn); + wwnsdnwn += pmmc_CommonCurveSpeed( + CubeValues, dPdt, vawns, SDn_x, SDn_y, SDn_z, SDs_x, + SDs_y, SDs_z, local_nws_pts, i, j, k, n_local_nws_pts); - } - // wns common curve averages - if (n_local_nws_pts > 0){ - efawns += pmmc_CubeContactAngle(CubeValues,Values,SDn_x,SDn_y,SDn_z,SDs_x,SDs_y,SDs_z, - local_nws_pts,i,j,k,n_local_nws_pts); + pmmc_CurveCurvature(SDn, SDs, SDn_x, SDn_y, SDn_z, SDs_x, + SDs_y, SDs_z, KNwns_values, + KGwns_values, KNwns, KGwns, nws_pts, + n_nws_pts, i, j, k); - wwnsdnwn += pmmc_CommonCurveSpeed(CubeValues, dPdt, vawns, SDn_x, SDn_y, SDn_z,SDs_x,SDs_y,SDs_z, - local_nws_pts,i,j,k,n_local_nws_pts); + lwns += + pmmc_CubeCurveLength(local_nws_pts, n_local_nws_pts); + } - pmmc_CurveCurvature(SDn, SDs, SDn_x, SDn_y, SDn_z, SDs_x, SDs_y, - SDs_z, KNwns_values, KGwns_values, KNwns, KGwns, - nws_pts, n_nws_pts, i, j, k); + // Solid interface averagees + if (n_local_sol_tris > 0) { + As += pmmc_CubeSurfaceArea(local_sol_pts, local_sol_tris, + n_local_sol_tris); - lwns += pmmc_CubeCurveLength(local_nws_pts,n_local_nws_pts); - } + // Compute the surface orientation and the interfacial area + ans += pmmc_CubeSurfaceOrientation(Gns, ns_pts, ns_tris, + n_ns_tris); + aws += pmmc_CubeSurfaceOrientation(Gws, ws_pts, ws_tris, + n_ws_tris); + } + //........................................................................... + // Compute the integral curvature of the non-wetting phase - // Solid interface averagees - if (n_local_sol_tris > 0){ - As += pmmc_CubeSurfaceArea(local_sol_pts,local_sol_tris,n_local_sol_tris); + n_nw_pts = n_nw_tris = 0; + // Compute the non-wetting phase surface and associated area + An += + geomavg_MarchingCubes(SDn, fluid_isovalue, i, j, k, nw_pts, + n_nw_pts, nw_tris, n_nw_tris); + // Compute the integral of mean curvature + if (n_nw_pts > 0) { + pmmc_CubeTrimSurfaceInterpValues( + CubeValues, MeanCurvature, SDs, nw_pts, nw_tris, Values, + DistanceValues, i, j, k, n_nw_pts, n_nw_tris, trimdist, + trawn, dummy); + } - // Compute the surface orientation and the interfacial area - ans += pmmc_CubeSurfaceOrientation(Gns,ns_pts,ns_tris,n_ns_tris); - aws += pmmc_CubeSurfaceOrientation(Gws,ws_pts,ws_tris,n_ws_tris); - } - //........................................................................... - // Compute the integral curvature of the non-wetting phase + Jn += pmmc_CubeSurfaceInterpValue(CubeValues, MeanCurvature, + nw_pts, nw_tris, Values, i, j, + k, n_nw_pts, n_nw_tris); + // Compute Euler characteristic from integral of gaussian curvature + Kn += pmmc_CubeSurfaceInterpValue(CubeValues, GaussCurvature, + nw_pts, nw_tris, Values, i, j, + k, n_nw_pts, n_nw_tris); - n_nw_pts=n_nw_tris=0; - // Compute the non-wetting phase surface and associated area - An += geomavg_MarchingCubes(SDn,fluid_isovalue,i,j,k,nw_pts,n_nw_pts,nw_tris,n_nw_tris); - // Compute the integral of mean curvature - if (n_nw_pts>0){ - pmmc_CubeTrimSurfaceInterpValues(CubeValues,MeanCurvature,SDs,nw_pts,nw_tris,Values,DistanceValues, - i,j,k,n_nw_pts,n_nw_tris,trimdist,trawn,dummy); - } - - Jn += pmmc_CubeSurfaceInterpValue(CubeValues,MeanCurvature,nw_pts,nw_tris,Values, - i,j,k,n_nw_pts,n_nw_tris); - // Compute Euler characteristic from integral of gaussian curvature - Kn += pmmc_CubeSurfaceInterpValue(CubeValues,GaussCurvature,nw_pts,nw_tris,Values, - i,j,k,n_nw_pts,n_nw_tris); - - euler += geomavg_EulerCharacteristic(nw_pts,nw_tris,n_nw_pts,n_nw_tris,i,j,k); - - } - } - } - - Array phase_label(Nx,Ny,Nz); - Array phase_distance(Nx,Ny,Nz); - // Analyze the wetting fluid - for (k=0; kid[n] > 0)){ - // Solid phase - phase_label(i,j,k) = 1; - } - else if (SDn(i,j,k) < 0.0){ - // wetting phase - phase_label(i,j,k) = 0; - } - else { - // non-wetting phase - phase_label(i,j,k) = 1; - } - phase_distance(i,j,k) =2.0*double(phase_label(i,j,k))-1.0; - } - } - } - CalcDist(phase_distance,phase_label,*Dm); - wet_morph->ComputeScalar(phase_distance,0.f); - //printf("generating distance at rank=%i \n",Dm->rank()); - // Analyze the wetting fluid - for (k=0; kid[n] > 0)){ - // Solid phase - phase_label(i,j,k) = 1; - } - else if (SDn(i,j,k) < 0.0){ - // wetting phase - phase_label(i,j,k) = 1; - } - else { - // non-wetting phase - phase_label(i,j,k) = 0; - } - phase_distance(i,j,k) =2.0*double(phase_label(i,j,k))-1.0; - } - } - } - //printf("calculate distance at rank=%i \n",Dm->rank()); - CalcDist(phase_distance,phase_label,*Dm); - //printf("morphological analysis at rank=%i \n",Dm->rank()); - nonwet_morph->ComputeScalar(phase_distance,0.f); - //printf("rank=%i completed \n",Dm->rank()); + euler += geomavg_EulerCharacteristic(nw_pts, nw_tris, n_nw_pts, + n_nw_tris, i, j, k); + } + } + } + Array phase_label(Nx, Ny, Nz); + Array phase_distance(Nx, Ny, Nz); + // Analyze the wetting fluid + for (k = 0; k < Nz; k++) { + for (j = 0; j < Ny; j++) { + for (i = 0; i < Nx; i++) { + n = k * Nx * Ny + j * Nx + i; + if (!(Dm->id[n] > 0)) { + // Solid phase + phase_label(i, j, k) = 1; + } else if (SDn(i, j, k) < 0.0) { + // wetting phase + phase_label(i, j, k) = 0; + } else { + // non-wetting phase + phase_label(i, j, k) = 1; + } + phase_distance(i, j, k) = + 2.0 * double(phase_label(i, j, k)) - 1.0; + } + } + } + CalcDist(phase_distance, phase_label, *Dm); + wet_morph->ComputeScalar(phase_distance, 0.f); + //printf("generating distance at rank=%i \n",Dm->rank()); + // Analyze the wetting fluid + for (k = 0; k < Nz; k++) { + for (j = 0; j < Ny; j++) { + for (i = 0; i < Nx; i++) { + n = k * Nx * Ny + j * Nx + i; + if (!(Dm->id[n] > 0)) { + // Solid phase + phase_label(i, j, k) = 1; + } else if (SDn(i, j, k) < 0.0) { + // wetting phase + phase_label(i, j, k) = 1; + } else { + // non-wetting phase + phase_label(i, j, k) = 0; + } + phase_distance(i, j, k) = + 2.0 * double(phase_label(i, j, k)) - 1.0; + } + } + } + //printf("calculate distance at rank=%i \n",Dm->rank()); + CalcDist(phase_distance, phase_label, *Dm); + //printf("morphological analysis at rank=%i \n",Dm->rank()); + nonwet_morph->ComputeScalar(phase_distance, 0.f); + //printf("rank=%i completed \n",Dm->rank()); } +void TwoPhase::AssignComponentLabels() { + //int LabelNWP=1; + //int LabelWP=2; + // NOTE: labeling the wetting phase components is tricky! One sandstone media had over 800,000 components + // NumberComponents_WP = ComputeGlobalPhaseComponent(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2,Dm->rank_info,PhaseID,LabelWP,Label_WP); + // treat all wetting phase is connected + NumberComponents_WP = 1; + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + Label_WP(i, j, k) = 0; + //if (SDs(i,j,k) > 0.0) PhaseID(i,j,k) = 0; + //else if (Phase(i,j,k) > 0.0) PhaseID(i,j,k) = LabelNWP; + //else PhaseID(i,j,k) = LabelWP; + } + } + } -void TwoPhase::AssignComponentLabels() -{ - //int LabelNWP=1; - //int LabelWP=2; - // NOTE: labeling the wetting phase components is tricky! One sandstone media had over 800,000 components - // NumberComponents_WP = ComputeGlobalPhaseComponent(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2,Dm->rank_info,PhaseID,LabelWP,Label_WP); - // treat all wetting phase is connected - NumberComponents_WP=1; - for (int k=0; k 0.0) PhaseID(i,j,k) = 0; - //else if (Phase(i,j,k) > 0.0) PhaseID(i,j,k) = LabelNWP; - //else PhaseID(i,j,k) = LabelWP; - } - } - } - - // Fewer non-wetting phase features are present - //NumberComponents_NWP = ComputeGlobalPhaseComponent(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2,Dm->rank_info,PhaseID,LabelNWP,Label_NWP); - NumberComponents_NWP = ComputeGlobalBlobIDs(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2,Dm->rank_info,SDs,SDn,solid_isovalue,fluid_isovalue,Label_NWP,Dm->Comm); + // Fewer non-wetting phase features are present + //NumberComponents_NWP = ComputeGlobalPhaseComponent(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2,Dm->rank_info,PhaseID,LabelNWP,Label_NWP); + NumberComponents_NWP = ComputeGlobalBlobIDs( + Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2, Dm->rank_info, SDs, SDn, + solid_isovalue, fluid_isovalue, Label_NWP, Dm->Comm); } -void TwoPhase::ComponentAverages() -{ - int i,j,k,n; - int kmin,kmax; - int LabelWP,LabelNWP; - double TempLocal; +void TwoPhase::ComponentAverages() { + int i, j, k, n; + int kmin, kmax; + int LabelWP, LabelNWP; + double TempLocal; - int cube[8][3] = {{0,0,0},{1,0,0},{0,1,0},{1,1,0},{0,0,1},{1,0,1},{0,1,1},{1,1,1}}; + int cube[8][3] = {{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {1, 1, 0}, + {0, 0, 1}, {1, 0, 1}, {0, 1, 1}, {1, 1, 1}}; - ComponentAverages_WP.resize(BLOB_AVG_COUNT,NumberComponents_WP); - ComponentAverages_NWP.resize(BLOB_AVG_COUNT,NumberComponents_NWP); + ComponentAverages_WP.resize(BLOB_AVG_COUNT, NumberComponents_WP); + ComponentAverages_NWP.resize(BLOB_AVG_COUNT, NumberComponents_NWP); - ComponentAverages_WP.fill(0.0); - ComponentAverages_NWP.fill(0.0); - - if (Dm->rank()==0){ - printf("Number of wetting phase components is %i \n",NumberComponents_WP); - printf("Number of non-wetting phase components is %i \n",NumberComponents_NWP); - } + ComponentAverages_WP.fill(0.0); + ComponentAverages_NWP.fill(0.0); - // If external boundary conditions are set, do not average over the inlet - kmin=1; kmax=Nz-1; - if (Dm->BoundaryCondition > 0 && Dm->kproc() == 0) kmin=4; - if (Dm->BoundaryCondition > 0 && Dm->kproc() == Dm->nprocz()-1) kmax=Nz-4; - - for (k=kmin; krank() == 0) { + printf("Number of wetting phase components is %i \n", + NumberComponents_WP); + printf("Number of non-wetting phase components is %i \n", + NumberComponents_NWP); + } - n_nw_pts=n_ns_pts=n_ws_pts=n_nws_pts=n_local_sol_pts=n_local_nws_pts=0; - n_nw_tris=n_ns_tris=n_ws_tris=n_nws_seg=n_local_sol_tris=0; + // If external boundary conditions are set, do not average over the inlet + kmin = 1; + kmax = Nz - 1; + if (Dm->BoundaryCondition > 0 && Dm->kproc() == 0) + kmin = 4; + if (Dm->BoundaryCondition > 0 && Dm->kproc() == Dm->nprocz() - 1) + kmax = Nz - 4; - // Initialize the averaged quantities - awn = aws = ans = lwns = 0.0; - vawn(0) = vawn(1) = vawn(2) = 0.0; - vawns(0) = vawns(1) = vawns(2) = 0.0; - Gwn(0) = Gwn(1) = Gwn(2) = 0.0; - Gwn(3) = Gwn(4) = Gwn(5) = 0.0; - Gws(0) = Gws(1) = Gws(2) = 0.0; - Gws(3) = Gws(4) = Gws(5) = 0.0; - Gns(0) = Gns(1) = Gns(2) = 0.0; - Gns(3) = Gns(4) = Gns(5) = 0.0; - KGwns = KNwns = 0.0; - Jwn = Kwn = efawns = 0.0; - trawn=trJwn=0.0; - euler=0.0; + for (k = kmin; k < kmax; k++) { + for (j = 1; j < Ny - 1; j++) { + for (i = 1; i < Nx - 1; i++) { + LabelWP = GetCubeLabel(i, j, k, Label_WP); + LabelNWP = GetCubeLabel(i, j, k, Label_NWP); - //........................................................................... - //........................................................................... - // Compute volume averages - for (int p=0;p<8;p++){ - n = i+cube[p][0] + (j+cube[p][1])*Nx + (k+cube[p][2])*Nx*Ny; - if ( Dm->id[n] > 0 ){ - // 1-D index for this cube corner - // compute the norm of the gradient of the phase indicator field - // Compute the non-wetting phase volume contribution - if ( Phase(i+cube[p][0],j+cube[p][1],k+cube[p][2]) > 0.0 && !(LabelNWP < 0) ){ - // volume - ComponentAverages_NWP(VOL,LabelNWP) += 0.125; - // velocity - ComponentAverages_NWP(VX,LabelNWP) += 0.125*Vel_x(n); - ComponentAverages_NWP(VY,LabelNWP) += 0.125*Vel_y(n); - ComponentAverages_NWP(VZ,LabelNWP) += 0.125*Vel_z(n); - // center of mass - ComponentAverages_NWP(CMX,LabelNWP) += 0.125*(i+cube[p][0]+Dm->iproc()*Nx); - ComponentAverages_NWP(CMY,LabelNWP) += 0.125*(j+cube[p][1]+Dm->jproc()*Ny); - ComponentAverages_NWP(CMZ,LabelNWP) += 0.125*(k+cube[p][2]+Dm->kproc()*Nz); + n_nw_pts = n_ns_pts = n_ws_pts = n_nws_pts = n_local_sol_pts = + n_local_nws_pts = 0; + n_nw_tris = n_ns_tris = n_ws_tris = n_nws_seg = + n_local_sol_tris = 0; - // twice the kinetic energy - ComponentAverages_NWP(VSQ,LabelNWP) += 0.125*(Vel_x(n)*Vel_x(n)+Vel_y(n)*Vel_y(n)+Vel_z(n)*Vel_z(n)); + // Initialize the averaged quantities + awn = aws = ans = lwns = 0.0; + vawn(0) = vawn(1) = vawn(2) = 0.0; + vawns(0) = vawns(1) = vawns(2) = 0.0; + Gwn(0) = Gwn(1) = Gwn(2) = 0.0; + Gwn(3) = Gwn(4) = Gwn(5) = 0.0; + Gws(0) = Gws(1) = Gws(2) = 0.0; + Gws(3) = Gws(4) = Gws(5) = 0.0; + Gns(0) = Gns(1) = Gns(2) = 0.0; + Gns(3) = Gns(4) = Gns(5) = 0.0; + KGwns = KNwns = 0.0; + Jwn = Kwn = efawns = 0.0; + trawn = trJwn = 0.0; + euler = 0.0; - // volume the for pressure averaging excludes the interfacial region - if (DelPhi(n) < 1e-4 ){ - ComponentAverages_NWP(TRIMVOL,LabelNWP) += 0.125; - ComponentAverages_NWP(PRS,LabelNWP) += 0.125*Press(n); - } - } - else if (!(LabelWP < 0)){ - ComponentAverages_WP(VOL,LabelWP) += 0.125; - // velocity - ComponentAverages_WP(VX,LabelWP) += 0.125*Vel_x(n); - ComponentAverages_WP(VY,LabelWP)+= 0.125*Vel_y(n); - ComponentAverages_WP(VZ,LabelWP) += 0.125*Vel_z(n); - // Center of mass - ComponentAverages_WP(CMX,LabelWP) += 0.125*(i+cube[p][0]+Dm->iproc()*Nx); - ComponentAverages_WP(CMY,LabelWP) += 0.125*(j+cube[p][1]+Dm->jproc()*Ny); - ComponentAverages_WP(CMZ,LabelWP) += 0.125*(k+cube[p][2]+Dm->kproc()*Nz); - // twice the kinetic energy - ComponentAverages_WP(VSQ,LabelWP) += 0.125*(Vel_x(n)*Vel_x(n)+Vel_y(n)*Vel_y(n)+Vel_z(n)*Vel_z(n)); + //........................................................................... + //........................................................................... + // Compute volume averages + for (int p = 0; p < 8; p++) { + n = i + cube[p][0] + (j + cube[p][1]) * Nx + + (k + cube[p][2]) * Nx * Ny; + if (Dm->id[n] > 0) { + // 1-D index for this cube corner + // compute the norm of the gradient of the phase indicator field + // Compute the non-wetting phase volume contribution + if (Phase(i + cube[p][0], j + cube[p][1], + k + cube[p][2]) > 0.0 && + !(LabelNWP < 0)) { + // volume + ComponentAverages_NWP(VOL, LabelNWP) += 0.125; + // velocity + ComponentAverages_NWP(VX, LabelNWP) += + 0.125 * Vel_x(n); + ComponentAverages_NWP(VY, LabelNWP) += + 0.125 * Vel_y(n); + ComponentAverages_NWP(VZ, LabelNWP) += + 0.125 * Vel_z(n); + // center of mass + ComponentAverages_NWP(CMX, LabelNWP) += + 0.125 * (i + cube[p][0] + Dm->iproc() * Nx); + ComponentAverages_NWP(CMY, LabelNWP) += + 0.125 * (j + cube[p][1] + Dm->jproc() * Ny); + ComponentAverages_NWP(CMZ, LabelNWP) += + 0.125 * (k + cube[p][2] + Dm->kproc() * Nz); - // volume the for pressure averaging excludes the interfacial region - if (DelPhi(n) < 1e-4){ - ComponentAverages_WP(TRIMVOL,LabelWP) += 0.125; - ComponentAverages_WP(PRS,LabelWP) += 0.125*Press(n); - } - } - } - } - //........................................................................... - // Construct the interfaces and common curve - pmmc_ConstructLocalCube(SDs, SDn, solid_isovalue, fluid_isovalue, - nw_pts, nw_tris, Values, ns_pts, ns_tris, ws_pts, ws_tris, - local_nws_pts, nws_pts, nws_seg, local_sol_pts, local_sol_tris, - n_local_sol_tris, n_local_sol_pts, n_nw_pts, n_nw_tris, - n_ws_pts, n_ws_tris, n_ns_tris, n_ns_pts, n_local_nws_pts, n_nws_pts, n_nws_seg, - i, j, k, Nx, Ny, Nz); + // twice the kinetic energy + ComponentAverages_NWP(VSQ, LabelNWP) += + 0.125 * + (Vel_x(n) * Vel_x(n) + Vel_y(n) * Vel_y(n) + + Vel_z(n) * Vel_z(n)); - //........................................................................... - // wn interface averages - if (n_nw_pts>0 && LabelNWP >=0 && LabelWP >=0 ){ - // Mean curvature - TempLocal = pmmc_CubeSurfaceInterpValue(CubeValues,MeanCurvature,nw_pts,nw_tris,Values,i,j,k,n_nw_pts,n_nw_tris); - ComponentAverages_WP(JWN,LabelWP) += TempLocal; - ComponentAverages_NWP(JWN,LabelNWP) += TempLocal; + // volume the for pressure averaging excludes the interfacial region + if (DelPhi(n) < 1e-4) { + ComponentAverages_NWP(TRIMVOL, LabelNWP) += + 0.125; + ComponentAverages_NWP(PRS, LabelNWP) += + 0.125 * Press(n); + } + } else if (!(LabelWP < 0)) { + ComponentAverages_WP(VOL, LabelWP) += 0.125; + // velocity + ComponentAverages_WP(VX, LabelWP) += + 0.125 * Vel_x(n); + ComponentAverages_WP(VY, LabelWP) += + 0.125 * Vel_y(n); + ComponentAverages_WP(VZ, LabelWP) += + 0.125 * Vel_z(n); + // Center of mass + ComponentAverages_WP(CMX, LabelWP) += + 0.125 * (i + cube[p][0] + Dm->iproc() * Nx); + ComponentAverages_WP(CMY, LabelWP) += + 0.125 * (j + cube[p][1] + Dm->jproc() * Ny); + ComponentAverages_WP(CMZ, LabelWP) += + 0.125 * (k + cube[p][2] + Dm->kproc() * Nz); + // twice the kinetic energy + ComponentAverages_WP(VSQ, LabelWP) += + 0.125 * + (Vel_x(n) * Vel_x(n) + Vel_y(n) * Vel_y(n) + + Vel_z(n) * Vel_z(n)); - // Trimmed Mean curvature - pmmc_CubeTrimSurfaceInterpValues(CubeValues,MeanCurvature,SDs,nw_pts,nw_tris,Values,DistanceValues, - i,j,k,n_nw_pts,n_nw_tris,trimdist,trawn,trJwn); - ComponentAverages_WP(TRAWN,LabelWP) += trawn; - ComponentAverages_WP(TRJWN,LabelWP) += trJwn; - ComponentAverages_NWP(TRAWN,LabelNWP) += trawn; - ComponentAverages_NWP(TRJWN,LabelNWP) += trJwn; + // volume the for pressure averaging excludes the interfacial region + if (DelPhi(n) < 1e-4) { + ComponentAverages_WP(TRIMVOL, LabelWP) += 0.125; + ComponentAverages_WP(PRS, LabelWP) += + 0.125 * Press(n); + } + } + } + } + //........................................................................... + // Construct the interfaces and common curve + pmmc_ConstructLocalCube( + SDs, SDn, solid_isovalue, fluid_isovalue, nw_pts, nw_tris, + Values, ns_pts, ns_tris, ws_pts, ws_tris, local_nws_pts, + nws_pts, nws_seg, local_sol_pts, local_sol_tris, + n_local_sol_tris, n_local_sol_pts, n_nw_pts, n_nw_tris, + n_ws_pts, n_ws_tris, n_ns_tris, n_ns_pts, n_local_nws_pts, + n_nws_pts, n_nws_seg, i, j, k, Nx, Ny, Nz); - // Gaussian curvature - TempLocal = pmmc_CubeSurfaceInterpValue(CubeValues,GaussCurvature,nw_pts,nw_tris,Values,i,j,k,n_nw_pts,n_nw_tris); - ComponentAverages_WP(KWN,LabelWP) += TempLocal; - ComponentAverages_NWP(KWN,LabelNWP) += TempLocal; + //........................................................................... + // wn interface averages + if (n_nw_pts > 0 && LabelNWP >= 0 && LabelWP >= 0) { + // Mean curvature + TempLocal = pmmc_CubeSurfaceInterpValue( + CubeValues, MeanCurvature, nw_pts, nw_tris, Values, i, + j, k, n_nw_pts, n_nw_tris); + ComponentAverages_WP(JWN, LabelWP) += TempLocal; + ComponentAverages_NWP(JWN, LabelNWP) += TempLocal; - // Compute the normal speed of the interface - pmmc_InterfaceSpeed(dPdt, SDn_x, SDn_y, SDn_z, CubeValues, nw_pts, nw_tris, - NormalVector, InterfaceSpeed, vawn, i, j, k, n_nw_pts, n_nw_tris); - ComponentAverages_WP(VWNX,LabelWP) += vawn(0); - ComponentAverages_WP(VWNY,LabelWP) += vawn(1); - ComponentAverages_WP(VWNZ,LabelWP) += vawn(2); - ComponentAverages_NWP(VWNX,LabelNWP) += vawn(0); - ComponentAverages_NWP(VWNY,LabelNWP) += vawn(1); - ComponentAverages_NWP(VWNZ,LabelNWP) += vawn(2); + // Trimmed Mean curvature + pmmc_CubeTrimSurfaceInterpValues( + CubeValues, MeanCurvature, SDs, nw_pts, nw_tris, Values, + DistanceValues, i, j, k, n_nw_pts, n_nw_tris, trimdist, + trawn, trJwn); + ComponentAverages_WP(TRAWN, LabelWP) += trawn; + ComponentAverages_WP(TRJWN, LabelWP) += trJwn; + ComponentAverages_NWP(TRAWN, LabelNWP) += trawn; + ComponentAverages_NWP(TRJWN, LabelNWP) += trJwn; - // Interfacial Area - TempLocal = pmmc_CubeSurfaceOrientation(Gwn,nw_pts,nw_tris,n_nw_tris); - ComponentAverages_WP(AWN,LabelWP) += TempLocal; - ComponentAverages_NWP(AWN,LabelNWP) += TempLocal; + // Gaussian curvature + TempLocal = pmmc_CubeSurfaceInterpValue( + CubeValues, GaussCurvature, nw_pts, nw_tris, Values, i, + j, k, n_nw_pts, n_nw_tris); + ComponentAverages_WP(KWN, LabelWP) += TempLocal; + ComponentAverages_NWP(KWN, LabelNWP) += TempLocal; - ComponentAverages_WP(GWNXX,LabelWP) += Gwn(0); - ComponentAverages_WP(GWNYY,LabelWP) += Gwn(1); - ComponentAverages_WP(GWNZZ,LabelWP) += Gwn(2); - ComponentAverages_WP(GWNXY,LabelWP) += Gwn(3); - ComponentAverages_WP(GWNXZ,LabelWP) += Gwn(4); - ComponentAverages_WP(GWNYZ,LabelWP) += Gwn(5); + // Compute the normal speed of the interface + pmmc_InterfaceSpeed(dPdt, SDn_x, SDn_y, SDn_z, CubeValues, + nw_pts, nw_tris, NormalVector, + InterfaceSpeed, vawn, i, j, k, n_nw_pts, + n_nw_tris); + ComponentAverages_WP(VWNX, LabelWP) += vawn(0); + ComponentAverages_WP(VWNY, LabelWP) += vawn(1); + ComponentAverages_WP(VWNZ, LabelWP) += vawn(2); + ComponentAverages_NWP(VWNX, LabelNWP) += vawn(0); + ComponentAverages_NWP(VWNY, LabelNWP) += vawn(1); + ComponentAverages_NWP(VWNZ, LabelNWP) += vawn(2); - ComponentAverages_NWP(GWNXX,LabelNWP) += Gwn(0); - ComponentAverages_NWP(GWNYY,LabelNWP) += Gwn(1); - ComponentAverages_NWP(GWNZZ,LabelNWP) += Gwn(2); - ComponentAverages_NWP(GWNXY,LabelNWP) += Gwn(3); - ComponentAverages_NWP(GWNXZ,LabelNWP) += Gwn(4); - ComponentAverages_NWP(GWNYZ,LabelNWP) += Gwn(5); + // Interfacial Area + TempLocal = pmmc_CubeSurfaceOrientation(Gwn, nw_pts, + nw_tris, n_nw_tris); + ComponentAverages_WP(AWN, LabelWP) += TempLocal; + ComponentAverages_NWP(AWN, LabelNWP) += TempLocal; - } - //........................................................................... - // Common curve averages - if (n_local_nws_pts > 0 && LabelNWP >=0 && LabelWP >=0){ - // Contact angle - TempLocal = pmmc_CubeContactAngle(CubeValues,Values,SDn_x,SDn_y,SDn_z,SDs_x,SDs_y,SDs_z, - local_nws_pts,i,j,k,n_local_nws_pts); - ComponentAverages_WP(CWNS,LabelWP) += TempLocal; - ComponentAverages_NWP(CWNS,LabelNWP) += TempLocal; + ComponentAverages_WP(GWNXX, LabelWP) += Gwn(0); + ComponentAverages_WP(GWNYY, LabelWP) += Gwn(1); + ComponentAverages_WP(GWNZZ, LabelWP) += Gwn(2); + ComponentAverages_WP(GWNXY, LabelWP) += Gwn(3); + ComponentAverages_WP(GWNXZ, LabelWP) += Gwn(4); + ComponentAverages_WP(GWNYZ, LabelWP) += Gwn(5); - // Kinematic velocity of the common curve - pmmc_CommonCurveSpeed(CubeValues, dPdt, vawns, SDn_x, SDn_y, SDn_z,SDs_x,SDs_y,SDs_z, - local_nws_pts,i,j,k,n_local_nws_pts); - ComponentAverages_WP(VWNSX,LabelWP) += vawns(0); - ComponentAverages_WP(VWNSY,LabelWP) += vawns(1); - ComponentAverages_WP(VWNSZ,LabelWP) += vawns(2); - ComponentAverages_NWP(VWNSX,LabelNWP) += vawns(0); - ComponentAverages_NWP(VWNSY,LabelNWP) += vawns(1); - ComponentAverages_NWP(VWNSZ,LabelNWP) += vawns(2); + ComponentAverages_NWP(GWNXX, LabelNWP) += Gwn(0); + ComponentAverages_NWP(GWNYY, LabelNWP) += Gwn(1); + ComponentAverages_NWP(GWNZZ, LabelNWP) += Gwn(2); + ComponentAverages_NWP(GWNXY, LabelNWP) += Gwn(3); + ComponentAverages_NWP(GWNXZ, LabelNWP) += Gwn(4); + ComponentAverages_NWP(GWNYZ, LabelNWP) += Gwn(5); + } + //........................................................................... + // Common curve averages + if (n_local_nws_pts > 0 && LabelNWP >= 0 && LabelWP >= 0) { + // Contact angle + TempLocal = pmmc_CubeContactAngle( + CubeValues, Values, SDn_x, SDn_y, SDn_z, SDs_x, SDs_y, + SDs_z, local_nws_pts, i, j, k, n_local_nws_pts); + ComponentAverages_WP(CWNS, LabelWP) += TempLocal; + ComponentAverages_NWP(CWNS, LabelNWP) += TempLocal; - // Curvature of the common curve - pmmc_CurveCurvature(SDn, SDs, SDn_x, SDn_y, SDn_z, SDs_x, SDs_y, - SDs_z, KNwns_values, KGwns_values, KNwns, KGwns, - nws_pts, n_nws_pts, i, j, k); - ComponentAverages_WP(KNWNS,LabelWP) += KNwns; - ComponentAverages_WP(KGWNS,LabelWP) += KGwns; - ComponentAverages_NWP(KNWNS,LabelNWP) += KNwns; - ComponentAverages_NWP(KGWNS,LabelNWP) += KGwns; + // Kinematic velocity of the common curve + pmmc_CommonCurveSpeed( + CubeValues, dPdt, vawns, SDn_x, SDn_y, SDn_z, SDs_x, + SDs_y, SDs_z, local_nws_pts, i, j, k, n_local_nws_pts); + ComponentAverages_WP(VWNSX, LabelWP) += vawns(0); + ComponentAverages_WP(VWNSY, LabelWP) += vawns(1); + ComponentAverages_WP(VWNSZ, LabelWP) += vawns(2); + ComponentAverages_NWP(VWNSX, LabelNWP) += vawns(0); + ComponentAverages_NWP(VWNSY, LabelNWP) += vawns(1); + ComponentAverages_NWP(VWNSZ, LabelNWP) += vawns(2); - // Length of the common curve - TempLocal = pmmc_CubeCurveLength(local_nws_pts,n_local_nws_pts); - ComponentAverages_NWP(LWNS,LabelNWP) += TempLocal; - } - //........................................................................... - // Solid interface averages - if (n_local_sol_pts > 0 && LabelWP >=0){ - As += pmmc_CubeSurfaceArea(local_sol_pts,local_sol_tris,n_local_sol_tris); - // Compute the surface orientation and the interfacial area + // Curvature of the common curve + pmmc_CurveCurvature(SDn, SDs, SDn_x, SDn_y, SDn_z, SDs_x, + SDs_y, SDs_z, KNwns_values, + KGwns_values, KNwns, KGwns, nws_pts, + n_nws_pts, i, j, k); + ComponentAverages_WP(KNWNS, LabelWP) += KNwns; + ComponentAverages_WP(KGWNS, LabelWP) += KGwns; + ComponentAverages_NWP(KNWNS, LabelNWP) += KNwns; + ComponentAverages_NWP(KGWNS, LabelNWP) += KGwns; - TempLocal = pmmc_CubeSurfaceOrientation(Gws,ws_pts,ws_tris,n_ws_tris); - ComponentAverages_WP(AWS,LabelWP) += TempLocal; - } - if (n_ns_pts > 0 && LabelNWP >=0 ){ - TempLocal = pmmc_CubeSurfaceOrientation(Gns,ns_pts,ns_tris,n_ns_tris); - ComponentAverages_NWP(ANS,LabelNWP) += TempLocal; - } - //........................................................................... + // Length of the common curve + TempLocal = + pmmc_CubeCurveLength(local_nws_pts, n_local_nws_pts); + ComponentAverages_NWP(LWNS, LabelNWP) += TempLocal; + } + //........................................................................... + // Solid interface averages + if (n_local_sol_pts > 0 && LabelWP >= 0) { + As += pmmc_CubeSurfaceArea(local_sol_pts, local_sol_tris, + n_local_sol_tris); + // Compute the surface orientation and the interfacial area + TempLocal = pmmc_CubeSurfaceOrientation(Gws, ws_pts, + ws_tris, n_ws_tris); + ComponentAverages_WP(AWS, LabelWP) += TempLocal; + } + if (n_ns_pts > 0 && LabelNWP >= 0) { + TempLocal = pmmc_CubeSurfaceOrientation(Gns, ns_pts, + ns_tris, n_ns_tris); + ComponentAverages_NWP(ANS, LabelNWP) += TempLocal; + } + //........................................................................... - /* Compute the Euler characteristic + /* Compute the Euler characteristic * count all vertices, edges and faces (triangles) * Euler Number = vertices - edges + faces * double geomavg_EulerCharacteristic(PointList, PointCount, TriList, TriCount); */ - if (LabelNWP >= 0 ){ - // find non-wetting phase interface -// double euler; - n_nw_pts=n_nw_tris=0; - geomavg_MarchingCubes(SDn,fluid_isovalue,i,j,k,nw_pts,n_nw_pts,nw_tris,n_nw_tris); - // Compute Euler characteristic from integral of gaussian curvature - euler = pmmc_CubeSurfaceInterpValue(CubeValues,GaussCurvature,nw_pts,nw_tris,Values, - i,j,k,n_nw_pts,n_nw_tris); - ComponentAverages_NWP(INTCURV,LabelNWP) += euler; + if (LabelNWP >= 0) { + // find non-wetting phase interface + // double euler; + n_nw_pts = n_nw_tris = 0; + geomavg_MarchingCubes(SDn, fluid_isovalue, i, j, k, nw_pts, + n_nw_pts, nw_tris, n_nw_tris); + // Compute Euler characteristic from integral of gaussian curvature + euler = pmmc_CubeSurfaceInterpValue( + CubeValues, GaussCurvature, nw_pts, nw_tris, Values, i, + j, k, n_nw_pts, n_nw_tris); + ComponentAverages_NWP(INTCURV, LabelNWP) += euler; - // Compute the Euler characteristic from vertices - faces + edges - euler = geomavg_EulerCharacteristic(nw_pts,nw_tris,n_nw_pts,n_nw_tris,i,j,k); - ComponentAverages_NWP(EULER,LabelNWP) += euler; + // Compute the Euler characteristic from vertices - faces + edges + euler = geomavg_EulerCharacteristic( + nw_pts, nw_tris, n_nw_pts, n_nw_tris, i, j, k); + ComponentAverages_NWP(EULER, LabelNWP) += euler; + } + } + } + } - } - } - } - } + Dm->Comm.barrier(); + if (Dm->rank() == 0) { + printf("Component averages computed locally -- reducing result... \n"); + } + // Globally reduce the non-wetting phase averages + RecvBuffer.resize(BLOB_AVG_COUNT, NumberComponents_NWP); - Dm->Comm.barrier(); - if (Dm->rank()==0){ - printf("Component averages computed locally -- reducing result... \n"); - } - // Globally reduce the non-wetting phase averages - RecvBuffer.resize(BLOB_AVG_COUNT,NumberComponents_NWP); - -/* for (int b=0; bComm.barrier(); Dm->Comm.sumReduce(&ComponentAverages_NWP(0,b),&RecvBuffer(0),BLOB_AVG_COUNT); for (int idx=0; idxComm.barrier(); - Dm->Comm.sumReduce(ComponentAverages_NWP.data(),RecvBuffer.data(),BLOB_AVG_COUNT*NumberComponents_NWP); - // Dm->Comm.sumReduce(ComponentAverages_NWP.data(),RecvBuffer.data(),BLOB_AVG_COUNT); + Dm->Comm.barrier(); + Dm->Comm.sumReduce(ComponentAverages_NWP.data(), RecvBuffer.data(), + BLOB_AVG_COUNT * NumberComponents_NWP); + // Dm->Comm.sumReduce(ComponentAverages_NWP.data(),RecvBuffer.data(),BLOB_AVG_COUNT); - if (Dm->rank()==0){ - printf("rescaling... \n"); - } + if (Dm->rank() == 0) { + printf("rescaling... \n"); + } - for (int b=0; b 0.0){ - double Vn,pn,awn,ans,Jwn,Kwn,lwns,cwns,vsq; + for (int b = 0; b < NumberComponents_NWP; b++) { + for (int idx = 0; idx < BLOB_AVG_COUNT; idx++) + ComponentAverages_NWP(idx, b) = RecvBuffer(idx, b); + } + for (int b = 0; b < NumberComponents_NWP; b++) { + if (ComponentAverages_NWP(VOL, b) > 0.0) { + double Vn, pn, awn, ans, Jwn, Kwn, lwns, cwns, vsq; - Vn = ComponentAverages_NWP(VOL,b); - awn = ComponentAverages_NWP(AWN,b); - ans = ComponentAverages_NWP(ANS,b); - van(0) = ComponentAverages_NWP(VX,b)/Vn; - van(1) = ComponentAverages_NWP(VY,b)/Vn; - van(2) = ComponentAverages_NWP(VZ,b)/Vn; - vsq = ComponentAverages_NWP(VSQ,b)/Vn; + Vn = ComponentAverages_NWP(VOL, b); + awn = ComponentAverages_NWP(AWN, b); + ans = ComponentAverages_NWP(ANS, b); + van(0) = ComponentAverages_NWP(VX, b) / Vn; + van(1) = ComponentAverages_NWP(VY, b) / Vn; + van(2) = ComponentAverages_NWP(VZ, b) / Vn; + vsq = ComponentAverages_NWP(VSQ, b) / Vn; NULL_USE(ans); - if (ComponentAverages_NWP(TRIMVOL,b) > 0.0){ - pn = ComponentAverages_NWP(PRS,b)/ComponentAverages_NWP(TRIMVOL,b); - } - else pn = 0.0; + if (ComponentAverages_NWP(TRIMVOL, b) > 0.0) { + pn = ComponentAverages_NWP(PRS, b) / + ComponentAverages_NWP(TRIMVOL, b); + } else + pn = 0.0; - if (awn != 0.0){ - Jwn = ComponentAverages_NWP(JWN,b)/awn; - Kwn = ComponentAverages_NWP(KWN,b)/awn; - vawn(0) = ComponentAverages_NWP(VWNSX,b)/awn; - vawn(1) = ComponentAverages_NWP(VWNSY,b)/awn; - vawn(2) = ComponentAverages_NWP(VWNSZ,b)/awn; - Gwn(0) = ComponentAverages_NWP(GWNXX,b)/awn; - Gwn(1) = ComponentAverages_NWP(GWNYY,b)/awn; - Gwn(2) = ComponentAverages_NWP(GWNZZ,b)/awn; - Gwn(3) = ComponentAverages_NWP(GWNXY,b)/awn; - Gwn(4) = ComponentAverages_NWP(GWNXZ,b)/awn; - Gwn(5) = ComponentAverages_NWP(GWNYZ,b)/awn; - } - else Jwn=Kwn=0.0; + if (awn != 0.0) { + Jwn = ComponentAverages_NWP(JWN, b) / awn; + Kwn = ComponentAverages_NWP(KWN, b) / awn; + vawn(0) = ComponentAverages_NWP(VWNSX, b) / awn; + vawn(1) = ComponentAverages_NWP(VWNSY, b) / awn; + vawn(2) = ComponentAverages_NWP(VWNSZ, b) / awn; + Gwn(0) = ComponentAverages_NWP(GWNXX, b) / awn; + Gwn(1) = ComponentAverages_NWP(GWNYY, b) / awn; + Gwn(2) = ComponentAverages_NWP(GWNZZ, b) / awn; + Gwn(3) = ComponentAverages_NWP(GWNXY, b) / awn; + Gwn(4) = ComponentAverages_NWP(GWNXZ, b) / awn; + Gwn(5) = ComponentAverages_NWP(GWNYZ, b) / awn; + } else + Jwn = Kwn = 0.0; - trawn = ComponentAverages_NWP(TRAWN,b); - trJwn = ComponentAverages_NWP(TRJWN,b); - if (trawn > 0.0) trJwn /= trawn; + trawn = ComponentAverages_NWP(TRAWN, b); + trJwn = ComponentAverages_NWP(TRJWN, b); + if (trawn > 0.0) + trJwn /= trawn; - lwns = ComponentAverages_NWP(LWNS,b); - if (lwns != 0.0){ - cwns = ComponentAverages_NWP(CWNS,b)/lwns; - vawns(0) = ComponentAverages_NWP(VWNSX,b)/lwns; - vawns(1) = ComponentAverages_NWP(VWNSY,b)/lwns; - vawns(2) = ComponentAverages_NWP(VWNSZ,b)/lwns; - } - else cwns=0.0; + lwns = ComponentAverages_NWP(LWNS, b); + if (lwns != 0.0) { + cwns = ComponentAverages_NWP(CWNS, b) / lwns; + vawns(0) = ComponentAverages_NWP(VWNSX, b) / lwns; + vawns(1) = ComponentAverages_NWP(VWNSY, b) / lwns; + vawns(2) = ComponentAverages_NWP(VWNSZ, b) / lwns; + } else + cwns = 0.0; - ComponentAverages_NWP(PRS,b) = pn; - ComponentAverages_NWP(VX,b) = van(0); - ComponentAverages_NWP(VY,b) = van(1); - ComponentAverages_NWP(VZ,b) = van(2); - ComponentAverages_NWP(VSQ,b) = vsq; + ComponentAverages_NWP(PRS, b) = pn; + ComponentAverages_NWP(VX, b) = van(0); + ComponentAverages_NWP(VY, b) = van(1); + ComponentAverages_NWP(VZ, b) = van(2); + ComponentAverages_NWP(VSQ, b) = vsq; - ComponentAverages_NWP(JWN,b) = Jwn; - ComponentAverages_NWP(KWN,b) = Kwn; - ComponentAverages_NWP(VWNX,b) = vawn(0); - ComponentAverages_NWP(VWNY,b) = vawn(1); - ComponentAverages_NWP(VWNZ,b) = vawn(2); - - ComponentAverages_NWP(GWNXX,b) = Gwn(0); - ComponentAverages_NWP(GWNYY,b) = Gwn(1); - ComponentAverages_NWP(GWNZZ,b) = Gwn(2); - ComponentAverages_NWP(GWNXY,b) = Gwn(3); - ComponentAverages_NWP(GWNXZ,b) = Gwn(4); - ComponentAverages_NWP(GWNYZ,b) = Gwn(5); + ComponentAverages_NWP(JWN, b) = Jwn; + ComponentAverages_NWP(KWN, b) = Kwn; + ComponentAverages_NWP(VWNX, b) = vawn(0); + ComponentAverages_NWP(VWNY, b) = vawn(1); + ComponentAverages_NWP(VWNZ, b) = vawn(2); - ComponentAverages_NWP(CWNS,b) = cwns; - ComponentAverages_NWP(VWNSX,b) = vawns(0); - ComponentAverages_NWP(VWNSY,b) = vawns(1); - ComponentAverages_NWP(VWNSZ,b) = vawns(2); + ComponentAverages_NWP(GWNXX, b) = Gwn(0); + ComponentAverages_NWP(GWNYY, b) = Gwn(1); + ComponentAverages_NWP(GWNZZ, b) = Gwn(2); + ComponentAverages_NWP(GWNXY, b) = Gwn(3); + ComponentAverages_NWP(GWNXZ, b) = Gwn(4); + ComponentAverages_NWP(GWNYZ, b) = Gwn(5); - ComponentAverages_NWP(CMX,b) /= Vn; - ComponentAverages_NWP(CMY,b) /= Vn; - ComponentAverages_NWP(CMZ,b) /= Vn; + ComponentAverages_NWP(CWNS, b) = cwns; + ComponentAverages_NWP(VWNSX, b) = vawns(0); + ComponentAverages_NWP(VWNSY, b) = vawns(1); + ComponentAverages_NWP(VWNSZ, b) = vawns(2); - ComponentAverages_NWP(TRJWN,b) = trJwn; + ComponentAverages_NWP(CMX, b) /= Vn; + ComponentAverages_NWP(CMY, b) /= Vn; + ComponentAverages_NWP(CMZ, b) /= Vn; - ComponentAverages_NWP(EULER,b) /= (2*PI); + ComponentAverages_NWP(TRJWN, b) = trJwn; - } - } + ComponentAverages_NWP(EULER, b) /= (2 * PI); + } + } - if (Dm->rank()==0){ - printf("reduce WP averages... \n"); - } + if (Dm->rank() == 0) { + printf("reduce WP averages... \n"); + } - // reduce the wetting phase averages - for (int b=0; bComm.barrier(); - Dm->Comm.sumReduce(&ComponentAverages_WP(0,b),RecvBuffer.data(),BLOB_AVG_COUNT); - for (int idx=0; idx 0.0){ - double Vw,pw,awn,ans,Jwn,Kwn,lwns,cwns,vsq; - Vw = ComponentAverages_WP(VOL,b); - awn = ComponentAverages_WP(AWN,b); - ans = ComponentAverages_WP(ANS,b); - vaw(0) = ComponentAverages_WP(VX,b)/Vw; - vaw(1) = ComponentAverages_WP(VY,b)/Vw; - vaw(2) = ComponentAverages_WP(VZ,b)/Vw; - vsq = ComponentAverages_WP(VSQ,b)/Vw; + // reduce the wetting phase averages + for (int b = 0; b < NumberComponents_WP; b++) { + Dm->Comm.barrier(); + Dm->Comm.sumReduce(&ComponentAverages_WP(0, b), RecvBuffer.data(), + BLOB_AVG_COUNT); + for (int idx = 0; idx < BLOB_AVG_COUNT; idx++) + ComponentAverages_WP(idx, b) = RecvBuffer(idx); + } + + for (int b = 0; b < NumberComponents_WP; b++) { + if (ComponentAverages_WP(VOL, b) > 0.0) { + double Vw, pw, awn, ans, Jwn, Kwn, lwns, cwns, vsq; + Vw = ComponentAverages_WP(VOL, b); + awn = ComponentAverages_WP(AWN, b); + ans = ComponentAverages_WP(ANS, b); + vaw(0) = ComponentAverages_WP(VX, b) / Vw; + vaw(1) = ComponentAverages_WP(VY, b) / Vw; + vaw(2) = ComponentAverages_WP(VZ, b) / Vw; + vsq = ComponentAverages_WP(VSQ, b) / Vw; NULL_USE(ans); - if (ComponentAverages_WP(TRIMVOL,b) > 0.0){ - pw = ComponentAverages_WP(PRS,b)/ComponentAverages_WP(TRIMVOL,b); - } - else pw = 0.0; + if (ComponentAverages_WP(TRIMVOL, b) > 0.0) { + pw = ComponentAverages_WP(PRS, b) / + ComponentAverages_WP(TRIMVOL, b); + } else + pw = 0.0; - if (awn != 0.0){ - Jwn = ComponentAverages_WP(JWN,b)/awn; - Kwn = ComponentAverages_WP(KWN,b)/awn; - vawn(0) = ComponentAverages_WP(VWNSX,b)/awn; - vawn(1) = ComponentAverages_WP(VWNSY,b)/awn; - vawn(2) = ComponentAverages_WP(VWNSZ,b)/awn; - Gwn(0) = ComponentAverages_WP(GWNXX,b)/awn; - Gwn(1) = ComponentAverages_WP(GWNYY,b)/awn; - Gwn(2) = ComponentAverages_WP(GWNZZ,b)/awn; - Gwn(3) = ComponentAverages_WP(GWNXY,b)/awn; - Gwn(4) = ComponentAverages_WP(GWNXZ,b)/awn; - Gwn(5) = ComponentAverages_WP(GWNYZ,b)/awn; - } - else Jwn=Kwn=0.0; + if (awn != 0.0) { + Jwn = ComponentAverages_WP(JWN, b) / awn; + Kwn = ComponentAverages_WP(KWN, b) / awn; + vawn(0) = ComponentAverages_WP(VWNSX, b) / awn; + vawn(1) = ComponentAverages_WP(VWNSY, b) / awn; + vawn(2) = ComponentAverages_WP(VWNSZ, b) / awn; + Gwn(0) = ComponentAverages_WP(GWNXX, b) / awn; + Gwn(1) = ComponentAverages_WP(GWNYY, b) / awn; + Gwn(2) = ComponentAverages_WP(GWNZZ, b) / awn; + Gwn(3) = ComponentAverages_WP(GWNXY, b) / awn; + Gwn(4) = ComponentAverages_WP(GWNXZ, b) / awn; + Gwn(5) = ComponentAverages_WP(GWNYZ, b) / awn; + } else + Jwn = Kwn = 0.0; - trawn = ComponentAverages_WP(TRAWN,b); - trJwn = ComponentAverages_WP(TRJWN,b); - if (trawn > 0.0) trJwn /= trawn; + trawn = ComponentAverages_WP(TRAWN, b); + trJwn = ComponentAverages_WP(TRJWN, b); + if (trawn > 0.0) + trJwn /= trawn; - lwns = ComponentAverages_WP(LWNS,b); - if (lwns != 0.0){ - cwns = ComponentAverages_WP(CWNS,b)/lwns; - vawns(0) = ComponentAverages_WP(VWNSX,b)/lwns; - vawns(1) = ComponentAverages_WP(VWNSY,b)/lwns; - vawns(2) = ComponentAverages_WP(VWNSZ,b)/lwns; - } - else cwns=0.0; + lwns = ComponentAverages_WP(LWNS, b); + if (lwns != 0.0) { + cwns = ComponentAverages_WP(CWNS, b) / lwns; + vawns(0) = ComponentAverages_WP(VWNSX, b) / lwns; + vawns(1) = ComponentAverages_WP(VWNSY, b) / lwns; + vawns(2) = ComponentAverages_WP(VWNSZ, b) / lwns; + } else + cwns = 0.0; - ComponentAverages_WP(PRS,b) = pw; - ComponentAverages_WP(VX,b) = vaw(0); - ComponentAverages_WP(VY,b) = vaw(1); - ComponentAverages_WP(VZ,b) = vaw(2); - ComponentAverages_WP(VSQ,b) = vsq; + ComponentAverages_WP(PRS, b) = pw; + ComponentAverages_WP(VX, b) = vaw(0); + ComponentAverages_WP(VY, b) = vaw(1); + ComponentAverages_WP(VZ, b) = vaw(2); + ComponentAverages_WP(VSQ, b) = vsq; - ComponentAverages_WP(JWN,b) = Jwn; - ComponentAverages_WP(KWN,b) = Kwn; - ComponentAverages_WP(VWNX,b) = vawn(0); - ComponentAverages_WP(VWNY,b) = vawn(1); - ComponentAverages_WP(VWNZ,b) = vawn(2); - - ComponentAverages_WP(GWNXX,b) = Gwn(0); - ComponentAverages_WP(GWNYY,b) = Gwn(1); - ComponentAverages_WP(GWNZZ,b) = Gwn(2); - ComponentAverages_WP(GWNXY,b) = Gwn(3); - ComponentAverages_WP(GWNXZ,b) = Gwn(4); - ComponentAverages_WP(GWNYZ,b) = Gwn(5); + ComponentAverages_WP(JWN, b) = Jwn; + ComponentAverages_WP(KWN, b) = Kwn; + ComponentAverages_WP(VWNX, b) = vawn(0); + ComponentAverages_WP(VWNY, b) = vawn(1); + ComponentAverages_WP(VWNZ, b) = vawn(2); - ComponentAverages_WP(CWNS,b) = cwns; - ComponentAverages_WP(VWNSX,b) = vawns(0); - ComponentAverages_WP(VWNSY,b) = vawns(1); - ComponentAverages_WP(VWNSZ,b) = vawns(2); + ComponentAverages_WP(GWNXX, b) = Gwn(0); + ComponentAverages_WP(GWNYY, b) = Gwn(1); + ComponentAverages_WP(GWNZZ, b) = Gwn(2); + ComponentAverages_WP(GWNXY, b) = Gwn(3); + ComponentAverages_WP(GWNXZ, b) = Gwn(4); + ComponentAverages_WP(GWNYZ, b) = Gwn(5); - ComponentAverages_WP(TRJWN,b) = trJwn; - } - } + ComponentAverages_WP(CWNS, b) = cwns; + ComponentAverages_WP(VWNSX, b) = vawns(0); + ComponentAverages_WP(VWNSY, b) = vawns(1); + ComponentAverages_WP(VWNSZ, b) = vawns(2); + + ComponentAverages_WP(TRJWN, b) = trJwn; + } + } } -void TwoPhase::Reduce() -{ - int i; - double iVol_global=1.0/Volume; - //........................................................................... - Dm->Comm.barrier(); - nwp_volume_global = Dm->Comm.sumReduce( nwp_volume ); - wp_volume_global = Dm->Comm.sumReduce( wp_volume ); - awn_global = Dm->Comm.sumReduce( awn ); - ans_global = Dm->Comm.sumReduce( ans ); - aws_global = Dm->Comm.sumReduce( aws ); - lwns_global = Dm->Comm.sumReduce( lwns ); - As_global = Dm->Comm.sumReduce( As ); - Jwn_global = Dm->Comm.sumReduce( Jwn ); - Kwn_global = Dm->Comm.sumReduce( Kwn ); - KGwns_global = Dm->Comm.sumReduce( KGwns ); - KNwns_global = Dm->Comm.sumReduce( KNwns ); - efawns_global = Dm->Comm.sumReduce( efawns ); - wwndnw_global = Dm->Comm.sumReduce( wwndnw ); - wwnsdnwn_global = Dm->Comm.sumReduce( wwnsdnwn ); - Jwnwwndnw_global = Dm->Comm.sumReduce( Jwnwwndnw ); - // Phase averages - vol_w_global = Dm->Comm.sumReduce( vol_w ); - vol_n_global = Dm->Comm.sumReduce( vol_n ); - paw_global = Dm->Comm.sumReduce( paw ); - pan_global = Dm->Comm.sumReduce( pan ); - for (int idx=0; idx<3; idx++) - vaw_global(idx) = Dm->Comm.sumReduce( vaw(idx) ); - for (int idx=0; idx<3; idx++) - van_global(idx) = Dm->Comm.sumReduce( van(idx)); - for (int idx=0; idx<3; idx++) - vawn_global(idx) = Dm->Comm.sumReduce( vawn(idx) ); - for (int idx=0; idx<3; idx++) - vawns_global(idx) = Dm->Comm.sumReduce( vawns(idx) ); - for (int idx=0; idx<6; idx++){ - Gwn_global(idx) = Dm->Comm.sumReduce( Gwn(idx) ); - Gns_global(idx) = Dm->Comm.sumReduce( Gns(idx) ); - Gws_global(idx) = Dm->Comm.sumReduce( Gws(idx) ); - } - trawn_global = Dm->Comm.sumReduce( trawn ); - trJwn_global = Dm->Comm.sumReduce( trJwn ); - trRwn_global = Dm->Comm.sumReduce( trRwn ); - euler_global = Dm->Comm.sumReduce( euler ); - An_global = Dm->Comm.sumReduce( An ); - Jn_global = Dm->Comm.sumReduce( Jn ); - Kn_global = Dm->Comm.sumReduce( Kn ); - Dm->Comm.barrier(); +void TwoPhase::Reduce() { + int i; + double iVol_global = 1.0 / Volume; + //........................................................................... + Dm->Comm.barrier(); + nwp_volume_global = Dm->Comm.sumReduce(nwp_volume); + wp_volume_global = Dm->Comm.sumReduce(wp_volume); + awn_global = Dm->Comm.sumReduce(awn); + ans_global = Dm->Comm.sumReduce(ans); + aws_global = Dm->Comm.sumReduce(aws); + lwns_global = Dm->Comm.sumReduce(lwns); + As_global = Dm->Comm.sumReduce(As); + Jwn_global = Dm->Comm.sumReduce(Jwn); + Kwn_global = Dm->Comm.sumReduce(Kwn); + KGwns_global = Dm->Comm.sumReduce(KGwns); + KNwns_global = Dm->Comm.sumReduce(KNwns); + efawns_global = Dm->Comm.sumReduce(efawns); + wwndnw_global = Dm->Comm.sumReduce(wwndnw); + wwnsdnwn_global = Dm->Comm.sumReduce(wwnsdnwn); + Jwnwwndnw_global = Dm->Comm.sumReduce(Jwnwwndnw); + // Phase averages + vol_w_global = Dm->Comm.sumReduce(vol_w); + vol_n_global = Dm->Comm.sumReduce(vol_n); + paw_global = Dm->Comm.sumReduce(paw); + pan_global = Dm->Comm.sumReduce(pan); + for (int idx = 0; idx < 3; idx++) + vaw_global(idx) = Dm->Comm.sumReduce(vaw(idx)); + for (int idx = 0; idx < 3; idx++) + van_global(idx) = Dm->Comm.sumReduce(van(idx)); + for (int idx = 0; idx < 3; idx++) + vawn_global(idx) = Dm->Comm.sumReduce(vawn(idx)); + for (int idx = 0; idx < 3; idx++) + vawns_global(idx) = Dm->Comm.sumReduce(vawns(idx)); + for (int idx = 0; idx < 6; idx++) { + Gwn_global(idx) = Dm->Comm.sumReduce(Gwn(idx)); + Gns_global(idx) = Dm->Comm.sumReduce(Gns(idx)); + Gws_global(idx) = Dm->Comm.sumReduce(Gws(idx)); + } + trawn_global = Dm->Comm.sumReduce(trawn); + trJwn_global = Dm->Comm.sumReduce(trJwn); + trRwn_global = Dm->Comm.sumReduce(trRwn); + euler_global = Dm->Comm.sumReduce(euler); + An_global = Dm->Comm.sumReduce(An); + Jn_global = Dm->Comm.sumReduce(Jn); + Kn_global = Dm->Comm.sumReduce(Kn); + Dm->Comm.barrier(); - // Normalize the phase averages - // (density of both components = 1.0) - if (vol_w_global > 0.0){ - paw_global = paw_global / vol_w_global; - } - if (wp_volume_global > 0.0){ - vaw_global(0) = vaw_global(0) / wp_volume_global; - vaw_global(1) = vaw_global(1) / wp_volume_global; - vaw_global(2) = vaw_global(2) / wp_volume_global; + // Normalize the phase averages + // (density of both components = 1.0) + if (vol_w_global > 0.0) { + paw_global = paw_global / vol_w_global; + } + if (wp_volume_global > 0.0) { + vaw_global(0) = vaw_global(0) / wp_volume_global; + vaw_global(1) = vaw_global(1) / wp_volume_global; + vaw_global(2) = vaw_global(2) / wp_volume_global; + } + if (vol_n_global > 0.0) { + pan_global = pan_global / vol_n_global; + } + if (nwp_volume_global > 0.0) { + van_global(0) = van_global(0) / nwp_volume_global; + van_global(1) = van_global(1) / nwp_volume_global; + van_global(2) = van_global(2) / nwp_volume_global; + } + // Normalize surface averages by the interfacial area + if (awn_global > 0.0) { + Jwn_global /= awn_global; + Kwn_global /= awn_global; + wwndnw_global /= awn_global; + for (i = 0; i < 3; i++) + vawn_global(i) /= awn_global; + for (i = 0; i < 6; i++) + Gwn_global(i) /= awn_global; + } + if (lwns_global > 0.0) { + efawns_global /= lwns_global; + KNwns_global /= lwns_global; + KGwns_global /= lwns_global; + for (i = 0; i < 3; i++) + vawns_global(i) /= lwns_global; + } + if (trawn_global > 0.0) { + trJwn_global /= trawn_global; + trRwn_global /= trawn_global; + trRwn_global = 2.0 * fabs(trRwn_global); + trJwn_global = fabs(trJwn_global); + } - } - if (vol_n_global > 0.0){ - pan_global = pan_global / vol_n_global; - } - if (nwp_volume_global > 0.0){ - van_global(0) = van_global(0) / nwp_volume_global; - van_global(1) = van_global(1) / nwp_volume_global; - van_global(2) = van_global(2) / nwp_volume_global; - } - // Normalize surface averages by the interfacial area - if (awn_global > 0.0){ - Jwn_global /= awn_global; - Kwn_global /= awn_global; - wwndnw_global /= awn_global; - for (i=0; i<3; i++) vawn_global(i) /= awn_global; - for (i=0; i<6; i++) Gwn_global(i) /= awn_global; - } - if (lwns_global > 0.0){ - efawns_global /= lwns_global; - KNwns_global /= lwns_global; - KGwns_global /= lwns_global; - for (i=0; i<3; i++) vawns_global(i) /= lwns_global; - } - if (trawn_global > 0.0){ - trJwn_global /= trawn_global; - trRwn_global /= trawn_global; - trRwn_global = 2.0*fabs(trRwn_global); - trJwn_global = fabs(trJwn_global); - } + if (ans_global > 0.0) + for (i = 0; i < 6; i++) + Gns_global(i) /= ans_global; + if (aws_global > 0.0) + for (i = 0; i < 6; i++) + Gws_global(i) /= aws_global; - if (ans_global > 0.0) for (i=0; i<6; i++) Gns_global(i) /= ans_global; - if (aws_global > 0.0) for (i=0; i<6; i++) Gws_global(i) /= aws_global; + euler_global /= (2 * PI); - euler_global /= (2*PI); - - //sat_w = 1.0 - nwp_volume_global*iVol_global/porosity; - sat_w = 1.0 - nwp_volume_global/(nwp_volume_global+wp_volume_global); - // Compute the specific interfacial areas and common line length (dimensionless per unit volume) - awn_global = awn_global*iVol_global; - ans_global = ans_global*iVol_global; - aws_global = aws_global*iVol_global; - dEs = dEs*iVol_global; - lwns_global = lwns_global*iVol_global; + //sat_w = 1.0 - nwp_volume_global*iVol_global/porosity; + sat_w = 1.0 - nwp_volume_global / (nwp_volume_global + wp_volume_global); + // Compute the specific interfacial areas and common line length (dimensionless per unit volume) + awn_global = awn_global * iVol_global; + ans_global = ans_global * iVol_global; + aws_global = aws_global * iVol_global; + dEs = dEs * iVol_global; + lwns_global = lwns_global * iVol_global; } -void TwoPhase::NonDimensionalize(double D, double viscosity, double IFT) -{ - NULL_USE( viscosity ); - NULL_USE( IFT ); - awn_global *= D; - ans_global *= D; - ans_global *= D; - lwns_global *= D*D; +void TwoPhase::NonDimensionalize(double D, double viscosity, double IFT) { + NULL_USE(viscosity); + NULL_USE(IFT); + awn_global *= D; + ans_global *= D; + ans_global *= D; + lwns_global *= D * D; } -void TwoPhase::PrintAll(int timestep) -{ - if (Dm->rank()==0){ - fprintf(TIMELOG,"%i %.5g %.5g %.5g %.5g %.5g %.5g %.5g %.5g ",timestep,rho_n,rho_w,nu_n,nu_w,Fx,Fy,Fz,gamma_wn); - fprintf(TIMELOG,"%.5g %.5g %.5g ",sat_w,paw_global,pan_global); // saturation and pressure - fprintf(TIMELOG,"%.5g %.5g %.5g ",awn_global,ans_global,aws_global); // interfacial areas - fprintf(TIMELOG,"%.5g %.5g ",Jwn_global, Kwn_global); // curvature of wn interface - fprintf(TIMELOG,"%.5g ",lwns_global); // common curve length - fprintf(TIMELOG,"%.5g ",efawns_global); // average contact angle - fprintf(TIMELOG,"%.5g %.5g ",KNwns_global, KGwns_global); // curvature of wn interface - fprintf(TIMELOG,"%.5g %.5g %.5g ",vaw_global(0),vaw_global(1),vaw_global(2)); // average velocity of w phase - fprintf(TIMELOG,"%.5g %.5g %.5g ",van_global(0),van_global(1),van_global(2)); // average velocity of n phase - fprintf(TIMELOG,"%.5g %.5g %.5g ",vawn_global(0),vawn_global(1),vawn_global(2)); // velocity of wn interface - fprintf(TIMELOG,"%.5g %.5g %.5g ",vawns_global(0),vawns_global(1),vawns_global(2)); // velocity of wn interface - fprintf(TIMELOG,"%.5g %.5g %.5g %.5g %.5g %.5g ", - Gwn_global(0),Gwn_global(1),Gwn_global(2),Gwn_global(3),Gwn_global(4),Gwn_global(5)); // orientation of wn interface - fprintf(TIMELOG,"%.5g %.5g %.5g %.5g %.5g %.5g ", - Gns_global(0),Gns_global(1),Gns_global(2),Gns_global(3),Gns_global(4),Gns_global(5)); // orientation of ns interface - fprintf(TIMELOG,"%.5g %.5g %.5g %.5g %.5g %.5g ", - Gws_global(0),Gws_global(1),Gws_global(2),Gws_global(3),Gws_global(4),Gws_global(5)); // orientation of ws interface - fprintf(TIMELOG,"%.5g %.5g %.5g ",trawn_global, trJwn_global, trRwn_global); // Trimmed curvature - fprintf(TIMELOG,"%.5g %.5g %.5g ",wwndnw_global, wwnsdnwn_global, Jwnwwndnw_global); // kinematic quantities - fprintf(TIMELOG,"%.5g %.5g %.5g %.5g ",wet_morph->V(), wet_morph->A(), wet_morph->H(), wet_morph->X()); - fprintf(TIMELOG,"%.5g %.5g %.5g %.5g\n",nonwet_morph->V(), nonwet_morph->A(), nonwet_morph->H(), nonwet_morph->X()); -// fprintf(TIMELOG,"%.5g %.5g %.5g %.5g\n",euler_global, Kn_global, Jn_global, An_global); // minkowski measures - fflush(TIMELOG); - } - else{ - sat_w = 1.0 - nwp_volume/(nwp_volume+wp_volume); - - fprintf(TIMELOG,"%i %.5g %.5g %.5g %.5g %.5g %.5g %.5g %.5g ",timestep,rho_n,rho_w,nu_n,nu_w,Fx,Fy,Fz,gamma_wn); - fprintf(TIMELOG,"%.5g %.5g %.5g ",sat_w,paw,pan); // saturation and pressure - fprintf(TIMELOG,"%.5g %.5g %.5g ",awn,ans,aws); // interfacial areas - fprintf(TIMELOG,"%.5g %.5g ",Jwn, Kwn); // curvature of wn interface - fprintf(TIMELOG,"%.5g ",lwns); // common curve length - fprintf(TIMELOG,"%.5g ",efawns); // average contact angle - fprintf(TIMELOG,"%.5g %.5g ",KNwns, KGwns); // curvature of wn interface - fprintf(TIMELOG,"%.5g %.5g %.5g ",vaw(0),vaw(1),vaw(2)); // average velocity of w phase - fprintf(TIMELOG,"%.5g %.5g %.5g ",van(0),van(1),van(2)); // average velocity of n phase - fprintf(TIMELOG,"%.5g %.5g %.5g ",vawn(0),vawn(1),vawn(2)); // velocity of wn interface - fprintf(TIMELOG,"%.5g %.5g %.5g ",vawns(0),vawns(1),vawns(2)); // velocity of wn interface - fprintf(TIMELOG,"%.5g %.5g %.5g %.5g %.5g %.5g ", - Gwn(0),Gwn(1),Gwn(2),Gwn(3),Gwn(4),Gwn(5)); // orientation of wn interface - fprintf(TIMELOG,"%.5g %.5g %.5g %.5g %.5g %.5g ", - Gns(0),Gns(1),Gns(2),Gns(3),Gns(4),Gns(5)); // orientation of ns interface - fprintf(TIMELOG,"%.5g %.5g %.5g %.5g %.5g %.5g ", - Gws(0),Gws(1),Gws(2),Gws(3),Gws(4),Gws(5)); // orientation of ws interface - fprintf(TIMELOG,"%.5g %.5g %.5g ",trawn, trJwn, trRwn); // Trimmed curvature - fprintf(TIMELOG,"%.5g %.5g %.5g ",wwndnw, wwnsdnwn, Jwnwwndnw); // kinematic quantities - fprintf(TIMELOG,"%.5g %.5g %.5g %.5g ",wet_morph->Vi, wet_morph->Ai, wet_morph->Ji, wet_morph->Xi); - fprintf(TIMELOG,"%.5g %.5g %.5g %.5g\n",nonwet_morph->Vi, nonwet_morph->Ai, nonwet_morph->Ji, nonwet_morph->Xi); -// fprintf(TIMELOG,"%.5g %.5g %.5g %.5g\n",euler, Kn, Jn, An); // minkowski measures - fflush(TIMELOG); - } +void TwoPhase::PrintAll(int timestep) { + if (Dm->rank() == 0) { + fprintf(TIMELOG, "%i %.5g %.5g %.5g %.5g %.5g %.5g %.5g %.5g ", + timestep, rho_n, rho_w, nu_n, nu_w, Fx, Fy, Fz, gamma_wn); + fprintf(TIMELOG, "%.5g %.5g %.5g ", sat_w, paw_global, + pan_global); // saturation and pressure + fprintf(TIMELOG, "%.5g %.5g %.5g ", awn_global, ans_global, + aws_global); // interfacial areas + fprintf(TIMELOG, "%.5g %.5g ", Jwn_global, + Kwn_global); // curvature of wn interface + fprintf(TIMELOG, "%.5g ", lwns_global); // common curve length + fprintf(TIMELOG, "%.5g ", efawns_global); // average contact angle + fprintf(TIMELOG, "%.5g %.5g ", KNwns_global, + KGwns_global); // curvature of wn interface + fprintf(TIMELOG, "%.5g %.5g %.5g ", vaw_global(0), vaw_global(1), + vaw_global(2)); // average velocity of w phase + fprintf(TIMELOG, "%.5g %.5g %.5g ", van_global(0), van_global(1), + van_global(2)); // average velocity of n phase + fprintf(TIMELOG, "%.5g %.5g %.5g ", vawn_global(0), vawn_global(1), + vawn_global(2)); // velocity of wn interface + fprintf(TIMELOG, "%.5g %.5g %.5g ", vawns_global(0), vawns_global(1), + vawns_global(2)); // velocity of wn interface + fprintf(TIMELOG, "%.5g %.5g %.5g %.5g %.5g %.5g ", Gwn_global(0), + Gwn_global(1), Gwn_global(2), Gwn_global(3), Gwn_global(4), + Gwn_global(5)); // orientation of wn interface + fprintf(TIMELOG, "%.5g %.5g %.5g %.5g %.5g %.5g ", Gns_global(0), + Gns_global(1), Gns_global(2), Gns_global(3), Gns_global(4), + Gns_global(5)); // orientation of ns interface + fprintf(TIMELOG, "%.5g %.5g %.5g %.5g %.5g %.5g ", Gws_global(0), + Gws_global(1), Gws_global(2), Gws_global(3), Gws_global(4), + Gws_global(5)); // orientation of ws interface + fprintf(TIMELOG, "%.5g %.5g %.5g ", trawn_global, trJwn_global, + trRwn_global); // Trimmed curvature + fprintf(TIMELOG, "%.5g %.5g %.5g ", wwndnw_global, wwnsdnwn_global, + Jwnwwndnw_global); // kinematic quantities + fprintf(TIMELOG, "%.5g %.5g %.5g %.5g ", wet_morph->V(), wet_morph->A(), + wet_morph->H(), wet_morph->X()); + fprintf(TIMELOG, "%.5g %.5g %.5g %.5g\n", nonwet_morph->V(), + nonwet_morph->A(), nonwet_morph->H(), nonwet_morph->X()); + // fprintf(TIMELOG,"%.5g %.5g %.5g %.5g\n",euler_global, Kn_global, Jn_global, An_global); // minkowski measures + fflush(TIMELOG); + } else { + sat_w = 1.0 - nwp_volume / (nwp_volume + wp_volume); + fprintf(TIMELOG, "%i %.5g %.5g %.5g %.5g %.5g %.5g %.5g %.5g ", + timestep, rho_n, rho_w, nu_n, nu_w, Fx, Fy, Fz, gamma_wn); + fprintf(TIMELOG, "%.5g %.5g %.5g ", sat_w, paw, + pan); // saturation and pressure + fprintf(TIMELOG, "%.5g %.5g %.5g ", awn, ans, aws); // interfacial areas + fprintf(TIMELOG, "%.5g %.5g ", Jwn, Kwn); // curvature of wn interface + fprintf(TIMELOG, "%.5g ", lwns); // common curve length + fprintf(TIMELOG, "%.5g ", efawns); // average contact angle + fprintf(TIMELOG, "%.5g %.5g ", KNwns, + KGwns); // curvature of wn interface + fprintf(TIMELOG, "%.5g %.5g %.5g ", vaw(0), vaw(1), + vaw(2)); // average velocity of w phase + fprintf(TIMELOG, "%.5g %.5g %.5g ", van(0), van(1), + van(2)); // average velocity of n phase + fprintf(TIMELOG, "%.5g %.5g %.5g ", vawn(0), vawn(1), + vawn(2)); // velocity of wn interface + fprintf(TIMELOG, "%.5g %.5g %.5g ", vawns(0), vawns(1), + vawns(2)); // velocity of wn interface + fprintf(TIMELOG, "%.5g %.5g %.5g %.5g %.5g %.5g ", Gwn(0), Gwn(1), + Gwn(2), Gwn(3), Gwn(4), Gwn(5)); // orientation of wn interface + fprintf(TIMELOG, "%.5g %.5g %.5g %.5g %.5g %.5g ", Gns(0), Gns(1), + Gns(2), Gns(3), Gns(4), Gns(5)); // orientation of ns interface + fprintf(TIMELOG, "%.5g %.5g %.5g %.5g %.5g %.5g ", Gws(0), Gws(1), + Gws(2), Gws(3), Gws(4), Gws(5)); // orientation of ws interface + fprintf(TIMELOG, "%.5g %.5g %.5g ", trawn, trJwn, + trRwn); // Trimmed curvature + fprintf(TIMELOG, "%.5g %.5g %.5g ", wwndnw, wwnsdnwn, + Jwnwwndnw); // kinematic quantities + fprintf(TIMELOG, "%.5g %.5g %.5g %.5g ", wet_morph->Vi, wet_morph->Ai, + wet_morph->Ji, wet_morph->Xi); + fprintf(TIMELOG, "%.5g %.5g %.5g %.5g\n", nonwet_morph->Vi, + nonwet_morph->Ai, nonwet_morph->Ji, nonwet_morph->Xi); + // fprintf(TIMELOG,"%.5g %.5g %.5g %.5g\n",euler, Kn, Jn, An); // minkowski measures + fflush(TIMELOG); + } } -void TwoPhase::PrintComponents(int timestep) -{ - if (Dm->rank()==0){ - printf("PRINT %i COMPONENT AVEREAGES: time = %i \n",(int)ComponentAverages_NWP.size(1),timestep); - for (int b=0; b 0.0){ - fprintf(NWPLOG,"%i ",timestep-5); - if ( Label_NWP_map.empty() ) { - // print index - fprintf(NWPLOG,"%i ",b); - } else { - // print final global id - fprintf(NWPLOG,"%i ",Label_NWP_map[b]); +void TwoPhase::PrintComponents(int timestep) { + if (Dm->rank() == 0) { + printf("PRINT %i COMPONENT AVEREAGES: time = %i \n", + (int)ComponentAverages_NWP.size(1), timestep); + for (int b = 0; b < NumberComponents_NWP; b++) { + //if (ComponentAverages_NWP(TRIMVOL,b) > 0.0){ + fprintf(NWPLOG, "%i ", timestep - 5); + if (Label_NWP_map.empty()) { + // print index + fprintf(NWPLOG, "%i ", b); + } else { + // print final global id + fprintf(NWPLOG, "%i ", Label_NWP_map[b]); + } + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(VOL, b)); + // fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(TRIMVOL,b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(PRS, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(AWN, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(ANS, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(JWN, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(KWN, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(LWNS, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(CWNS, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(VX, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(VY, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(VZ, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(VWNX, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(VWNY, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(VWNZ, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(VWNSX, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(VWNSY, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(VWNSZ, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(VSQ, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(GWNXX, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(GWNYY, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(GWNZZ, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(GWNXY, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(GWNXZ, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(GWNYZ, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(CMX, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(CMY, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(CMZ, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(TRAWN, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(TRJWN, b)); + fprintf(NWPLOG, "%.5g ", ComponentAverages_NWP(INTCURV, b)); + fprintf(NWPLOG, "%.5g\n", ComponentAverages_NWP(EULER, b)); + // fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(NVERT,b)); + // fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(NSIDE,b)); + // fprintf(NWPLOG,"%.5g\n",ComponentAverages_NWP(NFACE,b)); + //} + } + fflush(NWPLOG); + + for (int b = 0; b < NumberComponents_WP; b++) { + if (ComponentAverages_WP(TRIMVOL, b) > 0.0) { + fprintf(WPLOG, "%i ", timestep - 5); + fprintf(WPLOG, "%i ", b); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(VOL, b)); + // fprintf(WPLOG,"%.5g ",ComponentAverages_WP(TRIMVOL,b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(PRS, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(AWN, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(AWS, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(JWN, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(KWN, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(LWNS, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(CWNS, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(VX, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(VY, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(VZ, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(VWNX, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(VWNY, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(VWNZ, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(VWNSX, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(VWNSY, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(VWNSZ, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(VSQ, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(GWNXX, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(GWNYY, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(GWNZZ, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(GWNXY, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(GWNXZ, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(GWNYZ, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(CMX, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(CMY, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(CMZ, b)); + fprintf(WPLOG, "%.5g ", ComponentAverages_WP(TRAWN, b)); + fprintf(WPLOG, "%.5g\n", ComponentAverages_WP(TRJWN, b)); + } + } + fflush(WPLOG); + } +} + +inline int TwoPhase::GetCubeLabel(int i, int j, int k, IntArray &BlobLabel) { + int label; + label = BlobLabel(i, j, k); + label = max(label, BlobLabel(i + 1, j, k)); + label = max(label, BlobLabel(i, j + 1, k)); + label = max(label, BlobLabel(i + 1, j + 1, k)); + label = max(label, BlobLabel(i, j, k + 1)); + label = max(label, BlobLabel(i + 1, j, k + 1)); + label = max(label, BlobLabel(i, j + 1, k + 1)); + label = max(label, BlobLabel(i + 1, j + 1, k + 1)); + + return label; +} + +void TwoPhase::SortBlobs() { + //printf("Sorting the blobs based on volume \n"); + //printf("-----------------------------------------------\n"); + int TempLabel, a, aa, bb, i, j, k, idx; + double TempValue; + //....................................................................... + // Sort NWP components by volume + //....................................................................... + IntArray OldLabel(NumberComponents_NWP); + for (a = 0; a < NumberComponents_NWP; a++) + OldLabel(a) = a; + // Sort the blob averages based on volume + for (aa = 0; aa < NumberComponents_NWP - 1; aa++) { + for (bb = aa + 1; bb < NumberComponents_NWP; bb++) { + if (ComponentAverages_NWP(VOL, aa) < + ComponentAverages_NWP(VOL, bb)) { + // Exchange location of blobs aa and bb + //printf("Switch blob %i with %i \n", OldLabel(aa),OldLabel(bb)); + // switch the label + TempLabel = OldLabel(bb); + OldLabel(bb) = OldLabel(aa); + OldLabel(aa) = TempLabel; + // switch the averages + for (idx = 0; idx < BLOB_AVG_COUNT; idx++) { + TempValue = ComponentAverages_NWP(idx, bb); + ComponentAverages_NWP(idx, bb) = + ComponentAverages_NWP(idx, aa); + ComponentAverages_NWP(idx, aa) = TempValue; } - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(VOL,b)); - // fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(TRIMVOL,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(PRS,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(AWN,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(ANS,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(JWN,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(KWN,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(LWNS,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(CWNS,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(VX,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(VY,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(VZ,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(VWNX,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(VWNY,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(VWNZ,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(VWNSX,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(VWNSY,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(VWNSZ,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(VSQ,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(GWNXX,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(GWNYY,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(GWNZZ,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(GWNXY,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(GWNXZ,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(GWNYZ,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(CMX,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(CMY,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(CMZ,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(TRAWN,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(TRJWN,b)); - fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(INTCURV,b)); - fprintf(NWPLOG,"%.5g\n",ComponentAverages_NWP(EULER,b)); -// fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(NVERT,b)); - // fprintf(NWPLOG,"%.5g ",ComponentAverages_NWP(NSIDE,b)); - // fprintf(NWPLOG,"%.5g\n",ComponentAverages_NWP(NFACE,b)); - //} - } - fflush(NWPLOG); - - for (int b=0; b 0.0){ - fprintf(WPLOG,"%i ",timestep-5); - fprintf(WPLOG,"%i ",b); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(VOL,b)); - // fprintf(WPLOG,"%.5g ",ComponentAverages_WP(TRIMVOL,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(PRS,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(AWN,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(AWS,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(JWN,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(KWN,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(LWNS,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(CWNS,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(VX,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(VY,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(VZ,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(VWNX,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(VWNY,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(VWNZ,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(VWNSX,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(VWNSY,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(VWNSZ,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(VSQ,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(GWNXX,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(GWNYY,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(GWNZZ,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(GWNXY,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(GWNXZ,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(GWNYZ,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(CMX,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(CMY,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(CMZ,b)); - fprintf(WPLOG,"%.5g ",ComponentAverages_WP(TRAWN,b)); - fprintf(WPLOG,"%.5g\n",ComponentAverages_WP(TRJWN,b)); - - } - } - fflush(WPLOG); - } -} - -inline int TwoPhase::GetCubeLabel(int i, int j, int k, IntArray &BlobLabel) -{ - int label; - label=BlobLabel(i,j,k); - label=max(label,BlobLabel(i+1,j,k)); - label=max(label,BlobLabel(i,j+1,k)); - label=max(label,BlobLabel(i+1,j+1,k)); - label=max(label,BlobLabel(i,j,k+1)); - label=max(label,BlobLabel(i+1,j,k+1)); - label=max(label,BlobLabel(i,j+1,k+1)); - label=max(label,BlobLabel(i+1,j+1,k+1)); - - return label; -} - -void TwoPhase::SortBlobs() -{ - //printf("Sorting the blobs based on volume \n"); - //printf("-----------------------------------------------\n"); - int TempLabel,a,aa,bb,i,j,k,idx; - double TempValue; - //....................................................................... - // Sort NWP components by volume - //....................................................................... - IntArray OldLabel(NumberComponents_NWP); - for (a=0; a -1) - { - TempLabel = NewLabel(Label_NWP(i,j,k)); - Label_NWP(i,j,k) = TempLabel; - } - } - } - } - //....................................................................... - // Sort WP components by volume - //....................................................................... - OldLabel.resize(NumberComponents_WP); - for (a=0; a -1) - { - TempLabel = NewLabel(Label_WP(i,j,k)); - Label_WP(i,j,k) = TempLabel; - } - } - } - } - //....................................................................... - + } + } + } + IntArray NewLabel(NumberComponents_NWP); + for (aa = 0; aa < NumberComponents_NWP; aa++) { + // Match the new label for original blob aa + bb = 0; + while (OldLabel(bb) != aa) + bb++; + NewLabel(aa) = bb; + } + // Re-label the blob ID + // printf("Re-labeling the blobs, now indexed by volume \n"); + for (k = 0; k < Nz; k++) { + for (j = 0; j < Ny; j++) { + for (i = 0; i < Nx; i++) { + if (Label_NWP(i, j, k) > -1) { + TempLabel = NewLabel(Label_NWP(i, j, k)); + Label_NWP(i, j, k) = TempLabel; + } + } + } + } + //....................................................................... + // Sort WP components by volume + //....................................................................... + OldLabel.resize(NumberComponents_WP); + for (a = 0; a < NumberComponents_WP; a++) + OldLabel(a) = a; + // Sort the blob averages based on volume + for (aa = 0; aa < NumberComponents_WP - 1; aa++) { + for (bb = aa + 1; bb < NumberComponents_WP; bb++) { + if (ComponentAverages_WP(VOL, aa) < ComponentAverages_WP(VOL, bb)) { + // Exchange location of blobs aa and bb + //printf("Switch blob %i with %i \n", OldLabel(aa),OldLabel(bb)); + // switch the label + TempLabel = OldLabel(bb); + OldLabel(bb) = OldLabel(aa); + OldLabel(aa) = TempLabel; + // switch the averages + for (idx = 0; idx < BLOB_AVG_COUNT; idx++) { + TempValue = ComponentAverages_WP(idx, bb); + ComponentAverages_WP(idx, bb) = + ComponentAverages_WP(idx, aa); + ComponentAverages_WP(idx, aa) = TempValue; + } + } + } + } + NewLabel.resize(NumberComponents_WP); + for (aa = 0; aa < NumberComponents_WP; aa++) { + // Match the new label for original blob aa + bb = 0; + while (OldLabel(bb) != aa) + bb++; + NewLabel(aa) = bb; + } + // Re-label the blob ID + // printf("Re-labeling the blobs, now indexed by volume \n"); + for (k = 0; k < Nz; k++) { + for (j = 0; j < Ny; j++) { + for (i = 0; i < Nx; i++) { + if (Label_WP(i, j, k) > -1) { + TempLabel = NewLabel(Label_WP(i, j, k)); + Label_WP(i, j, k) = TempLabel; + } + } + } + } + //....................................................................... } diff --git a/analysis/TwoPhase.h b/analysis/TwoPhase.h index 5a89374d..8b00d81c 100644 --- a/analysis/TwoPhase.h +++ b/analysis/TwoPhase.h @@ -33,171 +33,170 @@ #include "IO/Reader.h" #include "IO/Writer.h" +class TwoPhase { -class TwoPhase{ + //........................................................................... + int n_nw_pts, n_ns_pts, n_ws_pts, n_nws_pts, n_local_sol_pts, + n_local_nws_pts; + int n_nw_tris, n_ns_tris, n_ws_tris, n_nws_seg, n_local_sol_tris; + //........................................................................... + int nc; + int kstart, kfinish; - //........................................................................... - int n_nw_pts,n_ns_pts,n_ws_pts,n_nws_pts,n_local_sol_pts,n_local_nws_pts; - int n_nw_tris,n_ns_tris,n_ws_tris,n_nws_seg,n_local_sol_tris; - //........................................................................... - int nc; - int kstart,kfinish; + double fluid_isovalue, solid_isovalue; + double Volume; + // initialize lists for vertices for surfaces, common line + DTMutableList nw_pts; + DTMutableList ns_pts; + DTMutableList ws_pts; + DTMutableList nws_pts; + DTMutableList local_sol_pts; + DTMutableList local_nws_pts; + DTMutableList tmp; - double fluid_isovalue, solid_isovalue; - double Volume; - // initialize lists for vertices for surfaces, common line - DTMutableList nw_pts; - DTMutableList ns_pts; - DTMutableList ws_pts; - DTMutableList nws_pts; - DTMutableList local_sol_pts; - DTMutableList local_nws_pts; - DTMutableList tmp; + // initialize triangle lists for surfaces + IntArray nw_tris; + IntArray ns_tris; + IntArray ws_tris; + IntArray nws_seg; + IntArray local_sol_tris; - // initialize triangle lists for surfaces - IntArray nw_tris; - IntArray ns_tris; - IntArray ws_tris; - IntArray nws_seg; - IntArray local_sol_tris; + // Temporary storage arrays + DoubleArray CubeValues; + DoubleArray Values; + DoubleArray DistanceValues; + DoubleArray KGwns_values; + DoubleArray KNwns_values; + DoubleArray InterfaceSpeed; + DoubleArray NormalVector; - // Temporary storage arrays - DoubleArray CubeValues; - DoubleArray Values; - DoubleArray DistanceValues; - DoubleArray KGwns_values; - DoubleArray KNwns_values; - DoubleArray InterfaceSpeed; - DoubleArray NormalVector; + DoubleArray RecvBuffer; - DoubleArray RecvBuffer; + char *TempID; - char *TempID; - - // CSV / text file where time history of averages is saved - FILE *TIMELOG; - FILE *NWPLOG; - FILE *WPLOG; + // CSV / text file where time history of averages is saved + FILE *TIMELOG; + FILE *NWPLOG; + FILE *WPLOG; public: - //........................................................................... - std::shared_ptr Dm; - int NumberComponents_WP,NumberComponents_NWP; - //........................................................................... - // Averaging variables - //........................................................................... - // local averages (to each MPI process) - double trimdist; // pixel distance to trim surface for specified averages - double porosity,poreVol; - double awn,ans,aws,lwns; - double wp_volume,nwp_volume; - double As, dummy; - double vol_w, vol_n; // volumes the exclude the interfacial region - double sat_w, sat_w_previous; - double pan,paw; // local phase averaged pressure - // Global averages (all processes) - double pan_global,paw_global; // local phase averaged pressure - double vol_w_global, vol_n_global; // volumes the exclude the interfacial region - double awn_global,ans_global,aws_global; - double lwns_global; - double efawns,efawns_global; // averaged contact angle - double euler,Kn,Jn,An; - double euler_global,Kn_global,Jn_global,An_global; - - double rho_n, rho_w; - double nu_n, nu_w; - double gamma_wn; - double Fx, Fy, Fz; + //........................................................................... + std::shared_ptr Dm; + int NumberComponents_WP, NumberComponents_NWP; + //........................................................................... + // Averaging variables + //........................................................................... + // local averages (to each MPI process) + double trimdist; // pixel distance to trim surface for specified averages + double porosity, poreVol; + double awn, ans, aws, lwns; + double wp_volume, nwp_volume; + double As, dummy; + double vol_w, vol_n; // volumes the exclude the interfacial region + double sat_w, sat_w_previous; + double pan, paw; // local phase averaged pressure + // Global averages (all processes) + double pan_global, paw_global; // local phase averaged pressure + double vol_w_global, + vol_n_global; // volumes the exclude the interfacial region + double awn_global, ans_global, aws_global; + double lwns_global; + double efawns, efawns_global; // averaged contact angle + double euler, Kn, Jn, An; + double euler_global, Kn_global, Jn_global, An_global; - double Jwn,Jwn_global; // average mean curavture - wn interface - double Kwn,Kwn_global; // average Gaussian curavture - wn interface - double KNwns,KNwns_global; // wns common curve normal curavture - double KGwns,KGwns_global; // wns common curve geodesic curavture - double trawn,trawn_global; // trimmed interfacial area - double trJwn,trJwn_global; // trimmed interfacial area - double trRwn,trRwn_global; // trimmed interfacial area - double nwp_volume_global; // volume for the non-wetting phase - double wp_volume_global; // volume for the wetting phase - double As_global; - double wwndnw, wwndnw_global; - double wwnsdnwn, wwnsdnwn_global; - double Jwnwwndnw, Jwnwwndnw_global; - double dEs,dAwn,dAns; // Global surface energy (calculated by rank=0) - DoubleArray van; - DoubleArray vaw; - DoubleArray vawn; - DoubleArray vawns; - DoubleArray Gwn; - DoubleArray Gns; - DoubleArray Gws; - DoubleArray van_global; - DoubleArray vaw_global; - DoubleArray vawn_global; - DoubleArray vawns_global; - DoubleArray Gwn_global; - DoubleArray Gns_global; - DoubleArray Gws_global; - //........................................................................... - //........................................................................... - int Nx,Ny,Nz; - IntArray PhaseID; // Phase ID array (solid=0, non-wetting=1, wetting=2) - BlobIDArray Label_WP; // Wetting phase label - BlobIDArray Label_NWP; // Non-wetting phase label index (0:nblobs-1) - std::vector Label_NWP_map; // Non-wetting phase label for each index - DoubleArray SDn; - DoubleArray SDs; - DoubleArray Phase; - DoubleArray Press; - DoubleArray dPdt; - DoubleArray MeanCurvature; - DoubleArray GaussCurvature; - DoubleArray SDs_x; // Gradient of the signed distance - DoubleArray SDs_y; - DoubleArray SDs_z; - DoubleArray SDn_x; // Gradient of the signed distance - DoubleArray SDn_y; - DoubleArray SDn_z; - DoubleArray DelPhi; // Magnitude of Gradient of the phase indicator field - DoubleArray Phase_tplus; - DoubleArray Phase_tminus; - DoubleArray Vel_x; // Velocity - DoubleArray Vel_y; - DoubleArray Vel_z; - DoubleArray PhaseDistance; + double rho_n, rho_w; + double nu_n, nu_w; + double gamma_wn; + double Fx, Fy, Fz; - std::shared_ptr wet_morph; - std::shared_ptr nonwet_morph; - - // Container for averages; - DoubleArray ComponentAverages_WP; - DoubleArray ComponentAverages_NWP; - //........................................................................... - TwoPhase(std::shared_ptr Dm); - ~TwoPhase(); - void Initialize(); -// void SetupCubes(Domain &Dm); - void UpdateMeshValues(); - void UpdateSolid(); - void ComputeDelPhi(); - void ColorToSignedDistance(double Beta, DoubleArray &ColorData, DoubleArray &DistData); - void ComputeLocal(); - void AssignComponentLabels(); - void ComponentAverages(); - void Reduce(); - void NonDimensionalize(double D, double viscosity, double IFT); - void PrintAll(int timestep); - int GetCubeLabel(int i, int j, int k, IntArray &BlobLabel); - void SortBlobs(); - void PrintComponents(int timestep); - void SetParams(double rhoA, double rhoB, double tauA, double tauB, double force_x, double force_y, double force_z, double alpha); - double Volume_w(){ - return wp_volume_global; - } - double Volume_n(){ - return nwp_volume_global; - } + double Jwn, Jwn_global; // average mean curavture - wn interface + double Kwn, Kwn_global; // average Gaussian curavture - wn interface + double KNwns, KNwns_global; // wns common curve normal curavture + double KGwns, KGwns_global; // wns common curve geodesic curavture + double trawn, trawn_global; // trimmed interfacial area + double trJwn, trJwn_global; // trimmed interfacial area + double trRwn, trRwn_global; // trimmed interfacial area + double nwp_volume_global; // volume for the non-wetting phase + double wp_volume_global; // volume for the wetting phase + double As_global; + double wwndnw, wwndnw_global; + double wwnsdnwn, wwnsdnwn_global; + double Jwnwwndnw, Jwnwwndnw_global; + double dEs, dAwn, dAns; // Global surface energy (calculated by rank=0) + DoubleArray van; + DoubleArray vaw; + DoubleArray vawn; + DoubleArray vawns; + DoubleArray Gwn; + DoubleArray Gns; + DoubleArray Gws; + DoubleArray van_global; + DoubleArray vaw_global; + DoubleArray vawn_global; + DoubleArray vawns_global; + DoubleArray Gwn_global; + DoubleArray Gns_global; + DoubleArray Gws_global; + //........................................................................... + //........................................................................... + int Nx, Ny, Nz; + IntArray PhaseID; // Phase ID array (solid=0, non-wetting=1, wetting=2) + BlobIDArray Label_WP; // Wetting phase label + BlobIDArray Label_NWP; // Non-wetting phase label index (0:nblobs-1) + std::vector + Label_NWP_map; // Non-wetting phase label for each index + DoubleArray SDn; + DoubleArray SDs; + DoubleArray Phase; + DoubleArray Press; + DoubleArray dPdt; + DoubleArray MeanCurvature; + DoubleArray GaussCurvature; + DoubleArray SDs_x; // Gradient of the signed distance + DoubleArray SDs_y; + DoubleArray SDs_z; + DoubleArray SDn_x; // Gradient of the signed distance + DoubleArray SDn_y; + DoubleArray SDn_z; + DoubleArray DelPhi; // Magnitude of Gradient of the phase indicator field + DoubleArray Phase_tplus; + DoubleArray Phase_tminus; + DoubleArray Vel_x; // Velocity + DoubleArray Vel_y; + DoubleArray Vel_z; + DoubleArray PhaseDistance; + + std::shared_ptr wet_morph; + std::shared_ptr nonwet_morph; + + // Container for averages; + DoubleArray ComponentAverages_WP; + DoubleArray ComponentAverages_NWP; + //........................................................................... + TwoPhase(std::shared_ptr Dm); + ~TwoPhase(); + void Initialize(); + // void SetupCubes(Domain &Dm); + void UpdateMeshValues(); + void UpdateSolid(); + void ComputeDelPhi(); + void ColorToSignedDistance(double Beta, DoubleArray &ColorData, + DoubleArray &DistData); + void ComputeLocal(); + void AssignComponentLabels(); + void ComponentAverages(); + void Reduce(); + void NonDimensionalize(double D, double viscosity, double IFT); + void PrintAll(int timestep); + int GetCubeLabel(int i, int j, int k, IntArray &BlobLabel); + void SortBlobs(); + void PrintComponents(int timestep); + void SetParams(double rhoA, double rhoB, double tauA, double tauB, + double force_x, double force_y, double force_z, + double alpha); + double Volume_w() { return wp_volume_global; } + double Volume_n() { return nwp_volume_global; } }; - #endif - diff --git a/analysis/analysis.cpp b/analysis/analysis.cpp index 8bb3b2fd..0d2b74fa 100644 --- a/analysis/analysis.cpp +++ b/analysis/analysis.cpp @@ -14,27 +14,28 @@ You should have received a copy of the GNU General Public License along with OPM. If not, see . */ + #include "analysis/analysis.h" #include "ProfilerApp.h" #include #include - -template -inline TYPE* getPtr( std::vector& x ) { return x.empty() ? NULL:&x[0]; } -template -inline const TYPE* getPtr( const std::vector& x ) { return x.empty() ? NULL:&x[0]; } - +template inline TYPE *getPtr(std::vector &x) { + return x.empty() ? NULL : &x[0]; +} +template inline const TYPE *getPtr(const std::vector &x) { + return x.empty() ? NULL : &x[0]; +} /****************************************************************** * Compute the blobs * ******************************************************************/ -int ComputeBlob( const Array& isPhase, BlobIDArray& LocalBlobID, bool periodic, int start_id ) -{ - PROFILE_START("ComputeBlob",1); - ASSERT(isPhase.size()==LocalBlobID.size()); - const int Nx = isPhase.size(0); // maxima for the meshes +int ComputeBlob(const Array &isPhase, BlobIDArray &LocalBlobID, + bool periodic, int start_id) { + PROFILE_START("ComputeBlob", 1); + ASSERT(isPhase.size() == LocalBlobID.size()); + const int Nx = isPhase.size(0); // maxima for the meshes const int Ny = isPhase.size(1); const int Nz = isPhase.size(2); std::vector map; @@ -42,226 +43,230 @@ int ComputeBlob( const Array& isPhase, BlobIDArray& LocalBlobID, bool peri // Get the list of neighbors we need to check int N_neighbors = 0; int d[26][3]; - bool include_corners = false; // Do we need to include cells that only touch at a corder/edge - if ( include_corners ) { + bool include_corners = + false; // Do we need to include cells that only touch at a corder/edge + if (include_corners) { // Include corners/edges as neighbors, check all cells N_neighbors = 26; - const int tmp[26][3] = {{1,0,0},{-1,0,0},{0,1,0},{0,-1,0},{0,0,1},{0,0,-1}, - {1,1,0},{1,-1,0},{-1,1,0},{-1,-1,0},{1,0,1},{-1,0,1}, - {1,0,-1},{-1,0,-1},{0,1,1},{0,-1,1},{0,1,-1},{0,-1,-1}, - {1,1,1},{1,1,-1},{1,-1,1},{1,-1,-1},{-1,1,1},{-1,1,-1}, - {-1,-1,1},{-1,-1,-1}}; // directions to neighbors - memcpy(d,tmp,sizeof(tmp)); + const int tmp[26][3] = { + {1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, + {0, 0, -1}, {1, 1, 0}, {1, -1, 0}, {-1, 1, 0}, {-1, -1, 0}, + {1, 0, 1}, {-1, 0, 1}, {1, 0, -1}, {-1, 0, -1}, {0, 1, 1}, + {0, -1, 1}, {0, 1, -1}, {0, -1, -1}, {1, 1, 1}, {1, 1, -1}, + {1, -1, 1}, {1, -1, -1}, {-1, 1, 1}, {-1, 1, -1}, {-1, -1, 1}, + {-1, -1, -1}}; // directions to neighbors + memcpy(d, tmp, sizeof(tmp)); } else { // Do not include corners/edges as neighbors - if ( periodic ) { + if (periodic) { // Include all neighbors for periodic problems N_neighbors = 6; - const int tmp[6][3] = {{1,0,0},{-1,0,0},{0,1,0},{0,-1,0},{0,0,1},{0,0,-1}}; // directions to neighbors - memcpy(d,tmp,sizeof(tmp)); + const int tmp[6][3] = { + {1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, + {0, -1, 0}, {0, 0, 1}, {0, 0, -1}}; // directions to neighbors + memcpy(d, tmp, sizeof(tmp)); } else { // We only need to include the lower neighbors for non-periodic problems N_neighbors = 3; - const int tmp[3][3] = {{-1,0,0},{0,-1,0},{0,0,-1}}; // directions to neighbors - memcpy(d,tmp,sizeof(tmp)); + const int tmp[3][3] = { + {-1, 0, 0}, {0, -1, 0}, {0, 0, -1}}; // directions to neighbors + memcpy(d, tmp, sizeof(tmp)); } } // Loop through all the points - int last = start_id-1; + int last = start_id - 1; std::vector neighbor_ids; neighbor_ids.reserve(N_neighbors); const bool *isPhasePtr = isPhase.data(); BlobIDType *LocalBlobIDPtr = LocalBlobID.data(); - for (int z=0; zNx-1 ? 0:x2; - y2 = y2<0 ? Ny-1:y2; // Periodic BC for x - y2 = y2>Ny-1 ? 0:y2; - z2 = z2<0 ? Nz-1:z2; // Periodic BC for x - z2 = z2>Nz-1 ? 0:z2; + int x2 = x + d[p][0]; + int y2 = y + d[p][1]; + int z2 = z + d[p][2]; + if (periodic) { + x2 = x2 < 0 ? Nx - 1 : x2; // Periodic BC for x + x2 = x2 > Nx - 1 ? 0 : x2; + y2 = y2 < 0 ? Ny - 1 : y2; // Periodic BC for x + y2 = y2 > Ny - 1 ? 0 : y2; + z2 = z2 < 0 ? Nz - 1 : z2; // Periodic BC for x + z2 = z2 > Nz - 1 ? 0 : z2; } else { - if ( x2<0 || x2>=Nx || y2<0 || y2>=Ny || z2<0 || z2>=Nz ) + if (x2 < 0 || x2 >= Nx || y2 < 0 || y2 >= Ny || + z2 < 0 || z2 >= Nz) continue; } // Check if a neighbor has a known blob id - size_t index2 = x2 + y2*Nx + z2*Nx*Ny; + size_t index2 = x2 + y2 * Nx + z2 * Nx * Ny; int id = LocalBlobIDPtr[index2]; - if ( !isPhasePtr[index2] || id<0 ) + if (!isPhasePtr[index2] || id < 0) continue; neighbor_ids[N_list] = id; N_list++; } - if ( N_list==0 ) { + if (N_list == 0) { // No neighbors with a blob id, create a new one - LocalBlobIDPtr[index] = last+1; - map.push_back(last+1); + LocalBlobIDPtr[index] = last + 1; + map.push_back(last + 1); last++; - } else if ( N_list==1 ) { + } else if (N_list == 1) { // We have one neighbor LocalBlobIDPtr[index] = neighbor_ids[0]; } else { // We have multiple neighbors int id = neighbor_ids[0]; - for (int i=1; i isPhase(Nx,Ny,Nz); - memset(isPhase.data(),0,Nx*Ny*Nz*sizeof(bool)); - for (size_t i=0; i isPhase(Nx, Ny, Nz); + memset(isPhase.data(), 0, Nx * Ny * Nz * sizeof(bool)); + for (size_t i = 0; i < N; i++) { + if (SignDist(i) <= vS) { + // Solid phase LocalBlobID(i) = -2; } else { LocalBlobID(i) = -1; - if ( Phase(i)>vF && SignDist(i)>vS ) + if (Phase(i) > vF && SignDist(i) > vS) isPhase(i) = true; } } - int nblobs = ComputeBlob( isPhase, LocalBlobID, periodic, 0 ); + int nblobs = ComputeBlob(isPhase, LocalBlobID, periodic, 0); PROFILE_STOP("ComputeLocalBlobIDs"); return nblobs; } -int ComputeLocalPhaseComponent(const IntArray &PhaseID, int &VALUE, BlobIDArray &ComponentLabel, bool periodic ) -{ +int ComputeLocalPhaseComponent(const IntArray &PhaseID, int &VALUE, + BlobIDArray &ComponentLabel, bool periodic) { PROFILE_START("ComputeLocalPhaseComponent"); size_t Nx = PhaseID.size(0); size_t Ny = PhaseID.size(1); size_t Nz = PhaseID.size(2); - size_t N = Nx*Ny*Nz; + size_t N = Nx * Ny * Nz; // Compute the local blob ids - ComponentLabel.resize(Nx,Ny,Nz); - Array isPhase(Nx,Ny,Nz); - for (size_t i=0; i isPhase(Nx, Ny, Nz); + for (size_t i = 0; i < N; i++) { + if (PhaseID(i) == VALUE) { ComponentLabel(i) = -1; isPhase(i) = true; - } else{ + } else { ComponentLabel(i) = -2; isPhase(i) = false; } } - int ncomponents = ComputeBlob( isPhase, ComponentLabel, periodic, 0 ); + int ncomponents = ComputeBlob(isPhase, ComponentLabel, periodic, 0); PROFILE_STOP("ComputeLocalPhaseComponent"); return ncomponents; } - /****************************************************************** * Reorder the global blob ids * ******************************************************************/ -static int ReorderBlobIDs2( BlobIDArray& ID, int N_blobs, int ngx, int ngy, int ngz, const Utilities::MPI& comm ) -{ - if ( N_blobs==0 ) +static int ReorderBlobIDs2(BlobIDArray &ID, int N_blobs, int ngx, int ngy, + int ngz, const Utilities::MPI &comm) { + if (N_blobs == 0) return 0; - PROFILE_START("ReorderBlobIDs2",1); - ASSERT(sizeof(long long int)==sizeof(int64_t)); + PROFILE_START("ReorderBlobIDs2", 1); + ASSERT(sizeof(long long int) == sizeof(int64_t)); double *local_size = new double[N_blobs]; double *global_size = new double[N_blobs]; - for (int i=0; i= 0 ) + for (size_t k = ngz; k < ID.size(2) - ngz; k++) { + for (size_t j = ngy; j < ID.size(1) - ngy; j++) { + for (size_t i = ngx; i < ID.size(0) - ngx; i++) { + int id = ID(i, j, k); + if (id >= 0) local_size[id] += 1; - max_id = std::max(max_id,id); + max_id = std::max(max_id, id); } } } - ASSERT(max_id > map1(N_blobs); + ASSERT(max_id < N_blobs); + comm.sumReduce(local_size, global_size, N_blobs); + std::vector> map1(N_blobs); int N_blobs2 = 0; - for (int i=0; i 0 ) + if (global_size[i] > 0) N_blobs2++; } - std::sort( map1.begin(), map1.end() ); - std::vector map2(N_blobs,-1); - for (int i=0; i map2(N_blobs, -1); + for (int i = 0; i < N_blobs; i++) { + map2[map1[N_blobs - i - 1].second] = i; } - for (size_t i=0; i= 0 ) + for (size_t i = 0; i < ID.length(); i++) { + if (ID(i) >= 0) ID(i) = map2[ID(i)]; } - delete [] local_size; - delete [] global_size; - PROFILE_STOP("ReorderBlobIDs2",1); + delete[] local_size; + delete[] global_size; + PROFILE_STOP("ReorderBlobIDs2", 1); return N_blobs2; } -void ReorderBlobIDs( BlobIDArray& ID, const Utilities::MPI& comm ) -{ +void ReorderBlobIDs(BlobIDArray &ID, const Utilities::MPI &comm) { PROFILE_START("ReorderBlobIDs"); - int tmp = ID.max()+1; + int tmp = ID.max() + 1; int N_blobs = 0; - N_blobs = comm.maxReduce( tmp ); - ReorderBlobIDs2(ID,N_blobs,1,1,1,comm); + N_blobs = comm.maxReduce(tmp); + ReorderBlobIDs2(ID, N_blobs, 1, 1, 1, comm); PROFILE_STOP("ReorderBlobIDs"); } - /****************************************************************** * Compute the global blob ids * ******************************************************************/ @@ -270,302 +275,312 @@ struct global_id_info_struct { std::set remote_ids; }; // Send the local ids and their new value to all neighbors -static void updateRemoteIds( - const std::map& map, - const std::vector& neighbors, - int N_send, const std::vector& N_recv, - int64_t *send_buf, std::vector& recv_buf, - std::map& remote_map, - const Utilities::MPI& comm ) -{ +static void updateRemoteIds(const std::map &map, + const std::vector &neighbors, int N_send, + const std::vector &N_recv, int64_t *send_buf, + std::vector &recv_buf, + std::map &remote_map, + const Utilities::MPI &comm) { std::vector send_req(neighbors.size()); std::vector recv_req(neighbors.size()); auto it = map.begin(); - ASSERT(N_send==(int)map.size()); - for (size_t i=0; ifirst; - send_buf[2*i+1] = it->second.new_id; + ASSERT(N_send == (int)map.size()); + for (size_t i = 0; i < map.size(); i++, ++it) { + send_buf[2 * i + 0] = it->first; + send_buf[2 * i + 1] = it->second.new_id; } - for (size_t i=0; ifirst] = it->second.new_id; } - for (size_t i=0; i& remote_map, - std::map& map ) -{ +static bool updateLocalIds(const std::map &remote_map, + std::map &map) { bool changed = false; - std::map::iterator it; - for (it=map.begin(); it!=map.end(); ++it) { + std::map::iterator it; + for (it = map.begin(); it != map.end(); ++it) { int64_t id = it->second.new_id; std::set::const_iterator it2; - for (it2=it->second.remote_ids.begin(); it2!=it->second.remote_ids.end(); ++it2) { + for (it2 = it->second.remote_ids.begin(); + it2 != it->second.remote_ids.end(); ++it2) { int64_t id2 = remote_map.find(*it2)->second; - id = std::min(id,id2); + id = std::min(id, id2); } - changed = changed || it->second.new_id!=id; + changed = changed || it->second.new_id != id; it->second.new_id = id; } return changed; } -static int LocalToGlobalIDs( int nx, int ny, int nz, const RankInfoStruct& rank_info, - int nblobs, BlobIDArray& IDs, const Utilities::MPI& comm ) -{ - PROFILE_START("LocalToGlobalIDs",1); +static int LocalToGlobalIDs(int nx, int ny, int nz, + const RankInfoStruct &rank_info, int nblobs, + BlobIDArray &IDs, const Utilities::MPI &comm) { + PROFILE_START("LocalToGlobalIDs", 1); const int rank = rank_info.rank[1][1][1]; int nprocs = comm.getSize(); - const int ngx = (IDs.size(0)-nx)/2; - const int ngy = (IDs.size(1)-ny)/2; - const int ngz = (IDs.size(2)-nz)/2; + const int ngx = (IDs.size(0) - nx) / 2; + const int ngy = (IDs.size(1) - ny) / 2; + const int ngz = (IDs.size(2) - nz) / 2; // Get the number of blobs for each rank - std::vector N_blobs(nprocs,0); - PROFILE_START("LocalToGlobalIDs-Allgather",1); - comm.allGather(nblobs,getPtr(N_blobs)); - PROFILE_STOP("LocalToGlobalIDs-Allgather",1); + std::vector N_blobs(nprocs, 0); + PROFILE_START("LocalToGlobalIDs-Allgather", 1); + comm.allGather(nblobs, getPtr(N_blobs)); + PROFILE_STOP("LocalToGlobalIDs-Allgather", 1); int64_t N_blobs_tot = 0; int offset = 0; - for (int i=0; i= 0 ) + for (size_t i = 0; i < IDs.length(); i++) { + if (IDs(i) >= 0) IDs(i) += offset; } const BlobIDArray LocalIDs = IDs; // Copy the ids and get the neighbors through the halos - fillHalo fillData(comm,rank_info,{nx,ny,nz},{1,1,1},0,1,{true,true,true}); + fillHalo fillData(comm, rank_info, {nx, ny, nz}, {1, 1, 1}, 0, + 1, {true, true, true}); fillData.fill(IDs); // Create a list of all neighbor ranks (excluding self) std::vector neighbors; - neighbors.push_back( rank_info.rank[0][1][1] ); - neighbors.push_back( rank_info.rank[2][1][1] ); - neighbors.push_back( rank_info.rank[1][0][1] ); - neighbors.push_back( rank_info.rank[1][2][1] ); - neighbors.push_back( rank_info.rank[1][1][0] ); - neighbors.push_back( rank_info.rank[1][1][2] ); - std::sort( neighbors.begin(), neighbors.end() ); - neighbors.erase( std::unique( neighbors.begin(), neighbors.end() ), neighbors.end() ); + neighbors.push_back(rank_info.rank[0][1][1]); + neighbors.push_back(rank_info.rank[2][1][1]); + neighbors.push_back(rank_info.rank[1][0][1]); + neighbors.push_back(rank_info.rank[1][2][1]); + neighbors.push_back(rank_info.rank[1][1][0]); + neighbors.push_back(rank_info.rank[1][1][2]); + std::sort(neighbors.begin(), neighbors.end()); + neighbors.erase(std::unique(neighbors.begin(), neighbors.end()), + neighbors.end()); // Create a map of all local ids to the neighbor ids - std::map map; + std::map map; std::set local; - for (size_t i=0; i=0 ) { + for (size_t i = 0; i < LocalIDs.length(); i++) { + if (LocalIDs(i) >= 0) { local.insert(LocalIDs(i)); - if ( LocalIDs(i)!=IDs(i) && IDs(i)>=0 ) + if (LocalIDs(i) != IDs(i) && IDs(i) >= 0) map[LocalIDs(i)].remote_ids.insert(IDs(i)); } } - std::map::iterator it; - for (it=map.begin(); it!=map.end(); ++it) { + std::map::iterator it; + for (it = map.begin(); it != map.end(); ++it) { it->second.new_id = it->first; local.erase(it->first); } // Get the number of ids we will recieve from each rank int N_send = map.size(); - std::vector N_recv(neighbors.size(),0); + std::vector N_recv(neighbors.size(), 0); std::vector send_req(neighbors.size()); std::vector recv_req(neighbors.size()); - for (size_t i=0; i recv_buf(neighbors.size()); - for (size_t i=0; i recv_buf(neighbors.size()); + for (size_t i = 0; i < neighbors.size(); i++) + recv_buf[i] = new int64_t[2 * N_recv[i]]; // Compute a map for the remote ids, and new local id for each id - std::map remote_map; - for (it=map.begin(); it!=map.end(); ++it) { + std::map remote_map; + for (it = map.begin(); it != map.end(); ++it) { int64_t id = it->first; std::set::const_iterator it2; - for (it2=it->second.remote_ids.begin(); it2!=it->second.remote_ids.end(); ++it2) { + for (it2 = it->second.remote_ids.begin(); + it2 != it->second.remote_ids.end(); ++it2) { int64_t id2 = *it2; - id = std::min(id,id2); - remote_map.insert(std::pair(id2,id2)); + id = std::min(id, id2); + remote_map.insert(std::pair(id2, id2)); } it->second.new_id = id; } // Iterate until we are done int iteration = 1; - PROFILE_START("LocalToGlobalIDs-loop",1); - while ( 1 ) { + PROFILE_START("LocalToGlobalIDs-loop", 1); + while (1) { iteration++; // Send the local ids and their new value to all neighbors - updateRemoteIds( map, neighbors, N_send, N_recv,send_buf, recv_buf, remote_map, comm ); + updateRemoteIds(map, neighbors, N_send, N_recv, send_buf, recv_buf, + remote_map, comm); // Compute a new local id for each local id - bool changed = updateLocalIds( remote_map, map ); + bool changed = updateLocalIds(remote_map, map); // Check if we are finished - int test = changed ? 1:0; - int result = comm.sumReduce( test ); - if ( result==0 ) + int test = changed ? 1 : 0; + int result = comm.sumReduce(test); + if (result == 0) break; } - PROFILE_STOP("LocalToGlobalIDs-loop",1); + PROFILE_STOP("LocalToGlobalIDs-loop", 1); // Relabel the ids - std::vector final_map(nblobs,-1); - for (it=map.begin(); it!=map.end(); ++it) - final_map[it->first-offset] = it->second.new_id; - for (std::set::const_iterator it2=local.begin(); it2!=local.end(); ++it2) - final_map[*it2-offset] = *it2; - for (size_t i=0; i=0); - for (size_t k=ngz; k= 0 ) - IDs(i,j,k) = final_map[id-offset]; + std::vector final_map(nblobs, -1); + for (it = map.begin(); it != map.end(); ++it) + final_map[it->first - offset] = it->second.new_id; + for (std::set::const_iterator it2 = local.begin(); + it2 != local.end(); ++it2) + final_map[*it2 - offset] = *it2; + for (size_t i = 0; i < final_map.size(); i++) + ASSERT(final_map[i] >= 0); + for (size_t k = ngz; k < IDs.size(2) - ngz; k++) { + for (size_t j = ngy; j < IDs.size(1) - ngy; j++) { + for (size_t i = ngx; i < IDs.size(0) - ngx; i++) { + BlobIDType id = IDs(i, j, k); + if (id >= 0) + IDs(i, j, k) = final_map[id - offset]; } } } // Fill the ghosts - fillHalo fillData2(comm,rank_info,{nx,ny,nz},{1,1,1},0,1,{true,true,true}); + fillHalo fillData2(comm, rank_info, {nx, ny, nz}, {1, 1, 1}, 0, + 1, {true, true, true}); fillData2.fill(IDs); // Reorder based on size (and compress the id space - int N_blobs_global = ReorderBlobIDs2(IDs,N_blobs_tot,ngx,ngy,ngz,comm); + int N_blobs_global = ReorderBlobIDs2(IDs, N_blobs_tot, ngx, ngy, ngz, comm); // Finished - delete [] send_buf; - for (size_t i=0; i > map_type; -template -void gatherSet( std::set& set, const Utilities::MPI& comm ) -{ +typedef std::map> map_type; +template +void gatherSet(std::set &set, const Utilities::MPI &comm) { int nprocs = comm.getSize(); - std::vector send_data(set.begin(),set.end()); + std::vector send_data(set.begin(), set.end()); int send_count = send_data.size(); - std::vector recv_count(nprocs,0), recv_disp(nprocs,0); - comm.allGather( send_count, getPtr(recv_count) ); - for (int i=1; i recv_data(recv_disp[nprocs-1]+recv_count[nprocs-1]); - comm.allGather( getPtr(send_data), send_count, getPtr(recv_data), - getPtr(recv_count), getPtr(recv_disp), true ); - for (size_t i=0; i recv_count(nprocs, 0), recv_disp(nprocs, 0); + comm.allGather(send_count, getPtr(recv_count)); + for (int i = 1; i < nprocs; i++) + recv_disp[i] = recv_disp[i - 1] + recv_count[i - 1]; + std::vector recv_data(recv_disp[nprocs - 1] + recv_count[nprocs - 1]); + comm.allGather(getPtr(send_data), send_count, getPtr(recv_data), + getPtr(recv_count), getPtr(recv_disp), true); + for (size_t i = 0; i < recv_data.size(); i++) set.insert(recv_data[i]); } -void gatherSrcIDMap( map_type& src_map, const Utilities::MPI& comm ) -{ +void gatherSrcIDMap(map_type &src_map, const Utilities::MPI &comm) { int nprocs = comm.getSize(); std::vector send_data; - for (auto it=src_map.begin(); it!=src_map.end(); ++it) { + for (auto it = src_map.begin(); it != src_map.end(); ++it) { int id = it->first; - const std::map& src_ids = it->second; + const std::map &src_ids = it->second; send_data.push_back(id); send_data.push_back(src_ids.size()); - std::map::const_iterator it2; - for (it2=src_ids.begin(); it2!=src_ids.end(); ++it2) { + std::map::const_iterator it2; + for (it2 = src_ids.begin(); it2 != src_ids.end(); ++it2) { send_data.push_back(it2->first); send_data.push_back(it2->second); } } int send_count = send_data.size(); - std::vector recv_count(nprocs,0), recv_disp(nprocs,0); - comm.allGather(send_count,getPtr(recv_count)); - for (int i=1; i recv_data(recv_disp[nprocs-1]+recv_count[nprocs-1]); - comm.allGather(getPtr(send_data),send_count, - getPtr(recv_data),getPtr(recv_count),getPtr(recv_disp),true); - size_t i=0; + std::vector recv_count(nprocs, 0), recv_disp(nprocs, 0); + comm.allGather(send_count, getPtr(recv_count)); + for (int i = 1; i < nprocs; i++) + recv_disp[i] = recv_disp[i - 1] + recv_count[i - 1]; + std::vector recv_data(recv_disp[nprocs - 1] + + recv_count[nprocs - 1]); + comm.allGather(getPtr(send_data), send_count, getPtr(recv_data), + getPtr(recv_count), getPtr(recv_disp), true); + size_t i = 0; src_map.clear(); - while ( i < recv_data.size() ) { + while (i < recv_data.size()) { BlobIDType id = recv_data[i]; - size_t count = recv_data[i+1]; + size_t count = recv_data[i + 1]; i += 2; - auto& src_ids = src_map[id]; - for (size_t j=0; j(recv_data[i],recv_data[i+1])); + if (it == src_ids.end()) + src_ids.insert(std::pair( + recv_data[i], recv_data[i + 1])); else - it->second += recv_data[i+1]; + it->second += recv_data[i + 1]; } } } -void addSrcDstIDs( BlobIDType src_id, map_type& src_map, map_type& dst_map, - std::set& src, std::set& dst ) -{ +void addSrcDstIDs(BlobIDType src_id, map_type &src_map, map_type &dst_map, + std::set &src, std::set &dst) { src.insert(src_id); - const std::map& dst_ids = dst_map[src_id]; - for (std::map::const_iterator it=dst_ids.begin(); it!=dst_ids.end(); ++it) { - if ( dst.find(it->first)==dst.end() ) - addSrcDstIDs(it->first,dst_map,src_map,dst,src); + const std::map &dst_ids = dst_map[src_id]; + for (std::map::const_iterator it = dst_ids.begin(); + it != dst_ids.end(); ++it) { + if (dst.find(it->first) == dst.end()) + addSrcDstIDs(it->first, dst_map, src_map, dst, src); } } -ID_map_struct computeIDMap( int nx, int ny, int nz, - const BlobIDArray& ID1, const BlobIDArray& ID2, const Utilities::MPI& comm ) -{ - ASSERT(ID1.size()==ID2.size()); +ID_map_struct computeIDMap(int nx, int ny, int nz, const BlobIDArray &ID1, + const BlobIDArray &ID2, const Utilities::MPI &comm) { + ASSERT(ID1.size() == ID2.size()); PROFILE_START("computeIDMap"); - const int ngx = (ID1.size(0)-nx)/2; - const int ngy = (ID1.size(1)-ny)/2; - const int ngz = (ID1.size(2)-nz)/2; + const int ngx = (ID1.size(0) - nx) / 2; + const int ngy = (ID1.size(1) - ny) / 2; + const int ngz = (ID1.size(2) - nz) / 2; // Get a global list of all src/dst ids and the src map for each local blob std::set src_set, dst_set; - map_type src_map; // Map of the src ids for each dst id - for (int k=ngz; k=0 ) + map_type src_map; // Map of the src ids for each dst id + for (int k = ngz; k < ngz + nz; k++) { + for (int j = ngy; j < ngy + ny; j++) { + for (int i = ngx; i < ngx + nx; i++) { + int id1 = ID1(i, j, k); + int id2 = ID2(i, j, k); + if (id1 >= 0) src_set.insert(id1); - if ( id2>=0 ) + if (id2 >= 0) dst_set.insert(id2); - if ( id1>=0 && id2>=0 ) { - std::map& src_ids = src_map[id2]; - std::map::iterator it = src_ids.find(id1); - if ( it == src_ids.end() ) { - src_ids.insert(std::pair(id1,0)); + if (id1 >= 0 && id2 >= 0) { + std::map &src_ids = src_map[id2]; + std::map::iterator it = + src_ids.find(id1); + if (it == src_ids.end()) { + src_ids.insert(std::pair(id1, 0)); it = src_ids.find(id1); } it->second++; @@ -574,82 +589,100 @@ ID_map_struct computeIDMap( int nx, int ny, int nz, } } // Communicate the src/dst ids and src id map to all processors and reduce - gatherSet( src_set, comm ); - gatherSet( dst_set, comm ); - gatherSrcIDMap( src_map, comm ); + gatherSet(src_set, comm); + gatherSet(dst_set, comm); + gatherSrcIDMap(src_map, comm); // Compute the dst id map - map_type dst_map; // Map of the dst ids for each src id - for (map_type::const_iterator it=src_map.begin(); it!=src_map.end(); ++it) { + map_type dst_map; // Map of the dst ids for each src id + for (map_type::const_iterator it = src_map.begin(); it != src_map.end(); + ++it) { BlobIDType id = it->first; - const std::map& src_ids = it->second; - for (std::map::const_iterator it2=src_ids.begin(); it2!=src_ids.end(); ++it2) { - std::map& dst_ids = dst_map[it2->first]; - dst_ids.insert(std::pair(id,it2->second)); + const std::map &src_ids = it->second; + for (std::map::const_iterator it2 = + src_ids.begin(); + it2 != src_ids.end(); ++it2) { + std::map &dst_ids = dst_map[it2->first]; + dst_ids.insert(std::pair(id, it2->second)); } } // Perform the mapping of ids ID_map_struct id_map; // Find new blobs - for (std::set::const_iterator it=dst_set.begin(); it!=dst_set.end(); ++it) { - if ( src_map.find(*it)==src_map.end() ) + for (std::set::const_iterator it = dst_set.begin(); + it != dst_set.end(); ++it) { + if (src_map.find(*it) == src_map.end()) id_map.created.push_back(*it); } // Fine blobs that disappeared - for (std::set::const_iterator it=src_set.begin(); it!=src_set.end(); ++it) { - if ( dst_map.find(*it)==dst_map.end() ) + for (std::set::const_iterator it = src_set.begin(); + it != src_set.end(); ++it) { + if (dst_map.find(*it) == dst_map.end()) id_map.destroyed.push_back(*it); } // Find blobs with a 1-to-1 mapping std::vector dst_list; dst_list.reserve(src_map.size()); - for (map_type::const_iterator it=src_map.begin(); it!=src_map.end(); ++it) + for (map_type::const_iterator it = src_map.begin(); it != src_map.end(); + ++it) dst_list.push_back(it->first); - for (size_t i=0; i& src_ids = src_map[dst_id]; - if ( src_ids.size()==1 ) { + const std::map &src_ids = src_map[dst_id]; + if (src_ids.size() == 1) { int src_id = src_ids.begin()->first; - const std::map& dst_ids = dst_map[src_id]; - if ( dst_ids.size()==1 ) { - ASSERT(dst_ids.begin()->first==dst_id); + const std::map &dst_ids = dst_map[src_id]; + if (dst_ids.size() == 1) { + ASSERT(dst_ids.begin()->first == dst_id); src_map.erase(dst_id); dst_map.erase(src_id); - id_map.src_dst.push_back(std::pair(src_id,dst_id)); + id_map.src_dst.push_back( + std::pair(src_id, dst_id)); } } } // Handle merge/splits - while ( !dst_map.empty() ) { + while (!dst_map.empty()) { // Get a lit of the src-dst ids std::set src, dst; - addSrcDstIDs( dst_map.begin()->first, src_map, dst_map, src, dst ); - if ( src.size()==1 ) { + addSrcDstIDs(dst_map.begin()->first, src_map, dst_map, src, dst); + if (src.size() == 1) { // Bubble split - id_map.split.push_back( BlobIDSplitStruct(*src.begin(),std::vector(dst.begin(),dst.end())) ); - } else if ( dst.size()==1 ) { + id_map.split.push_back(BlobIDSplitStruct( + *src.begin(), std::vector(dst.begin(), dst.end()))); + } else if (dst.size() == 1) { // Bubble merge - id_map.merge.push_back( BlobIDMergeStruct(std::vector(src.begin(),src.end()),*dst.begin()) ); + id_map.merge.push_back(BlobIDMergeStruct( + std::vector(src.begin(), src.end()), *dst.begin())); } else { // Bubble split/merge - id_map.merge_split.push_back( BlobIDMergeSplitStruct( - std::vector(src.begin(),src.end()), std::vector(dst.begin(),dst.end() ) ) ); + id_map.merge_split.push_back(BlobIDMergeSplitStruct( + std::vector(src.begin(), src.end()), + std::vector(dst.begin(), dst.end()))); } // Add the overlaps - for (std::set::const_iterator it1=src.begin(); it1!=src.end(); ++it1) { - const std::map& dst_ids = dst_map[*it1]; - for (std::set::const_iterator it2=dst.begin(); it2!=dst.end(); ++it2) { - std::pair id(*it1,*it2); + for (std::set::const_iterator it1 = src.begin(); + it1 != src.end(); ++it1) { + const std::map &dst_ids = dst_map[*it1]; + for (std::set::const_iterator it2 = dst.begin(); + it2 != dst.end(); ++it2) { + std::pair id(*it1, *it2); int64_t overlap = 0; - const std::map::const_iterator it = dst_ids.find(*it2); - if ( it != dst_ids.end() ) { overlap = it->second; } - id_map.overlap.insert(std::pair(id,overlap)); + const std::map::const_iterator it = + dst_ids.find(*it2); + if (it != dst_ids.end()) { + overlap = it->second; + } + id_map.overlap.insert( + std::pair(id, overlap)); } } // Clear the mapped entries - for (std::set::const_iterator it=src.begin(); it!=src.end(); ++it) + for (std::set::const_iterator it = src.begin(); + it != src.end(); ++it) dst_map.erase(*it); - for (std::set::const_iterator it=dst.begin(); it!=dst.end(); ++it) + for (std::set::const_iterator it = dst.begin(); + it != dst.end(); ++it) src_map.erase(*it); } ASSERT(src_map.empty()); @@ -658,181 +691,198 @@ ID_map_struct computeIDMap( int nx, int ny, int nz, return id_map; } - /****************************************************************** * Renumber the ids * ******************************************************************/ typedef std::vector IDvec; -inline void renumber( const std::vector& id1, const std::vector& id2, - const std::map& overlap, std::vector& new_ids, BlobIDType& id_max ) -{ - if ( id2.empty() ) { +inline void renumber(const std::vector &id1, + const std::vector &id2, + const std::map &overlap, + std::vector &new_ids, BlobIDType &id_max) { + if (id2.empty()) { // No dst ids to set - } else if ( id1.empty() ) { + } else if (id1.empty()) { // No src ids - for (size_t i=0; i cost(id1.size(),id2.size()); - for (size_t j=0; j(id1[i],id2[j]))->second; + Array cost(id1.size(), id2.size()); + for (size_t j = 0; j < id2.size(); j++) { + for (size_t i = 0; i < id1.size(); i++) { + cost(i, j) = + overlap + .find(std::pair(id1[i], id2[j])) + ->second; } } // While we have not mapped all dst ids - while ( 1 ) { + while (1) { size_t index = 1; int64_t cost2 = -1; - for (size_t i=0; i cost2 ) { + for (size_t i = 0; i < cost.length(); i++) { + if (cost(i) > cost2) { cost2 = cost(i); index = i; } } - if ( cost2 <= 0 ) + if (cost2 <= 0) break; // Use id1[i] for id2[j] - int i = index%id1.size(); - int j = index/id1.size(); - if ( (BlobIDType) new_ids.size() < id2[j]+1 ) - new_ids.resize(id2[j]+1,-1); + int i = index % id1.size(); + int j = index / id1.size(); + if ((BlobIDType)new_ids.size() < id2[j] + 1) + new_ids.resize(id2[j] + 1, -1); new_ids[id2[j]] = id1[i]; - for (size_t k=0; k& new_ids, BlobIDType& id ) -{ +inline void renumberIDs(const std::vector &new_ids, + BlobIDType &id) { id = new_ids[id]; } -inline void renumberIDs( const std::vector& new_ids, std::vector& ids ) -{ - for (size_t i=0; i &new_ids, + std::vector &ids) { + for (size_t i = 0; i < ids.size(); i++) ids[i] = new_ids[ids[i]]; } -void getNewIDs( ID_map_struct& map, BlobIDType& id_max, std::vector& new_ids ) -{ +void getNewIDs(ID_map_struct &map, BlobIDType &id_max, + std::vector &new_ids) { new_ids.resize(0); // Get the new id numbers for each map type - for (size_t i=0; i(),IDvec(1,map.created[i]),map.overlap,new_ids,id_max); - for (size_t i=0; i(),map.overlap,new_ids,id_max); - for (size_t i=0; i(), IDvec(1, map.created[i]), + map.overlap, new_ids, id_max); + for (size_t i = 0; i < map.destroyed.size(); i++) + renumber(IDvec(1, map.destroyed[i]), std::vector(), + map.overlap, new_ids, id_max); + for (size_t i = 0; i < map.split.size(); i++) + renumber(IDvec(1, map.split[i].first), map.split[i].second, map.overlap, + new_ids, id_max); + for (size_t i = 0; i < map.merge.size(); i++) + renumber(map.merge[i].first, IDvec(1, map.merge[i].second), map.overlap, + new_ids, id_max); + for (size_t i = 0; i < map.merge_split.size(); i++) + renumber(map.merge_split[i].first, map.merge_split[i].second, + map.overlap, new_ids, id_max); // Renumber the ids in the map - for (size_t i=0; i overlap2; - for (std::map::const_iterator it=map.overlap.begin(); it!=map.overlap.begin(); ++it) { + for (size_t i = 0; i < map.src_dst.size(); i++) + renumberIDs(new_ids, map.src_dst[i].second); + renumberIDs(new_ids, map.created); + for (size_t i = 0; i < map.split.size(); i++) + renumberIDs(new_ids, map.split[i].second); + for (size_t i = 0; i < map.merge.size(); i++) + renumberIDs(new_ids, map.merge[i].second); + for (size_t i = 0; i < map.merge_split.size(); i++) + renumberIDs(new_ids, map.merge_split[i].second); + std::map overlap2; + for (std::map::const_iterator it = map.overlap.begin(); + it != map.overlap.begin(); ++it) { OverlapID id = it->first; - renumberIDs( new_ids, id.second ); - overlap2.insert( std::pair(id,it->second) ); + renumberIDs(new_ids, id.second); + overlap2.insert(std::pair(id, it->second)); } } -void renumberIDs( const std::vector& new_ids, BlobIDArray& IDs ) -{ +void renumberIDs(const std::vector &new_ids, BlobIDArray &IDs) { size_t N = IDs.length(); - BlobIDType* ids = IDs.data(); - for (size_t i=0; i=0 ) + if (id >= 0) ids[i] = new_ids[id]; } } - /****************************************************************** * Write the id map for the given timestep * ******************************************************************/ -void writeIDMap( const ID_map_struct& map, long long int timestep, const std::string& filename ) -{ - int rank = Utilities::MPI( MPI_COMM_WORLD ).getRank(); - if ( rank!=0 ) +void writeIDMap(const ID_map_struct &map, long long int timestep, + const std::string &filename) { + int rank = Utilities::MPI(MPI_COMM_WORLD).getRank(); + if (rank != 0) return; bool empty = map.created.empty() && map.destroyed.empty() && - map.split.empty() && map.merge.empty() && map.merge_split.empty(); - for (size_t i=0; i(map.created[i])); - for (size_t i=0; i(map.destroyed[i])); - for (size_t i=0; i(map.src_dst[i].first), - static_cast(map.src_dst[i].second)); + fprintf(fid, "%lli:", timestep); + for (size_t i = 0; i < map.created.size(); i++) + fprintf(fid, " -%lli", static_cast(map.created[i])); + for (size_t i = 0; i < map.destroyed.size(); i++) + fprintf(fid, " %lli-", static_cast(map.destroyed[i])); + for (size_t i = 0; i < map.src_dst.size(); i++) { + if (map.src_dst[i].first != map.src_dst[i].second) + fprintf(fid, " %lli-%lli", + static_cast(map.src_dst[i].first), + static_cast(map.src_dst[i].second)); } - for (size_t i=0; i(map.split[i].first), - static_cast(map.split[i].second[0])); - for (size_t j=1; j(map.split[i].second[j])); + for (size_t i = 0; i < map.split.size(); i++) { + fprintf(fid, " %lli-%lli", + static_cast(map.split[i].first), + static_cast(map.split[i].second[0])); + for (size_t j = 1; j < map.split[i].second.size(); j++) + fprintf(fid, "/%lli", + static_cast(map.split[i].second[j])); } - for (size_t i=0; i(map.merge[i].first[0])); - for (size_t j=1; j(map.merge[i].first[j])); - fprintf(fid,"-%lli",static_cast(map.merge[i].second)); + for (size_t i = 0; i < map.merge.size(); i++) { + fprintf(fid, " %lli", + static_cast(map.merge[i].first[0])); + for (size_t j = 1; j < map.merge[i].first.size(); j++) + fprintf(fid, "/%lli", + static_cast(map.merge[i].first[j])); + fprintf(fid, "-%lli", static_cast(map.merge[i].second)); } - for (size_t i=0; i(map.merge_split[i].first[0])); - for (size_t j=1; j(map.merge_split[i].first[j])); - fprintf(fid,"-%lli",static_cast(map.merge_split[i].second[0])); - for (size_t j=1; j(map.merge_split[i].second[j])); + for (size_t i = 0; i < map.merge_split.size(); i++) { + fprintf(fid, " %lli", + static_cast(map.merge_split[i].first[0])); + for (size_t j = 1; j < map.merge_split[i].first.size(); j++) + fprintf(fid, "/%lli", + static_cast(map.merge_split[i].first[j])); + fprintf(fid, "-%lli", + static_cast(map.merge_split[i].second[0])); + for (size_t j = 1; j < map.merge_split[i].second.size(); j++) + fprintf(fid, "/%lli", + static_cast(map.merge_split[i].second[j])); } - fprintf(fid,"\n"); + fprintf(fid, "\n"); fclose(fid); } - diff --git a/analysis/analysis.h b/analysis/analysis.h index 9e7135c2..2bb47558 100644 --- a/analysis/analysis.h +++ b/analysis/analysis.h @@ -24,12 +24,10 @@ #include #include - // Define types to use for blob ids typedef int32_t BlobIDType; typedef Array BlobIDArray; - /*! * @brief Compute the blob * @details Compute the blob (F>vf|S>vs) starting from (i,j,k) - oil blob @@ -42,8 +40,9 @@ typedef Array BlobIDArray; * @param[in] periodic Optional value * @return Returns the number of blobs */ -int ComputeLocalBlobIDs( const DoubleArray& Phase, const DoubleArray& SignDist, - double vF, double vS, BlobIDArray& LocalBlobID, bool periodic=true ); +int ComputeLocalBlobIDs(const DoubleArray &Phase, const DoubleArray &SignDist, + double vF, double vS, BlobIDArray &LocalBlobID, + bool periodic = true); /*! * @brief Compute blob of an arbitrary phase @@ -54,8 +53,8 @@ int ComputeLocalBlobIDs( const DoubleArray& Phase, const DoubleArray& SignDist, * @param[out] ComponentLabel * @param[in] periodic */ -int ComputeLocalPhaseComponent( const IntArray &PhaseID, int &VALUE, IntArray &ComponentLabel, bool periodic ); - +int ComputeLocalPhaseComponent(const IntArray &PhaseID, int &VALUE, + IntArray &ComponentLabel, bool periodic); /*! * @brief Compute the blob @@ -73,10 +72,11 @@ int ComputeLocalPhaseComponent( const IntArray &PhaseID, int &VALUE, IntArray &C * @param[in] comm MPI communicator * @return Returns the number of blobs */ -int ComputeGlobalBlobIDs( int nx, int ny, int nz, const RankInfoStruct& rank_info, - const DoubleArray& Phase, const DoubleArray& SignDist, double vF, double vS, - BlobIDArray& GlobalBlobID, const Utilities::MPI& comm ); - +int ComputeGlobalBlobIDs(int nx, int ny, int nz, + const RankInfoStruct &rank_info, + const DoubleArray &Phase, const DoubleArray &SignDist, + double vF, double vS, BlobIDArray &GlobalBlobID, + const Utilities::MPI &comm); /*! * @brief Compute component of the specified phase @@ -92,9 +92,11 @@ int ComputeGlobalBlobIDs( int nx, int ny, int nz, const RankInfoStruct& rank_inf * @param[in] comm The communicator to use * @return Return the number of components in the specified phase */ -int ComputeGlobalPhaseComponent( int nx, int ny, int nz, const RankInfoStruct& rank_info, - const IntArray &PhaseID, int &VALUE, BlobIDArray &GlobalBlobID, const Utilities::MPI& comm ); - +int ComputeGlobalPhaseComponent(int nx, int ny, int nz, + const RankInfoStruct &rank_info, + const IntArray &PhaseID, int &VALUE, + BlobIDArray &GlobalBlobID, + const Utilities::MPI &comm); /*! * @brief Reorder the blobs @@ -103,31 +105,35 @@ int ComputeGlobalPhaseComponent( int nx, int ny, int nz, const RankInfoStruct& r * @param[in,out] ID The ids of the blobs * @param[in] comm MPI communicator */ -void ReorderBlobIDs( BlobIDArray& ID, const Utilities::MPI& comm ); +void ReorderBlobIDs(BlobIDArray &ID, const Utilities::MPI &comm); - -typedef std::pair > BlobIDSplitStruct; -typedef std::pair,BlobIDType> BlobIDMergeStruct; -typedef std::pair,std::vector > BlobIDMergeSplitStruct; -typedef std::pair OverlapID; +typedef std::pair> BlobIDSplitStruct; +typedef std::pair, BlobIDType> BlobIDMergeStruct; +typedef std::pair, std::vector> + BlobIDMergeSplitStruct; +typedef std::pair OverlapID; struct ID_map_struct { - std::vector created; // list of new blobs that were created - std::vector destroyed; // list of blobs that disappeared - std::vector > src_dst; // one-one mapping of blobs (first,second timestep id) - std::vector split; // list of blobs that split - std::vector merge; // list of blobs that merged - std::vector merge_split; // list of blobs that both merged and split - std::map overlap; // for ids that are not a 1-1 mapping, this is a list of the overlaps + std::vector created; // list of new blobs that were created + std::vector destroyed; // list of blobs that disappeared + std::vector> + src_dst; // one-one mapping of blobs (first,second timestep id) + std::vector split; // list of blobs that split + std::vector merge; // list of blobs that merged + std::vector + merge_split; // list of blobs that both merged and split + std::map + overlap; // for ids that are not a 1-1 mapping, this is a list of the overlaps //! Empty constructor ID_map_struct() {} //! Create initial map from N blobs (ordered 1:N-1) - ID_map_struct( int N ) { + ID_map_struct(int N) { created.resize(N); - for (int i=0; i& new_ids ); - +void getNewIDs(ID_map_struct &map, BlobIDType &id_max, + std::vector &new_ids); /*! * @brief Update the blob ids based on mapping @@ -161,8 +167,7 @@ void getNewIDs( ID_map_struct& map, BlobIDType& id_max, std::vector& * @param[out] new_ids The newly renumbered blob ids (0:ids.max()) * @param[in,out] IDs The blob ids to renumber */ -void renumberIDs( const std::vector& new_ids, BlobIDArray& IDs ); - +void renumberIDs(const std::vector &new_ids, BlobIDArray &IDs); /*! * @brief Write the ID map @@ -173,8 +178,7 @@ void renumberIDs( const std::vector& new_ids, BlobIDArray& IDs ); * @param[in] timestep The current timestep (timestep 0 creates the file) * @param[in] filename The filename to write/append */ -void writeIDMap( const ID_map_struct& map, long long int timestep, const std::string& filename ); - - +void writeIDMap(const ID_map_struct &map, long long int timestep, + const std::string &filename); #endif diff --git a/analysis/dcel.cpp b/analysis/dcel.cpp index 9e41673d..f616a7a8 100644 --- a/analysis/dcel.cpp +++ b/analysis/dcel.cpp @@ -1,337 +1,377 @@ #include "analysis/dcel.h" -DCEL::DCEL(){ +DCEL::DCEL() {} + +DCEL::~DCEL() { + TriangleCount = 0; + VertexCount = 0; } -DCEL::~DCEL(){ - TriangleCount=0; - VertexCount=0; - +int DCEL::Face(int index) { return FaceData[index]; } + +void DCEL::Write() { + int e1, e2, e3; + FILE *TRIANGLES; + TRIANGLES = fopen("triangles.stl", "w"); + fprintf(TRIANGLES, "solid \n"); + for (int idx = 0; idx < TriangleCount; idx++) { + e1 = Face(idx); + e2 = halfedge.next(e1); + e3 = halfedge.next(e2); + auto P1 = vertex.coords(halfedge.v1(e1)); + auto P2 = vertex.coords(halfedge.v1(e2)); + auto P3 = vertex.coords(halfedge.v1(e3)); + fprintf(TRIANGLES, "vertex %f %f %f\n", P1.x, P1.y, P1.z); + fprintf(TRIANGLES, "vertex %f %f %f\n", P2.x, P2.y, P2.z); + fprintf(TRIANGLES, "vertex %f %f %f\n", P3.x, P3.y, P3.z); + } + fclose(TRIANGLES); } -int DCEL::Face(int index){ - return FaceData[index]; +void DCEL::LocalIsosurface(const DoubleArray &A, double value, const int i, + const int j, const int k) { + Point P, Q; + Point PlaceHolder; + Point C0, C1, C2, C3, C4, C5, C6, C7; + + Point VertexList[12]; + Point NewVertexList[12]; + int LocalRemap[12]; + + Point cellvertices[20]; + std::array, 20> Triangles; + + // Values from array 'A' at the cube corners + double CubeValues[8]; + + // Points corresponding to cube corners + C0.x = 0.0; + C0.y = 0.0; + C0.z = 0.0; + C1.x = 1.0; + C1.y = 0.0; + C1.z = 0.0; + C2.x = 1.0; + C2.y = 1.0; + C2.z = 0.0; + C3.x = 0.0; + C3.y = 1.0; + C3.z = 0.0; + C4.x = 0.0; + C4.y = 0.0; + C4.z = 1.0; + C5.x = 1.0; + C5.y = 0.0; + C5.z = 1.0; + C6.x = 1.0; + C6.y = 1.0; + C6.z = 1.0; + C7.x = 0.0; + C7.y = 1.0; + C7.z = 1.0; + + CubeValues[0] = A(i, j, k) - value; + CubeValues[1] = A(i + 1, j, k) - value; + CubeValues[2] = A(i + 1, j + 1, k) - value; + CubeValues[3] = A(i, j + 1, k) - value; + CubeValues[4] = A(i, j, k + 1) - value; + CubeValues[5] = A(i + 1, j, k + 1) - value; + CubeValues[6] = A(i + 1, j + 1, k + 1) - value; + CubeValues[7] = A(i, j + 1, k + 1) - value; + //printf("Set cube values: %i, %i, %i \n",i,j,k); + + //Determine the index into the edge table which + //tells us which vertices are inside of the surface + int CubeIndex = 0; + if (CubeValues[0] < 0.0f) + CubeIndex |= 1; + if (CubeValues[1] < 0.0f) + CubeIndex |= 2; + if (CubeValues[2] < 0.0f) + CubeIndex |= 4; + if (CubeValues[3] < 0.0f) + CubeIndex |= 8; + if (CubeValues[4] < 0.0f) + CubeIndex |= 16; + if (CubeValues[5] < 0.0f) + CubeIndex |= 32; + if (CubeValues[6] < 0.0f) + CubeIndex |= 64; + if (CubeValues[7] < 0.0f) + CubeIndex |= 128; + + //Find the vertices where the surface intersects the cube + if (edgeTable[CubeIndex] & 1) { + P = VertexInterp(C0, C1, CubeValues[0], CubeValues[1]); + VertexList[0] = P; + Q = C0; + } + if (edgeTable[CubeIndex] & 2) { + P = VertexInterp(C1, C2, CubeValues[1], CubeValues[2]); + VertexList[1] = P; + Q = C1; + } + if (edgeTable[CubeIndex] & 4) { + P = VertexInterp(C2, C3, CubeValues[2], CubeValues[3]); + VertexList[2] = P; + Q = C2; + } + if (edgeTable[CubeIndex] & 8) { + P = VertexInterp(C3, C0, CubeValues[3], CubeValues[0]); + VertexList[3] = P; + Q = C3; + } + if (edgeTable[CubeIndex] & 16) { + P = VertexInterp(C4, C5, CubeValues[4], CubeValues[5]); + VertexList[4] = P; + Q = C4; + } + if (edgeTable[CubeIndex] & 32) { + P = VertexInterp(C5, C6, CubeValues[5], CubeValues[6]); + VertexList[5] = P; + Q = C5; + } + if (edgeTable[CubeIndex] & 64) { + P = VertexInterp(C6, C7, CubeValues[6], CubeValues[7]); + VertexList[6] = P; + Q = C6; + } + if (edgeTable[CubeIndex] & 128) { + P = VertexInterp(C7, C4, CubeValues[7], CubeValues[4]); + VertexList[7] = P; + Q = C7; + } + if (edgeTable[CubeIndex] & 256) { + P = VertexInterp(C0, C4, CubeValues[0], CubeValues[4]); + VertexList[8] = P; + Q = C0; + } + if (edgeTable[CubeIndex] & 512) { + P = VertexInterp(C1, C5, CubeValues[1], CubeValues[5]); + VertexList[9] = P; + Q = C1; + } + if (edgeTable[CubeIndex] & 1024) { + P = VertexInterp(C2, C6, CubeValues[2], CubeValues[6]); + VertexList[10] = P; + Q = C2; + } + if (edgeTable[CubeIndex] & 2048) { + P = VertexInterp(C3, C7, CubeValues[3], CubeValues[7]); + VertexList[11] = P; + Q = C3; + } + + VertexCount = 0; + for (int idx = 0; idx < 12; idx++) + LocalRemap[idx] = -1; + + for (int idx = 0; triTable[CubeIndex][idx] != -1; idx++) { + if (LocalRemap[triTable[CubeIndex][idx]] == -1) { + NewVertexList[VertexCount] = VertexList[triTable[CubeIndex][idx]]; + LocalRemap[triTable[CubeIndex][idx]] = VertexCount; + VertexCount++; + } + } + + //printf("Found %i vertices \n",VertexCount); + + for (int idx = 0; idx < VertexCount; idx++) { + P = NewVertexList[idx]; + //P.x += i; + //P.y += j; + //P.z += k; + cellvertices[idx] = P; + } + + TriangleCount = 0; + for (int idx = 0; triTable[CubeIndex][idx] != -1; idx += 3) { + Triangles[TriangleCount][0] = LocalRemap[triTable[CubeIndex][idx + 0]]; + Triangles[TriangleCount][1] = LocalRemap[triTable[CubeIndex][idx + 1]]; + Triangles[TriangleCount][2] = LocalRemap[triTable[CubeIndex][idx + 2]]; + TriangleCount++; + } + int nTris = TriangleCount; + + // Now add the local values to the DCEL data structure + if (nTris > 0) { + FaceData.resize(TriangleCount); + //printf("Construct halfedge structure... \n"); + //printf(" Construct %i triangles \n",nTris); + halfedge.resize(nTris * 3); + int idx_edge = 0; + for (int idx = 0; idx < TriangleCount; idx++) { + int V1 = Triangles[idx][0]; + int V2 = Triangles[idx][1]; + int V3 = Triangles[idx][2]; + FaceData[idx] = idx_edge; + // first edge: V1->V2 + halfedge.data(0, idx_edge) = V1; // first vertex + halfedge.data(1, idx_edge) = V2; // second vertex + halfedge.data(2, idx_edge) = idx; // triangle + halfedge.data(3, idx_edge) = -1; // twin + halfedge.data(4, idx_edge) = idx_edge + 2; // previous edge + halfedge.data(5, idx_edge) = idx_edge + 1; // next edge + idx_edge++; + // second edge: V2->V3 + halfedge.data(0, idx_edge) = V2; // first vertex + halfedge.data(1, idx_edge) = V3; // second vertex + halfedge.data(2, idx_edge) = idx; // triangle + halfedge.data(3, idx_edge) = -1; // twin + halfedge.data(4, idx_edge) = idx_edge - 1; // previous edge + halfedge.data(5, idx_edge) = idx_edge + 1; // next edge + idx_edge++; + // third edge: V3->V1 + halfedge.data(0, idx_edge) = V3; // first vertex + halfedge.data(1, idx_edge) = V1; // second vertex + halfedge.data(2, idx_edge) = idx; // triangle + halfedge.data(3, idx_edge) = -1; // twin + halfedge.data(4, idx_edge) = idx_edge - 1; // previous edge + halfedge.data(5, idx_edge) = idx_edge - 2; // next edge + idx_edge++; + //printf(" ***tri %i ***edge %i *** \n",idx, idx_edge); + } + //printf(" parsing halfedge structure\n"); + int EdgeCount = idx_edge; + for (int idx = 0; idx < EdgeCount; idx++) { + int V1 = halfedge.data(0, idx); + int V2 = halfedge.data(1, idx); + // Find all the twins within the cube + for (int jdx = 0; jdx < EdgeCount; jdx++) { + if (halfedge.data(1, jdx) == V1 && + halfedge.data(0, jdx) == V2) { + // this is the pair + halfedge.data(3, idx) = jdx; + halfedge.data(3, jdx) = idx; + } + if (halfedge.data(1, jdx) == V2 && + halfedge.data(0, jdx) == V1 && !(idx == jdx)) { + std::printf( + "WARNING: half edges with identical orientation! \n"); + } + } + // Use "ghost" twins if edge is on a cube face + P = cellvertices[V1]; + Q = cellvertices[V2]; + if (P.x == 0.0 && Q.x == 0.0) + halfedge.data(3, idx) = -1; // ghost twin for x=0 face + if (P.x == 1.0 && Q.x == 1.0) + halfedge.data(3, idx) = -4; // ghost twin for x=1 face + if (P.y == 0.0 && Q.y == 0.0) + halfedge.data(3, idx) = -2; // ghost twin for y=0 face + if (P.y == 1.0 && Q.y == 1.0) + halfedge.data(3, idx) = -5; // ghost twin for y=1 face + if (P.z == 0.0 && Q.z == 0.0) + halfedge.data(3, idx) = -3; // ghost twin for z=0 face + if (P.z == 1.0 && Q.z == 1.0) + halfedge.data(3, idx) = -6; // ghost twin for z=1 face + } + } + + // Map vertices to global coordinates + for (int idx = 0; idx < VertexCount; idx++) { + P = cellvertices[idx]; + P.x += i; + P.y += j; + P.z += k; + vertex.assign(idx, P); + } } -void DCEL::Write(){ - int e1,e2,e3; - FILE *TRIANGLES; - TRIANGLES = fopen("triangles.stl","w"); - fprintf(TRIANGLES,"solid \n"); - for (int idx=0; idx,20> Triangles; - - // Values from array 'A' at the cube corners - double CubeValues[8]; - - // Points corresponding to cube corners - C0.x = 0.0; C0.y = 0.0; C0.z = 0.0; - C1.x = 1.0; C1.y = 0.0; C1.z = 0.0; - C2.x = 1.0; C2.y = 1.0; C2.z = 0.0; - C3.x = 0.0; C3.y = 1.0; C3.z = 0.0; - C4.x = 0.0; C4.y = 0.0; C4.z = 1.0; - C5.x = 1.0; C5.y = 0.0; C5.z = 1.0; - C6.x = 1.0; C6.y = 1.0; C6.z = 1.0; - C7.x = 0.0; C7.y = 1.0; C7.z = 1.0; - - CubeValues[0] = A(i,j,k) - value; - CubeValues[1] = A(i+1,j,k) - value; - CubeValues[2] = A(i+1,j+1,k) - value; - CubeValues[3] = A(i,j+1,k) - value; - CubeValues[4] = A(i,j,k+1) - value; - CubeValues[5] = A(i+1,j,k+1) - value; - CubeValues[6] = A(i+1,j+1,k+1) - value; - CubeValues[7] = A(i,j+1,k+1) -value; - //printf("Set cube values: %i, %i, %i \n",i,j,k); - - //Determine the index into the edge table which - //tells us which vertices are inside of the surface - int CubeIndex = 0; - if (CubeValues[0] < 0.0f) CubeIndex |= 1; - if (CubeValues[1] < 0.0f) CubeIndex |= 2; - if (CubeValues[2] < 0.0f) CubeIndex |= 4; - if (CubeValues[3] < 0.0f) CubeIndex |= 8; - if (CubeValues[4] < 0.0f) CubeIndex |= 16; - if (CubeValues[5] < 0.0f) CubeIndex |= 32; - if (CubeValues[6] < 0.0f) CubeIndex |= 64; - if (CubeValues[7] < 0.0f) CubeIndex |= 128; - - //Find the vertices where the surface intersects the cube - if (edgeTable[CubeIndex] & 1){ - P = VertexInterp(C0,C1,CubeValues[0],CubeValues[1]); - VertexList[0] = P; - Q = C0; - } - if (edgeTable[CubeIndex] & 2){ - P = VertexInterp(C1,C2,CubeValues[1],CubeValues[2]); - VertexList[1] = P; - Q = C1; - } - if (edgeTable[CubeIndex] & 4){ - P = VertexInterp(C2,C3,CubeValues[2],CubeValues[3]); - VertexList[2] = P; - Q = C2; - } - if (edgeTable[CubeIndex] & 8){ - P = VertexInterp(C3,C0,CubeValues[3],CubeValues[0]); - VertexList[3] = P; - Q = C3; - } - if (edgeTable[CubeIndex] & 16){ - P = VertexInterp(C4,C5,CubeValues[4],CubeValues[5]); - VertexList[4] = P; - Q = C4; - } - if (edgeTable[CubeIndex] & 32){ - P = VertexInterp(C5,C6,CubeValues[5],CubeValues[6]); - VertexList[5] = P; - Q = C5; - } - if (edgeTable[CubeIndex] & 64){ - P = VertexInterp(C6,C7,CubeValues[6],CubeValues[7]); - VertexList[6] = P; - Q = C6; - } - if (edgeTable[CubeIndex] & 128){ - P = VertexInterp(C7,C4,CubeValues[7],CubeValues[4]); - VertexList[7] = P; - Q = C7; - } - if (edgeTable[CubeIndex] & 256){ - P = VertexInterp(C0,C4,CubeValues[0],CubeValues[4]); - VertexList[8] = P; - Q = C0; - } - if (edgeTable[CubeIndex] & 512){ - P = VertexInterp(C1,C5,CubeValues[1],CubeValues[5]); - VertexList[9] = P; - Q = C1; - } - if (edgeTable[CubeIndex] & 1024){ - P = VertexInterp(C2,C6,CubeValues[2],CubeValues[6]); - VertexList[10] = P; - Q = C2; - } - if (edgeTable[CubeIndex] & 2048){ - P = VertexInterp(C3,C7,CubeValues[3],CubeValues[7]); - VertexList[11] = P; - Q = C3; - } - - VertexCount=0; - for (int idx=0;idx<12;idx++) - LocalRemap[idx] = -1; - - for (int idx=0;triTable[CubeIndex][idx]!=-1;idx++) - { - if(LocalRemap[triTable[CubeIndex][idx]] == -1) - { - NewVertexList[VertexCount] = VertexList[triTable[CubeIndex][idx]]; - LocalRemap[triTable[CubeIndex][idx]] = VertexCount; - VertexCount++; - } - } - - //printf("Found %i vertices \n",VertexCount); - - for (int idx=0;idx0){ - FaceData.resize(TriangleCount); - //printf("Construct halfedge structure... \n"); - //printf(" Construct %i triangles \n",nTris); - halfedge.resize(nTris*3); - int idx_edge=0; - for (int idx=0; idxV2 - halfedge.data(0,idx_edge) = V1; // first vertex - halfedge.data(1,idx_edge) = V2; // second vertex - halfedge.data(2,idx_edge) = idx; // triangle - halfedge.data(3,idx_edge) = -1; // twin - halfedge.data(4,idx_edge) = idx_edge+2; // previous edge - halfedge.data(5,idx_edge) = idx_edge+1; // next edge - idx_edge++; - // second edge: V2->V3 - halfedge.data(0,idx_edge) = V2; // first vertex - halfedge.data(1,idx_edge) = V3; // second vertex - halfedge.data(2,idx_edge) = idx; // triangle - halfedge.data(3,idx_edge) = -1; // twin - halfedge.data(4,idx_edge) = idx_edge-1; // previous edge - halfedge.data(5,idx_edge) = idx_edge+1; // next edge - idx_edge++; - // third edge: V3->V1 - halfedge.data(0,idx_edge) = V3; // first vertex - halfedge.data(1,idx_edge) = V1; // second vertex - halfedge.data(2,idx_edge) = idx; // triangle - halfedge.data(3,idx_edge) = -1; // twin - halfedge.data(4,idx_edge) = idx_edge-1; // previous edge - halfedge.data(5,idx_edge) = idx_edge-2; // next edge - idx_edge++; - //printf(" ***tri %i ***edge %i *** \n",idx, idx_edge); - } - //printf(" parsing halfedge structure\n"); - int EdgeCount=idx_edge; - for (int idx=0; idx 1.f) dotprod=1.f; - if (dotprod < -1.f) dotprod=-1.f; - angle = acos(dotprod); - /* project onto plane of cube face also works + if (dotprod > 1.f) + dotprod = 1.f; + if (dotprod < -1.f) + dotprod = -1.f; + angle = acos(dotprod); + /* project onto plane of cube face also works W = U - dotprod*V; length = sqrt(W.x*W.x+W.y*W.y+W.z*W.z); // for normalization dotprod = (U.x*W.x + U.y*W.y + U.z*W.z)/length; @@ -339,71 +379,75 @@ double DCEL::EdgeAngle(int edge) if (dotprod < -1.f) dotprod=-1.f; angle = acos(dotprod); */ - } - else{ - dotprod=U.x*V.x + U.y*V.y + U.z*V.z; - if (dotprod > 1.f) dotprod=1.f; - if (dotprod < -1.f) dotprod=-1.f; - angle = 0.5*acos(dotprod); - } - // determine if angle is concave or convex based on edge normal - W.x = (P.y-Q.y)*U.z - (P.z-Q.z)*U.y; - W.y = (P.z-Q.z)*U.x - (P.x-Q.x)*U.z; - W.z = (P.x-Q.x)*U.y - (P.y-Q.y)*U.x; - //length = sqrt(nx*nx+ny*ny+nz*nz); - Point w=0.5*(P+Q)-R; - if (W.x*w.x + W.y*w.y + W.z*w.z < 0.f){ - //printf("flip edge normal \n"); - W.x = -W.x; - W.y = -W.y; - W.z = -W.z; - } - if (W.x*V.x + W.y*V.y + W.z*V.z > 0.f){ - // concave - angle = -angle; - } - if (angle != angle) angle = 0.0; - //printf("angle=%f,dot=%f (Edge=%i, twin=%i): P={%f, %f, %f}, Q={%f, %f, %f} U={%f, %f, %f}, V={%f, %f, %f}\n",angle,dotprod,edge,halfedge.twin(edge),P.x,P.y,P.z,Q.x,Q.y,Q.z,U.x,U.y,U.z,V.x,V.y,V.z); - return angle; -} - -void iso_surface(const Array&Field, const double isovalue) -{ - DCEL object; - int e1,e2,e3; - FILE *TRIANGLES; - TRIANGLES = fopen("isosurface.stl","w"); - fprintf(TRIANGLES,"solid isosurface\n"); - int Nx = Field.size(0); - int Ny = Field.size(1); - int Nz = Field.size(2); - for (int k=1; k 1.f) + dotprod = 1.f; + if (dotprod < -1.f) + dotprod = -1.f; + angle = 0.5 * acos(dotprod); } - } - fprintf(TRIANGLES,"endsolid isosurface\n"); - fclose(TRIANGLES); + // determine if angle is concave or convex based on edge normal + W.x = (P.y - Q.y) * U.z - (P.z - Q.z) * U.y; + W.y = (P.z - Q.z) * U.x - (P.x - Q.x) * U.z; + W.z = (P.x - Q.x) * U.y - (P.y - Q.y) * U.x; + //length = sqrt(nx*nx+ny*ny+nz*nz); + Point w = 0.5 * (P + Q) - R; + if (W.x * w.x + W.y * w.y + W.z * w.z < 0.f) { + //printf("flip edge normal \n"); + W.x = -W.x; + W.y = -W.y; + W.z = -W.z; + } + if (W.x * V.x + W.y * V.y + W.z * V.z > 0.f) { + // concave + angle = -angle; + } + if (angle != angle) + angle = 0.0; + //printf("angle=%f,dot=%f (Edge=%i, twin=%i): P={%f, %f, %f}, Q={%f, %f, %f} U={%f, %f, %f}, V={%f, %f, %f}\n",angle,dotprod,edge,halfedge.twin(edge),P.x,P.y,P.z,Q.x,Q.y,Q.z,U.x,U.y,U.z,V.x,V.y,V.z); + return angle; +} + +void iso_surface(const Array &Field, const double isovalue) { + DCEL object; + int e1, e2, e3; + FILE *TRIANGLES; + TRIANGLES = fopen("isosurface.stl", "w"); + fprintf(TRIANGLES, "solid isosurface\n"); + int Nx = Field.size(0); + int Ny = Field.size(1); + int Nz = Field.size(2); + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + object.LocalIsosurface(Field, isovalue, i, j, k); + for (int idx = 0; idx < object.TriangleCount; idx++) { + e1 = object.Face(idx); + e2 = object.halfedge.next(e1); + e3 = object.halfedge.next(e2); + auto P1 = object.vertex.coords(object.halfedge.v1(e1)); + auto P2 = object.vertex.coords(object.halfedge.v1(e2)); + auto P3 = object.vertex.coords(object.halfedge.v1(e3)); + auto Normal = object.TriNormal(e1); + // P1.x += 1.0*i; P1.y += 1.0*j; P1.z +=1.0*k; + //P2.x += 1.0*i; P2.y += 1.0*j; P2.z +=1.0*k; + //P3.x += 1.0*i; P3.y += 1.0*j; P3.z +=1.0*k; + fprintf(TRIANGLES, "facet normal %f %f %f\n", Normal.x, + Normal.y, Normal.z); + fprintf(TRIANGLES, " outer loop\n"); + fprintf(TRIANGLES, " vertex %f %f %f\n", P1.x, P1.y, + P1.z); + fprintf(TRIANGLES, " vertex %f %f %f\n", P2.x, P2.y, + P2.z); + fprintf(TRIANGLES, " vertex %f %f %f\n", P3.x, P3.y, + P3.z); + fprintf(TRIANGLES, " endloop\n"); + fprintf(TRIANGLES, "endfacet\n"); + } + } + } + } + fprintf(TRIANGLES, "endsolid isosurface\n"); + fclose(TRIANGLES); } diff --git a/analysis/dcel.h b/analysis/dcel.h index e18f6930..3469d60f 100644 --- a/analysis/dcel.h +++ b/analysis/dcel.h @@ -10,85 +10,85 @@ */ // Vertex structure -class Vertex{ +class Vertex { public: - Vertex() { d_data.resize(12); } - ~Vertex() = default; - Vertex( const Vertex& ) = delete; - Vertex operator=( const Vertex& ) = delete; + Vertex() { d_data.resize(12); } + ~Vertex() = default; + Vertex(const Vertex &) = delete; + Vertex operator=(const Vertex &) = delete; // Add/assign a point - inline void add( const Point& P ) { d_data.push_back( P ); } - inline void assign( int idx, const Point& P ) { d_data[idx] = P; } + inline void add(const Point &P) { d_data.push_back(P); } + inline void assign(int idx, const Point &P) { d_data[idx] = P; } // Get a point - inline Point& coords( int idx ) { return d_data[idx]; } - inline const Point& coords( int idx ) const { return d_data[idx]; } + inline Point &coords(int idx) { return d_data[idx]; } + inline const Point &coords(int idx) const { return d_data[idx]; } - int IncidentEdge(); + int IncidentEdge(); // Return the number of points - inline int size() const { return d_data.size(); } + inline int size() const { return d_data.size(); } private: - std::vector d_data; + std::vector d_data; }; - /** * \class Halfedge * @brief store half edge for DCEL data structure */ -class Halfedge{ +class Halfedge { public: - Halfedge() = default; - ~Halfedge() = default; - Halfedge( const Halfedge& ) = delete; - Halfedge operator=( const Halfedge& ) = delete; + Halfedge() = default; + ~Halfedge() = default; + Halfedge(const Halfedge &) = delete; + Halfedge operator=(const Halfedge &) = delete; - inline int v1(int edge) const { return d_data[edge][0]; } - inline int v2(int edge) const { return d_data[edge][1]; } - inline int face(int edge) const { return d_data[edge][2]; } - inline int twin(int edge) const { return d_data[edge][3]; } - inline int prev(int edge) const { return d_data[edge][4]; } - inline int next(int edge) const { return d_data[edge][5]; } + inline int v1(int edge) const { return d_data[edge][0]; } + inline int v2(int edge) const { return d_data[edge][1]; } + inline int face(int edge) const { return d_data[edge][2]; } + inline int twin(int edge) const { return d_data[edge][3]; } + inline int prev(int edge) const { return d_data[edge][4]; } + inline int next(int edge) const { return d_data[edge][5]; } - inline int size() const { return d_data.size(); } - inline void resize( int N ) { d_data.resize( N ); } + inline int size() const { return d_data.size(); } + inline void resize(int N) { d_data.resize(N); } - inline int& data( int i, int j ) { return d_data[j][i]; } - inline const int& data( int i, int j ) const { return d_data[j][i]; } + inline int &data(int i, int j) { return d_data[j][i]; } + inline const int &data(int i, int j) const { return d_data[j][i]; } private: - std::vector> d_data; + std::vector> d_data; }; /** * \class DCEL * @details doubly connected edge list data structure */ -class DCEL{ +class DCEL { public: - DCEL(); - ~DCEL(); - - int face(); - Vertex vertex; - Halfedge halfedge; - void LocalIsosurface(const DoubleArray& A, double value, int i, int j, int k); - void Write(); - int Face(int index); - - double origin(int edge); - double EdgeAngle(int edge); - Point TriNormal(int edge); - int TriangleCount; - int VertexCount; + DCEL(); + ~DCEL(); + + int face(); + Vertex vertex; + Halfedge halfedge; + void LocalIsosurface(const DoubleArray &A, double value, int i, int j, + int k); + void Write(); + int Face(int index); + + double origin(int edge); + double EdgeAngle(int edge); + Point TriNormal(int edge); + int TriangleCount; + int VertexCount; private: - std::vector FaceData; + std::vector FaceData; }; -void iso_surface(const Array&Field, const double isovalue); +void iso_surface(const Array &Field, const double isovalue); #endif diff --git a/analysis/distance.cpp b/analysis/distance.cpp index 82042c55..cf7f1e26 100644 --- a/analysis/distance.cpp +++ b/analysis/distance.cpp @@ -16,39 +16,37 @@ */ #include "analysis/distance.h" - - /****************************************************************** * A fast distance calculation * ******************************************************************/ -template -void CalcDist( Array &Distance, const Array &ID, const Domain &Dm, - const std::array& periodic, const std::array& dx ) -{ - ASSERT( Distance.size() == ID.size() ); - std::array n = { Dm.Nx-2, Dm.Ny-2, Dm.Nz-2 }; - fillHalo fillData( Dm.Comm, Dm.rank_info, n, {1,1,1}, 50, 1, {true,false,false}, periodic ); +template +void CalcDist(Array &Distance, const Array &ID, const Domain &Dm, + const std::array &periodic, + const std::array &dx) { + ASSERT(Distance.size() == ID.size()); + std::array n = {Dm.Nx - 2, Dm.Ny - 2, Dm.Nz - 2}; + fillHalo fillData(Dm.Comm, Dm.rank_info, n, {1, 1, 1}, 50, 1, + {true, false, false}, periodic); Array id(ID.size()); Array vecDist(Distance.size()); - for (size_t i=0; i &d, const Array &ID, double dx, double dy, double dz ) -{ - d.fill( Vec( 1e50, 1e50, 1e50 ) ); - const double dx0 = 0.5*dx; - const double dy0 = 0.5*dy; - const double dz0 = 0.5*dz; +static void calcVecInitialize(Array &d, const Array &ID, double dx, + double dy, double dz) { + d.fill(Vec(1e50, 1e50, 1e50)); + const double dx0 = 0.5 * dx; + const double dy0 = 0.5 * dy; + const double dz0 = 0.5 * dz; //const double dxy0 = 0.25*sqrt( dx*dx + dy*dy ); //const double dxz0 = 0.25*sqrt( dx*dx + dz*dz ); //const double dyz0 = 0.25*sqrt( dy*dy + dz*dz ); @@ -56,19 +54,25 @@ static void calcVecInitialize( Array &d, const Array &ID, double dx, d int Nx = d.size(0); int Ny = d.size(1); int Nz = d.size(2); - for (int k=1; k &d, const Array &ID, double dx, d } } } - } - /****************************************************************** * Vector-based distance calculation * * Update interior cells * ******************************************************************/ -static double calcVecUpdateInterior( Array &d, double dx, double dy, double dz ) -{ +static double calcVecUpdateInterior(Array &d, double dx, double dy, + double dz) { double err = 0; int Nx = d.size(0); int Ny = d.size(1); int Nz = d.size(2); // Propagate (+,+,+) - for (int k=1; k=0; k--) { - for (int j=Ny-2; j>=0; j--) { - for (int i=Nx-2; i>=0; i--) { - auto vx = d(i+1,j,k); - auto vy = d(i,j+1,k); - auto vz = d(i,j,k+1); + for (int k = Nz - 2; k >= 0; k--) { + for (int j = Ny - 2; j >= 0; j--) { + for (int i = Nx - 2; i >= 0; i--) { + auto vx = d(i + 1, j, k); + auto vy = d(i, j + 1, k); + auto vz = d(i, j, k + 1); vx.x -= dx; vy.y -= dy; vz.z -= dz; - auto v = std::min( std::min(vx,vy), vz ); + auto v = std::min(std::min(vx, vy), vz); double d1 = v.norm2(); - double d2 = d(i,j,k).norm2(); - if ( d1 < d2 ) { - d(i,j,k) = v; - err = std::max( err, sqrt(d2)-sqrt(d1) ); + double d2 = d(i, j, k).norm2(); + if (d1 < d2) { + d(i, j, k) = v; + err = std::max(err, sqrt(d2) - sqrt(d1)); } } } @@ -141,66 +143,68 @@ static double calcVecUpdateInterior( Array &d, double dx, double dy, double return err; } - /****************************************************************** * Vector-based distance calculation * ******************************************************************/ -void CalcVecDist( Array &d, const Array &ID0, const Domain &Dm, - const std::array& periodic, const std::array& dx ) -{ - std::array N = { Dm.Nx, Dm.Ny, Dm.Nz }; - std::array n = { Dm.Nx-2, Dm.Ny-2, Dm.Nz-2 }; +void CalcVecDist(Array &d, const Array &ID0, const Domain &Dm, + const std::array &periodic, + const std::array &dx) { + std::array N = {Dm.Nx, Dm.Ny, Dm.Nz}; + std::array n = {Dm.Nx - 2, Dm.Ny - 2, Dm.Nz - 2}; // Create ID with ghosts - Array ID(N[0],N[1],N[2]); - fillHalo fillDataID( Dm.Comm, Dm.rank_info, n, {1,1,1}, 50, 1, {true,true,true}, periodic ); - fillDataID.copy( ID0, ID ); + Array ID(N[0], N[1], N[2]); + fillHalo fillDataID(Dm.Comm, Dm.rank_info, n, {1, 1, 1}, 50, 1, + {true, true, true}, periodic); + fillDataID.copy(ID0, ID); // Fill ghosts with nearest neighbor - for (int k=1; k fillData( Dm.Comm, Dm.rank_info, n, {1,1,1}, 50, 1, {true,false,false}, periodic ); + fillHalo fillData(Dm.Comm, Dm.rank_info, n, {1, 1, 1}, 50, 1, + {true, false, false}, periodic); // Calculate the local distances - calcVecInitialize( d, ID, dx[0], dx[1], dx[2] ); + calcVecInitialize(d, ID, dx[0], dx[1], dx[2]); double err = 1e100; - double tol = 0.5 * std::min( std::min(dx[0],dx[1]), dx[2] ); - for (int it=0; it<=50 && err>tol; it++) { - err = calcVecUpdateInterior( d, dx[0], dx[1], dx[2] ); + double tol = 0.5 * std::min(std::min(dx[0], dx[1]), dx[2]); + for (int it = 0; it <= 50 && err > tol; it++) { + err = calcVecUpdateInterior(d, dx[0], dx[1], dx[2]); } // Calculate the global distances int N_it = Dm.nprocx() + Dm.nprocy() + Dm.nprocz() + 100; - for ( int it=0; it( Array&, const Array&, const Domain&, const std::array&, const std::array& ); -template void CalcDist( Array&, const Array&, const Domain&, const std::array&, const std::array& ); - - +template void CalcDist(Array &, const Array &, + const Domain &, const std::array &, + const std::array &); +template void CalcDist(Array &, const Array &, + const Domain &, const std::array &, + const std::array &); diff --git a/analysis/distance.h b/analysis/distance.h index f6eb5c11..6043ea56 100644 --- a/analysis/distance.h +++ b/analysis/distance.h @@ -20,18 +20,19 @@ #include "common/Domain.h" #include "common/Array.hpp" - struct Vec { double x; double y; double z; - inline Vec(): x(0), y(0), z(0) {} - inline Vec( double x_, double y_, double z_ ): x(x_), y(y_), z(z_) {} - inline double norm() const { return sqrt(x*x+y*y+z*z); } - inline double norm2() const { return x*x+y*y+z*z; } + inline Vec() : x(0), y(0), z(0) {} + inline Vec(double x_, double y_, double z_) : x(x_), y(y_), z(z_) {} + inline double norm() const { return sqrt(x * x + y * y + z * z); } + inline double norm2() const { return x * x + y * y + z * z; } }; -inline bool operator<(const Vec& l, const Vec& r){ return l.x*l.x+l.y*l.y+l.z*l.z < r.x*r.x+r.y*r.y+r.z*r.z; } - +inline bool operator<(const Vec &l, const Vec &r) { + return l.x * l.x + l.y * l.y + l.z * l.z < + r.x * r.x + r.y * r.y + r.z * r.z; +} /*! * @brief Calculate the distance using a simple method @@ -42,9 +43,10 @@ inline bool operator<(const Vec& l, const Vec& r){ return l.x*l.x+l.y*l.y+l.z*l. * @param[in] periodic Directions that are periodic * @param[in] dx Cell size */ -template -void CalcDist( Array &Distance, const Array &ID, const Domain &Dm, - const std::array& periodic = {true,true,true}, const std::array& dx = {1,1,1} ); +template +void CalcDist(Array &Distance, const Array &ID, const Domain &Dm, + const std::array &periodic = {true, true, true}, + const std::array &dx = {1, 1, 1}); /*! * @brief Calculate the distance using a simple method @@ -55,7 +57,8 @@ void CalcDist( Array &Distance, const Array &ID, const Domain &Dm, * @param[in] periodic Directions that are periodic * @param[in] dx Cell size */ -void CalcVecDist( Array &Distance, const Array &ID, const Domain &Dm, - const std::array& periodic = {true,true,true}, const std::array& dx = {1,1,1} ); +void CalcVecDist(Array &Distance, const Array &ID, const Domain &Dm, + const std::array &periodic = {true, true, true}, + const std::array &dx = {1, 1, 1}); #endif diff --git a/analysis/filters.cpp b/analysis/filters.cpp index d234dde4..22e6a3b4 100644 --- a/analysis/filters.cpp +++ b/analysis/filters.cpp @@ -18,175 +18,181 @@ #include "math.h" #include "ProfilerApp.h" -void Mean3D( const Array &Input, Array &Output ) -{ - PROFILE_START("Mean3D"); - // Perform a 3D Mean filter on Input array - int i,j,k; +void Mean3D(const Array &Input, Array &Output) { + PROFILE_START("Mean3D"); + // Perform a 3D Mean filter on Input array + int i, j, k; - int Nx = int(Input.size(0)); - int Ny = int(Input.size(1)); - int Nz = int(Input.size(2)); + int Nx = int(Input.size(0)); + int Ny = int(Input.size(1)); + int Nz = int(Input.size(2)); - for (k=1; k &Input, Array &Output ) -{ - PROFILE_START("Med3D"); - // Perform a 3D Median filter on Input array with specified width - int i,j,k,ii,jj,kk; - int imin,jmin,kmin,imax,jmax,kmax; +void Med3D(const Array &Input, Array &Output) { + PROFILE_START("Med3D"); + // Perform a 3D Median filter on Input array with specified width + int i, j, k, ii, jj, kk; + int imin, jmin, kmin, imax, jmax, kmax; - float *List; - List=new float[27]; + float *List; + List = new float[27]; - int Nx = int(Input.size(0)); - int Ny = int(Input.size(1)); - int Nz = int(Input.size(2)); + int Nx = int(Input.size(0)); + int Ny = int(Input.size(1)); + int Nz = int(Input.size(2)); - for (k=1; k &Input, Array &Mean, + const Array &Distance, Array &Output, const int d, + const float h) { + PROFILE_START("NLM3D"); + // Implemenation of 3D non-local means filter + // d determines the width of the search volume + // h is a free parameter for non-local means (i.e. 1/sigma^2) + // Distance is the signed distance function + // If Distance(i,j,k) > THRESHOLD_DIST then don't compute NLM -int NLM3D( const Array &Input, Array &Mean, - const Array &Distance, Array &Output, const int d, const float h) -{ - PROFILE_START("NLM3D"); - // Implemenation of 3D non-local means filter - // d determines the width of the search volume - // h is a free parameter for non-local means (i.e. 1/sigma^2) - // Distance is the signed distance function - // If Distance(i,j,k) > THRESHOLD_DIST then don't compute NLM + float THRESHOLD_DIST = float(d); + float weight, sum; + int i, j, k, ii, jj, kk; + int imin, jmin, kmin, imax, jmax, kmax; + int returnCount = 0; - float THRESHOLD_DIST = float(d); - float weight, sum; - int i,j,k,ii,jj,kk; - int imin,jmin,kmin,imax,jmax,kmax; - int returnCount=0; + int Nx = int(Input.size(0)); + int Ny = int(Input.size(1)); + int Nz = int(Input.size(2)); - int Nx = int(Input.size(0)); - int Ny = int(Input.size(1)); - int Nz = int(Input.size(2)); + // Compute the local means + for (k = 1; k < Nz - 1; k++) { + for (j = 1; j < Ny - 1; j++) { + for (i = 1; i < Nx - 1; i++) { - // Compute the local means - for (k=1; k &Input, Array &Output ); +void Mean3D(const Array &Input, Array &Output); /*! * @brief Filter image @@ -34,7 +33,7 @@ void Mean3D( const Array &Input, Array &Output ); * @param[in] Input Input image * @param[out] Output Output image */ -void Med3D( const Array &Input, Array &Output ); +void Med3D(const Array &Input, Array &Output); /*! * @brief Filter image @@ -46,8 +45,8 @@ void Med3D( const Array &Input, Array &Output ); * @param[in] d * @param[in] h */ -int NLM3D( const Array &Input, Array &Mean, - const Array &Distance, Array &Output, const int d, const float h); - +int NLM3D(const Array &Input, Array &Mean, + const Array &Distance, Array &Output, const int d, + const float h); #endif diff --git a/analysis/histogram.h b/analysis/histogram.h index cc12b226..b2cd53d9 100644 --- a/analysis/histogram.h +++ b/analysis/histogram.h @@ -21,43 +21,45 @@ #define HISTOGRAM_RESOLUTION 1000 -struct Histogram{ - Histogram(double v1, double v2){ - data = new double[HISTOGRAM_RESOLUTION]; - minimum = v1; - maximum = v2; - delta = (maximum-minimum)/HISTOGRAM_RESOLUTION; - } - ~Histogram{ - delete *data; - } - double *data; - double minimum,maximum,delta; - - // Adds value into the histogram - void IncludeValue(double value, double weight){ - idx = floor((value-min)/delta); - if (idx > HISTOGRAM_RESOLUTION) ; - else if (idx < 0) ; - else data[idx] += weight; - } - - // Returns the maximum value in the histogram - void GetMax(){ - max = minimum; - for (idx=1; idx max){ - max = minimum+idx*delta; - } - } - } - - // Resets the histogram to zero - void Reset(){ - for (idx=0; idx HISTOGRAM_RESOLUTION) + ; + else if (idx < 0) + ; + else + data[idx] += weight; + } + + // Returns the maximum value in the histogram + void GetMax() { + max = minimum; + for (idx = 1; idx < HISTOGRAM_RESOLUTION; idx++) { + if (data[idx] > max) { + max = minimum + idx * delta; + } + } + } + + // Resets the histogram to zero + void Reset() { + for (idx = 0; idx < HISTOGRAM_RESOLUTION; idx++) + data[idx] = 0.0; + } + private: - int idx; - double max,min; + int idx; + double max, min; }; diff --git a/analysis/imfilter.h b/analysis/imfilter.h index 649614bd..3390aba8 100644 --- a/analysis/imfilter.h +++ b/analysis/imfilter.h @@ -22,13 +22,10 @@ #include "common/Array.h" #include - namespace imfilter { - //! enum to store the BC type -enum class BC { fixed=0, symmetric=1, replicate=2, circular=3 }; - +enum class BC { fixed = 0, symmetric = 1, replicate = 2, circular = 3 }; /*! * @brief N-D filtering of multidimensional images @@ -47,9 +44,10 @@ enum class BC { fixed=0, symmetric=1, replicate=2, circular=3 }; * computed by implicitly assuming the input array is periodic. * @param[in] X The value to use for boundary conditions (only used if boundary==fixed) */ -template -Array imfilter( const Array& A, const Array& H, const std::vector& boundary, const TYPE X=0 ); - +template +Array imfilter(const Array &A, const Array &H, + const std::vector &boundary, + const TYPE X = 0); /*! * @brief N-D filtering of multidimensional images @@ -70,11 +68,11 @@ Array imfilter( const Array& A, const Array& H, const std::vec * computed by implicitly assuming the input array is periodic. * @param[in] X The value to use for boundary conditions (only used if boundary==fixed) */ -template -Array imfilter( const Array& A, const std::vector& Nh, - std::function&)> H, - const std::vector& boundary, const TYPE X=0 ); - +template +Array imfilter(const Array &A, const std::vector &Nh, + std::function &)> H, + const std::vector &boundary, + const TYPE X = 0); /*! * @brief N-D filtering of multidimensional images @@ -94,10 +92,10 @@ Array imfilter( const Array& A, const std::vector& Nh, * computed by implicitly assuming the input array is periodic. * @param[in] X The value to use for boundary conditions (only used if boundary==fixed) */ -template -Array imfilter_separable( const Array& A, const std::vector>& H, - const std::vector& boundary, const TYPE X=0 ); - +template +Array +imfilter_separable(const Array &A, const std::vector> &H, + const std::vector &boundary, const TYPE X = 0); /*! * @brief N-D filtering of multidimensional images @@ -117,11 +115,11 @@ Array imfilter_separable( const Array& A, const std::vector -Array imfilter_separable( const Array& A, const std::vector& Nh, - std::vector&)>> H, - const std::vector& boundary, const TYPE X=0 ); - +template +Array +imfilter_separable(const Array &A, const std::vector &Nh, + std::vector &)>> H, + const std::vector &boundary, const TYPE X = 0); /*! * @brief N-D filtering of multidimensional images @@ -142,11 +140,11 @@ Array imfilter_separable( const Array& A, const std::vector& Nh * computed by implicitly assuming the input array is periodic. * @param[in] X The value to use for boundary conditions (only used if boundary==fixed) */ -template -Array imfilter_separable( const Array& A, const std::vector& Nh, - std::vector> H, - const std::vector& boundary, const TYPE X=0 ); - +template +Array +imfilter_separable(const Array &A, const std::vector &Nh, + std::vector> H, + const std::vector &boundary, const TYPE X = 0); /** * @brief Create a filter to use with imfilter @@ -163,14 +161,12 @@ Array imfilter_separable( const Array& A, const std::vector& Nh * A default value of 0.5 is used if not provided. * \param[in] args An optional argument that some of the filters use */ -template -Array create_filter( const std::vector& N, const std::string &type, const void *args = NULL ); - - -} +template +Array create_filter(const std::vector &N, const std::string &type, + const void *args = NULL); +} // namespace imfilter #include "analysis/imfilter.hpp" #endif - diff --git a/analysis/imfilter.hpp b/analysis/imfilter.hpp index 75402df9..4272a47e 100644 --- a/analysis/imfilter.hpp +++ b/analysis/imfilter.hpp @@ -35,185 +35,176 @@ #include #include - #define IMFILTER_INSIST INSIST #define IMFILTER_ASSERT ASSERT -#define IMFILTER_ERROR ERROR - +#define IMFILTER_ERROR ERROR // Function to convert an index -static inline int imfilter_index( int index, const int N, const imfilter::BC bc ) -{ - if ( index < 0 || index >= N ) { - if ( bc == imfilter::BC::symmetric ) { - index = ( 2 * N - index ) % N; - } else if ( bc == imfilter::BC::replicate ) { +static inline int imfilter_index(int index, const int N, + const imfilter::BC bc) { + if (index < 0 || index >= N) { + if (bc == imfilter::BC::symmetric) { + index = (2 * N - index) % N; + } else if (bc == imfilter::BC::replicate) { index = index < 0 ? 0 : N - 1; - } else if ( bc == imfilter::BC::circular ) { - index = ( index + N ) % N; - } else if ( bc == imfilter::BC::fixed ) { + } else if (bc == imfilter::BC::circular) { + index = (index + N) % N; + } else if (bc == imfilter::BC::fixed) { index = -1; } } return index; } - // Function to copy a 1D array and pad with the appropriate BC -template -static inline void copy_array( const int N, const int Ns, const int Nh, - const TYPE *A, const imfilter::BC BC, const TYPE X, TYPE *B ) -{ +template +static inline void copy_array(const int N, const int Ns, const int Nh, + const TYPE *A, const imfilter::BC BC, + const TYPE X, TYPE *B) { // Fill the center with a memcpy - for (int i=0; i -static void filter_direction( int Ns, int N, int Ne, int Nh, const TYPE *H, - imfilter::BC boundary, TYPE X, TYPE *A ) -{ - if ( Nh < 0 ) +template +static void filter_direction(int Ns, int N, int Ne, int Nh, const TYPE *H, + imfilter::BC boundary, TYPE X, TYPE *A) { + if (Nh < 0) IMFILTER_ERROR("Invalid filter size"); - if ( Nh == 0 ) { - for (int i=0; i -static void filter_direction( int Ns, int N, int Ne, int Nh, - std::function&)> H, imfilter::BC boundary, TYPE X, TYPE *A ) -{ - if ( Nh < 0 ) +template +static void filter_direction(int Ns, int N, int Ne, int Nh, + std::function &)> H, + imfilter::BC boundary, TYPE X, TYPE *A) { + if (Nh < 0) IMFILTER_ERROR("Invalid filter size"); - TYPE *tmp = new TYPE[N+2*Nh]; - Array tmp2(2*Nh+1); - for (int j=0; j tmp2(2 * Nh + 1); + for (int j = 0; j < Ne; j++) { + for (int i = 0; i < Ns; i++) { + copy_array(N, Ns, Nh, &A[i + j * Ns * N], boundary, X, tmp); + for (int k = 0; k < N; k++) { + for (int m = 0; m <= 2 * Nh; m++) + tmp2(m) = tmp[k + m]; + A[i + k * Ns + j * Ns * N] = H(tmp2); } } } delete[] tmp; } -template -static void filter_direction( int Ns, int N, int Ne, int Nh, - std::function H, imfilter::BC boundary, TYPE X, TYPE *A ) -{ - if ( Nh < 0 ) +template +static void filter_direction(int Ns, int N, int Ne, int Nh, + std::function H, + imfilter::BC boundary, TYPE X, TYPE *A) { + if (Nh < 0) IMFILTER_ERROR("Invalid filter size"); - TYPE *tmp = new TYPE[N+2*Nh]; - int Nh2 = 2*Nh+1; - for (int j=0; j -Array imfilter::create_filter( const std::vector& N0, const std::string &type, const void *args ) -{ +template +Array imfilter::create_filter(const std::vector &N0, + const std::string &type, const void *args) { std::vector N2(N0.size()); - for (size_t i=0; i h(N2); h.fill(0); - if ( type == "average" ) { + if (type == "average") { // average - h.fill( 1.0 / static_cast( h.length() ) ); - } else if ( type == "gaussian" ) { + h.fill(1.0 / static_cast(h.length())); + } else if (type == "gaussian") { // gaussian - if ( N0.size() > 3 ) - IMFILTER_ERROR( "Not implimented for dimensions > 3" ); - TYPE std[3] = { 0.5, 0.5, 0.5 }; - if ( args != NULL ) { - const TYPE *args2 = reinterpret_cast( args ); - for ( size_t d = 0; d < N0.size(); d++ ) - std[d] = args2[d]; + if (N0.size() > 3) + IMFILTER_ERROR("Not implimented for dimensions > 3"); + TYPE std[3] = {0.5, 0.5, 0.5}; + if (args != NULL) { + const TYPE *args2 = reinterpret_cast(args); + for (size_t d = 0; d < N0.size(); d++) + std[d] = args2[d]; } auto N = N0; - N.resize(3,0); - for ( int k = -N[2]; k <= N[2]; k++ ) { - for ( int j = -N[1]; j <= N[1]; j++ ) { - for ( int i = -N[0]; i <= N[0]; i++ ) { - h(i+N[0],j+N[1],k+N[2]) = - exp( -i * i / ( 2 * std[0] * std[0] ) ) * - exp( -j * j / ( 2 * std[1] * std[1] ) ) * - exp( -k * k / ( 2 * std[2] * std[2] ) ); + N.resize(3, 0); + for (int k = -N[2]; k <= N[2]; k++) { + for (int j = -N[1]; j <= N[1]; j++) { + for (int i = -N[0]; i <= N[0]; i++) { + h(i + N[0], j + N[1], k + N[2]) = + exp(-i * i / (2 * std[0] * std[0])) * + exp(-j * j / (2 * std[1] * std[1])) * + exp(-k * k / (2 * std[2] * std[2])); } } } - h.scale( 1.0/h.sum() ); + h.scale(1.0 / h.sum()); } else { - IMFILTER_ERROR( "Unknown filter" ); + IMFILTER_ERROR("Unknown filter"); } return h; } - // Perform 2-D filtering -template -void imfilter_2D( int Nx, int Ny, const TYPE *A, int Nhx, int Nhy, const TYPE *H, - imfilter::BC BCx, imfilter::BC BCy, const TYPE X, TYPE *B ) -{ - IMFILTER_ASSERT( A != B ); - PROFILE_START( "imfilter_2D" ); - memset( B, 0, Nx * Ny * sizeof( TYPE ) ); - for ( int j1 = 0; j1 < Ny; j1++ ) { - for ( int i1 = 0; i1 < Nx; i1++ ) { +template +void imfilter_2D(int Nx, int Ny, const TYPE *A, int Nhx, int Nhy, const TYPE *H, + imfilter::BC BCx, imfilter::BC BCy, const TYPE X, TYPE *B) { + IMFILTER_ASSERT(A != B); + PROFILE_START("imfilter_2D"); + memset(B, 0, Nx * Ny * sizeof(TYPE)); + for (int j1 = 0; j1 < Ny; j1++) { + for (int i1 = 0; i1 < Nx; i1++) { TYPE tmp = 0; - if ( i1 >= Nhx && i1 < Nx - Nhx && j1 >= Nhy && j1 < Ny - Nhy ) { + if (i1 >= Nhx && i1 < Nx - Nhx && j1 >= Nhy && j1 < Ny - Nhy) { int ijkh = 0; - for ( int j2 = j1 - Nhy; j2 <= j1 + Nhy; j2++ ) { - for ( int i2 = i1 - Nhx; i2 <= i1 + Nhx; i2++, ijkh++ ) + for (int j2 = j1 - Nhy; j2 <= j1 + Nhy; j2++) { + for (int i2 = i1 - Nhx; i2 <= i1 + Nhx; i2++, ijkh++) tmp += H[ijkh] * A[i2 + j2 * Nx]; } } else { int ijkh = 0; - for ( int jh = -Nhy; jh <= Nhy; jh++ ) { - int j2 = imfilter_index( j1+jh, Ny, BCy ); - for ( int ih = -Nhx; ih <= Nhx; ih++ ) { - int i2 = imfilter_index( i1+ih, Nx, BCx ); + for (int jh = -Nhy; jh <= Nhy; jh++) { + int j2 = imfilter_index(j1 + jh, Ny, BCy); + for (int ih = -Nhx; ih <= Nhx; ih++) { + int i2 = imfilter_index(i1 + ih, Nx, BCx); bool fixed = i2 == -1 || j2 == -1; - TYPE A2 = fixed ? X : A[i2 + j2 * Nx]; + TYPE A2 = fixed ? X : A[i2 + j2 * Nx]; tmp += H[ijkh] * A2; ijkh++; } @@ -222,32 +213,31 @@ void imfilter_2D( int Nx, int Ny, const TYPE *A, int Nhx, int Nhy, const TYPE *H B[i1 + j1 * Nx] = tmp; } } - PROFILE_STOP( "imfilter_2D" ); + PROFILE_STOP("imfilter_2D"); } - // Perform 3-D filtering -template -void imfilter_3D( int Nx, int Ny, int Nz, const TYPE *A, int Nhx, int Nhy, int Nhz, - const TYPE *H, imfilter::BC BCx, imfilter::BC BCy, imfilter::BC BCz, - const TYPE X, TYPE *B ) -{ - IMFILTER_ASSERT( A != B ); - PROFILE_START( "imfilter_3D" ); - memset( B, 0, Nx * Ny * Nz * sizeof( TYPE ) ); - for ( int k1 = 0; k1 < Nz; k1++ ) { - for ( int j1 = 0; j1 < Ny; j1++ ) { - for ( int i1 = 0; i1 < Nx; i1++ ) { +template +void imfilter_3D(int Nx, int Ny, int Nz, const TYPE *A, int Nhx, int Nhy, + int Nhz, const TYPE *H, imfilter::BC BCx, imfilter::BC BCy, + imfilter::BC BCz, const TYPE X, TYPE *B) { + IMFILTER_ASSERT(A != B); + PROFILE_START("imfilter_3D"); + memset(B, 0, Nx * Ny * Nz * sizeof(TYPE)); + for (int k1 = 0; k1 < Nz; k1++) { + for (int j1 = 0; j1 < Ny; j1++) { + for (int i1 = 0; i1 < Nx; i1++) { TYPE tmp = 0; - int ijkh = 0; - for ( int kh = -Nhz; kh <= Nhz; kh++ ) { - int k2 = imfilter_index( k1+kh, Nz, BCz ); - for ( int jh = -Nhy; jh <= Nhy; jh++ ) { - int j2 = imfilter_index( j1+jh, Ny, BCy ); - for ( int ih = -Nhx; ih <= Nhx; ih++ ) { - int i2 = imfilter_index( i1+ih, Nx, BCx ); + int ijkh = 0; + for (int kh = -Nhz; kh <= Nhz; kh++) { + int k2 = imfilter_index(k1 + kh, Nz, BCz); + for (int jh = -Nhy; jh <= Nhy; jh++) { + int j2 = imfilter_index(j1 + jh, Ny, BCy); + for (int ih = -Nhx; ih <= Nhx; ih++) { + int i2 = imfilter_index(i1 + ih, Nx, BCx); bool fixed = i2 == -1 || j2 == -1 || k2 == -1; - TYPE A2 = fixed ? X : A[i2 + j2 * Nx + k2 * Nx * Ny]; + TYPE A2 = + fixed ? X : A[i2 + j2 * Nx + k2 * Nx * Ny]; tmp += H[ijkh] * A2; ijkh++; } @@ -257,154 +247,155 @@ void imfilter_3D( int Nx, int Ny, int Nz, const TYPE *A, int Nhx, int Nhy, int N } } } - PROFILE_STOP( "imfilter_3D" ); + PROFILE_STOP("imfilter_3D"); } - /******************************************************** * Perform N-D filtering * ********************************************************/ -template -Array imfilter::imfilter( const Array& A, - const Array& H, const std::vector& BC, const TYPE X ) -{ - IMFILTER_ASSERT( A.ndim() == H.ndim() ); - IMFILTER_ASSERT( A.ndim() == BC.size() ); +template +Array imfilter::imfilter(const Array &A, const Array &H, + const std::vector &BC, + const TYPE X) { + IMFILTER_ASSERT(A.ndim() == H.ndim()); + IMFILTER_ASSERT(A.ndim() == BC.size()); std::vector Nh = H.size(); - for (int d=0; d -Array imfilter::imfilter( const Array& A, const std::vector& Nh0, - std::function&)> H, - const std::vector& BC0, const TYPE X ) -{ - PROFILE_START( "imfilter (lambda)" ); - IMFILTER_ASSERT( A.ndim() == Nh0.size() ); - IMFILTER_ASSERT( A.ndim() == BC0.size() ); - std::vector Nh2( A.size() ); - for (int d=0; d +Array +imfilter::imfilter(const Array &A, const std::vector &Nh0, + std::function &)> H, + const std::vector &BC0, const TYPE X) { + PROFILE_START("imfilter (lambda)"); + IMFILTER_ASSERT(A.ndim() == Nh0.size()); + IMFILTER_ASSERT(A.ndim() == BC0.size()); + std::vector Nh2(A.size()); + for (int d = 0; d < A.ndim(); d++) + Nh2[d] = 2 * Nh0[d] + 1; auto B = A; Array data(Nh2); - IMFILTER_INSIST(A.ndim()<=3,"Not programmed for more than 3 dimensions yet"); + IMFILTER_INSIST(A.ndim() <= 3, + "Not programmed for more than 3 dimensions yet"); auto N = A.size(); auto Nh = Nh0; auto BC = BC0; - N.resize(3,1); - Nh.resize(3,0); - BC.resize(3,imfilter::BC::fixed); - for ( int k1 = 0; k1 < N[2]; k1++ ) { - for ( int j1 = 0; j1 < N[1]; j1++ ) { - for ( int i1 = 0; i1 < N[0]; i1++ ) { - for ( int kh = -Nh[2]; kh <= Nh[2]; kh++ ) { - int k2 = imfilter_index( k1+kh, N[2], BC[2] ); - for ( int jh = -Nh[1]; jh <= Nh[1]; jh++ ) { - int j2 = imfilter_index( j1+jh, N[1], BC[1] ); - for ( int ih = -Nh[0]; ih <= Nh[0]; ih++ ) { - int i2 = imfilter_index( i1+ih, N[0], BC[0] ); + N.resize(3, 1); + Nh.resize(3, 0); + BC.resize(3, imfilter::BC::fixed); + for (int k1 = 0; k1 < N[2]; k1++) { + for (int j1 = 0; j1 < N[1]; j1++) { + for (int i1 = 0; i1 < N[0]; i1++) { + for (int kh = -Nh[2]; kh <= Nh[2]; kh++) { + int k2 = imfilter_index(k1 + kh, N[2], BC[2]); + for (int jh = -Nh[1]; jh <= Nh[1]; jh++) { + int j2 = imfilter_index(j1 + jh, N[1], BC[1]); + for (int ih = -Nh[0]; ih <= Nh[0]; ih++) { + int i2 = imfilter_index(i1 + ih, N[0], BC[0]); bool fixed = i2 == -1 || j2 == -1 || k2 == -1; - data(ih+Nh[0],jh+Nh[1],kh+Nh[2]) = fixed ? X : A(i2,j2,k2); + data(ih + Nh[0], jh + Nh[1], kh + Nh[2]) = + fixed ? X : A(i2, j2, k2); } } } - B(i1,j1,k1) = H( data ); + B(i1, j1, k1) = H(data); } } } - PROFILE_STOP( "imfilter (lambda)" ); + PROFILE_STOP("imfilter (lambda)"); return B; } - /******************************************************** * imfilter with separable filter functions * ********************************************************/ -template -Array imfilter::imfilter_separable( const Array& A, - const std::vector>& H, - const std::vector& boundary, const TYPE X ) -{ - PROFILE_START( "imfilter_separable" ); - IMFILTER_ASSERT( A.ndim() == (int) H.size() ); - IMFILTER_ASSERT( A.ndim() == (int) boundary.size() ); - std::vector Nh( H.size() ); - for (int d=0; d +Array imfilter::imfilter_separable( + const Array &A, const std::vector> &H, + const std::vector &boundary, const TYPE X) { + PROFILE_START("imfilter_separable"); + IMFILTER_ASSERT(A.ndim() == (int)H.size()); + IMFILTER_ASSERT(A.ndim() == (int)boundary.size()); + std::vector Nh(H.size()); + for (int d = 0; d < A.ndim(); d++) { + IMFILTER_ASSERT(H[d].ndim() == 1); + Nh[d] = (H[d].length() - 1) / 2; + IMFILTER_INSIST(2 * Nh[d] + 1 == H[d].length(), + "Filter must be of size 2*N+1"); } auto B = A; - for ( int d = 0; d < A.ndim(); d++ ) { + for (int d = 0; d < A.ndim(); d++) { int N = A.size(d); int Ns = 1; int Ne = 1; - for ( int d2 = 0; d2 < d; d2++ ) + for (int d2 = 0; d2 < d; d2++) Ns *= A.size(d2); - for ( int d2 = d+1; d2 < A.ndim(); d2++ ) + for (int d2 = d + 1; d2 < A.ndim(); d2++) Ne *= A.size(d2); - filter_direction( Ns, N, Ne, Nh[d], H[d].data(), boundary[d], X, B.data() ); + filter_direction(Ns, N, Ne, Nh[d], H[d].data(), boundary[d], X, + B.data()); } - PROFILE_STOP( "imfilter_separable" ); + PROFILE_STOP("imfilter_separable"); return B; } -template -Array imfilter::imfilter_separable( const Array& A, const std::vector& Nh, - std::vector&)>> H, - const std::vector& boundary, const TYPE X ) -{ - PROFILE_START( "imfilter_separable (lambda)" ); - IMFILTER_ASSERT( A.ndim() == (int) boundary.size() ); +template +Array imfilter::imfilter_separable( + const Array &A, const std::vector &Nh, + std::vector &)>> H, + const std::vector &boundary, const TYPE X) { + PROFILE_START("imfilter_separable (lambda)"); + IMFILTER_ASSERT(A.ndim() == (int)boundary.size()); auto B = A; - for ( int d = 0; d < A.ndim(); d++ ) { + for (int d = 0; d < A.ndim(); d++) { int N = A.size(d); int Ns = 1; int Ne = 1; - for ( int d2 = 0; d2 < d; d2++ ) + for (int d2 = 0; d2 < d; d2++) Ns *= A.size(d2); - for ( int d2 = d+1; d2 < A.ndim(); d2++ ) + for (int d2 = d + 1; d2 < A.ndim(); d2++) Ne *= A.size(d2); - filter_direction( Ns, N, Ne, Nh[d], H[d], boundary[d], X, B.data() ); + filter_direction(Ns, N, Ne, Nh[d], H[d], boundary[d], X, B.data()); } - PROFILE_STOP( "imfilter_separable (lambda)" ); + PROFILE_STOP("imfilter_separable (lambda)"); return B; } -template -Array imfilter::imfilter_separable( const Array& A, const std::vector& Nh, - std::vector> H, - const std::vector& boundary, const TYPE X ) -{ - PROFILE_START( "imfilter_separable (function)" ); - IMFILTER_ASSERT( A.ndim() == (int) boundary.size() ); +template +Array imfilter::imfilter_separable( + const Array &A, const std::vector &Nh, + std::vector> H, + const std::vector &boundary, const TYPE X) { + PROFILE_START("imfilter_separable (function)"); + IMFILTER_ASSERT(A.ndim() == (int)boundary.size()); auto B = A; - for ( int d = 0; d < A.ndim(); d++ ) { + for (int d = 0; d < A.ndim(); d++) { int N = A.size(d); int Ns = 1; int Ne = 1; - for ( int d2 = 0; d2 < d; d2++ ) + for (int d2 = 0; d2 < d; d2++) Ns *= A.size(d2); - for ( int d2 = d+1; d2 < A.ndim(); d2++ ) + for (int d2 = d + 1; d2 < A.ndim(); d2++) Ne *= A.size(d2); - filter_direction( Ns, N, Ne, Nh[d], H[d], boundary[d], X, B.data() ); + filter_direction(Ns, N, Ne, Nh[d], H[d], boundary[d], X, B.data()); } - PROFILE_STOP( "imfilter_separable (function)" ); + PROFILE_STOP("imfilter_separable (function)"); return B; } - - diff --git a/analysis/morphology.cpp b/analysis/morphology.cpp index 650b3fdf..b167d9cf 100644 --- a/analysis/morphology.cpp +++ b/analysis/morphology.cpp @@ -1,897 +1,951 @@ #include // Implementation of morphological opening routine -inline void PackID(const int *list, int count, signed char *sendbuf, signed char *ID){ - // Fill in the phase ID values from neighboring processors - // This packs up the values that need to be sent from one processor to another - int idx,n; +inline void PackID(const int *list, int count, signed char *sendbuf, + signed char *ID) { + // Fill in the phase ID values from neighboring processors + // This packs up the values that need to be sent from one processor to another + int idx, n; - for (idx=0; idx Dm, DoubleArray &Distance){ - /* Loop over all faces and determine overlaps */ - size_t Nx = Dm->Nx; - size_t Ny = Dm->Ny; - size_t Nz = Dm->Nz; - size_t N = Nx*Ny*Nz; +void Morphology::Initialize(std::shared_ptr Dm, DoubleArray &Distance) { + /* Loop over all faces and determine overlaps */ + size_t Nx = Dm->Nx; + size_t Ny = Dm->Ny; + size_t Nz = Dm->Nz; + size_t N = Nx * Ny * Nz; - int *tmpShift_x, *tmpShift_y, *tmpShift_z; - double *tmpDistance; - tmpShift_x = new int [N]; - tmpShift_y= new int [N]; - tmpShift_z = new int [N]; - tmpDistance = new double [N]; - - double distance, boundary_distance; - - /* Loop over the local sub-domain and create overlap lists for each neighboring sub-domain */ - int sendLoc = 0; // counter for the local sub-domain send values - int recvLoc = 0; // counter for the local recv - //................................................... - /* x face */ - sendCount = recvCount = 0; - for (size_t k=1; k boundary_distance){ - tmpShift_x[sendCount] = Nx + i-2; - tmpShift_y[sendCount] = j; - tmpShift_z[sendCount] = k; - tmpDistance[sendCount++] = distance; - int n = k*Nx*Ny + j*Nx + i; - sendID.push_back(n); - } - } - } - } - Dm->Comm.Irecv(&recvCount,1,Dm->rank_X(),recvtag+0); - Dm->Comm.send(&sendCount,1,Dm->rank_x(),sendtag+0); - Dm->Comm.barrier(); - sendOffset_x = sendLoc; - recvOffset_X = recvLoc; - sendLoc += sendCount; - recvLoc += recvCount; - sendCount_x = sendCount; - recvCount_X = recvCount; - /* grow the arrays */ - xShift.resize(recvLoc); - yShift.resize(recvLoc); - zShift.resize(recvLoc); - morphRadius.resize(recvLoc); - //.............................. - /* send the morphological radius */ - Dm->Comm.Irecv(&morphRadius[recvOffset_X],recvCount,Dm->rank_X(),recvtag+0); - Dm->Comm.send(&tmpDistance[0],sendCount,Dm->rank_x(),sendtag+0); - /* send the shift values */ - Dm->Comm.Irecv(&xShift[recvOffset_X],recvCount,Dm->rank_X(),recvtag+1); - Dm->Comm.send(&tmpShift_x[0],sendCount,Dm->rank_x(),sendtag+1); - Dm->Comm.Irecv(&yShift[recvOffset_X],recvCount,Dm->rank_X(),recvtag+2); - Dm->Comm.send(&tmpShift_y[0],sendCount,Dm->rank_x(),sendtag+2); - Dm->Comm.Irecv(&zShift[recvOffset_X],recvCount,Dm->rank_X(),recvtag+3); - Dm->Comm.send(&tmpShift_z[0],sendCount,Dm->rank_x(),sendtag+3); - Dm->Comm.barrier(); - //................................................... - //................................................... - /* X face */ - sendCount = recvCount = 0; - for (size_t k=1; k boundary_distance){ - tmpShift_x[sendCount] = (i)-(Nx-2); - tmpShift_y[sendCount] = j; - tmpShift_z[sendCount] = k; - tmpDistance[sendCount++] = distance; - int n = k*Nx*Ny + j*Nx + i; - sendID.push_back(n); - } - } - } - } - Dm->Comm.Irecv(&recvCount,1,Dm->rank_x(),recvtag+0); - Dm->Comm.send(&sendCount,1,Dm->rank_X(),sendtag+0); - Dm->Comm.barrier(); - sendOffset_X = sendLoc; - recvOffset_x = recvLoc; - sendLoc += sendCount; - recvLoc += recvCount; - sendCount_X = sendCount; - recvCount_x = recvCount; - /* grow the arrays */ - xShift.resize(recvLoc); - yShift.resize(recvLoc); - zShift.resize(recvLoc); - morphRadius.resize(recvLoc); - //.............................. - /* send the morphological radius */ - Dm->Comm.Irecv(&morphRadius[recvOffset_x],recvCount,Dm->rank_x(),recvtag+0); - Dm->Comm.send(&tmpDistance[0],sendCount,Dm->rank_X(),sendtag+0); - /* send the shift values */ - Dm->Comm.Irecv(&xShift[recvOffset_x],recvCount,Dm->rank_x(),recvtag+1); - Dm->Comm.send(&tmpShift_x[0],sendCount,Dm->rank_X(),sendtag+1); - Dm->Comm.Irecv(&yShift[recvOffset_x],recvCount,Dm->rank_x(),recvtag+2); - Dm->Comm.send(&tmpShift_y[0],sendCount,Dm->rank_X(),sendtag+2); - Dm->Comm.Irecv(&zShift[recvOffset_x],recvCount,Dm->rank_x(),recvtag+3); - Dm->Comm.send(&tmpShift_z[0],sendCount,Dm->rank_X(),sendtag+3); - Dm->Comm.barrier(); - //................................................... - - //................................................... - /* y face */ - sendCount = recvCount = 0; - for (size_t k=1; k boundary_distance){ - tmpShift_x[sendCount] = i; - tmpShift_y[sendCount] = Ny + j-2; - tmpShift_z[sendCount] = k; - tmpDistance[sendCount++] = distance; - int n = k*Nx*Ny + j*Nx + i; - sendID.push_back(n); - } - } - } - } - Dm->Comm.Irecv(&recvCount,1,Dm->rank_Y(),recvtag+0); - Dm->Comm.send(&sendCount,1,Dm->rank_y(),sendtag+0); - Dm->Comm.barrier(); - sendOffset_y = sendLoc; - recvOffset_Y = recvLoc; - sendLoc += sendCount; - recvLoc += recvCount; - sendCount_y = sendCount; - recvCount_Y = recvCount; - /* grow the arrays */ - xShift.resize(recvLoc); - yShift.resize(recvLoc); - zShift.resize(recvLoc); - morphRadius.resize(recvLoc); - //.............................. - /* send the morphological radius */ - Dm->Comm.Irecv(&morphRadius[recvOffset_Y],recvCount,Dm->rank_Y(),recvtag+0); - Dm->Comm.send(&tmpDistance[0],sendCount,Dm->rank_y(),sendtag+0); - /* send the shift values */ - Dm->Comm.Irecv(&xShift[recvOffset_Y],recvCount,Dm->rank_Y(),recvtag+1); - Dm->Comm.send(&tmpShift_x[0],sendCount,Dm->rank_y(),sendtag+1); - Dm->Comm.Irecv(&yShift[recvOffset_Y],recvCount,Dm->rank_Y(),recvtag+2); - Dm->Comm.send(&tmpShift_y[0],sendCount,Dm->rank_y(),sendtag+2); - Dm->Comm.Irecv(&zShift[recvOffset_Y],recvCount,Dm->rank_Y(),recvtag+3); - Dm->Comm.send(&tmpShift_z[0],sendCount,Dm->rank_y(),sendtag+3); - Dm->Comm.barrier(); - //................................................... - //................................................... - /* X face */ - sendCount = recvCount = 0; - for (size_t k=1; k boundary_distance){ - tmpShift_x[sendCount] = i; - tmpShift_y[sendCount] = j-(Ny-2); - tmpShift_z[sendCount] = k; - tmpDistance[sendCount++] = distance; - int n = k*Nx*Ny + j*Nx + i; - sendID.push_back(n); - } - } - } - } - Dm->Comm.Irecv(&recvCount,1,Dm->rank_y(),recvtag+0); - Dm->Comm.send(&sendCount,1,Dm->rank_Y(),sendtag+0); - Dm->Comm.barrier(); - sendOffset_Y = sendLoc; - recvOffset_y = recvLoc; - sendLoc += sendCount; - recvLoc += recvCount; - sendCount_Y = sendCount; - recvCount_y = recvCount; - /* grow the arrays */ - xShift.resize(recvLoc); - yShift.resize(recvLoc); - zShift.resize(recvLoc); - morphRadius.resize(recvLoc); - //.............................. - /* send the morphological radius */ - Dm->Comm.Irecv(&morphRadius[recvOffset_y],recvCount,Dm->rank_y(),recvtag+0); - Dm->Comm.send(&tmpDistance[0],sendCount,Dm->rank_Y(),sendtag+0); - /* send the shift values */ - Dm->Comm.Irecv(&xShift[recvOffset_y],recvCount,Dm->rank_y(),recvtag+1); - Dm->Comm.send(&tmpShift_x[0],sendCount,Dm->rank_Y(),sendtag+1); - Dm->Comm.Irecv(&yShift[recvOffset_y],recvCount,Dm->rank_y(),recvtag+2); - Dm->Comm.send(&tmpShift_y[0],sendCount,Dm->rank_Y(),sendtag+2); - Dm->Comm.Irecv(&zShift[recvOffset_y],recvCount,Dm->rank_y(),recvtag+3); - Dm->Comm.send(&tmpShift_z[0],sendCount,Dm->rank_Y(),sendtag+3); - Dm->Comm.barrier(); - //................................................... - - //................................................... - /* z face */ - sendCount = recvCount = 0; - for (size_t k=1; k boundary_distance){ - tmpShift_x[sendCount] = i; - tmpShift_y[sendCount] = j; - tmpShift_z[sendCount] = (Nz-2) + k; - tmpDistance[sendCount++] = distance; - int n = k*Nx*Ny + j*Nx + i; - sendID.push_back(n); - } - } - } - } - Dm->Comm.Irecv(&recvCount,1,Dm->rank_Z(),recvtag+0); - Dm->Comm.send(&sendCount,1,Dm->rank_z(),sendtag+0); - Dm->Comm.barrier(); - sendOffset_z = sendLoc; - recvOffset_Z = recvLoc; - sendLoc += sendCount; - recvLoc += recvCount; - sendCount_z = sendCount; - recvCount_Z = recvCount; - /* grow the arrays */ - xShift.resize(recvLoc); - yShift.resize(recvLoc); - zShift.resize(recvLoc); - morphRadius.resize(recvLoc); - //.............................. - /* send the morphological radius */ - Dm->Comm.Irecv(&morphRadius[recvOffset_Z],recvCount,Dm->rank_Z(),recvtag+0); - Dm->Comm.send(&tmpDistance[0],sendCount,Dm->rank_z(),sendtag+0); - /* send the shift values */ - Dm->Comm.Irecv(&xShift[recvOffset_Z],recvCount,Dm->rank_Z(),recvtag+1); - Dm->Comm.send(&tmpShift_x[0],sendCount,Dm->rank_z(),sendtag+1); - Dm->Comm.Irecv(&yShift[recvOffset_Z],recvCount,Dm->rank_Z(),recvtag+2); - Dm->Comm.send(&tmpShift_y[0],sendCount,Dm->rank_z(),sendtag+2); - Dm->Comm.Irecv(&zShift[recvOffset_Z],recvCount,Dm->rank_Z(),recvtag+3); - Dm->Comm.send(&tmpShift_z[0],sendCount,Dm->rank_z(),sendtag+3); - Dm->Comm.barrier(); - //................................................... - /* Z face */ - sendCount = recvCount = 0; - for (size_t k=1; k boundary_distance){ - tmpShift_x[sendCount] = i; - tmpShift_y[sendCount] = j; - tmpShift_z[sendCount] = k-(Nz-2); - tmpDistance[sendCount++] = distance; - int n = k*Nx*Ny + j*Nx + i; - sendID.push_back(n); - } - } - } - } - Dm->Comm.Irecv(&recvCount,1,Dm->rank_z(),recvtag+0); - Dm->Comm.send(&sendCount,1,Dm->rank_Z(),sendtag+0); - Dm->Comm.barrier(); - sendOffset_Z = sendLoc; - recvOffset_z = recvLoc; - sendLoc += sendCount; - recvLoc += recvCount; - sendCount_Z = sendCount; - recvCount_z = recvCount; - /* grow the arrays */ - xShift.resize(recvLoc); - yShift.resize(recvLoc); - zShift.resize(recvLoc); - morphRadius.resize(recvLoc); - //.............................. - /* send the morphological radius */ - Dm->Comm.Irecv(&morphRadius[recvOffset_z],recvCount,Dm->rank_z(),recvtag+0); - Dm->Comm.send(&tmpDistance[0],sendCount,Dm->rank_Z(),sendtag+0); - /* send the shift values */ - Dm->Comm.Irecv(&xShift[recvOffset_z],recvCount,Dm->rank_z(),recvtag+1); - Dm->Comm.send(&tmpShift_x[0],sendCount,Dm->rank_Z(),sendtag+1); - Dm->Comm.Irecv(&yShift[recvOffset_z],recvCount,Dm->rank_z(),recvtag+2); - Dm->Comm.send(&tmpShift_y[0],sendCount,Dm->rank_Z(),sendtag+2); - Dm->Comm.Irecv(&zShift[recvOffset_z],recvCount,Dm->rank_z(),recvtag+3); - Dm->Comm.send(&tmpShift_z[0],sendCount,Dm->rank_Z(),sendtag+3); - Dm->Comm.barrier(); - //................................................... - - /* resize the send / recv lists */ - sendCount = sendLoc; - recvCount = recvLoc; - sendList.resize(sendLoc); - recvList.resize(recvLoc); - localID.resize(sendCount); - nonlocalID.resize(recvCount); - - /*printf(" offset %i for send (x) %i \n", sendOffset_x, sendCount_x); + int *tmpShift_x, *tmpShift_y, *tmpShift_z; + double *tmpDistance; + tmpShift_x = new int[N]; + tmpShift_y = new int[N]; + tmpShift_z = new int[N]; + tmpDistance = new double[N]; + + double distance, boundary_distance; + + /* Loop over the local sub-domain and create overlap lists for each neighboring sub-domain */ + int sendLoc = 0; // counter for the local sub-domain send values + int recvLoc = 0; // counter for the local recv + //................................................... + /* x face */ + sendCount = recvCount = 0; + for (size_t k = 1; k < Nz - 1; k++) { + for (size_t j = 1; j < Ny - 1; j++) { + for (size_t i = 1; i < Nx - 1; i++) { + distance = Distance(i, j, k); + // Distance to x boundary + boundary_distance = double(i - 1); + if (distance > boundary_distance) { + tmpShift_x[sendCount] = Nx + i - 2; + tmpShift_y[sendCount] = j; + tmpShift_z[sendCount] = k; + tmpDistance[sendCount++] = distance; + int n = k * Nx * Ny + j * Nx + i; + sendID.push_back(n); + } + } + } + } + Dm->Comm.Irecv(&recvCount, 1, Dm->rank_X(), recvtag + 0); + Dm->Comm.send(&sendCount, 1, Dm->rank_x(), sendtag + 0); + Dm->Comm.barrier(); + sendOffset_x = sendLoc; + recvOffset_X = recvLoc; + sendLoc += sendCount; + recvLoc += recvCount; + sendCount_x = sendCount; + recvCount_X = recvCount; + /* grow the arrays */ + xShift.resize(recvLoc); + yShift.resize(recvLoc); + zShift.resize(recvLoc); + morphRadius.resize(recvLoc); + //.............................. + /* send the morphological radius */ + Dm->Comm.Irecv(&morphRadius[recvOffset_X], recvCount, Dm->rank_X(), + recvtag + 0); + Dm->Comm.send(&tmpDistance[0], sendCount, Dm->rank_x(), sendtag + 0); + /* send the shift values */ + Dm->Comm.Irecv(&xShift[recvOffset_X], recvCount, Dm->rank_X(), recvtag + 1); + Dm->Comm.send(&tmpShift_x[0], sendCount, Dm->rank_x(), sendtag + 1); + Dm->Comm.Irecv(&yShift[recvOffset_X], recvCount, Dm->rank_X(), recvtag + 2); + Dm->Comm.send(&tmpShift_y[0], sendCount, Dm->rank_x(), sendtag + 2); + Dm->Comm.Irecv(&zShift[recvOffset_X], recvCount, Dm->rank_X(), recvtag + 3); + Dm->Comm.send(&tmpShift_z[0], sendCount, Dm->rank_x(), sendtag + 3); + Dm->Comm.barrier(); + //................................................... + //................................................... + /* X face */ + sendCount = recvCount = 0; + for (size_t k = 1; k < Nz - 1; k++) { + for (size_t j = 1; j < Ny - 1; j++) { + for (size_t i = 1; i < Nx - 1; i++) { + distance = Distance(i, j, k); + // Distance to x boundary + boundary_distance = double(Nx - i - 1); + if (distance > boundary_distance) { + tmpShift_x[sendCount] = (i) - (Nx - 2); + tmpShift_y[sendCount] = j; + tmpShift_z[sendCount] = k; + tmpDistance[sendCount++] = distance; + int n = k * Nx * Ny + j * Nx + i; + sendID.push_back(n); + } + } + } + } + Dm->Comm.Irecv(&recvCount, 1, Dm->rank_x(), recvtag + 0); + Dm->Comm.send(&sendCount, 1, Dm->rank_X(), sendtag + 0); + Dm->Comm.barrier(); + sendOffset_X = sendLoc; + recvOffset_x = recvLoc; + sendLoc += sendCount; + recvLoc += recvCount; + sendCount_X = sendCount; + recvCount_x = recvCount; + /* grow the arrays */ + xShift.resize(recvLoc); + yShift.resize(recvLoc); + zShift.resize(recvLoc); + morphRadius.resize(recvLoc); + //.............................. + /* send the morphological radius */ + Dm->Comm.Irecv(&morphRadius[recvOffset_x], recvCount, Dm->rank_x(), + recvtag + 0); + Dm->Comm.send(&tmpDistance[0], sendCount, Dm->rank_X(), sendtag + 0); + /* send the shift values */ + Dm->Comm.Irecv(&xShift[recvOffset_x], recvCount, Dm->rank_x(), recvtag + 1); + Dm->Comm.send(&tmpShift_x[0], sendCount, Dm->rank_X(), sendtag + 1); + Dm->Comm.Irecv(&yShift[recvOffset_x], recvCount, Dm->rank_x(), recvtag + 2); + Dm->Comm.send(&tmpShift_y[0], sendCount, Dm->rank_X(), sendtag + 2); + Dm->Comm.Irecv(&zShift[recvOffset_x], recvCount, Dm->rank_x(), recvtag + 3); + Dm->Comm.send(&tmpShift_z[0], sendCount, Dm->rank_X(), sendtag + 3); + Dm->Comm.barrier(); + //................................................... + + //................................................... + /* y face */ + sendCount = recvCount = 0; + for (size_t k = 1; k < Nz - 1; k++) { + for (size_t j = 1; j < Ny - 1; j++) { + for (size_t i = 1; i < Nx - 1; i++) { + distance = Distance(i, j, k); + // Distance to y boundary + boundary_distance = double(j - 1); + if (distance > boundary_distance) { + tmpShift_x[sendCount] = i; + tmpShift_y[sendCount] = Ny + j - 2; + tmpShift_z[sendCount] = k; + tmpDistance[sendCount++] = distance; + int n = k * Nx * Ny + j * Nx + i; + sendID.push_back(n); + } + } + } + } + Dm->Comm.Irecv(&recvCount, 1, Dm->rank_Y(), recvtag + 0); + Dm->Comm.send(&sendCount, 1, Dm->rank_y(), sendtag + 0); + Dm->Comm.barrier(); + sendOffset_y = sendLoc; + recvOffset_Y = recvLoc; + sendLoc += sendCount; + recvLoc += recvCount; + sendCount_y = sendCount; + recvCount_Y = recvCount; + /* grow the arrays */ + xShift.resize(recvLoc); + yShift.resize(recvLoc); + zShift.resize(recvLoc); + morphRadius.resize(recvLoc); + //.............................. + /* send the morphological radius */ + Dm->Comm.Irecv(&morphRadius[recvOffset_Y], recvCount, Dm->rank_Y(), + recvtag + 0); + Dm->Comm.send(&tmpDistance[0], sendCount, Dm->rank_y(), sendtag + 0); + /* send the shift values */ + Dm->Comm.Irecv(&xShift[recvOffset_Y], recvCount, Dm->rank_Y(), recvtag + 1); + Dm->Comm.send(&tmpShift_x[0], sendCount, Dm->rank_y(), sendtag + 1); + Dm->Comm.Irecv(&yShift[recvOffset_Y], recvCount, Dm->rank_Y(), recvtag + 2); + Dm->Comm.send(&tmpShift_y[0], sendCount, Dm->rank_y(), sendtag + 2); + Dm->Comm.Irecv(&zShift[recvOffset_Y], recvCount, Dm->rank_Y(), recvtag + 3); + Dm->Comm.send(&tmpShift_z[0], sendCount, Dm->rank_y(), sendtag + 3); + Dm->Comm.barrier(); + //................................................... + //................................................... + /* X face */ + sendCount = recvCount = 0; + for (size_t k = 1; k < Nz - 1; k++) { + for (size_t j = 1; j < Ny - 1; j++) { + for (size_t i = 1; i < Nx - 1; i++) { + distance = Distance(i, j, k); + // Distance to x boundary + boundary_distance = double(Ny - j - 1); + if (distance > boundary_distance) { + tmpShift_x[sendCount] = i; + tmpShift_y[sendCount] = j - (Ny - 2); + tmpShift_z[sendCount] = k; + tmpDistance[sendCount++] = distance; + int n = k * Nx * Ny + j * Nx + i; + sendID.push_back(n); + } + } + } + } + Dm->Comm.Irecv(&recvCount, 1, Dm->rank_y(), recvtag + 0); + Dm->Comm.send(&sendCount, 1, Dm->rank_Y(), sendtag + 0); + Dm->Comm.barrier(); + sendOffset_Y = sendLoc; + recvOffset_y = recvLoc; + sendLoc += sendCount; + recvLoc += recvCount; + sendCount_Y = sendCount; + recvCount_y = recvCount; + /* grow the arrays */ + xShift.resize(recvLoc); + yShift.resize(recvLoc); + zShift.resize(recvLoc); + morphRadius.resize(recvLoc); + //.............................. + /* send the morphological radius */ + Dm->Comm.Irecv(&morphRadius[recvOffset_y], recvCount, Dm->rank_y(), + recvtag + 0); + Dm->Comm.send(&tmpDistance[0], sendCount, Dm->rank_Y(), sendtag + 0); + /* send the shift values */ + Dm->Comm.Irecv(&xShift[recvOffset_y], recvCount, Dm->rank_y(), recvtag + 1); + Dm->Comm.send(&tmpShift_x[0], sendCount, Dm->rank_Y(), sendtag + 1); + Dm->Comm.Irecv(&yShift[recvOffset_y], recvCount, Dm->rank_y(), recvtag + 2); + Dm->Comm.send(&tmpShift_y[0], sendCount, Dm->rank_Y(), sendtag + 2); + Dm->Comm.Irecv(&zShift[recvOffset_y], recvCount, Dm->rank_y(), recvtag + 3); + Dm->Comm.send(&tmpShift_z[0], sendCount, Dm->rank_Y(), sendtag + 3); + Dm->Comm.barrier(); + //................................................... + + //................................................... + /* z face */ + sendCount = recvCount = 0; + for (size_t k = 1; k < Nz - 1; k++) { + for (size_t j = 1; j < Ny - 1; j++) { + for (size_t i = 1; i < Nx - 1; i++) { + distance = Distance(i, j, k); + // Distance to z boundary + boundary_distance = double(k - 1); + if (distance > boundary_distance) { + tmpShift_x[sendCount] = i; + tmpShift_y[sendCount] = j; + tmpShift_z[sendCount] = (Nz - 2) + k; + tmpDistance[sendCount++] = distance; + int n = k * Nx * Ny + j * Nx + i; + sendID.push_back(n); + } + } + } + } + Dm->Comm.Irecv(&recvCount, 1, Dm->rank_Z(), recvtag + 0); + Dm->Comm.send(&sendCount, 1, Dm->rank_z(), sendtag + 0); + Dm->Comm.barrier(); + sendOffset_z = sendLoc; + recvOffset_Z = recvLoc; + sendLoc += sendCount; + recvLoc += recvCount; + sendCount_z = sendCount; + recvCount_Z = recvCount; + /* grow the arrays */ + xShift.resize(recvLoc); + yShift.resize(recvLoc); + zShift.resize(recvLoc); + morphRadius.resize(recvLoc); + //.............................. + /* send the morphological radius */ + Dm->Comm.Irecv(&morphRadius[recvOffset_Z], recvCount, Dm->rank_Z(), + recvtag + 0); + Dm->Comm.send(&tmpDistance[0], sendCount, Dm->rank_z(), sendtag + 0); + /* send the shift values */ + Dm->Comm.Irecv(&xShift[recvOffset_Z], recvCount, Dm->rank_Z(), recvtag + 1); + Dm->Comm.send(&tmpShift_x[0], sendCount, Dm->rank_z(), sendtag + 1); + Dm->Comm.Irecv(&yShift[recvOffset_Z], recvCount, Dm->rank_Z(), recvtag + 2); + Dm->Comm.send(&tmpShift_y[0], sendCount, Dm->rank_z(), sendtag + 2); + Dm->Comm.Irecv(&zShift[recvOffset_Z], recvCount, Dm->rank_Z(), recvtag + 3); + Dm->Comm.send(&tmpShift_z[0], sendCount, Dm->rank_z(), sendtag + 3); + Dm->Comm.barrier(); + //................................................... + /* Z face */ + sendCount = recvCount = 0; + for (size_t k = 1; k < Nz - 1; k++) { + for (size_t j = 1; j < Ny - 1; j++) { + for (size_t i = 1; i < Nx - 1; i++) { + distance = Distance(i, j, k); + // Distance to x boundary + boundary_distance = double(Nz - k - 1); + if (distance > boundary_distance) { + tmpShift_x[sendCount] = i; + tmpShift_y[sendCount] = j; + tmpShift_z[sendCount] = k - (Nz - 2); + tmpDistance[sendCount++] = distance; + int n = k * Nx * Ny + j * Nx + i; + sendID.push_back(n); + } + } + } + } + Dm->Comm.Irecv(&recvCount, 1, Dm->rank_z(), recvtag + 0); + Dm->Comm.send(&sendCount, 1, Dm->rank_Z(), sendtag + 0); + Dm->Comm.barrier(); + sendOffset_Z = sendLoc; + recvOffset_z = recvLoc; + sendLoc += sendCount; + recvLoc += recvCount; + sendCount_Z = sendCount; + recvCount_z = recvCount; + /* grow the arrays */ + xShift.resize(recvLoc); + yShift.resize(recvLoc); + zShift.resize(recvLoc); + morphRadius.resize(recvLoc); + //.............................. + /* send the morphological radius */ + Dm->Comm.Irecv(&morphRadius[recvOffset_z], recvCount, Dm->rank_z(), + recvtag + 0); + Dm->Comm.send(&tmpDistance[0], sendCount, Dm->rank_Z(), sendtag + 0); + /* send the shift values */ + Dm->Comm.Irecv(&xShift[recvOffset_z], recvCount, Dm->rank_z(), recvtag + 1); + Dm->Comm.send(&tmpShift_x[0], sendCount, Dm->rank_Z(), sendtag + 1); + Dm->Comm.Irecv(&yShift[recvOffset_z], recvCount, Dm->rank_z(), recvtag + 2); + Dm->Comm.send(&tmpShift_y[0], sendCount, Dm->rank_Z(), sendtag + 2); + Dm->Comm.Irecv(&zShift[recvOffset_z], recvCount, Dm->rank_z(), recvtag + 3); + Dm->Comm.send(&tmpShift_z[0], sendCount, Dm->rank_Z(), sendtag + 3); + Dm->Comm.barrier(); + //................................................... + + /* resize the send / recv lists */ + sendCount = sendLoc; + recvCount = recvLoc; + sendList.resize(sendLoc); + recvList.resize(recvLoc); + localID.resize(sendCount); + nonlocalID.resize(recvCount); + + /*printf(" offset %i for send (x) %i \n", sendOffset_x, sendCount_x); printf(" offset %i for send (X) %i \n", sendOffset_X, sendCount_X); printf(" offset %i for send (y) %i \n", sendOffset_y, sendCount_y); printf(" offset %i for send (Y) %i \n", sendOffset_Y, sendCount_Y); printf(" offset %i for send (z) %i \n", sendOffset_z, sendCount_z); printf(" offset %i for send (Z) %i \n", sendOffset_Z, sendCount_Z); */ - } -int Morphology::GetOverlaps(std::shared_ptr Dm, signed char *id, const signed char ErodeLabel, const signed char NewLabel){ - - int Nx = Dm->Nx; - int Ny = Dm->Ny; - int Nz = Dm->Nz; - int LocalNumber=0; - int i,j,k,ii,jj,kk; - int imin,jmin,kmin,imax,jmax,kmax; +int Morphology::GetOverlaps(std::shared_ptr Dm, signed char *id, + const signed char ErodeLabel, + const signed char NewLabel) { - for (int idx=0; idxComm.Irecv(&nonlocalID[recvOffset_X],recvCount_X,Dm->rank_x(),recvtag+2); - Dm->Comm.send(&localID[sendOffset_x],sendCount_x,Dm->rank_X(),sendtag+2); + int Nx = Dm->Nx; + int Ny = Dm->Ny; + int Nz = Dm->Nz; + int LocalNumber = 0; + int i, j, k, ii, jj, kk; + int imin, jmin, kmin, imax, jmax, kmax; - //printf("send X \n"); - Dm->Comm.Irecv(&nonlocalID[recvOffset_x],recvCount_x,Dm->rank_X(),recvtag+3); - Dm->Comm.send(&localID[sendOffset_X],sendCount_X,Dm->rank_x(),sendtag+3); - - //printf("send y \n"); - Dm->Comm.Irecv(&nonlocalID[recvOffset_Y],recvCount_Y,Dm->rank_y(),recvtag+4); - Dm->Comm.send(&localID[sendOffset_y],sendCount_y,Dm->rank_Y(),sendtag+4); - - //printf("send Y \n"); - Dm->Comm.Irecv(&nonlocalID[recvOffset_y],recvCount_y,Dm->rank_Y(),recvtag+5); - Dm->Comm.send(&localID[sendOffset_Y],sendCount_Y,Dm->rank_y(),sendtag+5); - - //printf("send z \n"); - Dm->Comm.Irecv(&nonlocalID[recvOffset_Z],recvCount_Z,Dm->rank_z(),recvtag+6); - Dm->Comm.send(&localID[sendOffset_z],sendCount_z,Dm->rank_Z(),sendtag+6); - - //printf("send Z \n"); - Dm->Comm.Irecv(&nonlocalID[recvOffset_z],recvCount_z,Dm->rank_Z(),recvtag+7); - Dm->Comm.send(&localID[sendOffset_Z],sendCount_Z,Dm->rank_z(),sendtag+7); + for (int idx = 0; idx < sendCount; idx++) { + int n = sendID[idx]; + localID[idx] = id[n]; + } + //printf("send x -- offset: %i, count: %i \n",sendOffset_x,sendCount_x); + Dm->Comm.Irecv(&nonlocalID[recvOffset_X], recvCount_X, Dm->rank_x(), + recvtag + 2); + Dm->Comm.send(&localID[sendOffset_x], sendCount_x, Dm->rank_X(), + sendtag + 2); - for (int idx=0; idxComm.barrier(); + //printf("send X \n"); + Dm->Comm.Irecv(&nonlocalID[recvOffset_x], recvCount_x, Dm->rank_X(), + recvtag + 3); + Dm->Comm.send(&localID[sendOffset_X], sendCount_X, Dm->rank_x(), + sendtag + 3); - return LocalNumber; + //printf("send y \n"); + Dm->Comm.Irecv(&nonlocalID[recvOffset_Y], recvCount_Y, Dm->rank_y(), + recvtag + 4); + Dm->Comm.send(&localID[sendOffset_y], sendCount_y, Dm->rank_Y(), + sendtag + 4); + + //printf("send Y \n"); + Dm->Comm.Irecv(&nonlocalID[recvOffset_y], recvCount_y, Dm->rank_Y(), + recvtag + 5); + Dm->Comm.send(&localID[sendOffset_Y], sendCount_Y, Dm->rank_y(), + sendtag + 5); + + //printf("send z \n"); + Dm->Comm.Irecv(&nonlocalID[recvOffset_Z], recvCount_Z, Dm->rank_z(), + recvtag + 6); + Dm->Comm.send(&localID[sendOffset_z], sendCount_z, Dm->rank_Z(), + sendtag + 6); + + //printf("send Z \n"); + Dm->Comm.Irecv(&nonlocalID[recvOffset_z], recvCount_z, Dm->rank_Z(), + recvtag + 7); + Dm->Comm.send(&localID[sendOffset_Z], sendCount_Z, Dm->rank_z(), + sendtag + 7); + + for (int idx = 0; idx < recvCount; idx++) { + double radius = morphRadius[idx]; + signed char label = nonlocalID[idx]; + /* get the neighboring site index */ + i = xShift[idx]; + j = yShift[idx]; + k = zShift[idx]; + int Window = int(radius); + // loop over the window and update + if (label == NewLabel) { + imin = max(1, i - Window); + jmin = max(1, j - Window); + kmin = max(1, k - Window); + imax = min(Nx - 1, i + Window); + jmax = min(Ny - 1, j + Window); + kmax = min(Nz - 1, k + Window); + for (kk = kmin; kk < kmax; kk++) { + for (jj = jmin; jj < jmax; jj++) { + for (ii = imin; ii < imax; ii++) { + int nn = kk * Nx * Ny + jj * Nx + ii; + double dsq = + double((ii - i) * (ii - i) + (jj - j) * (jj - j) + + (kk - k) * (kk - k)); + if (id[nn] == ErodeLabel && dsq <= radius * radius) { + LocalNumber += 1.0; + id[nn] = NewLabel; + } + } + } + } + } + } + Dm->Comm.barrier(); + + return LocalNumber; } //*************************************************************************************** -double MorphOpen(DoubleArray &SignDist, signed char *id, std::shared_ptr Dm, double VoidFraction, signed char ErodeLabel, signed char NewLabel){ - // SignDist is the distance to the object that you want to constaing the morphological opening - // VoidFraction is the the empty space where the object inst - // id is a labeled map - // Dm contains information about the domain structure - - int nx = Dm->Nx; - int ny = Dm->Ny; - int nz = Dm->Nz; - int nprocx = Dm->nprocx(); - int nprocy = Dm->nprocy(); - int nprocz = Dm->nprocz(); - int rank = Dm->rank(); +double MorphOpen(DoubleArray &SignDist, signed char *id, + std::shared_ptr Dm, double VoidFraction, + signed char ErodeLabel, signed char NewLabel) { + // SignDist is the distance to the object that you want to constaing the morphological opening + // VoidFraction is the the empty space where the object inst + // id is a labeled map + // Dm contains information about the domain structure - int n; - double final_void_fraction; - double count,countGlobal,totalGlobal; - count = 0.f; - double maxdist=-200.f; - double maxdistGlobal; - for (int k=1; k maxdist) maxdist=SignDist(i,j,k); - if ( id[n] == ErodeLabel){ - count += 1.0; - //id[n] = 2; - } - } - } - } - Dm->Comm.barrier(); - - Morphology Structure; - Structure.Initialize(Dm,SignDist); - - // total Global is the number of nodes in the pore-space - totalGlobal = Dm->Comm.sumReduce( count ); - maxdistGlobal = Dm->Comm.sumReduce( maxdist ); - double volume=double(nprocx*nprocy*nprocz)*double(nx-2)*double(ny-2)*double(nz-2); - double volume_fraction=totalGlobal/volume; - if (rank==0) printf("Volume fraction for morphological opening: %f \n",volume_fraction); - if (rank==0) printf("Maximum pore size: %f \n",maxdistGlobal); - final_void_fraction = volume_fraction; //initialize + int nx = Dm->Nx; + int ny = Dm->Ny; + int nz = Dm->Nz; + int nprocx = Dm->nprocx(); + int nprocy = Dm->nprocy(); + int nprocz = Dm->nprocz(); + int rank = Dm->rank(); - int ii,jj,kk; - int imin,jmin,kmin,imax,jmax,kmax; - int Nx = nx; - int Ny = ny; - int Nz = nz; + int n; + double final_void_fraction; + double count, countGlobal, totalGlobal; + count = 0.f; + double maxdist = -200.f; + double maxdistGlobal; + for (int k = 1; k < nz - 1; k++) { + for (int j = 1; j < ny - 1; j++) { + for (int i = 1; i < nx - 1; i++) { + n = k * nx * ny + j * nx + i; + // extract maximum distance for critical radius + if (SignDist(i, j, k) > maxdist) + maxdist = SignDist(i, j, k); + if (id[n] == ErodeLabel) { + count += 1.0; + //id[n] = 2; + } + } + } + } + Dm->Comm.barrier(); - double void_fraction_old=1.0; - double void_fraction_new=1.0; - double void_fraction_diff_old = 1.0; - double void_fraction_diff_new = 1.0; - if (ErodeLabel == 1){ - VoidFraction = 1.0 - VoidFraction; - } + Morphology Structure; + Structure.Initialize(Dm, SignDist); - // Increase the critical radius until the target saturation is met - double deltaR=0.05; // amount to change the radius in voxel units - double Rcrit_old = maxdistGlobal; - double Rcrit_new = maxdistGlobal; + // total Global is the number of nodes in the pore-space + totalGlobal = Dm->Comm.sumReduce(count); + maxdistGlobal = Dm->Comm.sumReduce(maxdist); + double volume = double(nprocx * nprocy * nprocz) * double(nx - 2) * + double(ny - 2) * double(nz - 2); + double volume_fraction = totalGlobal / volume; + if (rank == 0) + printf("Volume fraction for morphological opening: %f \n", + volume_fraction); + if (rank == 0) + printf("Maximum pore size: %f \n", maxdistGlobal); + final_void_fraction = volume_fraction; //initialize - int numTry = 0; - int maxTry = 100; - while (void_fraction_new > VoidFraction && numTry < maxTry) - { - numTry++; - void_fraction_diff_old = void_fraction_diff_new; - void_fraction_old = void_fraction_new; - Rcrit_old = Rcrit_new; - Rcrit_new -= deltaR*Rcrit_old; - if (Rcrit_new < 0.5 ){ - numTry = maxTry; - } - int Window=round(Rcrit_new); - if (Window == 0) Window = 1; // If Window = 0 at the begining, after the following process will have sw=1.0 - // and sw Rcrit_new){ - // loop over the window and update - imin=max(1,i-Window); - jmin=max(1,j-Window); - kmin=max(1,k-Window); - imax=min(Nx-1,i+Window); - jmax=min(Ny-1,j+Window); - kmax=min(Nz-1,k+Window); - for (kk=kmin; kkComm.sumReduce( count ); - void_fraction_new = countGlobal/totalGlobal; - void_fraction_diff_new = abs(void_fraction_new-VoidFraction); - if (rank==0){ - printf(" %f ",void_fraction_new); - printf(" %f\n",Rcrit_new); - } - } + // Increase the critical radius until the target saturation is met + double deltaR = 0.05; // amount to change the radius in voxel units + double Rcrit_old = maxdistGlobal; + double Rcrit_new = maxdistGlobal; - if (void_fraction_diff_new VoidFraction && numTry < maxTry) { + numTry++; + void_fraction_diff_old = void_fraction_diff_new; + void_fraction_old = void_fraction_new; + Rcrit_old = Rcrit_new; + Rcrit_new -= deltaR * Rcrit_old; + if (Rcrit_new < 0.5) { + numTry = maxTry; + } + int Window = round(Rcrit_new); + if (Window == 0) + Window = + 1; // If Window = 0 at the begining, after the following process will have sw=1.0 + // and sw Rcrit_new) { + // loop over the window and update + imin = max(1, i - Window); + jmin = max(1, j - Window); + kmin = max(1, k - Window); + imax = min(Nx - 1, i + Window); + jmax = min(Ny - 1, j + Window); + kmax = min(Nz - 1, k + Window); + for (kk = kmin; kk < kmax; kk++) { + for (jj = jmin; jj < jmax; jj++) { + for (ii = imin; ii < imax; ii++) { + int nn = kk * nx * ny + jj * nx + ii; + double dsq = double((ii - i) * (ii - i) + + (jj - j) * (jj - j) + + (kk - k) * (kk - k)); + if (id[nn] == ErodeLabel && + dsq <= Rcrit_new * Rcrit_new) { + LocalNumber += 1.0; + id[nn] = NewLabel; + } + } + } + } + } + // move on + } + } + } + LocalNumber += Structure.GetOverlaps(Dm, id, ErodeLabel, NewLabel); + + count = 0.f; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + n = k * Nx * Ny + j * Nx + i; + if (id[n] == ErodeLabel) { + count += 1.0; + } + } + } + } + countGlobal = Dm->Comm.sumReduce(count); + void_fraction_new = countGlobal / totalGlobal; + void_fraction_diff_new = abs(void_fraction_new - VoidFraction); + if (rank == 0) { + printf(" %f ", void_fraction_new); + printf(" %f\n", Rcrit_new); + } + } + + if (void_fraction_diff_new < void_fraction_diff_old) { + final_void_fraction = void_fraction_new; + if (rank == 0) { + printf("Final void fraction =%f\n", void_fraction_new); + printf("Final critical radius=%f\n", Rcrit_new); + } + } else { + final_void_fraction = void_fraction_old; + if (rank == 0) { + printf("Final void fraction=%f\n", void_fraction_old); + printf("Final critical radius=%f\n", Rcrit_old); + } + } + return final_void_fraction; } - //*************************************************************************************** -double MorphDrain(DoubleArray &SignDist, signed char *id, std::shared_ptr Dm, double VoidFraction){ - // SignDist is the distance to the object that you want to constaing the morphological opening - // VoidFraction is the the empty space where the object inst - // id is a labeled map - // Dm contains information about the domain structure - - signed char ErodeLabel = 2; - signed char NewLabel = 1; - - int nx = Dm->Nx; - int ny = Dm->Ny; - int nz = Dm->Nz; - int nprocx = Dm->nprocx(); - int nprocy = Dm->nprocy(); - int nprocz = Dm->nprocz(); - int rank = Dm->rank(); - - DoubleArray phase(nx,ny,nz); - IntArray phase_label(nx,ny,nz); - Array ID(nx,ny,nz); - fillHalo fillChar(Dm->Comm,Dm->rank_info,{nx-2,ny-2,nz-2},{1,1,1},0,1); - - Morphology Structure; - Structure.Initialize(Dm,SignDist); +double MorphDrain(DoubleArray &SignDist, signed char *id, + std::shared_ptr Dm, double VoidFraction) { + // SignDist is the distance to the object that you want to constaing the morphological opening + // VoidFraction is the the empty space where the object inst + // id is a labeled map + // Dm contains information about the domain structure - int n; - double final_void_fraction; - double count,countGlobal,totalGlobal; - count = 0.f; - double maxdist=-200.f; - double maxdistGlobal; - for (int k=1; k maxdist) maxdist=SignDist(i,j,k); - if ( SignDist(i,j,k) > 0.0 ){ - count += 1.0; - id[n] = ErodeLabel; - } - ID(i,j,k) = id[n]; - } - } - } - fillChar.fill(ID); - Dm->Comm.barrier(); - - // total Global is the number of nodes in the pore-space - totalGlobal = Dm->Comm.sumReduce( count ); - maxdistGlobal = Dm->Comm.sumReduce( maxdist ); - double volume=double(nprocx*nprocy*nprocz)*double(nx-2)*double(ny-2)*double(nz-2); - double volume_fraction=totalGlobal/volume; - if (rank==0) printf("Volume fraction for morphological opening: %f \n",volume_fraction); - if (rank==0) printf("Maximum pore size: %f \n",maxdistGlobal); + signed char ErodeLabel = 2; + signed char NewLabel = 1; - int ii,jj,kk; - int imin,jmin,kmin,imax,jmax,kmax; - int Nx = nx; - int Ny = ny; - int Nz = nz; + int nx = Dm->Nx; + int ny = Dm->Ny; + int nz = Dm->Nz; + int nprocx = Dm->nprocx(); + int nprocy = Dm->nprocy(); + int nprocz = Dm->nprocz(); + int rank = Dm->rank(); - double void_fraction_old=1.0; - double void_fraction_new=1.0; - double void_fraction_diff_old = 1.0; - double void_fraction_diff_new = 1.0; + DoubleArray phase(nx, ny, nz); + IntArray phase_label(nx, ny, nz); + Array ID(nx, ny, nz); + fillHalo fillChar(Dm->Comm, Dm->rank_info, {nx - 2, ny - 2, nz - 2}, + {1, 1, 1}, 0, 1); - // Increase the critical radius until the target saturation is met - double deltaR=0.05; // amount to change the radius in voxel units - double Rcrit_old = maxdistGlobal; - double Rcrit_new = maxdistGlobal; - //if (argc>2){ - // Rcrit_new = strtod(argv[2],NULL); - // if (rank==0) printf("Max. distance =%f, Initial critical radius = %f \n",maxdistGlobal,Rcrit_new); - //} - Dm->Comm.barrier(); - - FILE *DRAIN = fopen("morphdrain.csv","w"); - fprintf(DRAIN,"sw radius\n"); + Morphology Structure; + Structure.Initialize(Dm, SignDist); - while (void_fraction_new > VoidFraction && Rcrit_new > 0.5) - { - void_fraction_diff_old = void_fraction_diff_new; - void_fraction_old = void_fraction_new; - Rcrit_old = Rcrit_new; - Rcrit_new -= deltaR*Rcrit_old; - int Window=round(Rcrit_new); - if (Window == 0) Window = 1; // If Window = 0 at the begining, after the following process will have sw=1.0 - // and sw Rcrit_new){ - // loop over the window and update - imin=max(1,i-Window); - jmin=max(1,j-Window); - kmin=max(1,k-Window); - imax=min(Nx-1,i+Window); - jmax=min(Ny-1,j+Window); - kmax=min(Nz-1,k+Window); - for (kk=kmin; kk maxdist) + maxdist = SignDist(i, j, k); + if (SignDist(i, j, k) > 0.0) { + count += 1.0; + id[n] = ErodeLabel; + } + ID(i, j, k) = id[n]; + } + } + } + fillChar.fill(ID); + Dm->Comm.barrier(); - for(int k=1; kComm.barrier(); + // total Global is the number of nodes in the pore-space + totalGlobal = Dm->Comm.sumReduce(count); + maxdistGlobal = Dm->Comm.sumReduce(maxdist); + double volume = double(nprocx * nprocy * nprocz) * double(nx - 2) * + double(ny - 2) * double(nz - 2); + double volume_fraction = totalGlobal / volume; + if (rank == 0) + printf("Volume fraction for morphological opening: %f \n", + volume_fraction); + if (rank == 0) + printf("Maximum pore size: %f \n", maxdistGlobal); - for (int k=0; krank_info,phase,SignDist,vF,vS,phase_label,Dm->Comm); - Dm->Comm.barrier(); - - for (int k=0; k 1){ - ID(i,j,k) = ErodeLabel; - } - id[n] = ID(i,j,k); - } - } - } + int ii, jj, kk; + int imin, jmin, kmin, imax, jmax, kmax; + int Nx = nx; + int Ny = ny; + int Nz = nz; - count = 0.f; - for (int k=1; k 1){ - count+=1.0; - } - } - } - } - countGlobal = Dm->Comm.sumReduce( count ); - void_fraction_new = countGlobal/totalGlobal; - void_fraction_diff_new = abs(void_fraction_new-VoidFraction); - if (rank==0){ - fprintf(DRAIN,"%f ",void_fraction_new); - fprintf(DRAIN,"%f\n",Rcrit_new); - printf(" %f ",void_fraction_new); - printf(" %f\n",Rcrit_new); - } - } + double void_fraction_old = 1.0; + double void_fraction_new = 1.0; + double void_fraction_diff_old = 1.0; + double void_fraction_diff_new = 1.0; - if (void_fraction_diff_new 1){ - id[n] = 2; - } - } - } - } - - return final_void_fraction; + // Increase the critical radius until the target saturation is met + double deltaR = 0.05; // amount to change the radius in voxel units + double Rcrit_old = maxdistGlobal; + double Rcrit_new = maxdistGlobal; + //if (argc>2){ + // Rcrit_new = strtod(argv[2],NULL); + // if (rank==0) printf("Max. distance =%f, Initial critical radius = %f \n",maxdistGlobal,Rcrit_new); + //} + Dm->Comm.barrier(); + + FILE *DRAIN = fopen("morphdrain.csv", "w"); + fprintf(DRAIN, "sw radius\n"); + + while (void_fraction_new > VoidFraction && Rcrit_new > 0.5) { + void_fraction_diff_old = void_fraction_diff_new; + void_fraction_old = void_fraction_new; + Rcrit_old = Rcrit_new; + Rcrit_new -= deltaR * Rcrit_old; + int Window = round(Rcrit_new); + if (Window == 0) + Window = + 1; // If Window = 0 at the begining, after the following process will have sw=1.0 + // and sw Rcrit_new) { + // loop over the window and update + imin = max(1, i - Window); + jmin = max(1, j - Window); + kmin = max(1, k - Window); + imax = min(Nx - 1, i + Window); + jmax = min(Ny - 1, j + Window); + kmax = min(Nz - 1, k + Window); + for (kk = kmin; kk < kmax; kk++) { + for (jj = jmin; jj < jmax; jj++) { + for (ii = imin; ii < imax; ii++) { + double dsq = double((ii - i) * (ii - i) + + (jj - j) * (jj - j) + + (kk - k) * (kk - k)); + if (ID(ii, jj, kk) == ErodeLabel && + dsq <= + (Rcrit_new + 1) * (Rcrit_new + 1)) { + LocalNumber += 1.0; + //id[nn]=1; + ID(ii, jj, kk) = NewLabel; + id[kk * Nx * Ny + jj * Nx + ii] = + NewLabel; + } + } + } + } + } + // move on + } + } + } + LocalNumber += Structure.GetOverlaps(Dm, id, ErodeLabel, NewLabel); + + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + ID(i, j, k) = id[k * Nx * Ny + j * Nx + i]; + } + } + } + fillChar.fill(ID); + Dm->Comm.barrier(); + + for (int k = 0; k < nz; k++) { + for (int j = 0; j < ny; j++) { + for (int i = 0; i < nx; i++) { + if (ID(i, j, k) == NewLabel) { + phase(i, j, k) = 1.0; + } else + phase(i, j, k) = -1.0; + } + } + } + + // Extract only the connected part of NWP + double vF = 0.0; + double vS = 0.0; + ComputeGlobalBlobIDs(nx - 2, ny - 2, nz - 2, Dm->rank_info, phase, + SignDist, vF, vS, phase_label, Dm->Comm); + Dm->Comm.barrier(); + + for (int k = 0; k < nz; k++) { + for (int j = 0; j < ny; j++) { + for (int i = 0; i < nx; i++) { + n = k * nx * ny + j * nx + i; + if (ID(i, j, k) == 1 && phase_label(i, j, k) > 1) { + ID(i, j, k) = ErodeLabel; + } + id[n] = ID(i, j, k); + } + } + } + + count = 0.f; + for (int k = 1; k < nz - 1; k++) { + for (int j = 1; j < ny - 1; j++) { + for (int i = 1; i < nx - 1; i++) { + n = k * nx * ny + j * nx + i; + if (id[n] > 1) { + count += 1.0; + } + } + } + } + countGlobal = Dm->Comm.sumReduce(count); + void_fraction_new = countGlobal / totalGlobal; + void_fraction_diff_new = abs(void_fraction_new - VoidFraction); + if (rank == 0) { + fprintf(DRAIN, "%f ", void_fraction_new); + fprintf(DRAIN, "%f\n", Rcrit_new); + printf(" %f ", void_fraction_new); + printf(" %f\n", Rcrit_new); + } + } + + if (void_fraction_diff_new < void_fraction_diff_old) { + final_void_fraction = void_fraction_new; + if (rank == 0) { + printf("Final void fraction =%f\n", void_fraction_new); + printf("Final critical radius=%f\n", Rcrit_new); + } + } else { + final_void_fraction = void_fraction_old; + if (rank == 0) { + printf("Final void fraction=%f\n", void_fraction_old); + printf("Final critical radius=%f\n", Rcrit_old); + } + } + // label all WP components as 2 + for (int k = 1; k < nz - 1; k++) { + for (int j = 1; j < ny - 1; j++) { + for (int i = 1; i < nx - 1; i++) { + n = k * nx * ny + j * nx + i; + if (id[n] > 1) { + id[n] = 2; + } + } + } + } + + return final_void_fraction; } //double MorphGrow(DoubleArray &BoundaryDist, DoubleArray &Dist, Array &id, std::shared_ptr Dm, double TargetGrowth) -double MorphGrow(DoubleArray &BoundaryDist, DoubleArray &Dist, Array &id, std::shared_ptr Dm, double TargetGrowth, double WallFactor) -{ - int Nx = Dm->Nx; - int Ny = Dm->Ny; - int Nz = Dm->Nz; - int rank = Dm->rank(); - - double count=0.0; - for (int k=1; kComm.sumReduce( count); +double MorphGrow(DoubleArray &BoundaryDist, DoubleArray &Dist, Array &id, + std::shared_ptr Dm, double TargetGrowth, + double WallFactor) { + int Nx = Dm->Nx; + int Ny = Dm->Ny; + int Nz = Dm->Nz; + int rank = Dm->rank(); - // Estimate morph_delta - double morph_delta = 0.0; - if (TargetGrowth > 0.0) morph_delta = 0.1; - else morph_delta = -0.1; - double morph_delta_previous = 0.0; - double GrowthEstimate = 0.0; - double GrowthPrevious = 0.0; - int COUNT_FOR_LOOP = 0; - double ERROR = 100.0; - if (rank == 0) printf("Estimate delta for growth=%f \n",TargetGrowth); - while ( ERROR > 0.01 && COUNT_FOR_LOOP < 10 ){ - COUNT_FOR_LOOP++; - count = 0.0; - double MAX_DISPLACEMENT = 0.0; - for (int k=1; k MAX_DISPLACEMENT) MAX_DISPLACEMENT= fabs(wallweight*morph_delta); - - if (Dist(i,j,k) - wallweight*morph_delta < 0.0){ - count+=1.0; - } - } - } - } - count = Dm->Comm.sumReduce( count ); - MAX_DISPLACEMENT = Dm->Comm.maxReduce( MAX_DISPLACEMENT ); - GrowthEstimate = count - count_original; - ERROR = fabs((GrowthEstimate-TargetGrowth) /TargetGrowth); + double count = 0.0; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + if (Dist(i, j, k) < 0.0) { + count += 1.0; + } + } + } + } + double count_original = Dm->Comm.sumReduce(count); - if (rank == 0) printf(" delta=%f, growth=%f, max. displacement = %f \n",morph_delta, GrowthEstimate, MAX_DISPLACEMENT); - // Now adjust morph_delta - if (fabs(GrowthEstimate - GrowthPrevious) > 0.0) { - double step_size = (TargetGrowth - GrowthEstimate)*(morph_delta - morph_delta_previous) / (GrowthEstimate - GrowthPrevious); - GrowthPrevious = GrowthEstimate; - morph_delta_previous = morph_delta; - morph_delta += step_size; - } - if (morph_delta / morph_delta_previous > 2.0 ) morph_delta = morph_delta_previous*2.0; + // Estimate morph_delta + double morph_delta = 0.0; + if (TargetGrowth > 0.0) + morph_delta = 0.1; + else + morph_delta = -0.1; + double morph_delta_previous = 0.0; + double GrowthEstimate = 0.0; + double GrowthPrevious = 0.0; + int COUNT_FOR_LOOP = 0; + double ERROR = 100.0; + if (rank == 0) + printf("Estimate delta for growth=%f \n", TargetGrowth); + while (ERROR > 0.01 && COUNT_FOR_LOOP < 10) { + COUNT_FOR_LOOP++; + count = 0.0; + double MAX_DISPLACEMENT = 0.0; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + double walldist = BoundaryDist(i, j, k); + //double wallweight = 1.0 / (1+exp(-5.f*(walldist-1.f))); + double wallweight = + WallFactor / (1 + exp(-5.f * (walldist - 1.f))); + //wallweight = 1.0; + if (fabs(wallweight * morph_delta) > MAX_DISPLACEMENT) + MAX_DISPLACEMENT = fabs(wallweight * morph_delta); - //MAX_DISPLACEMENT *= max(TargetGrowth/GrowthEstimate,1.25); - - if (morph_delta > 0.0 ){ - // object is growing - if (MAX_DISPLACEMENT > 3.0 ){ - morph_delta = 3.0; - COUNT_FOR_LOOP = 100; // exit loop if displacement is too large - } - } - else{ - // object is shrinking - if (MAX_DISPLACEMENT > 1.0 ){ - morph_delta = -1.0; - COUNT_FOR_LOOP = 100; // exit loop if displacement is too large - } - } - } - if (rank == 0) printf("Final delta=%f \n",morph_delta); + if (Dist(i, j, k) - wallweight * morph_delta < 0.0) { + count += 1.0; + } + } + } + } + count = Dm->Comm.sumReduce(count); + MAX_DISPLACEMENT = Dm->Comm.maxReduce(MAX_DISPLACEMENT); + GrowthEstimate = count - count_original; + ERROR = fabs((GrowthEstimate - TargetGrowth) / TargetGrowth); - count = 0.0; - for (int k=1; kComm.sumReduce( count ); + if (rank == 0) + printf(" delta=%f, growth=%f, max. displacement = %f \n", + morph_delta, GrowthEstimate, MAX_DISPLACEMENT); + // Now adjust morph_delta + if (fabs(GrowthEstimate - GrowthPrevious) > 0.0) { + double step_size = (TargetGrowth - GrowthEstimate) * + (morph_delta - morph_delta_previous) / + (GrowthEstimate - GrowthPrevious); + GrowthPrevious = GrowthEstimate; + morph_delta_previous = morph_delta; + morph_delta += step_size; + } + if (morph_delta / morph_delta_previous > 2.0) + morph_delta = morph_delta_previous * 2.0; - return count; + //MAX_DISPLACEMENT *= max(TargetGrowth/GrowthEstimate,1.25); + + if (morph_delta > 0.0) { + // object is growing + if (MAX_DISPLACEMENT > 3.0) { + morph_delta = 3.0; + COUNT_FOR_LOOP = 100; // exit loop if displacement is too large + } + } else { + // object is shrinking + if (MAX_DISPLACEMENT > 1.0) { + morph_delta = -1.0; + COUNT_FOR_LOOP = 100; // exit loop if displacement is too large + } + } + } + if (rank == 0) + printf("Final delta=%f \n", morph_delta); + + count = 0.0; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + double walldist = BoundaryDist(i, j, k); + //double wallweight = 1.0 / (1+exp(-5.f*(walldist-1.f))); + //wallweight = 1.0; + double wallweight = + WallFactor / (1 + exp(-5.f * (walldist - 1.f))); + Dist(i, j, k) -= wallweight * morph_delta; + + if (Dist(i, j, k) < 0.0) + count += 1.0; + } + } + } + count = Dm->Comm.sumReduce(count); + + return count; } - diff --git a/analysis/morphology.h b/analysis/morphology.h index ae69ed5c..4ef2301c 100644 --- a/analysis/morphology.h +++ b/analysis/morphology.h @@ -3,9 +3,14 @@ #include "common/Domain.h" #include "analysis/runAnalysis.h" -double MorphOpen(DoubleArray &SignDist, signed char *id, std::shared_ptr Dm, double VoidFraction, signed char ErodeLabel, signed char ReplaceLabel); -double MorphDrain(DoubleArray &SignDist, signed char *id, std::shared_ptr Dm, double VoidFraction); -double MorphGrow(DoubleArray &BoundaryDist, DoubleArray &Dist, Array &id, std::shared_ptr Dm, double TargetVol, double WallFactor); +double MorphOpen(DoubleArray &SignDist, signed char *id, + std::shared_ptr Dm, double VoidFraction, + signed char ErodeLabel, signed char ReplaceLabel); +double MorphDrain(DoubleArray &SignDist, signed char *id, + std::shared_ptr Dm, double VoidFraction); +double MorphGrow(DoubleArray &BoundaryDist, DoubleArray &Dist, Array &id, + std::shared_ptr Dm, double TargetVol, + double WallFactor); #ifndef MORPHOLOGY_INC #define MORPHOLOGY_INC @@ -15,84 +20,103 @@ double MorphGrow(DoubleArray &BoundaryDist, DoubleArray &Dist, Array &id, * The Morphology class supports morphological operations on complex structures * */ -class Morphology{ -public: - /** +class Morphology { +public: + /** * \brief Create a flow adaptor to operate on the LB model */ - Morphology(); + Morphology(); - /** + /** * \brief Destructor */ - ~Morphology(); - - /** + ~Morphology(); + + /** * \brief Initialize morphology structure from distance map * @param Dm Domain structure * @param Distance Signed distance to boundary of structure */ - void Initialize(std::shared_ptr Dm, DoubleArray &Distance); - - /** + void Initialize(std::shared_ptr Dm, DoubleArray &Distance); + + /** * \brief Find all sites such that the reach of the signed distance at the site overlaps with a sub-domain boundary * @param Dm Domain structure * @param id image labels * @param ErodeLabel label to erode based on morphological operation * @param NewLabel label to assign based on morphological operation */ - int GetOverlaps(std::shared_ptr Dm, signed char *id, const signed char ErodeLabel, const signed char NewLabel); - + int GetOverlaps(std::shared_ptr Dm, signed char *id, + const signed char ErodeLabel, const signed char NewLabel); + /* * data structures to store non-local morphological information */ std::vector xShift, yShift, zShift; std::vector sendID; - std::vector morphRadius; + std::vector morphRadius; std::vector localID; std::vector nonlocalID; - + private: - int sendtag,recvtag; - + int sendtag, recvtag; + //...................................................................................... int sendCount, recvCount; //...................................................................................... - int sendOffset_x, sendOffset_y, sendOffset_z, sendOffset_X, sendOffset_Y, sendOffset_Z; - int sendOffset_xy, sendOffset_yz, sendOffset_xz, sendOffset_Xy, sendOffset_Yz, sendOffset_xZ; - int sendOffset_xY, sendOffset_yZ, sendOffset_Xz, sendOffset_XY, sendOffset_YZ, sendOffset_XZ; + int sendOffset_x, sendOffset_y, sendOffset_z, sendOffset_X, sendOffset_Y, + sendOffset_Z; + int sendOffset_xy, sendOffset_yz, sendOffset_xz, sendOffset_Xy, + sendOffset_Yz, sendOffset_xZ; + int sendOffset_xY, sendOffset_yZ, sendOffset_Xz, sendOffset_XY, + sendOffset_YZ, sendOffset_XZ; int sendOffset_xyz, sendOffset_XYZ, sendOffset_xYz, sendOffset_XyZ; int sendOffset_Xyz, sendOffset_xYZ, sendOffset_xyZ, sendOffset_XYz; //...................................................................................... - int recvOffset_x, recvOffset_y, recvOffset_z, recvOffset_X, recvOffset_Y, recvOffset_Z; - int recvOffset_xy, recvOffset_yz, recvOffset_xz, recvOffset_Xy, recvOffset_Yz, recvOffset_xZ; - int recvOffset_xY, recvOffset_yZ, recvOffset_Xz, recvOffset_XY, recvOffset_YZ, recvOffset_XZ; + int recvOffset_x, recvOffset_y, recvOffset_z, recvOffset_X, recvOffset_Y, + recvOffset_Z; + int recvOffset_xy, recvOffset_yz, recvOffset_xz, recvOffset_Xy, + recvOffset_Yz, recvOffset_xZ; + int recvOffset_xY, recvOffset_yZ, recvOffset_Xz, recvOffset_XY, + recvOffset_YZ, recvOffset_XZ; int recvOffset_xyz, recvOffset_XYZ, recvOffset_xYz, recvOffset_XyZ; - int recvOffset_Xyz, recvOffset_xYZ, recvOffset_xyZ, recvOffset_XYz; + int recvOffset_Xyz, recvOffset_xYZ, recvOffset_xyZ, recvOffset_XYz; //...................................................................................... - int sendCount_x, sendCount_y, sendCount_z, sendCount_X, sendCount_Y, sendCount_Z; - int sendCount_xy, sendCount_yz, sendCount_xz, sendCount_Xy, sendCount_Yz, sendCount_xZ; - int sendCount_xY, sendCount_yZ, sendCount_Xz, sendCount_XY, sendCount_YZ, sendCount_XZ; + int sendCount_x, sendCount_y, sendCount_z, sendCount_X, sendCount_Y, + sendCount_Z; + int sendCount_xy, sendCount_yz, sendCount_xz, sendCount_Xy, sendCount_Yz, + sendCount_xZ; + int sendCount_xY, sendCount_yZ, sendCount_Xz, sendCount_XY, sendCount_YZ, + sendCount_XZ; int sendCount_xyz, sendCount_XYZ, sendCount_xYz, sendCount_XyZ; int sendCount_Xyz, sendCount_xYZ, sendCount_xyZ, sendCount_XYz; //...................................................................................... - int recvCount_x, recvCount_y, recvCount_z, recvCount_X, recvCount_Y, recvCount_Z; - int recvCount_xy, recvCount_yz, recvCount_xz, recvCount_Xy, recvCount_Yz, recvCount_xZ; - int recvCount_xY, recvCount_yZ, recvCount_Xz, recvCount_XY, recvCount_YZ, recvCount_XZ; + int recvCount_x, recvCount_y, recvCount_z, recvCount_X, recvCount_Y, + recvCount_Z; + int recvCount_xy, recvCount_yz, recvCount_xz, recvCount_Xy, recvCount_Yz, + recvCount_xZ; + int recvCount_xY, recvCount_yZ, recvCount_Xz, recvCount_XY, recvCount_YZ, + recvCount_XZ; int recvCount_xyz, recvCount_XYZ, recvCount_xYz, recvCount_XyZ; - int recvCount_Xyz, recvCount_xYZ, recvCount_xyZ, recvCount_XYz; + int recvCount_Xyz, recvCount_xYZ, recvCount_xyZ, recvCount_XYz; //...................................................................................... std::vector sendList; std::vector recvList; //...................................................................................... - - // Communication buffers - signed char *sendID_x, *sendID_y, *sendID_z, *sendID_X, *sendID_Y, *sendID_Z; - signed char *sendID_xy, *sendID_yz, *sendID_xz, *sendID_Xy, *sendID_Yz, *sendID_xZ; - signed char *sendID_xY, *sendID_yZ, *sendID_Xz, *sendID_XY, *sendID_YZ, *sendID_XZ; - signed char *recvID_x, *recvID_y, *recvID_z, *recvID_X, *recvID_Y, *recvID_Z; - signed char *recvID_xy, *recvID_yz, *recvID_xz, *recvID_Xy, *recvID_Yz, *recvID_xZ; - signed char *recvID_xY, *recvID_yZ, *recvID_Xz, *recvID_XY, *recvID_YZ, *recvID_XZ; + + // Communication buffers + signed char *sendID_x, *sendID_y, *sendID_z, *sendID_X, *sendID_Y, + *sendID_Z; + signed char *sendID_xy, *sendID_yz, *sendID_xz, *sendID_Xy, *sendID_Yz, + *sendID_xZ; + signed char *sendID_xY, *sendID_yZ, *sendID_Xz, *sendID_XY, *sendID_YZ, + *sendID_XZ; + signed char *recvID_x, *recvID_y, *recvID_z, *recvID_X, *recvID_Y, + *recvID_Z; + signed char *recvID_xy, *recvID_yz, *recvID_xz, *recvID_Xy, *recvID_Yz, + *recvID_xZ; + signed char *recvID_xY, *recvID_yZ, *recvID_Xz, *recvID_XY, *recvID_YZ, + *recvID_XZ; }; #endif \ No newline at end of file diff --git a/analysis/pmmc.h b/analysis/pmmc.h index f8b22c6d..a563d73a 100644 --- a/analysis/pmmc.h +++ b/analysis/pmmc.h @@ -28,365 +28,359 @@ using namespace std; -static int edgeTable[256]={ - 0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, - 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, - 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, - 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, - 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, - 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, - 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, - 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, - 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, - 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, - 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, - 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, - 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, - 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, - 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , - 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, - 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, - 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, - 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, - 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, - 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, - 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, - 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, - 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, - 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, - 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, - 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, - 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, - 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, - 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, - 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, - 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 }; -static signed char triTable[256][16] = - {{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, - {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, - {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, - {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, - {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, - {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, - {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, - {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, - {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, - {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, - {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, - {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, - {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, - {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, - {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, - {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, - {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, - {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, - {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, - {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, - {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, - {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, - {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, - {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, - {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, - {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, - {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, - {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, - {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, - {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, - {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, - {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, - {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, - {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, - {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, - {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, - {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, - {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, - {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, - {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, - {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, - {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, - {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, - {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, - {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, - {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, - {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, - {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, - {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, - {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, - {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, - {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, - {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, - {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, - {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, - {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, - {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, - {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, - {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, - {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, - {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, - {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, - {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, - {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, - {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, - {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, - {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, - {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, - {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, - {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, - {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, - {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, - {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, - {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, - {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, - {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, - {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, - {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, - {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, - {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, - {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, - {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, - {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, - {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, - {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, - {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, - {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, - {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, - {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, - {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, - {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, - {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, - {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, - {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, - {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, - {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, - {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, - {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, - {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, - {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, - {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, - {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, - {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, - {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, - {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, - {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, - {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, - {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, - {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, - {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, - {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, - {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, - {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, - {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, - {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, - {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, - {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, - {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, - {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, - {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, - {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, - {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, - {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, - {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, - {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, - {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, - {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, - {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, - {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, - {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, - {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, - {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, - {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, - {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, - {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, - {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, - {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, - {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, - {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, - {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, - {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, - {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, - {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, - {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, - {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, - {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, - {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, - {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, - {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, - {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, - {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, - {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, - {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, - {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, - {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, - {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, - {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, - {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, - {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, - {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, - {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, - {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, - {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, - {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, - {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, - {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, - {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, - {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, - {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, - {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, - {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, - {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, - {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, - {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, - {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, - {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, - {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, - {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, - {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, - {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, - {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, - {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, - {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, - {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, - {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, - {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, - {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, - {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, - {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}; +static int edgeTable[256] = { + 0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, + 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, 0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, + 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, 0x230, + 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, + 0xe3a, 0xf33, 0xc39, 0xd30, 0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, + 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, 0x460, 0x569, + 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, + 0x963, 0xa69, 0xb60, 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc, + 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, 0x650, 0x759, 0x453, + 0x55a, 0x256, 0x35f, 0x55, 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, + 0x859, 0x950, 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc, 0xfcc, + 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, 0x8c0, 0x9c9, 0xac3, 0xbca, + 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, + 0x7c0, 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55, + 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, + 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, + 0x66, 0x76a, 0x663, 0x569, 0x460, 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, + 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0, 0xd30, + 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, + 0x13a, 0x33, 0x339, 0x230, 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, + 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190, 0xf00, 0xe09, + 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, + 0x203, 0x109, 0x0}; +static signed char triTable[256][16] = { + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, + {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, + {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, + {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, + {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, + {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, + {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, + {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, + {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, + {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, + {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, + {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, + {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, + {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, + {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, + {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, + {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, + {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, + {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, + {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, + {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, + {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, + {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, + {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, + {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, + {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, + {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, + {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, + {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, + {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, + {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, + {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, + {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, + {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, + {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, + {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, + {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, + {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, + {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, + {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, + {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, + {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, + {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, + {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, + {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, + {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, + {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, + {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, + {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, + {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, + {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, + {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, + {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, + {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, + {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, + {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, + {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, + {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, + {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, + {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, + {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, + {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, + {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, + {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, + {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, + {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, + {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, + {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, + {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, + {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, + {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, + {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, + {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, + {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, + {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, + {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, + {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, + {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, + {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, + {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, + {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, + {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, + {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, + {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, + {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, + {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, + {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, + {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, + {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, + {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, + {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, + {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, + {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, + {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, + {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, + {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, + {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}; //-------------------------------------------------------------------------------------------------------- -class TriLinPoly{ - /* Compute a tri-linear polynomial within a given cube (i,j,k) x (i+1,j+1,k+1) +class TriLinPoly { + /* Compute a tri-linear polynomial within a given cube (i,j,k) x (i+1,j+1,k+1) * Values are provided at the corners in CubeValues * x,y,z must be defined on [0,1] where the length of the cube edge is one */ - int ic,jc,kc; - double a,b,c,d,e,f,g,h; - double x,y,z; + int ic, jc, kc; + double a, b, c, d, e, f, g, h; + double x, y, z; + public: - DoubleArray Corners; + DoubleArray Corners; - TriLinPoly(){ - Corners.resize(2,2,2); - } - ~TriLinPoly(){ - } - // Assign the polynomial within a cube from a mesh - void assign(DoubleArray &A, int i, int j, int k){ - // Save the cube indices - ic=i; jc=j; kc=k; - - // local copy of the cube values - Corners(0,0,0) = A(i,j,k); - Corners(1,0,0) = A(i+1,j,k); - Corners(0,1,0) = A(i,j+1,k); - Corners(1,1,0) = A(i+1,j+1,k); - Corners(0,0,1) = A(i,j,k+1); - Corners(1,0,1) = A(i+1,j,k+1); - Corners(0,1,1) = A(i,j+1,k+1); - Corners(1,1,1) = A(i+1,j+1,k+1); + TriLinPoly() { Corners.resize(2, 2, 2); } + ~TriLinPoly() {} + // Assign the polynomial within a cube from a mesh + void assign(DoubleArray &A, int i, int j, int k) { + // Save the cube indices + ic = i; + jc = j; + kc = k; - // coefficients of the tri-linear approximation - a = Corners(0,0,0); - b = Corners(1,0,0)-a; - c = Corners(0,1,0)-a; - d = Corners(0,0,1)-a; - e = Corners(1,1,0)-a-b-c; - f = Corners(1,0,1)-a-b-d; - g = Corners(0,1,1)-a-c-d; - h = Corners(1,1,1)-a-b-c-d-e-f-g; - } - - // Assign polynomial based on values manually assigned to Corners - void assign(){ - ic=0; jc=0; kc=0; + // local copy of the cube values + Corners(0, 0, 0) = A(i, j, k); + Corners(1, 0, 0) = A(i + 1, j, k); + Corners(0, 1, 0) = A(i, j + 1, k); + Corners(1, 1, 0) = A(i + 1, j + 1, k); + Corners(0, 0, 1) = A(i, j, k + 1); + Corners(1, 0, 1) = A(i + 1, j, k + 1); + Corners(0, 1, 1) = A(i, j + 1, k + 1); + Corners(1, 1, 1) = A(i + 1, j + 1, k + 1); - // coefficients of the tri-linear approximation - a = Corners(0,0,0); - b = Corners(1,0,0)-a; - c = Corners(0,1,0)-a; - d = Corners(0,0,1)-a; - e = Corners(1,1,0)-a-b-c; - f = Corners(1,0,1)-a-b-d; - g = Corners(0,1,1)-a-c-d; - h = Corners(1,1,1)-a-b-c-d-e-f-g; - } + // coefficients of the tri-linear approximation + a = Corners(0, 0, 0); + b = Corners(1, 0, 0) - a; + c = Corners(0, 1, 0) - a; + d = Corners(0, 0, 1) - a; + e = Corners(1, 1, 0) - a - b - c; + f = Corners(1, 0, 1) - a - b - d; + g = Corners(0, 1, 1) - a - c - d; + h = Corners(1, 1, 1) - a - b - c - d - e - f - g; + } - // Evaluate the polynomial at a point - double eval(Point P){ - double returnValue; - x = P.x - 1.0*ic; - y = P.y - 1.0*jc; - z = P.z - 1.0*kc; - returnValue = a + b*x + c*y+d*z + e*x*y + f*x*z + g*y*z + h*x*y*z; - return returnValue; - } - + // Assign polynomial based on values manually assigned to Corners + void assign() { + ic = 0; + jc = 0; + kc = 0; + + // coefficients of the tri-linear approximation + a = Corners(0, 0, 0); + b = Corners(1, 0, 0) - a; + c = Corners(0, 1, 0) - a; + d = Corners(0, 0, 1) - a; + e = Corners(1, 1, 0) - a - b - c; + f = Corners(1, 0, 1) - a - b - d; + g = Corners(0, 1, 1) - a - c - d; + h = Corners(1, 1, 1) - a - b - c - d - e - f - g; + } + + // Evaluate the polynomial at a point + double eval(Point P) { + double returnValue; + x = P.x - 1.0 * ic; + y = P.y - 1.0 * jc; + z = P.z - 1.0 * kc; + returnValue = a + b * x + c * y + d * z + e * x * y + f * x * z + + g * y * z + h * x * y * z; + return returnValue; + } }; //-------------------------------------------------------------------------------------------------- /* @@ -571,13 +565,14 @@ inline Point NEWTON(Point x0, DoubleArray &F, double &v,DoubleArray &S, int i, */ //-------------------------------------------------------------------------------------------------- -inline bool vertexcheck(Point &P, int n, int pos, DTMutableList &cellvertices){ +inline bool vertexcheck(Point &P, int n, int pos, + DTMutableList &cellvertices) { // returns true if P is a new vertex (one previously unencountered bool V = 1; - int i = pos-n; - for (i = pos-n; i < pos; i++){ - if ( P == cellvertices(i)){ + int i = pos - n; + for (i = pos - n; i < pos; i++) { + if (P == cellvertices(i)) { V = 0; } } @@ -587,21 +582,19 @@ inline bool vertexcheck(Point &P, int n, int pos, DTMutableList &cellvert //-------------------------------------------------------------------------------------------------- -inline bool ShareSide( Point &A, Point &B) -{ +inline bool ShareSide(Point &A, Point &B) { // returns true if points A and B share an x,y, or z coordinate bool l = 0; - if ( A.x != B.x && A.y != B.y && A.z != B.z){ - l=0; - } - else{ - if (floor(A.x)==A.x && floor(B.x)==B.x && A.x==B.x) { + if (A.x != B.x && A.y != B.y && A.z != B.z) { + l = 0; + } else { + if (floor(A.x) == A.x && floor(B.x) == B.x && A.x == B.x) { l = 1; } - if (floor(A.y)==A.y && floor(B.y)==B.y && A.y==B.y) { + if (floor(A.y) == A.y && floor(B.y) == B.y && A.y == B.y) { l = 1; } - if (floor(A.z)==A.z && floor(B.z)==B.z && A.z==B.z) { + if (floor(A.z) == A.z && floor(B.z) == B.z && A.z == B.z) { l = 1; } } @@ -610,358 +603,418 @@ inline bool ShareSide( Point &A, Point &B) } //-------------------------------------------------------------------------------------------------- -inline bool Interface( DoubleArray &A, const double v, int i, int j, int k){ +inline bool Interface(DoubleArray &A, const double v, int i, int j, int k) { // returns true if grid cell i, j, k contains a section of the interface bool Y = 0; - if ((A(i,j,k)-v)*(A(i+1,j,k)-v) < 0 ){ - Y=1; - + if ((A(i, j, k) - v) * (A(i + 1, j, k) - v) < 0) { + Y = 1; } // 2 - if ((A(i+1,j,k)-v)*(A(i+1,j+1,k)-v) < 0){ - Y=1; + if ((A(i + 1, j, k) - v) * (A(i + 1, j + 1, k) - v) < 0) { + Y = 1; } //3 - if ((A(i+1,j+1,k)-v)*(A(i,j+1,k)-v) < 0){ - Y=1; + if ((A(i + 1, j + 1, k) - v) * (A(i, j + 1, k) - v) < 0) { + Y = 1; } //4 - if ((A(i,j+1,k)-v)*(A(i,j,k)-v) < 0 ){ - Y=1; + if ((A(i, j + 1, k) - v) * (A(i, j, k) - v) < 0) { + Y = 1; } //5 - if ((A(i,j,k)-v)*(A(i,j,k+1)-v) < 0 ){ - Y=1; + if ((A(i, j, k) - v) * (A(i, j, k + 1) - v) < 0) { + Y = 1; } //6 - if ((A(i+1,j,k)-v)*(A(i+1,j,k+1)-v) < 0 ){ - Y=1; + if ((A(i + 1, j, k) - v) * (A(i + 1, j, k + 1) - v) < 0) { + Y = 1; } //7 - if ((A(i+1,j+1,k)-v)*(A(i+1,j+1,k+1)-v) < 0 ){ - Y=1; + if ((A(i + 1, j + 1, k) - v) * (A(i + 1, j + 1, k + 1) - v) < 0) { + Y = 1; } //8 - if ((A(i,j+1,k)-v)*(A(i,j+1,k+1)-v) < 0 ){ - Y=1; + if ((A(i, j + 1, k) - v) * (A(i, j + 1, k + 1) - v) < 0) { + Y = 1; } //9 - if ((A(i,j,k+1)-v)*(A(i+1,j,k+1)-v) < 0 ){ - Y=1; + if ((A(i, j, k + 1) - v) * (A(i + 1, j, k + 1) - v) < 0) { + Y = 1; } //10 - if ((A(i+1,j,k+1)-v)*(A(i+1,j+1,k+1)-v) < 0 ){ - Y=1; + if ((A(i + 1, j, k + 1) - v) * (A(i + 1, j + 1, k + 1) - v) < 0) { + Y = 1; } //11 - if ((A(i+1,j+1,k+1)-v)*(A(i,j+1,k+1)-v) < 0 ){ - Y=1; + if ((A(i + 1, j + 1, k + 1) - v) * (A(i, j + 1, k + 1) - v) < 0) { + Y = 1; } //12 - if ((A(i,j+1,k+1)-v)*(A(i,j,k+1)-v) < 0 ){ - Y=1; + if ((A(i, j + 1, k + 1) - v) * (A(i, j, k + 1) - v) < 0) { + Y = 1; } return Y; } //-------------------------------------------------------------------------------------------------- -inline bool Fluid_Interface( DoubleArray &A, DoubleArray &S, const double v, int i, int j, int k){ +inline bool Fluid_Interface(DoubleArray &A, DoubleArray &S, const double v, + int i, int j, int k) { // returns true if grid cell i, j, k contains a section of the interface bool Y = 0; - if ((A(i,j,k)-v)*(A(i+1,j,k)-v) < 0 && S(i,j,k) > 0 && S(i+1,j,k) > 0){ - Y=1; + if ((A(i, j, k) - v) * (A(i + 1, j, k) - v) < 0 && S(i, j, k) > 0 && + S(i + 1, j, k) > 0) { + Y = 1; } // 2 - if ((A(i+1,j,k)-v)*(A(i+1,j+1,k)-v) < 0 && S(i+1,j,k) > 0 && S(i+1,j+1,k) > 0){ - Y=1; + if ((A(i + 1, j, k) - v) * (A(i + 1, j + 1, k) - v) < 0 && + S(i + 1, j, k) > 0 && S(i + 1, j + 1, k) > 0) { + Y = 1; } //3 - if ((A(i+1,j+1,k)-v)*(A(i,j+1,k)-v) < 0 && S(i+1,j+1,k) > 0 && S(i,j+1,k) > 0){ - Y=1; + if ((A(i + 1, j + 1, k) - v) * (A(i, j + 1, k) - v) < 0 && + S(i + 1, j + 1, k) > 0 && S(i, j + 1, k) > 0) { + Y = 1; } //4 - if ((A(i,j+1,k)-v)*(A(i,j,k)-v) < 0 && S(i,j,k) > 0 && S(i,j+1,k) > 0){ - Y=1; + if ((A(i, j + 1, k) - v) * (A(i, j, k) - v) < 0 && S(i, j, k) > 0 && + S(i, j + 1, k) > 0) { + Y = 1; } //5 - if ((A(i,j,k)-v)*(A(i,j,k+1)-v) < 0 && S(i,j,k) > 0 && S(i,j,k+1) > 0){ - Y=1; + if ((A(i, j, k) - v) * (A(i, j, k + 1) - v) < 0 && S(i, j, k) > 0 && + S(i, j, k + 1) > 0) { + Y = 1; } //6 - if ((A(i+1,j,k)-v)*(A(i+1,j,k+1)-v) < 0 && S(i+1,j,k) > 0 && S(i+1,j,k+1) > 0){ - Y=1; + if ((A(i + 1, j, k) - v) * (A(i + 1, j, k + 1) - v) < 0 && + S(i + 1, j, k) > 0 && S(i + 1, j, k + 1) > 0) { + Y = 1; } //7 - if ((A(i+1,j+1,k)-v)*(A(i+1,j+1,k+1)-v) < 0 && S(i+1,j+1,k) > 0 && S(i+1,j+1,k+1) > 0){ - Y=1; + if ((A(i + 1, j + 1, k) - v) * (A(i + 1, j + 1, k + 1) - v) < 0 && + S(i + 1, j + 1, k) > 0 && S(i + 1, j + 1, k + 1) > 0) { + Y = 1; } //8 - if ((A(i,j+1,k)-v)*(A(i,j+1,k+1)-v) < 0 && S(i,j+1,k) > 0 && S(i,j+1,k+1) > 0){ - Y=1; + if ((A(i, j + 1, k) - v) * (A(i, j + 1, k + 1) - v) < 0 && + S(i, j + 1, k) > 0 && S(i, j + 1, k + 1) > 0) { + Y = 1; } //9 - if ((A(i,j,k+1)-v)*(A(i+1,j,k+1)-v) < 0 && S(i,j,k+1) > 0 && S(i+1,j,k+1) > 0){ - Y=1; + if ((A(i, j, k + 1) - v) * (A(i + 1, j, k + 1) - v) < 0 && + S(i, j, k + 1) > 0 && S(i + 1, j, k + 1) > 0) { + Y = 1; } //10 - if ((A(i+1,j,k+1)-v)*(A(i+1,j+1,k+1)-v) < 0 && S(i+1,j,k+1) > 0 && S(i+1,j+1,k+1) > 0){ - Y=1; + if ((A(i + 1, j, k + 1) - v) * (A(i + 1, j + 1, k + 1) - v) < 0 && + S(i + 1, j, k + 1) > 0 && S(i + 1, j + 1, k + 1) > 0) { + Y = 1; } //11 - if ((A(i+1,j+1,k+1)-v)*(A(i,j+1,k+1)-v) < 0 && S(i+1,j+1,k+1) > 0 && S(i,j+1,k+1) > 0){ - Y=1; + if ((A(i + 1, j + 1, k + 1) - v) * (A(i, j + 1, k + 1) - v) < 0 && + S(i + 1, j + 1, k + 1) > 0 && S(i, j + 1, k + 1) > 0) { + Y = 1; } //12 - if ((A(i,j+1,k+1)-v)*(A(i,j,k+1)-v) < 0 && S(i,j,k+1) > 0 && S(i,j+1,k+1) > 0){ - Y=1; + if ((A(i, j + 1, k + 1) - v) * (A(i, j, k + 1) - v) < 0 && + S(i, j, k + 1) > 0 && S(i, j + 1, k + 1) > 0) { + Y = 1; } return Y; } //-------------------------------------------------------------------------------------------------- -inline bool Solid( DoubleArray &A, int i, int j, int k){ +inline bool Solid(DoubleArray &A, int i, int j, int k) { bool X = 0; // return 0 if there is no solid phase in grid cell i,j,k - if (A(i,j,k) == 0){ + if (A(i, j, k) == 0) { X = 1; } - if (A(i+1,j,k) == 0){ + if (A(i + 1, j, k) == 0) { X = 1; } - if (A(i,j+1,k) == 0){ + if (A(i, j + 1, k) == 0) { X = 1; } - if (A(i,j,k+1) == 0){ + if (A(i, j, k + 1) == 0) { X = 1; } - if (A(i+1,j+1,k) == 0){ + if (A(i + 1, j + 1, k) == 0) { X = 1; } - if (A(i+1,j,k+1) == 0){ + if (A(i + 1, j, k + 1) == 0) { X = 1; } - if (A(i,j+1,k+1) == 0){ + if (A(i, j + 1, k + 1) == 0) { X = 1; } - if (A(i+1,j+1,k+1) == 0){ + if (A(i + 1, j + 1, k + 1) == 0) { X = 1; } return X; } //------------------------------------------------------------------------------- -inline Point VertexInterp(const Point &p1, const Point &p2, double valp1, double valp2) -{ +inline Point VertexInterp(const Point &p1, const Point &p2, double valp1, + double valp2) { return (p1 + (-valp1 / (valp2 - valp1)) * (p2 - p1)); } //------------------------------------------------------------------------------- -inline void SolidMarchingCubes(DoubleArray &A, const double &v, DoubleArray &B, const double &isovalue, - int i,int j,int k, int m, int n, int o, DTMutableList - &cellvertices, int &lengthvertices, IntArray &Triangles, int &nTris, - DoubleArray &values) -{ +inline void SolidMarchingCubes(DoubleArray &A, const double &v, DoubleArray &B, + const double &isovalue, int i, int j, int k, + int m, int n, int o, + DTMutableList &cellvertices, + int &lengthvertices, IntArray &Triangles, + int &nTris, DoubleArray &values) { - NULL_USE( isovalue ); - NULL_USE( m ); - NULL_USE( n ); - NULL_USE( o ); - - // THIS SUBROUTINE COMPUTES THE VERTICES FOR THE SOLID PHASE IN - // A PARTICULAR GRID CELL, THEN ARRANGES THEM INTO TRIANGLES - // ALSO ORGANIZES THE LIST OF VALUES TO CORRESPOND WITH VERTEX LIST - - NULL_USE( v ); + NULL_USE(isovalue); + NULL_USE(m); + NULL_USE(n); + NULL_USE(o); - //int N = 0; - Point P,Q; - Point PlaceHolder; - //int pos = lengthvertices; - double temp; - Point C0,C1,C2,C3,C4,C5,C6,C7; - - int TriangleCount; + // THIS SUBROUTINE COMPUTES THE VERTICES FOR THE SOLID PHASE IN + // A PARTICULAR GRID CELL, THEN ARRANGES THEM INTO TRIANGLES + // ALSO ORGANIZES THE LIST OF VALUES TO CORRESPOND WITH VERTEX LIST + + NULL_USE(v); + + //int N = 0; + Point P, Q; + Point PlaceHolder; + //int pos = lengthvertices; + double temp; + Point C0, C1, C2, C3, C4, C5, C6, C7; + + int TriangleCount; int NewVertexCount; - int CubeIndex; - - Point VertexList[12]; - Point NewVertexList[12]; - double ValueList[12]; - double NewValueList[12]; - int LocalRemap[12]; - - // int m; int n; int o; - //int p; int q; - - // Values from array 'A' at the cube corners - double CubeValues[8]; - CubeValues[0] = A(i,j,k); - CubeValues[1] = A(i+1,j,k); - CubeValues[2] = A(i+1,j+1,k); - CubeValues[3] = A(i,j+1,k); - CubeValues[4] = A(i,j,k+1); - CubeValues[5] = A(i+1,j,k+1); - CubeValues[6] = A(i+1,j+1,k+1); - CubeValues[7] = A(i,j+1,k+1); - - // Values from array 'B' at the cube corners - double CubeValues_B[8]; - CubeValues_B[0] = B(i,j,k); - CubeValues_B[1] = B(i+1,j,k); - CubeValues_B[2] = B(i+1,j+1,k); - CubeValues_B[3] = B(i,j+1,k); - CubeValues_B[4] = B(i,j,k+1); - CubeValues_B[5] = B(i+1,j,k+1); - CubeValues_B[6] = B(i+1,j+1,k+1); - CubeValues_B[7] = B(i,j+1,k+1); + int CubeIndex; - // Points corresponding to cube corners - C0.x = 0.0; C0.y = 0.0; C0.z = 0.0; - C1.x = 1.0; C1.y = 0.0; C1.z = 0.0; - C2.x = 1.0; C2.y = 1.0; C2.z = 0.0; - C3.x = 0.0; C3.y = 1.0; C3.z = 0.0; - C4.x = 0.0; C4.y = 0.0; C4.z = 1.0; - C5.x = 1.0; C5.y = 0.0; C5.z = 1.0; - C6.x = 1.0; C6.y = 1.0; C6.z = 1.0; - C7.x = 0.0; C7.y = 1.0; C7.z = 1.0; - //Determine the index into the edge table which + Point VertexList[12]; + Point NewVertexList[12]; + double ValueList[12]; + double NewValueList[12]; + int LocalRemap[12]; + + // int m; int n; int o; + //int p; int q; + + // Values from array 'A' at the cube corners + double CubeValues[8]; + CubeValues[0] = A(i, j, k); + CubeValues[1] = A(i + 1, j, k); + CubeValues[2] = A(i + 1, j + 1, k); + CubeValues[3] = A(i, j + 1, k); + CubeValues[4] = A(i, j, k + 1); + CubeValues[5] = A(i + 1, j, k + 1); + CubeValues[6] = A(i + 1, j + 1, k + 1); + CubeValues[7] = A(i, j + 1, k + 1); + + // Values from array 'B' at the cube corners + double CubeValues_B[8]; + CubeValues_B[0] = B(i, j, k); + CubeValues_B[1] = B(i + 1, j, k); + CubeValues_B[2] = B(i + 1, j + 1, k); + CubeValues_B[3] = B(i, j + 1, k); + CubeValues_B[4] = B(i, j, k + 1); + CubeValues_B[5] = B(i + 1, j, k + 1); + CubeValues_B[6] = B(i + 1, j + 1, k + 1); + CubeValues_B[7] = B(i, j + 1, k + 1); + + // Points corresponding to cube corners + C0.x = 0.0; + C0.y = 0.0; + C0.z = 0.0; + C1.x = 1.0; + C1.y = 0.0; + C1.z = 0.0; + C2.x = 1.0; + C2.y = 1.0; + C2.z = 0.0; + C3.x = 0.0; + C3.y = 1.0; + C3.z = 0.0; + C4.x = 0.0; + C4.y = 0.0; + C4.z = 1.0; + C5.x = 1.0; + C5.y = 0.0; + C5.z = 1.0; + C6.x = 1.0; + C6.y = 1.0; + C6.z = 1.0; + C7.x = 0.0; + C7.y = 1.0; + C7.z = 1.0; + //Determine the index into the edge table which //tells us which vertices are inside of the surface CubeIndex = 0; - if (CubeValues[0] < 0.0f) CubeIndex |= 1; - if (CubeValues[1] < 0.0f) CubeIndex |= 2; - if (CubeValues[2] < 0.0f) CubeIndex |= 4; - if (CubeValues[3] < 0.0f) CubeIndex |= 8; - if (CubeValues[4] < 0.0f) CubeIndex |= 16; - if (CubeValues[5] < 0.0f) CubeIndex |= 32; - if (CubeValues[6] < 0.0f) CubeIndex |= 64; - if (CubeValues[7] < 0.0f) CubeIndex |= 128; - + if (CubeValues[0] < 0.0f) + CubeIndex |= 1; + if (CubeValues[1] < 0.0f) + CubeIndex |= 2; + if (CubeValues[2] < 0.0f) + CubeIndex |= 4; + if (CubeValues[3] < 0.0f) + CubeIndex |= 8; + if (CubeValues[4] < 0.0f) + CubeIndex |= 16; + if (CubeValues[5] < 0.0f) + CubeIndex |= 32; + if (CubeValues[6] < 0.0f) + CubeIndex |= 64; + if (CubeValues[7] < 0.0f) + CubeIndex |= 128; + //Find the vertices where the surface intersects the cube - if (edgeTable[CubeIndex] & 1){ - P = VertexInterp(C0,C1,CubeValues[0],CubeValues[1]); + if (edgeTable[CubeIndex] & 1) { + P = VertexInterp(C0, C1, CubeValues[0], CubeValues[1]); VertexList[0] = P; - Q = C0; - temp = sqrt((P.x-Q.x)*(P.x-Q.x)+(P.y-Q.y)*(P.y-Q.y)+(P.z-Q.z)*(P.z-Q.z)); - ValueList[0] = CubeValues_B[0] + temp*(CubeValues_B[1]-CubeValues_B[0]); - } - if (edgeTable[CubeIndex] & 2){ - P = VertexInterp(C1,C2,CubeValues[1],CubeValues[2]); + Q = C0; + temp = sqrt((P.x - Q.x) * (P.x - Q.x) + (P.y - Q.y) * (P.y - Q.y) + + (P.z - Q.z) * (P.z - Q.z)); + ValueList[0] = + CubeValues_B[0] + temp * (CubeValues_B[1] - CubeValues_B[0]); + } + if (edgeTable[CubeIndex] & 2) { + P = VertexInterp(C1, C2, CubeValues[1], CubeValues[2]); VertexList[1] = P; - Q = C1; - temp = sqrt((P.x-Q.x)*(P.x-Q.x)+(P.y-Q.y)*(P.y-Q.y)+(P.z-Q.z)*(P.z-Q.z)); - ValueList[1] = CubeValues_B[1] + temp*(CubeValues_B[2]-CubeValues_B[1]); - } - if (edgeTable[CubeIndex] & 4){ - P = VertexInterp(C2,C3,CubeValues[2],CubeValues[3]); - VertexList[2] = P; - Q = C2; - temp = sqrt((P.x-Q.x)*(P.x-Q.x)+(P.y-Q.y)*(P.y-Q.y)+(P.z-Q.z)*(P.z-Q.z)); - ValueList[2] = CubeValues_B[2] + temp*(CubeValues_B[3]-CubeValues_B[2]); - } - if (edgeTable[CubeIndex] & 8){ - P = VertexInterp(C3,C0,CubeValues[3],CubeValues[0]); - VertexList[3] = P; - Q = C3; - temp = sqrt((P.x-Q.x)*(P.x-Q.x)+(P.y-Q.y)*(P.y-Q.y)+(P.z-Q.z)*(P.z-Q.z)); - ValueList[3] = CubeValues_B[3] + temp*(CubeValues_B[0]-CubeValues_B[3]); - } - if (edgeTable[CubeIndex] & 16){ - P = VertexInterp(C4,C5,CubeValues[4],CubeValues[5]); - VertexList[4] = P; - Q = C4; - temp = sqrt((P.x-Q.x)*(P.x-Q.x)+(P.y-Q.y)*(P.y-Q.y)+(P.z-Q.z)*(P.z-Q.z)); - ValueList[4] = CubeValues_B[4] + temp*(CubeValues_B[5]-CubeValues_B[4]); - } - if (edgeTable[CubeIndex] & 32){ - P = VertexInterp(C5,C6,CubeValues[5],CubeValues[6]); + Q = C1; + temp = sqrt((P.x - Q.x) * (P.x - Q.x) + (P.y - Q.y) * (P.y - Q.y) + + (P.z - Q.z) * (P.z - Q.z)); + ValueList[1] = + CubeValues_B[1] + temp * (CubeValues_B[2] - CubeValues_B[1]); + } + if (edgeTable[CubeIndex] & 4) { + P = VertexInterp(C2, C3, CubeValues[2], CubeValues[3]); + VertexList[2] = P; + Q = C2; + temp = sqrt((P.x - Q.x) * (P.x - Q.x) + (P.y - Q.y) * (P.y - Q.y) + + (P.z - Q.z) * (P.z - Q.z)); + ValueList[2] = + CubeValues_B[2] + temp * (CubeValues_B[3] - CubeValues_B[2]); + } + if (edgeTable[CubeIndex] & 8) { + P = VertexInterp(C3, C0, CubeValues[3], CubeValues[0]); + VertexList[3] = P; + Q = C3; + temp = sqrt((P.x - Q.x) * (P.x - Q.x) + (P.y - Q.y) * (P.y - Q.y) + + (P.z - Q.z) * (P.z - Q.z)); + ValueList[3] = + CubeValues_B[3] + temp * (CubeValues_B[0] - CubeValues_B[3]); + } + if (edgeTable[CubeIndex] & 16) { + P = VertexInterp(C4, C5, CubeValues[4], CubeValues[5]); + VertexList[4] = P; + Q = C4; + temp = sqrt((P.x - Q.x) * (P.x - Q.x) + (P.y - Q.y) * (P.y - Q.y) + + (P.z - Q.z) * (P.z - Q.z)); + ValueList[4] = + CubeValues_B[4] + temp * (CubeValues_B[5] - CubeValues_B[4]); + } + if (edgeTable[CubeIndex] & 32) { + P = VertexInterp(C5, C6, CubeValues[5], CubeValues[6]); VertexList[5] = P; - Q = C5; - temp = sqrt((P.x-Q.x)*(P.x-Q.x)+(P.y-Q.y)*(P.y-Q.y)+(P.z-Q.z)*(P.z-Q.z)); - ValueList[5] = CubeValues_B[5] + temp*(CubeValues_B[6]-CubeValues_B[5]); - } - if (edgeTable[CubeIndex] & 64){ - P = VertexInterp(C6,C7,CubeValues[6],CubeValues[7]); - VertexList[6] = P; - Q = C6; - temp = sqrt((P.x-Q.x)*(P.x-Q.x)+(P.y-Q.y)*(P.y-Q.y)+(P.z-Q.z)*(P.z-Q.z)); - ValueList[6] = CubeValues_B[6] + temp*(CubeValues_B[7]-CubeValues_B[6]); - } - if (edgeTable[CubeIndex] & 128){ - P = VertexInterp(C7,C4,CubeValues[7],CubeValues[4]); - VertexList[7] = P; - Q = C7; - temp = sqrt((P.x-Q.x)*(P.x-Q.x)+(P.y-Q.y)*(P.y-Q.y)+(P.z-Q.z)*(P.z-Q.z)); - ValueList[7] = CubeValues_B[7] + temp*(CubeValues_B[4]-CubeValues_B[7]); - } - if (edgeTable[CubeIndex] & 256){ - P = VertexInterp(C0,C4,CubeValues[0],CubeValues[4]); - VertexList[8] = P; - Q = C0; - temp = sqrt((P.x-Q.x)*(P.x-Q.x)+(P.y-Q.y)*(P.y-Q.y)+(P.z-Q.z)*(P.z-Q.z)); - ValueList[8] = CubeValues_B[0] + temp*(CubeValues_B[4]-CubeValues_B[0]); - } - if (edgeTable[CubeIndex] & 512){ - P = VertexInterp(C1,C5,CubeValues[1],CubeValues[5]); - VertexList[9] = P; - Q = C1; - temp = sqrt((P.x-Q.x)*(P.x-Q.x)+(P.y-Q.y)*(P.y-Q.y)+(P.z-Q.z)*(P.z-Q.z)); - ValueList[9] = CubeValues_B[1] + temp*(CubeValues_B[5]-CubeValues_B[1]); - } - if (edgeTable[CubeIndex] & 1024){ - P = VertexInterp(C2,C6,CubeValues[2],CubeValues[6]); + Q = C5; + temp = sqrt((P.x - Q.x) * (P.x - Q.x) + (P.y - Q.y) * (P.y - Q.y) + + (P.z - Q.z) * (P.z - Q.z)); + ValueList[5] = + CubeValues_B[5] + temp * (CubeValues_B[6] - CubeValues_B[5]); + } + if (edgeTable[CubeIndex] & 64) { + P = VertexInterp(C6, C7, CubeValues[6], CubeValues[7]); + VertexList[6] = P; + Q = C6; + temp = sqrt((P.x - Q.x) * (P.x - Q.x) + (P.y - Q.y) * (P.y - Q.y) + + (P.z - Q.z) * (P.z - Q.z)); + ValueList[6] = + CubeValues_B[6] + temp * (CubeValues_B[7] - CubeValues_B[6]); + } + if (edgeTable[CubeIndex] & 128) { + P = VertexInterp(C7, C4, CubeValues[7], CubeValues[4]); + VertexList[7] = P; + Q = C7; + temp = sqrt((P.x - Q.x) * (P.x - Q.x) + (P.y - Q.y) * (P.y - Q.y) + + (P.z - Q.z) * (P.z - Q.z)); + ValueList[7] = + CubeValues_B[7] + temp * (CubeValues_B[4] - CubeValues_B[7]); + } + if (edgeTable[CubeIndex] & 256) { + P = VertexInterp(C0, C4, CubeValues[0], CubeValues[4]); + VertexList[8] = P; + Q = C0; + temp = sqrt((P.x - Q.x) * (P.x - Q.x) + (P.y - Q.y) * (P.y - Q.y) + + (P.z - Q.z) * (P.z - Q.z)); + ValueList[8] = + CubeValues_B[0] + temp * (CubeValues_B[4] - CubeValues_B[0]); + } + if (edgeTable[CubeIndex] & 512) { + P = VertexInterp(C1, C5, CubeValues[1], CubeValues[5]); + VertexList[9] = P; + Q = C1; + temp = sqrt((P.x - Q.x) * (P.x - Q.x) + (P.y - Q.y) * (P.y - Q.y) + + (P.z - Q.z) * (P.z - Q.z)); + ValueList[9] = + CubeValues_B[1] + temp * (CubeValues_B[5] - CubeValues_B[1]); + } + if (edgeTable[CubeIndex] & 1024) { + P = VertexInterp(C2, C6, CubeValues[2], CubeValues[6]); VertexList[10] = P; - Q = C2; - temp = sqrt((P.x-Q.x)*(P.x-Q.x)+(P.y-Q.y)*(P.y-Q.y)+(P.z-Q.z)*(P.z-Q.z)); - ValueList[10] = CubeValues_B[2] + temp*(CubeValues_B[6]-CubeValues_B[2]); - } - if (edgeTable[CubeIndex] & 2048){ - P = VertexInterp(C3,C7,CubeValues[3],CubeValues[7]); + Q = C2; + temp = sqrt((P.x - Q.x) * (P.x - Q.x) + (P.y - Q.y) * (P.y - Q.y) + + (P.z - Q.z) * (P.z - Q.z)); + ValueList[10] = + CubeValues_B[2] + temp * (CubeValues_B[6] - CubeValues_B[2]); + } + if (edgeTable[CubeIndex] & 2048) { + P = VertexInterp(C3, C7, CubeValues[3], CubeValues[7]); VertexList[11] = P; - Q = C3; - temp = sqrt((P.x-Q.x)*(P.x-Q.x)+(P.y-Q.y)*(P.y-Q.y)+(P.z-Q.z)*(P.z-Q.z)); - ValueList[11] = CubeValues_B[3] + temp*(CubeValues_B[7]-CubeValues_B[3]); - } - - NewVertexCount=0; - for (int idx=0;idx<12;idx++) + Q = C3; + temp = sqrt((P.x - Q.x) * (P.x - Q.x) + (P.y - Q.y) * (P.y - Q.y) + + (P.z - Q.z) * (P.z - Q.z)); + ValueList[11] = + CubeValues_B[3] + temp * (CubeValues_B[7] - CubeValues_B[3]); + } + + NewVertexCount = 0; + for (int idx = 0; idx < 12; idx++) LocalRemap[idx] = -1; - - for (int idx=0;triTable[CubeIndex][idx]!=-1;idx++) - { - if(LocalRemap[triTable[CubeIndex][idx]] == -1) - { - NewVertexList[NewVertexCount] = VertexList[triTable[CubeIndex][idx]]; + + for (int idx = 0; triTable[CubeIndex][idx] != -1; idx++) { + if (LocalRemap[triTable[CubeIndex][idx]] == -1) { + NewVertexList[NewVertexCount] = + VertexList[triTable[CubeIndex][idx]]; NewValueList[NewVertexCount] = ValueList[triTable[CubeIndex][idx]]; LocalRemap[triTable[CubeIndex][idx]] = NewVertexCount; NewVertexCount++; } } - - for (int idx=0;idx - &cellvertices, int &lengthvertices, IntArray &Tlist, int &nTris, - DoubleArray &values) -{ - NULL_USE( isovalue ); - NULL_USE( m ); - NULL_USE( n ); - NULL_USE( o ); +inline void SOL_SURF(DoubleArray &A, const double &v, DoubleArray &B, + const double &isovalue, int i, int j, int k, int m, int n, + int o, DTMutableList &cellvertices, + int &lengthvertices, IntArray &Tlist, int &nTris, + DoubleArray &values) { + NULL_USE(isovalue); + NULL_USE(m); + NULL_USE(n); + NULL_USE(o); - // THIS SUBROUTINE COMPUTES THE VERTICES FOR THE SOLID PHASE IN - // A PARTICULAR GRID CELL, THEN ARRANGES THEM INTO TRIANGLES - // ALSO ORGANIZES THE LIST OF VALUES TO CORRESPOND WITH VERTEX LIST + // THIS SUBROUTINE COMPUTES THE VERTICES FOR THE SOLID PHASE IN + // A PARTICULAR GRID CELL, THEN ARRANGES THEM INTO TRIANGLES + // ALSO ORGANIZES THE LIST OF VALUES TO CORRESPOND WITH VERTEX LIST - int N = 0; - Point P; - Point PlaceHolder; - int pos = lengthvertices; - float temp; + int N = 0; + Point P; + Point PlaceHolder; + int pos = lengthvertices; + float temp; + // int m; int n; int o; + int p; + int q; - // int m; int n; int o; - int p; int q; + // for each leg of the triangle, see if a vertex exists + // if so, find the vertex, and perform an extrapolation - // for each leg of the triangle, see if a vertex exists - // if so, find the vertex, and perform an extrapolation - - // Go over each corner -- check to see if the corners are themselves vertices - //1 - if (A(i,j,k) == v){ - P.x = i; - P.y = j; - P.z = k; - values(pos) = B(i,j,k); - cellvertices(pos++) = P; - N++; - } - //2 - if (A(i+1,j,k) == v){ - P.x = i+1; - P.y = j; - P.z = k; - values(pos) = B(i+1,j,k); - cellvertices(pos++) = P; - N++; - } - //3 - if (A(i+1,j+1,k) == v){ - P.x = i+1; - P.y = j+1; - P.z = k; - values(pos) = B(i+1,j+1,k); - cellvertices(pos++) = P; - N++; - } - //4 - if (A(i,j+1,k) == v){ - P.x = i; - P.y = j+1; - P.z = k; - values(pos) = B(i,j+1,k); - cellvertices(pos++) = P; - N++; - } - //5 - if (A(i,j,k+1) == v){ - P.x = i; - P.y = j; - P.z = k+1; - values(pos) = B(i,j,k+1); - cellvertices(pos++) = P; - N++; - } - //6 - if (A(i+1,j,k+1) == v){ - P.x = i+1; - P.y = j; - P.z = k+1; - values(pos) = B(i+1,j,k+1); - cellvertices(pos++) = P; - N++; - } - //7 - if (A(i+1,j+1,k+1) == v){ - P.x = i+1; - P.y = j+1; - P.z = k+1; - values(pos) = B(i+1,j+1,k+1); - cellvertices(pos++) = P; - N++; - } - //8 - if (A(i,j+1,k+1) == v){ - P.x = i; - P.y = j+1; - P.z = k+1; - values(pos) = B(i,j+1,k+1); - cellvertices(pos++) = P; - N++; - } + // Go over each corner -- check to see if the corners are themselves vertices + //1 + if (A(i, j, k) == v) { + P.x = i; + P.y = j; + P.z = k; + values(pos) = B(i, j, k); + cellvertices(pos++) = P; + N++; + } + //2 + if (A(i + 1, j, k) == v) { + P.x = i + 1; + P.y = j; + P.z = k; + values(pos) = B(i + 1, j, k); + cellvertices(pos++) = P; + N++; + } + //3 + if (A(i + 1, j + 1, k) == v) { + P.x = i + 1; + P.y = j + 1; + P.z = k; + values(pos) = B(i + 1, j + 1, k); + cellvertices(pos++) = P; + N++; + } + //4 + if (A(i, j + 1, k) == v) { + P.x = i; + P.y = j + 1; + P.z = k; + values(pos) = B(i, j + 1, k); + cellvertices(pos++) = P; + N++; + } + //5 + if (A(i, j, k + 1) == v) { + P.x = i; + P.y = j; + P.z = k + 1; + values(pos) = B(i, j, k + 1); + cellvertices(pos++) = P; + N++; + } + //6 + if (A(i + 1, j, k + 1) == v) { + P.x = i + 1; + P.y = j; + P.z = k + 1; + values(pos) = B(i + 1, j, k + 1); + cellvertices(pos++) = P; + N++; + } + //7 + if (A(i + 1, j + 1, k + 1) == v) { + P.x = i + 1; + P.y = j + 1; + P.z = k + 1; + values(pos) = B(i + 1, j + 1, k + 1); + cellvertices(pos++) = P; + N++; + } + //8 + if (A(i, j + 1, k + 1) == v) { + P.x = i; + P.y = j + 1; + P.z = k + 1; + values(pos) = B(i, j + 1, k + 1); + cellvertices(pos++) = P; + N++; + } // Go through each side, compute P for sides of box spiraling up - - if ((A(i,j,k)-v)*(A(i+1,j,k)-v) < 0) - { - P.x = i + (A(i,j,k)-v)/(A(i,j,k)-A(i+1,j,k)); + if ((A(i, j, k) - v) * (A(i + 1, j, k) - v) < 0) { + P.x = i + (A(i, j, k) - v) / (A(i, j, k) - A(i + 1, j, k)); P.y = j; P.z = k; - // compute extrapolated fluid value at P - // if ( A(i,j,k) > v){ - // values(pos) = EXTRAP(B, isovalue, i,j,k,4, P); - // } - // else{ - // values(pos) = EXTRAP(B,isovalue, i+1,j,k, 1, P); - // } - // Interpolate value at P using padded mesh B - values(pos) = B(i,j,k)*(1-P.x+i)+B(i+1,j,k)*(P.x-i); + // compute extrapolated fluid value at P + // if ( A(i,j,k) > v){ + // values(pos) = EXTRAP(B, isovalue, i,j,k,4, P); + // } + // else{ + // values(pos) = EXTRAP(B,isovalue, i+1,j,k, 1, P); + // } + // Interpolate value at P using padded mesh B + values(pos) = B(i, j, k) * (1 - P.x + i) + B(i + 1, j, k) * (P.x - i); - cellvertices(pos++) = P; + cellvertices(pos++) = P; N++; - } + } // 2 - if ((A(i+1,j,k)-v)*(A(i+1,j+1,k)-v) < 0) - { - P.x = i+1; - P.y = j + (A(i+1,j,k)-v)/(A(i+1,j,k)-A(i+1,j+1,k)); + if ((A(i + 1, j, k) - v) * (A(i + 1, j + 1, k) - v) < 0) { + P.x = i + 1; + P.y = j + (A(i + 1, j, k) - v) / (A(i + 1, j, k) - A(i + 1, j + 1, k)); P.z = k; - if (vertexcheck(P, N, pos, cellvertices) == 1){ // P is a new vertex (not counted twice) - // compute extrapolated fluid value at P - // if ( A(i+1,j,k) > v){ - // values(pos) = EXTRAP(B,isovalue, i+1,j,k, 5, P); - // } - // else{ - // values(pos) = EXTRAP(B,isovalue, i+1,j+1,k, 2, P); - // } - // Interpolate value at P using padded mesh B - values(pos) = B(i+1,j,k)*(1-P.y+j)+B(i+1,j+1,k)*(P.y-j); - cellvertices(pos++) = P; + if (vertexcheck(P, N, pos, cellvertices) == + 1) { // P is a new vertex (not counted twice) + // compute extrapolated fluid value at P + // if ( A(i+1,j,k) > v){ + // values(pos) = EXTRAP(B,isovalue, i+1,j,k, 5, P); + // } + // else{ + // values(pos) = EXTRAP(B,isovalue, i+1,j+1,k, 2, P); + // } + // Interpolate value at P using padded mesh B + values(pos) = + B(i + 1, j, k) * (1 - P.y + j) + B(i + 1, j + 1, k) * (P.y - j); + cellvertices(pos++) = P; N++; } - } + } //3 - if ((A(i+1,j+1,k)-v)*(A(i,j+1,k)-v) < 0) - { - P.x = i + (A(i,j+1,k)-v) / (A(i,j+1,k)-A(i+1,j+1,k)); - P.y = j+1; + if ((A(i + 1, j + 1, k) - v) * (A(i, j + 1, k) - v) < 0) { + P.x = i + (A(i, j + 1, k) - v) / (A(i, j + 1, k) - A(i + 1, j + 1, k)); + P.y = j + 1; P.z = k; - if (vertexcheck(P, N, pos, cellvertices) == 1){ // P is a new vertex (not counted twice) - // compute extrapolated fluid value at P - // if ( A(i+1,j+1,k) > v){ - // values(pos) = EXTRAP(B,isovalue, i+1,j+1,k, 1, P); - // } - // else{ - // values(pos) = EXTRAP(B,isovalue, i,j+1,k, 4, P); - // } - // Interpolate value at P using padded mesh B - values(pos) = B(i,j+1,k)*(1-P.x+i)+B(i+1,j+1,k)*(P.x-i); - cellvertices(pos++) = P; + if (vertexcheck(P, N, pos, cellvertices) == + 1) { // P is a new vertex (not counted twice) + // compute extrapolated fluid value at P + // if ( A(i+1,j+1,k) > v){ + // values(pos) = EXTRAP(B,isovalue, i+1,j+1,k, 1, P); + // } + // else{ + // values(pos) = EXTRAP(B,isovalue, i,j+1,k, 4, P); + // } + // Interpolate value at P using padded mesh B + values(pos) = + B(i, j + 1, k) * (1 - P.x + i) + B(i + 1, j + 1, k) * (P.x - i); + cellvertices(pos++) = P; N++; } - } + } //4 - if ((A(i,j+1,k)-v)*(A(i,j,k)-v) < 0) - { + if ((A(i, j + 1, k) - v) * (A(i, j, k) - v) < 0) { P.x = i; - P.y = j + (A(i,j,k)-v) / (A(i,j,k)-A(i,j+1,k)); + P.y = j + (A(i, j, k) - v) / (A(i, j, k) - A(i, j + 1, k)); P.z = k; - if (vertexcheck(P, N, pos, cellvertices) == 1){ // P is a new vertex (not counted twice) - // compute extrapolated fluid value at P - // if ( A(i,j,k) > v){ - // values(pos) = EXTRAP(B,isovalue, i,j,k, 5, P); - // } - // else{ - // values(pos) = EXTRAP(B,isovalue, i,j+1,k, 2, P); - // } - // Interpolate value at P using padded mesh B - values(pos) = B(i,j,k)*(1-P.y+j)+B(i,j+1,k)*(P.y-j); - cellvertices(pos++) = P; + if (vertexcheck(P, N, pos, cellvertices) == + 1) { // P is a new vertex (not counted twice) + // compute extrapolated fluid value at P + // if ( A(i,j,k) > v){ + // values(pos) = EXTRAP(B,isovalue, i,j,k, 5, P); + // } + // else{ + // values(pos) = EXTRAP(B,isovalue, i,j+1,k, 2, P); + // } + // Interpolate value at P using padded mesh B + values(pos) = + B(i, j, k) * (1 - P.y + j) + B(i, j + 1, k) * (P.y - j); + cellvertices(pos++) = P; N++; } - } + } //5 - if ((A(i,j,k)-v)*(A(i,j,k+1)-v) < 0) - { + if ((A(i, j, k) - v) * (A(i, j, k + 1) - v) < 0) { P.x = i; P.y = j; - P.z = k + (A(i,j,k)-v) / (A(i,j,k)-A(i,j,k+1)); - if (vertexcheck(P, N, pos, cellvertices) == 1){ // P is a new vertex (not counted twice) - // compute extrapolated fluid value at P - // if ( A(i,j,k) > v){ - // values(pos) = EXTRAP(B,isovalue, i,j,k, 6, P); - // } - // else{ - // values(pos) = EXTRAP(B,isovalue, i,j,k+1, 3, P); - // } + P.z = k + (A(i, j, k) - v) / (A(i, j, k) - A(i, j, k + 1)); + if (vertexcheck(P, N, pos, cellvertices) == + 1) { // P is a new vertex (not counted twice) + // compute extrapolated fluid value at P + // if ( A(i,j,k) > v){ + // values(pos) = EXTRAP(B,isovalue, i,j,k, 6, P); + // } + // else{ + // values(pos) = EXTRAP(B,isovalue, i,j,k+1, 3, P); + // } - // Interpolate value at P using padded mesh B - values(pos) = B(i,j,k)*(1-P.z+k)+B(i,j,k+1)*(P.z-k); - cellvertices(pos++) = P; + // Interpolate value at P using padded mesh B + values(pos) = + B(i, j, k) * (1 - P.z + k) + B(i, j, k + 1) * (P.z - k); + cellvertices(pos++) = P; N++; } - } + } //6 - if ((A(i+1,j,k)-v)*(A(i+1,j,k+1)-v) < 0) - { - P.x = i+1; + if ((A(i + 1, j, k) - v) * (A(i + 1, j, k + 1) - v) < 0) { + P.x = i + 1; P.y = j; - P.z = k + (A(i+1,j,k)-v) / (A(i+1,j,k)-A(i+1,j,k+1)); - if (vertexcheck(P, N, pos, cellvertices) == 1){ // P is a new vertex (not counted twice) - // compute extrapolated fluid value at P - // if ( A(i+1,j,k) > v){ - // values(pos) = EXTRAP(B,isovalue, i+1,j,k, 6, P); - // } - // else{ - // values(pos) = EXTRAP(B,isovalue, i+1,j,k+1, 3, P); - // } + P.z = k + (A(i + 1, j, k) - v) / (A(i + 1, j, k) - A(i + 1, j, k + 1)); + if (vertexcheck(P, N, pos, cellvertices) == + 1) { // P is a new vertex (not counted twice) + // compute extrapolated fluid value at P + // if ( A(i+1,j,k) > v){ + // values(pos) = EXTRAP(B,isovalue, i+1,j,k, 6, P); + // } + // else{ + // values(pos) = EXTRAP(B,isovalue, i+1,j,k+1, 3, P); + // } - // Interpolate value at P using padded mesh B - values(pos) = B(i+1,j,k)*(1-P.z+k)+B(i+1,j,k+1)*(P.z-k); - cellvertices(pos++) = P; + // Interpolate value at P using padded mesh B + values(pos) = + B(i + 1, j, k) * (1 - P.z + k) + B(i + 1, j, k + 1) * (P.z - k); + cellvertices(pos++) = P; N++; } - } + } //7 - if ((A(i+1,j+1,k)-v)*(A(i+1,j+1,k+1)-v) < 0) - { - P.x = i+1; - P.y = j+1; - P.z = k + (A(i+1,j+1,k)-v) / (A(i+1,j+1,k)-A(i+1,j+1,k+1)); - if (vertexcheck(P, N, pos, cellvertices) == 1){ // P is a new vertex (not counted twice) - // compute extrapolated fluid value at P - // if ( A(i+1,j+1,k) > v){ - // values(pos) = EXTRAP(B,isovalue, i+1,j+1,k, 6, P); - // } - // else{ - // values(pos) = EXTRAP(B,isovalue, i+1,j+1,k+1, 3, P); - // } + if ((A(i + 1, j + 1, k) - v) * (A(i + 1, j + 1, k + 1) - v) < 0) { + P.x = i + 1; + P.y = j + 1; + P.z = k + (A(i + 1, j + 1, k) - v) / + (A(i + 1, j + 1, k) - A(i + 1, j + 1, k + 1)); + if (vertexcheck(P, N, pos, cellvertices) == + 1) { // P is a new vertex (not counted twice) + // compute extrapolated fluid value at P + // if ( A(i+1,j+1,k) > v){ + // values(pos) = EXTRAP(B,isovalue, i+1,j+1,k, 6, P); + // } + // else{ + // values(pos) = EXTRAP(B,isovalue, i+1,j+1,k+1, 3, P); + // } - // Interpolate value at P using padded mesh B - values(pos) = B(i+1,j+1,k)*(1-P.z+k)+B(i+1,j+1,k+1)*(P.z-k); - cellvertices(pos++) = P; + // Interpolate value at P using padded mesh B + values(pos) = B(i + 1, j + 1, k) * (1 - P.z + k) + + B(i + 1, j + 1, k + 1) * (P.z - k); + cellvertices(pos++) = P; N++; } - } + } //8 - if ((A(i,j+1,k)-v)*(A(i,j+1,k+1)-v) < 0) - { + if ((A(i, j + 1, k) - v) * (A(i, j + 1, k + 1) - v) < 0) { P.x = i; - P.y = j+1; - P.z = k + (A(i,j+1,k)-v) / (A(i,j+1,k)-A(i,j+1,k+1)); - if (vertexcheck(P, N, pos, cellvertices) == 1){ // P is a new vertex (not counted twice) - // compute extrapolated fluid value at P - // if ( A(i,j+1,k) > v){ - // values(pos) = EXTRAP(B,isovalue, i,j+1,k, 6, P); - // } - // else{ - // values(pos) = EXTRAP(B,isovalue, i,j+1,k+1, 3, P); - // } + P.y = j + 1; + P.z = k + (A(i, j + 1, k) - v) / (A(i, j + 1, k) - A(i, j + 1, k + 1)); + if (vertexcheck(P, N, pos, cellvertices) == + 1) { // P is a new vertex (not counted twice) + // compute extrapolated fluid value at P + // if ( A(i,j+1,k) > v){ + // values(pos) = EXTRAP(B,isovalue, i,j+1,k, 6, P); + // } + // else{ + // values(pos) = EXTRAP(B,isovalue, i,j+1,k+1, 3, P); + // } - // Interpolate value at P using padded mesh B - values(pos) = B(i,j+1,k)*(1-P.z+k)+B(i,j+1,k+1)*(P.z-k); - cellvertices(pos++) = P; + // Interpolate value at P using padded mesh B + values(pos) = + B(i, j + 1, k) * (1 - P.z + k) + B(i, j + 1, k + 1) * (P.z - k); + cellvertices(pos++) = P; N++; } - } + } //9 - if ((A(i,j,k+1)-v)*(A(i+1,j,k+1)-v) < 0) - { - P.x = i + (A(i,j,k+1)-v) / (A(i,j,k+1)-A(i+1,j,k+1)); + if ((A(i, j, k + 1) - v) * (A(i + 1, j, k + 1) - v) < 0) { + P.x = i + (A(i, j, k + 1) - v) / (A(i, j, k + 1) - A(i + 1, j, k + 1)); P.y = j; - P.z = k+1; - if (vertexcheck(P, N, pos, cellvertices) == 1){ // P is a new vertex (not counted twice) - // compute extrapolated fluid value at P - // if ( A(i,j,k+1) > v){ - // values(pos) = EXTRAP(B,isovalue, i,j,k+1, 4, P); - // } - // else{ - // values(pos) = EXTRAP(B,isovalue, i+1,j,k+1, 1, P); - // } + P.z = k + 1; + if (vertexcheck(P, N, pos, cellvertices) == + 1) { // P is a new vertex (not counted twice) + // compute extrapolated fluid value at P + // if ( A(i,j,k+1) > v){ + // values(pos) = EXTRAP(B,isovalue, i,j,k+1, 4, P); + // } + // else{ + // values(pos) = EXTRAP(B,isovalue, i+1,j,k+1, 1, P); + // } - // Interpolate value at P using padded mesh B - values(pos) = B(i,j,k+1)*(1-P.x+i)+B(i+1,j,k+1)*(P.x-i); - cellvertices(pos++) = P; + // Interpolate value at P using padded mesh B + values(pos) = + B(i, j, k + 1) * (1 - P.x + i) + B(i + 1, j, k + 1) * (P.x - i); + cellvertices(pos++) = P; N++; } - } + } //10 - if ((A(i+1,j,k+1)-v)*(A(i+1,j+1,k+1)-v) < 0) - { - P.x = i+1; - P.y = j + (A(i+1,j,k+1)-v) / (A(i+1,j,k+1)-A(i+1,j+1,k+1)); - P.z = k+1; - if (vertexcheck(P, N, pos, cellvertices) == 1){ // P is a new vertex (not counted twice) - // compute extrapolated fluid value at P - // if ( A(i+1,j,k+1) > v){ - // values(pos) = EXTRAP(B,isovalue, i+1,j,k+1, 5, P); - // } - // else{ - // values(pos) = EXTRAP(B,isovalue, i+1,j+1,k+1, 2, P); - // } + if ((A(i + 1, j, k + 1) - v) * (A(i + 1, j + 1, k + 1) - v) < 0) { + P.x = i + 1; + P.y = j + (A(i + 1, j, k + 1) - v) / + (A(i + 1, j, k + 1) - A(i + 1, j + 1, k + 1)); + P.z = k + 1; + if (vertexcheck(P, N, pos, cellvertices) == + 1) { // P is a new vertex (not counted twice) + // compute extrapolated fluid value at P + // if ( A(i+1,j,k+1) > v){ + // values(pos) = EXTRAP(B,isovalue, i+1,j,k+1, 5, P); + // } + // else{ + // values(pos) = EXTRAP(B,isovalue, i+1,j+1,k+1, 2, P); + // } - // Interpolate value at P using padded mesh B - values(pos) = B(i+1,j,k+1)*(1-P.y+j)+B(i+1,j+1,k+1)*(P.y-j); - cellvertices(pos++) = P; - N++; + // Interpolate value at P using padded mesh B + values(pos) = B(i + 1, j, k + 1) * (1 - P.y + j) + + B(i + 1, j + 1, k + 1) * (P.y - j); + cellvertices(pos++) = P; + N++; } - } + } //11 - if ((A(i+1,j+1,k+1)-v)*(A(i,j+1,k+1)-v) < 0) - { - P.x = i+(A(i,j+1,k+1)-v) / (A(i,j+1,k+1)-A(i+1,j+1,k+1)); - P.y = j+1; - P.z = k+1; - if (vertexcheck(P, N, pos, cellvertices) == 1){ // P is a new vertex (not counted twice) - // compute extrapolated fluid value at P - // if ( A(i,j+1,k+1) > v){ - // values(pos) = EXTRAP(B,isovalue, i,j+1,k+1, 4, P); - // } - // else{ - // values(pos) = EXTRAP(B,isovalue, i+1,j+1,k+1, 1, P); - // } + if ((A(i + 1, j + 1, k + 1) - v) * (A(i, j + 1, k + 1) - v) < 0) { + P.x = i + (A(i, j + 1, k + 1) - v) / + (A(i, j + 1, k + 1) - A(i + 1, j + 1, k + 1)); + P.y = j + 1; + P.z = k + 1; + if (vertexcheck(P, N, pos, cellvertices) == + 1) { // P is a new vertex (not counted twice) + // compute extrapolated fluid value at P + // if ( A(i,j+1,k+1) > v){ + // values(pos) = EXTRAP(B,isovalue, i,j+1,k+1, 4, P); + // } + // else{ + // values(pos) = EXTRAP(B,isovalue, i+1,j+1,k+1, 1, P); + // } - // Interpolate value at P using padded mesh B - values(pos) = B(i,j+1,k+1)*(1-P.x+i)+B(i+1,j+1,k+1)*(P.x-i); - cellvertices(pos++) = P; + // Interpolate value at P using padded mesh B + values(pos) = B(i, j + 1, k + 1) * (1 - P.x + i) + + B(i + 1, j + 1, k + 1) * (P.x - i); + cellvertices(pos++) = P; N++; } - } + } //12 - if ((A(i,j+1,k+1)-v)*(A(i,j,k+1)-v) < 0) - { + if ((A(i, j + 1, k + 1) - v) * (A(i, j, k + 1) - v) < 0) { P.x = i; - P.y = j + (A(i,j,k+1)-v) / (A(i,j,k+1)-A(i,j+1,k+1)); - P.z = k+1; - if (vertexcheck(P, N, pos, cellvertices) == 1){ // P is a new vertex (not counted twice) - // compute extrapolated fluid value at P - // if ( A(i,j,k+1) > v){ - // values(pos) = EXTRAP(B,isovalue, i,j,k+1, 5, P); - // } - // else{ - // values(pos) = EXTRAP(B,isovalue, i,j+1,k+1, 2, P); - // } + P.y = j + (A(i, j, k + 1) - v) / (A(i, j, k + 1) - A(i, j + 1, k + 1)); + P.z = k + 1; + if (vertexcheck(P, N, pos, cellvertices) == + 1) { // P is a new vertex (not counted twice) + // compute extrapolated fluid value at P + // if ( A(i,j,k+1) > v){ + // values(pos) = EXTRAP(B,isovalue, i,j,k+1, 5, P); + // } + // else{ + // values(pos) = EXTRAP(B,isovalue, i,j+1,k+1, 2, P); + // } - // Interpolate value at P using padded mesh B - values(pos) = B(i,j,k+1)*(1-P.y+j)+B(i,j+1,k+1)*(P.y-j); - cellvertices(pos++) = P; + // Interpolate value at P using padded mesh B + values(pos) = + B(i, j, k + 1) * (1 - P.y + j) + B(i, j + 1, k + 1) * (P.y - j); + cellvertices(pos++) = P; N++; } - } + } - lengthvertices = pos; + lengthvertices = pos; - // * * * ARRANGE VERTICES SO THAT NEIGHBORS SHARE A FACE * * * - // * * * PERFORM SAME OPERATIONS TO THE LIST OF VALUES * * * + // * * * ARRANGE VERTICES SO THAT NEIGHBORS SHARE A FACE * * * + // * * * PERFORM SAME OPERATIONS TO THE LIST OF VALUES * * * - for (q = pos-N; q < pos-2; q++) { - for (o = q+2; o < pos-1; o++) { + for (q = pos - N; q < pos - 2; q++) { + for (o = q + 2; o < pos - 1; o++) { if (ShareSide(cellvertices(q), cellvertices(o)) == 1) { - PlaceHolder = cellvertices(q+1); - cellvertices(q+1) = cellvertices(o); + PlaceHolder = cellvertices(q + 1); + cellvertices(q + 1) = cellvertices(o); cellvertices(o) = PlaceHolder; - temp = values(q+1); - values(q+1) = values(o); - values(o) = temp; + temp = values(q + 1); + values(q + 1) = values(o); + values(o) = temp; } } - // make sure other neighbor of vertex 1 is in last spot - if (q == pos-N){ - for (p = q+2; p < pos-1; p++){ - if (ShareSide(cellvertices(q), cellvertices(p)) == 1){ - PlaceHolder = cellvertices(pos-1); - cellvertices(pos-1) = cellvertices(p); + // make sure other neighbor of vertex 1 is in last spot + if (q == pos - N) { + for (p = q + 2; p < pos - 1; p++) { + if (ShareSide(cellvertices(q), cellvertices(p)) == 1) { + PlaceHolder = cellvertices(pos - 1); + cellvertices(pos - 1) = cellvertices(p); cellvertices(p) = PlaceHolder; - temp = values(pos-1); - values(pos-1) = values(p); - values(p) = temp; + temp = values(pos - 1); + values(pos - 1) = values(p); + values(p) = temp; } } } - if ( ShareSide(cellvertices(pos-2), cellvertices(pos-3)) != 1 ){ - if (ShareSide( cellvertices(pos-3), cellvertices(pos-1)) == 1 && ShareSide( cellvertices(pos-N), - cellvertices(pos-2)) == 1 ){ - PlaceHolder = cellvertices(pos-2); - cellvertices(pos-2) = cellvertices(pos-1); - cellvertices(pos-1) = PlaceHolder; + if (ShareSide(cellvertices(pos - 2), cellvertices(pos - 3)) != 1) { + if (ShareSide(cellvertices(pos - 3), cellvertices(pos - 1)) == 1 && + ShareSide(cellvertices(pos - N), cellvertices(pos - 2)) == 1) { + PlaceHolder = cellvertices(pos - 2); + cellvertices(pos - 2) = cellvertices(pos - 1); + cellvertices(pos - 1) = PlaceHolder; - temp = values(pos-2); - values(pos-2) = values(pos-1); - values(pos-1) = temp; + temp = values(pos - 2); + values(pos - 2) = values(pos - 1); + values(pos - 1) = temp; } } - if ( ShareSide(cellvertices(pos-1), cellvertices(pos-2)) != 1 ){ - if (ShareSide( cellvertices(pos-3), cellvertices(pos-1)) == 1 && ShareSide(cellvertices(pos-4), - cellvertices(pos-2)) == 1 ){ - PlaceHolder = cellvertices(pos-3); - cellvertices(pos-3) = cellvertices(pos-2); - cellvertices(pos-2) = PlaceHolder; + if (ShareSide(cellvertices(pos - 1), cellvertices(pos - 2)) != 1) { + if (ShareSide(cellvertices(pos - 3), cellvertices(pos - 1)) == 1 && + ShareSide(cellvertices(pos - 4), cellvertices(pos - 2)) == 1) { + PlaceHolder = cellvertices(pos - 3); + cellvertices(pos - 3) = cellvertices(pos - 2); + cellvertices(pos - 2) = PlaceHolder; - temp = values(pos-3); - values(pos-3) = values(pos-2); - values(pos-2) = temp; + temp = values(pos - 3); + values(pos - 3) = values(pos - 2); + values(pos - 2) = temp; } - if (ShareSide( cellvertices(pos-N+1), cellvertices(pos-3)) == 1 && ShareSide(cellvertices(pos-1), - cellvertices(pos-N+1)) == 1 ){ - PlaceHolder = cellvertices(pos-2); - cellvertices(pos-2) = cellvertices(pos-N+1); - cellvertices(pos-N+1) = PlaceHolder; + if (ShareSide(cellvertices(pos - N + 1), cellvertices(pos - 3)) == + 1 && + ShareSide(cellvertices(pos - 1), cellvertices(pos - N + 1)) == + 1) { + PlaceHolder = cellvertices(pos - 2); + cellvertices(pos - 2) = cellvertices(pos - N + 1); + cellvertices(pos - N + 1) = PlaceHolder; - temp = values(pos-2); - values(pos-2) = values(pos-N+1); - values(pos-N+1) = temp; + temp = values(pos - 2); + values(pos - 2) = values(pos - N + 1); + values(pos - N + 1) = temp; } } - if ( ShareSide(cellvertices(pos-N), cellvertices(pos-N+1)) != 1 ){ - if (ShareSide( cellvertices(pos-N), cellvertices(pos-2)) == 1 && ShareSide(cellvertices(pos-1), - cellvertices(pos-N+1)) == 1){ - PlaceHolder = cellvertices(pos-1); - cellvertices(pos-1) = cellvertices(pos-N); - cellvertices(pos-N) = PlaceHolder; + if (ShareSide(cellvertices(pos - N), cellvertices(pos - N + 1)) != 1) { + if (ShareSide(cellvertices(pos - N), cellvertices(pos - 2)) == 1 && + ShareSide(cellvertices(pos - 1), cellvertices(pos - N + 1)) == + 1) { + PlaceHolder = cellvertices(pos - 1); + cellvertices(pos - 1) = cellvertices(pos - N); + cellvertices(pos - N) = PlaceHolder; - temp = values(pos-1); - values(pos-1) = values(pos-N); - values(pos-N) = temp; + temp = values(pos - 1); + values(pos - 1) = values(pos - N); + values(pos - N) = temp; } } } + // * * * ESTABLISH TRIANGLE CONNECTIONS * * * - // * * * ESTABLISH TRIANGLE CONNECTIONS * * * - - for (p=pos-N+2; p &local_sol_pts, int &n_local_sol_pts, double isovalue, @@ -1558,940 +1625,978 @@ inline void TRIM(DTMutableList &local_sol_pts, int &n_local_sol_pts, DTMutableList &local_nws_pts, int &n_local_nws_pts) */ //------------------------------------------------------------------------------- -inline void TRIM(DTMutableList &local_sol_pts, int &n_local_sol_pts, double isovalue, - IntArray &local_sol_tris, int &n_local_sol_tris, - DTMutableList &ns_pts, int &n_ns_pts, IntArray &ns_tris, - int &n_ns_tris, DTMutableList &ws_pts, int &n_ws_pts, - IntArray &ws_tris, int &n_ws_tris, DoubleArray &values, - DTMutableList &local_nws_pts, int &n_local_nws_pts) -{ - // Trim the local solid surface - - int map_ws; - int map_ns; - int p; int q; - - int a; int b; int c; - Point A; Point B; Point C; - Point D; Point E; - Point P; - - bool all_to_ns = 1; - // Check to see if all triangles in the cell are in ns_surface - for (q=0; q < n_local_sol_pts; q++){ - if ( values(q) < isovalue && all_to_ns == 1){ - all_to_ns = 0; - } - } - bool all_to_ws = 1; - // Check to see if all triangles in the cell are in ws surface - for (q=0; q < n_local_sol_pts; q++){ - if ( values(q) > isovalue && all_to_ws == 1){ - all_to_ws = 0; - } - } - - if (all_to_ws == 1){ - map_ws = n_ws_pts; - map_ws = 0; - for ( p=0; p isovalue && values(b) > isovalue && values(c) > isovalue ){ - // Triangle is in ns surface - // Add points - ns_pts(n_ns_pts++) = A; - ns_pts(n_ns_pts++) = B; - ns_pts(n_ns_pts++) = C; - // Add triangles - ns_tris(0,n_ns_tris) = n_ns_pts-3; - ns_tris(1,n_ns_tris) = n_ns_pts-2; - ns_tris(2,n_ns_tris) = n_ns_pts-1; - n_ns_tris++; - } - else if (values(a) < isovalue && values(b) < isovalue && values(c) < isovalue ){ - // Triangle is in ws surface - // Add points - ws_pts(n_ws_pts++) = A; - ws_pts(n_ws_pts++) = B; - ws_pts(n_ws_pts++) = C; - // Add triangles - ws_tris(0,n_ws_tris) = n_ws_pts-3; - ws_tris(1,n_ws_tris) = n_ws_pts-2; - ws_tris(2,n_ws_tris) = n_ws_pts-1; - n_ws_tris++; - } - else { - // Triangle contains common line - - //////////////////////////////////////// - ///////// FIND THE COMMON LINE ///////// - //////////////////////////////////////// - - if ( (values(a)-isovalue)*(values(b)-isovalue) < 0){ - // compute common line vertex - // P = A + (values(a) - isovalue)/(values(a)-values(b))*(B-A); - P.x = A.x + (values(a) - isovalue)/(values(a)-values(b))*(B.x-A.x); - P.y = A.y + (values(a) - isovalue)/(values(a)-values(b))*(B.y-A.y); - P.z = A.z + (values(a) - isovalue)/(values(a)-values(b))*(B.z-A.z); +inline void TRIM(DTMutableList &local_sol_pts, int &n_local_sol_pts, + double isovalue, IntArray &local_sol_tris, + int &n_local_sol_tris, DTMutableList &ns_pts, + int &n_ns_pts, IntArray &ns_tris, int &n_ns_tris, + DTMutableList &ws_pts, int &n_ws_pts, IntArray &ws_tris, + int &n_ws_tris, DoubleArray &values, + DTMutableList &local_nws_pts, int &n_local_nws_pts) { + // Trim the local solid surface - local_nws_pts(n_local_nws_pts++) = P; -/* if ( n_local_nws_pts == 0 ){ - local_nws_pts(n_local_nws_pts++) = P; - } - else if ( P != local_nws_pts(n_local_nws_pts-1) ){ - local_nws_pts(n_local_nws_pts++) = P; - } -*/ } - if ( (values(b)-isovalue)*(values(c)-isovalue) < 0){ - // compute common line vertex -// P = B + (values(b) - isovalue)/(values(b)-values(c))*(C-B); - P.x = B.x + (values(b) - isovalue)/(values(b)-values(c))*(C.x-B.x); - P.y = B.y + (values(b) - isovalue)/(values(b)-values(c))*(C.y-B.y); - P.z = B.z + (values(b) - isovalue)/(values(b)-values(c))*(C.z-B.z); - - local_nws_pts(n_local_nws_pts++) = P; -/* if ( n_local_nws_pts == 0 ){ - local_nws_pts(n_local_nws_pts++) = P; - } - else if ( P != local_nws_pts(n_local_nws_pts-1) ){ - local_nws_pts(n_local_nws_pts++) = P; - } -*/ } - if ( (values(a)-isovalue)*(values(c)-isovalue) < 0){ - // compute common line vertex -// P = A + (values(a) - isovalue)/(values(a)-values(c))*(C-A); - P.x = A.x + (values(a) - isovalue)/(values(a)-values(c))*(C.x-A.x); - P.y = A.y + (values(a) - isovalue)/(values(a)-values(c))*(C.y-A.y); - P.z = A.z + (values(a) - isovalue)/(values(a)-values(c))*(C.z-A.z); - local_nws_pts(n_local_nws_pts++) = P; - } - // Store common line points as D and E - D = local_nws_pts(n_local_nws_pts-2); - E = local_nws_pts(n_local_nws_pts-1); - // Construct the new triangles - if ( (values(a)-isovalue)*(values(b)-isovalue) < 0 && - (values(b)-isovalue)*(values(c)-isovalue) < 0){ - if (values(b) > isovalue){ - // Points - ns_pts(n_ns_pts++) = B; - ns_pts(n_ns_pts++) = D; - ns_pts(n_ns_pts++) = E; - // Triangles - ns_tris(0,n_ns_tris) = n_ns_pts-3; // B - ns_tris(1,n_ns_tris) = n_ns_pts-2; // D - ns_tris(2,n_ns_tris) = n_ns_pts-1; // E - n_ns_tris++; - // Points - ws_pts(n_ws_pts++) = A; - ws_pts(n_ws_pts++) = C; - ws_pts(n_ws_pts++) = D; - ws_pts(n_ws_pts++) = E; - // Triangles (A,C,D),(C,D,E) - ws_tris(0,n_ws_tris) = n_ws_pts-4; // A - ws_tris(1,n_ws_tris) = n_ws_pts-3; // C - ws_tris(2,n_ws_tris) = n_ws_pts-2; // D - n_ws_tris++; - ws_tris(0,n_ws_tris) = n_ws_pts-3; // C - ws_tris(1,n_ws_tris) = n_ws_pts-2; // D - ws_tris(2,n_ws_tris) = n_ws_pts-1; // E - n_ws_tris++; - } - else { - // Points - ws_pts(n_ws_pts++) = B; - ws_pts(n_ws_pts++) = D; - ws_pts(n_ws_pts++) = E; - // Triangles - ws_tris(0,n_ws_tris) = n_ws_pts-3; // B - ws_tris(1,n_ws_tris) = n_ws_pts-2; // D - ws_tris(2,n_ws_tris) = n_ws_pts-1; // E - n_ws_tris++; - // Points - ns_pts(n_ns_pts++) = A; - ns_pts(n_ns_pts++) = C; - ns_pts(n_ns_pts++) = D; - ns_pts(n_ns_pts++) = E; - // Triangles (A,C,D),(C,D,E) - ns_tris(0,n_ns_tris) = n_ns_pts-4; // A - ns_tris(1,n_ns_tris) = n_ns_pts-3; // C - ns_tris(2,n_ns_tris) = n_ns_pts-2; // D - n_ns_tris++; - ns_tris(0,n_ns_tris) = n_ns_pts-3; // C - ns_tris(1,n_ns_tris) = n_ns_pts-1; // E - ns_tris(2,n_ns_tris) = n_ns_pts-2; // D - n_ns_tris++; - } - } - else if ( (values(a)-isovalue)*(values(b)-isovalue) < 0 && - (values(a)-isovalue)*(values(c)-isovalue) < 0){ - if (values(a) > isovalue){ - // Points - ns_pts(n_ns_pts++) = A; - ns_pts(n_ns_pts++) = D; - ns_pts(n_ns_pts++) = E; - // Triangles - ns_tris(0,n_ns_tris) = n_ns_pts-3; // A - ns_tris(1,n_ns_tris) = n_ns_pts-2; // D - ns_tris(2,n_ns_tris) = n_ns_pts-1; // E - n_ns_tris++; - // Points - ws_pts(n_ws_pts++) = B; - ws_pts(n_ws_pts++) = C; - ws_pts(n_ws_pts++) = D; - ws_pts(n_ws_pts++) = E; - // Triangles (B,C,D),(C,D,E) - ws_tris(0,n_ws_tris) = n_ws_pts-4; // B - ws_tris(1,n_ws_tris) = n_ws_pts-2; // D - ws_tris(2,n_ws_tris) = n_ws_pts-3; // C - n_ws_tris++; - ws_tris(0,n_ws_tris) = n_ws_pts-3; // C - ws_tris(1,n_ws_tris) = n_ws_pts-2; // D - ws_tris(2,n_ws_tris) = n_ws_pts-1; // E - n_ws_tris++; - } - else { - // Points - ws_pts(n_ws_pts++) = A; - ws_pts(n_ws_pts++) = D; - ws_pts(n_ws_pts++) = E; - // Triangles - ws_tris(0,n_ws_tris) = n_ws_pts-3; // A - ws_tris(1,n_ws_tris) = n_ws_pts-2; // D - ws_tris(2,n_ws_tris) = n_ws_pts-1; // E - n_ws_tris++; - // Points - ns_pts(n_ns_pts++) = B; - ns_pts(n_ns_pts++) = C; - ns_pts(n_ns_pts++) = D; - ns_pts(n_ns_pts++) = E; - // Triangles (B,C,D),(C,D,E) - ns_tris(0,n_ns_tris) = n_ns_pts-4; // B - ns_tris(1,n_ns_tris) = n_ns_pts-3; // C - ns_tris(2,n_ns_tris) = n_ns_pts-2; // D - n_ns_tris++; - ns_tris(0,n_ns_tris) = n_ns_pts-3; // C - ns_tris(1,n_ns_tris) = n_ns_pts-1; // E - ns_tris(2,n_ns_tris) = n_ns_pts-2; // D - n_ns_tris++; - } - } - else { - if (values(c) > isovalue){ - // Points - ns_pts(n_ns_pts++) = C; - ns_pts(n_ns_pts++) = D; - ns_pts(n_ns_pts++) = E; - // Triangles - ns_tris(0,n_ns_tris) = n_ns_pts-3; // C - ns_tris(1,n_ns_tris) = n_ns_pts-2; // D - ns_tris(2,n_ns_tris) = n_ns_pts-1; // E - n_ns_tris++; - // Points - ws_pts(n_ws_pts++) = A; - ws_pts(n_ws_pts++) = B; - ws_pts(n_ws_pts++) = D; - ws_pts(n_ws_pts++) = E; - // Triangles (A,B,D),(A,D,E) - ws_tris(0,n_ws_tris) = n_ws_pts-4; // A - ws_tris(1,n_ws_tris) = n_ws_pts-3; // B - ws_tris(2,n_ws_tris) = n_ws_pts-2; // D - n_ws_tris++; - ws_tris(0,n_ws_tris) = n_ws_pts-4; // A - ws_tris(1,n_ws_tris) = n_ws_pts-2; // D - ws_tris(2,n_ws_tris) = n_ws_pts-1; // E - n_ws_tris++; - } - else { - // Points - ws_pts(n_ws_pts++) = C; - ws_pts(n_ws_pts++) = D; - ws_pts(n_ws_pts++) = E; - // Triangles - ws_tris(0,n_ws_tris) = n_ws_pts-3; // C - ws_tris(1,n_ws_tris) = n_ws_pts-2; // D - ws_tris(2,n_ws_tris) = n_ws_pts-1; // E - n_ws_tris++; - // Points - ns_pts(n_ns_pts++) = A; - ns_pts(n_ns_pts++) = B; - ns_pts(n_ns_pts++) = D; - ns_pts(n_ns_pts++) = E; - // Triangles (A,B,D),(A,D,E) - ns_tris(0,n_ns_tris) = n_ns_pts-4; // A - ns_tris(1,n_ns_tris) = n_ns_pts-3; // B - ns_tris(2,n_ns_tris) = n_ns_pts-2; // D - n_ns_tris++; - ns_tris(0,n_ns_tris) = n_ns_pts-4; // A - ns_tris(1,n_ns_tris) = n_ns_pts-2; // D - ns_tris(2,n_ns_tris) = n_ns_pts-1; // E - n_ns_tris++; - } - } - } - } - } -} -inline double geomavg_MarchingCubes( DoubleArray &A, double &v, int &i, int &j, int &k, - DTMutableList &nw_pts, int &n_nw_pts, IntArray &nw_tris, - int &n_nw_tris) -{ - int N = 0; // n will be the number of vertices in this grid cell only + int map_ws; + int map_ns; + int p; + int q; + + int a; + int b; + int c; + Point A; + Point B; + Point C; + Point D; + Point E; Point P; - Point pt; + + bool all_to_ns = 1; + // Check to see if all triangles in the cell are in ns_surface + for (q = 0; q < n_local_sol_pts; q++) { + if (values(q) < isovalue && all_to_ns == 1) { + all_to_ns = 0; + } + } + bool all_to_ws = 1; + // Check to see if all triangles in the cell are in ws surface + for (q = 0; q < n_local_sol_pts; q++) { + if (values(q) > isovalue && all_to_ws == 1) { + all_to_ws = 0; + } + } + + if (all_to_ws == 1) { + map_ws = n_ws_pts; + map_ws = 0; + for (p = 0; p < n_local_sol_pts; p++) { + ws_pts(n_ws_pts++) = local_sol_pts(p); + } + for (p = 0; p < n_local_sol_tris; p++) { + ws_tris(0, n_ws_tris) = local_sol_tris(0, p) + map_ws; + ws_tris(1, n_ws_tris) = local_sol_tris(1, p) + map_ws; + ws_tris(2, n_ws_tris) = local_sol_tris(2, p) + map_ws; + n_ws_tris++; + } + } else if (all_to_ns == 1) { + map_ns = n_ns_pts; + map_ns = 0; + for (p = 0; p < n_local_sol_pts; p++) { + ns_pts(n_ns_pts++) = local_sol_pts(p); + } + for (p = 0; p < n_local_sol_tris; p++) { + ns_tris(0, n_ns_tris) = local_sol_tris(0, p) + map_ns; + ns_tris(1, n_ns_tris) = local_sol_tris(1, p) + map_ns; + ns_tris(2, n_ns_tris) = local_sol_tris(2, p) + map_ns; + n_ns_tris++; + } + } else { + // this section of surface contains a common line + map_ns = n_ns_tris; + map_ws = n_ws_tris; + // Go through all triangles + for (p = 0; p < n_local_sol_tris; p++) { + a = local_sol_tris(0, p); + b = local_sol_tris(1, p); + c = local_sol_tris(2, p); + A = local_sol_pts(a); + B = local_sol_pts(b); + C = local_sol_pts(c); + if (values(a) > isovalue && values(b) > isovalue && + values(c) > isovalue) { + // Triangle is in ns surface + // Add points + ns_pts(n_ns_pts++) = A; + ns_pts(n_ns_pts++) = B; + ns_pts(n_ns_pts++) = C; + // Add triangles + ns_tris(0, n_ns_tris) = n_ns_pts - 3; + ns_tris(1, n_ns_tris) = n_ns_pts - 2; + ns_tris(2, n_ns_tris) = n_ns_pts - 1; + n_ns_tris++; + } else if (values(a) < isovalue && values(b) < isovalue && + values(c) < isovalue) { + // Triangle is in ws surface + // Add points + ws_pts(n_ws_pts++) = A; + ws_pts(n_ws_pts++) = B; + ws_pts(n_ws_pts++) = C; + // Add triangles + ws_tris(0, n_ws_tris) = n_ws_pts - 3; + ws_tris(1, n_ws_tris) = n_ws_pts - 2; + ws_tris(2, n_ws_tris) = n_ws_pts - 1; + n_ws_tris++; + } else { + // Triangle contains common line + + //////////////////////////////////////// + ///////// FIND THE COMMON LINE ///////// + //////////////////////////////////////// + + if ((values(a) - isovalue) * (values(b) - isovalue) < 0) { + // compute common line vertex + // P = A + (values(a) - isovalue)/(values(a)-values(b))*(B-A); + P.x = A.x + (values(a) - isovalue) / + (values(a) - values(b)) * (B.x - A.x); + P.y = A.y + (values(a) - isovalue) / + (values(a) - values(b)) * (B.y - A.y); + P.z = A.z + (values(a) - isovalue) / + (values(a) - values(b)) * (B.z - A.z); + + local_nws_pts(n_local_nws_pts++) = P; +/* if ( n_local_nws_pts == 0 ){ + local_nws_pts(n_local_nws_pts++) = P; + } + else if ( P != local_nws_pts(n_local_nws_pts-1) ){ + local_nws_pts(n_local_nws_pts++) = P; + } +*/ } +if ((values(b) - isovalue) * (values(c) - isovalue) < 0) { + // compute common line vertex + // P = B + (values(b) - isovalue)/(values(b)-values(c))*(C-B); + P.x = B.x + (values(b) - isovalue) / (values(b) - values(c)) * (C.x - B.x); + P.y = B.y + (values(b) - isovalue) / (values(b) - values(c)) * (C.y - B.y); + P.z = B.z + (values(b) - isovalue) / (values(b) - values(c)) * (C.z - B.z); + + local_nws_pts(n_local_nws_pts++) = P; +/* if ( n_local_nws_pts == 0 ){ + local_nws_pts(n_local_nws_pts++) = P; + } + else if ( P != local_nws_pts(n_local_nws_pts-1) ){ + local_nws_pts(n_local_nws_pts++) = P; + } +*/ } +if ((values(a) - isovalue) * (values(c) - isovalue) < 0) { + // compute common line vertex + // P = A + (values(a) - isovalue)/(values(a)-values(c))*(C-A); + P.x = A.x + (values(a) - isovalue) / (values(a) - values(c)) * (C.x - A.x); + P.y = A.y + (values(a) - isovalue) / (values(a) - values(c)) * (C.y - A.y); + P.z = A.z + (values(a) - isovalue) / (values(a) - values(c)) * (C.z - A.z); + local_nws_pts(n_local_nws_pts++) = P; +} +// Store common line points as D and E +D = local_nws_pts(n_local_nws_pts - 2); +E = local_nws_pts(n_local_nws_pts - 1); +// Construct the new triangles +if ((values(a) - isovalue) * (values(b) - isovalue) < 0 && + (values(b) - isovalue) * (values(c) - isovalue) < 0) { + if (values(b) > isovalue) { + // Points + ns_pts(n_ns_pts++) = B; + ns_pts(n_ns_pts++) = D; + ns_pts(n_ns_pts++) = E; + // Triangles + ns_tris(0, n_ns_tris) = n_ns_pts - 3; // B + ns_tris(1, n_ns_tris) = n_ns_pts - 2; // D + ns_tris(2, n_ns_tris) = n_ns_pts - 1; // E + n_ns_tris++; + // Points + ws_pts(n_ws_pts++) = A; + ws_pts(n_ws_pts++) = C; + ws_pts(n_ws_pts++) = D; + ws_pts(n_ws_pts++) = E; + // Triangles (A,C,D),(C,D,E) + ws_tris(0, n_ws_tris) = n_ws_pts - 4; // A + ws_tris(1, n_ws_tris) = n_ws_pts - 3; // C + ws_tris(2, n_ws_tris) = n_ws_pts - 2; // D + n_ws_tris++; + ws_tris(0, n_ws_tris) = n_ws_pts - 3; // C + ws_tris(1, n_ws_tris) = n_ws_pts - 2; // D + ws_tris(2, n_ws_tris) = n_ws_pts - 1; // E + n_ws_tris++; + } else { + // Points + ws_pts(n_ws_pts++) = B; + ws_pts(n_ws_pts++) = D; + ws_pts(n_ws_pts++) = E; + // Triangles + ws_tris(0, n_ws_tris) = n_ws_pts - 3; // B + ws_tris(1, n_ws_tris) = n_ws_pts - 2; // D + ws_tris(2, n_ws_tris) = n_ws_pts - 1; // E + n_ws_tris++; + // Points + ns_pts(n_ns_pts++) = A; + ns_pts(n_ns_pts++) = C; + ns_pts(n_ns_pts++) = D; + ns_pts(n_ns_pts++) = E; + // Triangles (A,C,D),(C,D,E) + ns_tris(0, n_ns_tris) = n_ns_pts - 4; // A + ns_tris(1, n_ns_tris) = n_ns_pts - 3; // C + ns_tris(2, n_ns_tris) = n_ns_pts - 2; // D + n_ns_tris++; + ns_tris(0, n_ns_tris) = n_ns_pts - 3; // C + ns_tris(1, n_ns_tris) = n_ns_pts - 1; // E + ns_tris(2, n_ns_tris) = n_ns_pts - 2; // D + n_ns_tris++; + } +} else if ((values(a) - isovalue) * (values(b) - isovalue) < 0 && + (values(a) - isovalue) * (values(c) - isovalue) < 0) { + if (values(a) > isovalue) { + // Points + ns_pts(n_ns_pts++) = A; + ns_pts(n_ns_pts++) = D; + ns_pts(n_ns_pts++) = E; + // Triangles + ns_tris(0, n_ns_tris) = n_ns_pts - 3; // A + ns_tris(1, n_ns_tris) = n_ns_pts - 2; // D + ns_tris(2, n_ns_tris) = n_ns_pts - 1; // E + n_ns_tris++; + // Points + ws_pts(n_ws_pts++) = B; + ws_pts(n_ws_pts++) = C; + ws_pts(n_ws_pts++) = D; + ws_pts(n_ws_pts++) = E; + // Triangles (B,C,D),(C,D,E) + ws_tris(0, n_ws_tris) = n_ws_pts - 4; // B + ws_tris(1, n_ws_tris) = n_ws_pts - 2; // D + ws_tris(2, n_ws_tris) = n_ws_pts - 3; // C + n_ws_tris++; + ws_tris(0, n_ws_tris) = n_ws_pts - 3; // C + ws_tris(1, n_ws_tris) = n_ws_pts - 2; // D + ws_tris(2, n_ws_tris) = n_ws_pts - 1; // E + n_ws_tris++; + } else { + // Points + ws_pts(n_ws_pts++) = A; + ws_pts(n_ws_pts++) = D; + ws_pts(n_ws_pts++) = E; + // Triangles + ws_tris(0, n_ws_tris) = n_ws_pts - 3; // A + ws_tris(1, n_ws_tris) = n_ws_pts - 2; // D + ws_tris(2, n_ws_tris) = n_ws_pts - 1; // E + n_ws_tris++; + // Points + ns_pts(n_ns_pts++) = B; + ns_pts(n_ns_pts++) = C; + ns_pts(n_ns_pts++) = D; + ns_pts(n_ns_pts++) = E; + // Triangles (B,C,D),(C,D,E) + ns_tris(0, n_ns_tris) = n_ns_pts - 4; // B + ns_tris(1, n_ns_tris) = n_ns_pts - 3; // C + ns_tris(2, n_ns_tris) = n_ns_pts - 2; // D + n_ns_tris++; + ns_tris(0, n_ns_tris) = n_ns_pts - 3; // C + ns_tris(1, n_ns_tris) = n_ns_pts - 1; // E + ns_tris(2, n_ns_tris) = n_ns_pts - 2; // D + n_ns_tris++; + } +} else { + if (values(c) > isovalue) { + // Points + ns_pts(n_ns_pts++) = C; + ns_pts(n_ns_pts++) = D; + ns_pts(n_ns_pts++) = E; + // Triangles + ns_tris(0, n_ns_tris) = n_ns_pts - 3; // C + ns_tris(1, n_ns_tris) = n_ns_pts - 2; // D + ns_tris(2, n_ns_tris) = n_ns_pts - 1; // E + n_ns_tris++; + // Points + ws_pts(n_ws_pts++) = A; + ws_pts(n_ws_pts++) = B; + ws_pts(n_ws_pts++) = D; + ws_pts(n_ws_pts++) = E; + // Triangles (A,B,D),(A,D,E) + ws_tris(0, n_ws_tris) = n_ws_pts - 4; // A + ws_tris(1, n_ws_tris) = n_ws_pts - 3; // B + ws_tris(2, n_ws_tris) = n_ws_pts - 2; // D + n_ws_tris++; + ws_tris(0, n_ws_tris) = n_ws_pts - 4; // A + ws_tris(1, n_ws_tris) = n_ws_pts - 2; // D + ws_tris(2, n_ws_tris) = n_ws_pts - 1; // E + n_ws_tris++; + } else { + // Points + ws_pts(n_ws_pts++) = C; + ws_pts(n_ws_pts++) = D; + ws_pts(n_ws_pts++) = E; + // Triangles + ws_tris(0, n_ws_tris) = n_ws_pts - 3; // C + ws_tris(1, n_ws_tris) = n_ws_pts - 2; // D + ws_tris(2, n_ws_tris) = n_ws_pts - 1; // E + n_ws_tris++; + // Points + ns_pts(n_ns_pts++) = A; + ns_pts(n_ns_pts++) = B; + ns_pts(n_ns_pts++) = D; + ns_pts(n_ns_pts++) = E; + // Triangles (A,B,D),(A,D,E) + ns_tris(0, n_ns_tris) = n_ns_pts - 4; // A + ns_tris(1, n_ns_tris) = n_ns_pts - 3; // B + ns_tris(2, n_ns_tris) = n_ns_pts - 2; // D + n_ns_tris++; + ns_tris(0, n_ns_tris) = n_ns_pts - 4; // A + ns_tris(1, n_ns_tris) = n_ns_pts - 2; // D + ns_tris(2, n_ns_tris) = n_ns_pts - 1; // E + n_ns_tris++; + } +} + } + } + } +} +inline double geomavg_MarchingCubes(DoubleArray &A, double &v, int &i, int &j, + int &k, DTMutableList &nw_pts, + int &n_nw_pts, IntArray &nw_tris, + int &n_nw_tris) { + int N = 0; // n will be the number of vertices in this grid cell only + Point P; + Point pt; Point PlaceHolder; int m; int o; int p; - // Go over each corner -- check to see if the corners are themselves vertices - //1 - if (A(i,j,k) == v){ - P.x = i; - P.y = j; - P.z = k; - nw_pts(n_nw_pts++) = P; - N++; - } - //2 - if (A(i+1,j,k) == v){ - P.x = i+1; - P.y = j; - P.z = k; - nw_pts(n_nw_pts++) = P; - N++; - } - //3 - if (A(i+1,j+1,k) == v){ - P.x = i+1; - P.y = j+1; - P.z = k; - nw_pts(n_nw_pts++) = P; - N++; - } - //4 - if (A(i,j+1,k) == v){ - P.x = i; - P.y = j+1; - P.z = k; - nw_pts(n_nw_pts++) = P; - N++; - } - //5 - if (A(i,j,k+1) == v){ - P.x = i; - P.y = j; - P.z = k+1; - nw_pts(n_nw_pts++) = P; - N++; - } - //6 - if (A(i+1,j,k+1) == v){ - P.x = i+1; - P.y = j; - P.z = k+1; - nw_pts(n_nw_pts++) = P; - N++; - } - //7 - if (A(i+1,j+1,k+1) == v){ - P.x = i+1; - P.y = j+1; - P.z = k+1; - nw_pts(n_nw_pts++) = P; - N++; - } - //8 - if (A(i,j+1,k+1) == v){ - P.x = i; - P.y = j+1; - P.z = k+1; + // Go over each corner -- check to see if the corners are themselves vertices + //1 + if (A(i, j, k) == v) { + P.x = i; + P.y = j; + P.z = k; + nw_pts(n_nw_pts++) = P; + N++; + } + //2 + if (A(i + 1, j, k) == v) { + P.x = i + 1; + P.y = j; + P.z = k; + nw_pts(n_nw_pts++) = P; + N++; + } + //3 + if (A(i + 1, j + 1, k) == v) { + P.x = i + 1; + P.y = j + 1; + P.z = k; + nw_pts(n_nw_pts++) = P; + N++; + } + //4 + if (A(i, j + 1, k) == v) { + P.x = i; + P.y = j + 1; + P.z = k; + nw_pts(n_nw_pts++) = P; + N++; + } + //5 + if (A(i, j, k + 1) == v) { + P.x = i; + P.y = j; + P.z = k + 1; + nw_pts(n_nw_pts++) = P; + N++; + } + //6 + if (A(i + 1, j, k + 1) == v) { + P.x = i + 1; + P.y = j; + P.z = k + 1; + nw_pts(n_nw_pts++) = P; + N++; + } + //7 + if (A(i + 1, j + 1, k + 1) == v) { + P.x = i + 1; + P.y = j + 1; + P.z = k + 1; + nw_pts(n_nw_pts++) = P; + N++; + } + //8 + if (A(i, j + 1, k + 1) == v) { + P.x = i; + P.y = j + 1; + P.z = k + 1; - nw_pts(n_nw_pts++) = P; - N++; - } + nw_pts(n_nw_pts++) = P; + N++; + } // Go through each side, compute P for sides of box spiraling up + // float val; + if ((A(i, j, k) - v) * (A(i + 1, j, k) - v) < 0) { + // If both points are in the fluid region + if (A(i, j, k) != 0 && A(i + 1, j, k) != 0) { + P.x = i + (A(i, j, k) - v) / (A(i, j, k) - A(i + 1, j, k)); + P.y = j; + P.z = k; -// float val; - if ((A(i,j,k)-v)*(A(i+1,j,k)-v) < 0) - { - // If both points are in the fluid region - if (A(i,j,k) != 0 && A(i+1,j,k) != 0){ - P.x = i + (A(i,j,k)-v)/(A(i,j,k)-A(i+1,j,k)); - P.y = j; - P.z = k; + nw_pts(n_nw_pts++) = P; + N++; + } + } - nw_pts(n_nw_pts++) = P; - N++; - } - } + if ((A(i + 1, j, k) - v) * (A(i + 1, j + 1, k) - v) < 0) { + if (A(i + 1, j, k) != 0 && A(i + 1, j + 1, k) != 0) { + P.x = i + 1; + P.y = j + + (A(i + 1, j, k) - v) / (A(i + 1, j, k) - A(i + 1, j + 1, k)); + P.z = k; - if ((A(i+1,j,k)-v)*(A(i+1,j+1,k)-v) < 0) - { - if ( A(i+1,j,k) != 0 && A(i+1,j+1,k) != 0 ){ - P.x = i+1; - P.y = j + (A(i+1,j,k)-v)/(A(i+1,j,k)-A(i+1,j+1,k)); - P.z = k; + if (vertexcheck(P, N, n_nw_pts, nw_pts) == + 1) { // P is a new vertex (not counted twice) + nw_pts(n_nw_pts++) = P; + N++; + } + } + } - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ // P is a new vertex (not counted twice) - nw_pts(n_nw_pts++) = P; - N++; - } - } - } + if ((A(i + 1, j + 1, k) - v) * (A(i, j + 1, k) - v) < 0) { + if (A(i + 1, j + 1, k) != 0 && A(i, j + 1, k) != 0) { + P.x = i + + (A(i, j + 1, k) - v) / (A(i, j + 1, k) - A(i + 1, j + 1, k)); + P.y = j + 1; + P.z = k; + if (vertexcheck(P, N, n_nw_pts, nw_pts) == + 1) { // P is a new vertex (not counted twice) + nw_pts(n_nw_pts++) = P; + N++; + } + } + } - if ((A(i+1,j+1,k)-v)*(A(i,j+1,k)-v) < 0 ) - { - if ( A(i+1,j+1,k) != 0 && A(i,j+1,k) != 0 ){ - P.x = i + (A(i,j+1,k)-v) / (A(i,j+1,k)-A(i+1,j+1,k)); - P.y = j+1; - P.z = k; - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ // P is a new vertex (not counted twice) - nw_pts(n_nw_pts++) = P; - N++; - } - } - } + //4 + if ((A(i, j + 1, k) - v) * (A(i, j, k) - v) < 0) { + if (A(i, j + 1, k) != 0 && A(i, j, k) != 0) { + P.x = i; + P.y = j + (A(i, j, k) - v) / (A(i, j, k) - A(i, j + 1, k)); + P.z = k; + if (vertexcheck(P, N, n_nw_pts, nw_pts) == + 1) { // P is a new vertex (not counted twice) + nw_pts(n_nw_pts++) = P; + N++; + } + } + } - //4 - if ((A(i,j+1,k)-v)*(A(i,j,k)-v) < 0 ) - { - if (A(i,j+1,k) != 0 && A(i,j,k) != 0 ){ - P.x = i; - P.y = j + (A(i,j,k)-v) / (A(i,j,k)-A(i,j+1,k)); - P.z = k; - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ // P is a new vertex (not counted twice) - nw_pts(n_nw_pts++) = P; - N++; - } - } - } + //5 + if ((A(i, j, k) - v) * (A(i, j, k + 1) - v) < 0) { + if (A(i, j, k) != 0 && A(i, j, k + 1) != 0) { + P.x = i; + P.y = j; + P.z = k + (A(i, j, k) - v) / (A(i, j, k) - A(i, j, k + 1)); - //5 - if ((A(i,j,k)-v)*(A(i,j,k+1)-v) < 0 ) - { - if ( A(i,j,k) != 0 && A(i,j,k+1) != 0 ){ - P.x = i; - P.y = j; - P.z = k + (A(i,j,k)-v) / (A(i,j,k)-A(i,j,k+1)); + if (vertexcheck(P, N, n_nw_pts, nw_pts) == + 1) { // P is a new vertex (not counted twice) + nw_pts(n_nw_pts++) = P; + N++; + } + } + } - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ // P is a new vertex (not counted twice) - nw_pts(n_nw_pts++) = P; - N++; - } - } - } + //6 + if ((A(i + 1, j, k) - v) * (A(i + 1, j, k + 1) - v) < 0) { + if (A(i + 1, j, k) != 0 && A(i + 1, j, k + 1) != 0) { + P.x = i + 1; + P.y = j; + P.z = k + + (A(i + 1, j, k) - v) / (A(i + 1, j, k) - A(i + 1, j, k + 1)); + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } - //6 - if ((A(i+1,j,k)-v)*(A(i+1,j,k+1)-v) < 0 ) - { - if ( A(i+1,j,k) != 0 && A(i+1,j,k+1) != 0 ){ - P.x = i+1; - P.y = j; - P.z = k + (A(i+1,j,k)-v) / (A(i+1,j,k)-A(i+1,j,k+1)); - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } + //7 + if ((A(i + 1, j + 1, k) - v) * (A(i + 1, j + 1, k + 1) - v) < 0) { + if (A(i + 1, j + 1, k) != 0 && A(i + 1, j + 1, k + 1) != 0) { + P.x = i + 1; + P.y = j + 1; + P.z = k + (A(i + 1, j + 1, k) - v) / + (A(i + 1, j + 1, k) - A(i + 1, j + 1, k + 1)); + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } - //7 - if ((A(i+1,j+1,k)-v)*(A(i+1,j+1,k+1)-v) < 0 ) - { - if ( A(i+1,j+1,k) != 0 && A(i+1,j+1,k+1) != 0 ){ - P.x = i+1; - P.y = j+1; - P.z = k + (A(i+1,j+1,k)-v) / (A(i+1,j+1,k)-A(i+1,j+1,k+1)); - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } + //8 + if ((A(i, j + 1, k) - v) * (A(i, j + 1, k + 1) - v) < 0) { + if (A(i, j + 1, k) != 0 && A(i, j + 1, k + 1) != 0) { + P.x = i; + P.y = j + 1; + P.z = k + + (A(i, j + 1, k) - v) / (A(i, j + 1, k) - A(i, j + 1, k + 1)); + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } - //8 - if ((A(i,j+1,k)-v)*(A(i,j+1,k+1)-v) < 0 ) - { - if ( A(i,j+1,k) != 0 && A(i,j+1,k+1) != 0 ){ - P.x = i; - P.y = j+1; - P.z = k + (A(i,j+1,k)-v) / (A(i,j+1,k)-A(i,j+1,k+1)); - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } + //9 + if ((A(i, j, k + 1) - v) * (A(i + 1, j, k + 1) - v) < 0) { + if (A(i, j, k + 1) != 0 && A(i + 1, j, k + 1) != 0) { + P.x = i + + (A(i, j, k + 1) - v) / (A(i, j, k + 1) - A(i + 1, j, k + 1)); + P.y = j; + P.z = k + 1; + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } - //9 - if ((A(i,j,k+1)-v)*(A(i+1,j,k+1)-v) < 0 ) - { - if ( A(i,j,k+1) != 0 && A(i+1,j,k+1) != 0 ){ - P.x = i + (A(i,j,k+1)-v) / (A(i,j,k+1)-A(i+1,j,k+1)); - P.y = j; - P.z = k+1; - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } + //10 + if ((A(i + 1, j, k + 1) - v) * (A(i + 1, j + 1, k + 1) - v) < 0) { + if (A(i + 1, j, k + 1) != 0 && A(i + 1, j + 1, k + 1) != 0) { + P.x = i + 1; + P.y = j + (A(i + 1, j, k + 1) - v) / + (A(i + 1, j, k + 1) - A(i + 1, j + 1, k + 1)); + P.z = k + 1; + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } - //10 - if ((A(i+1,j,k+1)-v)*(A(i+1,j+1,k+1)-v) < 0 ) - { - if ( A(i+1,j,k+1) != 0 && A(i+1,j+1,k+1) != 0 ){ - P.x = i+1; - P.y = j + (A(i+1,j,k+1)-v) / (A(i+1,j,k+1)-A(i+1,j+1,k+1)); - P.z = k+1; - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - - //11 - if ((A(i+1,j+1,k+1)-v)*(A(i,j+1,k+1)-v) < 0 ) - { - if ( A(i+1,j+1,k+1) != 0 && A(i,j+1,k+1) != 0 ){ - P.x = i+(A(i,j+1,k+1)-v) / (A(i,j+1,k+1)-A(i+1,j+1,k+1)); - P.y = j+1; - P.z = k+1; - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - - //12 - if ((A(i,j+1,k+1)-v)*(A(i,j,k+1)-v) < 0 ) - { - if ( A(i,j+1,k+1) != 0 && A(i,j,k+1) != 0 ){ - P.x = i; - P.y = j + (A(i,j,k+1)-v) / (A(i,j,k+1)-A(i,j+1,k+1)); - P.z = k+1; - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } + //11 + if ((A(i + 1, j + 1, k + 1) - v) * (A(i, j + 1, k + 1) - v) < 0) { + if (A(i + 1, j + 1, k + 1) != 0 && A(i, j + 1, k + 1) != 0) { + P.x = i + (A(i, j + 1, k + 1) - v) / + (A(i, j + 1, k + 1) - A(i + 1, j + 1, k + 1)); + P.y = j + 1; + P.z = k + 1; + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + //12 + if ((A(i, j + 1, k + 1) - v) * (A(i, j, k + 1) - v) < 0) { + if (A(i, j + 1, k + 1) != 0 && A(i, j, k + 1) != 0) { + P.x = i; + P.y = j + + (A(i, j, k + 1) - v) / (A(i, j, k + 1) - A(i, j + 1, k + 1)); + P.z = k + 1; + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } // Assemble the triangles as long as points are found - if (N > 0){ - for (m = n_nw_pts-N; m < n_nw_pts-2; m++) { - for (o = m+2; o < n_nw_pts-1; o++) { - if (ShareSide(nw_pts(m), nw_pts(o)) == 1) { - PlaceHolder = nw_pts(m+1); - nw_pts(m+1) = nw_pts(o); - nw_pts(o) = PlaceHolder; - } - } + if (N > 0) { + for (m = n_nw_pts - N; m < n_nw_pts - 2; m++) { + for (o = m + 2; o < n_nw_pts - 1; o++) { + if (ShareSide(nw_pts(m), nw_pts(o)) == 1) { + PlaceHolder = nw_pts(m + 1); + nw_pts(m + 1) = nw_pts(o); + nw_pts(o) = PlaceHolder; + } + } - // make sure other neighbor of vertex 1 is in last spot - if (m == n_nw_pts-N){ - for (p = m+2; p < n_nw_pts-1; p++){ - if (ShareSide(nw_pts(m), nw_pts(p)) == 1){ - PlaceHolder = nw_pts(n_nw_pts-1); - nw_pts(n_nw_pts-1) = nw_pts(p); - nw_pts(p) = PlaceHolder; - } - } - } - if ( ShareSide(nw_pts(n_nw_pts-2), nw_pts(n_nw_pts-3)) != 1 ){ - if (ShareSide( nw_pts(n_nw_pts-3), nw_pts(n_nw_pts-1)) == 1 && - ShareSide( nw_pts(n_nw_pts-N),nw_pts(n_nw_pts-2)) == 1 ){ - PlaceHolder = nw_pts(n_nw_pts-2); - nw_pts(n_nw_pts-2) = nw_pts(n_nw_pts-1); - nw_pts(n_nw_pts-1) = PlaceHolder; - } - } - if ( ShareSide(nw_pts(n_nw_pts-1), nw_pts(n_nw_pts-2)) != 1 ){ - if (ShareSide( nw_pts(n_nw_pts-3), nw_pts(n_nw_pts-1)) == 1 && - ShareSide(nw_pts(n_nw_pts-4),nw_pts(n_nw_pts-2)) == 1 ){ - PlaceHolder = nw_pts(n_nw_pts-3); - nw_pts(n_nw_pts-3) = nw_pts(n_nw_pts-2); - nw_pts(n_nw_pts-2) = PlaceHolder; - } - if (ShareSide( nw_pts(n_nw_pts-N+1), nw_pts(n_nw_pts-3)) == 1 && - ShareSide(nw_pts(n_nw_pts-1),nw_pts(n_nw_pts-N+1)) == 1 ){ - PlaceHolder = nw_pts(n_nw_pts-2); - nw_pts(n_nw_pts-2) = nw_pts(n_nw_pts-N+1); - nw_pts(n_nw_pts-N+1) = PlaceHolder; - } - } - if ( ShareSide(nw_pts(n_nw_pts-N), nw_pts(n_nw_pts-N+1)) != 1 ){ - if (ShareSide( nw_pts(n_nw_pts-N), nw_pts(n_nw_pts-2)) == 1 && - ShareSide(nw_pts(n_nw_pts-1), nw_pts(n_nw_pts-N+1)) == 1){ - PlaceHolder = nw_pts(n_nw_pts-1); - nw_pts(n_nw_pts-1) = nw_pts(n_nw_pts-N); - nw_pts(n_nw_pts-N) = PlaceHolder; - } - } - } + // make sure other neighbor of vertex 1 is in last spot + if (m == n_nw_pts - N) { + for (p = m + 2; p < n_nw_pts - 1; p++) { + if (ShareSide(nw_pts(m), nw_pts(p)) == 1) { + PlaceHolder = nw_pts(n_nw_pts - 1); + nw_pts(n_nw_pts - 1) = nw_pts(p); + nw_pts(p) = PlaceHolder; + } + } + } + if (ShareSide(nw_pts(n_nw_pts - 2), nw_pts(n_nw_pts - 3)) != 1) { + if (ShareSide(nw_pts(n_nw_pts - 3), nw_pts(n_nw_pts - 1)) == + 1 && + ShareSide(nw_pts(n_nw_pts - N), nw_pts(n_nw_pts - 2)) == + 1) { + PlaceHolder = nw_pts(n_nw_pts - 2); + nw_pts(n_nw_pts - 2) = nw_pts(n_nw_pts - 1); + nw_pts(n_nw_pts - 1) = PlaceHolder; + } + } + if (ShareSide(nw_pts(n_nw_pts - 1), nw_pts(n_nw_pts - 2)) != 1) { + if (ShareSide(nw_pts(n_nw_pts - 3), nw_pts(n_nw_pts - 1)) == + 1 && + ShareSide(nw_pts(n_nw_pts - 4), nw_pts(n_nw_pts - 2)) == + 1) { + PlaceHolder = nw_pts(n_nw_pts - 3); + nw_pts(n_nw_pts - 3) = nw_pts(n_nw_pts - 2); + nw_pts(n_nw_pts - 2) = PlaceHolder; + } + if (ShareSide(nw_pts(n_nw_pts - N + 1), nw_pts(n_nw_pts - 3)) == + 1 && + ShareSide(nw_pts(n_nw_pts - 1), nw_pts(n_nw_pts - N + 1)) == + 1) { + PlaceHolder = nw_pts(n_nw_pts - 2); + nw_pts(n_nw_pts - 2) = nw_pts(n_nw_pts - N + 1); + nw_pts(n_nw_pts - N + 1) = PlaceHolder; + } + } + if (ShareSide(nw_pts(n_nw_pts - N), nw_pts(n_nw_pts - N + 1)) != + 1) { + if (ShareSide(nw_pts(n_nw_pts - N), nw_pts(n_nw_pts - 2)) == + 1 && + ShareSide(nw_pts(n_nw_pts - 1), nw_pts(n_nw_pts - N + 1)) == + 1) { + PlaceHolder = nw_pts(n_nw_pts - 1); + nw_pts(n_nw_pts - 1) = nw_pts(n_nw_pts - N); + nw_pts(n_nw_pts - N) = PlaceHolder; + } + } + } - // * * * ESTABLISH TRIANGLE CONNECTIONS * * * + // * * * ESTABLISH TRIANGLE CONNECTIONS * * * - for (p=n_nw_pts-N+2; p &nw_pts, int &n_nw_pts, IntArray &nw_tris, - int &n_nw_tris) -{ - int N = 0; // n will be the number of vertices in this grid cell only +inline void MC(DoubleArray &A, double &v, DoubleArray &solid, int &i, int &j, + int &k, DTMutableList &nw_pts, int &n_nw_pts, + IntArray &nw_tris, int &n_nw_tris) { + int N = 0; // n will be the number of vertices in this grid cell only Point P; - Point pt; - DoubleArray TEST(3); + Point pt; + DoubleArray TEST(3); Point PlaceHolder; int m; int o; int p; + // Go over each corner -- check to see if the corners are themselves vertices + //1 + if (A(i, j, k) == v) { + P.x = i; + P.y = j; + P.z = k; + nw_pts(n_nw_pts++) = P; + N++; + } + //2 + if (A(i + 1, j, k) == v) { + P.x = i + 1; + P.y = j; + P.z = k; + nw_pts(n_nw_pts++) = P; + N++; + } + //3 + if (A(i + 1, j + 1, k) == v) { + P.x = i + 1; + P.y = j + 1; + P.z = k; + nw_pts(n_nw_pts++) = P; + N++; + } + //4 + if (A(i, j + 1, k) == v) { + P.x = i; + P.y = j + 1; + P.z = k; + nw_pts(n_nw_pts++) = P; + N++; + } + //5 + if (A(i, j, k + 1) == v) { + P.x = i; + P.y = j; + P.z = k + 1; + nw_pts(n_nw_pts++) = P; + N++; + } + //6 + if (A(i + 1, j, k + 1) == v) { + P.x = i + 1; + P.y = j; + P.z = k + 1; + nw_pts(n_nw_pts++) = P; + N++; + } + //7 + if (A(i + 1, j + 1, k + 1) == v) { + P.x = i + 1; + P.y = j + 1; + P.z = k + 1; + nw_pts(n_nw_pts++) = P; + N++; + } + //8 + if (A(i, j + 1, k + 1) == v) { + P.x = i; + P.y = j + 1; + P.z = k + 1; - // Go over each corner -- check to see if the corners are themselves vertices - //1 - if (A(i,j,k) == v){ - P.x = i; - P.y = j; - P.z = k; - nw_pts(n_nw_pts++) = P; - N++; - } - //2 - if (A(i+1,j,k) == v){ - P.x = i+1; - P.y = j; - P.z = k; - nw_pts(n_nw_pts++) = P; - N++; - } - //3 - if (A(i+1,j+1,k) == v){ - P.x = i+1; - P.y = j+1; - P.z = k; - nw_pts(n_nw_pts++) = P; - N++; - } - //4 - if (A(i,j+1,k) == v){ - P.x = i; - P.y = j+1; - P.z = k; - nw_pts(n_nw_pts++) = P; - N++; - } - //5 - if (A(i,j,k+1) == v){ - P.x = i; - P.y = j; - P.z = k+1; - nw_pts(n_nw_pts++) = P; - N++; - } - //6 - if (A(i+1,j,k+1) == v){ - P.x = i+1; - P.y = j; - P.z = k+1; - nw_pts(n_nw_pts++) = P; - N++; - } - //7 - if (A(i+1,j+1,k+1) == v){ - P.x = i+1; - P.y = j+1; - P.z = k+1; - nw_pts(n_nw_pts++) = P; - N++; - } - //8 - if (A(i,j+1,k+1) == v){ - P.x = i; - P.y = j+1; - P.z = k+1; - - nw_pts(n_nw_pts++) = P; - N++; - } + nw_pts(n_nw_pts++) = P; + N++; + } // Go through each side, compute P for sides of box spiraling up + // float val; + if ((A(i, j, k) - v) * (A(i + 1, j, k) - v) < 0) { + // If both points are in the fluid region + if (A(i, j, k) != 0 && A(i + 1, j, k) != 0) { + P.x = i + (A(i, j, k) - v) / (A(i, j, k) - A(i + 1, j, k)); + P.y = j; + P.z = k; + // Evaluate the function S at the new point + if (solid(i, j, k) * (1 - P.x + i) + + solid(i + 1, j, k) * (P.x - i) > + 0) { + // This point is in the fluid region + nw_pts(n_nw_pts++) = P; + N++; + } + } + } -// float val; - if ((A(i,j,k)-v)*(A(i+1,j,k)-v) < 0) - { - // If both points are in the fluid region - if (A(i,j,k) != 0 && A(i+1,j,k) != 0){ - P.x = i + (A(i,j,k)-v)/(A(i,j,k)-A(i+1,j,k)); - P.y = j; - P.z = k; - // Evaluate the function S at the new point - if ( solid(i,j,k)*(1-P.x+i) + solid(i+1,j,k)*(P.x-i) > 0 ){ - // This point is in the fluid region - nw_pts(n_nw_pts++) = P; - N++; - } - } - } + if ((A(i + 1, j, k) - v) * (A(i + 1, j + 1, k) - v) < 0) { + if (A(i + 1, j, k) != 0 && A(i + 1, j + 1, k) != 0) { + P.x = i + 1; + P.y = j + + (A(i + 1, j, k) - v) / (A(i + 1, j, k) - A(i + 1, j + 1, k)); + P.z = k; + // Evaluate the function S at the new point + if (solid(i + 1, j, k) * (1 - P.y + j) + + solid(i + 1, j + 1, k) * (P.y - j) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == + 1) { // P is a new vertex (not counted twice) + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } - if ((A(i+1,j,k)-v)*(A(i+1,j+1,k)-v) < 0) - { - if ( A(i+1,j,k) != 0 && A(i+1,j+1,k) != 0 ){ - P.x = i+1; - P.y = j + (A(i+1,j,k)-v)/(A(i+1,j,k)-A(i+1,j+1,k)); - P.z = k; - // Evaluate the function S at the new point - if ( solid(i+1,j,k)*(1-P.y+j) + solid(i+1,j+1,k)*(P.y-j) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ // P is a new vertex (not counted twice) - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } - - if ((A(i+1,j+1,k)-v)*(A(i,j+1,k)-v) < 0 ) - { - if ( A(i+1,j+1,k) != 0 && A(i,j+1,k) != 0 ){ - P.x = i + (A(i,j+1,k)-v) / (A(i,j+1,k)-A(i+1,j+1,k)); - P.y = j+1; - P.z = k; - // Evaluate the function S at the new point - if ( solid(i,j+1,k)*(1-P.x+i) + solid(i+1,j+1,k)*(P.x-i) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ // P is a new vertex (not counted twice) - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i + 1, j + 1, k) - v) * (A(i, j + 1, k) - v) < 0) { + if (A(i + 1, j + 1, k) != 0 && A(i, j + 1, k) != 0) { + P.x = i + + (A(i, j + 1, k) - v) / (A(i, j + 1, k) - A(i + 1, j + 1, k)); + P.y = j + 1; + P.z = k; + // Evaluate the function S at the new point + if (solid(i, j + 1, k) * (1 - P.x + i) + + solid(i + 1, j + 1, k) * (P.x - i) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == + 1) { // P is a new vertex (not counted twice) + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //4 - if ((A(i,j+1,k)-v)*(A(i,j,k)-v) < 0 ) - { - if (A(i,j+1,k) != 0 && A(i,j,k) != 0 ){ - P.x = i; - P.y = j + (A(i,j,k)-v) / (A(i,j,k)-A(i,j+1,k)); - P.z = k; - // Evaluate the function S at the new point - if ( solid(i,j,k)*(1-P.y+j) + solid(i,j+1,k)*(P.y-j) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ // P is a new vertex (not counted twice) - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i, j + 1, k) - v) * (A(i, j, k) - v) < 0) { + if (A(i, j + 1, k) != 0 && A(i, j, k) != 0) { + P.x = i; + P.y = j + (A(i, j, k) - v) / (A(i, j, k) - A(i, j + 1, k)); + P.z = k; + // Evaluate the function S at the new point + if (solid(i, j, k) * (1 - P.y + j) + + solid(i, j + 1, k) * (P.y - j) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == + 1) { // P is a new vertex (not counted twice) + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //5 - if ((A(i,j,k)-v)*(A(i,j,k+1)-v) < 0 ) - { - if ( A(i,j,k) != 0 && A(i,j,k+1) != 0 ){ - P.x = i; - P.y = j; - P.z = k + (A(i,j,k)-v) / (A(i,j,k)-A(i,j,k+1)); - // Evaluate the function S at the new point - if ( solid(i,j,k)*(1-P.z+k) + solid(i,j,k+1)*(P.z-k) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ // P is a new vertex (not counted twice) - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i, j, k) - v) * (A(i, j, k + 1) - v) < 0) { + if (A(i, j, k) != 0 && A(i, j, k + 1) != 0) { + P.x = i; + P.y = j; + P.z = k + (A(i, j, k) - v) / (A(i, j, k) - A(i, j, k + 1)); + // Evaluate the function S at the new point + if (solid(i, j, k) * (1 - P.z + k) + + solid(i, j, k + 1) * (P.z - k) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == + 1) { // P is a new vertex (not counted twice) + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //6 - if ((A(i+1,j,k)-v)*(A(i+1,j,k+1)-v) < 0 ) - { - if ( A(i+1,j,k) != 0 && A(i+1,j,k+1) != 0 ){ - P.x = i+1; - P.y = j; - P.z = k + (A(i+1,j,k)-v) / (A(i+1,j,k)-A(i+1,j,k+1)); - // Evaluate the function S at the new point - if ( solid(i+1,j,k)*(1-P.z+k) + solid(i+1,j,k+1)*(P.z-k) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i + 1, j, k) - v) * (A(i + 1, j, k + 1) - v) < 0) { + if (A(i + 1, j, k) != 0 && A(i + 1, j, k + 1) != 0) { + P.x = i + 1; + P.y = j; + P.z = k + + (A(i + 1, j, k) - v) / (A(i + 1, j, k) - A(i + 1, j, k + 1)); + // Evaluate the function S at the new point + if (solid(i + 1, j, k) * (1 - P.z + k) + + solid(i + 1, j, k + 1) * (P.z - k) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //7 - if ((A(i+1,j+1,k)-v)*(A(i+1,j+1,k+1)-v) < 0 ) - { - if ( A(i+1,j+1,k) != 0 && A(i+1,j+1,k+1) != 0 ){ - P.x = i+1; - P.y = j+1; - P.z = k + (A(i+1,j+1,k)-v) / (A(i+1,j+1,k)-A(i+1,j+1,k+1)); - // Evaluate the function S at the new point - if ( solid(i+1,j+1,k)*(1-P.z+k) + solid(i+1,j+1,k+1)*(P.z-k) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i + 1, j + 1, k) - v) * (A(i + 1, j + 1, k + 1) - v) < 0) { + if (A(i + 1, j + 1, k) != 0 && A(i + 1, j + 1, k + 1) != 0) { + P.x = i + 1; + P.y = j + 1; + P.z = k + (A(i + 1, j + 1, k) - v) / + (A(i + 1, j + 1, k) - A(i + 1, j + 1, k + 1)); + // Evaluate the function S at the new point + if (solid(i + 1, j + 1, k) * (1 - P.z + k) + + solid(i + 1, j + 1, k + 1) * (P.z - k) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //8 - if ((A(i,j+1,k)-v)*(A(i,j+1,k+1)-v) < 0 ) - { - if ( A(i,j+1,k) != 0 && A(i,j+1,k+1) != 0 ){ - P.x = i; - P.y = j+1; - P.z = k + (A(i,j+1,k)-v) / (A(i,j+1,k)-A(i,j+1,k+1)); - // Evaluate the function S at the new point - if ( solid(i,j+1,k)*(1-P.z+k) + solid(i,j+1,k+1)*(P.z-k) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i, j + 1, k) - v) * (A(i, j + 1, k + 1) - v) < 0) { + if (A(i, j + 1, k) != 0 && A(i, j + 1, k + 1) != 0) { + P.x = i; + P.y = j + 1; + P.z = k + + (A(i, j + 1, k) - v) / (A(i, j + 1, k) - A(i, j + 1, k + 1)); + // Evaluate the function S at the new point + if (solid(i, j + 1, k) * (1 - P.z + k) + + solid(i, j + 1, k + 1) * (P.z - k) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //9 - if ((A(i,j,k+1)-v)*(A(i+1,j,k+1)-v) < 0 ) - { - if ( A(i,j,k+1) != 0 && A(i+1,j,k+1) != 0 ){ - P.x = i + (A(i,j,k+1)-v) / (A(i,j,k+1)-A(i+1,j,k+1)); - P.y = j; - P.z = k+1; - // Evaluate the function S at the new point - if ( solid(i,j,k+1)*(1-P.x+i) + solid(i+1,j,k+1)*(P.x-i) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i, j, k + 1) - v) * (A(i + 1, j, k + 1) - v) < 0) { + if (A(i, j, k + 1) != 0 && A(i + 1, j, k + 1) != 0) { + P.x = i + + (A(i, j, k + 1) - v) / (A(i, j, k + 1) - A(i + 1, j, k + 1)); + P.y = j; + P.z = k + 1; + // Evaluate the function S at the new point + if (solid(i, j, k + 1) * (1 - P.x + i) + + solid(i + 1, j, k + 1) * (P.x - i) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //10 - if ((A(i+1,j,k+1)-v)*(A(i+1,j+1,k+1)-v) < 0 ) - { - if ( A(i+1,j,k+1) != 0 && A(i+1,j+1,k+1) != 0 ){ - P.x = i+1; - P.y = j + (A(i+1,j,k+1)-v) / (A(i+1,j,k+1)-A(i+1,j+1,k+1)); - P.z = k+1; - // Evaluate the function S at the new point - if ( solid(i+1,j,k+1)*(1-P.y+j) + solid(i+1,j+1,k+1)*(P.y-j) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i + 1, j, k + 1) - v) * (A(i + 1, j + 1, k + 1) - v) < 0) { + if (A(i + 1, j, k + 1) != 0 && A(i + 1, j + 1, k + 1) != 0) { + P.x = i + 1; + P.y = j + (A(i + 1, j, k + 1) - v) / + (A(i + 1, j, k + 1) - A(i + 1, j + 1, k + 1)); + P.z = k + 1; + // Evaluate the function S at the new point + if (solid(i + 1, j, k + 1) * (1 - P.y + j) + + solid(i + 1, j + 1, k + 1) * (P.y - j) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //11 - if ((A(i+1,j+1,k+1)-v)*(A(i,j+1,k+1)-v) < 0 ) - { - if ( A(i+1,j+1,k+1) != 0 && A(i,j+1,k+1) != 0 ){ - P.x = i+(A(i,j+1,k+1)-v) / (A(i,j+1,k+1)-A(i+1,j+1,k+1)); - P.y = j+1; - P.z = k+1; - // Evaluate the function S at the new point - if ( solid(i,j+1,k+1)*(1-P.x+i) + solid(i+1,j+1,k+1)*(P.x-i) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i + 1, j + 1, k + 1) - v) * (A(i, j + 1, k + 1) - v) < 0) { + if (A(i + 1, j + 1, k + 1) != 0 && A(i, j + 1, k + 1) != 0) { + P.x = i + (A(i, j + 1, k + 1) - v) / + (A(i, j + 1, k + 1) - A(i + 1, j + 1, k + 1)); + P.y = j + 1; + P.z = k + 1; + // Evaluate the function S at the new point + if (solid(i, j + 1, k + 1) * (1 - P.x + i) + + solid(i + 1, j + 1, k + 1) * (P.x - i) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //12 - if ((A(i,j+1,k+1)-v)*(A(i,j,k+1)-v) < 0 ) - { - if ( A(i,j+1,k+1) != 0 && A(i,j,k+1) != 0 ){ - P.x = i; - P.y = j + (A(i,j,k+1)-v) / (A(i,j,k+1)-A(i,j+1,k+1)); - P.z = k+1; - // Evaluate the function S at the new point - if ( solid(i,j,k+1)*(1-P.y+j) + solid(i,j+1,k+1)*(P.y-j) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i, j + 1, k + 1) - v) * (A(i, j, k + 1) - v) < 0) { + if (A(i, j + 1, k + 1) != 0 && A(i, j, k + 1) != 0) { + P.x = i; + P.y = j + + (A(i, j, k + 1) - v) / (A(i, j, k + 1) - A(i, j + 1, k + 1)); + P.z = k + 1; + // Evaluate the function S at the new point + if (solid(i, j, k + 1) * (1 - P.y + j) + + solid(i, j + 1, k + 1) * (P.y - j) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } // sort vertices so that they are connected to "neighbors" @@ -2500,1583 +2605,1715 @@ inline void MC( DoubleArray &A, double &v, DoubleArray &solid, int &i, int &j, i // n_nw_pts = number of vertices in total (location n_nw_pts is first unfilled position) // n = number of vertices in this grid cell - // Assemble the triangles as long as points are found - if (N > 0){ - for (m = n_nw_pts-N; m < n_nw_pts-2; m++) { - for (o = m+2; o < n_nw_pts-1; o++) { - if (ShareSide(nw_pts(m), nw_pts(o)) == 1) { - PlaceHolder = nw_pts(m+1); - nw_pts(m+1) = nw_pts(o); - nw_pts(o) = PlaceHolder; - } - } + if (N > 0) { + for (m = n_nw_pts - N; m < n_nw_pts - 2; m++) { + for (o = m + 2; o < n_nw_pts - 1; o++) { + if (ShareSide(nw_pts(m), nw_pts(o)) == 1) { + PlaceHolder = nw_pts(m + 1); + nw_pts(m + 1) = nw_pts(o); + nw_pts(o) = PlaceHolder; + } + } - // make sure other neighbor of vertex 1 is in last spot - if (m == n_nw_pts-N){ - for (p = m+2; p < n_nw_pts-1; p++){ - if (ShareSide(nw_pts(m), nw_pts(p)) == 1){ - PlaceHolder = nw_pts(n_nw_pts-1); - nw_pts(n_nw_pts-1) = nw_pts(p); - nw_pts(p) = PlaceHolder; - } - } - } - if ( ShareSide(nw_pts(n_nw_pts-2), nw_pts(n_nw_pts-3)) != 1 ){ - if (ShareSide( nw_pts(n_nw_pts-3), nw_pts(n_nw_pts-1)) == 1 && - ShareSide( nw_pts(n_nw_pts-N),nw_pts(n_nw_pts-2)) == 1 ){ - PlaceHolder = nw_pts(n_nw_pts-2); - nw_pts(n_nw_pts-2) = nw_pts(n_nw_pts-1); - nw_pts(n_nw_pts-1) = PlaceHolder; - } - } - if ( ShareSide(nw_pts(n_nw_pts-1), nw_pts(n_nw_pts-2)) != 1 ){ - if (ShareSide( nw_pts(n_nw_pts-3), nw_pts(n_nw_pts-1)) == 1 && - ShareSide(nw_pts(n_nw_pts-4),nw_pts(n_nw_pts-2)) == 1 ){ - PlaceHolder = nw_pts(n_nw_pts-3); - nw_pts(n_nw_pts-3) = nw_pts(n_nw_pts-2); - nw_pts(n_nw_pts-2) = PlaceHolder; - } - if (ShareSide( nw_pts(n_nw_pts-N+1), nw_pts(n_nw_pts-3)) == 1 && - ShareSide(nw_pts(n_nw_pts-1),nw_pts(n_nw_pts-N+1)) == 1 ){ - PlaceHolder = nw_pts(n_nw_pts-2); - nw_pts(n_nw_pts-2) = nw_pts(n_nw_pts-N+1); - nw_pts(n_nw_pts-N+1) = PlaceHolder; - } - } - if ( ShareSide(nw_pts(n_nw_pts-N), nw_pts(n_nw_pts-N+1)) != 1 ){ - if (ShareSide( nw_pts(n_nw_pts-N), nw_pts(n_nw_pts-2)) == 1 && - ShareSide(nw_pts(n_nw_pts-1), nw_pts(n_nw_pts-N+1)) == 1){ - PlaceHolder = nw_pts(n_nw_pts-1); - nw_pts(n_nw_pts-1) = nw_pts(n_nw_pts-N); - nw_pts(n_nw_pts-N) = PlaceHolder; - } - } - } + // make sure other neighbor of vertex 1 is in last spot + if (m == n_nw_pts - N) { + for (p = m + 2; p < n_nw_pts - 1; p++) { + if (ShareSide(nw_pts(m), nw_pts(p)) == 1) { + PlaceHolder = nw_pts(n_nw_pts - 1); + nw_pts(n_nw_pts - 1) = nw_pts(p); + nw_pts(p) = PlaceHolder; + } + } + } + if (ShareSide(nw_pts(n_nw_pts - 2), nw_pts(n_nw_pts - 3)) != 1) { + if (ShareSide(nw_pts(n_nw_pts - 3), nw_pts(n_nw_pts - 1)) == + 1 && + ShareSide(nw_pts(n_nw_pts - N), nw_pts(n_nw_pts - 2)) == + 1) { + PlaceHolder = nw_pts(n_nw_pts - 2); + nw_pts(n_nw_pts - 2) = nw_pts(n_nw_pts - 1); + nw_pts(n_nw_pts - 1) = PlaceHolder; + } + } + if (ShareSide(nw_pts(n_nw_pts - 1), nw_pts(n_nw_pts - 2)) != 1) { + if (ShareSide(nw_pts(n_nw_pts - 3), nw_pts(n_nw_pts - 1)) == + 1 && + ShareSide(nw_pts(n_nw_pts - 4), nw_pts(n_nw_pts - 2)) == + 1) { + PlaceHolder = nw_pts(n_nw_pts - 3); + nw_pts(n_nw_pts - 3) = nw_pts(n_nw_pts - 2); + nw_pts(n_nw_pts - 2) = PlaceHolder; + } + if (ShareSide(nw_pts(n_nw_pts - N + 1), nw_pts(n_nw_pts - 3)) == + 1 && + ShareSide(nw_pts(n_nw_pts - 1), nw_pts(n_nw_pts - N + 1)) == + 1) { + PlaceHolder = nw_pts(n_nw_pts - 2); + nw_pts(n_nw_pts - 2) = nw_pts(n_nw_pts - N + 1); + nw_pts(n_nw_pts - N + 1) = PlaceHolder; + } + } + if (ShareSide(nw_pts(n_nw_pts - N), nw_pts(n_nw_pts - N + 1)) != + 1) { + if (ShareSide(nw_pts(n_nw_pts - N), nw_pts(n_nw_pts - 2)) == + 1 && + ShareSide(nw_pts(n_nw_pts - 1), nw_pts(n_nw_pts - N + 1)) == + 1) { + PlaceHolder = nw_pts(n_nw_pts - 1); + nw_pts(n_nw_pts - 1) = nw_pts(n_nw_pts - N); + nw_pts(n_nw_pts - N) = PlaceHolder; + } + } + } - // * * * ESTABLISH TRIANGLE CONNECTIONS * * * + // * * * ESTABLISH TRIANGLE CONNECTIONS * * * - for (p=n_nw_pts-N+2; p &nw_pts, int &n_nw_pts, IntArray &nw_tris, int &n_nw_tris, - DTMutableList &local_nws_pts, int &n_local_nws_pts) -{ +inline void EDGE(DoubleArray &A, double &v, DoubleArray &solid, int &i, int &j, + int &k, int &m, int &n, int &o, DTMutableList &nw_pts, + int &n_nw_pts, IntArray &nw_tris, int &n_nw_tris, + DTMutableList &local_nws_pts, int &n_local_nws_pts) { - NULL_USE( m ); - NULL_USE( n ); - NULL_USE( o ); + NULL_USE(m); + NULL_USE(n); + NULL_USE(o); - // FIND THE POINTS ON THE nw SURFACE THAT ARE ON THE EDGE (COMMON LINE WITH SOLID PHASE) - // function A is the fluid data padded (so that it has values inside the solid phase) + // FIND THE POINTS ON THE nw SURFACE THAT ARE ON THE EDGE (COMMON LINE WITH SOLID PHASE) + // function A is the fluid data padded (so that it has values inside the solid phase) - int N = 0; // n will be the number of vertices in this grid cell only + int N = 0; // n will be the number of vertices in this grid cell only Point P; Point temp; - int p; int q; int r; + int p; + int q; + int r; - // Add common line points to nw_pts - for (p=0;p 0 ){ - // This point is in the fluid region - nw_pts(n_nw_pts++) = P; - N++; - } - } - } + // 1 + if ((A(i, j, k) - v) * (A(i + 1, j, k) - v) < 0) { + // If both points are in the fluid region + if (A(i, j, k) != 0 && A(i + 1, j, k) != 0) { + P.x = i + (A(i, j, k) - v) / (A(i, j, k) - A(i + 1, j, k)); + P.y = j; + P.z = k; + // Evaluate the function S at the new point + if (solid(i, j, k) * (1 - P.x + i) + + solid(i + 1, j, k) * (P.x - i) > + 0) { + // This point is in the fluid region + nw_pts(n_nw_pts++) = P; + N++; + } + } + } // 2 - if ((A(i+1,j,k)-v)*(A(i+1,j+1,k)-v) < 0) - { - if ( A(i+1,j,k) != 0 && A(i+1,j+1,k) != 0 ){ - P.x = i+1; - P.y = j + (A(i+1,j,k)-v)/(A(i+1,j,k)-A(i+1,j+1,k)); - P.z = k; - // Evaluate the function S at the new point - if ( solid(i+1,j,k)*(1-P.y+j) + solid(i+1,j+1,k)*(P.y-j) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ // P is a new vertex (not counted twice) - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i + 1, j, k) - v) * (A(i + 1, j + 1, k) - v) < 0) { + if (A(i + 1, j, k) != 0 && A(i + 1, j + 1, k) != 0) { + P.x = i + 1; + P.y = j + + (A(i + 1, j, k) - v) / (A(i + 1, j, k) - A(i + 1, j + 1, k)); + P.z = k; + // Evaluate the function S at the new point + if (solid(i + 1, j, k) * (1 - P.y + j) + + solid(i + 1, j + 1, k) * (P.y - j) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == + 1) { // P is a new vertex (not counted twice) + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //3 - if ((A(i+1,j+1,k)-v)*(A(i,j+1,k)-v) < 0 ) - { - if ( A(i+1,j+1,k) != 0 && A(i,j+1,k) != 0 ){ - P.x = i + (A(i,j+1,k)-v) / (A(i,j+1,k)-A(i+1,j+1,k)); - P.y = j+1; - P.z = k; - // Evaluate the function S at the new point - if ( solid(i,j+1,k)*(1-P.x+i) + solid(i+1,j+1,k)*(P.x-i) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ // P is a new vertex (not counted twice) - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i + 1, j + 1, k) - v) * (A(i, j + 1, k) - v) < 0) { + if (A(i + 1, j + 1, k) != 0 && A(i, j + 1, k) != 0) { + P.x = i + + (A(i, j + 1, k) - v) / (A(i, j + 1, k) - A(i + 1, j + 1, k)); + P.y = j + 1; + P.z = k; + // Evaluate the function S at the new point + if (solid(i, j + 1, k) * (1 - P.x + i) + + solid(i + 1, j + 1, k) * (P.x - i) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == + 1) { // P is a new vertex (not counted twice) + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //4 - if ((A(i,j+1,k)-v)*(A(i,j,k)-v) < 0 ) - { - if (A(i,j+1,k) != 0 && A(i,j,k) != 0 ){ - P.x = i; - P.y = j + (A(i,j,k)-v) / (A(i,j,k)-A(i,j+1,k)); - P.z = k; - // Evaluate the function S at the new point - if ( solid(i,j,k)*(1-P.y+j) + solid(i,j+1,k)*(P.y-j) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ // P is a new vertex (not counted twice) - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i, j + 1, k) - v) * (A(i, j, k) - v) < 0) { + if (A(i, j + 1, k) != 0 && A(i, j, k) != 0) { + P.x = i; + P.y = j + (A(i, j, k) - v) / (A(i, j, k) - A(i, j + 1, k)); + P.z = k; + // Evaluate the function S at the new point + if (solid(i, j, k) * (1 - P.y + j) + + solid(i, j + 1, k) * (P.y - j) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == + 1) { // P is a new vertex (not counted twice) + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //5 - if ((A(i,j,k)-v)*(A(i,j,k+1)-v) < 0 ) - { - if ( A(i,j,k) != 0 && A(i,j,k+1) != 0 ){ - P.x = i; - P.y = j; - P.z = k + (A(i,j,k)-v) / (A(i,j,k)-A(i,j,k+1)); - // Evaluate the function S at the new point - if ( solid(i,j,k)*(1-P.z+k) + solid(i,j,k+1)*(P.z-k) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ // P is a new vertex (not counted twice) - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i, j, k) - v) * (A(i, j, k + 1) - v) < 0) { + if (A(i, j, k) != 0 && A(i, j, k + 1) != 0) { + P.x = i; + P.y = j; + P.z = k + (A(i, j, k) - v) / (A(i, j, k) - A(i, j, k + 1)); + // Evaluate the function S at the new point + if (solid(i, j, k) * (1 - P.z + k) + + solid(i, j, k + 1) * (P.z - k) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == + 1) { // P is a new vertex (not counted twice) + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //6 - if ((A(i+1,j,k)-v)*(A(i+1,j,k+1)-v) < 0 ) - { - if ( A(i+1,j,k) != 0 && A(i+1,j,k+1) != 0 ){ - P.x = i+1; - P.y = j; - P.z = k + (A(i+1,j,k)-v) / (A(i+1,j,k)-A(i+1,j,k+1)); - // Evaluate the function S at the new point - if ( solid(i+1,j,k)*(1-P.z+k) + solid(i+1,j,k+1)*(P.z-k) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i + 1, j, k) - v) * (A(i + 1, j, k + 1) - v) < 0) { + if (A(i + 1, j, k) != 0 && A(i + 1, j, k + 1) != 0) { + P.x = i + 1; + P.y = j; + P.z = k + + (A(i + 1, j, k) - v) / (A(i + 1, j, k) - A(i + 1, j, k + 1)); + // Evaluate the function S at the new point + if (solid(i + 1, j, k) * (1 - P.z + k) + + solid(i + 1, j, k + 1) * (P.z - k) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //7 - if ((A(i+1,j+1,k)-v)*(A(i+1,j+1,k+1)-v) < 0 ) - { - if ( A(i+1,j+1,k) != 0 && A(i+1,j+1,k+1) != 0 ){ - P.x = i+1; - P.y = j+1; - P.z = k + (A(i+1,j+1,k)-v) / (A(i+1,j+1,k)-A(i+1,j+1,k+1)); - // Evaluate the function S at the new point - if ( solid(i+1,j+1,k)*(1-P.z+k) + solid(i+1,j+1,k+1)*(P.z-k) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i + 1, j + 1, k) - v) * (A(i + 1, j + 1, k + 1) - v) < 0) { + if (A(i + 1, j + 1, k) != 0 && A(i + 1, j + 1, k + 1) != 0) { + P.x = i + 1; + P.y = j + 1; + P.z = k + (A(i + 1, j + 1, k) - v) / + (A(i + 1, j + 1, k) - A(i + 1, j + 1, k + 1)); + // Evaluate the function S at the new point + if (solid(i + 1, j + 1, k) * (1 - P.z + k) + + solid(i + 1, j + 1, k + 1) * (P.z - k) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //8 - if ((A(i,j+1,k)-v)*(A(i,j+1,k+1)-v) < 0 ) - { - if ( A(i,j+1,k) != 0 && A(i,j+1,k+1) != 0 ){ - P.x = i; - P.y = j+1; - P.z = k + (A(i,j+1,k)-v) / (A(i,j+1,k)-A(i,j+1,k+1)); - // Evaluate the function S at the new point - if ( solid(i,j+1,k)*(1-P.z+k) + solid(i,j+1,k+1)*(P.z-k) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i, j + 1, k) - v) * (A(i, j + 1, k + 1) - v) < 0) { + if (A(i, j + 1, k) != 0 && A(i, j + 1, k + 1) != 0) { + P.x = i; + P.y = j + 1; + P.z = k + + (A(i, j + 1, k) - v) / (A(i, j + 1, k) - A(i, j + 1, k + 1)); + // Evaluate the function S at the new point + if (solid(i, j + 1, k) * (1 - P.z + k) + + solid(i, j + 1, k + 1) * (P.z - k) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //9 - if ((A(i,j,k+1)-v)*(A(i+1,j,k+1)-v) < 0 ) - { - if ( A(i,j,k+1) != 0 && A(i+1,j,k+1) != 0 ){ - P.x = i + (A(i,j,k+1)-v) / (A(i,j,k+1)-A(i+1,j,k+1)); - P.y = j; - P.z = k+1; - // Evaluate the function S at the new point - if ( solid(i,j,k+1)*(1-P.x+i) + solid(i+1,j,k+1)*(P.x-i) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i, j, k + 1) - v) * (A(i + 1, j, k + 1) - v) < 0) { + if (A(i, j, k + 1) != 0 && A(i + 1, j, k + 1) != 0) { + P.x = i + + (A(i, j, k + 1) - v) / (A(i, j, k + 1) - A(i + 1, j, k + 1)); + P.y = j; + P.z = k + 1; + // Evaluate the function S at the new point + if (solid(i, j, k + 1) * (1 - P.x + i) + + solid(i + 1, j, k + 1) * (P.x - i) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //10 - if ((A(i+1,j,k+1)-v)*(A(i+1,j+1,k+1)-v) < 0 ) - { - if ( A(i+1,j,k+1) != 0 && A(i+1,j+1,k+1) != 0 ){ - P.x = i+1; - P.y = j + (A(i+1,j,k+1)-v) / (A(i+1,j,k+1)-A(i+1,j+1,k+1)); - P.z = k+1; - // Evaluate the function S at the new point - if ( solid(i+1,j,k+1)*(1-P.y+j) + solid(i+1,j+1,k+1)*(P.y-j) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i + 1, j, k + 1) - v) * (A(i + 1, j + 1, k + 1) - v) < 0) { + if (A(i + 1, j, k + 1) != 0 && A(i + 1, j + 1, k + 1) != 0) { + P.x = i + 1; + P.y = j + (A(i + 1, j, k + 1) - v) / + (A(i + 1, j, k + 1) - A(i + 1, j + 1, k + 1)); + P.z = k + 1; + // Evaluate the function S at the new point + if (solid(i + 1, j, k + 1) * (1 - P.y + j) + + solid(i + 1, j + 1, k + 1) * (P.y - j) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //11 - if ((A(i+1,j+1,k+1)-v)*(A(i,j+1,k+1)-v) < 0 ) - { - if ( A(i+1,j+1,k+1) != 0 && A(i,j+1,k+1) != 0 ){ - P.x = i+(A(i,j+1,k+1)-v) / (A(i,j+1,k+1)-A(i+1,j+1,k+1)); - P.y = j+1; - P.z = k+1; - // Evaluate the function S at the new point - if ( solid(i,j+1,k+1)*(1-P.x+i) + solid(i+1,j+1,k+1)*(P.x-i) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i + 1, j + 1, k + 1) - v) * (A(i, j + 1, k + 1) - v) < 0) { + if (A(i + 1, j + 1, k + 1) != 0 && A(i, j + 1, k + 1) != 0) { + P.x = i + (A(i, j + 1, k + 1) - v) / + (A(i, j + 1, k + 1) - A(i + 1, j + 1, k + 1)); + P.y = j + 1; + P.z = k + 1; + // Evaluate the function S at the new point + if (solid(i, j + 1, k + 1) * (1 - P.x + i) + + solid(i + 1, j + 1, k + 1) * (P.x - i) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } //12 - if ((A(i,j+1,k+1)-v)*(A(i,j,k+1)-v) < 0 ) - { - if ( A(i,j+1,k+1) != 0 && A(i,j,k+1) != 0 ){ - P.x = i; - P.y = j + (A(i,j,k+1)-v) / (A(i,j,k+1)-A(i,j+1,k+1)); - P.z = k+1; - // Evaluate the function S at the new point - if ( solid(i,j,k+1)*(1-P.y+j) + solid(i,j+1,k+1)*(P.y-j) > 0 ){ - // This point is in the fluid region - if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1){ - nw_pts(n_nw_pts++) = P; - N++; - } - } - } - } + if ((A(i, j + 1, k + 1) - v) * (A(i, j, k + 1) - v) < 0) { + if (A(i, j + 1, k + 1) != 0 && A(i, j, k + 1) != 0) { + P.x = i; + P.y = j + + (A(i, j, k + 1) - v) / (A(i, j, k + 1) - A(i, j + 1, k + 1)); + P.z = k + 1; + // Evaluate the function S at the new point + if (solid(i, j, k + 1) * (1 - P.y + j) + + solid(i, j + 1, k + 1) * (P.y - j) > + 0) { + // This point is in the fluid region + if (vertexcheck(P, N, n_nw_pts, nw_pts) == 1) { + nw_pts(n_nw_pts++) = P; + N++; + } + } + } + } - //////////////////////////////////////////////// - ////// SORT VERTICES SO THAT THEY CONNECT ////// - ////// TO ALL "NEIGHBORS" ////// - //////////////////////////////////////////////// + //////////////////////////////////////////////// + ////// SORT VERTICES SO THAT THEY CONNECT ////// + ////// TO ALL "NEIGHBORS" ////// + //////////////////////////////////////////////// - // First common line point should connect to last MC point - for (q=n_nw_pts-N; q vF) - int cube[8][3] = {{0,0,0},{1,0,0},{0,1,0},{1,1,0},{0,0,1},{1,0,1},{0,1,1},{1,1,1}}; // cube corners -// int count_in=0,count_out=0; -// int nodx,nody,nodz; - // initialize lists for vertices for surfaces, common line - DTMutableList nw_pts(20); - DTMutableList ns_pts(20); - DTMutableList ws_pts(20); - DTMutableList nws_pts(20); - // initialize triangle lists for surfaces - IntArray nw_tris(3,20); - IntArray ns_tris(3,20); - IntArray ws_tris(3,20); - // initialize list for line segments - IntArray nws_seg(2,20); - - DTMutableList tmp(20); - // IntArray store; - - int i,j,k,p,q,r; - int n_nw_pts=0,n_ns_pts=0,n_ws_pts=0,n_nws_pts=0, map=0; - int n_nw_tris=0, n_ns_tris=0, n_ws_tris=0, n_nws_seg=0; - - double s,s1,s2,s3; // Triangle sides (lengths) - Point A,B,C,P; -// double area; - - // Initialize arrays for local solid surface - DTMutableList local_sol_pts(20); - int n_local_sol_pts = 0; - IntArray local_sol_tris(3,18); - int n_local_sol_tris; - DoubleArray values(20); - DTMutableList local_nws_pts(20); - int n_local_nws_pts; - - int n_nw_tris_beg, n_ns_tris_beg, n_ws_tris_beg; - int c; -// int newton_steps = 0; -// double blob_volume; - - /* **************************************************************** + awn = aws = ans = lwns = 0.0; + + // bool add=1; // Set to false if any corners contain nw-phase ( F > vF) + int cube[8][3] = {{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {1, 1, 0}, {0, 0, 1}, + {1, 0, 1}, {0, 1, 1}, {1, 1, 1}}; // cube corners + // int count_in=0,count_out=0; + // int nodx,nody,nodz; + // initialize lists for vertices for surfaces, common line + DTMutableList nw_pts(20); + DTMutableList ns_pts(20); + DTMutableList ws_pts(20); + DTMutableList nws_pts(20); + // initialize triangle lists for surfaces + IntArray nw_tris(3, 20); + IntArray ns_tris(3, 20); + IntArray ws_tris(3, 20); + // initialize list for line segments + IntArray nws_seg(2, 20); + + DTMutableList tmp(20); + // IntArray store; + + int i, j, k, p, q, r; + int n_nw_pts = 0, n_ns_pts = 0, n_ws_pts = 0, n_nws_pts = 0, map = 0; + int n_nw_tris = 0, n_ns_tris = 0, n_ws_tris = 0, n_nws_seg = 0; + + double s, s1, s2, s3; // Triangle sides (lengths) + Point A, B, C, P; + // double area; + + // Initialize arrays for local solid surface + DTMutableList local_sol_pts(20); + int n_local_sol_pts = 0; + IntArray local_sol_tris(3, 18); + int n_local_sol_tris; + DoubleArray values(20); + DTMutableList local_nws_pts(20); + int n_local_nws_pts; + + int n_nw_tris_beg, n_ns_tris_beg, n_ws_tris_beg; + int c; + // int newton_steps = 0; + // double blob_volume; + + /* **************************************************************** RUN PMMC ON EACH BLOB ****************************************************************** */ -// printf("Running the PMMC Algorithm \n"); -// printf("The number of blobs is %i \n",nblobs); + // printf("Running the PMMC Algorithm \n"); + // printf("The number of blobs is %i \n",nblobs); - // Store beginning points for surfaces for blob p - n_nw_tris_beg = n_nw_tris; - n_ns_tris_beg = n_ns_tris; - n_ws_tris_beg = n_ws_tris; -// n_nws_seg_beg = n_nws_seg; - // Loop over all cubes - blob_volume = 0; // Initialize the volume for blob a to zero - for (c=start;c -1){ blob_volume += 0.125; } } -*/ - for (p=0;p<8;p++){ - if ( F(i+cube[p][0],j+cube[p][1],k+cube[p][2]) > 0 - && S(i+cube[p][0],j+cube[p][1],k+cube[p][2]) > 0 ){ - blob_volume += 0.125; - } - } - - // Run PMMC - n_local_sol_tris = 0; - n_local_sol_pts = 0; - n_local_nws_pts = 0; - - // if there is a solid phase interface in the grid cell - if (Interface(S,vS,i,j,k) == 1){ - - ///////////////////////////////////////// - /// CONSTRUCT THE LOCAL SOLID SURFACE /// - ///////////////////////////////////////// - - // find the local solid surface - SOL_SURF(S,0,F,vF,i,j,k, Nx,Ny,Nz,local_sol_pts,n_local_sol_pts, - local_sol_tris,n_local_sol_tris,values); - - ///////////////////////////////////////// - //////// TRIM THE SOLID SURFACE ///////// - ///////////////////////////////////////// -/* TRIM(local_sol_pts, n_local_sol_pts, fluid_isovalue,local_sol_tris, n_local_sol_tris, +*/ + for (p = 0; p < 8; p++) { + if (F(i + cube[p][0], j + cube[p][1], k + cube[p][2]) > 0 && + S(i + cube[p][0], j + cube[p][1], k + cube[p][2]) > 0) { + blob_volume += 0.125; + } + } + + // Run PMMC + n_local_sol_tris = 0; + n_local_sol_pts = 0; + n_local_nws_pts = 0; + + // if there is a solid phase interface in the grid cell + if (Interface(S, vS, i, j, k) == 1) { + + ///////////////////////////////////////// + /// CONSTRUCT THE LOCAL SOLID SURFACE /// + ///////////////////////////////////////// + + // find the local solid surface + SOL_SURF(S, 0, F, vF, i, j, k, Nx, Ny, Nz, local_sol_pts, + n_local_sol_pts, local_sol_tris, n_local_sol_tris, values); + + ///////////////////////////////////////// + //////// TRIM THE SOLID SURFACE ///////// + ///////////////////////////////////////// + /* TRIM(local_sol_pts, n_local_sol_pts, fluid_isovalue,local_sol_tris, n_local_sol_tris, ns_pts, n_ns_pts, ns_tris, n_ns_tris, ws_pts, n_ws_pts, ws_tris, n_ws_tris, values, local_nws_pts, n_local_nws_pts, Phase, SignDist, i, j, k, newton_steps); -*/ - TRIM(local_sol_pts, n_local_sol_pts, vF, local_sol_tris, n_local_sol_tris, - ns_pts, n_ns_pts, ns_tris, n_ns_tris, ws_pts, n_ws_pts, - ws_tris, n_ws_tris, values, local_nws_pts, n_local_nws_pts); +*/ + TRIM(local_sol_pts, n_local_sol_pts, vF, local_sol_tris, + n_local_sol_tris, ns_pts, n_ns_pts, ns_tris, n_ns_tris, ws_pts, + n_ws_pts, ws_tris, n_ws_tris, values, local_nws_pts, + n_local_nws_pts); - ///////////////////////////////////////// - //////// WRITE COMMON LINE POINTS /////// - //////// TO MAIN ARRAYS /////// - ///////////////////////////////////////// - map = n_nws_pts; - for (p=0; p < n_local_nws_pts; p++){ - nws_pts(n_nws_pts++) = local_nws_pts(p); - } - for (q=0; q < n_local_nws_pts-1; q++){ - nws_seg(0,n_nws_seg) = map+q; - nws_seg(1,n_nws_seg) = map+q+1; - n_nws_seg++; - } - - ///////////////////////////////////////// - ////// CONSTRUCT THE nw SURFACE ///////// - ///////////////////////////////////////// - if ( n_local_nws_pts > 0){ - EDGE(F, vF, S, i,j,k, Nx, Ny, Nz, nw_pts, n_nw_pts, nw_tris, n_nw_tris, - local_nws_pts, n_local_nws_pts); - } - else { - MC(F, vF, S, i,j,k, nw_pts, n_nw_pts, nw_tris, n_nw_tris); - } - } - - ///////////////////////////////////////// - ////// CONSTRUCT THE nw SURFACE ///////// - ///////////////////////////////////////// - - else if (Fluid_Interface(F,S,vF,i,j,k) == 1){ - MC(F, vF, S, i,j,k, nw_pts, n_nw_pts, nw_tris, n_nw_tris); - } - //******END OF BLOB PMMC********************************************* + ///////////////////////////////////////// + //////// WRITE COMMON LINE POINTS /////// + //////// TO MAIN ARRAYS /////// + ///////////////////////////////////////// + map = n_nws_pts; + for (p = 0; p < n_local_nws_pts; p++) { + nws_pts(n_nws_pts++) = local_nws_pts(p); + } + for (q = 0; q < n_local_nws_pts - 1; q++) { + nws_seg(0, n_nws_seg) = map + q; + nws_seg(1, n_nws_seg) = map + q + 1; + n_nws_seg++; + } - //******************************************************************* - // Compute the Interfacial Areas, Common Line length for blob p - // nw surface - for (r=n_nw_tris_beg;r 0) { + EDGE(F, vF, S, i, j, k, Nx, Ny, Nz, nw_pts, n_nw_pts, nw_tris, + n_nw_tris, local_nws_pts, n_local_nws_pts); + } else { + MC(F, vF, S, i, j, k, nw_pts, n_nw_pts, nw_tris, n_nw_tris); + } + } + + ///////////////////////////////////////// + ////// CONSTRUCT THE nw SURFACE ///////// + ///////////////////////////////////////// + + else if (Fluid_Interface(F, S, vF, i, j, k) == 1) { + MC(F, vF, S, i, j, k, nw_pts, n_nw_pts, nw_tris, n_nw_tris); + } + //******END OF BLOB PMMC********************************************* + + //******************************************************************* + // Compute the Interfacial Areas, Common Line length for blob p + // nw surface + for (r = n_nw_tris_beg; r < n_nw_tris; r++) { + A = nw_pts(nw_tris(0, r)); + B = nw_pts(nw_tris(1, r)); + C = nw_pts(nw_tris(2, r)); + // Compute length of sides (assume dx=dy=dz) + s1 = sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + + (A.z - B.z) * (A.z - B.z)); + s2 = sqrt((A.x - C.x) * (A.x - C.x) + (A.y - C.y) * (A.y - C.y) + + (A.z - C.z) * (A.z - C.z)); + s3 = sqrt((B.x - C.x) * (B.x - C.x) + (B.y - C.y) * (B.y - C.y) + + (B.z - C.z) * (B.z - C.z)); + s = 0.5 * (s1 + s2 + s3); + awn = awn + sqrt(s * (s - s1) * (s - s2) * (s - s3)); + } + for (r = n_ns_tris_beg; r < n_ns_tris; r++) { + A = ns_pts(ns_tris(0, r)); + B = ns_pts(ns_tris(1, r)); + C = ns_pts(ns_tris(2, r)); + // Compute length of sides (assume dx=dy=dz) + s1 = sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + + (A.z - B.z) * (A.z - B.z)); + s2 = sqrt((A.x - C.x) * (A.x - C.x) + (A.y - C.y) * (A.y - C.y) + + (A.z - C.z) * (A.z - C.z)); + s3 = sqrt((B.x - C.x) * (B.x - C.x) + (B.y - C.y) * (B.y - C.y) + + (B.z - C.z) * (B.z - C.z)); + s = 0.5 * (s1 + s2 + s3); + ans = ans + sqrt(s * (s - s1) * (s - s2) * (s - s3)); + } + for (r = n_ws_tris_beg; r < n_ws_tris; r++) { + A = ws_pts(ws_tris(0, r)); + B = ws_pts(ws_tris(1, r)); + C = ws_pts(ws_tris(2, r)); + // Compute length of sides (assume dx=dy=dz) + s1 = sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + + (A.z - B.z) * (A.z - B.z)); + s2 = sqrt((A.x - C.x) * (A.x - C.x) + (A.y - C.y) * (A.y - C.y) + + (A.z - C.z) * (A.z - C.z)); + s3 = sqrt((B.x - C.x) * (B.x - C.x) + (B.y - C.y) * (B.y - C.y) + + (B.z - C.z) * (B.z - C.z)); + s = 0.5 * (s1 + s2 + s3); + aws = aws + sqrt(s * (s - s1) * (s - s2) * (s - s3)); + } + //******************************************************************* + // Reset the triangle counts to zero + n_nw_pts = 0, n_ns_pts = 0, n_ws_pts = 0, n_nws_pts = 0, map = 0; + n_nw_tris = 0, n_ns_tris = 0, n_ws_tris = 0, n_nws_seg = 0; + + n_nw_tris_beg = n_nw_tris; + n_ns_tris_beg = n_ns_tris; + n_ws_tris_beg = n_ws_tris; + // n_nws_seg_beg = n_nws_seg; + //******************************************************************* + } } //-------------------------------------------------------------------------------------------------------- -inline void pmmc_ConstructLocalCube(DoubleArray &SignDist, DoubleArray &Phase, double solid_isovalue, double fluid_isovalue, - DTMutableList &nw_pts, IntArray &nw_tris, DoubleArray &values, - DTMutableList &ns_pts, IntArray &ns_tris, - DTMutableList &ws_pts, IntArray &ws_tris, - DTMutableList &local_nws_pts, DTMutableList &nws_pts, IntArray &nws_seg, - DTMutableList &local_sol_pts, IntArray &local_sol_tris, - int &n_local_sol_tris, int &n_local_sol_pts, int &n_nw_pts, int &n_nw_tris, - int &n_ws_pts, int &n_ws_tris, int &n_ns_tris, int &n_ns_pts, - int &n_local_nws_pts, int &n_nws_pts, int &n_nws_seg, - int i, int j, int k, int Nx, int Ny, int Nz) -{ +inline void pmmc_ConstructLocalCube( + DoubleArray &SignDist, DoubleArray &Phase, double solid_isovalue, + double fluid_isovalue, DTMutableList &nw_pts, IntArray &nw_tris, + DoubleArray &values, DTMutableList &ns_pts, IntArray &ns_tris, + DTMutableList &ws_pts, IntArray &ws_tris, + DTMutableList &local_nws_pts, DTMutableList &nws_pts, + IntArray &nws_seg, DTMutableList &local_sol_pts, + IntArray &local_sol_tris, int &n_local_sol_tris, int &n_local_sol_pts, + int &n_nw_pts, int &n_nw_tris, int &n_ws_pts, int &n_ws_tris, + int &n_ns_tris, int &n_ns_pts, int &n_local_nws_pts, int &n_nws_pts, + int &n_nws_seg, int i, int j, int k, int Nx, int Ny, int Nz) { - int p,q,map; - Point A,B,C,P; + int p, q, map; + Point A, B, C, P; - // Only the local values are constructed and retained! (set counts to zero to force this) - n_local_sol_tris = 0; - n_local_sol_pts = 0; - n_local_nws_pts = 0; + // Only the local values are constructed and retained! (set counts to zero to force this) + n_local_sol_tris = 0; + n_local_sol_pts = 0; + n_local_nws_pts = 0; - n_nw_pts=0,n_ns_pts=0,n_ws_pts=0,n_nws_pts=0, map=0; - n_nw_tris=0, n_ns_tris=0, n_ws_tris=0, n_nws_seg=0; + n_nw_pts = 0, n_ns_pts = 0, n_ws_pts = 0, n_nws_pts = 0, map = 0; + n_nw_tris = 0, n_ns_tris = 0, n_ws_tris = 0, n_nws_seg = 0; - // if there is a solid phase interface in the grid cell - if (Interface(SignDist,solid_isovalue,i,j,k) == 1){ + // if there is a solid phase interface in the grid cell + if (Interface(SignDist, solid_isovalue, i, j, k) == 1) { - ///////////////////////////////////////// - /// CONSTRUCT THE LOCAL SOLID SURFACE /// - ///////////////////////////////////////// + ///////////////////////////////////////// + /// CONSTRUCT THE LOCAL SOLID SURFACE /// + ///////////////////////////////////////// - // find the local solid surface using the regular Marching Cubes algorithm - SolidMarchingCubes(SignDist,0.0,Phase,fluid_isovalue,i,j,k,Nx,Ny,Nz,local_sol_pts,n_local_sol_pts, - local_sol_tris,n_local_sol_tris,values); + // find the local solid surface using the regular Marching Cubes algorithm + SolidMarchingCubes(SignDist, 0.0, Phase, fluid_isovalue, i, j, k, Nx, + Ny, Nz, local_sol_pts, n_local_sol_pts, + local_sol_tris, n_local_sol_tris, values); - ///////////////////////////////////////// - //////// TRIM THE SOLID SURFACE ///////// - ///////////////////////////////////////// - TRIM(local_sol_pts, n_local_sol_pts, fluid_isovalue,local_sol_tris, n_local_sol_tris, - ns_pts, n_ns_pts, ns_tris, n_ns_tris, ws_pts, n_ws_pts, - ws_tris, n_ws_tris, values, local_nws_pts, n_local_nws_pts); - - + ///////////////////////////////////////// + //////// TRIM THE SOLID SURFACE ///////// + ///////////////////////////////////////// + TRIM(local_sol_pts, n_local_sol_pts, fluid_isovalue, local_sol_tris, + n_local_sol_tris, ns_pts, n_ns_pts, ns_tris, n_ns_tris, ws_pts, + n_ws_pts, ws_tris, n_ws_tris, values, local_nws_pts, + n_local_nws_pts); - ///////////////////////////////////////// - //////// WRITE COMMON LINE POINTS /////// - //////// TO MAIN ARRAYS /////// - ///////////////////////////////////////// - // SORT THE LOCAL COMMON LINE POINTS - ///////////////////////////////////////// - // Make sure the first common line point is on a face - // Common curve points are located pairwise and must - // be searched and rearranged accordingly - if (n_local_nws_pts > 0){ - for (p=0; p 0) { + for (p = 0; p < n_local_nws_pts - 1; p++) { + P = local_nws_pts(p); + if (P.x == 1.0 * i || P.x == 1.0 * (i + 1) || P.y == 1.0 * j || + P.y == 1.0 * (j + 1) || P.z == 1.0 * k || + P.z == 1.0 * (k + 1)) { + if (p % 2 == 0) { + // even points + // Swap the pair of points + local_nws_pts(p) = local_nws_pts(0); + local_nws_pts(0) = P; + P = local_nws_pts(p + 1); + local_nws_pts(p + 1) = local_nws_pts(1); + local_nws_pts(1) = P; + p = n_local_nws_pts; - } - else{ - // odd points - flip the order - local_nws_pts(p) = local_nws_pts(p-1); - local_nws_pts(p-1) = P; - p-=2; - } - // guarantee exit from the loop - } - } - // Two common curve points per triangle - // 0-(1=2)-(3=4)-... - for (p=1; p 0) { + n_nw_tris = 0; + EDGE(Phase, fluid_isovalue, SignDist, i, j, k, Nx, Ny, Nz, nw_pts, + n_nw_pts, nw_tris, n_nw_tris, nws_pts, n_nws_pts); + } else { + MC(Phase, fluid_isovalue, SignDist, i, j, k, nw_pts, n_nw_pts, + nw_tris, n_nw_tris); + } + } - ///////////////////////////////////////// - ////// CONSTRUCT THE nw SURFACE ///////// - ///////////////////////////////////////// - if ( n_local_nws_pts > 0){ - n_nw_tris =0; - EDGE(Phase, fluid_isovalue, SignDist, i,j,k, Nx, Ny, Nz, nw_pts, n_nw_pts, nw_tris, n_nw_tris, - nws_pts, n_nws_pts); - } - else { - MC(Phase, fluid_isovalue, SignDist, i,j,k, nw_pts, n_nw_pts, nw_tris, n_nw_tris); - } - } + ///////////////////////////////////////// + ////// CONSTRUCT THE nw SURFACE ///////// + ///////////////////////////////////////// - ///////////////////////////////////////// - ////// CONSTRUCT THE nw SURFACE ///////// - ///////////////////////////////////////// - - else if (Fluid_Interface(Phase,SignDist,fluid_isovalue,i,j,k) == 1){ - MC(Phase, fluid_isovalue, SignDist, i,j,k, nw_pts, n_nw_pts, nw_tris, n_nw_tris); - } + else if (Fluid_Interface(Phase, SignDist, fluid_isovalue, i, j, k) == 1) { + MC(Phase, fluid_isovalue, SignDist, i, j, k, nw_pts, n_nw_pts, nw_tris, + n_nw_tris); + } } //-------------------------------------------------------------------------------------------------------- -inline void pmmc_MeshGradient(DoubleArray &f, DoubleArray &fx, DoubleArray &fy, DoubleArray &fz, int Nx, int Ny, int Nz) -{ - int i,j,k; - // Compute the Gradient everywhere except the halo region - for (k=1; k &Points, + IntArray &Triangles, int ntris) { + int r; + double temp, area, s, s1, s2, s3; + Point A, B, C; + area = 0.0; + for (r = 0; r < ntris; r++) { + A = Points(Triangles(0, r)); + B = Points(Triangles(1, r)); + C = Points(Triangles(2, r)); + // Compute length of sides (assume dx=dy=dz) + s1 = sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + + (A.z - B.z) * (A.z - B.z)); + s2 = sqrt((A.x - C.x) * (A.x - C.x) + (A.y - C.y) * (A.y - C.y) + + (A.z - C.z) * (A.z - C.z)); + s3 = sqrt((B.x - C.x) * (B.x - C.x) + (B.y - C.y) * (B.y - C.y) + + (B.z - C.z) * (B.z - C.z)); + s = 0.5 * (s1 + s2 + s3); + temp = s * (s - s1) * (s - s2) * (s - s3); + if (temp > 0.0) + area += sqrt(temp); + } + return area; } //-------------------------------------------------------------------------------------------------------- -inline double pmmc_CubeSurfaceArea(DTMutableList &Points, IntArray &Triangles, int ntris) -{ - int r; - double temp,area,s,s1,s2,s3; - Point A,B,C; - area = 0.0; - for (r=0;r 0.0) area += sqrt(temp); - } - return area; +inline double pmmc_CubeCurveLength(DTMutableList &Points, int npts) { + int p; + double s, lwns; + Point A, B; + lwns = 0.0; + for (p = 0; p < npts - 1; p++) { + // Extract the line segment + A = Points(p); + B = Points(p + 1); + // Compute the length of the segment + s = sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + + (A.z - B.z) * (A.z - B.z)); + // Add the length to the common line + lwns += s; + } + return lwns; } //-------------------------------------------------------------------------------------------------------- -inline double pmmc_CubeCurveLength(DTMutableList &Points, int npts) -{ - int p; - double s,lwns; - Point A,B; - lwns = 0.0; - for (p=0; p < npts-1; p++){ - // Extract the line segment - A = Points(p); - B = Points(p+1); - // Compute the length of the segment - s = sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y)+(A.z-B.z)*(A.z-B.z)); - // Add the length to the common line - lwns += s; - } - return lwns; +inline void pmmc_CubeTrimSurfaceInterpValues( + DoubleArray &CubeValues, DoubleArray &MeshValues, DoubleArray &SignDist, + DTMutableList &Points, IntArray &Triangles, + DoubleArray &SurfaceValues, DoubleArray &DistanceValues, int i, int j, + int k, int npts, int ntris, double mindist, double &area, + double &integral) { + // mindist - minimum distance to consider in the average (in voxel lengths) + Point A, B, C; + int p; + double vA, vB, vC; + double dA, dB, dC; + double x, y, z; + double s, s1, s2, s3, temp; + double a, b, c, d, e, f, g, h; + + // Copy the curvature values for the cube + CubeValues(0, 0, 0) = MeshValues(i, j, k); + CubeValues(1, 0, 0) = MeshValues(i + 1, j, k); + CubeValues(0, 1, 0) = MeshValues(i, j + 1, k); + CubeValues(1, 1, 0) = MeshValues(i + 1, j + 1, k); + CubeValues(0, 0, 1) = MeshValues(i, j, k + 1); + CubeValues(1, 0, 1) = MeshValues(i + 1, j, k + 1); + CubeValues(0, 1, 1) = MeshValues(i, j + 1, k + 1); + CubeValues(1, 1, 1) = MeshValues(i + 1, j + 1, k + 1); + + // trilinear coefficients: f(x,y,z) = a+bx+cy+dz+exy+fxz+gyz+hxyz + // Evaluate the coefficients + a = CubeValues(0, 0, 0); + b = CubeValues(1, 0, 0) - a; + c = CubeValues(0, 1, 0) - a; + d = CubeValues(0, 0, 1) - a; + e = CubeValues(1, 1, 0) - a - b - c; + f = CubeValues(1, 0, 1) - a - b - d; + g = CubeValues(0, 1, 1) - a - c - d; + h = CubeValues(1, 1, 1) - a - b - c - d - e - f - g; + + for (p = 0; p < npts; p++) { + A = Points(p); + x = A.x - 1.0 * i; + y = A.y - 1.0 * j; + z = A.z - 1.0 * k; + SurfaceValues(p) = a + b * x + c * y + d * z + e * x * y + f * x * z + + g * y * z + h * x * y * z; + } + + // Copy the signed distance values for the cube + CubeValues(0, 0, 0) = SignDist(i, j, k); + CubeValues(1, 0, 0) = SignDist(i + 1, j, k); + CubeValues(0, 1, 0) = SignDist(i, j + 1, k); + CubeValues(1, 1, 0) = SignDist(i + 1, j + 1, k); + CubeValues(0, 0, 1) = SignDist(i, j, k + 1); + CubeValues(1, 0, 1) = SignDist(i + 1, j, k + 1); + CubeValues(0, 1, 1) = SignDist(i, j + 1, k + 1); + CubeValues(1, 1, 1) = SignDist(i + 1, j + 1, k + 1); + + // trilinear coefficients: f(x,y,z) = a+bx+cy+dz+exy+fxz+gyz+hxyz + // Evaluate the coefficients + a = CubeValues(0, 0, 0); + b = CubeValues(1, 0, 0) - a; + c = CubeValues(0, 1, 0) - a; + d = CubeValues(0, 0, 1) - a; + e = CubeValues(1, 1, 0) - a - b - c; + f = CubeValues(1, 0, 1) - a - b - d; + g = CubeValues(0, 1, 1) - a - c - d; + h = CubeValues(1, 1, 1) - a - b - c - d - e - f - g; + + for (p = 0; p < npts; p++) { + A = Points(p); + x = A.x - 1.0 * i; + y = A.y - 1.0 * j; + z = A.z - 1.0 * k; + DistanceValues(p) = a + b * x + c * y + d * z + e * x * y + f * x * z + + g * y * z + h * x * y * z; + } + + for (int r = 0; r < ntris; r++) { + A = Points(Triangles(0, r)); + B = Points(Triangles(1, r)); + C = Points(Triangles(2, r)); + + vA = SurfaceValues(Triangles(0, r)); + vB = SurfaceValues(Triangles(1, r)); + vC = SurfaceValues(Triangles(2, r)); + + dA = DistanceValues(Triangles(0, r)); + dB = DistanceValues(Triangles(1, r)); + dC = DistanceValues(Triangles(2, r)); + + // Compute length of sides (assume dx=dy=dz) + s1 = sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + + (A.z - B.z) * (A.z - B.z)); + s2 = sqrt((A.x - C.x) * (A.x - C.x) + (A.y - C.y) * (A.y - C.y) + + (A.z - C.z) * (A.z - C.z)); + s3 = sqrt((B.x - C.x) * (B.x - C.x) + (B.y - C.y) * (B.y - C.y) + + (B.z - C.z) * (B.z - C.z)); + s = 0.5 * (s1 + s2 + s3); + temp = s * (s - s1) * (s - s2) * (s - s3); + + if (temp > 0.0) { + if (dA > mindist) { + integral += sqrt(temp) * 0.33333333333333333 * (vA); + area += sqrt(temp) * 0.33333333333333333; + } + if (dB > mindist) { + integral += sqrt(temp) * 0.33333333333333333 * (vB); + area += sqrt(temp) * 0.33333333333333333; + } + if (dC > mindist) { + integral += sqrt(temp) * 0.33333333333333333 * (vC); + area += sqrt(temp) * 0.33333333333333333; + } + } + } +} +inline void pmmc_CubeTrimSurfaceInterpInverseValues( + DoubleArray &CubeValues, DoubleArray &MeshValues, DoubleArray &SignDist, + DTMutableList &Points, IntArray &Triangles, + DoubleArray &SurfaceValues, DoubleArray &DistanceValues, int i, int j, + int k, int npts, int ntris, double mindist, double &area, + double &integral) { + // mindist - minimum distance to consider in the average (in voxel lengths) + Point A, B, C; + int p; + double vA, vB, vC; + double dA, dB, dC; + double x, y, z; + double s, s1, s2, s3, temp; + double a, b, c, d, e, f, g, h; + + // Copy the curvature values for the cube + CubeValues(0, 0, 0) = MeshValues(i, j, k); + CubeValues(1, 0, 0) = MeshValues(i + 1, j, k); + CubeValues(0, 1, 0) = MeshValues(i, j + 1, k); + CubeValues(1, 1, 0) = MeshValues(i + 1, j + 1, k); + CubeValues(0, 0, 1) = MeshValues(i, j, k + 1); + CubeValues(1, 0, 1) = MeshValues(i + 1, j, k + 1); + CubeValues(0, 1, 1) = MeshValues(i, j + 1, k + 1); + CubeValues(1, 1, 1) = MeshValues(i + 1, j + 1, k + 1); + + // trilinear coefficients: f(x,y,z) = a+bx+cy+dz+exy+fxz+gyz+hxyz + // Evaluate the coefficients + a = CubeValues(0, 0, 0); + b = CubeValues(1, 0, 0) - a; + c = CubeValues(0, 1, 0) - a; + d = CubeValues(0, 0, 1) - a; + e = CubeValues(1, 1, 0) - a - b - c; + f = CubeValues(1, 0, 1) - a - b - d; + g = CubeValues(0, 1, 1) - a - c - d; + h = CubeValues(1, 1, 1) - a - b - c - d - e - f - g; + + for (p = 0; p < npts; p++) { + A = Points(p); + x = A.x - 1.0 * i; + y = A.y - 1.0 * j; + z = A.z - 1.0 * k; + SurfaceValues(p) = a + b * x + c * y + d * z + e * x * y + f * x * z + + g * y * z + h * x * y * z; + } + + // Copy the signed distance values for the cube + CubeValues(0, 0, 0) = SignDist(i, j, k); + CubeValues(1, 0, 0) = SignDist(i + 1, j, k); + CubeValues(0, 1, 0) = SignDist(i, j + 1, k); + CubeValues(1, 1, 0) = SignDist(i + 1, j + 1, k); + CubeValues(0, 0, 1) = SignDist(i, j, k + 1); + CubeValues(1, 0, 1) = SignDist(i + 1, j, k + 1); + CubeValues(0, 1, 1) = SignDist(i, j + 1, k + 1); + CubeValues(1, 1, 1) = SignDist(i + 1, j + 1, k + 1); + + // trilinear coefficients: f(x,y,z) = a+bx+cy+dz+exy+fxz+gyz+hxyz + // Evaluate the coefficients + a = CubeValues(0, 0, 0); + b = CubeValues(1, 0, 0) - a; + c = CubeValues(0, 1, 0) - a; + d = CubeValues(0, 0, 1) - a; + e = CubeValues(1, 1, 0) - a - b - c; + f = CubeValues(1, 0, 1) - a - b - d; + g = CubeValues(0, 1, 1) - a - c - d; + h = CubeValues(1, 1, 1) - a - b - c - d - e - f - g; + + for (p = 0; p < npts; p++) { + A = Points(p); + x = A.x - 1.0 * i; + y = A.y - 1.0 * j; + z = A.z - 1.0 * k; + DistanceValues(p) = a + b * x + c * y + d * z + e * x * y + f * x * z + + g * y * z + h * x * y * z; + } + + for (int r = 0; r < ntris; r++) { + A = Points(Triangles(0, r)); + B = Points(Triangles(1, r)); + C = Points(Triangles(2, r)); + + vA = SurfaceValues(Triangles(0, r)); + vB = SurfaceValues(Triangles(1, r)); + vC = SurfaceValues(Triangles(2, r)); + + dA = DistanceValues(Triangles(0, r)); + dB = DistanceValues(Triangles(1, r)); + dC = DistanceValues(Triangles(2, r)); + + // Compute length of sides (assume dx=dy=dz) + s1 = sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + + (A.z - B.z) * (A.z - B.z)); + s2 = sqrt((A.x - C.x) * (A.x - C.x) + (A.y - C.y) * (A.y - C.y) + + (A.z - C.z) * (A.z - C.z)); + s3 = sqrt((B.x - C.x) * (B.x - C.x) + (B.y - C.y) * (B.y - C.y) + + (B.z - C.z) * (B.z - C.z)); + s = 0.5 * (s1 + s2 + s3); + temp = s * (s - s1) * (s - s2) * (s - s3); + + if (temp > 0.0) { + if (dA > mindist && vA != 0.0) { + integral += sqrt(temp) * 0.33333333333333333 * (1.0 / vA); + area += sqrt(temp) * 0.33333333333333333; + } + if (dB > mindist && vB != 0.0) { + integral += sqrt(temp) * 0.33333333333333333 * (1.0 / vB); + area += sqrt(temp) * 0.33333333333333333; + } + if (dC > mindist && vC != 0.0) { + integral += sqrt(temp) * 0.33333333333333333 * (1.0 / vC); + area += sqrt(temp) * 0.33333333333333333; + } + } + } +} + +inline double pmmc_CubeSurfaceInterpValue(DoubleArray &CubeValues, + DoubleArray &MeshValues, + DTMutableList &Points, + IntArray &Triangles, + DoubleArray &SurfaceValues, int i, + int j, int k, int npts, int ntris) { + Point A, B, C; + int p; + double vA, vB, vC; + double x, y, z; + double s, s1, s2, s3, temp; + double a, b, c, d, e, f, g, h; + double integral; + + // Copy the curvature values for the cube + CubeValues(0, 0, 0) = MeshValues(i, j, k); + CubeValues(1, 0, 0) = MeshValues(i + 1, j, k); + CubeValues(0, 1, 0) = MeshValues(i, j + 1, k); + CubeValues(1, 1, 0) = MeshValues(i + 1, j + 1, k); + CubeValues(0, 0, 1) = MeshValues(i, j, k + 1); + CubeValues(1, 0, 1) = MeshValues(i + 1, j, k + 1); + CubeValues(0, 1, 1) = MeshValues(i, j + 1, k + 1); + CubeValues(1, 1, 1) = MeshValues(i + 1, j + 1, k + 1); + + // trilinear coefficients: f(x,y,z) = a+bx+cy+dz+exy+fxz+gyz+hxyz + // Evaluate the coefficients + a = CubeValues(0, 0, 0); + b = CubeValues(1, 0, 0) - a; + c = CubeValues(0, 1, 0) - a; + d = CubeValues(0, 0, 1) - a; + e = CubeValues(1, 1, 0) - a - b - c; + f = CubeValues(1, 0, 1) - a - b - d; + g = CubeValues(0, 1, 1) - a - c - d; + h = CubeValues(1, 1, 1) - a - b - c - d - e - f - g; + + for (p = 0; p < npts; p++) { + A = Points(p); + x = A.x - 1.0 * i; + y = A.y - 1.0 * j; + z = A.z - 1.0 * k; + SurfaceValues(p) = a + b * x + c * y + d * z + e * x * y + f * x * z + + g * y * z + h * x * y * z; + } + + integral = 0.0; + for (int r = 0; r < ntris; r++) { + A = Points(Triangles(0, r)); + B = Points(Triangles(1, r)); + C = Points(Triangles(2, r)); + vA = SurfaceValues(Triangles(0, r)); + vB = SurfaceValues(Triangles(1, r)); + vC = SurfaceValues(Triangles(2, r)); + // Compute length of sides (assume dx=dy=dz) + s1 = sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + + (A.z - B.z) * (A.z - B.z)); + s2 = sqrt((A.x - C.x) * (A.x - C.x) + (A.y - C.y) * (A.y - C.y) + + (A.z - C.z) * (A.z - C.z)); + s3 = sqrt((B.x - C.x) * (B.x - C.x) + (B.y - C.y) * (B.y - C.y) + + (B.z - C.z) * (B.z - C.z)); + s = 0.5 * (s1 + s2 + s3); + temp = s * (s - s1) * (s - s2) * (s - s3); + if (temp > 0.0) + integral += sqrt(temp) * 0.33333333333333333 * (vA + vB + vC); + } + return integral; } //-------------------------------------------------------------------------------------------------------- -inline void pmmc_CubeTrimSurfaceInterpValues(DoubleArray &CubeValues, DoubleArray &MeshValues, DoubleArray &SignDist, - DTMutableList &Points, IntArray &Triangles, - DoubleArray &SurfaceValues, DoubleArray &DistanceValues, int i, int j, int k, int npts, int ntris, - double mindist, double &area, double &integral) -{ - // mindist - minimum distance to consider in the average (in voxel lengths) - Point A,B,C; - int p; - double vA,vB,vC; - double dA,dB,dC; - double x,y,z; - double s,s1,s2,s3,temp; - double a,b,c,d,e,f,g,h; +inline double pmmc_CubeCurveInterpValue(DoubleArray &CubeValues, + DoubleArray &CurveValues, + DTMutableList &Points, int i, + int j, int k, int npts) { + int p; + Point A, B; + double vA, vB; + double x, y, z; + // double s,s1,s2,s3,temp; + double a, b, c, d, e, f, g, h; + double integral; + double length; - // Copy the curvature values for the cube - CubeValues(0,0,0) = MeshValues(i,j,k); - CubeValues(1,0,0) = MeshValues(i+1,j,k); - CubeValues(0,1,0) = MeshValues(i,j+1,k); - CubeValues(1,1,0) = MeshValues(i+1,j+1,k); - CubeValues(0,0,1) = MeshValues(i,j,k+1); - CubeValues(1,0,1) = MeshValues(i+1,j,k+1); - CubeValues(0,1,1) = MeshValues(i,j+1,k+1); - CubeValues(1,1,1) = MeshValues(i+1,j+1,k+1); - - // trilinear coefficients: f(x,y,z) = a+bx+cy+dz+exy+fxz+gyz+hxyz - // Evaluate the coefficients - a = CubeValues(0,0,0); - b = CubeValues(1,0,0)-a; - c = CubeValues(0,1,0)-a; - d = CubeValues(0,0,1)-a; - e = CubeValues(1,1,0)-a-b-c; - f = CubeValues(1,0,1)-a-b-d; - g = CubeValues(0,1,1)-a-c-d; - h = CubeValues(1,1,1)-a-b-c-d-e-f-g; + // trilinear coefficients: f(x,y,z) = a+bx+cy+dz+exy+fxz+gyz+hxyz + // Evaluate the coefficients + a = CubeValues(0, 0, 0); + b = CubeValues(1, 0, 0) - a; + c = CubeValues(0, 1, 0) - a; + d = CubeValues(0, 0, 1) - a; + e = CubeValues(1, 1, 0) - a - b - c; + f = CubeValues(1, 0, 1) - a - b - d; + g = CubeValues(0, 1, 1) - a - c - d; + h = CubeValues(1, 1, 1) - a - b - c - d - e - f - g; - for (p=0; p 0.0){ - if (dA > mindist){ - integral += sqrt(temp)*0.33333333333333333*(vA); - area += sqrt(temp)*0.33333333333333333; - } - if (dB > mindist){ - integral += sqrt(temp)*0.33333333333333333*(vB); - area += sqrt(temp)*0.33333333333333333; - } - if (dC > mindist){ - integral += sqrt(temp)*0.33333333333333333*(vC); - area += sqrt(temp)*0.33333333333333333; - } - } - } -} -inline void pmmc_CubeTrimSurfaceInterpInverseValues(DoubleArray &CubeValues, DoubleArray &MeshValues, DoubleArray &SignDist, - DTMutableList &Points, IntArray &Triangles, - DoubleArray &SurfaceValues, DoubleArray &DistanceValues, int i, int j, int k, int npts, int ntris, - double mindist, double &area, double &integral) -{ - // mindist - minimum distance to consider in the average (in voxel lengths) - Point A,B,C; - int p; - double vA,vB,vC; - double dA,dB,dC; - double x,y,z; - double s,s1,s2,s3,temp; - double a,b,c,d,e,f,g,h; - - // Copy the curvature values for the cube - CubeValues(0,0,0) = MeshValues(i,j,k); - CubeValues(1,0,0) = MeshValues(i+1,j,k); - CubeValues(0,1,0) = MeshValues(i,j+1,k); - CubeValues(1,1,0) = MeshValues(i+1,j+1,k); - CubeValues(0,0,1) = MeshValues(i,j,k+1); - CubeValues(1,0,1) = MeshValues(i+1,j,k+1); - CubeValues(0,1,1) = MeshValues(i,j+1,k+1); - CubeValues(1,1,1) = MeshValues(i+1,j+1,k+1); - - // trilinear coefficients: f(x,y,z) = a+bx+cy+dz+exy+fxz+gyz+hxyz - // Evaluate the coefficients - a = CubeValues(0,0,0); - b = CubeValues(1,0,0)-a; - c = CubeValues(0,1,0)-a; - d = CubeValues(0,0,1)-a; - e = CubeValues(1,1,0)-a-b-c; - f = CubeValues(1,0,1)-a-b-d; - g = CubeValues(0,1,1)-a-c-d; - h = CubeValues(1,1,1)-a-b-c-d-e-f-g; - - for (p=0; p 0.0){ - if (dA > mindist && vA != 0.0){ - integral += sqrt(temp)*0.33333333333333333*(1.0/vA); - area += sqrt(temp)*0.33333333333333333; - } - if (dB > mindist && vB != 0.0){ - integral += sqrt(temp)*0.33333333333333333*(1.0/vB); - area += sqrt(temp)*0.33333333333333333; - } - if (dC > mindist && vC != 0.0){ - integral += sqrt(temp)*0.33333333333333333*(1.0/vC); - area += sqrt(temp)*0.33333333333333333; - } - } - } -} - -inline double pmmc_CubeSurfaceInterpValue(DoubleArray &CubeValues, DoubleArray &MeshValues, DTMutableList &Points, IntArray &Triangles, - DoubleArray &SurfaceValues, int i, int j, int k, int npts, int ntris) -{ - Point A,B,C; - int p; - double vA,vB,vC; - double x,y,z; - double s,s1,s2,s3,temp; - double a,b,c,d,e,f,g,h; - double integral; - - // Copy the curvature values for the cube - CubeValues(0,0,0) = MeshValues(i,j,k); - CubeValues(1,0,0) = MeshValues(i+1,j,k); - CubeValues(0,1,0) = MeshValues(i,j+1,k); - CubeValues(1,1,0) = MeshValues(i+1,j+1,k); - CubeValues(0,0,1) = MeshValues(i,j,k+1); - CubeValues(1,0,1) = MeshValues(i+1,j,k+1); - CubeValues(0,1,1) = MeshValues(i,j+1,k+1); - CubeValues(1,1,1) = MeshValues(i+1,j+1,k+1); - - // trilinear coefficients: f(x,y,z) = a+bx+cy+dz+exy+fxz+gyz+hxyz - // Evaluate the coefficients - a = CubeValues(0,0,0); - b = CubeValues(1,0,0)-a; - c = CubeValues(0,1,0)-a; - d = CubeValues(0,0,1)-a; - e = CubeValues(1,1,0)-a-b-c; - f = CubeValues(1,0,1)-a-b-d; - g = CubeValues(0,1,1)-a-c-d; - h = CubeValues(1,1,1)-a-b-c-d-e-f-g; - - for (p=0; p 0.0) integral += sqrt(temp)*0.33333333333333333*(vA+vB+vC); - } - return integral; + integral = 0.0; + for (p = 0; p < npts - 1; p++) { + // Extract the line segment + A = Points(p); + B = Points(p + 1); + vA = CurveValues(p); + vB = CurveValues(p + 1); + // Compute the length of the segment + length = sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + + (A.z - B.z) * (A.z - B.z)); + integral += 0.5 * length * (vA + vB); + } + return integral; } //-------------------------------------------------------------------------------------------------------- -inline double pmmc_CubeCurveInterpValue(DoubleArray &CubeValues, DoubleArray &CurveValues, - DTMutableList &Points, int i, int j, int k, int npts) -{ - int p; - Point A,B; - double vA,vB; - double x,y,z; -// double s,s1,s2,s3,temp; - double a,b,c,d,e,f,g,h; - double integral; - double length; +inline double pmmc_CubeContactAngle(DoubleArray &CubeValues, + DoubleArray &CurveValues, DoubleArray &Fx, + DoubleArray &Fy, DoubleArray &Fz, + DoubleArray &Sx, DoubleArray &Sy, + DoubleArray &Sz, + DTMutableList &Points, int i, int j, + int k, int npts) { + int p; + Point A, B; + double vA, vB; + double x, y, z; + // double s,s1,s2,s3,temp; + double a, b, c, d, e, f, g, h; + double integral; + double length; + double denom; - // trilinear coefficients: f(x,y,z) = a+bx+cy+dz+exy+fxz+gyz+hxyz - // Evaluate the coefficients - a = CubeValues(0,0,0); - b = CubeValues(1,0,0)-a; - c = CubeValues(0,1,0)-a; - d = CubeValues(0,0,1)-a; - e = CubeValues(1,1,0)-a-b-c; - f = CubeValues(1,0,1)-a-b-d; - g = CubeValues(0,1,1)-a-c-d; - h = CubeValues(1,1,1)-a-b-c-d-e-f-g; + // theta = acos ( -(gradF*gradS) / (|gradF| |gradS|) ) - for (p=0; p &Points, int i, int j, int k, int npts) -{ - int p; - Point A,B; - double vA,vB; - double x,y,z; -// double s,s1,s2,s3,temp; - double a,b,c,d,e,f,g,h; - double integral; - double length; - double denom; +inline void +pmmc_CubeSurfaceInterpVector(DoubleArray &Vec_x, DoubleArray &Vec_y, + DoubleArray &Vec_z, DoubleArray &CubeValues, + DTMutableList &Points, IntArray &Triangles, + DoubleArray &SurfaceVector, DoubleArray &VecAvg, + int i, int j, int k, int npts, int ntris) { + Point A, B, C; + int p; + double vA, vB, vC; + double x, y, z; + double s, s1, s2, s3, temp; + double a, b, c, d, e, f, g, h; - // theta = acos ( -(gradF*gradS) / (|gradF| |gradS|) ) + // ................x component ............................. + // Copy the curvature values for the cube + CubeValues(0, 0, 0) = Vec_x(i, j, k); + CubeValues(1, 0, 0) = Vec_x(i + 1, j, k); + CubeValues(0, 1, 0) = Vec_x(i, j + 1, k); + CubeValues(1, 1, 0) = Vec_x(i + 1, j + 1, k); + CubeValues(0, 0, 1) = Vec_x(i, j, k + 1); + CubeValues(1, 0, 1) = Vec_x(i + 1, j, k + 1); + CubeValues(0, 1, 1) = Vec_x(i, j + 1, k + 1); + CubeValues(1, 1, 1) = Vec_x(i + 1, j + 1, k + 1); - denom = sqrt(pow(Fx(i,j,k),2)+pow(Fy(i,j,k),2)+pow(Fz(i,j,k),2)) - *sqrt(pow(Sx(i,j,k),2)+pow(Sy(i,j,k),2)+pow(Sz(i,j,k),2)); - if (denom == 0.0) denom =1.0; - CubeValues(0,0,0) = -( Fx(i,j,k)*Sx(i,j,k)+Fy(i,j,k)*Sy(i,j,k)+Fz(i,j,k)*Sz(i,j,k) )/(denom); - - denom = sqrt(pow(Fx(i+1,j,k),2)+pow(Fy(i+1,j,k),2)+pow(Fz(i+1,j,k),2)) - *sqrt(pow(Sx(i+1,j,k),2)+pow(Sy(i+1,j,k),2)+pow(Sz(i+1,j,k),2)); - if (denom == 0.0) denom =1.0; - CubeValues(1,0,0) = -( Fx(i+1,j,k)*Sx(i+1,j,k)+Fy(i+1,j,k)*Sy(i+1,j,k)+Fz(i+1,j,k)*Sz(i+1,j,k) ) - /( denom ); + // trilinear coefficients: f(x,y,z) = a+bx+cy+dz+exy+fxz+gyz+hxyz + a = CubeValues(0, 0, 0); + b = CubeValues(1, 0, 0) - a; + c = CubeValues(0, 1, 0) - a; + d = CubeValues(0, 0, 1) - a; + e = CubeValues(1, 1, 0) - a - b - c; + f = CubeValues(1, 0, 1) - a - b - d; + g = CubeValues(0, 1, 1) - a - c - d; + h = CubeValues(1, 1, 1) - a - b - c - d - e - f - g; - denom = sqrt(pow(Fx(i,j+1,k),2)+pow(Fy(i,j+1,k),2)+pow(Fz(i,j+1,k),2)) - *sqrt(pow(Sx(i,j+1,k),2)+pow(Sy(i,j+1,k),2)+pow(Sz(i,j+1,k),2)); - if (denom == 0.0) denom =1.0; - CubeValues(0,1,0) = -( Fx(i,j+1,k)*Sx(i,j+1,k)+Fy(i,j+1,k)*Sy(i,j+1,k)+Fz(i,j+1,k)*Sz(i,j+1,k) ) - /( denom ); + for (p = 0; p < npts; p++) { + A = Points(p); + x = A.x - 1.0 * i; + y = A.y - 1.0 * j; + z = A.z - 1.0 * k; + SurfaceVector(p) = a + b * x + c * y + d * z + e * x * y + f * x * z + + g * y * z + h * x * y * z; + } - denom = sqrt(pow(Fx(i,j,k+1),2)+pow(Fy(i,j,k+1),2)+pow(Fz(i,j,k+1),2)) - *sqrt(pow(Sx(i,j,k+1),2)+pow(Sy(i,j,k+1),2)+pow(Sz(i,j,k+1),2)); - if (denom == 0.0) denom =1.0; - CubeValues(0,0,1) = -( Fx(i,j,k+1)*Sx(i,j,k+1)+Fy(i,j,k+1)*Sy(i,j,k+1)+Fz(i,j,k+1)*Sz(i,j,k+1) ) - /( denom); + // ................y component ............................. + // Copy the curvature values for the cube + CubeValues(0, 0, 0) = Vec_y(i, j, k); + CubeValues(1, 0, 0) = Vec_y(i + 1, j, k); + CubeValues(0, 1, 0) = Vec_y(i, j + 1, k); + CubeValues(1, 1, 0) = Vec_y(i + 1, j + 1, k); + CubeValues(0, 0, 1) = Vec_y(i, j, k + 1); + CubeValues(1, 0, 1) = Vec_y(i + 1, j, k + 1); + CubeValues(0, 1, 1) = Vec_y(i, j + 1, k + 1); + CubeValues(1, 1, 1) = Vec_y(i + 1, j + 1, k + 1); - denom = sqrt(pow(Fx(i+1,j+1,k),2)+pow(Fy(i+1,j+1,k),2)+pow(Fz(i+1,j+1,k),2)) - *sqrt(pow(Sx(i+1,j+1,k),2)+pow(Sy(i+1,j+1,k),2)+pow(Sz(i+1,j+1,k),2)); - if (denom == 0.0) denom =1.0; - CubeValues(1,1,0) = -( Fx(i+1,j+1,k)*Sx(i+1,j+1,k)+Fy(i+1,j+1,k)*Sy(i+1,j+1,k)+Fz(i+1,j+1,k)*Sz(i+1,j+1,k) ) - /( denom ); + // trilinear coefficients: f(x,y,z) = a+bx+cy+dz+exy+fxz+gyz+hxyz + a = CubeValues(0, 0, 0); + b = CubeValues(1, 0, 0) - a; + c = CubeValues(0, 1, 0) - a; + d = CubeValues(0, 0, 1) - a; + e = CubeValues(1, 1, 0) - a - b - c; + f = CubeValues(1, 0, 1) - a - b - d; + g = CubeValues(0, 1, 1) - a - c - d; + h = CubeValues(1, 1, 1) - a - b - c - d - e - f - g; - denom = sqrt(pow(Fx(i+1,j,k+1),2)+pow(Fy(i+1,j,k+1),2)+pow(Fz(i+1,j,k+1),2)) - *sqrt(pow(Sx(i+1,j,k+1),2)+pow(Sy(i+1,j,k+1),2)+pow(Sz(i+1,j,k+1),2)); - if (denom == 0.0) denom =1.0; - CubeValues(1,0,1) = -( Fx(i+1,j,k+1)*Sx(i+1,j,k+1)+Fy(i+1,j,k+1)*Sy(i+1,j,k+1)+Fz(i+1,j,k+1)*Sz(i+1,j,k+1) ) - /( denom ); + for (p = 0; p < npts; p++) { + A = Points(p); + x = A.x - 1.0 * i; + y = A.y - 1.0 * j; + z = A.z - 1.0 * k; + SurfaceVector(npts + p) = a + b * x + c * y + d * z + e * x * y + + f * x * z + g * y * z + h * x * y * z; + } - denom = sqrt(pow(Fx(i,j+1,k+1),2)+pow(Fy(i,j+1,k+1),2)+pow(Fz(i,j+1,k+1),2)) - *sqrt(pow(Sx(i,j+1,k+1),2)+pow(Sy(i,j+1,k+1),2)+pow(Sz(i,j+1,k+1),2)); - if (denom == 0.0) denom =1.0; - CubeValues(0,1,1) = -( Fx(i,j+1,k+1)*Sx(i,j+1,k+1)+Fy(i,j+1,k+1)*Sy(i,j+1,k+1)+Fz(i,j+1,k+1)*Sz(i,j+1,k+1) ) - /( denom ); + // ................z component ............................. + // Copy the curvature values for the cube + CubeValues(0, 0, 0) = Vec_z(i, j, k); + CubeValues(1, 0, 0) = Vec_z(i + 1, j, k); + CubeValues(0, 1, 0) = Vec_z(i, j + 1, k); + CubeValues(1, 1, 0) = Vec_z(i + 1, j + 1, k); + CubeValues(0, 0, 1) = Vec_z(i, j, k + 1); + CubeValues(1, 0, 1) = Vec_z(i + 1, j, k + 1); + CubeValues(0, 1, 1) = Vec_z(i, j + 1, k + 1); + CubeValues(1, 1, 1) = Vec_z(i + 1, j + 1, k + 1); - denom = sqrt(pow(Fx(i+1,j+1,k+1),2)+pow(Fy(i+1,j+1,k+1),2)+pow(Fz(i+1,j+1,k+1),2)) - *sqrt(pow(Sx(i+1,j+1,k+1),2)+pow(Sy(i+1,j+1,k+1),2)+pow(Sz(i+1,j+1,k+1),2)); - if (denom == 0.0) denom =1.0; - CubeValues(1,1,1) = -( Fx(i+1,j+1,k+1)*Sx(i+1,j+1,k+1)+Fy(i+1,j+1,k+1)*Sy(i+1,j+1,k+1)+Fz(i+1,j+1,k+1)*Sz(i+1,j+1,k+1) ) - /( denom ); + // trilinear coefficients: f(x,y,z) = a+bx+cy+dz+exy+fxz+gyz+hxyz + a = CubeValues(0, 0, 0); + b = CubeValues(1, 0, 0) - a; + c = CubeValues(0, 1, 0) - a; + d = CubeValues(0, 0, 1) - a; + e = CubeValues(1, 1, 0) - a - b - c; + f = CubeValues(1, 0, 1) - a - b - d; + g = CubeValues(0, 1, 1) - a - c - d; + h = CubeValues(1, 1, 1) - a - b - c - d - e - f - g; - // trilinear coefficients: f(x,y,z) = a+bx+cy+dz+exy+fxz+gyz+hxyz - // Evaluate the coefficients - a = CubeValues(0,0,0); - b = CubeValues(1,0,0)-a; - c = CubeValues(0,1,0)-a; - d = CubeValues(0,0,1)-a; - e = CubeValues(1,1,0)-a-b-c; - f = CubeValues(1,0,1)-a-b-d; - g = CubeValues(0,1,1)-a-c-d; - h = CubeValues(1,1,1)-a-b-c-d-e-f-g; - - for (p=0; p 0.0) { + // Increment the averaged values + // x component + vA = SurfaceVector(Triangles(0, r)); + vB = SurfaceVector(Triangles(1, r)); + vC = SurfaceVector(Triangles(2, r)); + VecAvg(0) += sqrt(temp) * 0.33333333333333333 * (vA + vB + vC); + // y component + vA = SurfaceVector(npts + Triangles(0, r)); + vB = SurfaceVector(npts + Triangles(1, r)); + vC = SurfaceVector(npts + Triangles(2, r)); + VecAvg(1) += sqrt(temp) * 0.33333333333333333 * (vA + vB + vC); + // z component + vA = SurfaceVector(2 * npts + Triangles(0, r)); + vB = SurfaceVector(2 * npts + Triangles(1, r)); + vC = SurfaceVector(2 * npts + Triangles(2, r)); + VecAvg(2) += sqrt(temp) * 0.33333333333333333 * (vA + vB + vC); + } + } } //-------------------------------------------------------------------------------------------------------- -inline void pmmc_CubeSurfaceInterpVector(DoubleArray &Vec_x, DoubleArray &Vec_y, DoubleArray &Vec_z, - DoubleArray &CubeValues, DTMutableList &Points, IntArray &Triangles, - DoubleArray &SurfaceVector, DoubleArray &VecAvg, int i, int j, int k, int npts, int ntris) -{ - Point A,B,C; - int p; - double vA,vB,vC; - double x,y,z; - double s,s1,s2,s3,temp; - double a,b,c,d,e,f,g,h; - - // ................x component ............................. - // Copy the curvature values for the cube - CubeValues(0,0,0) = Vec_x(i,j,k); - CubeValues(1,0,0) = Vec_x(i+1,j,k); - CubeValues(0,1,0) = Vec_x(i,j+1,k); - CubeValues(1,1,0) = Vec_x(i+1,j+1,k); - CubeValues(0,0,1) = Vec_x(i,j,k+1); - CubeValues(1,0,1) = Vec_x(i+1,j,k+1); - CubeValues(0,1,1) = Vec_x(i,j+1,k+1); - CubeValues(1,1,1) = Vec_x(i+1,j+1,k+1); - - // trilinear coefficients: f(x,y,z) = a+bx+cy+dz+exy+fxz+gyz+hxyz - a = CubeValues(0,0,0); - b = CubeValues(1,0,0)-a; - c = CubeValues(0,1,0)-a; - d = CubeValues(0,0,1)-a; - e = CubeValues(1,1,0)-a-b-c; - f = CubeValues(1,0,1)-a-b-d; - g = CubeValues(0,1,1)-a-c-d; - h = CubeValues(1,1,1)-a-b-c-d-e-f-g; - - for (p=0; p 0.0){ - // Increment the averaged values - // x component - vA = SurfaceVector(Triangles(0,r)); - vB = SurfaceVector(Triangles(1,r)); - vC = SurfaceVector(Triangles(2,r)); - VecAvg(0) += sqrt(temp)*0.33333333333333333*(vA+vB+vC); - // y component - vA = SurfaceVector(npts+Triangles(0,r)); - vB = SurfaceVector(npts+Triangles(1,r)); - vC = SurfaceVector(npts+Triangles(2,r)); - VecAvg(1) += sqrt(temp)*0.33333333333333333*(vA+vB+vC); - // z component - vA = SurfaceVector(2*npts+Triangles(0,r)); - vB = SurfaceVector(2*npts+Triangles(1,r)); - vC = SurfaceVector(2*npts+Triangles(2,r)); - VecAvg(2) += sqrt(temp)*0.33333333333333333*(vA+vB+vC); - } - } +inline double pmmc_CubeSurfaceOrientation(DoubleArray &Orientation, + DTMutableList &Points, + IntArray &Triangles, int ntris) { + int r; + double temp, area, s, s1, s2, s3; + double nx, ny, nz, normsq; + Point A, B, C; + area = 0.0; + for (r = 0; r < ntris; r++) { + A = Points(Triangles(0, r)); + B = Points(Triangles(1, r)); + C = Points(Triangles(2, r)); + // Compute the triangle normal vector + nx = (B.y - A.y) * (C.z - A.z) - (B.z - A.z) * (C.y - A.y); + ny = (B.z - A.z) * (C.x - A.x) - (B.x - A.x) * (C.z - A.z); + nz = (B.x - A.x) * (C.y - A.y) - (B.y - A.y) * (C.x - A.x); + normsq = 1.0 / (nx * nx + ny * ny + nz * nz); + // Compute length of sides (assume dx=dy=dz) + s1 = sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + + (A.z - B.z) * (A.z - B.z)); + s2 = sqrt((A.x - C.x) * (A.x - C.x) + (A.y - C.y) * (A.y - C.y) + + (A.z - C.z) * (A.z - C.z)); + s3 = sqrt((B.x - C.x) * (B.x - C.x) + (B.y - C.y) * (B.y - C.y) + + (B.z - C.z) * (B.z - C.z)); + s = 0.5 * (s1 + s2 + s3); + temp = s * (s - s1) * (s - s2) * (s - s3); + if (temp > 0.0) { + temp = sqrt(temp); + area += temp; + Orientation(0) += temp * nx * nx * normsq; // Gxx + Orientation(1) += temp * ny * ny * normsq; // Gyy + Orientation(2) += temp * nz * nz * normsq; // Gzz + Orientation(3) += temp * nx * ny * normsq; // Gxy + Orientation(4) += temp * nx * nz * normsq; // Gxz + Orientation(5) += temp * ny * nz * normsq; // Gyz + } + } + return area; } //-------------------------------------------------------------------------------------------------------- -inline double pmmc_CubeSurfaceOrientation(DoubleArray &Orientation, DTMutableList &Points, IntArray &Triangles, int ntris) -{ - int r; - double temp,area,s,s1,s2,s3; - double nx,ny,nz,normsq; - Point A,B,C; - area = 0.0; - for (r=0;r 0.0){ - temp = sqrt(temp); - area += temp; - Orientation(0) += temp*nx*nx*normsq; // Gxx - Orientation(1) += temp*ny*ny*normsq; // Gyy - Orientation(2) += temp*nz*nz*normsq; // Gzz - Orientation(3) += temp*nx*ny*normsq; // Gxy - Orientation(4) += temp*nx*nz*normsq; // Gxz - Orientation(5) += temp*ny*nz*normsq; // Gyz - } - } - return area; -} -//-------------------------------------------------------------------------------------------------------- -inline double pmmc_CommonCurveSpeed(DoubleArray &CubeValues, DoubleArray &dPdt, DoubleArray &ReturnVector, - DoubleArray &Fx, DoubleArray &Fy, DoubleArray &Fz, - DoubleArray &Sx, DoubleArray &Sy, DoubleArray &Sz, - DTMutableList &Points, int i, int j, int k, int npts) -{ - NULL_USE( CubeValues ); +inline double pmmc_CommonCurveSpeed(DoubleArray &CubeValues, DoubleArray &dPdt, + DoubleArray &ReturnVector, DoubleArray &Fx, + DoubleArray &Fy, DoubleArray &Fz, + DoubleArray &Sx, DoubleArray &Sy, + DoubleArray &Sz, + DTMutableList &Points, int i, int j, + int k, int npts) { + NULL_USE(CubeValues); - int p; - double s,lwns,norm; - double zeta; - double tangent_x,tangent_y,tangent_z; - double ns_x, ns_y, ns_z; - double nwn_x, nwn_y, nwn_z; - double nwns_x, nwns_y, nwns_z; - Point P,A,B; - lwns = 0.0; - double ReturnValue = 0; + int p; + double s, lwns, norm; + double zeta; + double tangent_x, tangent_y, tangent_z; + double ns_x, ns_y, ns_z; + double nwn_x, nwn_y, nwn_z; + double nwns_x, nwns_y, nwns_z; + Point P, A, B; + lwns = 0.0; + double ReturnValue = 0; NULL_USE(lwns); - TriLinPoly Px,Py,Pz,SDx,SDy,SDz,Pt; - Px.assign(Fx,i,j,k); - Py.assign(Fy,i,j,k); - Pz.assign(Fz,i,j,k); - SDx.assign(Sx,i,j,k); - SDy.assign(Sy,i,j,k); - SDz.assign(Sz,i,j,k); - Pt.assign(dPdt,i,j,k); - - for (p=0; p < npts-1; p++){ - // Extract the line segment - A = Points(p); - B = Points(p+1); - // Midpoint of the line - P.x = 0.5*(A.x+B.x); - P.y = 0.5*(A.y+B.y); - P.z = 0.5*(A.z+B.z); - // Compute the curve tangent - tangent_x = A.x - B.x; - tangent_y = A.y - B.y; - tangent_z = A.z - B.z; + TriLinPoly Px, Py, Pz, SDx, SDy, SDz, Pt; + Px.assign(Fx, i, j, k); + Py.assign(Fy, i, j, k); + Pz.assign(Fz, i, j, k); + SDx.assign(Sx, i, j, k); + SDy.assign(Sy, i, j, k); + SDz.assign(Sz, i, j, k); + Pt.assign(dPdt, i, j, k); - // Get the normal to the solid surface - ns_x = SDx.eval(P); - ns_y = SDy.eval(P); - ns_z = SDz.eval(P); - norm = ns_x*ns_x + ns_y*ns_y + ns_z*ns_z; - if (norm > 0.0){ - ns_x /= norm; - ns_y /= norm; - ns_z /= norm; - } + for (p = 0; p < npts - 1; p++) { + // Extract the line segment + A = Points(p); + B = Points(p + 1); + // Midpoint of the line + P.x = 0.5 * (A.x + B.x); + P.y = 0.5 * (A.y + B.y); + P.z = 0.5 * (A.z + B.z); + // Compute the curve tangent + tangent_x = A.x - B.x; + tangent_y = A.y - B.y; + tangent_z = A.z - B.z; - // Get the Color gradient - nwn_x = Px.eval(P); - nwn_y = Py.eval(P); - nwn_z = Pz.eval(P); - // to compute zeta, consider the change only in the plane of the solid surface - norm = nwn_x*ns_x + nwn_y*ns_y + nwn_z*ns_z; // component of color gradient aligned with solid - nwns_x = nwn_x - norm*ns_x; - nwns_y = nwn_y - norm*ns_y; - nwns_z = nwn_z - norm*ns_z; - // now {nwns_x, nwns_y, nwns_z} is the color gradient confined to the solid plane - norm = sqrt(nwns_x*nwns_x + nwns_y*nwns_y + nwns_z*nwns_z); - zeta = -Pt.eval(P) / norm; - // normalize the normal to the common curve within the solid surface - if (norm > 0.0){ - nwns_x /= norm; - nwns_y /= norm; - nwns_z /= norm; - } + // Get the normal to the solid surface + ns_x = SDx.eval(P); + ns_y = SDy.eval(P); + ns_z = SDz.eval(P); + norm = ns_x * ns_x + ns_y * ns_y + ns_z * ns_z; + if (norm > 0.0) { + ns_x /= norm; + ns_y /= norm; + ns_z /= norm; + } - /* + // Get the Color gradient + nwn_x = Px.eval(P); + nwn_y = Py.eval(P); + nwn_z = Pz.eval(P); + // to compute zeta, consider the change only in the plane of the solid surface + norm = nwn_x * ns_x + nwn_y * ns_y + + nwn_z * ns_z; // component of color gradient aligned with solid + nwns_x = nwn_x - norm * ns_x; + nwns_y = nwn_y - norm * ns_y; + nwns_z = nwn_z - norm * ns_z; + // now {nwns_x, nwns_y, nwns_z} is the color gradient confined to the solid plane + norm = sqrt(nwns_x * nwns_x + nwns_y * nwns_y + nwns_z * nwns_z); + zeta = -Pt.eval(P) / norm; + // normalize the normal to the common curve within the solid surface + if (norm > 0.0) { + nwns_x /= norm; + nwns_y /= norm; + nwns_z /= norm; + } + + /* // normal to the wn interface norm = nwn_x*nwn_x + nwn_y*nwn_y + nwn_z*nwn_z; // Compute the interface speed @@ -4104,116 +4341,120 @@ inline double pmmc_CommonCurveSpeed(DoubleArray &CubeValues, DoubleArray &dPdt, nwns_z /= norm; } */ - // Compute the length of the segment - s = sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y)+(A.z-B.z)*(A.z-B.z)); - // Add the length to the common line - // lwns += s; - // Compute the common curve velocity - if (norm > 0.0){ - ReturnVector(0) += zeta*nwns_x*s; - ReturnVector(1) += zeta*nwns_y*s; - ReturnVector(2) += zeta*nwns_z*s; - ReturnValue += zeta*s; - } - } + // Compute the length of the segment + s = sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + + (A.z - B.z) * (A.z - B.z)); + // Add the length to the common line + // lwns += s; + // Compute the common curve velocity + if (norm > 0.0) { + ReturnVector(0) += zeta * nwns_x * s; + ReturnVector(1) += zeta * nwns_y * s; + ReturnVector(2) += zeta * nwns_z * s; + ReturnValue += zeta * s; + } + } NULL_USE(tangent_x); NULL_USE(tangent_y); NULL_USE(tangent_z); return ReturnValue; } -inline void pmmc_CurveOrientation(DoubleArray &Orientation, DTMutableList &Points, int npts, int i, int j, int k){ +inline void pmmc_CurveOrientation(DoubleArray &Orientation, + DTMutableList &Points, int npts, int i, + int j, int k) { + double twnsx, twnsy, twnsz, length; // tangent, norm - double twnsx,twnsy,twnsz,length; // tangent, norm + Point P, A, B; - Point P,A,B; + for (int p = 0; p < npts - 1; p++) { + // Extract the line segment + A = Points(p); + B = Points(p + 1); + P.x = 0.5 * (A.x + B.x) - 1.0 * i; + P.y = 0.5 * (A.y + B.y) - 1.0 * j; + P.z = 0.5 * (A.z + B.z) - 1.0 * k; - for (int p=0; p &Points, int npts, int ic, int jc, int kc) -{ - NULL_USE( f ); - NULL_USE( s ); - NULL_USE( KN ); - NULL_USE( KG ); - - int p,i,j,k; - double length; - double fxx,fyy,fzz,fxy,fxz,fyz,fx,fy,fz; - double sxx,syy,szz,sxy,sxz,syz,sx,sy,sz; -// double Axx,Axy,Axz,Ayx,Ayy,Ayz,Azx,Azy,Azz; -// double Tx[8],Ty[8],Tz[8]; // Tangent vector -// double Nx[8],Ny[8],Nz[8]; // Principle normal - double twnsx,twnsy,twnsz,nwnsx,nwnsy,nwnsz,K; // tangent,normal and curvature - double nsx,nsy,nsz,norm; - double nwsx,nwsy,nwsz; - - Point P,A,B; - // Local trilinear approximation for tangent and normal vector - TriLinPoly Tx,Ty,Tz,Nx,Ny,Nz,Sx,Sy,Sz; +inline void pmmc_CurveCurvature(DoubleArray &f, DoubleArray &s, + DoubleArray &f_x, DoubleArray &f_y, + DoubleArray &f_z, DoubleArray &s_x, + DoubleArray &s_y, DoubleArray &s_z, + DoubleArray &KN, DoubleArray &KG, double &KNavg, + double &KGavg, DTMutableList &Points, + int npts, int ic, int jc, int kc) { + NULL_USE(f); + NULL_USE(s); + NULL_USE(KN); + NULL_USE(KG); - // Loop over the cube and compute the derivatives - for (k=kc; k 0.0){ - // normal curvature component in the direction of the solid surface - KNavg += K*(nsx*nwnsx + nsy*nwnsy + nsz*nwnsz)*length; - //geodesic curvature - KGavg += K*(nwsx*nwnsx + nwsy*nwnsy + nwsz*nwnsz)*length; - } - } - NULL_USE(fxx); NULL_USE(fyy); NULL_USE(fzz); - NULL_USE(fxy); NULL_USE(fxz); NULL_USE(fyz); - NULL_USE(fx); NULL_USE(fy); NULL_USE(fz); - NULL_USE(sxx); NULL_USE(syy); NULL_USE(szz); - NULL_USE(sxy); NULL_USE(sxz); NULL_USE(syz); - NULL_USE(sx); NULL_USE(sy); NULL_USE(sz); + length = sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + + (A.z - B.z) * (A.z - B.z)); + + // tangent vector + twnsx = B.x - A.x; + twnsy = B.y - A.y; + twnsz = B.z - A.z; + norm = sqrt(twnsx * twnsx + twnsy * twnsy + twnsz * twnsz); + if (norm == 0.0) + norm = 1.0; + twnsx /= norm; + twnsy /= norm; + twnsz /= norm; + + // ***************************************************** + // Compute tangent vector at A and B and normalize + tax = Tx.eval(A); + tay = Ty.eval(A); + taz = Tz.eval(A); + norm = sqrt(tax * tax + tay * tay + taz * taz); + if (norm == 0.0) + norm = 1.0; + tax /= norm; + tay /= norm; + taz /= norm; + + tbx = Tx.eval(B); + tby = Ty.eval(B); + tbz = Tz.eval(B); + norm = sqrt(tbx * tbx + tby * tby + tbz * tbz); + if (norm == 0.0) + norm = 1.0; + tbx /= norm; + tby /= norm; + tbz /= norm; + // ***************************************************** + + // ***************************************************** + // Compute the divergence of the tangent vector + nwnsx = (tax - tbx) / length; + nwnsy = (tay - tby) / length; + nwnsz = (taz - tbz) / length; + K = sqrt(nwnsx * nwnsx + nwnsy * nwnsy + nwnsz * nwnsz); + if (K == 0.0) + K = 1.0; + nwnsx /= K; + nwnsy /= K; + nwnsz /= K; + // ***************************************************** + + // Normal vector to the solid surface + nsx = Sx.eval(P); + nsy = Sy.eval(P); + nsz = Sz.eval(P); + norm = sqrt(nsx * nsx + nsy * nsy + nsz * nsz); + if (norm == 0.0) + norm = 1.0; + nsx /= norm; + nsy /= norm; + nsz /= norm; + + // normal in the surface tangent plane (rel. geodesic curvature) + nwsx = twnsy * nsz - twnsz * nsy; + nwsy = twnsz * nsx - twnsx * nsz; + nwsz = twnsx * nsy - twnsy * nsx; + norm = sqrt(nwsx * nwsx + nwsy * nwsy + nwsz * nwsz); + if (norm == 0.0) + norm = 1.0; + nwsx /= norm; + nwsy /= norm; + nwsz /= norm; + + if (nsx * nwnsx + nsy * nwnsy + nsz * nwnsz < 0.0) { + nwnsx = -nwnsx; + nwnsy = -nwnsy; + nwnsz = -nwnsz; + } + + if (length > 0.0) { + // normal curvature component in the direction of the solid surface + KNavg += K * (nsx * nwnsx + nsy * nwnsy + nsz * nwnsz) * length; + //geodesic curvature + KGavg += K * (nwsx * nwnsx + nwsy * nwnsy + nwsz * nwnsz) * length; + } + } + NULL_USE(fxx); + NULL_USE(fyy); + NULL_USE(fzz); + NULL_USE(fxy); + NULL_USE(fxz); + NULL_USE(fyz); + NULL_USE(fx); + NULL_USE(fy); + NULL_USE(fz); + NULL_USE(sxx); + NULL_USE(syy); + NULL_USE(szz); + NULL_USE(sxy); + NULL_USE(sxz); + NULL_USE(syz); + NULL_USE(sx); + NULL_USE(sy); + NULL_USE(sz); } - //-------------------------------------------------------------------------------------------------------- -inline double pmmc_InterfaceSpeed(DoubleArray &dPdt, DoubleArray &P_x, DoubleArray &P_y, DoubleArray &P_z, - DoubleArray &CubeValues, DTMutableList &Points, IntArray &Triangles, - DoubleArray &SurfaceVector, DoubleArray &AvgSpeed, DoubleArray &AvgVel, - int i, int j, int k, int npts, int ntris) -{ - NULL_USE( CubeValues ); - NULL_USE( SurfaceVector ); - NULL_USE( npts ); +inline double pmmc_InterfaceSpeed( + DoubleArray &dPdt, DoubleArray &P_x, DoubleArray &P_y, DoubleArray &P_z, + DoubleArray &CubeValues, DTMutableList &Points, IntArray &Triangles, + DoubleArray &SurfaceVector, DoubleArray &AvgSpeed, DoubleArray &AvgVel, + int i, int j, int k, int npts, int ntris) { + NULL_USE(CubeValues); + NULL_USE(SurfaceVector); + NULL_USE(npts); - Point A,B,C,P; - double x,y,z; - double s,s1,s2,s3,area; - double norm, zeta; - double ReturnValue=0.0; + Point A, B, C, P; + double x, y, z; + double s, s1, s2, s3, area; + double norm, zeta; + double ReturnValue = 0.0; - TriLinPoly Px,Py,Pz,Pt; - Px.assign(P_x,i,j,k); - Py.assign(P_y,i,j,k); - Pz.assign(P_z,i,j,k); - Pt.assign(dPdt,i,j,k); + TriLinPoly Px, Py, Pz, Pt; + Px.assign(P_x, i, j, k); + Py.assign(P_y, i, j, k); + Pz.assign(P_z, i, j, k); + Pt.assign(dPdt, i, j, k); - //............................................................................. - // Compute the average speed of the interface - for (int r=0; r 0.0){ - x = Px.eval(P); - y = Py.eval(P); - z = Pz.eval(P); - norm = sqrt(x*x+y*y+z*z); - if (norm==0.0) norm=1.0; - // Compute the interface speed from time derivative and gradient (Level Set Equation) - zeta = -Pt.eval(P) / norm; - // Normalize the normal vector - x /= norm; - y /= norm; - z /= norm; + //............................................................................. + // Compute the average speed of the interface + for (int r = 0; r < ntris; r++) { + A = Points(Triangles(0, r)); + B = Points(Triangles(1, r)); + C = Points(Triangles(2, r)); + // Compute length of sides (assume dx=dy=dz) + s1 = sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + + (A.z - B.z) * (A.z - B.z)); + s2 = sqrt((A.x - C.x) * (A.x - C.x) + (A.y - C.y) * (A.y - C.y) + + (A.z - C.z) * (A.z - C.z)); + s3 = sqrt((B.x - C.x) * (B.x - C.x) + (B.y - C.y) * (B.y - C.y) + + (B.z - C.z) * (B.z - C.z)); + s = 0.5 * (s1 + s2 + s3); + area = sqrt(s * (s - s1) * (s - s2) * (s - s3)); + // Compute the centroid P + P.x = 0.33333333333333333 * (A.x + B.x + C.x); + P.y = 0.33333333333333333 * (A.y + B.y + C.y); + P.z = 0.33333333333333333 * (A.z + B.z + C.z); + if (area > 0.0) { + x = Px.eval(P); + y = Py.eval(P); + z = Pz.eval(P); + norm = sqrt(x * x + y * y + z * z); + if (norm == 0.0) + norm = 1.0; + // Compute the interface speed from time derivative and gradient (Level Set Equation) + zeta = -Pt.eval(P) / norm; + // Normalize the normal vector + x /= norm; + y /= norm; + z /= norm; - // Compute the average - AvgVel(0) += area*zeta*x; - AvgVel(1) += area*zeta*y; - AvgVel(2) += area*zeta*z; - AvgSpeed(r) = zeta*area; - ReturnValue += zeta*area; - } - } - return ReturnValue; - //............................................................................. + // Compute the average + AvgVel(0) += area * zeta * x; + AvgVel(1) += area * zeta * y; + AvgVel(2) += area * zeta * z; + AvgSpeed(r) = zeta * area; + ReturnValue += zeta * area; + } + } + return ReturnValue; + //............................................................................. } //-------------------------------------------------------------------------------------------------------- -inline double geomavg_EulerCharacteristic(DTMutableList &Points, IntArray &Triangles, - int &npts, int &ntris, int &i, int &j, int &k) -{ - NULL_USE( Points ); - NULL_USE( Triangles ); - NULL_USE( npts ); - NULL_USE( ntris ); - NULL_USE( i ); - NULL_USE( j ); - NULL_USE( k ); +inline double geomavg_EulerCharacteristic(DTMutableList &Points, + IntArray &Triangles, int &npts, + int &ntris, int &i, int &j, int &k) { + NULL_USE(Points); + NULL_USE(Triangles); + NULL_USE(npts); + NULL_USE(ntris); + NULL_USE(i); + NULL_USE(j); + NULL_USE(k); - /* REFERENCE + /* REFERENCE * Huang, Liu, Lee, Yang, Tsang * On concise 3-D simple point comparisons: a marching cubes paradigm * IEEE Transactions on Medical Imaging, Vol. 28, No. 1 * January 2009 */ - // Compute the Euler characteristic for triangles in a cube - /* + // Compute the Euler characteristic for triangles in a cube + /* bool graph[3][3]; double CountSideExternal=0; @@ -4498,25 +4763,26 @@ inline double geomavg_EulerCharacteristic(DTMutableList &Points, IntArray } } */ - double EulerChar,nvert,nface; + double EulerChar, nvert, nface; - // Number of faces is number of triangles - nface = double(ntris); - // Each vertex is shared by four cubes - nvert = double(npts); - // Subtract shared sides to avoid double counting -// nside = 2.0*double(npts)- 3.0 - 0.5*double(npts); - double nside_extern = double(npts); - double nside_intern = double(npts)-3.0; - EulerChar=0.0; - if (npts > 0) EulerChar = (0.25*nvert - nside_intern - 0.5*nside_extern + nface); - return EulerChar; + // Number of faces is number of triangles + nface = double(ntris); + // Each vertex is shared by four cubes + nvert = double(npts); + // Subtract shared sides to avoid double counting + // nside = 2.0*double(npts)- 3.0 - 0.5*double(npts); + double nside_extern = double(npts); + double nside_intern = double(npts) - 3.0; + EulerChar = 0.0; + if (npts > 0) + EulerChar = (0.25 * nvert - nside_intern - 0.5 * nside_extern + nface); + return EulerChar; } -inline double mink_phase_epc6(IntArray &PhaseID, DoubleArray &CubeValues, int PhaseLabel, - int &i, int &j, int &k){ +inline double mink_phase_epc6(IntArray &PhaseID, DoubleArray &CubeValues, + int PhaseLabel, int &i, int &j, int &k) { - /* + /* * Compute the Euler Poincare characteristic for a phase based on 6 adjacency * compute the local contribution within the specified cube must sum to get total * PhaseID -- label of phases on a mesh @@ -4524,101 +4790,122 @@ inline double mink_phase_epc6(IntArray &PhaseID, DoubleArray &CubeValues, int Ph * i,j,k -- identifies the local cube */ - double epc_ncubes, epc_nface, epc_nedge, epc_nvert; - epc_ncubes=epc_nface=epc_nedge=epc_nvert = 0; + double epc_ncubes, epc_nface, epc_nedge, epc_nvert; + epc_ncubes = epc_nface = epc_nedge = epc_nvert = 0; - // Assign CubeValues to be phase indicator for - for (int kk=0; kk<2; kk++){ - for (int jj=0; jj<2; jj++){ - for (int ii=0; ii<2; ii++){ - if ( PhaseID(i+ii,j+jj,k+kk) == PhaseLabel) CubeValues(ii,jj,kk)=1; - else CubeValues(ii,jj,kk)=0; - } - } - } - // update faces edges and cubes for NWP - epc_ncubes += CubeValues(0,0,0)*CubeValues(1,0,0)*CubeValues(0,1,0)*CubeValues(0,0,1)* - CubeValues(1,1,0)*CubeValues(1,0,1)*CubeValues(0,1,1)*CubeValues(1,1,1); - // three faces (others shared by other cubes) - epc_nface += CubeValues(1,0,0)*CubeValues(1,1,0)*CubeValues(1,0,1)*CubeValues(1,1,1); - epc_nface += CubeValues(0,1,0)*CubeValues(1,1,0)*CubeValues(0,1,1)*CubeValues(1,1,1); - epc_nface += CubeValues(0,0,1)*CubeValues(0,1,1)*CubeValues(1,0,1)*CubeValues(1,1,1); - // six of twelve edges (others shared by other cubes) - epc_nedge += CubeValues(1,1,1)*CubeValues(1,1,0); - epc_nedge += CubeValues(1,1,1)*CubeValues(1,0,1); - epc_nedge += CubeValues(1,1,1)*CubeValues(0,1,1); - epc_nedge += CubeValues(1,0,1)*CubeValues(1,0,0); - epc_nedge += CubeValues(1,0,1)*CubeValues(0,0,1); - epc_nedge += CubeValues(0,1,1)*CubeValues(0,0,1); - // four of eight vertices - epc_nvert += CubeValues(1,1,0); - epc_nvert += CubeValues(1,0,1); - epc_nvert += CubeValues(0,1,1); - epc_nvert += CubeValues(1,1,1); + // Assign CubeValues to be phase indicator for + for (int kk = 0; kk < 2; kk++) { + for (int jj = 0; jj < 2; jj++) { + for (int ii = 0; ii < 2; ii++) { + if (PhaseID(i + ii, j + jj, k + kk) == PhaseLabel) + CubeValues(ii, jj, kk) = 1; + else + CubeValues(ii, jj, kk) = 0; + } + } + } + // update faces edges and cubes for NWP + epc_ncubes += CubeValues(0, 0, 0) * CubeValues(1, 0, 0) * + CubeValues(0, 1, 0) * CubeValues(0, 0, 1) * + CubeValues(1, 1, 0) * CubeValues(1, 0, 1) * + CubeValues(0, 1, 1) * CubeValues(1, 1, 1); + // three faces (others shared by other cubes) + epc_nface += CubeValues(1, 0, 0) * CubeValues(1, 1, 0) * + CubeValues(1, 0, 1) * CubeValues(1, 1, 1); + epc_nface += CubeValues(0, 1, 0) * CubeValues(1, 1, 0) * + CubeValues(0, 1, 1) * CubeValues(1, 1, 1); + epc_nface += CubeValues(0, 0, 1) * CubeValues(0, 1, 1) * + CubeValues(1, 0, 1) * CubeValues(1, 1, 1); + // six of twelve edges (others shared by other cubes) + epc_nedge += CubeValues(1, 1, 1) * CubeValues(1, 1, 0); + epc_nedge += CubeValues(1, 1, 1) * CubeValues(1, 0, 1); + epc_nedge += CubeValues(1, 1, 1) * CubeValues(0, 1, 1); + epc_nedge += CubeValues(1, 0, 1) * CubeValues(1, 0, 0); + epc_nedge += CubeValues(1, 0, 1) * CubeValues(0, 0, 1); + epc_nedge += CubeValues(0, 1, 1) * CubeValues(0, 0, 1); + // four of eight vertices + epc_nvert += CubeValues(1, 1, 0); + epc_nvert += CubeValues(1, 0, 1); + epc_nvert += CubeValues(0, 1, 1); + epc_nvert += CubeValues(1, 1, 1); - double chi= epc_nvert - epc_nedge + epc_nface - epc_ncubes; - return chi; + double chi = epc_nvert - epc_nedge + epc_nface - epc_ncubes; + return chi; } -inline double mink_EulerCharacteristic(DTMutableList &Points, IntArray &Triangles, - DoubleArray &CubeValues, int &npts, int &ntris, int &i, int &j, int &k) -{ - - NULL_USE( CubeValues ); +inline double mink_EulerCharacteristic(DTMutableList &Points, + IntArray &Triangles, + DoubleArray &CubeValues, int &npts, + int &ntris, int &i, int &j, int &k) { - // Compute the Euler characteristic for triangles in a cube - // Exclude edges and vertices shared with between multiple cubes - double EulerChar; - int nvert=npts; - int nside=2*nvert-3; - int nface=nvert-2; + NULL_USE(CubeValues); - //if (ntris != nface){ - // nface = ntris; - // nside = - //} - //........................................................... - // Check that this point is not on a previously computed face - // Note direction that the marching cubes algorithm marches - // In parallel, other sub-domains fill in the lower boundary - for (int p=0; p( static_cast::type>( lhs ) | - static_cast::type>( rhs ) ); +AnalysisType &operator|=(AnalysisType &lhs, AnalysisType rhs) { + lhs = static_cast( + static_cast::type>(lhs) | + static_cast::type>(rhs)); return lhs; } -bool matches( AnalysisType x, AnalysisType y ) -{ - return ( static_cast::type>( x ) & - static_cast::type>( y ) ) != 0; +bool matches(AnalysisType x, AnalysisType y) { + return (static_cast::type>(x) & + static_cast::type>(y)) != 0; } - // Create a shared_ptr to an array of values -template -static inline std::shared_ptr make_shared_array( size_t N ) -{ - return std::shared_ptr( new TYPE[N], []( const TYPE *p ) { delete[] p; } ); +template +static inline std::shared_ptr make_shared_array(size_t N) { + return std::shared_ptr(new TYPE[N], + [](const TYPE *p) { delete[] p; }); } - // Helper class to write the restart file from a seperate thread -class WriteRestartWorkItem : public ThreadPool::WorkItemRet -{ +class WriteRestartWorkItem : public ThreadPool::WorkItemRet { public: - WriteRestartWorkItem( const std::string &filename_, std::shared_ptr cDen_, - std::shared_ptr cfq_, int N_ ) - : filename( filename_ ), cfq( cfq_ ), cDen( cDen_ ), N( N_ ) - { - } - virtual void run() - { - PROFILE_START( "Save Checkpoint", 1 ); + WriteRestartWorkItem(const std::string &filename_, + std::shared_ptr cDen_, + std::shared_ptr cfq_, int N_) + : filename(filename_), cfq(cfq_), cDen(cDen_), N(N_) {} + virtual void run() { + PROFILE_START("Save Checkpoint", 1); double value; - ofstream File( filename, ios::binary ); - for ( int n = 0; n < N; n++ ) { + ofstream File(filename, ios::binary); + for (int n = 0; n < N; n++) { // Write the two density values value = cDen.get()[n]; - File.write( (char *) &value, sizeof( value ) ); + File.write((char *)&value, sizeof(value)); value = cDen.get()[N + n]; - File.write( (char *) &value, sizeof( value ) ); + File.write((char *)&value, sizeof(value)); } - for ( int n = 0; n < N; n++ ) { + for (int n = 0; n < N; n++) { // Write the distributions - for ( int q = 0; q < 19; q++ ) { + for (int q = 0; q < 19; q++) { value = cfq.get()[q * N + n]; - File.write( (char *) &value, sizeof( value ) ); + File.write((char *)&value, sizeof(value)); } } File.close(); - PROFILE_STOP( "Save Checkpoint", 1 ); + PROFILE_STOP("Save Checkpoint", 1); }; private: @@ -89,43 +82,33 @@ private: const int N; }; - // Helper class to compute the blob ids typedef std::shared_ptr> BlobIDstruct; typedef std::shared_ptr> BlobIDList; static const std::string id_map_filename = "lbpm_id_map.txt"; -class BlobIdentificationWorkItem1 : public ThreadPool::WorkItemRet -{ +class BlobIdentificationWorkItem1 : public ThreadPool::WorkItemRet { public: - BlobIdentificationWorkItem1( int timestep_, int Nx_, int Ny_, int Nz_, - const RankInfoStruct &rank_info_, std::shared_ptr phase_, - const DoubleArray &dist_, BlobIDstruct last_id_, BlobIDstruct new_index_, - BlobIDstruct new_id_, BlobIDList new_list_, runAnalysis::commWrapper &&comm_ ) - : timestep( timestep_ ), - Nx( Nx_ ), - Ny( Ny_ ), - Nz( Nz_ ), - rank_info( rank_info_ ), - phase( phase_ ), - dist( dist_ ), - last_id( last_id_ ), - new_index( new_index_ ), - new_id( new_id_ ), - new_list( new_list_ ), - comm( std::move( comm_ ) ) - { - } + BlobIdentificationWorkItem1(int timestep_, int Nx_, int Ny_, int Nz_, + const RankInfoStruct &rank_info_, + std::shared_ptr phase_, + const DoubleArray &dist_, BlobIDstruct last_id_, + BlobIDstruct new_index_, BlobIDstruct new_id_, + BlobIDList new_list_, + runAnalysis::commWrapper &&comm_) + : timestep(timestep_), Nx(Nx_), Ny(Ny_), Nz(Nz_), rank_info(rank_info_), + phase(phase_), dist(dist_), last_id(last_id_), new_index(new_index_), + new_id(new_id_), new_list(new_list_), comm(std::move(comm_)) {} ~BlobIdentificationWorkItem1() {} - virtual void run() - { + virtual void run() { // Compute the global blob id and compare to the previous version - PROFILE_START( "Identify blobs", 1 ); - double vF = 0.0; - double vS = -1.0; // one voxel buffer region around solid - IntArray &ids = new_index->second; - new_index->first = ComputeGlobalBlobIDs( - Nx - 2, Ny - 2, Nz - 2, rank_info, *phase, dist, vF, vS, ids, comm.comm ); - PROFILE_STOP( "Identify blobs", 1 ); + PROFILE_START("Identify blobs", 1); + double vF = 0.0; + double vS = -1.0; // one voxel buffer region around solid + IntArray &ids = new_index->second; + new_index->first = + ComputeGlobalBlobIDs(Nx - 2, Ny - 2, Nz - 2, rank_info, *phase, + dist, vF, vS, ids, comm.comm); + PROFILE_STOP("Identify blobs", 1); } private: @@ -139,51 +122,42 @@ private: BlobIDList new_list; runAnalysis::commWrapper comm; }; -class BlobIdentificationWorkItem2 : public ThreadPool::WorkItemRet -{ +class BlobIdentificationWorkItem2 : public ThreadPool::WorkItemRet { public: - BlobIdentificationWorkItem2( int timestep_, int Nx_, int Ny_, int Nz_, - const RankInfoStruct &rank_info_, std::shared_ptr phase_, - const DoubleArray &dist_, BlobIDstruct last_id_, BlobIDstruct new_index_, - BlobIDstruct new_id_, BlobIDList new_list_, runAnalysis::commWrapper &&comm_ ) - : timestep( timestep_ ), - Nx( Nx_ ), - Ny( Ny_ ), - Nz( Nz_ ), - rank_info( rank_info_ ), - phase( phase_ ), - dist( dist_ ), - last_id( last_id_ ), - new_index( new_index_ ), - new_id( new_id_ ), - new_list( new_list_ ), - comm( std::move( comm_ ) ) - { - } + BlobIdentificationWorkItem2(int timestep_, int Nx_, int Ny_, int Nz_, + const RankInfoStruct &rank_info_, + std::shared_ptr phase_, + const DoubleArray &dist_, BlobIDstruct last_id_, + BlobIDstruct new_index_, BlobIDstruct new_id_, + BlobIDList new_list_, + runAnalysis::commWrapper &&comm_) + : timestep(timestep_), Nx(Nx_), Ny(Ny_), Nz(Nz_), rank_info(rank_info_), + phase(phase_), dist(dist_), last_id(last_id_), new_index(new_index_), + new_id(new_id_), new_list(new_list_), comm(std::move(comm_)) {} ~BlobIdentificationWorkItem2() {} - virtual void run() - { + virtual void run() { // Compute the global blob id and compare to the previous version - PROFILE_START( "Identify blobs maps", 1 ); + PROFILE_START("Identify blobs maps", 1); const IntArray &ids = new_index->second; - static int max_id = -1; - new_id->first = new_index->first; - new_id->second = new_index->second; - if ( last_id.get() != NULL ) { + static int max_id = -1; + new_id->first = new_index->first; + new_id->second = new_index->second; + if (last_id.get() != NULL) { // Compute the timestep-timestep map const IntArray &old_ids = last_id->second; - ID_map_struct map = computeIDMap( Nx, Ny, Nz, old_ids, ids, comm.comm ); + ID_map_struct map = + computeIDMap(Nx, Ny, Nz, old_ids, ids, comm.comm); // Renumber the current timestep's ids - getNewIDs( map, max_id, *new_list ); - renumberIDs( *new_list, new_id->second ); - writeIDMap( map, timestep, id_map_filename ); + getNewIDs(map, max_id, *new_list); + renumberIDs(*new_list, new_id->second); + writeIDMap(map, timestep, id_map_filename); } else { max_id = -1; - ID_map_struct map( new_id->first ); - getNewIDs( map, max_id, *new_list ); - writeIDMap( map, timestep, id_map_filename ); + ID_map_struct map(new_id->first); + getNewIDs(map, max_id, *new_list); + writeIDMap(map, timestep, id_map_filename); } - PROFILE_STOP( "Identify blobs maps", 1 ); + PROFILE_STOP("Identify blobs maps", 1); } private: @@ -198,57 +172,50 @@ private: runAnalysis::commWrapper comm; }; - // Helper class to write the vis file from a thread -class WriteVisWorkItem : public ThreadPool::WorkItemRet -{ +class WriteVisWorkItem : public ThreadPool::WorkItemRet { public: - WriteVisWorkItem( int timestep_, std::vector &visData_, - TwoPhase &Avgerages_, std::array n_, RankInfoStruct rank_info_, - runAnalysis::commWrapper &&comm_ ) - : timestep( timestep_ ), - visData( visData_ ), - Averages( Avgerages_ ), - n( std::move( n_ ) ), - rank_info( std::move( rank_info_ ) ), - comm( std::move( comm_ ) ) - { - } + WriteVisWorkItem(int timestep_, std::vector &visData_, + TwoPhase &Avgerages_, std::array n_, + RankInfoStruct rank_info_, + runAnalysis::commWrapper &&comm_) + : timestep(timestep_), visData(visData_), Averages(Avgerages_), + n(std::move(n_)), rank_info(std::move(rank_info_)), + comm(std::move(comm_)) {} ~WriteVisWorkItem() {} - virtual void run() - { - PROFILE_START( "Save Vis", 1 ); + virtual void run() { + PROFILE_START("Save Vis", 1); - fillHalo fillData( comm.comm, rank_info, n, { 1, 1, 1 }, 0, 1 ); + fillHalo fillData(comm.comm, rank_info, n, {1, 1, 1}, 0, 1); - ASSERT( visData[0].vars[0]->name == "phase" ); + ASSERT(visData[0].vars[0]->name == "phase"); Array &PhaseData = visData[0].vars[0]->data; - fillData.copy( Averages.SDn, PhaseData ); + fillData.copy(Averages.SDn, PhaseData); - ASSERT( visData[0].vars[5]->name == "SignDist" ); + ASSERT(visData[0].vars[5]->name == "SignDist"); Array &SignData = visData[0].vars[5]->data; - fillData.copy( Averages.SDs, SignData ); + fillData.copy(Averages.SDs, SignData); - ASSERT( visData[0].vars[1]->name == "Pressure" ); + ASSERT(visData[0].vars[1]->name == "Pressure"); Array &PressData = visData[0].vars[1]->data; - fillData.copy( Averages.Press, PressData ); + fillData.copy(Averages.Press, PressData); - ASSERT( visData[0].vars[2]->name == "Velocity_x" ); - ASSERT( visData[0].vars[3]->name == "Velocity_y" ); - ASSERT( visData[0].vars[4]->name == "Velocity_z" ); + ASSERT(visData[0].vars[2]->name == "Velocity_x"); + ASSERT(visData[0].vars[3]->name == "Velocity_y"); + ASSERT(visData[0].vars[4]->name == "Velocity_z"); Array &VelxData = visData[0].vars[2]->data; Array &VelyData = visData[0].vars[3]->data; Array &VelzData = visData[0].vars[4]->data; - fillData.copy( Averages.Vel_x, VelxData ); - fillData.copy( Averages.Vel_y, VelyData ); - fillData.copy( Averages.Vel_z, VelzData ); + fillData.copy(Averages.Vel_x, VelxData); + fillData.copy(Averages.Vel_y, VelyData); + fillData.copy(Averages.Vel_z, VelzData); - ASSERT( visData[0].vars[6]->name == "BlobID" ); + ASSERT(visData[0].vars[6]->name == "BlobID"); Array &BlobData = visData[0].vars[6]->data; - fillData.copy( Averages.Label_NWP, BlobData ); - IO::writeData( timestep, visData, comm.comm ); + fillData.copy(Averages.Label_NWP, BlobData); + IO::writeData(timestep, visData, comm.comm); - PROFILE_STOP( "Save Vis", 1 ); + PROFILE_STOP("Save Vis", 1); }; private: @@ -262,84 +229,77 @@ private: }; // Helper class to write the vis file from a thread -class IOWorkItem : public ThreadPool::WorkItemRet -{ +class IOWorkItem : public ThreadPool::WorkItemRet { public: - IOWorkItem( int timestep_, std::shared_ptr input_db_, - std::vector &visData_, SubPhase &Averages_, std::array n_, - RankInfoStruct rank_info_, runAnalysis::commWrapper &&comm_ ) - : timestep( timestep_ ), - input_db( input_db_ ), - visData( visData_ ), - Averages( Averages_ ), - n( std::move( n_ ) ), - rank_info( std::move( rank_info_ ) ), - comm( std::move( comm_ ) ) - { - } + IOWorkItem(int timestep_, std::shared_ptr input_db_, + std::vector &visData_, SubPhase &Averages_, + std::array n_, RankInfoStruct rank_info_, + runAnalysis::commWrapper &&comm_) + : timestep(timestep_), input_db(input_db_), visData(visData_), + Averages(Averages_), n(std::move(n_)), + rank_info(std::move(rank_info_)), comm(std::move(comm_)) {} ~IOWorkItem() {} - virtual void run() - { - PROFILE_START( "Save Vis", 1 ); + virtual void run() { + PROFILE_START("Save Vis", 1); - auto color_db = input_db->getDatabase( "Color" ); - auto vis_db = input_db->getDatabase( "Visualization" ); + auto color_db = input_db->getDatabase("Color"); + auto vis_db = input_db->getDatabase("Visualization"); // int timestep = color_db->getWithDefault( "timestep", 0 ); - fillHalo fillData( comm.comm, rank_info, n, { 1, 1, 1 }, 0, 1 ); + fillHalo fillData(comm.comm, rank_info, n, {1, 1, 1}, 0, 1); - if ( vis_db->getWithDefault( "save_phase_field", true ) ) { - ASSERT( visData[0].vars[0]->name == "phase" ); + if (vis_db->getWithDefault("save_phase_field", true)) { + ASSERT(visData[0].vars[0]->name == "phase"); Array &PhaseData = visData[0].vars[0]->data; - fillData.copy( Averages.Phi, PhaseData ); + fillData.copy(Averages.Phi, PhaseData); } - if ( vis_db->getWithDefault( "save_pressure", false ) ) { - ASSERT( visData[0].vars[1]->name == "Pressure" ); + if (vis_db->getWithDefault("save_pressure", false)) { + ASSERT(visData[0].vars[1]->name == "Pressure"); Array &PressData = visData[0].vars[1]->data; - fillData.copy( Averages.Pressure, PressData ); + fillData.copy(Averages.Pressure, PressData); } - if ( vis_db->getWithDefault( "save_velocity", false ) ) { - ASSERT( visData[0].vars[2]->name == "Velocity_x" ); - ASSERT( visData[0].vars[3]->name == "Velocity_y" ); - ASSERT( visData[0].vars[4]->name == "Velocity_z" ); + if (vis_db->getWithDefault("save_velocity", false)) { + ASSERT(visData[0].vars[2]->name == "Velocity_x"); + ASSERT(visData[0].vars[3]->name == "Velocity_y"); + ASSERT(visData[0].vars[4]->name == "Velocity_z"); Array &VelxData = visData[0].vars[2]->data; Array &VelyData = visData[0].vars[3]->data; Array &VelzData = visData[0].vars[4]->data; - fillData.copy( Averages.Vel_x, VelxData ); - fillData.copy( Averages.Vel_y, VelyData ); - fillData.copy( Averages.Vel_z, VelzData ); + fillData.copy(Averages.Vel_x, VelxData); + fillData.copy(Averages.Vel_y, VelyData); + fillData.copy(Averages.Vel_z, VelzData); } - if ( vis_db->getWithDefault( "save_dissipation", false ) ) { - ASSERT( visData[0].vars[5]->name == "ViscousDissipation" ); + if (vis_db->getWithDefault("save_dissipation", false)) { + ASSERT(visData[0].vars[5]->name == "ViscousDissipation"); Array &ViscousDissipation = visData[0].vars[5]->data; - fillData.copy( Averages.Dissipation, ViscousDissipation ); + fillData.copy(Averages.Dissipation, ViscousDissipation); } - if ( vis_db->getWithDefault( "save_distance", false ) ) { - ASSERT( visData[0].vars[6]->name == "SignDist" ); + if (vis_db->getWithDefault("save_distance", false)) { + ASSERT(visData[0].vars[6]->name == "SignDist"); Array &SignData = visData[0].vars[6]->data; - fillData.copy( Averages.SDs, SignData ); + fillData.copy(Averages.SDs, SignData); } - if ( vis_db->getWithDefault( "save_connected_components", false ) ) { - ASSERT( visData[0].vars[7]->name == "BlobID" ); + if (vis_db->getWithDefault("save_connected_components", false)) { + ASSERT(visData[0].vars[7]->name == "BlobID"); Array &BlobData = visData[0].vars[7]->data; - fillData.copy( Averages.morph_n->label, BlobData ); + fillData.copy(Averages.morph_n->label, BlobData); } - if ( vis_db->getWithDefault( "write_silo", true ) ) - IO::writeData( timestep, visData, comm.comm ); + if (vis_db->getWithDefault("write_silo", true)) + IO::writeData(timestep, visData, comm.comm); - if ( vis_db->getWithDefault( "save_8bit_raw", true ) ) { + if (vis_db->getWithDefault("save_8bit_raw", true)) { char CurrentIDFilename[40]; - sprintf( CurrentIDFilename, "id_t%d.raw", timestep ); - Averages.AggregateLabels( CurrentIDFilename ); + sprintf(CurrentIDFilename, "id_t%d.raw", timestep); + Averages.AggregateLabels(CurrentIDFilename); } - PROFILE_STOP( "Save Vis", 1 ); + PROFILE_STOP("Save Vis", 1); }; private: @@ -353,49 +313,42 @@ private: runAnalysis::commWrapper comm; }; - // Helper class to run the analysis from within a thread // Note: Averages will be modified after the constructor is called -class AnalysisWorkItem : public ThreadPool::WorkItemRet -{ +class AnalysisWorkItem : public ThreadPool::WorkItemRet { public: - AnalysisWorkItem( AnalysisType type_, int timestep_, TwoPhase &Averages_, BlobIDstruct ids, - BlobIDList id_list_, double beta_ ) - : type( type_ ), - timestep( timestep_ ), - Averages( Averages_ ), - blob_ids( ids ), - id_list( id_list_ ), - beta( beta_ ) - { - } + AnalysisWorkItem(AnalysisType type_, int timestep_, TwoPhase &Averages_, + BlobIDstruct ids, BlobIDList id_list_, double beta_) + : type(type_), timestep(timestep_), Averages(Averages_), blob_ids(ids), + id_list(id_list_), beta(beta_) {} ~AnalysisWorkItem() {} - virtual void run() - { + virtual void run() { Averages.NumberComponents_NWP = blob_ids->first; - Averages.Label_NWP = blob_ids->second; - Averages.Label_NWP_map = *id_list; - Averages.NumberComponents_WP = 1; - Averages.Label_WP.fill( 0.0 ); - if ( matches( type, AnalysisType::CopyPhaseIndicator ) ) { + Averages.Label_NWP = blob_ids->second; + Averages.Label_NWP_map = *id_list; + Averages.NumberComponents_WP = 1; + Averages.Label_WP.fill(0.0); + if (matches(type, AnalysisType::CopyPhaseIndicator)) { // Averages.ColorToSignedDistance(beta,Averages.Phase,Averages.Phase_tplus); } - if ( matches( type, AnalysisType::ComputeAverages ) ) { - PROFILE_START( "Compute dist", 1 ); + if (matches(type, AnalysisType::ComputeAverages)) { + PROFILE_START("Compute dist", 1); Averages.Initialize(); Averages.ComputeDelPhi(); - Averages.ColorToSignedDistance( beta, Averages.Phase, Averages.SDn ); - Averages.ColorToSignedDistance( beta, Averages.Phase_tminus, Averages.Phase_tminus ); - Averages.ColorToSignedDistance( beta, Averages.Phase_tplus, Averages.Phase_tplus ); + Averages.ColorToSignedDistance(beta, Averages.Phase, Averages.SDn); + Averages.ColorToSignedDistance(beta, Averages.Phase_tminus, + Averages.Phase_tminus); + Averages.ColorToSignedDistance(beta, Averages.Phase_tplus, + Averages.Phase_tplus); Averages.UpdateMeshValues(); Averages.ComputeLocal(); Averages.Reduce(); - Averages.PrintAll( timestep ); + Averages.PrintAll(timestep); Averages.Initialize(); Averages.ComponentAverages(); Averages.SortBlobs(); - Averages.PrintComponents( timestep ); - PROFILE_STOP( "Compute dist", 1 ); + Averages.PrintComponents(timestep); + PROFILE_STOP("Compute dist", 1); } } @@ -409,43 +362,36 @@ private: double beta; }; - -class TCATWorkItem : public ThreadPool::WorkItemRet -{ +class TCATWorkItem : public ThreadPool::WorkItemRet { public: - TCATWorkItem( AnalysisType type_, int timestep_, TwoPhase &Averages_, BlobIDstruct ids, - BlobIDList id_list_, double beta_ ) - : type( type_ ), - timestep( timestep_ ), - Averages( Averages_ ), - blob_ids( ids ), - id_list( id_list_ ), - beta( beta_ ) - { - } + TCATWorkItem(AnalysisType type_, int timestep_, TwoPhase &Averages_, + BlobIDstruct ids, BlobIDList id_list_, double beta_) + : type(type_), timestep(timestep_), Averages(Averages_), blob_ids(ids), + id_list(id_list_), beta(beta_) {} ~TCATWorkItem() {} - virtual void run() - { + virtual void run() { Averages.NumberComponents_NWP = blob_ids->first; - Averages.Label_NWP = blob_ids->second; - Averages.Label_NWP_map = *id_list; - Averages.NumberComponents_WP = 1; - Averages.Label_WP.fill( 0.0 ); - if ( matches( type, AnalysisType::CopyPhaseIndicator ) ) { + Averages.Label_NWP = blob_ids->second; + Averages.Label_NWP_map = *id_list; + Averages.NumberComponents_WP = 1; + Averages.Label_WP.fill(0.0); + if (matches(type, AnalysisType::CopyPhaseIndicator)) { // Averages.ColorToSignedDistance(beta,Averages.Phase,Averages.Phase_tplus); } - if ( matches( type, AnalysisType::ComputeAverages ) ) { - PROFILE_START( "Compute TCAT", 1 ); + if (matches(type, AnalysisType::ComputeAverages)) { + PROFILE_START("Compute TCAT", 1); Averages.Initialize(); Averages.ComputeDelPhi(); - Averages.ColorToSignedDistance( beta, Averages.Phase, Averages.SDn ); - Averages.ColorToSignedDistance( beta, Averages.Phase_tminus, Averages.Phase_tminus ); - Averages.ColorToSignedDistance( beta, Averages.Phase_tplus, Averages.Phase_tplus ); + Averages.ColorToSignedDistance(beta, Averages.Phase, Averages.SDn); + Averages.ColorToSignedDistance(beta, Averages.Phase_tminus, + Averages.Phase_tminus); + Averages.ColorToSignedDistance(beta, Averages.Phase_tplus, + Averages.Phase_tplus); Averages.UpdateMeshValues(); Averages.ComputeLocal(); Averages.Reduce(); - Averages.PrintAll( timestep ); - PROFILE_STOP( "Compute TCAT", 1 ); + Averages.PrintAll(timestep); + PROFILE_STOP("Compute TCAT", 1); } } @@ -459,43 +405,37 @@ private: double beta; }; - -class GanglionTrackingWorkItem : public ThreadPool::WorkItemRet -{ +class GanglionTrackingWorkItem : public ThreadPool::WorkItemRet { public: - GanglionTrackingWorkItem( AnalysisType type_, int timestep_, TwoPhase &Averages_, - BlobIDstruct ids, BlobIDList id_list_, double beta_ ) - : type( type_ ), - timestep( timestep_ ), - Averages( Averages_ ), - blob_ids( ids ), - id_list( id_list_ ), - beta( beta_ ) - { - } + GanglionTrackingWorkItem(AnalysisType type_, int timestep_, + TwoPhase &Averages_, BlobIDstruct ids, + BlobIDList id_list_, double beta_) + : type(type_), timestep(timestep_), Averages(Averages_), blob_ids(ids), + id_list(id_list_), beta(beta_) {} ~GanglionTrackingWorkItem() {} - virtual void run() - { + virtual void run() { Averages.NumberComponents_NWP = blob_ids->first; - Averages.Label_NWP = blob_ids->second; - Averages.Label_NWP_map = *id_list; - Averages.NumberComponents_WP = 1; - Averages.Label_WP.fill( 0.0 ); - if ( matches( type, AnalysisType::CopyPhaseIndicator ) ) { + Averages.Label_NWP = blob_ids->second; + Averages.Label_NWP_map = *id_list; + Averages.NumberComponents_WP = 1; + Averages.Label_WP.fill(0.0); + if (matches(type, AnalysisType::CopyPhaseIndicator)) { // Averages.ColorToSignedDistance(beta,Averages.Phase,Averages.Phase_tplus); } - if ( matches( type, AnalysisType::ComputeAverages ) ) { - PROFILE_START( "Compute ganglion", 1 ); + if (matches(type, AnalysisType::ComputeAverages)) { + PROFILE_START("Compute ganglion", 1); Averages.Initialize(); Averages.ComputeDelPhi(); - Averages.ColorToSignedDistance( beta, Averages.Phase, Averages.SDn ); - Averages.ColorToSignedDistance( beta, Averages.Phase_tminus, Averages.Phase_tminus ); - Averages.ColorToSignedDistance( beta, Averages.Phase_tplus, Averages.Phase_tplus ); + Averages.ColorToSignedDistance(beta, Averages.Phase, Averages.SDn); + Averages.ColorToSignedDistance(beta, Averages.Phase_tminus, + Averages.Phase_tminus); + Averages.ColorToSignedDistance(beta, Averages.Phase_tplus, + Averages.Phase_tplus); Averages.UpdateMeshValues(); Averages.ComponentAverages(); Averages.SortBlobs(); - Averages.PrintComponents( timestep ); - PROFILE_STOP( "Compute ganglion", 1 ); + Averages.PrintComponents(timestep); + PROFILE_STOP("Compute ganglion", 1); } } @@ -509,25 +449,20 @@ private: double beta; }; - -class BasicWorkItem : public ThreadPool::WorkItemRet -{ +class BasicWorkItem : public ThreadPool::WorkItemRet { public: - BasicWorkItem( AnalysisType type_, int timestep_, SubPhase &Averages_ ) - : type( type_ ), timestep( timestep_ ), Averages( Averages_ ) - { - } + BasicWorkItem(AnalysisType type_, int timestep_, SubPhase &Averages_) + : type(type_), timestep(timestep_), Averages(Averages_) {} ~BasicWorkItem() {} - virtual void run() - { + virtual void run() { - if ( matches( type, AnalysisType::CopyPhaseIndicator ) ) { + if (matches(type, AnalysisType::CopyPhaseIndicator)) { // Averages.ColorToSignedDistance(beta,Averages.Phase,Averages.Phase_tplus); } - if ( matches( type, AnalysisType::ComputeAverages ) ) { - PROFILE_START( "Compute basic averages", 1 ); + if (matches(type, AnalysisType::ComputeAverages)) { + PROFILE_START("Compute basic averages", 1); Averages.Basic(); - PROFILE_STOP( "Compute basic averages", 1 ); + PROFILE_STOP("Compute basic averages", 1); } } @@ -539,21 +474,17 @@ private: double beta; }; -class SubphaseWorkItem : public ThreadPool::WorkItemRet -{ +class SubphaseWorkItem : public ThreadPool::WorkItemRet { public: - SubphaseWorkItem( AnalysisType type_, int timestep_, SubPhase &Averages_ ) - : type( type_ ), timestep( timestep_ ), Averages( Averages_ ) - { - } + SubphaseWorkItem(AnalysisType type_, int timestep_, SubPhase &Averages_) + : type(type_), timestep(timestep_), Averages(Averages_) {} ~SubphaseWorkItem() {} - virtual void run() - { + virtual void run() { - PROFILE_START( "Compute subphase", 1 ); + PROFILE_START("Compute subphase", 1); Averages.Full(); - Averages.Write( timestep ); - PROFILE_STOP( "Compute subphase", 1 ); + Averages.Write(timestep); + PROFILE_STOP("Compute subphase", 1); } private: @@ -564,68 +495,58 @@ private: double beta; }; - /****************************************************************** * MPI comm wrapper for use with analysis * ******************************************************************/ -runAnalysis::commWrapper::commWrapper( - int tag_, const Utilities::MPI &comm_, runAnalysis *analysis_ ) - : comm( comm_ ), tag( tag_ ), analysis( analysis_ ) -{ -} -runAnalysis::commWrapper::commWrapper( commWrapper &&rhs ) - : comm( rhs.comm ), tag( rhs.tag ), analysis( rhs.analysis ) -{ +runAnalysis::commWrapper::commWrapper(int tag_, const Utilities::MPI &comm_, + runAnalysis *analysis_) + : comm(comm_), tag(tag_), analysis(analysis_) {} +runAnalysis::commWrapper::commWrapper(commWrapper &&rhs) + : comm(rhs.comm), tag(rhs.tag), analysis(rhs.analysis) { rhs.tag = -1; } -runAnalysis::commWrapper::~commWrapper() -{ - if ( tag == -1 ) +runAnalysis::commWrapper::~commWrapper() { + if (tag == -1) return; comm.barrier(); analysis->d_comm_used[tag] = false; } -runAnalysis::commWrapper runAnalysis::getComm() -{ +runAnalysis::commWrapper runAnalysis::getComm() { // Get a tag from root int tag = -1; - if ( d_rank == 0 ) { - for ( int i = 0; i < 1024; i++ ) { - if ( !d_comm_used[i] ) { + if (d_rank == 0) { + for (int i = 0; i < 1024; i++) { + if (!d_comm_used[i]) { tag = i; break; } } - if ( tag == -1 ) - ERROR( "Unable to get comm" ); + if (tag == -1) + ERROR("Unable to get comm"); } - tag = d_comm.bcast( tag, 0 ); + tag = d_comm.bcast(tag, 0); d_comm_used[tag] = true; - if ( d_comms[tag].isNull() ) + if (d_comms[tag].isNull()) d_comms[tag] = d_comm.dup(); - return commWrapper( tag, d_comms[tag], this ); + return commWrapper(tag, d_comms[tag], this); } - /****************************************************************** * Constructor/Destructors * ******************************************************************/ -runAnalysis::runAnalysis( std::shared_ptr input_db, const RankInfoStruct &rank_info, - std::shared_ptr ScaLBL_Comm, std::shared_ptr Dm, int Np, - bool Regular, IntArray Map ) - : d_Np( Np ), - d_regular( Regular ), - d_rank_info( rank_info ), - d_Map( Map ), - d_comm( Dm->Comm.dup() ), - d_ScaLBL_Comm( ScaLBL_Comm ) -{ +runAnalysis::runAnalysis(std::shared_ptr input_db, + const RankInfoStruct &rank_info, + std::shared_ptr ScaLBL_Comm, + std::shared_ptr Dm, int Np, bool Regular, + IntArray Map) + : d_Np(Np), d_regular(Regular), d_rank_info(rank_info), d_Map(Map), + d_comm(Dm->Comm.dup()), d_ScaLBL_Comm(ScaLBL_Comm) { - auto db = input_db->getDatabase( "Analysis" ); - auto vis_db = input_db->getDatabase( "Visualization" ); - - /* set the I/O format */ - format = vis_db->getWithDefault( "format", "silo" ); + auto db = input_db->getDatabase("Analysis"); + auto vis_db = input_db->getDatabase("Visualization"); + + /* set the I/O format */ + format = vis_db->getWithDefault("format", "silo"); // Ids of work items to use for dependencies ThreadPool::thread_id_t d_wait_blobID; @@ -635,7 +556,7 @@ runAnalysis::runAnalysis( std::shared_ptr input_db, const RankInfoStru ThreadPool::thread_id_t d_wait_subphase; char rankString[20]; - sprintf( rankString, "%05d", Dm->rank() ); + sprintf(rankString, "%05d", Dm->rank()); d_n[0] = Dm->Nx - 2; d_n[1] = Dm->Ny - 2; d_n[2] = Dm->Nz - 2; @@ -643,117 +564,117 @@ runAnalysis::runAnalysis( std::shared_ptr input_db, const RankInfoStru d_N[1] = Dm->Ny; d_N[2] = Dm->Nz; - d_restart_interval = db->getWithDefault( "restart_interval", 100000 ); - d_analysis_interval = db->getWithDefault( "analysis_interval", 1000 ); + d_restart_interval = db->getWithDefault("restart_interval", 100000); + d_analysis_interval = db->getWithDefault("analysis_interval", 1000); d_subphase_analysis_interval = INT_MAX; - d_visualization_interval = INT_MAX; - d_blobid_interval = INT_MAX; - if ( db->keyExists( "blobid_interval" ) ) { - d_blobid_interval = db->getScalar( "blobid_interval" ); + d_visualization_interval = INT_MAX; + d_blobid_interval = INT_MAX; + if (db->keyExists("blobid_interval")) { + d_blobid_interval = db->getScalar("blobid_interval"); } - if ( db->keyExists( "visualization_interval" ) ) { - d_visualization_interval = db->getScalar( "visualization_interval" ); + if (db->keyExists("visualization_interval")) { + d_visualization_interval = db->getScalar("visualization_interval"); } - if ( db->keyExists( "subphase_analysis_interval" ) ) { - d_subphase_analysis_interval = db->getScalar( "subphase_analysis_interval" ); + if (db->keyExists("subphase_analysis_interval")) { + d_subphase_analysis_interval = + db->getScalar("subphase_analysis_interval"); } - auto restart_file = db->getWithDefault( "restart_file", "Restart"); - d_restartFile = restart_file + "." + rankString; - + auto restart_file = + db->getWithDefault("restart_file", "Restart"); + d_restartFile = restart_file + "." + rankString; d_rank = d_comm.getRank(); - writeIDMap( ID_map_struct(), 0, id_map_filename ); - + writeIDMap(ID_map_struct(), 0, id_map_filename); + // Initialize IO for silo //std::string format = "silo"; - format = vis_db->getWithDefault( "format", "silo" ); + format = vis_db->getWithDefault("format", "silo"); - IO::initialize( "", format, "false" ); + IO::initialize("", format, "false"); // Create the MeshDataStruct - d_meshData.resize( 1 ); + d_meshData.resize(1); d_meshData[0].meshName = "domain"; - d_meshData[0].mesh = std::make_shared( - d_rank_info, d_n[0], d_n[1], d_n[2], Dm->Lx, Dm->Ly, Dm->Lz ); - auto PhaseVar = std::make_shared(); - auto PressVar = std::make_shared(); - auto VxVar = std::make_shared(); - auto VyVar = std::make_shared(); - auto VzVar = std::make_shared(); + d_meshData[0].mesh = std::make_shared( + d_rank_info, d_n[0], d_n[1], d_n[2], Dm->Lx, Dm->Ly, Dm->Lz); + auto PhaseVar = std::make_shared(); + auto PressVar = std::make_shared(); + auto VxVar = std::make_shared(); + auto VyVar = std::make_shared(); + auto VzVar = std::make_shared(); auto ViscousDissipationVar = std::make_shared(); auto SignDistVar = std::make_shared(); - auto BlobIDVar = std::make_shared(); + auto BlobIDVar = std::make_shared(); - if ( vis_db->getWithDefault( "save_phase_field", true ) ) { + if (vis_db->getWithDefault("save_phase_field", true)) { PhaseVar->name = "phase"; PhaseVar->type = IO::VariableType::VolumeVariable; - PhaseVar->dim = 1; - PhaseVar->data.resize( d_n[0], d_n[1], d_n[2] ); - d_meshData[0].vars.push_back( PhaseVar ); + PhaseVar->dim = 1; + PhaseVar->data.resize(d_n[0], d_n[1], d_n[2]); + d_meshData[0].vars.push_back(PhaseVar); } - if ( vis_db->getWithDefault( "save_pressure", false ) ) { + if (vis_db->getWithDefault("save_pressure", false)) { PressVar->name = "Pressure"; PressVar->type = IO::VariableType::VolumeVariable; - PressVar->dim = 1; - PressVar->data.resize( d_n[0], d_n[1], d_n[2] ); - d_meshData[0].vars.push_back( PressVar ); + PressVar->dim = 1; + PressVar->data.resize(d_n[0], d_n[1], d_n[2]); + d_meshData[0].vars.push_back(PressVar); } - if ( vis_db->getWithDefault( "save_velocity", false ) ) { + if (vis_db->getWithDefault("save_velocity", false)) { VxVar->name = "Velocity_x"; VxVar->type = IO::VariableType::VolumeVariable; - VxVar->dim = 1; - VxVar->data.resize( d_n[0], d_n[1], d_n[2] ); - d_meshData[0].vars.push_back( VxVar ); + VxVar->dim = 1; + VxVar->data.resize(d_n[0], d_n[1], d_n[2]); + d_meshData[0].vars.push_back(VxVar); VyVar->name = "Velocity_y"; VyVar->type = IO::VariableType::VolumeVariable; - VyVar->dim = 1; - VyVar->data.resize( d_n[0], d_n[1], d_n[2] ); - d_meshData[0].vars.push_back( VyVar ); + VyVar->dim = 1; + VyVar->data.resize(d_n[0], d_n[1], d_n[2]); + d_meshData[0].vars.push_back(VyVar); VzVar->name = "Velocity_z"; VzVar->type = IO::VariableType::VolumeVariable; - VzVar->dim = 1; - VzVar->data.resize( d_n[0], d_n[1], d_n[2] ); - d_meshData[0].vars.push_back( VzVar ); - } - - if ( vis_db->getWithDefault( "save_dissipation", false ) ) { - ViscousDissipationVar->name = "ViscousDissipation"; - ViscousDissipationVar->type = IO::VariableType::VolumeVariable; - ViscousDissipationVar->dim = 1; - ViscousDissipationVar->data.resize( d_n[0], d_n[1], d_n[2] ); - d_meshData[0].vars.push_back( ViscousDissipationVar ); + VzVar->dim = 1; + VzVar->data.resize(d_n[0], d_n[1], d_n[2]); + d_meshData[0].vars.push_back(VzVar); } - if ( vis_db->getWithDefault( "save_distance", false ) ) { + if (vis_db->getWithDefault("save_dissipation", false)) { + ViscousDissipationVar->name = "ViscousDissipation"; + ViscousDissipationVar->type = IO::VariableType::VolumeVariable; + ViscousDissipationVar->dim = 1; + ViscousDissipationVar->data.resize(d_n[0], d_n[1], d_n[2]); + d_meshData[0].vars.push_back(ViscousDissipationVar); + } + + if (vis_db->getWithDefault("save_distance", false)) { SignDistVar->name = "SignDist"; SignDistVar->type = IO::VariableType::VolumeVariable; - SignDistVar->dim = 1; - SignDistVar->data.resize( d_n[0], d_n[1], d_n[2] ); - d_meshData[0].vars.push_back( SignDistVar ); + SignDistVar->dim = 1; + SignDistVar->data.resize(d_n[0], d_n[1], d_n[2]); + d_meshData[0].vars.push_back(SignDistVar); } - if ( vis_db->getWithDefault( "save_connected_components", false ) ) { + if (vis_db->getWithDefault("save_connected_components", false)) { BlobIDVar->name = "BlobID"; BlobIDVar->type = IO::VariableType::VolumeVariable; - BlobIDVar->dim = 1; - BlobIDVar->data.resize( d_n[0], d_n[1], d_n[2] ); - d_meshData[0].vars.push_back( BlobIDVar ); + BlobIDVar->dim = 1; + BlobIDVar->data.resize(d_n[0], d_n[1], d_n[2]); + d_meshData[0].vars.push_back(BlobIDVar); } - // Initialize the comms - for ( int i = 0; i < 1024; i++ ) + for (int i = 0; i < 1024; i++) d_comm_used[i] = false; // Initialize the threads - int N_threads = db->getWithDefault( "N_threads", 4 ); - auto method = db->getWithDefault( "load_balance", "default" ); - createThreads( method, N_threads ); + int N_threads = db->getWithDefault("N_threads", 4); + auto method = db->getWithDefault("load_balance", "default"); + createThreads(method, N_threads); } -runAnalysis::runAnalysis( ScaLBL_ColorModel &ColorModel) +runAnalysis::runAnalysis(ScaLBL_ColorModel &ColorModel) /* std::shared_ptr input_db, const RankInfoStruct &rank_info, std::shared_ptr ScaLBL_Comm, std::shared_ptr Dm, int Np, bool Regular, IntArray Map ) @@ -764,13 +685,13 @@ runAnalysis::runAnalysis( ScaLBL_ColorModel &ColorModel) d_comm( Dm->Comm.dup() ), d_ScaLBL_Comm( ScaLBL_Comm )*/ { - - d_comm = ColorModel.Dm->Comm.dup(); - d_Np = ColorModel.Np; - - auto input_db = ColorModel.db; - auto db = input_db->getDatabase( "Analysis" ); - auto vis_db = input_db->getDatabase( "Visualization" ); + + d_comm = ColorModel.Dm->Comm.dup(); + d_Np = ColorModel.Np; + + auto input_db = ColorModel.db; + auto db = input_db->getDatabase("Analysis"); + auto vis_db = input_db->getDatabase("Visualization"); // Ids of work items to use for dependencies ThreadPool::thread_id_t d_wait_blobID; @@ -780,7 +701,7 @@ runAnalysis::runAnalysis( ScaLBL_ColorModel &ColorModel) ThreadPool::thread_id_t d_wait_subphase; char rankString[20]; - sprintf( rankString, "%05d", ColorModel.Dm->rank() ); + sprintf(rankString, "%05d", ColorModel.Dm->rank()); d_n[0] = ColorModel.Dm->Nx - 2; d_n[1] = ColorModel.Dm->Ny - 2; d_n[2] = ColorModel.Dm->Nz - 2; @@ -788,113 +709,113 @@ runAnalysis::runAnalysis( ScaLBL_ColorModel &ColorModel) d_N[1] = ColorModel.Dm->Ny; d_N[2] = ColorModel.Dm->Nz; - d_restart_interval = db->getWithDefault( "restart_interval", 100000 ); - d_analysis_interval = db->getWithDefault( "analysis_interval", 1000 ); + d_restart_interval = db->getWithDefault("restart_interval", 100000); + d_analysis_interval = db->getWithDefault("analysis_interval", 1000); d_subphase_analysis_interval = INT_MAX; - d_visualization_interval = INT_MAX; - d_blobid_interval = INT_MAX; - if ( db->keyExists( "blobid_interval" ) ) { - d_blobid_interval = db->getScalar( "blobid_interval" ); + d_visualization_interval = INT_MAX; + d_blobid_interval = INT_MAX; + if (db->keyExists("blobid_interval")) { + d_blobid_interval = db->getScalar("blobid_interval"); } - if ( db->keyExists( "visualization_interval" ) ) { - d_visualization_interval = db->getScalar( "visualization_interval" ); + if (db->keyExists("visualization_interval")) { + d_visualization_interval = db->getScalar("visualization_interval"); } - if ( db->keyExists( "subphase_analysis_interval" ) ) { - d_subphase_analysis_interval = db->getScalar( "subphase_analysis_interval" ); + if (db->keyExists("subphase_analysis_interval")) { + d_subphase_analysis_interval = + db->getScalar("subphase_analysis_interval"); } - auto restart_file = db->getWithDefault( "restart_file", "Restart"); - d_restartFile = restart_file + "." + rankString; - + auto restart_file = + db->getWithDefault("restart_file", "Restart"); + d_restartFile = restart_file + "." + rankString; + d_rank = d_comm.getRank(); - writeIDMap( ID_map_struct(), 0, id_map_filename ); + writeIDMap(ID_map_struct(), 0, id_map_filename); // Initialize IO for silo //std::string format = "silo"; - - format = vis_db->getWithDefault( "format", "silo" ); - - IO::initialize( "", format, "false" ); + + format = vis_db->getWithDefault("format", "silo"); + + IO::initialize("", format, "false"); // Create the MeshDataStruct - d_meshData.resize( 1 ); + d_meshData.resize(1); d_meshData[0].meshName = "domain"; - d_meshData[0].mesh = std::make_shared( - d_rank_info, d_n[0], d_n[1], d_n[2], ColorModel.Dm->Lx, ColorModel.Dm->Ly, ColorModel.Dm->Lz ); - auto PhaseVar = std::make_shared(); - auto PressVar = std::make_shared(); - auto VxVar = std::make_shared(); - auto VyVar = std::make_shared(); - auto VzVar = std::make_shared(); + d_meshData[0].mesh = std::make_shared( + d_rank_info, d_n[0], d_n[1], d_n[2], ColorModel.Dm->Lx, + ColorModel.Dm->Ly, ColorModel.Dm->Lz); + auto PhaseVar = std::make_shared(); + auto PressVar = std::make_shared(); + auto VxVar = std::make_shared(); + auto VyVar = std::make_shared(); + auto VzVar = std::make_shared(); auto SignDistVar = std::make_shared(); - auto BlobIDVar = std::make_shared(); + auto BlobIDVar = std::make_shared(); - if ( vis_db->getWithDefault( "save_phase_field", true ) ) { + if (vis_db->getWithDefault("save_phase_field", true)) { PhaseVar->name = "phase"; PhaseVar->type = IO::VariableType::VolumeVariable; - PhaseVar->dim = 1; - PhaseVar->data.resize( d_n[0], d_n[1], d_n[2] ); - d_meshData[0].vars.push_back( PhaseVar ); + PhaseVar->dim = 1; + PhaseVar->data.resize(d_n[0], d_n[1], d_n[2]); + d_meshData[0].vars.push_back(PhaseVar); } - if ( vis_db->getWithDefault( "save_pressure", false ) ) { + if (vis_db->getWithDefault("save_pressure", false)) { PressVar->name = "Pressure"; PressVar->type = IO::VariableType::VolumeVariable; - PressVar->dim = 1; - PressVar->data.resize( d_n[0], d_n[1], d_n[2] ); - d_meshData[0].vars.push_back( PressVar ); + PressVar->dim = 1; + PressVar->data.resize(d_n[0], d_n[1], d_n[2]); + d_meshData[0].vars.push_back(PressVar); } - if ( vis_db->getWithDefault( "save_velocity", false ) ) { + if (vis_db->getWithDefault("save_velocity", false)) { VxVar->name = "Velocity_x"; VxVar->type = IO::VariableType::VolumeVariable; - VxVar->dim = 1; - VxVar->data.resize( d_n[0], d_n[1], d_n[2] ); - d_meshData[0].vars.push_back( VxVar ); + VxVar->dim = 1; + VxVar->data.resize(d_n[0], d_n[1], d_n[2]); + d_meshData[0].vars.push_back(VxVar); VyVar->name = "Velocity_y"; VyVar->type = IO::VariableType::VolumeVariable; - VyVar->dim = 1; - VyVar->data.resize( d_n[0], d_n[1], d_n[2] ); - d_meshData[0].vars.push_back( VyVar ); + VyVar->dim = 1; + VyVar->data.resize(d_n[0], d_n[1], d_n[2]); + d_meshData[0].vars.push_back(VyVar); VzVar->name = "Velocity_z"; VzVar->type = IO::VariableType::VolumeVariable; - VzVar->dim = 1; - VzVar->data.resize( d_n[0], d_n[1], d_n[2] ); - d_meshData[0].vars.push_back( VzVar ); + VzVar->dim = 1; + VzVar->data.resize(d_n[0], d_n[1], d_n[2]); + d_meshData[0].vars.push_back(VzVar); } - if ( vis_db->getWithDefault( "save_distance", false ) ) { + if (vis_db->getWithDefault("save_distance", false)) { SignDistVar->name = "SignDist"; SignDistVar->type = IO::VariableType::VolumeVariable; - SignDistVar->dim = 1; - SignDistVar->data.resize( d_n[0], d_n[1], d_n[2] ); - d_meshData[0].vars.push_back( SignDistVar ); + SignDistVar->dim = 1; + SignDistVar->data.resize(d_n[0], d_n[1], d_n[2]); + d_meshData[0].vars.push_back(SignDistVar); } - if ( vis_db->getWithDefault( "save_connected_components", false ) ) { + if (vis_db->getWithDefault("save_connected_components", false)) { BlobIDVar->name = "BlobID"; BlobIDVar->type = IO::VariableType::VolumeVariable; - BlobIDVar->dim = 1; - BlobIDVar->data.resize( d_n[0], d_n[1], d_n[2] ); - d_meshData[0].vars.push_back( BlobIDVar ); + BlobIDVar->dim = 1; + BlobIDVar->data.resize(d_n[0], d_n[1], d_n[2]); + d_meshData[0].vars.push_back(BlobIDVar); } - // Initialize the comms - for ( int i = 0; i < 1024; i++ ) + for (int i = 0; i < 1024; i++) d_comm_used[i] = false; // Initialize the threads - int N_threads = db->getWithDefault( "N_threads", 4 ); - auto method = db->getWithDefault( "load_balance", "default" ); - createThreads( method, N_threads ); + int N_threads = db->getWithDefault("N_threads", 4); + auto method = db->getWithDefault("load_balance", "default"); + createThreads(method, N_threads); } -runAnalysis::~runAnalysis() -{ +runAnalysis::~runAnalysis() { // Finish processing analysis finish(); } -void runAnalysis::finish() -{ - PROFILE_START( "finish" ); +void runAnalysis::finish() { + PROFILE_START("finish"); // Wait for the work items to finish d_tpool.wait_pool_finished(); // Clear the wait ids @@ -905,75 +826,71 @@ void runAnalysis::finish() d_wait_restart.reset(); // Syncronize d_comm.barrier(); - PROFILE_STOP( "finish" ); + PROFILE_STOP("finish"); } - /****************************************************************** * Set the thread affinities * ******************************************************************/ -void print( const std::vector &ids ) -{ - if ( ids.empty() ) +void print(const std::vector &ids) { + if (ids.empty()) return; - printf( "%i", ids[0] ); - for ( size_t i = 1; i < ids.size(); i++ ) - printf( ", %i", ids[i] ); - printf( "\n" ); + printf("%i", ids[0]); + for (size_t i = 1; i < ids.size(); i++) + printf(", %i", ids[i]); + printf("\n"); } -void runAnalysis::createThreads( const std::string &method, int N_threads ) -{ +void runAnalysis::createThreads(const std::string &method, int N_threads) { // Check if we are not using analysis threads - if ( method == "none" ) + if (method == "none") return; // Check if we have thread support auto thread_support = Utilities::MPI::queryThreadSupport(); - if ( thread_support != Utilities::MPI::ThreadSupport::MULTIPLE && N_threads > 0 ) - std::cerr - << "Warning: Failed to start MPI with necessary thread support, errors may occur\n"; + if (thread_support != Utilities::MPI::ThreadSupport::MULTIPLE && + N_threads > 0) + std::cerr << "Warning: Failed to start MPI with necessary thread " + "support, errors may occur\n"; // Create the threads const auto cores = d_tpool.getProcessAffinity(); - if ( N_threads == 0 ) { + if (N_threads == 0) { // Special case to serials the analysis for debugging - d_tpool.setNumThreads( 0 ); - } else if ( cores.empty() ) { + d_tpool.setNumThreads(0); + } else if (cores.empty()) { // We were not able to get the cores for the process - d_tpool.setNumThreads( N_threads ); - } else if ( method == "default" ) { + d_tpool.setNumThreads(N_threads); + } else if (method == "default") { // Create the given number of threads, but let the OS manage affinities - d_tpool.setNumThreads( N_threads ); - } else if ( method == "independent" ) { + d_tpool.setNumThreads(N_threads); + } else if (method == "independent") { int N = cores.size() - 1; - d_tpool.setNumThreads( N ); - d_tpool.setThreadAffinity( { cores[0] } ); - for ( int i = 0; i < N; i++ ) - d_tpool.setThreadAffinity( i, { cores[i + 1] } ); + d_tpool.setNumThreads(N); + d_tpool.setThreadAffinity({cores[0]}); + for (int i = 0; i < N; i++) + d_tpool.setThreadAffinity(i, {cores[i + 1]}); } // Print the current affinities - if ( d_rank == 0 ) { - printf( "Affinities - rank 0:\n" ); - printf( "Main: " ); - print( d_tpool.getProcessAffinity() ); - for ( int i = 0; i < d_tpool.getNumThreads(); i++ ) { - printf( "Thread %i: ", i + 1 ); - print( d_tpool.getThreadAffinity( i ) ); + if (d_rank == 0) { + printf("Affinities - rank 0:\n"); + printf("Main: "); + print(d_tpool.getProcessAffinity()); + for (int i = 0; i < d_tpool.getNumThreads(); i++) { + printf("Thread %i: ", i + 1); + print(d_tpool.getThreadAffinity(i)); } } } - /****************************************************************** * Check which analysis we want to perform * ******************************************************************/ -AnalysisType runAnalysis::computeAnalysisType( int timestep ) -{ +AnalysisType runAnalysis::computeAnalysisType(int timestep) { AnalysisType type = AnalysisType::AnalyzeNone; - if ( timestep % d_analysis_interval + 8 == d_analysis_interval ) { + if (timestep % d_analysis_interval + 8 == d_analysis_interval) { // Copy the phase indicator field for the earlier timestep // printf("Copy phase indicator,timestep=%i\n",timestep); type |= AnalysisType::CopyPhaseIndicator; } - if ( timestep % d_blobid_interval == 0 ) { + if (timestep % d_blobid_interval == 0) { // Identify blobs and update global ids in time type |= AnalysisType::IdentifyBlobs; } @@ -985,22 +902,22 @@ AnalysisType runAnalysis::computeAnalysisType( int timestep ) type |= AnalysisType::IdentifyBlobs; } #endif */ - if ( timestep % d_analysis_interval + 4 == d_analysis_interval ) { + if (timestep % d_analysis_interval + 4 == d_analysis_interval) { // Copy the averages to the CPU (and identify blobs) // printf("Copy sim state, timestep=%i \n",timestep); type |= AnalysisType::CopySimState; type |= AnalysisType::IdentifyBlobs; } - if ( timestep % d_analysis_interval == 0 ) { + if (timestep % d_analysis_interval == 0) { // Run the analysis // printf("Compute averages, timestep=%i \n",timestep); type |= AnalysisType::ComputeAverages; } - if ( timestep % d_restart_interval == 0 ) { + if (timestep % d_restart_interval == 0) { // Write the restart file type |= AnalysisType::CreateRestart; } - if ( timestep % d_visualization_interval == 0 ) { + if (timestep % d_visualization_interval == 0) { // Write the visualization data type |= AnalysisType::WriteVis; type |= AnalysisType::CopySimState; @@ -1009,36 +926,35 @@ AnalysisType runAnalysis::computeAnalysisType( int timestep ) return type; } - /****************************************************************** * Run the analysis * ******************************************************************/ -void runAnalysis::run( int timestep, std::shared_ptr input_db, TwoPhase &Averages, - const double *Phi, double *Pressure, double *Velocity, double *fq, double *Den ) -{ +void runAnalysis::run(int timestep, std::shared_ptr input_db, + TwoPhase &Averages, const double *Phi, double *Pressure, + double *Velocity, double *fq, double *Den) { int N = d_N[0] * d_N[1] * d_N[2]; - NULL_USE( N ); - NULL_USE( Phi ); + NULL_USE(N); + NULL_USE(Phi); - auto db = input_db->getDatabase( "Analysis" ); + auto db = input_db->getDatabase("Analysis"); // int timestep = db->getWithDefault( "timestep", 0 ); // Check which analysis steps we need to perform - auto type = computeAnalysisType( timestep ); - if ( type == AnalysisType::AnalyzeNone ) + auto type = computeAnalysisType(timestep); + if (type == AnalysisType::AnalyzeNone) return; // Check how may queued items we have - if ( d_tpool.N_queued() > 20 ) { + if (d_tpool.N_queued() > 20) { std::cerr << "Analysis queue is getting behind, waiting ...\n"; finish(); } - PROFILE_START( "run" ); + PROFILE_START("run"); // Copy the appropriate variables to the host (so we can spawn new threads) ScaLBL_DeviceBarrier(); - PROFILE_START( "Copy data to host", 1 ); + PROFILE_START("Copy data to host", 1); std::shared_ptr phase; /* if ( matches(type,AnalysisType::CopyPhaseIndicator) || matches(type,AnalysisType::ComputeAverages) || @@ -1072,250 +988,260 @@ void runAnalysis::run( int timestep, std::shared_ptr input_db, TwoPhas } */ // if ( matches(type,AnalysisType::CopyPhaseIndicator) ) { - if ( timestep % d_analysis_interval + 8 == d_analysis_interval ) { - if ( d_regular ) - d_ScaLBL_Comm->RegularLayout( d_Map, Phi, Averages.Phase_tplus ); + if (timestep % d_analysis_interval + 8 == d_analysis_interval) { + if (d_regular) + d_ScaLBL_Comm->RegularLayout(d_Map, Phi, Averages.Phase_tplus); else - ScaLBL_CopyToHost( Averages.Phase_tplus.data(), Phi, N * sizeof( double ) ); + ScaLBL_CopyToHost(Averages.Phase_tplus.data(), Phi, + N * sizeof(double)); // memcpy(Averages.Phase_tplus.data(),phase->data(),N*sizeof(double)); } - if ( timestep % d_analysis_interval == 0 ) { - if ( d_regular ) - d_ScaLBL_Comm->RegularLayout( d_Map, Phi, Averages.Phase_tminus ); + if (timestep % d_analysis_interval == 0) { + if (d_regular) + d_ScaLBL_Comm->RegularLayout(d_Map, Phi, Averages.Phase_tminus); else - ScaLBL_CopyToHost( Averages.Phase_tminus.data(), Phi, N * sizeof( double ) ); + ScaLBL_CopyToHost(Averages.Phase_tminus.data(), Phi, + N * sizeof(double)); // memcpy(Averages.Phase_tminus.data(),phase->data(),N*sizeof(double)); } // if ( matches(type,AnalysisType::CopySimState) ) { - if ( timestep % d_analysis_interval + 4 == d_analysis_interval ) { + if (timestep % d_analysis_interval + 4 == d_analysis_interval) { // Copy the members of Averages to the cpu (phase was copied above) - PROFILE_START( "Copy-Pressure", 1 ); - ScaLBL_D3Q19_Pressure( fq, Pressure, d_Np ); + PROFILE_START("Copy-Pressure", 1); + ScaLBL_D3Q19_Pressure(fq, Pressure, d_Np); // ScaLBL_D3Q19_Momentum(fq,Velocity,d_Np); ScaLBL_DeviceBarrier(); - PROFILE_STOP( "Copy-Pressure", 1 ); - PROFILE_START( "Copy-Wait", 1 ); - PROFILE_STOP( "Copy-Wait", 1 ); - PROFILE_START( "Copy-State", 1 ); + PROFILE_STOP("Copy-Pressure", 1); + PROFILE_START("Copy-Wait", 1); + PROFILE_STOP("Copy-Wait", 1); + PROFILE_START("Copy-State", 1); // memcpy(Averages.Phase.data(),phase->data(),N*sizeof(double)); - if ( d_regular ) - d_ScaLBL_Comm->RegularLayout( d_Map, Phi, Averages.Phase ); + if (d_regular) + d_ScaLBL_Comm->RegularLayout(d_Map, Phi, Averages.Phase); else - ScaLBL_CopyToHost( Averages.Phase.data(), Phi, N * sizeof( double ) ); + ScaLBL_CopyToHost(Averages.Phase.data(), Phi, N * sizeof(double)); // copy other variables - d_ScaLBL_Comm->RegularLayout( d_Map, Pressure, Averages.Press ); - d_ScaLBL_Comm->RegularLayout( d_Map, &Velocity[0], Averages.Vel_x ); - d_ScaLBL_Comm->RegularLayout( d_Map, &Velocity[d_Np], Averages.Vel_y ); - d_ScaLBL_Comm->RegularLayout( d_Map, &Velocity[2 * d_Np], Averages.Vel_z ); - PROFILE_STOP( "Copy-State", 1 ); + d_ScaLBL_Comm->RegularLayout(d_Map, Pressure, Averages.Press); + d_ScaLBL_Comm->RegularLayout(d_Map, &Velocity[0], Averages.Vel_x); + d_ScaLBL_Comm->RegularLayout(d_Map, &Velocity[d_Np], Averages.Vel_y); + d_ScaLBL_Comm->RegularLayout(d_Map, &Velocity[2 * d_Np], + Averages.Vel_z); + PROFILE_STOP("Copy-State", 1); } std::shared_ptr cfq, cDen; // if ( matches(type,AnalysisType::CreateRestart) ) { - if ( timestep % d_restart_interval == 0 ) { + if (timestep % d_restart_interval == 0) { // Copy restart data to the CPU - cDen = make_shared_array( 2 * d_Np ); - cfq = make_shared_array( 19 * d_Np ); - ScaLBL_CopyToHost( cfq.get(), fq, 19 * d_Np * sizeof( double ) ); - ScaLBL_CopyToHost( cDen.get(), Den, 2 * d_Np * sizeof( double ) ); + cDen = make_shared_array(2 * d_Np); + cfq = make_shared_array(19 * d_Np); + ScaLBL_CopyToHost(cfq.get(), fq, 19 * d_Np * sizeof(double)); + ScaLBL_CopyToHost(cDen.get(), Den, 2 * d_Np * sizeof(double)); } - PROFILE_STOP( "Copy data to host", 1 ); + PROFILE_STOP("Copy data to host", 1); // Spawn threads to do blob identification work - if ( matches( type, AnalysisType::IdentifyBlobs ) ) { - phase = std::make_shared( d_N[0], d_N[1], d_N[2] ); - if ( d_regular ) - d_ScaLBL_Comm->RegularLayout( d_Map, Phi, *phase ); + if (matches(type, AnalysisType::IdentifyBlobs)) { + phase = std::make_shared(d_N[0], d_N[1], d_N[2]); + if (d_regular) + d_ScaLBL_Comm->RegularLayout(d_Map, Phi, *phase); else - ScaLBL_CopyToHost( phase->data(), Phi, N * sizeof( double ) ); + ScaLBL_CopyToHost(phase->data(), Phi, N * sizeof(double)); - auto new_index = std::make_shared>( 0, IntArray() ); - auto new_ids = std::make_shared>( 0, IntArray() ); - auto new_list = std::make_shared>(); - auto work1 = new BlobIdentificationWorkItem1( timestep, d_N[0], d_N[1], d_N[2], d_rank_info, - phase, Averages.SDs, d_last_ids, new_index, new_ids, new_list, getComm() ); - auto work2 = new BlobIdentificationWorkItem2( timestep, d_N[0], d_N[1], d_N[2], d_rank_info, - phase, Averages.SDs, d_last_ids, new_index, new_ids, new_list, getComm() ); - work1->add_dependency( d_wait_blobID ); - work2->add_dependency( d_tpool.add_work( work1 ) ); - d_wait_blobID = d_tpool.add_work( work2 ); - d_last_index = new_index; - d_last_ids = new_ids; + auto new_index = + std::make_shared>(0, IntArray()); + auto new_ids = + std::make_shared>(0, IntArray()); + auto new_list = std::make_shared>(); + auto work1 = new BlobIdentificationWorkItem1( + timestep, d_N[0], d_N[1], d_N[2], d_rank_info, phase, Averages.SDs, + d_last_ids, new_index, new_ids, new_list, getComm()); + auto work2 = new BlobIdentificationWorkItem2( + timestep, d_N[0], d_N[1], d_N[2], d_rank_info, phase, Averages.SDs, + d_last_ids, new_index, new_ids, new_list, getComm()); + work1->add_dependency(d_wait_blobID); + work2->add_dependency(d_tpool.add_work(work1)); + d_wait_blobID = d_tpool.add_work(work2); + d_last_index = new_index; + d_last_ids = new_ids; d_last_id_map = new_list; } // Spawn threads to do the analysis work // if (timestep%d_restart_interval==0){ // if ( matches(type,AnalysisType::ComputeAverages) ) { - if ( timestep % d_analysis_interval == 0 ) { - auto work = - new AnalysisWorkItem( type, timestep, Averages, d_last_index, d_last_id_map, d_beta ); - work->add_dependency( d_wait_blobID ); - work->add_dependency( d_wait_analysis ); - work->add_dependency( d_wait_vis ); // Make sure we are done using analysis before modifying - d_wait_analysis = d_tpool.add_work( work ); + if (timestep % d_analysis_interval == 0) { + auto work = new AnalysisWorkItem(type, timestep, Averages, d_last_index, + d_last_id_map, d_beta); + work->add_dependency(d_wait_blobID); + work->add_dependency(d_wait_analysis); + work->add_dependency( + d_wait_vis); // Make sure we are done using analysis before modifying + d_wait_analysis = d_tpool.add_work(work); } // Spawn a thread to write the restart file // if ( matches(type,AnalysisType::CreateRestart) ) { - if ( timestep % d_restart_interval == 0 ) { + if (timestep % d_restart_interval == 0) { - if ( d_rank == 0 ) { - input_db->putScalar( "Restart", true ); - std::ofstream OutStream( "Restart.db" ); - input_db->print( OutStream, "" ); + if (d_rank == 0) { + input_db->putScalar("Restart", true); + std::ofstream OutStream("Restart.db"); + input_db->print(OutStream, ""); OutStream.close(); } // Write the restart file (using a seperate thread) - auto work = new WriteRestartWorkItem( d_restartFile.c_str(), cDen, cfq, d_Np ); - work->add_dependency( d_wait_restart ); - d_wait_restart = d_tpool.add_work( work ); + auto work = + new WriteRestartWorkItem(d_restartFile.c_str(), cDen, cfq, d_Np); + work->add_dependency(d_wait_restart); + d_wait_restart = d_tpool.add_work(work); } // Save the results for visualization // if ( matches(type,AnalysisType::CreateRestart) ) { - if ( timestep % d_restart_interval == 0 ) { + if (timestep % d_restart_interval == 0) { // Write the vis files - auto work = - new WriteVisWorkItem( timestep, d_meshData, Averages, d_n, d_rank_info, getComm() ); - work->add_dependency( d_wait_blobID ); - work->add_dependency( d_wait_analysis ); - work->add_dependency( d_wait_vis ); - d_wait_vis = d_tpool.add_work( work ); + auto work = new WriteVisWorkItem(timestep, d_meshData, Averages, d_n, + d_rank_info, getComm()); + work->add_dependency(d_wait_blobID); + work->add_dependency(d_wait_analysis); + work->add_dependency(d_wait_vis); + d_wait_vis = d_tpool.add_work(work); } - PROFILE_STOP( "run" ); + PROFILE_STOP("run"); } - /****************************************************************** * Run the analysis * ******************************************************************/ -void runAnalysis::basic( int timestep, std::shared_ptr input_db, SubPhase &Averages, - const double *Phi, double *Pressure, double *Velocity, double *fq, double *Den ) -{ +void runAnalysis::basic(int timestep, std::shared_ptr input_db, + SubPhase &Averages, const double *Phi, double *Pressure, + double *Velocity, double *fq, double *Den) { int Nx = d_N[0]; int Ny = d_N[1]; int Nz = d_N[2]; - int N = Nx * Ny * Nz; - NULL_USE( N ); + int N = Nx * Ny * Nz; + NULL_USE(N); // Check which analysis steps we need to perform - auto color_db = input_db->getDatabase( "Color" ); - auto vis_db = input_db->getDatabase( "Visualization" ); + auto color_db = input_db->getDatabase("Color"); + auto vis_db = input_db->getDatabase("Visualization"); // int timestep = color_db->getWithDefault( "timestep", 0 ); - auto type = computeAnalysisType( timestep ); - if ( type == AnalysisType::AnalyzeNone ) + auto type = computeAnalysisType(timestep); + if (type == AnalysisType::AnalyzeNone) return; // Check how may queued items we have - if ( d_tpool.N_queued() > 20 ) { + if (d_tpool.N_queued() > 20) { std::cerr << "Analysis queue is getting behind, waiting ...\n"; finish(); } - PROFILE_START( "basic" ); + PROFILE_START("basic"); // Copy the appropriate variables to the host (so we can spawn new threads) ScaLBL_DeviceBarrier(); - PROFILE_START( "Copy data to host", 1 ); + PROFILE_START("Copy data to host", 1); // if ( matches(type,AnalysisType::CopySimState) ) { - if ( timestep % d_analysis_interval == 0 ) { + if (timestep % d_analysis_interval == 0) { finish(); // can't copy if threads are still working on data // Copy the members of Averages to the cpu (phase was copied above) - PROFILE_START( "Copy-Pressure", 1 ); - ScaLBL_D3Q19_Pressure( fq, Pressure, d_Np ); + PROFILE_START("Copy-Pressure", 1); + ScaLBL_D3Q19_Pressure(fq, Pressure, d_Np); // ScaLBL_D3Q19_Momentum(fq,Velocity,d_Np); ScaLBL_DeviceBarrier(); - PROFILE_STOP( "Copy-Pressure", 1 ); - PROFILE_START( "Copy-Wait", 1 ); - PROFILE_STOP( "Copy-Wait", 1 ); - PROFILE_START( "Copy-State", 1 ); + PROFILE_STOP("Copy-Pressure", 1); + PROFILE_START("Copy-Wait", 1); + PROFILE_STOP("Copy-Wait", 1); + PROFILE_START("Copy-State", 1); /*if (d_regular) d_ScaLBL_Comm->RegularLayout(d_Map,Phi,Averages.Phi); else */ - ScaLBL_CopyToHost( Averages.Phi.data(), Phi, N * sizeof( double ) ); + ScaLBL_CopyToHost(Averages.Phi.data(), Phi, N * sizeof(double)); // copy other variables - d_ScaLBL_Comm->RegularLayout( d_Map, Pressure, Averages.Pressure ); - d_ScaLBL_Comm->RegularLayout( d_Map, &Den[0], Averages.Rho_n ); - d_ScaLBL_Comm->RegularLayout( d_Map, &Den[d_Np], Averages.Rho_w ); - d_ScaLBL_Comm->RegularLayout( d_Map, &Velocity[0], Averages.Vel_x ); - d_ScaLBL_Comm->RegularLayout( d_Map, &Velocity[d_Np], Averages.Vel_y ); - d_ScaLBL_Comm->RegularLayout( d_Map, &Velocity[2 * d_Np], Averages.Vel_z ); - PROFILE_STOP( "Copy-State", 1 ); + d_ScaLBL_Comm->RegularLayout(d_Map, Pressure, Averages.Pressure); + d_ScaLBL_Comm->RegularLayout(d_Map, &Den[0], Averages.Rho_n); + d_ScaLBL_Comm->RegularLayout(d_Map, &Den[d_Np], Averages.Rho_w); + d_ScaLBL_Comm->RegularLayout(d_Map, &Velocity[0], Averages.Vel_x); + d_ScaLBL_Comm->RegularLayout(d_Map, &Velocity[d_Np], Averages.Vel_y); + d_ScaLBL_Comm->RegularLayout(d_Map, &Velocity[2 * d_Np], + Averages.Vel_z); + PROFILE_STOP("Copy-State", 1); } - PROFILE_STOP( "Copy data to host" ); + PROFILE_STOP("Copy data to host"); // Spawn threads to do the analysis work // if (timestep%d_restart_interval==0){ // if ( matches(type,AnalysisType::ComputeAverages) ) { - if ( timestep % d_analysis_interval == 0 ) { - auto work = new BasicWorkItem( type, timestep, Averages ); + if (timestep % d_analysis_interval == 0) { + auto work = new BasicWorkItem(type, timestep, Averages); work->add_dependency( - d_wait_subphase ); // Make sure we are done using analysis before modifying - work->add_dependency( d_wait_analysis ); - work->add_dependency( d_wait_vis ); - d_wait_analysis = d_tpool.add_work( work ); + d_wait_subphase); // Make sure we are done using analysis before modifying + work->add_dependency(d_wait_analysis); + work->add_dependency(d_wait_vis); + d_wait_analysis = d_tpool.add_work(work); } - if ( timestep % d_subphase_analysis_interval == 0 ) { - auto work = new SubphaseWorkItem( type, timestep, Averages ); + if (timestep % d_subphase_analysis_interval == 0) { + auto work = new SubphaseWorkItem(type, timestep, Averages); work->add_dependency( - d_wait_subphase ); // Make sure we are done using analysis before modifying - work->add_dependency( d_wait_analysis ); - work->add_dependency( d_wait_vis ); - d_wait_subphase = d_tpool.add_work( work ); + d_wait_subphase); // Make sure we are done using analysis before modifying + work->add_dependency(d_wait_analysis); + work->add_dependency(d_wait_vis); + d_wait_subphase = d_tpool.add_work(work); } - if ( timestep % d_restart_interval == 0 ) { + if (timestep % d_restart_interval == 0) { std::shared_ptr cfq, cDen; // Copy restart data to the CPU - cDen = make_shared_array( 2 * d_Np ); - cfq = make_shared_array( 19 * d_Np ); - ScaLBL_CopyToHost( cfq.get(), fq, 19 * d_Np * sizeof( double ) ); - ScaLBL_CopyToHost( cDen.get(), Den, 2 * d_Np * sizeof( double ) ); + cDen = make_shared_array(2 * d_Np); + cfq = make_shared_array(19 * d_Np); + ScaLBL_CopyToHost(cfq.get(), fq, 19 * d_Np * sizeof(double)); + ScaLBL_CopyToHost(cDen.get(), Den, 2 * d_Np * sizeof(double)); - if ( d_rank == 0 ) { - color_db->putScalar( "timestep", timestep ); - color_db->putScalar( "Restart", true ); - input_db->putDatabase( "Color", color_db ); - std::ofstream OutStream( "Restart.db" ); - input_db->print( OutStream, "" ); + if (d_rank == 0) { + color_db->putScalar("timestep", timestep); + color_db->putScalar("Restart", true); + input_db->putDatabase("Color", color_db); + std::ofstream OutStream("Restart.db"); + input_db->print(OutStream, ""); OutStream.close(); } // Write the restart file (using a seperate thread) - auto work1 = new WriteRestartWorkItem( d_restartFile.c_str(), cDen, cfq, d_Np ); - work1->add_dependency( d_wait_restart ); - d_wait_restart = d_tpool.add_work( work1 ); + auto work1 = + new WriteRestartWorkItem(d_restartFile.c_str(), cDen, cfq, d_Np); + work1->add_dependency(d_wait_restart); + d_wait_restart = d_tpool.add_work(work1); } - if ( timestep % d_visualization_interval == 0 ) { + if (timestep % d_visualization_interval == 0) { // Write the vis files - auto work = - new IOWorkItem( timestep, input_db, d_meshData, Averages, d_n, d_rank_info, getComm() ); - work->add_dependency( d_wait_analysis ); - work->add_dependency( d_wait_subphase ); - work->add_dependency( d_wait_vis ); - d_wait_vis = d_tpool.add_work( work ); + auto work = new IOWorkItem(timestep, input_db, d_meshData, Averages, + d_n, d_rank_info, getComm()); + work->add_dependency(d_wait_analysis); + work->add_dependency(d_wait_subphase); + work->add_dependency(d_wait_vis); + d_wait_vis = d_tpool.add_work(work); } - PROFILE_STOP( "basic" ); + PROFILE_STOP("basic"); } -void runAnalysis::WriteVisData( int timestep, std::shared_ptr input_db, - SubPhase &Averages, const double *Phi, double *Pressure, double *Velocity, double *fq, - double *Den ) -{ - auto color_db = input_db->getDatabase( "Color" ); - auto vis_db = input_db->getDatabase( "Visualization" ); +void runAnalysis::WriteVisData(int timestep, std::shared_ptr input_db, + SubPhase &Averages, const double *Phi, + double *Pressure, double *Velocity, double *fq, + double *Den) { + auto color_db = input_db->getDatabase("Color"); + auto vis_db = input_db->getDatabase("Visualization"); // int timestep = color_db->getWithDefault( "timestep", 0 ); // Check which analysis steps we need to perform - auto type = computeAnalysisType( timestep ); - if ( type == AnalysisType::AnalyzeNone ) + auto type = computeAnalysisType(timestep); + if (type == AnalysisType::AnalyzeNone) return; // Check how may queued items we have - if ( d_tpool.N_queued() > 20 ) { + if (d_tpool.N_queued() > 20) { std::cerr << "Analysis queue is getting behind, waiting ...\n"; finish(); } @@ -1323,15 +1249,15 @@ void runAnalysis::WriteVisData( int timestep, std::shared_ptr input_db // Copy the appropriate variables to the host (so we can spawn new threads) ScaLBL_DeviceBarrier(); - PROFILE_START( "write vis", 1 ); + PROFILE_START("write vis", 1); // if (Averages.WriteVis == true){ - auto work2 = - new IOWorkItem( timestep, input_db, d_meshData, Averages, d_n, d_rank_info, getComm() ); - work2->add_dependency( d_wait_vis ); - d_wait_vis = d_tpool.add_work( work2 ); + auto work2 = new IOWorkItem(timestep, input_db, d_meshData, Averages, d_n, + d_rank_info, getComm()); + work2->add_dependency(d_wait_vis); + d_wait_vis = d_tpool.add_work(work2); // Averages.WriteVis = false; - PROFILE_STOP( "write vis" ); + PROFILE_STOP("write vis"); } diff --git a/analysis/runAnalysis.h b/analysis/runAnalysis.h index 6adad3b9..bff537f1 100644 --- a/analysis/runAnalysis.h +++ b/analysis/runAnalysis.h @@ -26,42 +26,42 @@ #include "models/ColorModel.h" #include - // Types of analysis enum class AnalysisType : uint64_t { - AnalyzeNone = 0, - IdentifyBlobs = 0x01, + AnalyzeNone = 0, + IdentifyBlobs = 0x01, CopyPhaseIndicator = 0x02, - CopySimState = 0x04, - ComputeAverages = 0x08, - CreateRestart = 0x10, - WriteVis = 0x20, - ComputeSubphase = 0x40 + CopySimState = 0x04, + ComputeAverages = 0x08, + CreateRestart = 0x10, + WriteVis = 0x20, + ComputeSubphase = 0x40 }; - //! Class to run the analysis in multiple threads -class runAnalysis -{ +class runAnalysis { public: //! Constructor - runAnalysis( std::shared_ptr db, const RankInfoStruct &rank_info, - std::shared_ptr ScaLBL_Comm, std::shared_ptr dm, int Np, - bool Regular, IntArray Map ); - - runAnalysis( ScaLBL_ColorModel &ColorModel); + runAnalysis(std::shared_ptr db, const RankInfoStruct &rank_info, + std::shared_ptr ScaLBL_Comm, + std::shared_ptr dm, int Np, bool Regular, IntArray Map); + + runAnalysis(ScaLBL_ColorModel &ColorModel); //! Destructor ~runAnalysis(); //! Run the next analysis - void run( int timestep, std::shared_ptr db, TwoPhase &Averages, const double *Phi, - double *Pressure, double *Velocity, double *fq, double *Den ); + void run(int timestep, std::shared_ptr db, TwoPhase &Averages, + const double *Phi, double *Pressure, double *Velocity, double *fq, + double *Den); - void basic( int timestep, std::shared_ptr db, SubPhase &Averages, const double *Phi, - double *Pressure, double *Velocity, double *fq, double *Den ); - void WriteVisData( int timestep, std::shared_ptr vis_db, SubPhase &Averages, - const double *Phi, double *Pressure, double *Velocity, double *fq, double *Den ); + void basic(int timestep, std::shared_ptr db, SubPhase &Averages, + const double *Phi, double *Pressure, double *Velocity, + double *fq, double *Den); + void WriteVisData(int timestep, std::shared_ptr vis_db, + SubPhase &Averages, const double *Phi, double *Pressure, + double *Velocity, double *fq, double *Den); //! Finish all active analysis void finish(); @@ -80,27 +80,26 @@ public: * that all threads run on independent cores * @param[in] N_threads Number of threads, only used by some of the methods */ - void createThreads( const std::string &method = "default", int N_threads = 4 ); - + void createThreads(const std::string &method = "default", + int N_threads = 4); private: runAnalysis(); // Determine the analysis to perform - AnalysisType computeAnalysisType( int timestep ); + AnalysisType computeAnalysisType(int timestep); public: - class commWrapper - { + class commWrapper { public: Utilities::MPI comm; int tag; runAnalysis *analysis; - commWrapper( int tag, const Utilities::MPI &comm, runAnalysis *analysis ); - commWrapper() = delete; - commWrapper( const commWrapper &rhs ) = delete; - commWrapper &operator=( const commWrapper &rhs ) = delete; - commWrapper( commWrapper &&rhs ); + commWrapper(int tag, const Utilities::MPI &comm, runAnalysis *analysis); + commWrapper() = delete; + commWrapper(const commWrapper &rhs) = delete; + commWrapper &operator=(const commWrapper &rhs) = delete; + commWrapper(commWrapper &&rhs); ~commWrapper(); }; @@ -112,12 +111,13 @@ private: std::array d_N; // Number of local cells with ghosts int d_Np; int d_rank; - int d_restart_interval, d_analysis_interval, d_blobid_interval, d_visualization_interval; + int d_restart_interval, d_analysis_interval, d_blobid_interval, + d_visualization_interval; int d_subphase_analysis_interval; double d_beta; bool d_regular; - std::string format; // IO format string "silo" or "hdf5" - + std::string format; // IO format string "silo" or "hdf5" + ThreadPool d_tpool; RankInfoStruct d_rank_info; IntArray d_Map; diff --git a/analysis/uCT.cpp b/analysis/uCT.cpp index f25206db..d7ca0886 100644 --- a/analysis/uCT.cpp +++ b/analysis/uCT.cpp @@ -20,316 +20,312 @@ #include "analysis/filters.h" #include "analysis/imfilter.h" - -template -inline int sign( T x ) -{ - if ( x==0 ) +template inline int sign(T x) { + if (x == 0) return 0; - return x>0 ? 1:-1; + return x > 0 ? 1 : -1; } - -inline float trilinear( float dx, float dy, float dz, float f1, float f2, - float f3, float f4, float f5, float f6, float f7, float f8 ) -{ +inline float trilinear(float dx, float dy, float dz, float f1, float f2, + float f3, float f4, float f5, float f6, float f7, + float f8) { double f, dx2, dy2, dz2, h0, h1; dx2 = 1.0 - dx; dy2 = 1.0 - dy; dz2 = 1.0 - dz; - h0 = ( dx * f2 + dx2 * f1 ) * dy2 + ( dx * f4 + dx2 * f3 ) * dy; - h1 = ( dx * f6 + dx2 * f5 ) * dy2 + ( dx * f8 + dx2 * f7 ) * dy; - f = h0 * dz2 + h1 * dz; - return ( f ); + h0 = (dx * f2 + dx2 * f1) * dy2 + (dx * f4 + dx2 * f3) * dy; + h1 = (dx * f6 + dx2 * f5) * dy2 + (dx * f8 + dx2 * f7) * dy; + f = h0 * dz2 + h1 * dz; + return (f); } +void InterpolateMesh(const Array &Coarse, Array &Fine) { + PROFILE_START("InterpolateMesh"); -void InterpolateMesh( const Array &Coarse, Array &Fine ) -{ - PROFILE_START("InterpolateMesh"); + // Interpolate values from a Coarse mesh to a fine one + // This routine assumes cell-centered meshes with 1 ghost cell - // Interpolate values from a Coarse mesh to a fine one - // This routine assumes cell-centered meshes with 1 ghost cell + // Fine mesh + int Nx = int(Fine.size(0)) - 2; + int Ny = int(Fine.size(1)) - 2; + int Nz = int(Fine.size(2)) - 2; - // Fine mesh - int Nx = int(Fine.size(0))-2; - int Ny = int(Fine.size(1))-2; - int Nz = int(Fine.size(2))-2; + // Coarse mesh + int nx = int(Coarse.size(0)) - 2; + int ny = int(Coarse.size(1)) - 2; + int nz = int(Coarse.size(2)) - 2; - // Coarse mesh - int nx = int(Coarse.size(0))-2; - int ny = int(Coarse.size(1))-2; - int nz = int(Coarse.size(2))-2; + // compute the stride + int hx = Nx / nx; + int hy = Ny / ny; + int hz = Nz / nz; + ASSERT(nx * hx == Nx); + ASSERT(ny * hy == Ny); + ASSERT(nz * hz == Nz); - // compute the stride - int hx = Nx/nx; - int hy = Ny/ny; - int hz = Nz/nz; - ASSERT(nx*hx==Nx); - ASSERT(ny*hy==Ny); - ASSERT(nz*hz==Nz); + // value to map distance between meshes (since distance is in voxels) + // usually hx=hy=hz (or something very close) + // the mapping is not exact + // however, it's assumed the coarse solution will be refined + // a good guess is the goal here! + float mapvalue = sqrt(hx * hx + hy * hy + hz * hz); - // value to map distance between meshes (since distance is in voxels) - // usually hx=hy=hz (or something very close) - // the mapping is not exact - // however, it's assumed the coarse solution will be refined - // a good guess is the goal here! - float mapvalue = sqrt(hx*hx+hy*hy+hz*hz); - - // Interpolate to the fine mesh - for (int k=-1; k=-1&&k0=0&&dz<=1); - for (int j=-1; j=-1&&j0=0&&dy<=1); - for (int i=-1; i=-1&&i0=0&&dx<=1); - float val = trilinear( dx, dy, dz, - Coarse(i1,j1,k1), Coarse(i2,j1,k1), Coarse(i1,j2,k1), Coarse(i2,j2,k1), - Coarse(i1,j1,k2), Coarse(i2,j1,k2), Coarse(i1,j2,k2), Coarse(i2,j2,k2) ); - Fine(i+1,j+1,k+1) = mapvalue*val; - } - } - } - PROFILE_STOP("InterpolateMesh"); + // Interpolate to the fine mesh + for (int k = -1; k < Nz + 1; k++) { + int k0 = floor((k - 0.5 * hz) / hz); + int k1 = k0 + 1; + int k2 = k0 + 2; + float dz = ((k + 0.5) - (k0 + 0.5) * hz) / hz; + ASSERT(k0 >= -1 && k0 < nz + 1 && dz >= 0 && dz <= 1); + for (int j = -1; j < Ny + 1; j++) { + int j0 = floor((j - 0.5 * hy) / hy); + int j1 = j0 + 1; + int j2 = j0 + 2; + float dy = ((j + 0.5) - (j0 + 0.5) * hy) / hy; + ASSERT(j0 >= -1 && j0 < ny + 1 && dy >= 0 && dy <= 1); + for (int i = -1; i < Nx + 1; i++) { + int i0 = floor((i - 0.5 * hx) / hx); + int i1 = i0 + 1; + int i2 = i0 + 2; + float dx = ((i + 0.5) - (i0 + 0.5) * hx) / hx; + ASSERT(i0 >= -1 && i0 < nx + 1 && dx >= 0 && dx <= 1); + float val = trilinear( + dx, dy, dz, Coarse(i1, j1, k1), Coarse(i2, j1, k1), + Coarse(i1, j2, k1), Coarse(i2, j2, k1), Coarse(i1, j1, k2), + Coarse(i2, j1, k2), Coarse(i1, j2, k2), Coarse(i2, j2, k2)); + Fine(i + 1, j + 1, k + 1) = mapvalue * val; + } + } + } + PROFILE_STOP("InterpolateMesh"); } - // Smooth the data using the distance -void smooth( const Array& VOL, const Array& Dist, float sigma, Array& MultiScaleSmooth, fillHalo& fillFloat ) -{ - for (size_t i=0; i0 ? -1:1; - MultiScaleSmooth(i) = tmp*VOL(i) + (1-tmp)*value; - } - fillFloat.fill(MultiScaleSmooth); +void smooth(const Array &VOL, const Array &Dist, float sigma, + Array &MultiScaleSmooth, fillHalo &fillFloat) { + for (size_t i = 0; i < VOL.length(); i++) { + // use exponential weight based on the distance + float dst = Dist(i); + float tmp = exp(-(dst * dst) / (sigma * sigma)); + float value = dst > 0 ? -1 : 1; + MultiScaleSmooth(i) = tmp * VOL(i) + (1 - tmp) * value; + } + fillFloat.fill(MultiScaleSmooth); } - // Segment the data -void segment( const Array& data, Array& ID, float tol ) -{ - ASSERT(data.size()==ID.size()); - for (size_t i=0; i tol ) +void segment(const Array &data, Array &ID, float tol) { + ASSERT(data.size() == ID.size()); + for (size_t i = 0; i < data.length(); i++) { + if (data(i) > tol) ID(i) = 0; else ID(i) = 1; } } - // Remove disconnected phases -void removeDisconnected( Array& ID, const Domain& Dm ) -{ +void removeDisconnected(Array &ID, const Domain &Dm) { // Run blob identification to remove disconnected volumes BlobIDArray GlobalBlobID; DoubleArray SignDist(ID.size()); DoubleArray Phase(ID.size()); - for (size_t i=0; i 0 ) + ComputeGlobalBlobIDs(ID.size(0) - 2, ID.size(1) - 2, ID.size(2) - 2, + Dm.rank_info, Phase, SignDist, 0, 0, GlobalBlobID, + Dm.Comm); + for (size_t i = 0; i < ID.length(); i++) { + if (GlobalBlobID(i) > 0) ID(i) = 0; ID(i) = GlobalBlobID(i); } } - // Solve a level (without any coarse level information) -void solve( const Array& VOL, Array& Mean, Array& ID, - Array& Dist, Array& MultiScaleSmooth, Array& NonLocalMean, - fillHalo& fillFloat, const Domain& Dm, int nprocx, - float threshold, float lamda, float sigsq, int depth) -{ - PROFILE_SCOPED(timer,"solve"); +void solve(const Array &VOL, Array &Mean, Array &ID, + Array &Dist, Array &MultiScaleSmooth, + Array &NonLocalMean, fillHalo &fillFloat, + const Domain &Dm, int nprocx, float threshold, float lamda, + float sigsq, int depth) { + PROFILE_SCOPED(timer, "solve"); // Compute the median filter on the sparse array - Med3D( VOL, Mean ); - fillFloat.fill( Mean ); - segment( Mean, ID, threshold ); + Med3D(VOL, Mean); + fillFloat.fill(Mean); + segment(Mean, ID, threshold); // Compute the distance using the segmented volume - CalcDist( Dist, ID, Dm ); - fillFloat.fill(Dist); - smooth( VOL, Dist, 2.0, MultiScaleSmooth, fillFloat ); + CalcDist(Dist, ID, Dm); + fillFloat.fill(Dist); + smooth(VOL, Dist, 2.0, MultiScaleSmooth, fillFloat); // Compute non-local mean // int depth = 5; // float sigsq=0.1; - int nlm_count = NLM3D( MultiScaleSmooth, Mean, Dist, NonLocalMean, depth, sigsq); - NULL_USE( nlm_count ); - fillFloat.fill(NonLocalMean); + int nlm_count = + NLM3D(MultiScaleSmooth, Mean, Dist, NonLocalMean, depth, sigsq); + NULL_USE(nlm_count); + fillFloat.fill(NonLocalMean); } - // Refine a solution from a coarse grid to a fine grid -void refine( const Array& Dist_coarse, - const Array& VOL, Array& Mean, Array& ID, - Array& Dist, Array& MultiScaleSmooth, Array& NonLocalMean, - fillHalo& fillFloat, const Domain& Dm, int nprocx, int level, - float threshold, float lamda, float sigsq, int depth) -{ - PROFILE_SCOPED(timer,"refine"); - int ratio[3] = { int(Dist.size(0)/Dist_coarse.size(0)), - int(Dist.size(1)/Dist_coarse.size(1)), - int(Dist.size(2)/Dist_coarse.size(2)) }; +void refine(const Array &Dist_coarse, const Array &VOL, + Array &Mean, Array &ID, Array &Dist, + Array &MultiScaleSmooth, Array &NonLocalMean, + fillHalo &fillFloat, const Domain &Dm, int nprocx, int level, + float threshold, float lamda, float sigsq, int depth) { + PROFILE_SCOPED(timer, "refine"); + int ratio[3] = {int(Dist.size(0) / Dist_coarse.size(0)), + int(Dist.size(1) / Dist_coarse.size(1)), + int(Dist.size(2) / Dist_coarse.size(2))}; // Interpolate the distance from the coarse to fine grid - InterpolateMesh( Dist_coarse, Dist ); + InterpolateMesh(Dist_coarse, Dist); // Compute the median filter on the array and segment - Med3D( VOL, Mean ); - fillFloat.fill( Mean ); - segment( Mean, ID, threshold ); + Med3D(VOL, Mean); + fillFloat.fill(Mean); + segment(Mean, ID, threshold); // If the ID has the wrong distance, set the distance to 0 and run a simple filter to set neighbors to 0 - for (size_t i=0; i0 ? 1:0; - if ( id != ID(i) ) + for (size_t i = 0; i < ID.length(); i++) { + char id = Dist(i) > 0 ? 1 : 0; + if (id != ID(i)) Dist(i) = 0; } - fillFloat.fill( Dist ); - std::function filter_1D = []( int N, const float* data ) - { - bool zero = data[0]==0 || data[2]==0; - return zero ? data[1]*1e-12 : data[1]; + fillFloat.fill(Dist); + std::function filter_1D = [](int N, + const float *data) { + bool zero = data[0] == 0 || data[2] == 0; + return zero ? data[1] * 1e-12 : data[1]; }; - std::vector BC(3,imfilter::BC::replicate); - std::vector> filter_set(3,filter_1D); - Dist = imfilter::imfilter_separable( Dist, {1,1,1}, filter_set, BC ); - fillFloat.fill( Dist ); + std::vector BC(3, imfilter::BC::replicate); + std::vector> filter_set(3, + filter_1D); + Dist = imfilter::imfilter_separable(Dist, {1, 1, 1}, filter_set, BC); + fillFloat.fill(Dist); // Smooth the volume data - float h = 2*lamda*sqrt(double(ratio[0]*ratio[0]+ratio[1]*ratio[1]+ratio[2]*ratio[2])); - smooth( VOL, Dist, h, MultiScaleSmooth, fillFloat ); + float h = 2 * lamda * + sqrt(double(ratio[0] * ratio[0] + ratio[1] * ratio[1] + + ratio[2] * ratio[2])); + smooth(VOL, Dist, h, MultiScaleSmooth, fillFloat); // Compute non-local mean -// int depth = 3; -// float sigsq = 0.1; - int nlm_count = NLM3D( MultiScaleSmooth, Mean, Dist, NonLocalMean, depth, sigsq); - NULL_USE( nlm_count ); - fillFloat.fill(NonLocalMean); - segment( NonLocalMean, ID, 0.001 ); - for (size_t i=0; i0 ? 1:0; - if ( id!=ID(i) || fabs(Dist(i))<1 ) - Dist(i) = 2.0*ID(i)-1.0; + // int depth = 3; + // float sigsq = 0.1; + int nlm_count = + NLM3D(MultiScaleSmooth, Mean, Dist, NonLocalMean, depth, sigsq); + NULL_USE(nlm_count); + fillFloat.fill(NonLocalMean); + segment(NonLocalMean, ID, 0.001); + for (size_t i = 0; i < ID.length(); i++) { + char id = Dist(i) > 0 ? 1 : 0; + if (id != ID(i) || fabs(Dist(i)) < 1) + Dist(i) = 2.0 * ID(i) - 1.0; } // Remove disconnected domains //removeDisconnected( ID, Dm ); // Compute the distance using the segmented volume - if ( level > 0 ) { - CalcDist( Dist, ID, Dm ); - fillFloat.fill(Dist); + if (level > 0) { + CalcDist(Dist, ID, Dm); + fillFloat.fill(Dist); } } - // Remove regions that are likely noise by shrinking the volumes by dx, // removing all values that are more than dx+delta from the surface, and then // growing by dx+delta and intersecting with the original data -void filter_final( Array& ID, Array& Dist, - fillHalo& fillFloat, const Domain& Dm, - Array& Mean, Array& Dist1, Array& Dist2 ) -{ - PROFILE_SCOPED(timer,"filter_final"); - int rank = Dm.Comm.getRank(); - int Nx = Dm.Nx-2; - int Ny = Dm.Ny-2; - int Nz = Dm.Nz-2; +void filter_final(Array &ID, Array &Dist, + fillHalo &fillFloat, const Domain &Dm, + Array &Mean, Array &Dist1, + Array &Dist2) { + PROFILE_SCOPED(timer, "filter_final"); + int rank = Dm.Comm.getRank(); + int Nx = Dm.Nx - 2; + int Ny = Dm.Ny - 2; + int Nz = Dm.Nz - 2; // Calculate the distance - CalcDist( Dist, ID, Dm ); + CalcDist(Dist, ID, Dm); fillFloat.fill(Dist); // Compute the range to shrink the volume based on the L2 norm of the distance - Array Dist0(Nx,Ny,Nz); - fillFloat.copy(Dist,Dist0); + Array Dist0(Nx, Ny, Nz); + fillFloat.copy(Dist, Dist0); float tmp = 0; - for (size_t i=0; i(Dist0.length()) ); - const float dx1 = 0.3*tmp; - const float dx2 = 1.05*dx1; - if (rank==0) - printf(" %0.1f %0.1f %0.1f\n",tmp,dx1,dx2); + for (size_t i = 0; i < Dist0.length(); i++) + tmp += Dist0(i) * Dist0(i); + tmp = + sqrt(Dm.Comm.sumReduce(tmp) / Dm.Comm.sumReduce(Dist0.length())); + const float dx1 = 0.3 * tmp; + const float dx2 = 1.05 * dx1; + if (rank == 0) + printf(" %0.1f %0.1f %0.1f\n", tmp, dx1, dx2); // Update the IDs/Distance removing regions that are < dx of the range Dist1 = Dist; Dist2 = Dist; Array ID1 = ID; Array ID2 = ID; - for (size_t i=0; i dx1 ? 1:0; + for (size_t i = 0; i < ID.length(); i++) { + ID1(i) = Dist(i) < -dx1 ? 1 : 0; + ID2(i) = Dist(i) > dx1 ? 1 : 0; } //Array Dist1 = Dist; //Array Dist2 = Dist; - CalcDist( Dist1, ID1, Dm ); - CalcDist( Dist2, ID2, Dm ); + CalcDist(Dist1, ID1, Dm); + CalcDist(Dist2, ID2, Dm); fillFloat.fill(Dist1); fillFloat.fill(Dist2); // Keep those regions that are within dx2 of the new volumes Mean = Dist; - for (size_t i=0; i0 && ID(i)<=0 ) { + for (size_t i = 0; i < ID.length(); i++) { + if (Dist1(i) + dx2 > 0 && ID(i) <= 0) { Mean(i) = -1; - } else if ( Dist2(i)+dx2>0 && ID(i)>0 ) { + } else if (Dist2(i) + dx2 > 0 && ID(i) > 0) { Mean(i) = 1; } else { - Mean(i) = Dist(i)>0 ? 0.5:-0.5; + Mean(i) = Dist(i) > 0 ? 0.5 : -0.5; } } // Find regions of uncertainty that are entirely contained within another region - fillHalo fillDouble(Dm.Comm,Dm.rank_info,{Nx,Ny,Nz},{1,1,1},0,1); - fillHalo fillInt(Dm.Comm,Dm.rank_info,{Nx,Ny,Nz},{1,1,1},0,1); + fillHalo fillDouble(Dm.Comm, Dm.rank_info, {Nx, Ny, Nz}, {1, 1, 1}, + 0, 1); + fillHalo fillInt(Dm.Comm, Dm.rank_info, {Nx, Ny, Nz}, {1, 1, 1}, + 0, 1); BlobIDArray GlobalBlobID; DoubleArray SignDist(ID.size()); - for (size_t i=0; i mean(N_blobs,0); - std::vector count(N_blobs,0); - for (int k=1; k<=Nz; k++) { - for (int j=1; j<=Ny; j++) { - for (int i=1; i<=Nx; i++) { - int id = GlobalBlobID(i,j,k); - if ( id >= 0 ) { - if ( GlobalBlobID(i-1,j,k)<0 ) { - mean[id] += Mean(i-1,j,k); + int N_blobs = Dm.Comm.maxReduce(GlobalBlobID.max() + 1); + std::vector mean(N_blobs, 0); + std::vector count(N_blobs, 0); + for (int k = 1; k <= Nz; k++) { + for (int j = 1; j <= Ny; j++) { + for (int i = 1; i <= Nx; i++) { + int id = GlobalBlobID(i, j, k); + if (id >= 0) { + if (GlobalBlobID(i - 1, j, k) < 0) { + mean[id] += Mean(i - 1, j, k); count[id]++; } - if ( GlobalBlobID(i+1,j,k)<0 ) { - mean[id] += Mean(i+1,j,k); + if (GlobalBlobID(i + 1, j, k) < 0) { + mean[id] += Mean(i + 1, j, k); count[id]++; } - if ( GlobalBlobID(i,j-1,k)<0 ) { - mean[id] += Mean(i,j-1,k); + if (GlobalBlobID(i, j - 1, k) < 0) { + mean[id] += Mean(i, j - 1, k); count[id]++; } - if ( GlobalBlobID(i,j+1,k)<0 ) { - mean[id] += Mean(i,j+1,k); + if (GlobalBlobID(i, j + 1, k) < 0) { + mean[id] += Mean(i, j + 1, k); count[id]++; } - if ( GlobalBlobID(i,j,k-1)<0 ) { - mean[id] += Mean(i,j,k-1); + if (GlobalBlobID(i, j, k - 1) < 0) { + mean[id] += Mean(i, j, k - 1); count[id]++; } - if ( GlobalBlobID(i,j,k+1)<0 ) { - mean[id] += Mean(i,j,k+1); + if (GlobalBlobID(i, j, k + 1) < 0) { + mean[id] += Mean(i, j, k + 1); count[id]++; } } @@ -338,16 +334,16 @@ void filter_final( Array& ID, Array& Dist, } mean = Dm.Comm.sumReduce(mean); count = Dm.Comm.sumReduce(count); - for (size_t i=0; i= 0 ) { - if ( fabs(mean[id]) > 0.95 ) { + if (id >= 0) { + if (fabs(mean[id]) > 0.95) { // Isolated domain surrounded by one domain GlobalBlobID(i) = -2; Mean(i) = sign(mean[id]); @@ -359,53 +355,54 @@ void filter_final( Array& ID, Array& Dist, } // Perform the final segmentation and update the distance fillFloat.fill(Mean); - segment( Mean, ID, 0.01 ); - CalcDist( Dist, ID, Dm ); + segment(Mean, ID, 0.01); + CalcDist(Dist, ID, Dm); fillFloat.fill(Dist); } - - // Filter the original data -void filter_src( const Domain& Dm, Array& src ) -{ - PROFILE_START("Filter source data"); - int Nx = Dm.Nx-2; - int Ny = Dm.Ny-2; - int Nz = Dm.Nz-2; - fillHalo fillFloat(Dm.Comm,Dm.rank_info,{Nx,Ny,Nz},{1,1,1},0,1); +void filter_src(const Domain &Dm, Array &src) { + PROFILE_START("Filter source data"); + int Nx = Dm.Nx - 2; + int Ny = Dm.Ny - 2; + int Nz = Dm.Nz - 2; + fillHalo fillFloat(Dm.Comm, Dm.rank_info, {Nx, Ny, Nz}, {1, 1, 1}, 0, + 1); // Perform a hot-spot filter on the data - std::vector BC = { imfilter::BC::replicate, imfilter::BC::replicate, imfilter::BC::replicate }; - std::function&)> filter_3D = []( const Array& data ) - { - float min1 = std::min(data(0,1,1),data(2,1,1)); - float min2 = std::min(data(1,0,1),data(1,2,1)); - float min3 = std::min(data(1,1,0),data(1,1,2)); - float max1 = std::max(data(0,1,1),data(2,1,1)); - float max2 = std::max(data(1,0,1),data(1,2,1)); - float max3 = std::max(data(1,1,0),data(1,1,2)); - float min = std::min(min1,std::min(min2,min3)); - float max = std::max(max1,std::max(max2,max3)); - return std::max(std::min(data(1,1,1),max),min); - }; - std::function&)> filter_1D = []( const Array& data ) - { - float min = std::min(data(0),data(2)); - float max = std::max(data(0),data(2)); - return std::max(std::min(data(1),max),min); - }; + std::vector BC = {imfilter::BC::replicate, + imfilter::BC::replicate, + imfilter::BC::replicate}; + std::function &)> filter_3D = + [](const Array &data) { + float min1 = std::min(data(0, 1, 1), data(2, 1, 1)); + float min2 = std::min(data(1, 0, 1), data(1, 2, 1)); + float min3 = std::min(data(1, 1, 0), data(1, 1, 2)); + float max1 = std::max(data(0, 1, 1), data(2, 1, 1)); + float max2 = std::max(data(1, 0, 1), data(1, 2, 1)); + float max3 = std::max(data(1, 1, 0), data(1, 1, 2)); + float min = std::min(min1, std::min(min2, min3)); + float max = std::max(max1, std::max(max2, max3)); + return std::max(std::min(data(1, 1, 1), max), min); + }; + std::function &)> filter_1D = + [](const Array &data) { + float min = std::min(data(0), data(2)); + float max = std::max(data(0), data(2)); + return std::max(std::min(data(1), max), min); + }; //LOCVOL[0] = imfilter::imfilter( LOCVOL[0], {1,1,1}, filter_3D, BC ); - std::vector&)>> filter_set(3,filter_1D); - src = imfilter::imfilter_separable( src, {1,1,1}, filter_set, BC ); - fillFloat.fill( src ); + std::vector &)>> filter_set( + 3, filter_1D); + src = imfilter::imfilter_separable(src, {1, 1, 1}, filter_set, BC); + fillFloat.fill(src); // Perform a gaussian filter on the data - int Nh[3] = { 2, 2, 2 }; - float sigma[3] = { 1.0, 1.0, 1.0 }; + int Nh[3] = {2, 2, 2}; + float sigma[3] = {1.0, 1.0, 1.0}; std::vector> H(3); - H[0] = imfilter::create_filter( { Nh[0] }, "gaussian", &sigma[0] ); - H[1] = imfilter::create_filter( { Nh[1] }, "gaussian", &sigma[1] ); - H[2] = imfilter::create_filter( { Nh[2] }, "gaussian", &sigma[2] ); - src = imfilter::imfilter_separable( src, H, BC ); - fillFloat.fill( src ); - PROFILE_STOP("Filter source data"); + H[0] = imfilter::create_filter({Nh[0]}, "gaussian", &sigma[0]); + H[1] = imfilter::create_filter({Nh[1]}, "gaussian", &sigma[1]); + H[2] = imfilter::create_filter({Nh[2]}, "gaussian", &sigma[2]); + src = imfilter::imfilter_separable(src, H, BC); + fillFloat.fill(src); + PROFILE_STOP("Filter source data"); } diff --git a/analysis/uCT.h b/analysis/uCT.h index 940b3d38..63c3f83b 100644 --- a/analysis/uCT.h +++ b/analysis/uCT.h @@ -21,54 +21,46 @@ #include "common/Domain.h" #include "common/Communication.h" - - /*! * @brief Interpolate between meshes * @details This routine interpolates from a coarse to a fine mesh * @param[in] Coarse Coarse mesh solution * @param[out] Fine Fine mesh solution */ -void InterpolateMesh( const Array &Coarse, Array &Fine ); - +void InterpolateMesh(const Array &Coarse, Array &Fine); // Smooth the data using the distance -void smooth( const Array& VOL, const Array& Dist, float sigma, Array& MultiScaleSmooth, fillHalo& fillFloat ); - +void smooth(const Array &VOL, const Array &Dist, float sigma, + Array &MultiScaleSmooth, fillHalo &fillFloat); // Segment the data -void segment( const Array& data, Array& ID, float tol ); - +void segment(const Array &data, Array &ID, float tol); // Remove disconnected phases -void removeDisconnected( Array& ID, const Domain& Dm ); - +void removeDisconnected(Array &ID, const Domain &Dm); // Solve a level (without any coarse level information) -void solve( const Array& VOL, Array& Mean, Array& ID, - Array& Dist, Array& MultiScaleSmooth, Array& NonLocalMean, - fillHalo& fillFloat, const Domain& Dm, int nprocx, - float threshold, float lamda, float sigsq, int depth); - +void solve(const Array &VOL, Array &Mean, Array &ID, + Array &Dist, Array &MultiScaleSmooth, + Array &NonLocalMean, fillHalo &fillFloat, + const Domain &Dm, int nprocx, float threshold, float lamda, + float sigsq, int depth); // Refine a solution from a coarse grid to a fine grid -void refine( const Array& Dist_coarse, - const Array& VOL, Array& Mean, Array& ID, - Array& Dist, Array& MultiScaleSmooth, Array& NonLocalMean, - fillHalo& fillFloat, const Domain& Dm, int nprocx, int level, - float threshold, float lamda, float sigsq, int depth); - +void refine(const Array &Dist_coarse, const Array &VOL, + Array &Mean, Array &ID, Array &Dist, + Array &MultiScaleSmooth, Array &NonLocalMean, + fillHalo &fillFloat, const Domain &Dm, int nprocx, int level, + float threshold, float lamda, float sigsq, int depth); // Remove regions that are likely noise by shrinking the volumes by dx, // removing all values that are more than dx+delta from the surface, and then // growing by dx+delta and intersecting with the original data -void filter_final( Array& ID, Array& Dist, - fillHalo& fillFloat, const Domain& Dm, - Array& Mean, Array& Dist1, Array& Dist2 ); - +void filter_final(Array &ID, Array &Dist, + fillHalo &fillFloat, const Domain &Dm, + Array &Mean, Array &Dist1, Array &Dist2); // Filter the original data -void filter_src( const Domain& Dm, Array& src ); - +void filter_src(const Domain &Dm, Array &src); #endif diff --git a/common/Array.h b/common/Array.h index 08f0ce11..8f76f518 100644 --- a/common/Array.h +++ b/common/Array.h @@ -27,13 +27,10 @@ #include #include - /*! * Class Array is a multi-dimensional array class written by Mark Berrill */ -template -class Array final -{ +template class Array final { public: // Constructors / assignment operators /*! * Create a new empty Array @@ -44,20 +41,20 @@ public: // Constructors / assignment operators * Create an Array with the given size * @param N Size of the array */ - explicit Array( const ArraySize &N ); + explicit Array(const ArraySize &N); /*! * Create a new 1D Array with the given number of elements * @param N Number of elements in the array */ - explicit Array( size_t N ); + explicit Array(size_t N); /*! * Create a new 2D Array with the given number of rows and columns * @param N_rows Number of rows * @param N_columns Number of columns */ - explicit Array( size_t N_rows, size_t N_columns ); + explicit Array(size_t N_rows, size_t N_columns); /*! * Create a new 3D Array with the given number of rows and columns @@ -65,7 +62,7 @@ public: // Constructors / assignment operators * @param N2 Number of columns * @param N3 Number of elements in the third dimension */ - explicit Array( size_t N1, size_t N2, size_t N3 ); + explicit Array(size_t N1, size_t N2, size_t N3); /*! * Create a new 4D Array with the given number of rows and columns @@ -74,7 +71,7 @@ public: // Constructors / assignment operators * @param N3 Number of elements in the third dimension * @param N4 Number of elements in the fourth dimension */ - explicit Array( size_t N1, size_t N2, size_t N3, size_t N4 ); + explicit Array(size_t N1, size_t N2, size_t N3, size_t N4); /*! * Create a new 4D Array with the given number of rows and columns @@ -84,76 +81,74 @@ public: // Constructors / assignment operators * @param N4 Number of elements in the fourth dimension * @param N5 Number of elements in the fifth dimension */ - explicit Array( size_t N1, size_t N2, size_t N3, size_t N4, size_t N5 ); + explicit Array(size_t N1, size_t N2, size_t N3, size_t N4, size_t N5); /*! * Create a multi-dimensional Array with the given number of elements * @param N Number of elements in each dimension * @param data Optional raw array to copy the src data */ - explicit Array( const std::vector &N, const TYPE *data = nullptr ); + explicit Array(const std::vector &N, const TYPE *data = nullptr); /*! * Create a 1D Array using a string that mimic's MATLAB * @param range Range of the data */ - explicit Array( std::string range ); + explicit Array(std::string range); /*! * Create a 1D Array with the given initializer list * @param data Input data */ - Array( std::initializer_list data ); + Array(std::initializer_list data); /*! * Create a 2D Array with the given initializer lists * @param data Input data */ - Array( std::initializer_list> data ); - + Array(std::initializer_list> data); /*! * Copy constructor * @param rhs Array to copy */ - Array( const Array &rhs ); + Array(const Array &rhs); /*! * Move constructor * @param rhs Array to copy */ - Array( Array &&rhs ); + Array(Array &&rhs); /*! * Assignment operator * @param rhs Array to copy */ - Array &operator=( const Array &rhs ); + Array &operator=(const Array &rhs); /*! * Move assignment operator * @param rhs Array to copy */ - Array &operator=( Array &&rhs ); + Array &operator=(Array &&rhs); /*! * Assignment operator * @param rhs std::vector to copy */ - Array &operator=( const std::vector &rhs ); + Array &operator=(const std::vector &rhs); //! Is copyable? inline bool isCopyable() const { return d_isCopyable; } //! Set is copyable - inline void setCopyable( bool flag ) { d_isCopyable = flag; } + inline void setCopyable(bool flag) { d_isCopyable = flag; } //! Is fixed size? inline bool isFixedSize() const { return d_isFixedSize; } //! Set is copyable - inline void setFixedSize( bool flag ) { d_isFixedSize = flag; } - + inline void setFixedSize(bool flag) { d_isFixedSize = flag; } public: // Views/copies/subset /*! @@ -161,30 +156,29 @@ public: // Views/copies/subset * @param N Number of elements in each dimension * @param data Pointer to the data */ - static std::unique_ptr view( const ArraySize &N, std::shared_ptr data ); - + static std::unique_ptr view(const ArraySize &N, + std::shared_ptr data); /*! * Create a multi-dimensional Array view to a raw block of data * @param N Number of elements in each dimension * @param data Pointer to the data */ - static std::unique_ptr constView( const ArraySize &N, - std::shared_ptr const &data ); - + static std::unique_ptr + constView(const ArraySize &N, std::shared_ptr const &data); /*! * Make this object a view of the src * @param src Source vector to take the view of */ - void view2( Array &src ); + void view2(Array &src); /*! * Make this object a view of the data * @param N Number of elements in each dimension * @param data Pointer to the data */ - void view2( const ArraySize &N, std::shared_ptr data ); + void view2(const ArraySize &N, std::shared_ptr data); /*! * Make this object a view of the raw data (expert use only). @@ -199,10 +193,9 @@ public: // Views/copies/subset * @param isCopyable Once the view is created, can the array be copied * @param isFixedSize Once the view is created, is the array size fixed */ - inline void viewRaw( - int ndim, const size_t *dims, TYPE *data, bool isCopyable = true, bool isFixedSize = true ) - { - viewRaw( ArraySize( ndim, dims ), data, isCopyable, isFixedSize ); + inline void viewRaw(int ndim, const size_t *dims, TYPE *data, + bool isCopyable = true, bool isFixedSize = true) { + viewRaw(ArraySize(ndim, dims), data, isCopyable, isFixedSize); } /*! @@ -217,7 +210,8 @@ public: // Views/copies/subset * @param isCopyable Once the view is created, can the array be copied * @param isFixedSize Once the view is created, is the array size fixed */ - void viewRaw( const ArraySize &N, TYPE *data, bool isCopyable = true, bool isFixedSize = true ); + void viewRaw(const ArraySize &N, TYPE *data, bool isCopyable = true, + bool isFixedSize = true); /*! * Create an array view of the given data (expert use only). @@ -229,10 +223,9 @@ public: // Views/copies/subset * @param N Number of elements in each dimension * @param data Pointer to the data */ - static inline Array staticView( const ArraySize &N, TYPE *data ) - { + static inline Array staticView(const ArraySize &N, TYPE *data) { Array x; - x.viewRaw( N, data, true, true ); + x.viewRaw(N, data, true, true); return x; } @@ -240,39 +233,34 @@ public: // Views/copies/subset * Convert an array of one type to another. This may or may not allocate new memory. * @param array Input array */ - template + template static inline std::unique_ptr> - convert( std::shared_ptr> array ) - { - auto array2 = std::make_unique>( array->size() ); - array2.copy( *array ); + convert(std::shared_ptr> array) { + auto array2 = std::make_unique>(array->size()); + array2.copy(*array); return array2; } - /*! * Convert an array of one type to another. This may or may not allocate new memory. * @param array Input array */ - template + template static inline std::unique_ptr> - convert( std::shared_ptr> array ) - { - auto array2 = std::make_unique>( array->size() ); - array2.copy( *array ); + convert(std::shared_ptr> array) { + auto array2 = std::make_unique>(array->size()); + array2.copy(*array); return array2; } - /*! * Copy and convert data from another array to this array * @param array Source array */ - template - void inline copy( const Array &array ) - { - resize( array.size() ); - copy( array.data() ); + template + void inline copy(const Array &array) { + resize(array.size()); + copy(array.data()); } /*! @@ -280,39 +268,33 @@ public: // Views/copies/subset * Note: The current array must be allocated to the proper size first. * @param data Source data */ - template - inline void copy( const TYPE2 *data ); + template inline void copy(const TYPE2 *data); /*! * Copy and convert data from this array to a raw vector. * @param data Source data */ - template - inline void copyTo( TYPE2 *data ) const; + template inline void copyTo(TYPE2 *data) const; /*! * Copy and convert data from this array to a new array */ - template - Array> inline cloneTo() const - { - Array> dst( this->size() ); - copyTo( dst.data() ); + template + Array> inline cloneTo() const { + Array> dst(this->size()); + copyTo(dst.data()); return dst; } - /*! swap the raw data pointers for the Arrays after checking for compatibility */ - void swap( Array &other ); - + void swap(Array &other); /*! * Fill the array with the given value * @param y Value to fill */ - inline void fill( const TYPE &y ) - { - for ( auto &x : *this ) + inline void fill(const TYPE &y) { + for (auto &x : *this) x = y; } @@ -320,67 +302,56 @@ public: // Views/copies/subset * Scale the array by the given value * @param y Value to scale by */ - template - inline void scale( const TYPE2 &y ) - { - for ( auto &x : *this ) + template inline void scale(const TYPE2 &y) { + for (auto &x : *this) x *= y; } - /*! * Set the values of this array to pow(base, exp) * @param base Base array * @param exp Exponent value */ - void pow( const Array &base, const TYPE &exp ); - + void pow(const Array &base, const TYPE &exp); //! Destructor ~Array(); - //! Clear the data in the array void clear(); - //! Return the size of the Array inline int ndim() const { return d_size.ndim(); } - //! Return the size of the Array inline const ArraySize &size() const { return d_size; } - //! Return the size of the Array - inline size_t size( int d ) const { return d_size[d]; } - + inline size_t size(int d) const { return d_size[d]; } //! Return the size of the Array inline size_t length() const { return d_size.length(); } - //! Return true if the Array is empty inline bool empty() const { return d_size.length() == 0; } - //! Return true if the Array is not empty inline operator bool() const { return d_size.length() != 0; } - /*! * Resize the Array * @param N NUmber of elements */ - inline void resize( size_t N ) { resize( ArraySize( N ) ); } - + inline void resize(size_t N) { resize(ArraySize(N)); } /*! * Resize the Array * @param N_row Number of rows * @param N_col Number of columns */ - inline void resize( size_t N_row, size_t N_col ) { resize( ArraySize( N_row, N_col ) ); } + inline void resize(size_t N_row, size_t N_col) { + resize(ArraySize(N_row, N_col)); + } /*! * Resize the Array @@ -388,14 +359,15 @@ public: // Views/copies/subset * @param N2 Number of columns * @param N3 Number of elements in the third dimension */ - inline void resize( size_t N1, size_t N2, size_t N3 ) { resize( ArraySize( N1, N2, N3 ) ); } + inline void resize(size_t N1, size_t N2, size_t N3) { + resize(ArraySize(N1, N2, N3)); + } /*! * Resize the Array * @param N Number of elements in each dimension */ - void resize( const ArraySize &N ); - + void resize(const ArraySize &N); /*! * Resize the given dimension of the array @@ -403,87 +375,83 @@ public: // Views/copies/subset * @param N Number of elements for the given dimension * @param value Value to initialize new elements */ - void resizeDim( int dim, size_t N, const TYPE &value ); - + void resizeDim(int dim, size_t N, const TYPE &value); /*! * Reshape the Array (total size of array will not change) * @param N Number of elements in each dimension */ - void reshape( const ArraySize &N ); - + void reshape(const ArraySize &N); /*! * Remove singleton dimensions. */ void squeeze(); - /*! * Reshape the Array so that the number of dimensions is the * max of ndim and the largest dim>1. * @param ndim Desired number of dimensions */ - inline void setNdim( int ndim ) { d_size.setNdim( ndim ); } - + inline void setNdim(int ndim) { d_size.setNdim(ndim); } /*! * Subset the Array * @param index Index to subset (imin,imax,jmin,jmax,kmin,kmax,...) */ - Array subset( const std::vector &index ) const; - + Array subset(const std::vector &index) const; /*! * Subset the Array * @param index Index to subset (ix:kx:jx,iy:ky:jy,...) */ - Array subset( const std::vector> &index ) const; - + Array subset(const std::vector> &index) const; /*! * Copy data from an array into a subset of this array * @param index Index of the subset (imin,imax,jmin,jmax,kmin,kmax,...) * @param subset The subset array to copy from */ - void copySubset( const std::vector &index, const Array &subset ); + void copySubset(const std::vector &index, const Array &subset); /*! * Copy data from an array into a subset of this array * @param index Index of the subset * @param subset The subset array to copy from */ - void copySubset( const std::vector> &index, const Array &subset ); + void copySubset(const std::vector> &index, + const Array &subset); /*! * Add data from an array into a subset of this array * @param index Index of the subset (imin,imax,jmin,jmax,kmin,kmax,...) * @param subset The subset array to add from */ - void addSubset( const std::vector &index, const Array &subset ); + void addSubset(const std::vector &index, const Array &subset); /*! * Add data from an array into a subset of this array * @param index Index of the subset * @param subset The subset array to add from */ - void addSubset( const std::vector> &index, const Array &subset ); - + void addSubset(const std::vector> &index, + const Array &subset); public: // Accessors /*! * Access the desired element * @param i The row index */ - ARRAY_ATTRIBUTE inline TYPE &operator()( size_t i ) { return d_data[d_size.index( i )]; } + ARRAY_ATTRIBUTE inline TYPE &operator()(size_t i) { + return d_data[d_size.index(i)]; + } /*! * Access the desired element * @param i The row index */ - ARRAY_ATTRIBUTE inline const TYPE &operator()( size_t i ) const - { - return d_data[d_size.index( i )]; + ARRAY_ATTRIBUTE inline const TYPE &operator()(size_t i) const { + return d_data[d_size.index(i)]; } /*! @@ -491,9 +459,8 @@ public: // Accessors * @param i The row index * @param j The column index */ - ARRAY_ATTRIBUTE inline TYPE &operator()( size_t i, size_t j ) - { - return d_data[d_size.index( i, j )]; + ARRAY_ATTRIBUTE inline TYPE &operator()(size_t i, size_t j) { + return d_data[d_size.index(i, j)]; } /*! @@ -501,9 +468,8 @@ public: // Accessors * @param i The row index * @param j The column index */ - ARRAY_ATTRIBUTE inline const TYPE &operator()( size_t i, size_t j ) const - { - return d_data[d_size.index( i, j )]; + ARRAY_ATTRIBUTE inline const TYPE &operator()(size_t i, size_t j) const { + return d_data[d_size.index(i, j)]; } /*! @@ -512,9 +478,8 @@ public: // Accessors * @param j The column index * @param k The third index */ - ARRAY_ATTRIBUTE inline TYPE &operator()( size_t i, size_t j, size_t k ) - { - return d_data[d_size.index( i, j, k )]; + ARRAY_ATTRIBUTE inline TYPE &operator()(size_t i, size_t j, size_t k) { + return d_data[d_size.index(i, j, k)]; } /*! @@ -523,9 +488,9 @@ public: // Accessors * @param j The column index * @param k The third index */ - ARRAY_ATTRIBUTE inline const TYPE &operator()( size_t i, size_t j, size_t k ) const - { - return d_data[d_size.index( i, j, k )]; + ARRAY_ATTRIBUTE inline const TYPE &operator()(size_t i, size_t j, + size_t k) const { + return d_data[d_size.index(i, j, k)]; } /*! @@ -535,9 +500,9 @@ public: // Accessors * @param i3 The third index * @param i4 The fourth index */ - ARRAY_ATTRIBUTE inline TYPE &operator()( size_t i1, size_t i2, size_t i3, size_t i4 ) - { - return d_data[d_size.index( i1, i2, i3, i4 )]; + ARRAY_ATTRIBUTE inline TYPE &operator()(size_t i1, size_t i2, size_t i3, + size_t i4) { + return d_data[d_size.index(i1, i2, i3, i4)]; } /*! @@ -547,10 +512,9 @@ public: // Accessors * @param i3 The third index * @param i4 The fourth index */ - ARRAY_ATTRIBUTE inline const TYPE & - operator()( size_t i1, size_t i2, size_t i3, size_t i4 ) const - { - return d_data[d_size.index( i1, i2, i3, i4 )]; + ARRAY_ATTRIBUTE inline const TYPE &operator()(size_t i1, size_t i2, + size_t i3, size_t i4) const { + return d_data[d_size.index(i1, i2, i3, i4)]; } /*! @@ -561,9 +525,9 @@ public: // Accessors * @param i4 The fourth index * @param i5 The fifth index */ - ARRAY_ATTRIBUTE inline TYPE &operator()( size_t i1, size_t i2, size_t i3, size_t i4, size_t i5 ) - { - return d_data[d_size.index( i1, i2, i3, i4, i5 )]; + ARRAY_ATTRIBUTE inline TYPE &operator()(size_t i1, size_t i2, size_t i3, + size_t i4, size_t i5) { + return d_data[d_size.index(i1, i2, i3, i4, i5)]; } /*! @@ -575,17 +539,15 @@ public: // Accessors * @param i5 The fifth index */ ARRAY_ATTRIBUTE inline const TYPE & - operator()( size_t i1, size_t i2, size_t i3, size_t i4, size_t i5 ) const - { - return d_data[d_size.index( i1, i2, i3, i4, i5 )]; + operator()(size_t i1, size_t i2, size_t i3, size_t i4, size_t i5) const { + return d_data[d_size.index(i1, i2, i3, i4, i5)]; } /*! * Access the desired element as a raw pointer * @param i The global index */ - ARRAY_ATTRIBUTE inline TYPE *ptr( size_t i ) - { + ARRAY_ATTRIBUTE inline TYPE *ptr(size_t i) { return i >= d_size.length() ? nullptr : &d_data[i]; } @@ -593,8 +555,7 @@ public: // Accessors * Access the desired element as a raw pointer * @param i The global index */ - ARRAY_ATTRIBUTE inline const TYPE *ptr( size_t i ) const - { + ARRAY_ATTRIBUTE inline const TYPE *ptr(size_t i) const { return i >= d_size.length() ? nullptr : &d_data[i]; } @@ -622,40 +583,40 @@ public: // Accessors //! Return the pointer to the raw data ARRAY_ATTRIBUTE inline const TYPE *data() const { return d_data; } - public: // Operator overloading //! Check if two matrices are equal // Equality means the dimensions and data have to be identical - bool operator==( const Array &rhs ) const; + bool operator==(const Array &rhs) const; //! Check if two matrices are not equal - inline bool operator!=( const Array &rhs ) const { return !this->operator==( rhs ); } + inline bool operator!=(const Array &rhs) const { + return !this->operator==(rhs); + } //! Add another array - Array &operator+=( const Array &rhs ); + Array &operator+=(const Array &rhs); //! Subtract another array - Array &operator-=( const Array &rhs ); + Array &operator-=(const Array &rhs); //! Add a scalar - Array &operator+=( const TYPE &rhs ); + Array &operator+=(const TYPE &rhs); //! Subtract a scalar - Array &operator-=( const TYPE &rhs ); - + Array &operator-=(const TYPE &rhs); public: // Math operations //! Concatenates the arrays along the dimension dim. - static Array cat( const std::vector &x, int dim = 0 ); + static Array cat(const std::vector &x, int dim = 0); //! Concatenates the arrays along the dimension dim. - static Array cat( const std::initializer_list &x, int dim = 0 ); + static Array cat(const std::initializer_list &x, int dim = 0); //! Concatenates the arrays along the dimension dim. - static Array cat( size_t N_array, const Array *x, int dim ); + static Array cat(size_t N_array, const Array *x, int dim); //! Concatenates a given array with the current array - void cat( const Array &x, int dim = 0 ); + void cat(const Array &x, int dim = 0); //! Initialize the array with random values (defined from the function table) //void rand(); @@ -676,46 +637,46 @@ public: // Math operations TYPE mean() const; //! Return the min of all elements in a given direction - Array min( int dir ) const; + Array min(int dir) const; //! Return the max of all elements in a given direction - Array max( int dir ) const; + Array max(int dir) const; //! Return the sum of all elements in a given direction - Array sum( int dir ) const; + Array sum(int dir) const; //! Return the smallest value - TYPE min( const std::vector &index ) const; + TYPE min(const std::vector &index) const; //! Return the largest value - TYPE max( const std::vector &index ) const; + TYPE max(const std::vector &index) const; //! Return the sum of all elements - TYPE sum( const std::vector &index ) const; + TYPE sum(const std::vector &index) const; //! Return the mean of all elements - TYPE mean( const std::vector &index ) const; + TYPE mean(const std::vector &index) const; //! Return the smallest value - TYPE min( const std::vector> &index ) const; + TYPE min(const std::vector> &index) const; //! Return the largest value - TYPE max( const std::vector> &index ) const; + TYPE max(const std::vector> &index) const; //! Return the sum of all elements - TYPE sum( const std::vector> &index ) const; + TYPE sum(const std::vector> &index) const; //! Return the mean of all elements - TYPE mean( const std::vector> &index ) const; + TYPE mean(const std::vector> &index) const; //! Find all elements that match the operator - std::vector find( const TYPE &value, - std::function compare ) const; - + std::vector + find(const TYPE &value, + std::function compare) const; //! Print an array - void - print( std::ostream &os, const std::string &name = "A", const std::string &prefix = "" ) const; + void print(std::ostream &os, const std::string &name = "A", + const std::string &prefix = "") const; //! Transpose an array Array reverseDim() const; @@ -728,7 +689,7 @@ public: // Math operations * shiftDim shifts the dimensions to the right and pads with singletons. * @param N Desired shift */ - Array shiftDim( int N ) const; + Array shiftDim(int N) const; /*! * @brief Permute array dimensions @@ -738,24 +699,25 @@ public: // Math operations * needed to access any particular element are rearranged as specified. * @param index Desired order of the subscripts */ - Array permute( const std::vector &index ) const; + Array permute(const std::vector &index) const; //! Replicate an array a given number of times in each direction - Array repmat( const std::vector &N ) const; + Array repmat(const std::vector &N) const; //! Coarsen an array using the given filter - Array coarsen( const Array &filter ) const; + Array coarsen(const Array &filter) const; //! Coarsen an array using the given filter - Array coarsen( const std::vector &ratio, - std::function filter ) const; + Array coarsen(const std::vector &ratio, + std::function filter) const; /*! * Perform a element-wise operation y = f(x) * @param[in] fun The function operation * @param[in] x The input array */ - static Array transform( std::function fun, const Array &x ); + static Array transform(std::function fun, + const Array &x); /*! * Perform a element-wise operation z = f(x,y) @@ -763,9 +725,8 @@ public: // Math operations * @param[in] x The first array * @param[in] y The second array */ - static Array transform( std::function fun, - const Array &x, - const Array &y ); + static Array transform(std::function fun, + const Array &x, const Array &y); /*! * axpby operation: this = alpha*x + beta*this @@ -773,19 +734,21 @@ public: // Math operations * @param[in] x x * @param[in] beta beta */ - void axpby( const TYPE &alpha, const Array &x, const TYPE &beta ); + void axpby(const TYPE &alpha, const Array &x, const TYPE &beta); /*! * Linear interpolation * @param[in] x Position as a decimal index */ - inline TYPE interp( const std::vector &x ) const { return interp( x.data() ); } + inline TYPE interp(const std::vector &x) const { + return interp(x.data()); + } /*! * Linear interpolation * @param[in] x Position as a decimal index */ - TYPE interp( const double *x ) const; + TYPE interp(const double *x) const; /** * \fn equals (Array & const rhs, TYPE tol ) @@ -794,7 +757,7 @@ public: // Math operations * \param[in] tol Tolerance of comparison * \return True iff \f$||\mathit{rhs} - x||_\infty < \mathit{tol}\f$ */ - bool equals( const Array &rhs, TYPE tol = 0.000001 ) const; + bool equals(const Array &rhs, TYPE tol = 0.000001) const; private: bool d_isCopyable; // Can the array be copied @@ -802,115 +765,106 @@ private: ArraySize d_size; // Size of each dimension TYPE *d_data; // Raw pointer to data in array std::shared_ptr d_ptr; // Shared pointer to data in array - void allocate( const ArraySize &N ); + void allocate(const ArraySize &N); private: - inline void checkSubsetIndex( const std::vector> &range ) const; - inline std::vector> convert( const std::vector &index ) const; - static inline void getSubsetArrays( const std::vector> &range, - std::array &first, - std::array &last, - std::array &inc, - std::array &N ); + inline void checkSubsetIndex(const std::vector> &range) const; + inline std::vector> + convert(const std::vector &index) const; + static inline void getSubsetArrays(const std::vector> &range, + std::array &first, + std::array &last, + std::array &inc, + std::array &N); }; - /******************************************************** * ostream operator * ********************************************************/ -inline std::ostream &operator<<( std::ostream &out, const ArraySize &s ) -{ +inline std::ostream &operator<<(std::ostream &out, const ArraySize &s) { out << "[" << s[0]; - for ( size_t i = 1; i < s.ndim(); i++ ) + for (size_t i = 1; i < s.ndim(); i++) out << "," << s[i]; out << "]"; return out; } - /******************************************************** * Math operations * ********************************************************/ -template -inline Array operator+( - const Array &a, const Array &b ) -{ +template +inline Array +operator+(const Array &a, + const Array &b) { Array c; - const auto &op = []( const TYPE &a, const TYPE &b ) { return a + b; }; - FUN::transform( op, a, b, c ); + const auto &op = [](const TYPE &a, const TYPE &b) { return a + b; }; + FUN::transform(op, a, b, c); return c; } -template -inline Array operator-( - const Array &a, const Array &b ) -{ +template +inline Array +operator-(const Array &a, + const Array &b) { Array c; - const auto &op = []( const TYPE &a, const TYPE &b ) { return a - b; }; - FUN::transform( op, a, b, c ); + const auto &op = [](const TYPE &a, const TYPE &b) { return a - b; }; + FUN::transform(op, a, b, c); return c; } -template -inline Array operator*( - const Array &a, const Array &b ) -{ +template +inline Array +operator*(const Array &a, + const Array &b) { Array c; - FUN::multiply( a, b, c ); + FUN::multiply(a, b, c); return c; } -template -inline Array operator*( - const Array &a, const std::vector &b ) -{ +template +inline Array +operator*(const Array &a, const std::vector &b) { Array b2, c; - b2.viewRaw( { b.size() }, const_cast( b.data() ) ); - FUN::multiply( a, b2, c ); + b2.viewRaw({b.size()}, const_cast(b.data())); + FUN::multiply(a, b2, c); return c; } -template -inline Array operator*( const TYPE &a, - const Array &b ) -{ +template +inline Array +operator*(const TYPE &a, const Array &b) { auto c = b; - c.scale( a ); + c.scale(a); return c; } -template -inline Array operator*( const Array &a, - const TYPE &b ) -{ +template +inline Array +operator*(const Array &a, const TYPE &b) { auto c = a; - c.scale( b ); + c.scale(b); return c; } - /******************************************************** * Copy array * ********************************************************/ -template -template -inline void Array::copy( const TYPE2 *data ) -{ - if ( std::is_same::value ) { - std::copy( data, data + d_size.length(), d_data ); +template +template +inline void Array::copy(const TYPE2 *data) { + if (std::is_same::value) { + std::copy(data, data + d_size.length(), d_data); } else { - for ( size_t i = 0; i < d_size.length(); i++ ) - d_data[i] = static_cast( data[i] ); + for (size_t i = 0; i < d_size.length(); i++) + d_data[i] = static_cast(data[i]); } } -template -template -inline void Array::copyTo( TYPE2 *data ) const -{ - if ( std::is_same::value ) { - std::copy( d_data, d_data + d_size.length(), data ); +template +template +inline void Array::copyTo(TYPE2 *data) const { + if (std::is_same::value) { + std::copy(d_data, d_data + d_size.length(), data); } else { - for ( size_t i = 0; i < d_size.length(); i++ ) - data[i] = static_cast( d_data[i] ); + for (size_t i = 0; i < d_size.length(); i++) + data[i] = static_cast(d_data[i]); } } - /******************************************************** * Convience typedefs * * Copy array * @@ -918,5 +872,4 @@ inline void Array::copyTo( TYPE2 *data ) const typedef Array DoubleArray; typedef Array IntArray; - #endif diff --git a/common/Array.hpp b/common/Array.hpp index 19d0c93d..971f8663 100644 --- a/common/Array.hpp +++ b/common/Array.hpp @@ -45,7 +45,6 @@ #include #include - /******************************************************** * External instantiations * ********************************************************/ @@ -61,7 +60,6 @@ extern template class Array; extern template class Array; extern template class Array; - /******************************************************** * Macros to help instantiate functions * ********************************************************/ @@ -90,285 +88,267 @@ extern template class Array; template Array &Array::operator=( Array && ); // clang-format on - /******************************************************** * Constructors * ********************************************************/ -template +template Array::Array() - : d_isCopyable( true ), d_isFixedSize( false ), d_data( nullptr ) -{ + : d_isCopyable(true), d_isFixedSize(false), d_data(nullptr) {} +template +Array::Array(const ArraySize &N) + : d_isCopyable(true), d_isFixedSize(false) { + allocate(N); } -template -Array::Array( const ArraySize &N ) - : d_isCopyable( true ), d_isFixedSize( false ) -{ - allocate( N ); +template +Array::Array(size_t N) + : d_isCopyable(true), d_isFixedSize(false) { + allocate(ArraySize(N)); } -template -Array::Array( size_t N ) : d_isCopyable( true ), d_isFixedSize( false ) -{ - allocate( ArraySize( N ) ); +template +Array::Array(size_t N_rows, size_t N_cols) + : d_isCopyable(true), d_isFixedSize(false) { + allocate(ArraySize(N_rows, N_cols)); } -template -Array::Array( size_t N_rows, size_t N_cols ) - : d_isCopyable( true ), d_isFixedSize( false ) -{ - allocate( ArraySize( N_rows, N_cols ) ); +template +Array::Array(size_t N1, size_t N2, size_t N3) + : d_isCopyable(true), d_isFixedSize(false) { + allocate(ArraySize(N1, N2, N3)); } -template -Array::Array( size_t N1, size_t N2, size_t N3 ) - : d_isCopyable( true ), d_isFixedSize( false ) -{ - allocate( ArraySize( N1, N2, N3 ) ); +template +Array::Array(size_t N1, size_t N2, size_t N3, size_t N4) + : d_isCopyable(true), d_isFixedSize(false) { + allocate(ArraySize(N1, N2, N3, N4)); } -template -Array::Array( size_t N1, size_t N2, size_t N3, size_t N4 ) - : d_isCopyable( true ), d_isFixedSize( false ) -{ - allocate( ArraySize( N1, N2, N3, N4 ) ); +template +Array::Array(size_t N1, size_t N2, size_t N3, size_t N4, + size_t N5) + : d_isCopyable(true), d_isFixedSize(false) { + allocate(ArraySize(N1, N2, N3, N4, N5)); } -template -Array::Array( size_t N1, size_t N2, size_t N3, size_t N4, size_t N5 ) - : d_isCopyable( true ), d_isFixedSize( false ) -{ - allocate( ArraySize( N1, N2, N3, N4, N5 ) ); +template +Array::Array(const std::vector &N, + const TYPE *data) + : d_isCopyable(true), d_isFixedSize(false) { + allocate(N); + if (data) + copy(data); } -template -Array::Array( const std::vector &N, const TYPE *data ) - : d_isCopyable( true ), d_isFixedSize( false ) -{ - allocate( N ); - if ( data ) - copy( data ); -} -template -Array::Array( std::string str ) : d_isCopyable( true ), d_isFixedSize( false ) -{ - allocate( 0 ); - if ( (int) std::count( str.begin(), str.end(), ' ' ) == (int) str.length() ) { +template +Array::Array(std::string str) + : d_isCopyable(true), d_isFixedSize(false) { + allocate(0); + if ((int)std::count(str.begin(), str.end(), ' ') == (int)str.length()) { // Empty string return; } // Remove unnecessary whitespace - while ( str.front() == ' ' ) - str.erase( 0, 1 ); - while ( str.back() == ' ' ) - str.resize( str.length() - 1 ); - while ( str.find( ',' ) != std::string::npos ) - str[str.find( ',' )] = ' '; - while ( str.find( " " ) != std::string::npos ) - str.replace( str.find( " " ), 2, " " ); + while (str.front() == ' ') + str.erase(0, 1); + while (str.back() == ' ') + str.resize(str.length() - 1); + while (str.find(',') != std::string::npos) + str[str.find(',')] = ' '; + while (str.find(" ") != std::string::npos) + str.replace(str.find(" "), 2, " "); // Check if the string is of the format [...] - if ( str.front() == '[' && str.back() == ']' ) { - str.erase( 0, 1 ); - str.resize( str.length() - 1 ); - *this = Array( str ); + if (str.front() == '[' && str.back() == ']') { + str.erase(0, 1); + str.resize(str.length() - 1); + *this = Array(str); return; } // Check if we are dealing with a 2D array - if ( str.find( ';' ) != std::string::npos ) { + if (str.find(';') != std::string::npos) { size_t i1 = 0; - size_t i2 = str.find( ';' ); + size_t i2 = str.find(';'); std::vector x; - while ( i2 > i1 ) { - auto tmp = str.substr( i1, i2 - i1 ); - x.emplace_back( Array( tmp ) ); + while (i2 > i1) { + auto tmp = str.substr(i1, i2 - i1); + x.emplace_back(Array(tmp)); i1 = i2 + 1; - i2 = str.find( ';', i1 + 1 ); - if ( i2 == std::string::npos ) + i2 = str.find(';', i1 + 1); + if (i2 == std::string::npos) i2 = str.length(); } - for ( auto &y : x ) - y.reshape( { 1, y.length() } ); - *this = cat( x, 0 ); + for (auto &y : x) + y.reshape({1, y.length()}); + *this = cat(x, 0); return; } // Begin parsing the array constructor size_t i1 = 0; - size_t i2 = str.find( ' ' ); + size_t i2 = str.find(' '); std::vector data; - while ( i2 > i1 ) { - auto tmp = str.substr( i1, i2 - i1 ); - int type = std::count( tmp.begin(), tmp.end(), ':' ); - if ( type == 0 ) { - data.push_back( std::stod( tmp ) ); - } else if ( type == 1 ) { - size_t k = tmp.find( ':' ); - TYPE x1 = std::stod( tmp.substr( 0, k ) ); - TYPE x2 = std::stod( tmp.substr( k + 1 ) ); - double tol = 1e-8 * ( x2 - x1 ); - for ( TYPE x = x1; x <= x2 + tol; x += 1 ) - data.push_back( x ); - } else if ( type == 2 ) { - size_t k1 = tmp.find( ':' ); - size_t k2 = tmp.find( ':', k1 + 1 ); - TYPE x1 = std::stod( tmp.substr( 0, k1 ) ); - TYPE dx = std::stod( tmp.substr( k1 + 1, k2 - k1 - 1 ) ); - TYPE x2 = std::stod( tmp.substr( k2 + 1 ) ); - double tol = 1e-8 * ( x2 - x1 ); - for ( TYPE x = x1; x <= x2 + tol; x += dx ) - data.push_back( x ); + while (i2 > i1) { + auto tmp = str.substr(i1, i2 - i1); + int type = std::count(tmp.begin(), tmp.end(), ':'); + if (type == 0) { + data.push_back(std::stod(tmp)); + } else if (type == 1) { + size_t k = tmp.find(':'); + TYPE x1 = std::stod(tmp.substr(0, k)); + TYPE x2 = std::stod(tmp.substr(k + 1)); + double tol = 1e-8 * (x2 - x1); + for (TYPE x = x1; x <= x2 + tol; x += 1) + data.push_back(x); + } else if (type == 2) { + size_t k1 = tmp.find(':'); + size_t k2 = tmp.find(':', k1 + 1); + TYPE x1 = std::stod(tmp.substr(0, k1)); + TYPE dx = std::stod(tmp.substr(k1 + 1, k2 - k1 - 1)); + TYPE x2 = std::stod(tmp.substr(k2 + 1)); + double tol = 1e-8 * (x2 - x1); + for (TYPE x = x1; x <= x2 + tol; x += dx) + data.push_back(x); } else { - throw std::logic_error( "Failed to parse string constructor: " + str ); + throw std::logic_error("Failed to parse string constructor: " + + str); } i1 = i2; - i2 = str.find( ' ', i1 + 1 ); - if ( i2 == std::string::npos ) + i2 = str.find(' ', i1 + 1); + if (i2 == std::string::npos) i2 = str.length(); } - allocate( data.size() ); - if ( std::is_same::value ) { - for ( size_t i = 0; i < data.size(); i++ ) + allocate(data.size()); + if (std::is_same::value) { + for (size_t i = 0; i < data.size(); i++) d_data[i] = data[i]; } else { - copy( data.data() ); + copy(data.data()); } } -template -Array::Array( std::initializer_list x ) - : d_isCopyable( true ), d_isFixedSize( false ) -{ - allocate( { x.size() } ); +template +Array::Array(std::initializer_list x) + : d_isCopyable(true), d_isFixedSize(false) { + allocate({x.size()}); auto it = x.begin(); - for ( size_t i = 0; i < x.size(); ++i, ++it ) + for (size_t i = 0; i < x.size(); ++i, ++it) d_data[i] = *it; } -template -Array::Array( std::initializer_list> x ) - : d_isCopyable( true ), d_isFixedSize( false ) -{ +template +Array::Array( + std::initializer_list> x) + : d_isCopyable(true), d_isFixedSize(false) { size_t Nx = x.size(); size_t Ny = 0; - for ( const auto y : x ) - Ny = std::max( Ny, y.size() ); - allocate( { Nx, Ny } ); + for (const auto y : x) + Ny = std::max(Ny, y.size()); + allocate({Nx, Ny}); auto itx = x.begin(); - for ( size_t i = 0; i < x.size(); ++i, ++itx ) { + for (size_t i = 0; i < x.size(); ++i, ++itx) { auto ity = itx->begin(); - for ( size_t j = 0; j < itx->size(); ++j, ++ity ) { + for (size_t j = 0; j < itx->size(); ++j, ++ity) { d_data[i + j * Nx] = *ity; } } } -template -void Array::allocate( const ArraySize &N ) -{ - if ( d_isFixedSize ) - throw std::logic_error( "Array cannot be resized" ); - d_size = N; +template +void Array::allocate(const ArraySize &N) { + if (d_isFixedSize) + throw std::logic_error("Array cannot be resized"); + d_size = N; auto length = d_size.length(); - d_data = nullptr; - if ( length > 0 ) { + d_data = nullptr; + if (length > 0) { try { d_data = new TYPE[length]; - } catch ( ... ) { - throw std::logic_error( "Failed to allocate array" ); + } catch (...) { + throw std::logic_error("Failed to allocate array"); } } - d_ptr.reset( d_data, []( TYPE *p ) { delete[] p; } ); + d_ptr.reset(d_data, [](TYPE *p) { delete[] p; }); } -template -Array::Array( const Array &rhs ) - : d_isCopyable( true ), d_isFixedSize( false ) -{ - if ( !rhs.d_isCopyable ) - throw std::logic_error( "Array cannot be copied" ); - allocate( rhs.size() ); - copy( rhs.d_data ); +template +Array::Array(const Array &rhs) + : d_isCopyable(true), d_isFixedSize(false) { + if (!rhs.d_isCopyable) + throw std::logic_error("Array cannot be copied"); + allocate(rhs.size()); + copy(rhs.d_data); } -template -Array::Array( Array &&rhs ) - : d_isCopyable( rhs.d_isCopyable ), - d_isFixedSize( rhs.d_isFixedSize ), - d_size( rhs.d_size ), - d_data( rhs.d_data ), - d_ptr( std::move( rhs.d_ptr ) ) -{ +template +Array::Array(Array &&rhs) + : d_isCopyable(rhs.d_isCopyable), d_isFixedSize(rhs.d_isFixedSize), + d_size(rhs.d_size), d_data(rhs.d_data), d_ptr(std::move(rhs.d_ptr)) { rhs.d_size = ArraySize(); rhs.d_data = nullptr; - rhs.d_ptr = nullptr; + rhs.d_ptr = nullptr; } -template -Array &Array::operator=( const Array &rhs ) -{ - if ( this == &rhs ) +template +Array &Array:: +operator=(const Array &rhs) { + if (this == &rhs) return *this; - if ( !rhs.d_isCopyable ) - throw std::logic_error( "Array cannot be copied" ); - allocate( rhs.size() ); - copy( rhs.d_data ); + if (!rhs.d_isCopyable) + throw std::logic_error("Array cannot be copied"); + allocate(rhs.size()); + copy(rhs.d_data); return *this; } -template -Array &Array::operator=( Array &&rhs ) -{ - if ( this == &rhs ) +template +Array &Array:: +operator=(Array &&rhs) { + if (this == &rhs) return *this; - d_isCopyable = rhs.d_isCopyable; + d_isCopyable = rhs.d_isCopyable; d_isFixedSize = rhs.d_isFixedSize; - d_size = rhs.d_size; - d_data = rhs.d_data; - d_ptr = std::move( rhs.d_ptr ); - rhs.d_size = ArraySize(); - rhs.d_data = nullptr; - rhs.d_ptr = nullptr; + d_size = rhs.d_size; + d_data = rhs.d_data; + d_ptr = std::move(rhs.d_ptr); + rhs.d_size = ArraySize(); + rhs.d_data = nullptr; + rhs.d_ptr = nullptr; return *this; } -template -Array &Array::operator=( const std::vector &rhs ) -{ - allocate( ArraySize( rhs.size() ) ); - for ( size_t i = 0; i < rhs.size(); i++ ) +template +Array &Array:: +operator=(const std::vector &rhs) { + allocate(ArraySize(rhs.size())); + for (size_t i = 0; i < rhs.size(); i++) d_data[i] = rhs[i]; return *this; } -template -Array::~Array() -{ -} -template -void Array::clear() -{ - d_isCopyable = true; +template +Array::~Array() {} +template +void Array::clear() { + d_isCopyable = true; d_isFixedSize = false; - d_size = ArraySize(); + d_size = ArraySize(); d_ptr.reset(); d_data = nullptr; } - /******************************************************** * Copy/move values from one array to another (resize) * ********************************************************/ -template -static inline void moveValues( const ArraySize &N1, const ArraySize &N2, TYPE *data1, TYPE *data2 ) -{ - for ( size_t i5 = 0; i5 < std::min( N1[4], N2[4] ); i5++ ) { - for ( size_t i4 = 0; i4 < std::min( N1[3], N2[3] ); i4++ ) { - for ( size_t i3 = 0; i3 < std::min( N1[2], N2[2] ); i3++ ) { - for ( size_t i2 = 0; i2 < std::min( N1[1], N2[1] ); i2++ ) { - for ( size_t i1 = 0; i1 < std::min( N1[0], N2[0] ); i1++ ) { - size_t index1 = N1.index( i1, i2, i3, i4, i5 ); - size_t index2 = N2.index( i1, i2, i3, i4, i5 ); - data2[index2] = std::move( data1[index1] ); +template +static inline void moveValues(const ArraySize &N1, const ArraySize &N2, + TYPE *data1, TYPE *data2) { + for (size_t i5 = 0; i5 < std::min(N1[4], N2[4]); i5++) { + for (size_t i4 = 0; i4 < std::min(N1[3], N2[3]); i4++) { + for (size_t i3 = 0; i3 < std::min(N1[2], N2[2]); i3++) { + for (size_t i2 = 0; i2 < std::min(N1[1], N2[1]); i2++) { + for (size_t i1 = 0; i1 < std::min(N1[0], N2[0]); i1++) { + size_t index1 = N1.index(i1, i2, i3, i4, i5); + size_t index2 = N2.index(i1, i2, i3, i4, i5); + data2[index2] = std::move(data1[index1]); } } } } } } -template -static inline void -copyValues( const ArraySize &N1, const ArraySize &N2, const TYPE *data1, TYPE *data2 ) -{ - for ( size_t i5 = 0; i5 < std::min( N1[4], N2[4] ); i5++ ) { - for ( size_t i4 = 0; i4 < std::min( N1[3], N2[3] ); i4++ ) { - for ( size_t i3 = 0; i3 < std::min( N1[2], N2[2] ); i3++ ) { - for ( size_t i2 = 0; i2 < std::min( N1[1], N2[1] ); i2++ ) { - for ( size_t i1 = 0; i1 < std::min( N1[0], N2[0] ); i1++ ) { - size_t index1 = N1.index( i1, i2, i3, i4, i5 ); - size_t index2 = N2.index( i1, i2, i3, i4, i5 ); +template +static inline void copyValues(const ArraySize &N1, const ArraySize &N2, + const TYPE *data1, TYPE *data2) { + for (size_t i5 = 0; i5 < std::min(N1[4], N2[4]); i5++) { + for (size_t i4 = 0; i4 < std::min(N1[3], N2[3]); i4++) { + for (size_t i3 = 0; i3 < std::min(N1[2], N2[2]); i3++) { + for (size_t i2 = 0; i2 < std::min(N1[1], N2[1]); i2++) { + for (size_t i1 = 0; i1 < std::min(N1[0], N2[0]); i1++) { + size_t index1 = N1.index(i1, i2, i3, i4, i5); + size_t index2 = N2.index(i1, i2, i3, i4, i5); data2[index2] = data1[index1]; } } @@ -377,145 +357,142 @@ copyValues( const ArraySize &N1, const ArraySize &N2, const TYPE *data1, TYPE *d } } - /******************************************************** * Resize the array * ********************************************************/ -template -void Array::resize( const ArraySize &N ) -{ +template +void Array::resize(const ArraySize &N) { // Check if the array actually changed size bool equal = true; - for ( size_t i = 0; i < ArraySize::maxDim(); i++ ) + for (size_t i = 0; i < ArraySize::maxDim(); i++) equal = equal && N[i] == d_size[i]; - if ( equal ) { + if (equal) { d_size = N; return; } // Store the old data - auto N0 = d_size; + auto N0 = d_size; auto data0 = d_ptr; // Allocate new data - allocate( N ); + allocate(N); // Copy the old values - if ( N.length() > 0 && d_size.length() > 0 ) { - if ( data0.use_count() <= 1 ) { + if (N.length() > 0 && d_size.length() > 0) { + if (data0.use_count() <= 1) { // We own the data, use std:move - moveValues( N0, N, data0.get(), d_data ); - } else if ( std::is_copy_constructible::value ) { + moveValues(N0, N, data0.get(), d_data); + } else if (std::is_copy_constructible::value) { // We do not own the data, copy - copyValues( N0, N, data0.get(), d_data ); + copyValues(N0, N, data0.get(), d_data); } else { - throw std::logic_error( "No copy constructor" ); + throw std::logic_error("No copy constructor"); } } } -template -void Array::resizeDim( int dim, size_t N, const TYPE &value ) -{ - if ( dim < 0 || dim > d_size.ndim() ) - throw std::out_of_range( "Invalid dimension" ); +template +void Array::resizeDim(int dim, size_t N, + const TYPE &value) { + if (dim < 0 || dim > d_size.ndim()) + throw std::out_of_range("Invalid dimension"); size_t N0 = d_size[dim]; auto size = d_size; - size.resize( dim, N ); - resize( size ); + size.resize(dim, N); + resize(size); size_t n1 = 1, n2 = 1; - for ( int d = 0; d < dim; d++ ) + for (int d = 0; d < dim; d++) n1 *= size[d]; - for ( size_t d = dim + 1; d < size.ndim(); d++ ) + for (size_t d = dim + 1; d < size.ndim(); d++) n2 *= size[d]; - for ( size_t k = 0; k < n2; k++ ) { - for ( size_t j = N0; j < N; j++ ) { - for ( size_t i = 0; i < n1; i++ ) { + for (size_t k = 0; k < n2; k++) { + for (size_t j = N0; j < N; j++) { + for (size_t i = 0; i < n1; i++) { d_data[i + j * n1 + k * n1 * N] = value; } } } } - /******************************************************** * Reshape/squeeze the array * ********************************************************/ -template -void Array::reshape( const ArraySize &N ) -{ - if ( N.length() != d_size.length() ) - throw std::logic_error( "reshape is not allowed to change the array size" ); +template +void Array::reshape(const ArraySize &N) { + if (N.length() != d_size.length()) + throw std::logic_error( + "reshape is not allowed to change the array size"); d_size = N; } -template -void Array::squeeze() -{ +template +void Array::squeeze() { d_size.squeeze(); } - /******************************************************** * Shift/permute the array * ********************************************************/ -template -Array Array::shiftDim( int N ) const -{ - if ( N > 0 ) +template +Array Array::shiftDim(int N) const { + if (N > 0) N = N % d_size.ndim(); - if ( N == 0 ) { + if (N == 0) { // No shift required return *this; - } else if ( N > 0 ) { + } else if (N > 0) { // Shift to the left and wrap - std::vector index( d_size.ndim() ); + std::vector index(d_size.ndim()); size_t i = 0; - for ( size_t j=N; j -Array Array::permute( const std::vector &index ) const -{ +template +Array +Array::permute(const std::vector &index) const { // Check the permutation - ASSERT( (int) index.size() == ndim() ); - for ( int i=0; i < ndim(); i++) { - ASSERT( index[i] < ndim() ); - for ( int j=0; j < i; j++) - ASSERT( index[i] != index[j] ); + ASSERT((int)index.size() == ndim()); + for (int i = 0; i < ndim(); i++) { + ASSERT(index[i] < ndim()); + for (int j = 0; j < i; j++) + ASSERT(index[i] != index[j]); } // Create the new Array - size_t dims[5] = { 1u, 1u, 1u, 1u, 1u }; - for ( size_t i=0; ilength() ); + Array y(ArraySize(ndim(), dims)); + y.fill(-1); + ASSERT(y.length() == this->length()); // Fill the data - size_t N[5] = { 1u, 1u, 1u, 1u, 1u }; - for ( int i=0; i < ndim(); i++) { - std::array ijk = { 0, 0, 0, 0, 0 }; + size_t N[5] = {1u, 1u, 1u, 1u, 1u}; + for (int i = 0; i < ndim(); i++) { + std::array ijk = {0, 0, 0, 0, 0}; ijk[index[i]] = 1; - N[i] = d_size.index( ijk ); + N[i] = d_size.index(ijk); } - size_t tmp = ( dims[0] - 1 ) * N[0] + ( dims[1] - 1 ) * N[1] + ( dims[2] - 1 ) * N[2] + ( dims[3] - 1 ) * N[3] + ( dims[4] - 1 ) * N[4] + 1; - ASSERT( tmp == length() ); - for ( size_t i4 = 0; i4 < dims[4]; i4++ ) { - for ( size_t i3 = 0; i3 < dims[3]; i3++ ) { - for ( size_t i2 = 0; i2 < dims[2]; i2++ ) { - for ( size_t i1 = 0; i1 < dims[1]; i1++ ) { - for ( size_t i0 = 0; i0 < dims[0]; i0++ ) { - size_t index2 = i0 * N[0] + i1 * N[1] + i2 * N[2] + i3 * N[3] + i4 * N[4]; - y( i0, i1, i2, i3, i4 ) = d_data[index2]; + size_t tmp = (dims[0] - 1) * N[0] + (dims[1] - 1) * N[1] + + (dims[2] - 1) * N[2] + (dims[3] - 1) * N[3] + + (dims[4] - 1) * N[4] + 1; + ASSERT(tmp == length()); + for (size_t i4 = 0; i4 < dims[4]; i4++) { + for (size_t i3 = 0; i3 < dims[3]; i3++) { + for (size_t i2 = 0; i2 < dims[2]; i2++) { + for (size_t i1 = 0; i1 < dims[1]; i1++) { + for (size_t i0 = 0; i0 < dims[0]; i0++) { + size_t index2 = i0 * N[0] + i1 * N[1] + i2 * N[2] + + i3 * N[3] + i4 * N[4]; + y(i0, i1, i2, i3, i4) = d_data[index2]; } } } @@ -524,71 +501,67 @@ Array Array::permute( const std::vec return y; } - /******************************************************** * Subset the array * ********************************************************/ // Helper function to check subset indices -template -inline void -Array::checkSubsetIndex( const std::vector> &range ) const -{ - bool test = (int) range.size() == d_size.ndim(); - for ( size_t d = 0; d < range.size(); d++ ) +template +inline void Array::checkSubsetIndex( + const std::vector> &range) const { + bool test = (int)range.size() == d_size.ndim(); + for (size_t d = 0; d < range.size(); d++) test = test && range[d].j <= d_size[d]; - if ( !test ) - throw std::logic_error( "indices for subset are invalid" ); + if (!test) + throw std::logic_error("indices for subset are invalid"); } -template +template std::vector> -Array::convert( const std::vector &index ) const -{ - std::vector> range( d_size.ndim() ); - if ( index.size() % 2 != 0 || static_cast( index.size() / 2 ) < d_size.ndim() ) - throw std::logic_error( "indices for subset are invalid" ); - for ( int d = 0; d < d_size.ndim(); d++ ) - range[d] = Range( index[2 * d + 0], index[2 * d + 1] ); +Array::convert(const std::vector &index) const { + std::vector> range(d_size.ndim()); + if (index.size() % 2 != 0 || + static_cast(index.size() / 2) < d_size.ndim()) + throw std::logic_error("indices for subset are invalid"); + for (int d = 0; d < d_size.ndim(); d++) + range[d] = Range(index[2 * d + 0], index[2 * d + 1]); return range; } // Helper function to return dimensions for the subset array -template -void Array::getSubsetArrays( const std::vector> &index, - std::array &first, - std::array &last, - std::array &inc, - std::array &N ) -{ - first.fill( 0 ); - last.fill( 0 ); - inc.fill( 1 ); - N.fill( 1 ); +template +void Array::getSubsetArrays( + const std::vector> &index, std::array &first, + std::array &last, std::array &inc, + std::array &N) { + first.fill(0); + last.fill(0); + inc.fill(1); + N.fill(1); size_t ndim = index.size(); - for ( size_t d = 0; d < ndim; d++ ) { + for (size_t d = 0; d < ndim; d++) { first[d] = index[d].i; - last[d] = index[d].j; - inc[d] = index[d].k; - N[d] = ( last[d] - first[d] + inc[d] ) / inc[d]; + last[d] = index[d].j; + inc[d] = index[d].k; + N[d] = (last[d] - first[d] + inc[d]) / inc[d]; } } -template -Array -Array::subset( const std::vector> &index ) const -{ +template +Array Array::subset( + const std::vector> &index) const { // Get the subset indicies - checkSubsetIndex( index ); + checkSubsetIndex(index); std::array first, last, inc, N1; - getSubsetArrays( index, first, last, inc, N1 ); - ArraySize S1( d_size.ndim(), N1.data() ); + getSubsetArrays(index, first, last, inc, N1); + ArraySize S1(d_size.ndim(), N1.data()); // Create the new array - Array subset_array( S1 ); + Array subset_array(S1); // Fill the new array TYPE *subset_data = subset_array.data(); - for ( size_t i4 = first[4], k1 = 0; i4 <= last[4]; i4 += inc[4] ) { - for ( size_t i3 = first[3]; i3 <= last[3]; i3 += inc[3] ) { - for ( size_t i2 = first[2]; i2 <= last[2]; i2 += inc[2] ) { - for ( size_t i1 = first[1]; i1 <= last[1]; i1 += inc[1] ) { - for ( size_t i0 = first[0]; i0 <= last[0]; i0 += inc[0], k1++ ) { - size_t k2 = d_size.index( i0, i1, i2, i3, i4 ); + for (size_t i4 = first[4], k1 = 0; i4 <= last[4]; i4 += inc[4]) { + for (size_t i3 = first[3]; i3 <= last[3]; i3 += inc[3]) { + for (size_t i2 = first[2]; i2 <= last[2]; i2 += inc[2]) { + for (size_t i1 = first[1]; i1 <= last[1]; i1 += inc[1]) { + for (size_t i0 = first[0]; i0 <= last[0]; + i0 += inc[0], k1++) { + size_t k2 = d_size.index(i0, i1, i2, i3, i4); subset_data[k1] = d_data[k2]; } } @@ -597,29 +570,29 @@ Array::subset( const std::vector> &index ) c } return subset_array; } -template +template Array -Array::subset( const std::vector &index ) const -{ - auto range = convert( index ); - return subset( range ); +Array::subset(const std::vector &index) const { + auto range = convert(index); + return subset(range); } -template -void Array::copySubset( const std::vector> &index, - const Array &subset ) -{ +template +void Array::copySubset( + const std::vector> &index, + const Array &subset) { // Get the subset indices - checkSubsetIndex( index ); + checkSubsetIndex(index); std::array first, last, inc, N1; - getSubsetArrays( index, first, last, inc, N1 ); + getSubsetArrays(index, first, last, inc, N1); // Copy the sub-array const TYPE *src_data = subset.data(); - for ( size_t i4 = first[4], k1 = 0; i4 <= last[4]; i4 += inc[4] ) { - for ( size_t i3 = first[3]; i3 <= last[3]; i3 += inc[3] ) { - for ( size_t i2 = first[2]; i2 <= last[2]; i2 += inc[2] ) { - for ( size_t i1 = first[1]; i1 <= last[1]; i1 += inc[1] ) { - for ( size_t i0 = first[0]; i0 <= last[0]; i0 += inc[0], k1++ ) { - size_t k2 = d_size.index( i0, i1, i2, i3, i4 ); + for (size_t i4 = first[4], k1 = 0; i4 <= last[4]; i4 += inc[4]) { + for (size_t i3 = first[3]; i3 <= last[3]; i3 += inc[3]) { + for (size_t i2 = first[2]; i2 <= last[2]; i2 += inc[2]) { + for (size_t i1 = first[1]; i1 <= last[1]; i1 += inc[1]) { + for (size_t i0 = first[0]; i0 <= last[0]; + i0 += inc[0], k1++) { + size_t k2 = d_size.index(i0, i1, i2, i3, i4); d_data[k2] = src_data[k1]; } } @@ -627,21 +600,22 @@ void Array::copySubset( const std::vector> & } } } -template -void Array::addSubset( const std::vector> &index, - const Array &subset ) -{ +template +void Array::addSubset( + const std::vector> &index, + const Array &subset) { // Get the subset indices - checkSubsetIndex( index ); + checkSubsetIndex(index); std::array first, last, inc, N1; - getSubsetArrays( index, first, last, inc, N1 ); + getSubsetArrays(index, first, last, inc, N1); // add the sub-array - for ( size_t i4 = first[4], k1 = 0; i4 <= last[4]; i4 += inc[4] ) { - for ( size_t i3 = first[3]; i3 <= last[3]; i3 += inc[3] ) { - for ( size_t i2 = first[2]; i2 <= last[2]; i2 += inc[2] ) { - for ( size_t i1 = first[1]; i1 <= last[1]; i1 += inc[1] ) { - for ( size_t i0 = first[0]; i0 <= last[0]; i0 += inc[0], k1++ ) { - size_t k2 = d_size.index( i0, i1, i2, i3, i4 ); + for (size_t i4 = first[4], k1 = 0; i4 <= last[4]; i4 += inc[4]) { + for (size_t i3 = first[3]; i3 <= last[3]; i3 += inc[3]) { + for (size_t i2 = first[2]; i2 <= last[2]; i2 += inc[2]) { + for (size_t i1 = first[1]; i1 <= last[1]; i1 += inc[1]) { + for (size_t i0 = first[0]; i0 <= last[0]; + i0 += inc[0], k1++) { + size_t k2 = d_size.index(i0, i1, i2, i3, i4); d_data[k2] += subset.d_data[k1]; } } @@ -649,151 +623,140 @@ void Array::addSubset( const std::vector> &i } } } -template -void Array::copySubset( const std::vector &index, - const Array &subset ) -{ - auto range = convert( index ); - copySubset( range, subset ); +template +void Array::copySubset( + const std::vector &index, + const Array &subset) { + auto range = convert(index); + copySubset(range, subset); } -template -void Array::addSubset( const std::vector &index, - const Array &subset ) -{ - auto range = convert( index ); - addSubset( range, subset ); +template +void Array::addSubset( + const std::vector &index, + const Array &subset) { + auto range = convert(index); + addSubset(range, subset); } - /******************************************************** * Operator overloading * ********************************************************/ -template -bool Array::operator==( const Array &rhs ) const -{ - if ( this == &rhs ) +template +bool Array::operator==(const Array &rhs) const { + if (this == &rhs) return true; - if ( d_size != rhs.d_size ) + if (d_size != rhs.d_size) return false; bool match = true; - for ( size_t i = 0; i < d_size.length(); i++ ) + for (size_t i = 0; i < d_size.length(); i++) match = match && d_data[i] == rhs.d_data[i]; return match; } - /******************************************************** * Get a view of an C array * ********************************************************/ -template +template std::unique_ptr> -Array::view( const ArraySize &N, std::shared_ptr data ) -{ - auto array = std::make_unique>(); +Array::view(const ArraySize &N, + std::shared_ptr data) { + auto array = std::make_unique>(); array->d_size = N; - array->d_ptr = data; + array->d_ptr = data; array->d_data = array->d_ptr.get(); return array; } -template +template std::unique_ptr> -Array::constView( const ArraySize &N, - std::shared_ptr const &data ) -{ - auto array = std::make_unique>(); +Array::constView( + const ArraySize &N, std::shared_ptr const &data) { + auto array = std::make_unique>(); array->d_size = N; - array->d_ptr = std::const_pointer_cast( data ); + array->d_ptr = std::const_pointer_cast(data); array->d_data = array->d_ptr.get(); return array; } -template -void Array::view2( Array &src ) -{ - view2( src.size(), src.getPtr() ); +template +void Array::view2(Array &src) { + view2(src.size(), src.getPtr()); d_data = src.d_data; } -template -void Array::view2( const ArraySize &N, std::shared_ptr data ) -{ +template +void Array::view2(const ArraySize &N, + std::shared_ptr data) { d_size = N; - d_ptr = data; + d_ptr = data; d_data = d_ptr.get(); } -template -void Array::viewRaw( const ArraySize &N, - TYPE *data, - bool isCopyable, - bool isFixedSize ) -{ - d_isCopyable = isCopyable; +template +void Array::viewRaw(const ArraySize &N, TYPE *data, + bool isCopyable, bool isFixedSize) { + d_isCopyable = isCopyable; d_isFixedSize = isFixedSize; d_ptr.reset(); d_size = N; d_data = data; } - /******************************************************** * Basic functions * ********************************************************/ -template -void Array::swap( Array &other ) -{ +template +void Array::swap(Array &other) { // check that dimensions match - if ( d_size != other.d_size ) - throw std::logic_error( "length of arrays do not match" ); + if (d_size != other.d_size) + throw std::logic_error("length of arrays do not match"); // swap the data - std::swap( d_data, other.d_data ); - std::swap( d_ptr, other.d_ptr ); + std::swap(d_data, other.d_data); + std::swap(d_ptr, other.d_ptr); } -template -void Array::pow( const Array &baseArray, - const TYPE &exp ) -{ +template +void Array::pow( + const Array &baseArray, const TYPE &exp) { // not insisting on the shapes being the same // but insisting on the total size being the same - if ( d_size.length() != baseArray.length() ) - throw std::logic_error( "length of arrays do not match" ); + if (d_size.length() != baseArray.length()) + throw std::logic_error("length of arrays do not match"); const auto base_data = baseArray.data(); - for ( size_t i = 0; i < d_size.length(); i++ ) - d_data[i] = std::pow( base_data[i], exp ); + for (size_t i = 0; i < d_size.length(); i++) + d_data[i] = std::pow(base_data[i], exp); } - /******************************************************** * Replicate the array * ********************************************************/ -template +template Array -Array::repmat( const std::vector &N_rep ) const -{ - std::vector N2( d_size.begin(), d_size.end() ); - if ( N2.size() < N_rep.size() ) - N2.resize( N_rep.size(), 1 ); +Array::repmat(const std::vector &N_rep) const { + std::vector N2(d_size.begin(), d_size.end()); + if (N2.size() < N_rep.size()) + N2.resize(N_rep.size(), 1); std::array N1, Nr; - N1.fill( 1 ); - Nr.fill( 1 ); - for ( size_t d = 0; d < N_rep.size(); d++ ) { + N1.fill(1); + Nr.fill(1); + for (size_t d = 0; d < N_rep.size(); d++) { N1[d] = d_size[d]; Nr[d] = N_rep[d]; N2[d] *= N_rep[d]; } - Array y( N2 ); + Array y(N2); TYPE *y2 = y.data(); - for ( size_t i4 = 0, index = 0; i4 < N1[4]; i4++ ) { - for ( size_t j4 = 0; j4 < Nr[4]; j4++ ) { - for ( size_t i3 = 0; i3 < N1[3]; i3++ ) { - for ( size_t j3 = 0; j3 < Nr[3]; j3++ ) { - for ( size_t i2 = 0; i2 < N1[2]; i2++ ) { - for ( size_t j2 = 0; j2 < Nr[2]; j2++ ) { - for ( size_t i1 = 0; i1 < N1[1]; i1++ ) { - for ( size_t j1 = 0; j1 < Nr[1]; j1++ ) { - for ( size_t i0 = 0; i0 < N1[0]; i0++ ) { - size_t k = d_size.index( i0, i1, i2, i3, i4 ); - TYPE x = d_data[k]; - for ( size_t j0 = 0; j0 < Nr[0]; j0++, index++ ) + for (size_t i4 = 0, index = 0; i4 < N1[4]; i4++) { + for (size_t j4 = 0; j4 < Nr[4]; j4++) { + for (size_t i3 = 0; i3 < N1[3]; i3++) { + for (size_t j3 = 0; j3 < Nr[3]; j3++) { + for (size_t i2 = 0; i2 < N1[2]; i2++) { + for (size_t j2 = 0; j2 < Nr[2]; j2++) { + for (size_t i1 = 0; i1 < N1[1]; i1++) { + for (size_t j1 = 0; j1 < Nr[1]; j1++) { + for (size_t i0 = 0; i0 < N1[0]; i0++) { + size_t k = + d_size.index(i0, i1, i2, i3, i4); + TYPE x = d_data[k]; + for (size_t j0 = 0; j0 < Nr[0]; + j0++, index++) y2[index] = x; } } @@ -807,111 +770,105 @@ Array::repmat( const std::vector &N_rep ) const return y; } - /******************************************************** * Simple math operations * ********************************************************/ -template -bool Array::NaNs() const -{ +template +bool Array::NaNs() const { bool test = false; - for ( size_t i = 0; i < d_size.length(); i++ ) + for (size_t i = 0; i < d_size.length(); i++) test = test || d_data[i] != d_data[i]; return test; } -template -TYPE Array::mean( void ) const -{ +template +TYPE Array::mean(void) const { TYPE x = sum() / d_size.length(); return x; } -template -Array Array::min( int dir ) const -{ +template +Array Array::min(int dir) const { auto size_ans = d_size; - size_ans.resize( dir, 1 ); - Array ans( size_ans ); + size_ans.resize(dir, 1); + Array ans(size_ans); size_t N1 = 1, N2 = 1, N3 = 1; - for ( int d = 0; d < std::min( dir, d_size.ndim() ); d++ ) + for (int d = 0; d < std::min(dir, d_size.ndim()); d++) N1 *= d_size[d]; N2 = d_size[dir]; - for ( size_t d = dir + 1; d < d_size.ndim(); d++ ) + for (size_t d = dir + 1; d < d_size.ndim(); d++) N3 *= d_size[d]; TYPE *data2 = ans.d_data; - for ( size_t i3 = 0; i3 < N3; i3++ ) { - for ( size_t i1 = 0; i1 < N1; i1++ ) { + for (size_t i3 = 0; i3 < N3; i3++) { + for (size_t i1 = 0; i1 < N1; i1++) { TYPE x = d_data[i1 + i3 * N1 * N2]; - for ( size_t i2 = 0; i2 < N2; i2++ ) - x = std::min( x, d_data[i1 + i2 * N1 + i3 * N1 * N2] ); + for (size_t i2 = 0; i2 < N2; i2++) + x = std::min(x, d_data[i1 + i2 * N1 + i3 * N1 * N2]); data2[i1 + i3 * N1] = x; } } return ans; } -template -Array Array::max( int dir ) const -{ +template +Array Array::max(int dir) const { auto size_ans = d_size; - size_ans.resize( dir, 1 ); - Array ans( size_ans ); + size_ans.resize(dir, 1); + Array ans(size_ans); size_t N1 = 1, N2 = 1, N3 = 1; - for ( int d = 0; d < std::min( dir, d_size.ndim() ); d++ ) + for (int d = 0; d < std::min(dir, d_size.ndim()); d++) N1 *= d_size[d]; N2 = d_size[dir]; DISABLE_WARNINGS // Suppress false array subscript is above array bounds - for ( size_t d = dir + 1; d < d_size.ndim(); d++ ) N3 *= d_size[d]; + for (size_t d = dir + 1; d < d_size.ndim(); d++) N3 *= d_size[d]; ENABLE_WARNINGS // Enable warnings TYPE *data2 = ans.d_data; - for ( size_t i3 = 0; i3 < N3; i3++ ) { - for ( size_t i1 = 0; i1 < N1; i1++ ) { + for (size_t i3 = 0; i3 < N3; i3++) { + for (size_t i1 = 0; i1 < N1; i1++) { TYPE x = d_data[i1 + i3 * N1 * N2]; - for ( size_t i2 = 0; i2 < N2; i2++ ) - x = std::max( x, d_data[i1 + i2 * N1 + i3 * N1 * N2] ); + for (size_t i2 = 0; i2 < N2; i2++) + x = std::max(x, d_data[i1 + i2 * N1 + i3 * N1 * N2]); data2[i1 + i3 * N1] = x; } } return ans; } -template -Array Array::sum( int dir ) const -{ +template +Array Array::sum(int dir) const { auto size_ans = d_size; - size_ans.resize( dir, 1 ); - Array ans( size_ans ); + size_ans.resize(dir, 1); + Array ans(size_ans); size_t N1 = 1, N2 = 1, N3 = 1; - for ( int d = 0; d < std::min( dir, d_size.ndim() ); d++ ) + for (int d = 0; d < std::min(dir, d_size.ndim()); d++) N1 *= d_size[d]; N2 = d_size[dir]; DISABLE_WARNINGS - for ( size_t d = dir + 1; d < d_size.ndim(); d++ ) + for (size_t d = dir + 1; d < d_size.ndim(); d++) N3 *= d_size[d]; ENABLE_WARNINGS TYPE *data2 = ans.d_data; - for ( size_t i3 = 0; i3 < N3; i3++ ) { - for ( size_t i1 = 0; i1 < N1; i1++ ) { + for (size_t i3 = 0; i3 < N3; i3++) { + for (size_t i1 = 0; i1 < N1; i1++) { TYPE x = 0; - for ( size_t i2 = 0; i2 < N2; i2++ ) + for (size_t i2 = 0; i2 < N2; i2++) x += d_data[i1 + i2 * N1 + i3 * N1 * N2]; data2[i1 + i3 * N1] = x; } } return ans; } -template -TYPE Array::min( const std::vector> &range ) const -{ +template +TYPE Array::min( + const std::vector> &range) const { // Get the subset indicies - checkSubsetIndex( range ); + checkSubsetIndex(range); std::array first, last, inc, N1; - getSubsetArrays( range, first, last, inc, N1 ); + getSubsetArrays(range, first, last, inc, N1); TYPE x = std::numeric_limits::max(); - for ( size_t i4 = first[4]; i4 <= last[4]; i4 += inc[4] ) { - for ( size_t i3 = first[3]; i3 <= last[3]; i3 += inc[3] ) { - for ( size_t i2 = first[2]; i2 <= last[2]; i2 += inc[2] ) { - for ( size_t i1 = first[1]; i1 <= last[1]; i1 += inc[1] ) { - for ( size_t i0 = first[0]; i0 <= last[0]; i0 += inc[0] ) { - size_t k1 = d_size.index( i0, i1, i2, i3, i4 ); - x = std::min( x, d_data[k1] ); + for (size_t i4 = first[4]; i4 <= last[4]; i4 += inc[4]) { + for (size_t i3 = first[3]; i3 <= last[3]; i3 += inc[3]) { + for (size_t i2 = first[2]; i2 <= last[2]; i2 += inc[2]) { + for (size_t i1 = first[1]; i1 <= last[1]; i1 += inc[1]) { + for (size_t i0 = first[0]; i0 <= last[0]; i0 += inc[0]) { + size_t k1 = d_size.index(i0, i1, i2, i3, i4); + x = std::min(x, d_data[k1]); } } } @@ -919,21 +876,21 @@ TYPE Array::min( const std::vector> &range ) } return x; } -template -TYPE Array::max( const std::vector> &range ) const -{ +template +TYPE Array::max( + const std::vector> &range) const { // Get the subset indicies - checkSubsetIndex( range ); + checkSubsetIndex(range); std::array first, last, inc, N1; - getSubsetArrays( range, first, last, inc, N1 ); + getSubsetArrays(range, first, last, inc, N1); TYPE x = std::numeric_limits::min(); - for ( size_t i4 = first[4]; i4 <= last[4]; i4 += inc[4] ) { - for ( size_t i3 = first[3]; i3 <= last[3]; i3 += inc[3] ) { - for ( size_t i2 = first[2]; i2 <= last[2]; i2 += inc[2] ) { - for ( size_t i1 = first[1]; i1 <= last[1]; i1 += inc[1] ) { - for ( size_t i0 = first[0]; i0 <= last[0]; i0 += inc[0] ) { - size_t k1 = d_size.index( i0, i1, i2, i3, i4 ); - x = std::max( x, d_data[k1] ); + for (size_t i4 = first[4]; i4 <= last[4]; i4 += inc[4]) { + for (size_t i3 = first[3]; i3 <= last[3]; i3 += inc[3]) { + for (size_t i2 = first[2]; i2 <= last[2]; i2 += inc[2]) { + for (size_t i1 = first[1]; i1 <= last[1]; i1 += inc[1]) { + for (size_t i0 = first[0]; i0 <= last[0]; i0 += inc[0]) { + size_t k1 = d_size.index(i0, i1, i2, i3, i4); + x = std::max(x, d_data[k1]); } } } @@ -941,20 +898,20 @@ TYPE Array::max( const std::vector> &range ) } return x; } -template -TYPE Array::sum( const std::vector> &range ) const -{ +template +TYPE Array::sum( + const std::vector> &range) const { // Get the subset indicies - checkSubsetIndex( range ); + checkSubsetIndex(range); std::array first, last, inc, N1; - getSubsetArrays( range, first, last, inc, N1 ); + getSubsetArrays(range, first, last, inc, N1); TYPE x = 0; - for ( size_t i4 = first[4]; i4 <= last[4]; i4 += inc[4] ) { - for ( size_t i3 = first[3]; i3 <= last[3]; i3 += inc[3] ) { - for ( size_t i2 = first[2]; i2 <= last[2]; i2 += inc[2] ) { - for ( size_t i1 = first[1]; i1 <= last[1]; i1 += inc[1] ) { - for ( size_t i0 = first[0]; i0 <= last[0]; i0 += inc[0] ) { - size_t k1 = d_size.index( i0, i1, i2, i3, i4 ); + for (size_t i4 = first[4]; i4 <= last[4]; i4 += inc[4]) { + for (size_t i3 = first[3]; i3 <= last[3]; i3 += inc[3]) { + for (size_t i2 = first[2]; i2 <= last[2]; i2 += inc[2]) { + for (size_t i1 = first[1]; i1 <= last[1]; i1 += inc[1]) { + for (size_t i0 = first[0]; i0 <= last[0]; i0 += inc[0]) { + size_t k1 = d_size.index(i0, i1, i2, i3, i4); x += d_data[k1]; } } @@ -963,249 +920,243 @@ TYPE Array::sum( const std::vector> &range ) } return x; } -template -TYPE Array::mean( const std::vector> &range ) const -{ +template +TYPE Array::mean( + const std::vector> &range) const { // Get the subset indicies - checkSubsetIndex( range ); + checkSubsetIndex(range); std::array first, last, inc, N1; - getSubsetArrays( range, first, last, inc, N1 ); + getSubsetArrays(range, first, last, inc, N1); size_t n = 1; - for ( auto &d : N1 ) + for (auto &d : N1) n *= d; - TYPE x = sum( range ) / n; + TYPE x = sum(range) / n; return x; } -template -TYPE Array::min( const std::vector &index ) const -{ - auto range = convert( index ); - return min( range ); +template +TYPE Array::min(const std::vector &index) const { + auto range = convert(index); + return min(range); } -template -TYPE Array::max( const std::vector &index ) const -{ - auto range = convert( index ); - return max( range ); +template +TYPE Array::max(const std::vector &index) const { + auto range = convert(index); + return max(range); } -template -TYPE Array::sum( const std::vector &index ) const -{ - auto range = convert( index ); - return sum( range ); +template +TYPE Array::sum(const std::vector &index) const { + auto range = convert(index); + return sum(range); } -template -TYPE Array::mean( const std::vector &index ) const -{ - auto range = convert( index ); - return mean( range ); +template +TYPE Array::mean(const std::vector &index) const { + auto range = convert(index); + return mean(range); } - /******************************************************** * Find all elements that match the given operation * ********************************************************/ -template -std::vector -Array::find( const TYPE &value, - std::function compare ) const -{ +template +std::vector Array::find( + const TYPE &value, + std::function compare) const { std::vector result; - result.reserve( d_size.length() ); - for ( size_t i = 0; i < d_size.length(); i++ ) { - if ( compare( d_data[i], value ) ) - result.push_back( i ); + result.reserve(d_size.length()); + for (size_t i = 0; i < d_size.length(); i++) { + if (compare(d_data[i], value)) + result.push_back(i); } return result; } - /******************************************************** * Print an array to an output stream * ********************************************************/ -template -void Array::print( std::ostream &os, - const std::string &name, - const std::string &prefix ) const -{ - if ( d_size.ndim() == 1 ) { - for ( size_t i = 0; i < d_size[0]; i++ ) - os << prefix << name << "[" << i << "] = " << d_data[i] << std::endl; - } else if ( d_size.ndim() == 2 ) { +template +void Array::print(std::ostream &os, + const std::string &name, + const std::string &prefix) const { + if (d_size.ndim() == 1) { + for (size_t i = 0; i < d_size[0]; i++) + os << prefix << name << "[" << i << "] = " << d_data[i] + << std::endl; + } else if (d_size.ndim() == 2) { os << prefix << name << ":" << std::endl; - for ( size_t i = 0; i < d_size[0]; i++ ) { - for ( size_t j = 0; j < d_size[1]; j++ ) - os << prefix << " " << operator()( i, j ); + for (size_t i = 0; i < d_size[0]; i++) { + for (size_t j = 0; j < d_size[1]; j++) + os << prefix << " " << operator()(i, j); os << std::endl; } } else { - throw std::logic_error( "Not programmed for this dimension" ); + throw std::logic_error("Not programmed for this dimension"); } } - /******************************************************** * Reverse dimensions (transpose) * ********************************************************/ -template -Array Array::reverseDim() const -{ +template +Array Array::reverseDim() const { size_t N2[5]; - for ( int d = 0; d < ArraySize::maxDim(); d++ ) + for (int d = 0; d < ArraySize::maxDim(); d++) N2[d] = d_size[ArraySize::maxDim() - d - 1]; - ArraySize S2( ArraySize::maxDim(), N2 ); - Array y( S2 ); + ArraySize S2(ArraySize::maxDim(), N2); + Array y(S2); TYPE *y2 = y.data(); - for ( size_t i0 = 0; i0 < d_size[0]; i0++ ) { - for ( size_t i1 = 0; i1 < d_size[1]; i1++ ) { - for ( size_t i2 = 0; i2 < d_size[2]; i2++ ) { - for ( size_t i3 = 0; i3 < d_size[3]; i3++ ) { - for ( size_t i4 = 0; i4 < d_size[4]; i4++ ) { - y2[S2.index( i4, i3, i2, i1, i0 )] = - d_data[d_size.index( i0, i1, i2, i3, i4 )]; + for (size_t i0 = 0; i0 < d_size[0]; i0++) { + for (size_t i1 = 0; i1 < d_size[1]; i1++) { + for (size_t i2 = 0; i2 < d_size[2]; i2++) { + for (size_t i3 = 0; i3 < d_size[3]; i3++) { + for (size_t i4 = 0; i4 < d_size[4]; i4++) { + y2[S2.index(i4, i3, i2, i1, i0)] = + d_data[d_size.index(i0, i1, i2, i3, i4)]; } } } } } - for ( int d = 0; d < d_size.ndim(); d++ ) + for (int d = 0; d < d_size.ndim(); d++) N2[d] = d_size[d_size.ndim() - d - 1]; - y.reshape( ArraySize( d_size.ndim(), N2 ) ); + y.reshape(ArraySize(d_size.ndim(), N2)); return y; } - /******************************************************** * Coarsen the array * ********************************************************/ -template -Array -Array::coarsen( const Array &filter ) const -{ +template +Array Array::coarsen( + const Array &filter) const { auto S2 = size(); - for ( size_t i = 0; i < S2.size(); i++ ) { - size_t s = S2[i] / filter.size( i ); - S2.resize( i, s ); - if ( S2[i] * filter.size( i ) != size( i ) ) - throw std::invalid_argument( "Array must be multiple of filter size" ); + for (size_t i = 0; i < S2.size(); i++) { + size_t s = S2[i] / filter.size(i); + S2.resize(i, s); + if (S2[i] * filter.size(i) != size(i)) + throw std::invalid_argument( + "Array must be multiple of filter size"); } - Array y( S2 ); - if ( d_size.ndim() > 3 ) - throw std::logic_error( "Function not programmed for more than 3 dimensions" ); + Array y(S2); + if (d_size.ndim() > 3) + throw std::logic_error( + "Function not programmed for more than 3 dimensions"); const auto &Nh = filter.d_size; - for ( size_t k1 = 0; k1 < y.d_size[2]; k1++ ) { - for ( size_t j1 = 0; j1 < y.d_size[1]; j1++ ) { - for ( size_t i1 = 0; i1 < y.d_size[0]; i1++ ) { + for (size_t k1 = 0; k1 < y.d_size[2]; k1++) { + for (size_t j1 = 0; j1 < y.d_size[1]; j1++) { + for (size_t i1 = 0; i1 < y.d_size[0]; i1++) { TYPE tmp = 0; - for ( size_t k2 = 0; k2 < Nh[2]; k2++ ) { - for ( size_t j2 = 0; j2 < Nh[1]; j2++ ) { - for ( size_t i2 = 0; i2 < Nh[0]; i2++ ) { - tmp += filter( i2, j2, k2 ) * operator()( i1 *Nh[0] + i2, - j1 * Nh[1] + j2, - k1 * Nh[2] + k2 ); + for (size_t k2 = 0; k2 < Nh[2]; k2++) { + for (size_t j2 = 0; j2 < Nh[1]; j2++) { + for (size_t i2 = 0; i2 < Nh[0]; i2++) { + tmp += filter(i2, j2, k2) * operator()( + i1 *Nh[0] + i2, + j1 * Nh[1] + j2, + k1 * Nh[2] + k2); } } } - y( i1, j1, k1 ) = tmp; + y(i1, j1, k1) = tmp; } } } return y; } -template +template Array Array::coarsen( const std::vector &ratio, - std::function & )> filter ) const -{ - if ( ratio.size() != d_size.ndim() ) - throw std::logic_error( "ratio size does not match ndim" ); + std::function &)> filter) const { + if (ratio.size() != d_size.ndim()) + throw std::logic_error("ratio size does not match ndim"); auto S2 = size(); - for ( size_t i = 0; i < S2.size(); i++ ) { - S2.resize( i, S2[i] / ratio[i] ); - if ( S2[i] * ratio[i] != size( i ) ) - throw std::invalid_argument( "Array must be multiple of filter size" ); + for (size_t i = 0; i < S2.size(); i++) { + S2.resize(i, S2[i] / ratio[i]); + if (S2[i] * ratio[i] != size(i)) + throw std::invalid_argument( + "Array must be multiple of filter size"); } - Array tmp( ratio ); - Array y( S2 ); - if ( d_size.ndim() > 3 ) - throw std::logic_error( "Function not programmed for more than 3 dimensions" ); - for ( size_t k1 = 0; k1 < y.d_size[2]; k1++ ) { - for ( size_t j1 = 0; j1 < y.d_size[1]; j1++ ) { - for ( size_t i1 = 0; i1 < y.d_size[0]; i1++ ) { - for ( size_t k2 = 0; k2 < ratio[2]; k2++ ) { - for ( size_t j2 = 0; j2 < ratio[1]; j2++ ) { - for ( size_t i2 = 0; i2 < ratio[0]; i2++ ) { - tmp( i2, j2, k2 ) = operator()( - i1 *ratio[0] + i2, j1 * ratio[1] + j2, k1 * ratio[2] + k2 ); + Array tmp(ratio); + Array y(S2); + if (d_size.ndim() > 3) + throw std::logic_error( + "Function not programmed for more than 3 dimensions"); + for (size_t k1 = 0; k1 < y.d_size[2]; k1++) { + for (size_t j1 = 0; j1 < y.d_size[1]; j1++) { + for (size_t i1 = 0; i1 < y.d_size[0]; i1++) { + for (size_t k2 = 0; k2 < ratio[2]; k2++) { + for (size_t j2 = 0; j2 < ratio[1]; j2++) { + for (size_t i2 = 0; i2 < ratio[0]; i2++) { + tmp(i2, j2, k2) = operator()(i1 *ratio[0] + i2, + j1 * ratio[1] + j2, + k1 * ratio[2] + k2); } } } - y( i1, j1, k1 ) = filter( tmp ); + y(i1, j1, k1) = filter(tmp); } } } return y; } - /******************************************************** * Concatenates the arrays * ********************************************************/ -template -void Array::cat( const Array &x, int dim ) -{ - std::vector> tmp( 2 ); - tmp[0].view2( *this ); - tmp[1].view2( const_cast &>( x ) ); - *this = cat( tmp, dim ); +template +void Array::cat(const Array &x, + int dim) { + std::vector> tmp(2); + tmp[0].view2(*this); + tmp[1].view2(const_cast &>(x)); + *this = cat(tmp, dim); } -template -Array Array::cat( const std::initializer_list &x, - int dim ) -{ - return cat( x.size(), x.begin(), dim ); -} -template -Array Array::cat( const std::vector &x, int dim ) -{ - return cat( x.size(), x.data(), dim ); -} -template +template Array -Array::cat( size_t N_array, const Array *x, int dim ) -{ - if ( N_array == 0 ) +Array::cat(const std::initializer_list &x, + int dim) { + return cat(x.size(), x.begin(), dim); +} +template +Array +Array::cat(const std::vector &x, int dim) { + return cat(x.size(), x.data(), dim); +} +template +Array +Array::cat(size_t N_array, const Array *x, int dim) { + if (N_array == 0) return Array(); // Check that the dimensions match bool check = true; - for ( size_t i = 1; i < N_array; i++ ) { + for (size_t i = 1; i < N_array; i++) { check = check && x[i].ndim() == x[0].ndim(); - for ( int d = 0; d < x[0].ndim(); d++ ) - if ( d != dim ) - check = check && x[i].size( d ) == x[0].size( d ); + for (int d = 0; d < x[0].ndim(); d++) + if (d != dim) + check = check && x[i].size(d) == x[0].size(d); } - if ( !check ) - throw std::logic_error( "Array dimensions do not match for concatenation" ); + if (!check) + throw std::logic_error( + "Array dimensions do not match for concatenation"); // Create the output array auto size = x[0].d_size; - for ( size_t i = 1; i < N_array; i++ ) - size.resize( dim, size[dim] + x[i].size( dim ) ); - Array out( size ); + for (size_t i = 1; i < N_array; i++) + size.resize(dim, size[dim] + x[i].size(dim)); + Array out(size); size_t N1 = 1; size_t N2 = size[dim]; size_t N3 = 1; - for ( int d = 0; d < dim; d++ ) + for (int d = 0; d < dim; d++) N1 *= size[d]; - for ( size_t d = dim + 1; d < size.ndim(); d++ ) + for (size_t d = dim + 1; d < size.ndim(); d++) N3 *= size[d]; TYPE *data = out.data(); - for ( size_t i = 0, i0 = 0; i < N_array; i++ ) { + for (size_t i = 0, i0 = 0; i < N_array; i++) { const TYPE *src = x[i].data(); - size_t N22 = x[i].size( dim ); - for ( size_t j2 = 0; j2 < N3; j2++ ) { - for ( size_t i1 = 0; i1 < N22; i1++ ) { - for ( size_t j1 = 0; j1 < N1; j1++ ) { - data[j1 + ( i1 + i0 ) * N1 + j2 * N1 * N2] = src[j1 + i1 * N1 + j2 * N1 * N22]; + size_t N22 = x[i].size(dim); + for (size_t j2 = 0; j2 < N3; j2++) { + for (size_t i1 = 0; i1 < N22; i1++) { + for (size_t j1 = 0; j1 < N1; j1++) { + data[j1 + (i1 + i0) * N1 + j2 * N1 * N2] = + src[j1 + i1 * N1 + j2 * N1 * N22]; } } } @@ -1214,113 +1165,108 @@ Array::cat( size_t N_array, const Array *x, int dim ) return out; } - /******************************************************** * Interpolate * ********************************************************/ -template -constexpr bool is_compatible_double() -{ +template constexpr bool is_compatible_double() { return std::is_floating_point::value || std::is_integral::value; } -template -inline TYPE Array_interp_1D( double x, int N, const TYPE *data ) -{ - if ( is_compatible_double() ) { - int i = floor( x ); - i = std::max( i, 0 ); - i = std::min( i, N - 2 ); - return ( i + 1 - x ) * data[i] + ( x - i ) * data[i + 1]; +template +inline TYPE Array_interp_1D(double x, int N, const TYPE *data) { + if (is_compatible_double()) { + int i = floor(x); + i = std::max(i, 0); + i = std::min(i, N - 2); + return (i + 1 - x) * data[i] + (x - i) * data[i + 1]; } else { - throw std::logic_error( "Invalid conversion" ); + throw std::logic_error("Invalid conversion"); } } -template -inline TYPE Array_interp_2D( double x, double y, int Nx, int Ny, const TYPE *data ) -{ - if ( is_compatible_double() ) { - int i = floor( x ); - i = std::max( i, 0 ); - i = std::min( i, Nx - 2 ); - double dx = x - i; - double dx2 = 1.0 - dx; - int j = floor( y ); - j = std::max( j, 0 ); - j = std::min( j, Ny - 2 ); - double dy = y - j; - double dy2 = 1.0 - dy; - double f[4] = { (double) data[i + j * Nx], - (double) data[i + 1 + j * Nx], - (double) data[i + ( j + 1 ) * Nx], - (double) data[i + 1 + ( j + 1 ) * Nx] }; - return ( dx * f[1] + dx2 * f[0] ) * dy2 + ( dx * f[3] + dx2 * f[2] ) * dy; +template +inline TYPE Array_interp_2D(double x, double y, int Nx, int Ny, + const TYPE *data) { + if (is_compatible_double()) { + int i = floor(x); + i = std::max(i, 0); + i = std::min(i, Nx - 2); + double dx = x - i; + double dx2 = 1.0 - dx; + int j = floor(y); + j = std::max(j, 0); + j = std::min(j, Ny - 2); + double dy = y - j; + double dy2 = 1.0 - dy; + double f[4] = {(double)data[i + j * Nx], (double)data[i + 1 + j * Nx], + (double)data[i + (j + 1) * Nx], + (double)data[i + 1 + (j + 1) * Nx]}; + return (dx * f[1] + dx2 * f[0]) * dy2 + (dx * f[3] + dx2 * f[2]) * dy; } else { - throw std::logic_error( "Invalid conversion" ); + throw std::logic_error("Invalid conversion"); } } -template -inline TYPE -Array_interp_3D( double x, double y, double z, int Nx, int Ny, int Nz, const TYPE *data ) -{ - if ( is_compatible_double() ) { - int i = floor( x ); - i = std::max( i, 0 ); - i = std::min( i, Nx - 2 ); - double dx = x - i; - double dx2 = 1.0 - dx; - int j = floor( y ); - j = std::max( j, 0 ); - j = std::min( j, Ny - 2 ); - double dy = y - j; - double dy2 = 1.0 - dy; - int k = floor( z ); - k = std::max( k, 0 ); - k = std::min( k, Nz - 2 ); - double dz = z - k; - double dz2 = 1.0 - dz; - double f[8] = { (double) data[i + j * Nx + k * Nx * Ny], - (double) data[i + 1 + j * Nx + k * Nx * Ny], - (double) data[i + ( j + 1 ) * Nx + k * Nx * Ny], - (double) data[i + 1 + ( j + 1 ) * Nx + k * Nx * Ny], - (double) data[i + j * Nx + ( k + 1 ) * Nx * Ny], - (double) data[i + 1 + j * Nx + ( k + 1 ) * Nx * Ny], - (double) data[i + ( j + 1 ) * Nx + ( k + 1 ) * Nx * Ny], - (double) data[i + 1 + ( j + 1 ) * Nx + ( k + 1 ) * Nx * Ny] }; - double h0 = ( dx * f[1] + dx2 * f[0] ) * dy2 + ( dx * f[3] + dx2 * f[2] ) * dy; - double h1 = ( dx * f[5] + dx2 * f[4] ) * dy2 + ( dx * f[7] + dx2 * f[6] ) * dy; +template +inline TYPE Array_interp_3D(double x, double y, double z, int Nx, int Ny, + int Nz, const TYPE *data) { + if (is_compatible_double()) { + int i = floor(x); + i = std::max(i, 0); + i = std::min(i, Nx - 2); + double dx = x - i; + double dx2 = 1.0 - dx; + int j = floor(y); + j = std::max(j, 0); + j = std::min(j, Ny - 2); + double dy = y - j; + double dy2 = 1.0 - dy; + int k = floor(z); + k = std::max(k, 0); + k = std::min(k, Nz - 2); + double dz = z - k; + double dz2 = 1.0 - dz; + double f[8] = {(double)data[i + j * Nx + k * Nx * Ny], + (double)data[i + 1 + j * Nx + k * Nx * Ny], + (double)data[i + (j + 1) * Nx + k * Nx * Ny], + (double)data[i + 1 + (j + 1) * Nx + k * Nx * Ny], + (double)data[i + j * Nx + (k + 1) * Nx * Ny], + (double)data[i + 1 + j * Nx + (k + 1) * Nx * Ny], + (double)data[i + (j + 1) * Nx + (k + 1) * Nx * Ny], + (double)data[i + 1 + (j + 1) * Nx + (k + 1) * Nx * Ny]}; + double h0 = + (dx * f[1] + dx2 * f[0]) * dy2 + (dx * f[3] + dx2 * f[2]) * dy; + double h1 = + (dx * f[5] + dx2 * f[4]) * dy2 + (dx * f[7] + dx2 * f[6]) * dy; return h0 * dz2 + h1 * dz; } else { - throw std::logic_error( "Invalid conversion" ); + throw std::logic_error("Invalid conversion"); } } -template -TYPE Array::interp( const double *x ) const -{ +template +TYPE Array::interp(const double *x) const { int ndim = 0, dim[5]; double x2[5]; - for ( int d = 0; d < d_size.ndim(); d++ ) { - if ( d_size[d] > 1 ) { - x2[ndim] = x[d]; + for (int d = 0; d < d_size.ndim(); d++) { + if (d_size[d] > 1) { + x2[ndim] = x[d]; dim[ndim] = d_size[d]; ndim++; } } TYPE f = 0; - if ( ndim == 0 ) { + if (ndim == 0) { // No data, do nothing - } else if ( ndim == 1 ) { - f = Array_interp_1D( x2[0], dim[0], d_data ); - } else if ( ndim == 2 ) { - f = Array_interp_2D( x2[0], x2[1], dim[0], dim[1], d_data ); - } else if ( ndim == 3 ) { - f = Array_interp_3D( x2[0], x2[1], x2[2], dim[0], dim[1], dim[2], d_data ); + } else if (ndim == 1) { + f = Array_interp_1D(x2[0], dim[0], d_data); + } else if (ndim == 2) { + f = Array_interp_2D(x2[0], x2[1], dim[0], dim[1], d_data); + } else if (ndim == 3) { + f = Array_interp_3D(x2[0], x2[1], x2[2], dim[0], dim[1], dim[2], + d_data); } else { - throw std::logic_error( "Not finished" ); + throw std::logic_error("Not finished"); } return f; } - /******************************************************** * Math operations (should call the Math class) * ********************************************************/ @@ -1330,86 +1276,78 @@ void Array::rand() FUN::rand( *this ); } */ -template -Array & -Array::operator+=( const Array &rhs ) -{ - auto op = []( const TYPE &a, const TYPE &b ) { return a + b; }; - FUN::transform( op, *this, rhs, *this ); +template +Array &Array:: +operator+=(const Array &rhs) { + auto op = [](const TYPE &a, const TYPE &b) { return a + b; }; + FUN::transform(op, *this, rhs, *this); return *this; } -template -Array & -Array::operator-=( const Array &rhs ) -{ - auto op = []( const TYPE &a, const TYPE &b ) { return a - b; }; - FUN::transform( op, *this, rhs, *this ); +template +Array &Array:: +operator-=(const Array &rhs) { + auto op = [](const TYPE &a, const TYPE &b) { return a - b; }; + FUN::transform(op, *this, rhs, *this); return *this; } -template -Array &Array::operator+=( const TYPE &rhs ) -{ - auto op = [rhs]( const TYPE &x ) { return x + rhs; }; - FUN::transform( op, *this, *this ); +template +Array &Array:: +operator+=(const TYPE &rhs) { + auto op = [rhs](const TYPE &x) { return x + rhs; }; + FUN::transform(op, *this, *this); return *this; } -template -Array &Array::operator-=( const TYPE &rhs ) -{ - auto op = [rhs]( const TYPE &x ) { return x - rhs; }; - FUN::transform( op, *this, *this ); +template +Array &Array:: +operator-=(const TYPE &rhs) { + auto op = [rhs](const TYPE &x) { return x - rhs; }; + FUN::transform(op, *this, *this); return *this; } -template -TYPE Array::min() const -{ - const auto &op = []( const TYPE &a, const TYPE &b ) { return a < b ? a : b; }; - return FUN::reduce( op, *this, d_data[0] ); +template +TYPE Array::min() const { + const auto &op = [](const TYPE &a, const TYPE &b) { return a < b ? a : b; }; + return FUN::reduce(op, *this, d_data[0]); } -template -TYPE Array::max() const -{ - const auto &op = []( const TYPE &a, const TYPE &b ) { return a > b ? a : b; }; - return FUN::reduce( op, *this, d_data[0] ); +template +TYPE Array::max() const { + const auto &op = [](const TYPE &a, const TYPE &b) { return a > b ? a : b; }; + return FUN::reduce(op, *this, d_data[0]); } -template -TYPE Array::sum() const -{ - const auto &op = []( const TYPE &a, const TYPE &b ) { return a + b; }; - return FUN::reduce( op, *this, static_cast( 0 ) ); +template +TYPE Array::sum() const { + const auto &op = [](const TYPE &a, const TYPE &b) { return a + b; }; + return FUN::reduce(op, *this, static_cast(0)); } -template -void Array::axpby( const TYPE &alpha, - const Array &x, - const TYPE &beta ) -{ - const auto &op = [alpha, beta]( const TYPE &x, const TYPE &y ) { return alpha * x + beta * y; }; - return FUN::transform( op, x, *this, *this ); +template +void Array::axpby(const TYPE &alpha, + const Array &x, + const TYPE &beta) { + const auto &op = [alpha, beta](const TYPE &x, const TYPE &y) { + return alpha * x + beta * y; + }; + return FUN::transform(op, x, *this, *this); } -template +template Array -Array::transform( std::function fun, - const Array &x ) -{ +Array::transform(std::function fun, + const Array &x) { Array y; - FUN::transform( fun, x, y ); + FUN::transform(fun, x, y); return y; } -template -Array -Array::transform( std::function fun, - const Array &x, - const Array &y ) -{ +template +Array Array::transform( + std::function fun, + const Array &x, + const Array &y) { Array z; - FUN::transform( fun, x, y, z ); + FUN::transform(fun, x, y, z); return z; } -template -bool Array::equals( const Array &rhs, TYPE tol ) const -{ - return FUN::equals( *this, rhs, tol ); +template +bool Array::equals(const Array &rhs, TYPE tol) const { + return FUN::equals(*this, rhs, tol); } - #endif diff --git a/common/ArraySize.h b/common/ArraySize.h index 808b7863..a4df4191 100644 --- a/common/ArraySize.h +++ b/common/ArraySize.h @@ -11,47 +11,41 @@ #include #include - -#if defined( __CUDA_ARCH__ ) +#if defined(__CUDA_ARCH__) #include #define HOST_DEVICE __host__ __device__ #else #define HOST_DEVICE #endif -#if defined( USING_GCC ) || defined( USING_CLANG ) -#define ARRAY_ATTRIBUTE HOST_DEVICE __attribute__( ( always_inline ) ) +#if defined(USING_GCC) || defined(USING_CLANG) +#define ARRAY_ATTRIBUTE HOST_DEVICE __attribute__((always_inline)) #else #define ARRAY_ATTRIBUTE HOST_DEVICE #endif - -#if ( defined( DEBUG ) || defined( _DEBUG ) ) && !defined( NDEBUG ) -#define CHECK_ARRAY_LENGTH( i, length ) \ - do { \ - if ( i >= length ) \ - throw std::out_of_range( "Index exceeds array bounds" ); \ - } while ( 0 ) +#if (defined(DEBUG) || defined(_DEBUG)) && !defined(NDEBUG) +#define CHECK_ARRAY_LENGTH(i, length) \ + do { \ + if (i >= length) \ + throw std::out_of_range("Index exceeds array bounds"); \ + } while (0) #else -#define CHECK_ARRAY_LENGTH( i, length ) \ - do { \ - } while ( 0 ) +#define CHECK_ARRAY_LENGTH(i, length) \ + do { \ + } while (0) #endif - - // Forward declerations class FunctionTable; -template> +template > class Array; - //! Simple range class -template -class Range final -{ +template class Range final { public: //! Empty constructor - Range() : i( 0 ), j( -1 ), k( 1 ) {} + Range() : i(0), j(-1), k(1) {} /*! * Create a range i:k:j (or i:j) @@ -59,26 +53,23 @@ public: * @param j_ Ending value * @param k_ Increment value */ - Range( const TYPE &i_, const TYPE &j_, const TYPE &k_ = 1 ) - : i( i_ ), j( j_ ), k( k_ ) - { - } + Range(const TYPE &i_, const TYPE &j_, const TYPE &k_ = 1) + : i(i_), j(j_), k(k_) {} //! Get the number of values in the range - size_t size() const - { - if ( std::is_integral::value ) { - return ( static_cast( j ) - static_cast( i ) ) / - static_cast( k ); - } else if ( std::is_floating_point::value ) { - double tmp = static_cast( ( j - i ) ) / static_cast( k ); - return static_cast( floor( tmp + 1e-12 ) + 1 ); - } else if ( std::is_same>::value || - std::is_same>::value ) { - double tmp = std::real( ( j - i ) / ( k ) ); - return static_cast( floor( tmp + 1e-12 ) + 1 ); + size_t size() const { + if (std::is_integral::value) { + return (static_cast(j) - static_cast(i)) / + static_cast(k); + } else if (std::is_floating_point::value) { + double tmp = static_cast((j - i)) / static_cast(k); + return static_cast(floor(tmp + 1e-12) + 1); + } else if (std::is_same>::value || + std::is_same>::value) { + double tmp = std::real((j - i) / (k)); + return static_cast(floor(tmp + 1e-12) + 1); } else { - ERROR( "Unsupported type for range" ); + ERROR("Unsupported type for range"); } } @@ -86,29 +77,25 @@ public: TYPE i, j, k; }; - //! Simple class to store the array dimensions -class ArraySize final -{ +class ArraySize final { public: //! Empty constructor - ArraySize() : d_ndim( 1 ), d_length( 0 ), d_N{ 0, 1, 1, 1, 1 } {} + ArraySize() : d_ndim(1), d_length(0), d_N{0, 1, 1, 1, 1} {} /*! * Create the vector size * @param N1 Number of elements in the first dimension */ - ArraySize( size_t N1 ) : d_ndim( 1 ), d_length( N1 ), d_N{ N1, 1, 1, 1, 1 } {} + ArraySize(size_t N1) : d_ndim(1), d_length(N1), d_N{N1, 1, 1, 1, 1} {} /*! * Create the vector size * @param N1 Number of elements in the first dimension * @param N2 Number of elements in the second dimension */ - ArraySize( size_t N1, size_t N2 ) - : d_ndim( 2 ), d_length( N1 * N2 ), d_N{ N1, N2, 1, 1, 1 } - { - } + ArraySize(size_t N1, size_t N2) + : d_ndim(2), d_length(N1 * N2), d_N{N1, N2, 1, 1, 1} {} /*! * Create the vector size @@ -116,10 +103,8 @@ public: * @param N2 Number of elements in the second dimension * @param N3 Number of elements in the third dimension */ - ArraySize( size_t N1, size_t N2, size_t N3 ) - : d_ndim( 3 ), d_length( N1 * N2 * N3 ), d_N{ N1, N2, N3, 1, 1 } - { - } + ArraySize(size_t N1, size_t N2, size_t N3) + : d_ndim(3), d_length(N1 * N2 * N3), d_N{N1, N2, N3, 1, 1} {} /*! * Create the vector size @@ -128,10 +113,8 @@ public: * @param N3 Number of elements in the third dimension * @param N4 Number of elements in the fourth dimension */ - ArraySize( size_t N1, size_t N2, size_t N3, size_t N4 ) - : d_ndim( 4 ), d_length( N1 * N2 * N3 * N4 ), d_N{ N1, N2, N3, N4, 1 } - { - } + ArraySize(size_t N1, size_t N2, size_t N3, size_t N4) + : d_ndim(4), d_length(N1 * N2 * N3 * N4), d_N{N1, N2, N3, N4, 1} {} /*! * Create the vector size @@ -141,9 +124,8 @@ public: * @param N4 Number of elements in the fourth dimension * @param N5 Number of elements in the fifth dimension */ - ArraySize( size_t N1, size_t N2, size_t N3, size_t N4, size_t N5 ) - : d_ndim( 5 ), d_length( N1 * N2 * N3 * N4 * N5 ), d_N{ N1, N2, N3, N4, N5 } - { + ArraySize(size_t N1, size_t N2, size_t N3, size_t N4, size_t N5) + : d_ndim(5), d_length(N1 * N2 * N3 * N4 * N5), d_N{N1, N2, N3, N4, N5} { } /*! @@ -151,40 +133,37 @@ public: * @param N Size of the array * @param ndim Number of dimensions */ - ArraySize( std::initializer_list N, int ndim = -1 ) - : d_ndim( N.size() ), d_length( 0 ), d_N{ 0, 1, 1, 1, 1 } - { - if ( ndim >= 0 ) + ArraySize(std::initializer_list N, int ndim = -1) + : d_ndim(N.size()), d_length(0), d_N{0, 1, 1, 1, 1} { + if (ndim >= 0) d_ndim = ndim; - if ( d_ndim > 5 ) - throw std::out_of_range( "Maximum number of dimensions exceeded" ); + if (d_ndim > 5) + throw std::out_of_range("Maximum number of dimensions exceeded"); auto it = N.begin(); - for ( size_t i = 0; i < d_ndim; i++, ++it ) + for (size_t i = 0; i < d_ndim; i++, ++it) d_N[i] = *it; d_length = 1; - for ( unsigned long i : d_N ) + for (unsigned long i : d_N) d_length *= i; - if ( d_ndim == 0 ) + if (d_ndim == 0) d_length = 0; } - /*! * Create from raw pointer * @param ndim Number of dimensions * @param dims Dimensions */ - ArraySize( size_t ndim, const size_t *dims ) - : d_ndim( ndim ), d_length( 0 ), d_N{ 0, 1, 1, 1, 1 } - { - if ( d_ndim > 5 ) - throw std::out_of_range( "Maximum number of dimensions exceeded" ); - for ( size_t i = 0; i < ndim; i++ ) + ArraySize(size_t ndim, const size_t *dims) + : d_ndim(ndim), d_length(0), d_N{0, 1, 1, 1, 1} { + if (d_ndim > 5) + throw std::out_of_range("Maximum number of dimensions exceeded"); + for (size_t i = 0; i < ndim; i++) d_N[i] = dims[i]; d_length = 1; - for ( unsigned long i : d_N ) + for (unsigned long i : d_N) d_length *= i; - if ( d_ndim == 0 ) + if (d_ndim == 0) d_length = 0; } @@ -192,28 +171,27 @@ public: * Create from std::array * @param N Size of the array */ - template - ArraySize( const std::array &N ) : ArraySize( NDIM, N.data() ) - { - } + template + ArraySize(const std::array &N) : ArraySize(NDIM, N.data()) {} /*! * Create from std::vector * @param N Size of the array */ - inline ArraySize( const std::vector &N ) : ArraySize( N.size(), N.data() ) {} + inline ArraySize(const std::vector &N) + : ArraySize(N.size(), N.data()) {} // Copy/assignment constructors - ArraySize( ArraySize &&rhs ) = default; - ArraySize( const ArraySize &rhs ) = default; - ArraySize &operator=( ArraySize &&rhs ) = default; - ArraySize &operator=( const ArraySize &rhs ) = default; + ArraySize(ArraySize &&rhs) = default; + ArraySize(const ArraySize &rhs) = default; + ArraySize &operator=(ArraySize &&rhs) = default; + ArraySize &operator=(const ArraySize &rhs) = default; /*! * Access the ith dimension * @param i Index to access */ - ARRAY_ATTRIBUTE size_t operator[]( size_t i ) const { return d_N[i]; } + ARRAY_ATTRIBUTE size_t operator[](size_t i) const { return d_N[i]; } //! Return the number of dimensions ARRAY_ATTRIBUTE uint8_t ndim() const { return d_ndim; } @@ -225,13 +203,12 @@ public: ARRAY_ATTRIBUTE size_t length() const { return d_length; } //! Resize the dimension - void resize( uint8_t dim, size_t N ) - { - if ( dim >= d_ndim ) - throw std::out_of_range( "Invalid dimension" ); + void resize(uint8_t dim, size_t N) { + if (dim >= d_ndim) + throw std::out_of_range("Invalid dimension"); d_N[dim] = N; d_length = 1; - for ( unsigned long i : d_N ) + for (unsigned long i : d_N) d_length *= i; } @@ -240,16 +217,15 @@ public: * max of ndim and the largest dim>1. * @param ndim Desired number of dimensions */ - void setNdim( uint8_t ndim ) { d_ndim = std::max( ndim, d_ndim ); } + void setNdim(uint8_t ndim) { d_ndim = std::max(ndim, d_ndim); } /*! * Remove singleton dimensions */ - void squeeze() - { + void squeeze() { d_ndim = 0; - for ( uint8_t i = 0; i < maxDim(); i++ ) { - if ( d_N[i] != 1 ) + for (uint8_t i = 0; i < maxDim(); i++) { + if (d_N[i] != 1) d_N[d_ndim++] = d_N[i]; } } @@ -261,71 +237,65 @@ public: const size_t *end() const { return d_N + d_ndim; } // Check if two array sizes are equal - ARRAY_ATTRIBUTE bool operator==( const ArraySize &rhs ) const - { - return d_ndim == rhs.d_ndim && memcmp( d_N, rhs.d_N, sizeof( d_N ) ) == 0; + ARRAY_ATTRIBUTE bool operator==(const ArraySize &rhs) const { + return d_ndim == rhs.d_ndim && memcmp(d_N, rhs.d_N, sizeof(d_N)) == 0; } // Check if two array sizes are equal (ignoring the dimension) - ARRAY_ATTRIBUTE bool approxEqual( const ArraySize &rhs ) const - { - return ( length() == 0 && rhs.length() == 0 ) || memcmp( d_N, rhs.d_N, sizeof( d_N ) ) == 0; + ARRAY_ATTRIBUTE bool approxEqual(const ArraySize &rhs) const { + return (length() == 0 && rhs.length() == 0) || + memcmp(d_N, rhs.d_N, sizeof(d_N)) == 0; } //! Check if two matrices are not equal - ARRAY_ATTRIBUTE bool operator!=( const ArraySize &rhs ) const - { - return d_ndim != rhs.d_ndim || memcmp( d_N, rhs.d_N, sizeof( d_N ) ) != 0; + ARRAY_ATTRIBUTE bool operator!=(const ArraySize &rhs) const { + return d_ndim != rhs.d_ndim || memcmp(d_N, rhs.d_N, sizeof(d_N)) != 0; } //! Maximum supported dimension ARRAY_ATTRIBUTE static uint8_t maxDim() { return 5; } //! Get the index - ARRAY_ATTRIBUTE size_t index( size_t i ) const - { - CHECK_ARRAY_LENGTH( i, d_length ); + ARRAY_ATTRIBUTE size_t index(size_t i) const { + CHECK_ARRAY_LENGTH(i, d_length); return i; } //! Get the index - ARRAY_ATTRIBUTE size_t index( size_t i1, size_t i2 ) const - { + ARRAY_ATTRIBUTE size_t index(size_t i1, size_t i2) const { size_t index = i1 + i2 * d_N[0]; - CHECK_ARRAY_LENGTH( index, d_length ); + CHECK_ARRAY_LENGTH(index, d_length); return index; } //! Get the index - ARRAY_ATTRIBUTE size_t index( size_t i1, size_t i2, size_t i3 ) const - { - size_t index = i1 + d_N[0] * ( i2 + d_N[1] * i3 ); - CHECK_ARRAY_LENGTH( index, d_length ); + ARRAY_ATTRIBUTE size_t index(size_t i1, size_t i2, size_t i3) const { + size_t index = i1 + d_N[0] * (i2 + d_N[1] * i3); + CHECK_ARRAY_LENGTH(index, d_length); return index; } //! Get the index - ARRAY_ATTRIBUTE size_t index( size_t i1, size_t i2, size_t i3, size_t i4 ) const - { - size_t index = i1 + d_N[0] * ( i2 + d_N[1] * ( i3 + d_N[2] * i4 ) ); - CHECK_ARRAY_LENGTH( index, d_length ); + ARRAY_ATTRIBUTE size_t index(size_t i1, size_t i2, size_t i3, + size_t i4) const { + size_t index = i1 + d_N[0] * (i2 + d_N[1] * (i3 + d_N[2] * i4)); + CHECK_ARRAY_LENGTH(index, d_length); return index; } //! Get the index - ARRAY_ATTRIBUTE size_t - index( size_t i1, size_t i2, size_t i3, size_t i4, size_t i5 ) const - { - size_t index = i1 + d_N[0] * ( i2 + d_N[1] * ( i3 + d_N[2] * ( i4 + d_N[3] * i5 ) ) ); - CHECK_ARRAY_LENGTH( index, d_length ); + ARRAY_ATTRIBUTE size_t index(size_t i1, size_t i2, size_t i3, size_t i4, + size_t i5) const { + size_t index = + i1 + d_N[0] * (i2 + d_N[1] * (i3 + d_N[2] * (i4 + d_N[3] * i5))); + CHECK_ARRAY_LENGTH(index, d_length); return index; } //! Get the index - size_t index( const std::array &i ) const - { + size_t index(const std::array &i) const { size_t j = 0; - for ( size_t m = 0, N = 1; m < 5; m++ ) { + for (size_t m = 0, N = 1; m < 5; m++) { j += i[m] * N; N *= d_N[m]; } @@ -333,12 +303,11 @@ public: } //! Get the index - size_t index( std::initializer_list i ) const - { + size_t index(std::initializer_list i) const { size_t N = 1; size_t j = 0; size_t m = 0; - for ( size_t k : i ) { + for (size_t k : i) { j += k * N; N *= d_N[m++]; } @@ -346,33 +315,31 @@ public: } //! Convert the index to ijk values - std::array ijk( size_t index ) const - { - CHECK_ARRAY_LENGTH( index, d_length ); + std::array ijk(size_t index) const { + CHECK_ARRAY_LENGTH(index, d_length); size_t i0 = index % d_N[0]; - index = index / d_N[0]; + index = index / d_N[0]; size_t i1 = index % d_N[1]; - index = index / d_N[1]; + index = index / d_N[1]; size_t i2 = index % d_N[2]; - index = index / d_N[2]; + index = index / d_N[2]; size_t i3 = index % d_N[3]; - index = index / d_N[3]; - return { i0, i1, i2, i3, index }; + index = index / d_N[3]; + return {i0, i1, i2, i3, index}; } //! Convert the index to ijk values - void ijk( size_t index, size_t *x ) const - { - CHECK_ARRAY_LENGTH( index, d_length ); - x[0] = index % d_N[0]; + void ijk(size_t index, size_t *x) const { + CHECK_ARRAY_LENGTH(index, d_length); + x[0] = index % d_N[0]; index = index / d_N[0]; - x[1] = index % d_N[1]; + x[1] = index % d_N[1]; index = index / d_N[1]; - x[2] = index % d_N[2]; + x[2] = index % d_N[2]; index = index / d_N[2]; - x[3] = index % d_N[3]; + x[3] = index % d_N[3]; index = index / d_N[3]; - x[4] = index; + x[4] = index; } private: @@ -381,51 +348,42 @@ private: size_t d_N[5]; }; - // Function to concatenate dimensions of two array sizes -inline ArraySize cat( const ArraySize &x, const ArraySize &y ) -{ - if ( x.ndim() + y.ndim() > 5 ) - throw std::out_of_range( "Maximum number of dimensions exceeded" ); - size_t N[5] = { 0 }; - for ( int i = 0; i < x.ndim(); i++ ) +inline ArraySize cat(const ArraySize &x, const ArraySize &y) { + if (x.ndim() + y.ndim() > 5) + throw std::out_of_range("Maximum number of dimensions exceeded"); + size_t N[5] = {0}; + for (int i = 0; i < x.ndim(); i++) N[i] = x[i]; - for ( int i = 0; i < y.ndim(); i++ ) + for (int i = 0; i < y.ndim(); i++) N[i + x.ndim()] = y[i]; - return ArraySize( x.ndim() + y.ndim(), N ); + return ArraySize(x.ndim() + y.ndim(), N); } - // Operator overloads -inline ArraySize operator*( size_t v, const ArraySize &x ) -{ - size_t N[5] = { v * x[0], v * x[1], v * x[2], v * x[3], v * x[4] }; - return ArraySize( x.ndim(), N ); +inline ArraySize operator*(size_t v, const ArraySize &x) { + size_t N[5] = {v * x[0], v * x[1], v * x[2], v * x[3], v * x[4]}; + return ArraySize(x.ndim(), N); } -inline ArraySize operator*( const ArraySize &x, size_t v ) -{ - size_t N[5] = { v * x[0], v * x[1], v * x[2], v * x[3], v * x[4] }; - return ArraySize( x.ndim(), N ); +inline ArraySize operator*(const ArraySize &x, size_t v) { + size_t N[5] = {v * x[0], v * x[1], v * x[2], v * x[3], v * x[4]}; + return ArraySize(x.ndim(), N); } -inline ArraySize operator-( const ArraySize &x, size_t v ) -{ - size_t N[5] = { x[0] - v, x[1] - v, x[2] - v, x[3] - v, x[4] - v }; - return ArraySize( x.ndim(), N ); +inline ArraySize operator-(const ArraySize &x, size_t v) { + size_t N[5] = {x[0] - v, x[1] - v, x[2] - v, x[3] - v, x[4] - v}; + return ArraySize(x.ndim(), N); } -inline ArraySize operator+( const ArraySize &x, size_t v ) -{ - size_t N[5] = { x[0] + v, x[1] + v, x[2] + v, x[3] + v, x[4] + v }; - return ArraySize( x.ndim(), N ); +inline ArraySize operator+(const ArraySize &x, size_t v) { + size_t N[5] = {x[0] + v, x[1] + v, x[2] + v, x[3] + v, x[4] + v}; + return ArraySize(x.ndim(), N); } -inline ArraySize operator+( size_t v, const ArraySize &x ) -{ - size_t N[5] = { x[0] + v, x[1] + v, x[2] + v, x[3] + v, x[4] + v }; - return ArraySize( x.ndim(), N ); +inline ArraySize operator+(size_t v, const ArraySize &x) { + size_t N[5] = {x[0] + v, x[1] + v, x[2] + v, x[3] + v, x[4] + v}; + return ArraySize(x.ndim(), N); } -#if defined( USING_ICC ) +#if defined(USING_ICC) ENABLE_WARNINGS #endif - #endif diff --git a/common/Communication.cpp b/common/Communication.cpp index 3d383d3d..22ef57f6 100644 --- a/common/Communication.cpp +++ b/common/Communication.cpp @@ -16,100 +16,92 @@ */ #include "common/Communication.h" - /******************************************************** * Structure to store the rank info * ********************************************************/ -int RankInfoStruct::getRankForBlock( int i, int j, int k ) const -{ - int i2 = (i+nx)%nx; - int j2 = (j+ny)%ny; - int k2 = (k+nz)%nz; - return i2 + j2*nx + k2*nx*ny; +int RankInfoStruct::getRankForBlock(int i, int j, int k) const { + int i2 = (i + nx) % nx; + int j2 = (j + ny) % ny; + int k2 = (k + nz) % nz; + return i2 + j2 * nx + k2 * nx * ny; } -RankInfoStruct::RankInfoStruct() -{ +RankInfoStruct::RankInfoStruct() { nx = 0; ny = 0; nz = 0; ix = -1; jy = -1; kz = -1; - for (int i=-1; i<=1; i++) { - for (int j=-1; j<=1; j++) { - for (int k=-1; k<=1; k++) { - rank[i+1][j+1][k+1] = -1; + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + for (int k = -1; k <= 1; k++) { + rank[i + 1][j + 1][k + 1] = -1; } } } } -RankInfoStruct::RankInfoStruct( int rank0, int nprocx, int nprocy, int nprocz ) -{ - memset(this,0,sizeof(RankInfoStruct)); +RankInfoStruct::RankInfoStruct(int rank0, int nprocx, int nprocy, int nprocz) { + memset(this, 0, sizeof(RankInfoStruct)); nx = nprocx; ny = nprocy; nz = nprocz; - if ( rank0 >= nprocx * nprocy * nprocz ) { - ix = -1; - jy = -1; - kz = -1; - for (int i=-1; i<=1; i++) { - for (int j=-1; j<=1; j++) { - for (int k=-1; k<=1; k++) { - rank[i+1][j+1][k+1] = -1; + if (rank0 >= nprocx * nprocy * nprocz) { + ix = -1; + jy = -1; + kz = -1; + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + for (int k = -1; k <= 1; k++) { + rank[i + 1][j + 1][k + 1] = -1; } } } } else { - ix = rank0%nprocx; - jy = (rank0/nprocx)%nprocy; - kz = rank0/(nprocx*nprocy); - for (int i=-1; i<=1; i++) { - for (int j=-1; j<=1; j++) { - for (int k=-1; k<=1; k++) { - rank[i+1][j+1][k+1] = getRankForBlock(ix+i,jy+j,kz+k); + ix = rank0 % nprocx; + jy = (rank0 / nprocx) % nprocy; + kz = rank0 / (nprocx * nprocy); + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + for (int k = -1; k <= 1; k++) { + rank[i + 1][j + 1][k + 1] = + getRankForBlock(ix + i, jy + j, kz + k); } } } - ASSERT(rank[1][1][1]==rank0); + ASSERT(rank[1][1][1] == rank0); } } - /******************************************************** * Deprecated functions * ********************************************************/ -void InitializeRanks( const int rank, const int nprocx, const int nprocy, const int nprocz, - int& iproc, int& jproc, int& kproc, - int& rank_x, int& rank_y, int& rank_z, - int& rank_X, int& rank_Y, int& rank_Z, - int& rank_xy, int& rank_XY, int& rank_xY, int& rank_Xy, - int& rank_xz, int& rank_XZ, int& rank_xZ, int& rank_Xz, - int& rank_yz, int& rank_YZ, int& rank_yZ, int& rank_Yz ) -{ - const RankInfoStruct data(rank,nprocx,nprocy,nprocz); - iproc = data.ix; +void InitializeRanks(const int rank, const int nprocx, const int nprocy, + const int nprocz, int &iproc, int &jproc, int &kproc, + int &rank_x, int &rank_y, int &rank_z, int &rank_X, + int &rank_Y, int &rank_Z, int &rank_xy, int &rank_XY, + int &rank_xY, int &rank_Xy, int &rank_xz, int &rank_XZ, + int &rank_xZ, int &rank_Xz, int &rank_yz, int &rank_YZ, + int &rank_yZ, int &rank_Yz) { + const RankInfoStruct data(rank, nprocx, nprocy, nprocz); + iproc = data.ix; jproc = data.jy; kproc = data.kz; - rank_X = data.rank[2][1][1]; - rank_x = data.rank[0][1][1]; - rank_Y = data.rank[1][2][1]; - rank_y = data.rank[1][0][1]; - rank_Z = data.rank[1][1][2]; - rank_z = data.rank[1][1][0]; - rank_XY = data.rank[2][2][1]; - rank_xy = data.rank[0][0][1]; - rank_Xy = data.rank[2][0][1]; - rank_xY = data.rank[0][2][1]; - rank_XZ = data.rank[2][1][2]; - rank_xz = data.rank[0][1][0]; - rank_Xz = data.rank[2][1][0]; - rank_xZ = data.rank[0][1][2]; - rank_YZ = data.rank[1][2][2]; - rank_yz = data.rank[1][0][0]; - rank_Yz = data.rank[1][2][0]; - rank_yZ = data.rank[1][0][2]; + rank_X = data.rank[2][1][1]; + rank_x = data.rank[0][1][1]; + rank_Y = data.rank[1][2][1]; + rank_y = data.rank[1][0][1]; + rank_Z = data.rank[1][1][2]; + rank_z = data.rank[1][1][0]; + rank_XY = data.rank[2][2][1]; + rank_xy = data.rank[0][0][1]; + rank_Xy = data.rank[2][0][1]; + rank_xY = data.rank[0][2][1]; + rank_XZ = data.rank[2][1][2]; + rank_xz = data.rank[0][1][0]; + rank_Xz = data.rank[2][1][0]; + rank_xZ = data.rank[0][1][2]; + rank_YZ = data.rank[1][2][2]; + rank_yz = data.rank[1][0][0]; + rank_Yz = data.rank[1][2][0]; + rank_yZ = data.rank[1][0][2]; } - - - diff --git a/common/Communication.h b/common/Communication.h index 2e868b02..6df1e1d4 100644 --- a/common/Communication.h +++ b/common/Communication.h @@ -32,38 +32,35 @@ using namespace std; - /*! * @brief Rank info structure * @details Structure used to hold the ranks for the current process and it's neighbors */ struct RankInfoStruct { - int nx; //!< The number of processors in the x direction - int ny; //!< The number of processors in the y direction - int nz; //!< The number of processors in the z direction - int ix; //!< The index of the current process in the x direction - int jy; //!< The index of the current process in the y direction - int kz; //!< The index of the current process in the z direction - int rank[3][3][3]; //!< The rank for the neighbor [i][j][k] + int nx; //!< The number of processors in the x direction + int ny; //!< The number of processors in the y direction + int nz; //!< The number of processors in the z direction + int ix; //!< The index of the current process in the x direction + int jy; //!< The index of the current process in the y direction + int kz; //!< The index of the current process in the z direction + int rank[3][3][3]; //!< The rank for the neighbor [i][j][k] RankInfoStruct(); - RankInfoStruct( int rank, int nprocx, int nprocy, int nprocz ); - int getRankForBlock( int i, int j, int k ) const; + RankInfoStruct(int rank, int nprocx, int nprocy, int nprocz); + int getRankForBlock(int i, int j, int k) const; }; - //! Redistribute domain data (dst may be smaller than the src) -template -Array redistribute( const RankInfoStruct& src_rank, const Array& src_data, - const RankInfoStruct& dst_rank, std::array dst_size, const Utilities::MPI& comm ); - +template +Array +redistribute(const RankInfoStruct &src_rank, const Array &src_data, + const RankInfoStruct &dst_rank, std::array dst_size, + const Utilities::MPI &comm); /*! * @brief Communicate halo * @details Fill the halo cells in an array from the neighboring processes */ -template -class fillHalo -{ +template class fillHalo { public: /*! * @brief Default constructor @@ -76,37 +73,36 @@ public: * @param[in] fill Fill {faces,edges,corners} * @param[in] periodic Periodic dimensions */ - fillHalo( const Utilities::MPI& comm, const RankInfoStruct& info, - std::array n, std::array ng, int tag, int depth, - std::array fill = {true,true,true}, - std::array periodic = {true,true,true} ); + fillHalo(const Utilities::MPI &comm, const RankInfoStruct &info, + std::array n, std::array ng, int tag, int depth, + std::array fill = {true, true, true}, + std::array periodic = {true, true, true}); //! Destructor - ~fillHalo( ); + ~fillHalo(); fillHalo() = delete; - fillHalo(const fillHalo&) = delete; - fillHalo& operator=(const fillHalo&) = delete; + fillHalo(const fillHalo &) = delete; + fillHalo &operator=(const fillHalo &) = delete; /*! * @brief Communicate the halos * @param[in] array The array on which we fill the halos */ - void fill( Array& array ); + void fill(Array &array); /*! * @brief Copy data from the src array to the dst array * @param[in] src The src array with or without halos * @param[in] dst The dst array with or without halos */ - template - void copy( const Array& src, Array& dst ); - + template + void copy(const Array &src, Array &dst); private: Utilities::MPI comm; RankInfoStruct info; - std::array n, ng; + std::array n, ng; int depth; bool fill_pattern[3][3][3]; int tag[3][3][3]; @@ -114,266 +110,286 @@ private: TYPE *mem; TYPE *send[3][3][3], *recv[3][3][3]; MPI_Request send_req[3][3][3], recv_req[3][3][3]; - void pack( const Array& array, int i, int j, int k, TYPE *buffer ); - void unpack( Array& array, int i, int j, int k, const TYPE *buffer ); + void pack(const Array &array, int i, int j, int k, TYPE *buffer); + void unpack(Array &array, int i, int j, int k, const TYPE *buffer); }; - //*************************************************************************************** -inline void PackMeshData(const int *list, int count, double *sendbuf, double *data){ - // Fill in the phase ID values from neighboring processors - // This packs up the values that need to be sent from one processor to another - int idx,n; - for (idx=0; idx -Array redistribute( const RankInfoStruct& src_rank, const Array& src_data, - const RankInfoStruct& dst_rank, std::array dst_size, const Utilities::MPI& comm ) -{ - if ( comm.getSize() == 1 ) { - return src_data.subset( { 0, (size_t) dst_size[0]-1, 0, (size_t) dst_size[1]-1, 0, (size_t) dst_size[2]-1 } ); +template +Array +redistribute(const RankInfoStruct &src_rank, const Array &src_data, + const RankInfoStruct &dst_rank, std::array dst_size, + const Utilities::MPI &comm) { + if (comm.getSize() == 1) { + return src_data.subset({0, (size_t)dst_size[0] - 1, 0, + (size_t)dst_size[1] - 1, 0, + (size_t)dst_size[2] - 1}); } // Get the src size - std::array src_size; - int size0[3] = { (int) src_data.size(0), (int) src_data.size(1), (int) src_data.size(2) }; - comm.maxReduce( size0, src_size.data(), 3 ); - if ( !src_data.empty() ) - ASSERT( src_size[0] == size0[0] && src_size[1] == size0[1] && src_size[2] == size0[2] ); + std::array src_size; + int size0[3] = {(int)src_data.size(0), (int)src_data.size(1), + (int)src_data.size(2)}; + comm.maxReduce(size0, src_size.data(), 3); + if (!src_data.empty()) + ASSERT(src_size[0] == size0[0] && src_size[1] == size0[1] && + src_size[2] == size0[2]); // Check that dst_size matches on all ranks - comm.maxReduce( dst_size.data(), size0, 3 ); - ASSERT( dst_size[0] == size0[0] && dst_size[1] == size0[1] && dst_size[2] == size0[2] ); + comm.maxReduce(dst_size.data(), size0, 3); + ASSERT(dst_size[0] == size0[0] && dst_size[1] == size0[1] && + dst_size[2] == size0[2]); // Function to get overlap range - auto calcOverlap = []( int i1[3], int i2[3], int j1[3], int j2[3] ) { + auto calcOverlap = [](int i1[3], int i2[3], int j1[3], int j2[3]) { std::vector index; - if ( i1[0] > j2[0] || i2[0] < j1[0] || i1[1] > j2[1] || i2[1] < j1[1] || i1[2] > j2[2] || i2[2] < j1[2] ) + if (i1[0] > j2[0] || i2[0] < j1[0] || i1[1] > j2[1] || i2[1] < j1[1] || + i1[2] > j2[2] || i2[2] < j1[2]) return index; - index.resize( 6 ); - index[0] = std::max( j1[0] - i1[0], 0 ); - index[1] = std::min( j2[0] - i1[0], i2[0] - i1[0] ); - index[2] = std::max( j1[1] - i1[1], 0 ); - index[3] = std::min( j2[1] - i1[1], i2[1] - i1[1] ); - index[4] = std::max( j1[2] - i1[2], 0 ); - index[5] = std::min( j2[2] - i1[2], i2[2] - i1[2] ); + index.resize(6); + index[0] = std::max(j1[0] - i1[0], 0); + index[1] = std::min(j2[0] - i1[0], i2[0] - i1[0]); + index[2] = std::max(j1[1] - i1[1], 0); + index[3] = std::min(j2[1] - i1[1], i2[1] - i1[1]); + index[4] = std::max(j1[2] - i1[2], 0); + index[5] = std::min(j2[2] - i1[2], i2[2] - i1[2]); return index; }; // Pack and send my data to the appropriate ranks (including myself) std::vector send_rank; std::vector> send_data; - if ( !src_data.empty() ) { - int i1[3] = { src_size[0] * src_rank.ix, src_size[1] * src_rank.jy, src_size[2] * src_rank.kz }; - int i2[3] = { i1[0] + src_size[0] - 1, i1[1] + src_size[1] - 1, i1[2] + src_size[2] - 1 }; - for ( int i=0; i send_request( send_rank.size() ); - for (size_t i=0; i send_request(send_rank.size()); + for (size_t i = 0; i < send_rank.size(); i++) + send_request[i] = comm.Isend(send_data[i].data(), send_data[i].length(), + send_rank[i], 5462); // Unpack data from the appropriate ranks (including myself) - Array dst_data( dst_size[0], dst_size[1], dst_size[2] ); - int i1[3] = { dst_size[0] * dst_rank.ix, dst_size[1] * dst_rank.jy, dst_size[2] * dst_rank.kz }; - int i2[3] = { i1[0] + dst_size[0] - 1, i1[1] + dst_size[1] - 1, i1[2] + dst_size[2] - 1 }; - for ( int i=0; i dst_data(dst_size[0], dst_size[1], dst_size[2]); + int i1[3] = {dst_size[0] * dst_rank.ix, dst_size[1] * dst_rank.jy, + dst_size[2] * dst_rank.kz}; + int i2[3] = {i1[0] + dst_size[0] - 1, i1[1] + dst_size[1] - 1, + i1[2] + dst_size[2] - 1}; + for (int i = 0; i < src_rank.nx; i++) { + for (int j = 0; j < src_rank.ny; j++) { + for (int k = 0; k < src_rank.nz; k++) { + int j1[3] = {i * src_size[0], j * src_size[1], k * src_size[2]}; + int j2[3] = {j1[0] + src_size[0] - 1, j1[1] + src_size[1] - 1, + j1[2] + src_size[2] - 1}; + auto index = calcOverlap(i1, i2, j1, j2); + if (index.empty()) continue; - int rank = src_rank.getRankForBlock(i,j,k); - Array data( index[1] - index[0] + 1, index[3] - index[2] + 1, index[5] - index[4] + 1 ); - comm.recv( data.data(), data.length(), rank, 5462 ); - dst_data.copySubset( index, data ); + int rank = src_rank.getRankForBlock(i, j, k); + Array data(index[1] - index[0] + 1, + index[3] - index[2] + 1, + index[5] - index[4] + 1); + comm.recv(data.data(), data.length(), rank, 5462); + dst_data.copySubset(index, data); } } } // Free data - comm.waitAll( send_request.size(), send_request.data() ); + comm.waitAll(send_request.size(), send_request.data()); return dst_data; } - - /******************************************************** * Structure to fill halo cells * ********************************************************/ -template -fillHalo::fillHalo( const Utilities::MPI& comm_, const RankInfoStruct& info_, - std::array n_, std::array ng_, int tag0, int depth_, - std::array fill, std::array periodic ): - comm(comm_), info(info_), n(n_), ng(ng_), depth(depth_) -{ +template +fillHalo::fillHalo(const Utilities::MPI &comm_, + const RankInfoStruct &info_, std::array n_, + std::array ng_, int tag0, int depth_, + std::array fill, std::array periodic) + : comm(comm_), info(info_), n(n_), ng(ng_), depth(depth_) { // Set the fill pattern - memset(fill_pattern,0,sizeof(fill_pattern)); - if ( fill[0] ) { + memset(fill_pattern, 0, sizeof(fill_pattern)); + if (fill[0]) { fill_pattern[0][1][1] = true; fill_pattern[2][1][1] = true; fill_pattern[1][0][1] = true; @@ -139,7 +154,7 @@ fillHalo::fillHalo( const Utilities::MPI& comm_, const RankInfoStruct& inf fill_pattern[1][1][0] = true; fill_pattern[1][1][2] = true; } - if ( fill[1] ) { + if (fill[1]) { fill_pattern[0][0][1] = true; fill_pattern[0][2][1] = true; fill_pattern[2][0][1] = true; @@ -153,7 +168,7 @@ fillHalo::fillHalo( const Utilities::MPI& comm_, const RankInfoStruct& inf fill_pattern[1][2][0] = true; fill_pattern[1][2][2] = true; } - if ( fill[2] ) { + if (fill[2]) { fill_pattern[0][0][0] = true; fill_pattern[0][0][2] = true; fill_pattern[0][2][0] = true; @@ -164,238 +179,233 @@ fillHalo::fillHalo( const Utilities::MPI& comm_, const RankInfoStruct& inf fill_pattern[2][2][2] = true; } // Remove communication for non-perioidic directions - if ( !periodic[0] && info.ix==0 ) { - for (int j=0; j<3; j++) { - for (int k=0; k<3; k++) + if (!periodic[0] && info.ix == 0) { + for (int j = 0; j < 3; j++) { + for (int k = 0; k < 3; k++) fill_pattern[0][j][k] = false; } } - if ( !periodic[0] && info.ix==info.nx-1 ) { - for (int j=0; j<3; j++) { - for (int k=0; k<3; k++) + if (!periodic[0] && info.ix == info.nx - 1) { + for (int j = 0; j < 3; j++) { + for (int k = 0; k < 3; k++) fill_pattern[2][j][k] = false; } } - if ( !periodic[1] && info.jy==0 ) { - for (int i=0; i<3; i++) { - for (int k=0; k<3; k++) + if (!periodic[1] && info.jy == 0) { + for (int i = 0; i < 3; i++) { + for (int k = 0; k < 3; k++) fill_pattern[i][0][k] = false; } } - if ( !periodic[1] && info.jy==info.ny-1 ) { - for (int i=0; i<3; i++) { - for (int k=0; k<3; k++) + if (!periodic[1] && info.jy == info.ny - 1) { + for (int i = 0; i < 3; i++) { + for (int k = 0; k < 3; k++) fill_pattern[i][2][k] = false; } } - if ( !periodic[2] && info.kz==0 ) { - for (int i=0; i<3; i++) { - for (int j=0; j<3; j++) + if (!periodic[2] && info.kz == 0) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) fill_pattern[i][j][0] = false; } } - if ( !periodic[2] && info.kz==info.nz-1 ) { - for (int i=0; i<3; i++) { - for (int j=0; j<3; j++) + if (!periodic[2] && info.kz == info.nz - 1) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) fill_pattern[i][j][2] = false; } } // Determine the number of elements for each send/recv - for (int i=0; i<3; i++) { - int ni = (i-1)==0 ? n[0]:ng[0]; - for (int j=0; j<3; j++) { - int nj = (j-1)==0 ? n[1]:ng[1]; - for (int k=0; k<3; k++) { - int nk = (k-1)==0 ? n[2]:ng[2]; - if ( fill_pattern[i][j][k] ) - N_send_recv[i][j][k] = ni*nj*nk; + for (int i = 0; i < 3; i++) { + int ni = (i - 1) == 0 ? n[0] : ng[0]; + for (int j = 0; j < 3; j++) { + int nj = (j - 1) == 0 ? n[1] : ng[1]; + for (int k = 0; k < 3; k++) { + int nk = (k - 1) == 0 ? n[2] : ng[2]; + if (fill_pattern[i][j][k]) + N_send_recv[i][j][k] = ni * nj * nk; else N_send_recv[i][j][k] = 0; } } } // Create send/recv buffers - size_t N_mem=0; - for (int i=0; i<3; i++) { - for (int j=0; j<3; j++) { - for (int k=0; k<3; k++) + size_t N_mem = 0; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + for (int k = 0; k < 3; k++) N_mem += N_send_recv[i][j][k]; } } - mem = new TYPE[2*depth*N_mem]; + mem = new TYPE[2 * depth * N_mem]; size_t index = 0; - for (int i=0; i<3; i++) { - for (int j=0; j<3; j++) { - for (int k=0; k<3; k++) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + for (int k = 0; k < 3; k++) { send[i][j][k] = &mem[index]; - index += depth*N_send_recv[i][j][k]; + index += depth * N_send_recv[i][j][k]; recv[i][j][k] = &mem[index]; - index += depth*N_send_recv[i][j][k]; + index += depth * N_send_recv[i][j][k]; } } } // Create the tags - for (int i=0; i<3; i++) { - for (int j=0; j<3; j++) { - for (int k=0; k<3; k++) { - tag[i][j][k] = tag0 + i + j*3 + k*9; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + for (int k = 0; k < 3; k++) { + tag[i][j][k] = tag0 + i + j * 3 + k * 9; } } } - } -template -fillHalo::~fillHalo( ) -{ - delete [] mem; -} -template -void fillHalo::fill( Array& data ) -{ +template fillHalo::~fillHalo() { delete[] mem; } +template void fillHalo::fill(Array &data) { //PROFILE_START("fillHalo::fill",1); int depth2 = data.size(3); - ASSERT((int)data.size(0)==n[0]+2*ng[0]); - ASSERT((int)data.size(1)==n[1]+2*ng[1]); - ASSERT((int)data.size(2)==n[2]+2*ng[2]); - ASSERT(depth2<=depth); - ASSERT(data.ndim()==3||data.ndim()==4); + ASSERT((int)data.size(0) == n[0] + 2 * ng[0]); + ASSERT((int)data.size(1) == n[1] + 2 * ng[1]); + ASSERT((int)data.size(2) == n[2] + 2 * ng[2]); + ASSERT(depth2 <= depth); + ASSERT(data.ndim() == 3 || data.ndim() == 4); // Start the recieves - for (int i=0; i<3; i++) { - for (int j=0; j<3; j++) { - for (int k=0; k<3; k++) { - if ( !fill_pattern[i][j][k] ) + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + for (int k = 0; k < 3; k++) { + if (!fill_pattern[i][j][k]) continue; - recv_req[i][j][k] = comm.Irecv( recv[i][j][k], depth2*N_send_recv[i][j][k], - info.rank[i][j][k], tag[2-i][2-j][2-k] ); + recv_req[i][j][k] = + comm.Irecv(recv[i][j][k], depth2 * N_send_recv[i][j][k], + info.rank[i][j][k], tag[2 - i][2 - j][2 - k]); } } } // Pack the src data and start the sends - for (int i=0; i<3; i++) { - for (int j=0; j<3; j++) { - for (int k=0; k<3; k++) { - if ( !fill_pattern[i][j][k] ) + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + for (int k = 0; k < 3; k++) { + if (!fill_pattern[i][j][k]) continue; - pack( data, i-1, j-1, k-1, send[i][j][k] ); - send_req[i][j][k] = comm.Isend( send[i][j][k], depth2*N_send_recv[i][j][k], - info.rank[i][j][k], tag[i][j][k] ); + pack(data, i - 1, j - 1, k - 1, send[i][j][k]); + send_req[i][j][k] = + comm.Isend(send[i][j][k], depth2 * N_send_recv[i][j][k], + info.rank[i][j][k], tag[i][j][k]); } } } // Recv the dst data and unpack (we recive in reverse order to match the sends) - for (int i=2; i>=0; i--) { - for (int j=2; j>=0; j--) { - for (int k=2; k>=0; k--) { - if ( !fill_pattern[i][j][k] ) + for (int i = 2; i >= 0; i--) { + for (int j = 2; j >= 0; j--) { + for (int k = 2; k >= 0; k--) { + if (!fill_pattern[i][j][k]) continue; - comm.wait( recv_req[i][j][k] ); - unpack( data, i-1, j-1, k-1, recv[i][j][k] ); + comm.wait(recv_req[i][j][k]); + unpack(data, i - 1, j - 1, k - 1, recv[i][j][k]); } } } // Wait until all sends have completed - for (int i=0; i<3; i++) { - for (int j=0; j<3; j++) { - for (int k=0; k<3; k++) { - if ( !fill_pattern[i][j][k] ) + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + for (int k = 0; k < 3; k++) { + if (!fill_pattern[i][j][k]) continue; - comm.wait( send_req[i][j][k] ); + comm.wait(send_req[i][j][k]); } } } //PROFILE_STOP("fillHalo::fill",1); } -template -void fillHalo::pack( const Array& data, int i0, int j0, int k0, TYPE *buffer ) -{ +template +void fillHalo::pack(const Array &data, int i0, int j0, int k0, + TYPE *buffer) { int depth2 = data.size(3); - int ni = i0==0 ? n[0]:ng[0]; - int nj = j0==0 ? n[1]:ng[1]; - int nk = k0==0 ? n[2]:ng[2]; - int is = i0==0 ? ng[0]:((i0==-1)?ng[0]:n[0]); - int js = j0==0 ? ng[1]:((j0==-1)?ng[1]:n[1]); - int ks = k0==0 ? ng[2]:((k0==-1)?ng[2]:n[2]); - for (int d=0; d -void fillHalo::unpack( Array& data, int i0, int j0, int k0, const TYPE *buffer ) -{ +template +void fillHalo::unpack(Array &data, int i0, int j0, int k0, + const TYPE *buffer) { int depth2 = data.size(3); - int ni = i0==0 ? n[0]:ng[0]; - int nj = j0==0 ? n[1]:ng[1]; - int nk = k0==0 ? n[2]:ng[2]; - int is = i0==0 ? ng[0]:((i0==-1)?0:n[0]+ng[0]); - int js = j0==0 ? ng[1]:((j0==-1)?0:n[1]+ng[1]); - int ks = k0==0 ? ng[2]:((k0==-1)?0:n[2]+ng[2]); - for (int d=0; d -template -void fillHalo::copy( const Array& src, Array& dst ) -{ +template +template +void fillHalo::copy(const Array &src, Array &dst) { //PROFILE_START("fillHalo::copy",1); - ASSERT( (int)src.size(0)==n[0] || (int)src.size(0)==n[0]+2*ng[0] ); - ASSERT( (int)dst.size(0)==n[0] || (int)dst.size(0)==n[0]+2*ng[0] ); - bool src_halo = (int)src.size(0)==n[0]+2*ng[0]; - bool dst_halo = (int)dst.size(0)==n[0]+2*ng[0]; - if ( src_halo ) { - ASSERT((int)src.size(0)==n[0]+2*ng[0]); - ASSERT((int)src.size(1)==n[1]+2*ng[1]); - ASSERT((int)src.size(2)==n[2]+2*ng[2]); + ASSERT((int)src.size(0) == n[0] || (int)src.size(0) == n[0] + 2 * ng[0]); + ASSERT((int)dst.size(0) == n[0] || (int)dst.size(0) == n[0] + 2 * ng[0]); + bool src_halo = (int)src.size(0) == n[0] + 2 * ng[0]; + bool dst_halo = (int)dst.size(0) == n[0] + 2 * ng[0]; + if (src_halo) { + ASSERT((int)src.size(0) == n[0] + 2 * ng[0]); + ASSERT((int)src.size(1) == n[1] + 2 * ng[1]); + ASSERT((int)src.size(2) == n[2] + 2 * ng[2]); } else { - ASSERT((int)src.size(0)==n[0]); - ASSERT((int)src.size(1)==n[1]); - ASSERT((int)src.size(2)==n[2]); + ASSERT((int)src.size(0) == n[0]); + ASSERT((int)src.size(1) == n[1]); + ASSERT((int)src.size(2) == n[2]); } - if ( dst_halo ) { - ASSERT((int)dst.size(0)==n[0]+2*ng[0]); - ASSERT((int)dst.size(1)==n[1]+2*ng[1]); - ASSERT((int)dst.size(2)==n[2]+2*ng[2]); + if (dst_halo) { + ASSERT((int)dst.size(0) == n[0] + 2 * ng[0]); + ASSERT((int)dst.size(1) == n[1] + 2 * ng[1]); + ASSERT((int)dst.size(2) == n[2] + 2 * ng[2]); } else { - ASSERT((int)dst.size(0)==n[0]); - ASSERT((int)dst.size(1)==n[1]); - ASSERT((int)dst.size(2)==n[2]); + ASSERT((int)dst.size(0) == n[0]); + ASSERT((int)dst.size(1) == n[1]); + ASSERT((int)dst.size(2) == n[2]); } - if ( src_halo == dst_halo ) { + if (src_halo == dst_halo) { // Src and dst halos match - for (size_t i=0; i::copy( const Array& src, Array& dst ) //PROFILE_STOP("fillHalo::copy",1); } - #endif diff --git a/common/Database.cpp b/common/Database.cpp index 9d73d5ef..8ef80914 100644 --- a/common/Database.cpp +++ b/common/Database.cpp @@ -24,303 +24,268 @@ #include #include - /******************************************************************** * Constructors/destructor * ********************************************************************/ -Database::Database() = default; +Database::Database() = default; Database::~Database() = default; -Database::Database( const Database& rhs ) : KeyData( rhs ) -{ +Database::Database(const Database &rhs) : KeyData(rhs) { d_data.clear(); - for ( const auto& tmp : rhs.d_data ) - putData( tmp.first, tmp.second->clone() ); + for (const auto &tmp : rhs.d_data) + putData(tmp.first, tmp.second->clone()); } -Database& Database::operator=( const Database& rhs ) -{ - if ( this == &rhs ) +Database &Database::operator=(const Database &rhs) { + if (this == &rhs) return *this; d_data.clear(); - for ( const auto& tmp : rhs.d_data ) - putData( tmp.first, tmp.second->clone() ); + for (const auto &tmp : rhs.d_data) + putData(tmp.first, tmp.second->clone()); return *this; } -Database::Database( Database&& rhs ) { std::swap( d_data, rhs.d_data ); } -Database& Database::operator=( Database&& rhs ) -{ - if ( this != &rhs ) - std::swap( d_data, rhs.d_data ); +Database::Database(Database &&rhs) { std::swap(d_data, rhs.d_data); } +Database &Database::operator=(Database &&rhs) { + if (this != &rhs) + std::swap(d_data, rhs.d_data); return *this; } - /******************************************************************** * Clone the database * ********************************************************************/ std::shared_ptr Database::clone() const { return cloneDatabase(); } -std::shared_ptr Database::cloneDatabase() const -{ +std::shared_ptr Database::cloneDatabase() const { auto db = std::make_shared(); - for ( const auto& tmp : d_data ) - db->putData( tmp.first, tmp.second->clone() ); + for (const auto &tmp : d_data) + db->putData(tmp.first, tmp.second->clone()); return db; } - /******************************************************************** * Get the data object * ********************************************************************/ -bool Database::keyExists( const std::string& key ) const -{ - return d_data.find( key ) != d_data.end(); +bool Database::keyExists(const std::string &key) const { + return d_data.find(key) != d_data.end(); } -std::shared_ptr Database::getData( const std::string& key ) -{ - auto it = d_data.find( key ); - if ( it == d_data.end() ) { +std::shared_ptr Database::getData(const std::string &key) { + auto it = d_data.find(key); + if (it == d_data.end()) { char msg[1000]; - sprintf( msg, "Variable %s was not found in database", key.c_str() ); - ERROR( msg ); + sprintf(msg, "Variable %s was not found in database", key.c_str()); + ERROR(msg); } return it->second; } -std::shared_ptr Database::getData( const std::string& key ) const -{ - return const_cast( this )->getData( key ); +std::shared_ptr Database::getData(const std::string &key) const { + return const_cast(this)->getData(key); } -bool Database::isDatabase( const std::string& key ) const -{ - auto ptr = getData( key ); - auto ptr2 = std::dynamic_pointer_cast( ptr ); +bool Database::isDatabase(const std::string &key) const { + auto ptr = getData(key); + auto ptr2 = std::dynamic_pointer_cast(ptr); return ptr2 != nullptr; } -std::shared_ptr Database::getDatabase( const std::string& key ) -{ - std::shared_ptr ptr = getData( key ); - std::shared_ptr ptr2 = std::dynamic_pointer_cast( ptr ); - if ( ptr2 == nullptr ) { +std::shared_ptr Database::getDatabase(const std::string &key) { + std::shared_ptr ptr = getData(key); + std::shared_ptr ptr2 = std::dynamic_pointer_cast(ptr); + if (ptr2 == nullptr) { char msg[1000]; - sprintf( msg, "Variable %s is not a database", key.c_str() ); - ERROR( msg ); + sprintf(msg, "Variable %s is not a database", key.c_str()); + ERROR(msg); } return ptr2; } -std::shared_ptr Database::getDatabase( const std::string& key ) const -{ - return const_cast( this )->getDatabase( key ); +std::shared_ptr +Database::getDatabase(const std::string &key) const { + return const_cast(this)->getDatabase(key); } -std::vector Database::getAllKeys() const -{ +std::vector Database::getAllKeys() const { std::vector keys; - keys.reserve( d_data.size() ); - for ( const auto& it : d_data ) - keys.push_back( it.first ); + keys.reserve(d_data.size()); + for (const auto &it : d_data) + keys.push_back(it.first); return keys; } -void Database::putDatabase( const std::string& key, std::shared_ptr db ) -{ - d_data[key] = std::move( db ); +void Database::putDatabase(const std::string &key, + std::shared_ptr db) { + d_data[key] = std::move(db); } -void Database::putData( const std::string& key, std::shared_ptr data ) -{ - d_data[key] = std::move( data ); +void Database::putData(const std::string &key, std::shared_ptr data) { + d_data[key] = std::move(data); } - /******************************************************************** * Is the data of the given type * ********************************************************************/ -template<> -bool Database::isType( const std::string& key ) const -{ - auto type = getData( key )->type(); +template <> bool Database::isType(const std::string &key) const { + auto type = getData(key)->type(); return type == "double"; } -template<> -bool Database::isType( const std::string& key ) const -{ - auto type = getData( key )->type(); +template <> bool Database::isType(const std::string &key) const { + auto type = getData(key)->type(); return type == "double"; } -template<> -bool Database::isType( const std::string& key ) const -{ +template <> bool Database::isType(const std::string &key) const { bool pass = true; - auto type = getData( key )->type(); - if ( type == "double" ) { - auto data = getVector( key ); - for ( auto tmp : data ) - pass = pass && static_cast( static_cast( tmp ) ) == tmp; + auto type = getData(key)->type(); + if (type == "double") { + auto data = getVector(key); + for (auto tmp : data) + pass = pass && static_cast(static_cast(tmp)) == tmp; } else { pass = false; } return pass; } -template<> -bool Database::isType( const std::string& key ) const -{ - auto type = getData( key )->type(); +template <> bool Database::isType(const std::string &key) const { + auto type = getData(key)->type(); return type == "string"; } -template<> -bool Database::isType( const std::string& key ) const -{ - auto type = getData( key )->type(); +template <> bool Database::isType(const std::string &key) const { + auto type = getData(key)->type(); return type == "bool"; } - /******************************************************************** * Get a vector * ********************************************************************/ -template<> -std::vector Database::getVector( - const std::string& key, const Units& ) const -{ - std::shared_ptr ptr = getData( key ); - if ( std::dynamic_pointer_cast( ptr ) ) +template <> +std::vector +Database::getVector(const std::string &key, const Units &) const { + std::shared_ptr ptr = getData(key); + if (std::dynamic_pointer_cast(ptr)) return std::vector(); - const auto* ptr2 = dynamic_cast( ptr.get() ); - if ( ptr2 == nullptr ) { - ERROR( "Key '" + key + "' is not a string" ); + const auto *ptr2 = dynamic_cast(ptr.get()); + if (ptr2 == nullptr) { + ERROR("Key '" + key + "' is not a string"); } return ptr2->d_data; } -template<> -std::vector Database::getVector( const std::string& key, const Units& ) const -{ - std::shared_ptr ptr = getData( key ); - if ( std::dynamic_pointer_cast( ptr ) ) +template <> +std::vector Database::getVector(const std::string &key, + const Units &) const { + std::shared_ptr ptr = getData(key); + if (std::dynamic_pointer_cast(ptr)) return std::vector(); - const auto* ptr2 = dynamic_cast( ptr.get() ); - if ( ptr2 == nullptr ) { - ERROR( "Key '" + key + "' is not a bool" ); + const auto *ptr2 = dynamic_cast(ptr.get()); + if (ptr2 == nullptr) { + ERROR("Key '" + key + "' is not a bool"); } return ptr2->d_data; } -template -std::vector Database::getVector( const std::string& key, const Units& unit ) const -{ - std::shared_ptr ptr = getData( key ); - if ( std::dynamic_pointer_cast( ptr ) ) +template +std::vector Database::getVector(const std::string &key, + const Units &unit) const { + std::shared_ptr ptr = getData(key); + if (std::dynamic_pointer_cast(ptr)) return std::vector(); std::vector data; - if ( std::dynamic_pointer_cast( ptr ) ) { - const auto* ptr2 = dynamic_cast( ptr.get() ); - const std::vector& data2 = ptr2->d_data; - double factor = 1; - if ( !unit.isNull() ) { - INSIST( !ptr2->d_unit.isNull(), "Field " + key + " must have units" ); - factor = ptr2->d_unit.convert( unit ); - INSIST( factor != 0, "Unit conversion failed" ); + if (std::dynamic_pointer_cast(ptr)) { + const auto *ptr2 = dynamic_cast(ptr.get()); + const std::vector &data2 = ptr2->d_data; + double factor = 1; + if (!unit.isNull()) { + INSIST(!ptr2->d_unit.isNull(), "Field " + key + " must have units"); + factor = ptr2->d_unit.convert(unit); + INSIST(factor != 0, "Unit conversion failed"); } - data.resize( data2.size() ); - for ( size_t i = 0; i < data2.size(); i++ ) - data[i] = static_cast( factor * data2[i] ); - } else if ( std::dynamic_pointer_cast( ptr ) ) { - ERROR( "Converting std::string to another type" ); - } else if ( std::dynamic_pointer_cast( ptr ) ) { - ERROR( "Converting std::bool to another type" ); + data.resize(data2.size()); + for (size_t i = 0; i < data2.size(); i++) + data[i] = static_cast(factor * data2[i]); + } else if (std::dynamic_pointer_cast(ptr)) { + ERROR("Converting std::string to another type"); + } else if (std::dynamic_pointer_cast(ptr)) { + ERROR("Converting std::bool to another type"); } else { - ERROR( "Unable to convert data format" ); + ERROR("Unable to convert data format"); } return data; } - /******************************************************************** * Put a vector * ********************************************************************/ -template<> -void Database::putVector( - const std::string& key, const std::vector& data, const Units& ) -{ - std::shared_ptr ptr( new KeyDataString() ); +template <> +void Database::putVector(const std::string &key, + const std::vector &data, + const Units &) { + std::shared_ptr ptr(new KeyDataString()); ptr->d_data = data; d_data[key] = ptr; } -template<> -void Database::putVector( - const std::string& key, const std::vector& data, const Units& ) -{ - std::shared_ptr ptr( new KeyDataBool() ); +template <> +void Database::putVector(const std::string &key, + const std::vector &data, const Units &) { + std::shared_ptr ptr(new KeyDataBool()); ptr->d_data = data; d_data[key] = ptr; } -template -void Database::putVector( const std::string& key, const std::vector& data, const Units& unit ) -{ - std::shared_ptr ptr( new KeyDataDouble() ); +template +void Database::putVector(const std::string &key, const std::vector &data, + const Units &unit) { + std::shared_ptr ptr(new KeyDataDouble()); ptr->d_unit = unit; - ptr->d_data.resize( data.size() ); - for ( size_t i = 0; i < data.size(); i++ ) - ptr->d_data[i] = static_cast( data[i] ); + ptr->d_data.resize(data.size()); + for (size_t i = 0; i < data.size(); i++) + ptr->d_data[i] = static_cast(data[i]); d_data[key] = ptr; } - /******************************************************************** * Print the database * ********************************************************************/ -void Database::print( std::ostream& os, const std::string& indent ) const -{ - for ( const auto& it : d_data ) { +void Database::print(std::ostream &os, const std::string &indent) const { + for (const auto &it : d_data) { os << indent << it.first; - if ( dynamic_cast( it.second.get() ) ) { - const auto* db = dynamic_cast( it.second.get() ); + if (dynamic_cast(it.second.get())) { + const auto *db = dynamic_cast(it.second.get()); os << " {\n"; - db->print( os, indent + " " ); + db->print(os, indent + " "); os << indent << "}\n"; } else { os << " = "; - it.second->print( os, "" ); + it.second->print(os, ""); } } } -std::string Database::print( const std::string& indent ) const -{ +std::string Database::print(const std::string &indent) const { std::stringstream ss; - print( ss, indent ); + print(ss, indent); return ss.str(); } - /******************************************************************** * Read input database file * ********************************************************************/ -Database::Database( const std::string& filename ) -{ +Database::Database(const std::string &filename) { // Read the input file into memory - FILE* fid = fopen( filename.c_str(), "rb" ); - if ( fid == nullptr ) - ERROR( "Error opening file " + filename ); - fseek( fid, 0, SEEK_END ); - size_t bytes = ftell( fid ); - rewind( fid ); - auto* buffer = new char[bytes + 4]; - size_t result = fread( buffer, 1, bytes, fid ); - fclose( fid ); - if ( result != bytes ) - ERROR( "Error reading file " + filename ); + FILE *fid = fopen(filename.c_str(), "rb"); + if (fid == nullptr) + ERROR("Error opening file " + filename); + fseek(fid, 0, SEEK_END); + size_t bytes = ftell(fid); + rewind(fid); + auto *buffer = new char[bytes + 4]; + size_t result = fread(buffer, 1, bytes, fid); + fclose(fid); + if (result != bytes) + ERROR("Error reading file " + filename); buffer[bytes + 0] = '\n'; buffer[bytes + 1] = '}'; buffer[bytes + 2] = '\n'; buffer[bytes + 3] = 0; // Create the database entries - loadDatabase( buffer, *this ); + loadDatabase(buffer, *this); // Free temporary memory delete[] buffer; } -std::shared_ptr Database::createFromString( const std::string& data ) -{ - std::shared_ptr db( new Database() ); - auto* buffer = new char[data.size() + 4]; - memcpy( buffer, data.data(), data.size() ); +std::shared_ptr Database::createFromString(const std::string &data) { + std::shared_ptr db(new Database()); + auto *buffer = new char[data.size() + 4]; + memcpy(buffer, data.data(), data.size()); buffer[data.size() + 0] = '\n'; buffer[data.size() + 1] = '}'; buffer[data.size() + 2] = '\n'; buffer[data.size() + 3] = 0; - loadDatabase( buffer, *db ); + loadDatabase(buffer, *db); delete[] buffer; return db; } @@ -335,279 +300,285 @@ enum class token_type { end_bracket, end }; -inline size_t length( token_type type ) -{ +inline size_t length(token_type type) { size_t len = 0; - if ( type == token_type::newline || type == token_type::quote || type == token_type::equal || - type == token_type::bracket || type == token_type::end_bracket || - type == token_type::end ) { + if (type == token_type::newline || type == token_type::quote || + type == token_type::equal || type == token_type::bracket || + type == token_type::end_bracket || type == token_type::end) { len = 1; - } else if ( type == token_type::line_comment || type == token_type::block_start || - type == token_type::block_stop ) { + } else if (type == token_type::line_comment || + type == token_type::block_start || + type == token_type::block_stop) { len = 2; } return len; } -inline std::tuple find_next_token( const char* buffer ) -{ +inline std::tuple find_next_token(const char *buffer) { size_t i = 0; - while ( true ) { - if ( buffer[i] == '\n' || buffer[i] == '\r' ) { - return std::pair( i + 1, token_type::newline ); - } else if ( buffer[i] == 0 ) { - return std::pair( i + 1, token_type::end ); - } else if ( buffer[i] == '"' ) { - return std::pair( i + 1, token_type::quote ); - } else if ( buffer[i] == '=' ) { - return std::pair( i + 1, token_type::equal ); - } else if ( buffer[i] == '{' ) { - return std::pair( i + 1, token_type::bracket ); - } else if ( buffer[i] == '}' ) { - return std::pair( i + 1, token_type::end_bracket ); - } else if ( buffer[i] == '/' ) { - if ( buffer[i + 1] == '/' ) { - return std::pair( i + 2, token_type::line_comment ); - } else if ( buffer[i + 1] == '*' ) { - return std::pair( i + 2, token_type::block_start ); + while (true) { + if (buffer[i] == '\n' || buffer[i] == '\r') { + return std::pair(i + 1, token_type::newline); + } else if (buffer[i] == 0) { + return std::pair(i + 1, token_type::end); + } else if (buffer[i] == '"') { + return std::pair(i + 1, token_type::quote); + } else if (buffer[i] == '=') { + return std::pair(i + 1, token_type::equal); + } else if (buffer[i] == '{') { + return std::pair(i + 1, token_type::bracket); + } else if (buffer[i] == '}') { + return std::pair(i + 1, + token_type::end_bracket); + } else if (buffer[i] == '/') { + if (buffer[i + 1] == '/') { + return std::pair(i + 2, + token_type::line_comment); + } else if (buffer[i + 1] == '*') { + return std::pair(i + 2, + token_type::block_start); } - } else if ( buffer[i] == '*' ) { - if ( buffer[i + 1] == '/' ) - return std::pair( i + 2, token_type::block_stop ); + } else if (buffer[i] == '*') { + if (buffer[i + 1] == '/') + return std::pair(i + 2, + token_type::block_stop); } i++; } - return std::pair( 0, token_type::end ); + return std::pair(0, token_type::end); } -inline std::string deblank( const std::string& str ) -{ +inline std::string deblank(const std::string &str) { size_t i1 = 0xFFFFFFF, i2 = 0; - for ( size_t i = 0; i < str.size(); i++ ) { - if ( str[i] != ' ' ) { - i1 = std::min( i1, i ); - i2 = std::max( i2, i ); + for (size_t i = 0; i < str.size(); i++) { + if (str[i] != ' ') { + i1 = std::min(i1, i); + i2 = std::max(i2, i); } } - return i1 <= i2 ? str.substr( i1, i2 - i1 + 1 ) : std::string(); + return i1 <= i2 ? str.substr(i1, i2 - i1 + 1) : std::string(); } -size_t skip_comment( const char* buffer ) -{ - auto tmp = find_next_token( buffer ); - const token_type end_comment = ( std::get<1>( tmp ) == token_type::line_comment ) ? - token_type::newline : - token_type::block_stop; +size_t skip_comment(const char *buffer) { + auto tmp = find_next_token(buffer); + const token_type end_comment = + (std::get<1>(tmp) == token_type::line_comment) ? token_type::newline + : token_type::block_stop; size_t pos = 0; - while ( std::get<1>( tmp ) != end_comment ) { - if ( std::get<1>( tmp ) == token_type::end ) - ERROR( "Encountered end of file before block comment end" ); - pos += std::get<0>( tmp ); - tmp = find_next_token( &buffer[pos] ); + while (std::get<1>(tmp) != end_comment) { + if (std::get<1>(tmp) == token_type::end) + ERROR("Encountered end of file before block comment end"); + pos += std::get<0>(tmp); + tmp = find_next_token(&buffer[pos]); } - pos += std::get<0>( tmp ); + pos += std::get<0>(tmp); return pos; } -inline std::string lower( const std::string& str ) -{ - std::string tmp( str ); - std::transform( tmp.begin(), tmp.end(), tmp.begin(), ::tolower ); +inline std::string lower(const std::string &str) { + std::string tmp(str); + std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower); return tmp; } -static std::tuple> read_value( - const char* buffer, const std::string& key ) -{ +static std::tuple> +read_value(const char *buffer, const std::string &key) { // Get the value as a std::string - size_t pos = 0; - token_type type = token_type::end; - std::tie( pos, type ) = find_next_token( &buffer[pos] ); - size_t len = pos - length( type ); - while ( type != token_type::newline ) { - if ( type == token_type::quote ) { - size_t i = 0; - std::tie( i, type ) = find_next_token( &buffer[pos] ); + size_t pos = 0; + token_type type = token_type::end; + std::tie(pos, type) = find_next_token(&buffer[pos]); + size_t len = pos - length(type); + while (type != token_type::newline) { + if (type == token_type::quote) { + size_t i = 0; + std::tie(i, type) = find_next_token(&buffer[pos]); pos += i; - while ( type != token_type::quote ) { - ASSERT( type != token_type::end ); - std::tie( i, type ) = find_next_token( &buffer[pos] ); + while (type != token_type::quote) { + ASSERT(type != token_type::end); + std::tie(i, type) = find_next_token(&buffer[pos]); pos += i; } - } else if ( type == token_type::line_comment || type == token_type::block_start ) { - len = pos - length( type ); - pos += skip_comment( &buffer[pos - length( type )] ) - length( type ); + } else if (type == token_type::line_comment || + type == token_type::block_start) { + len = pos - length(type); + pos += skip_comment(&buffer[pos - length(type)]) - length(type); break; } - size_t i = 0; - std::tie( i, type ) = find_next_token( &buffer[pos] ); + size_t i = 0; + std::tie(i, type) = find_next_token(&buffer[pos]); pos += i; - len = pos - length( type ); + len = pos - length(type); } - const std::string value = deblank( std::string( buffer, len ) ); + const std::string value = deblank(std::string(buffer, len)); // Split the value to an array of values std::vector values; size_t i0 = 0, i = 0, count = 0; - for ( ; i < value.size(); i++ ) { - if ( value[i] == '"' ) { + for (; i < value.size(); i++) { + if (value[i] == '"') { count++; - } else if ( value[i] == ',' && count % 2 == 0 ) { - values.push_back( deblank( value.substr( i0, i - i0 ) ) ); + } else if (value[i] == ',' && count % 2 == 0) { + values.push_back(deblank(value.substr(i0, i - i0))); i0 = i + 1; } } - values.push_back( deblank( value.substr( i0 ) ) ); + values.push_back(deblank(value.substr(i0))); // Convert the string value to the database value std::shared_ptr data; - if ( value.empty() ) { - data.reset( new EmptyKeyData() ); - } else if ( value.find( '"' ) != std::string::npos ) { - auto* data2 = new KeyDataString(); - data.reset( data2 ); - data2->d_data.resize( values.size() ); - for ( size_t i = 0; i < values.size(); i++ ) { - ASSERT( values[i].size() >= 2 ); - ASSERT( values[i][0] == '"' && values[i][values[i].size() - 1] == '"' ); - data2->d_data[i] = values[i].substr( 1, values[i].size() - 2 ); + if (value.empty()) { + data.reset(new EmptyKeyData()); + } else if (value.find('"') != std::string::npos) { + auto *data2 = new KeyDataString(); + data.reset(data2); + data2->d_data.resize(values.size()); + for (size_t i = 0; i < values.size(); i++) { + ASSERT(values[i].size() >= 2); + ASSERT(values[i][0] == '"' && + values[i][values[i].size() - 1] == '"'); + data2->d_data[i] = values[i].substr(1, values[i].size() - 2); } - } else if ( lower( value ) == "true" || lower( value ) == "false" ) { - auto* data2 = new KeyDataBool(); - data.reset( data2 ); - data2->d_data.resize( values.size() ); - for ( size_t i = 0; i < values.size(); i++ ) { - ASSERT( values[i].size() >= 2 ); - if ( lower( values[i] ) != "true" && lower( values[i] ) != "false" ) - ERROR( "Error converting " + key + " to logical array" ); - data2->d_data[i] = lower( values[i] ) == "true"; + } else if (lower(value) == "true" || lower(value) == "false") { + auto *data2 = new KeyDataBool(); + data.reset(data2); + data2->d_data.resize(values.size()); + for (size_t i = 0; i < values.size(); i++) { + ASSERT(values[i].size() >= 2); + if (lower(values[i]) != "true" && lower(values[i]) != "false") + ERROR("Error converting " + key + " to logical array"); + data2->d_data[i] = lower(values[i]) == "true"; } } else { // if ( value.find('.')!=std::string::npos || value.find('e')!=std::string::npos ) { - auto* data2 = new KeyDataDouble(); - data.reset( data2 ); - data2->d_data.resize( values.size(), 0 ); - for ( size_t i = 0; i < values.size(); i++ ) { + auto *data2 = new KeyDataDouble(); + data.reset(data2); + data2->d_data.resize(values.size(), 0); + for (size_t i = 0; i < values.size(); i++) { Units unit; - std::tie( data2->d_data[i], unit ) = KeyDataDouble::read( values[i] ); - if ( !unit.isNull() ) + std::tie(data2->d_data[i], unit) = KeyDataDouble::read(values[i]); + if (!unit.isNull()) data2->d_unit = unit; } //} else { // ERROR("Unable to determine data type: "+value); } - return std::tuple>( pos, data ); + return std::tuple>(pos, data); } -size_t Database::loadDatabase( const char* buffer, Database& db ) -{ +size_t Database::loadDatabase(const char *buffer, Database &db) { size_t pos = 0; - while ( true ) { + while (true) { size_t i; token_type type; - std::tie( i, type ) = find_next_token( &buffer[pos] ); - const std::string key = - deblank( std::string( &buffer[pos], std::max( i - length( type ), 1 ) - 1 ) ); - if ( type == token_type::line_comment || type == token_type::block_start ) { + std::tie(i, type) = find_next_token(&buffer[pos]); + const std::string key = deblank( + std::string(&buffer[pos], std::max(i - length(type), 1) - 1)); + if (type == token_type::line_comment || + type == token_type::block_start) { // Comment - INSIST( key.empty(), "Key should be empty: " + key ); - pos += skip_comment( &buffer[pos] ); - } else if ( type == token_type::newline ) { - INSIST( key.empty(), "Key should be empty: " + key ); + INSIST(key.empty(), "Key should be empty: " + key); + pos += skip_comment(&buffer[pos]); + } else if (type == token_type::newline) { + INSIST(key.empty(), "Key should be empty: " + key); pos += i; - } else if ( type == token_type::equal ) { + } else if (type == token_type::equal) { // Reading key/value pair - ASSERT( !key.empty() ); + ASSERT(!key.empty()); pos += i; std::shared_ptr data; - std::tie( i, data ) = read_value( &buffer[pos], key ); - ASSERT( data.get() != nullptr ); + std::tie(i, data) = read_value(&buffer[pos], key); + ASSERT(data.get() != nullptr); db.d_data[key] = data; pos += i; - } else if ( type == token_type::bracket ) { + } else if (type == token_type::bracket) { // Read database - ASSERT( !key.empty() ); + ASSERT(!key.empty()); pos += i; - std::shared_ptr database( new Database() ); - pos += loadDatabase( &buffer[pos], *database ); + std::shared_ptr database(new Database()); + pos += loadDatabase(&buffer[pos], *database); db.d_data[key] = database; - } else if ( type == token_type::end_bracket ) { + } else if (type == token_type::end_bracket) { // Finished with the database pos += i; break; } else { - ERROR( "Error loading data" ); + ERROR("Error loading data"); } } return pos; } - /******************************************************************** * Data type helper functions * ********************************************************************/ -void KeyDataDouble::print( std::ostream& os, const std::string& indent ) const -{ +void KeyDataDouble::print(std::ostream &os, const std::string &indent) const { os << indent; - for ( size_t i = 0; i < d_data.size(); i++ ) { - if ( i > 0 ) + for (size_t i = 0; i < d_data.size(); i++) { + if (i > 0) os << ", "; - if ( d_data[i] != d_data[i] ) { + if (d_data[i] != d_data[i]) { os << "nan"; - } else if ( d_data[i] == std::numeric_limits::infinity() ) { + } else if (d_data[i] == std::numeric_limits::infinity()) { os << "inf"; - } else if ( d_data[i] == -std::numeric_limits::infinity() ) { + } else if (d_data[i] == -std::numeric_limits::infinity()) { os << "-inf"; } else { - os << std::setprecision( 12 ) << d_data[i]; + os << std::setprecision(12) << d_data[i]; } } - if ( !d_unit.isNull() ) + if (!d_unit.isNull()) os << " " << d_unit.str(); os << std::endl; } -std::tuple KeyDataDouble::read( const std::string& str ) -{ - std::string tmp = deblank( str ); - size_t index = tmp.find( " " ); - if ( index != std::string::npos ) { - return std::make_tuple( - readValue( tmp.substr( 0, index ) ), Units( tmp.substr( index + 1 ) ) ); +std::tuple KeyDataDouble::read(const std::string &str) { + std::string tmp = deblank(str); + size_t index = tmp.find(" "); + if (index != std::string::npos) { + return std::make_tuple(readValue(tmp.substr(0, index)), + Units(tmp.substr(index + 1))); } else { - return std::make_tuple( readValue( tmp ), Units() ); + return std::make_tuple(readValue(tmp), Units()); } } -double KeyDataDouble::readValue( const std::string& str ) -{ - const std::string tmp = lower( str ); - double data = 0; - if ( tmp == "inf" || tmp == "infinity" ) { +double KeyDataDouble::readValue(const std::string &str) { + const std::string tmp = lower(str); + double data = 0; + if (tmp == "inf" || tmp == "infinity") { data = std::numeric_limits::infinity(); - } else if ( tmp == "-inf" || tmp == "-infinity" ) { + } else if (tmp == "-inf" || tmp == "-infinity") { data = -std::numeric_limits::infinity(); - } else if ( tmp == "nan" ) { + } else if (tmp == "nan") { data = std::numeric_limits::quiet_NaN(); - } else if ( tmp.find( '/' ) != std::string::npos ) { - ERROR( "Error reading value" ); + } else if (tmp.find('/') != std::string::npos) { + ERROR("Error reading value"); } else { - char* pos = nullptr; - data = strtod( tmp.c_str(), &pos ); - if ( static_cast( pos - tmp.c_str() ) == tmp.size() + 1 ) - ERROR( "Error reading value" ); + char *pos = nullptr; + data = strtod(tmp.c_str(), &pos); + if (static_cast(pos - tmp.c_str()) == tmp.size() + 1) + ERROR("Error reading value"); } return data; } - /******************************************************************** * Instantiations * ********************************************************************/ -template std::vector Database::getVector( const std::string&, const Units& ) const; -template std::vector Database::getVector( const std::string&, const Units& ) const; -template std::vector Database::getVector( const std::string&, const Units& ) const; -template std::vector Database::getVector( const std::string&, const Units& ) const; -template std::vector Database::getVector( const std::string&, const Units& ) const; -template void Database::putVector( - const std::string&, const std::vector&, const Units& ); -template void Database::putVector( const std::string&, const std::vector&, const Units& ); -template void Database::putVector( - const std::string&, const std::vector&, const Units& ); -template void Database::putVector( - const std::string&, const std::vector&, const Units& ); -template void Database::putVector( - const std::string&, const std::vector&, const Units& ); -template bool Database::isType( const std::string& ) const; -template bool Database::isType( const std::string& ) const; -template bool Database::isType( const std::string& ) const; -template bool Database::isType( const std::string& ) const; +template std::vector Database::getVector(const std::string &, + const Units &) const; +template std::vector Database::getVector(const std::string &, + const Units &) const; +template std::vector Database::getVector(const std::string &, + const Units &) const; +template std::vector Database::getVector(const std::string &, + const Units &) const; +template std::vector Database::getVector(const std::string &, + const Units &) const; +template void Database::putVector(const std::string &, + const std::vector &, + const Units &); +template void Database::putVector(const std::string &, + const std::vector &, const Units &); +template void Database::putVector(const std::string &, + const std::vector &, + const Units &); +template void Database::putVector(const std::string &, + const std::vector &, + const Units &); +template void Database::putVector(const std::string &, + const std::vector &, + const Units &); +template bool Database::isType(const std::string &) const; +template bool Database::isType(const std::string &) const; +template bool Database::isType(const std::string &) const; +template bool Database::isType(const std::string &) const; diff --git a/common/Database.h b/common/Database.h index 016868cf..0f14f360 100644 --- a/common/Database.h +++ b/common/Database.h @@ -26,17 +26,13 @@ #include "common/Units.h" - -inline bool exists( const std::string& filename ) -{ - std::ifstream domain( filename ); - return domain.good(); +inline bool exists(const std::string &filename) { + std::ifstream domain(filename); + return domain.good(); } - //! Base class to hold data of a given type -class KeyData -{ +class KeyData { protected: //! Empty constructor KeyData() {} @@ -47,19 +43,18 @@ public: //! Copy the data virtual std::shared_ptr clone() const = 0; //! Print the data to a stream - virtual void print( std::ostream& os, const std::string& indent = "" ) const = 0; + virtual void print(std::ostream &os, + const std::string &indent = "") const = 0; //! Return the native data type virtual std::string type() const = 0; protected: - KeyData( const KeyData& ) {} - KeyData& operator=( const KeyData& ); + KeyData(const KeyData &) {} + KeyData &operator=(const KeyData &); }; - //! Class to a database -class Database : public KeyData -{ +class Database : public KeyData { public: //! Empty constructor Database(); @@ -68,25 +63,25 @@ public: * Open an database file. * @param filename Name of input file to open */ - explicit Database( const std::string& filename ); + explicit Database(const std::string &filename); /** * Create database from string * @param data String containing the database data */ - static std::shared_ptr createFromString( const std::string& data ); + static std::shared_ptr createFromString(const std::string &data); //! Copy constructor - Database( const Database& ); + Database(const Database &); //! Assignment operator - Database& operator=( const Database& ); + Database &operator=(const Database &); //! Move constructor - Database( Database&& rhs ); + Database(Database &&rhs); //! Move assignment operator - Database& operator=( Database&& rhs ); + Database &operator=(Database &&rhs); //! Destructor virtual ~Database(); @@ -97,25 +92,21 @@ public: //! Copy the data std::shared_ptr cloneDatabase() const; - /** * Return true if the specified key exists in the database and false * otherwise. * @param[in] key Key name to lookup. */ - bool keyExists( const std::string& key ) const; - + bool keyExists(const std::string &key) const; /** * Return all keys in the database. */ std::vector getAllKeys() const; - //! Return the number of entries in the database size_t size() const { return d_data.size(); } - /** * Get the scalar entry from the database with the specified key * name. If the specified key does not exist in the database or @@ -125,14 +116,14 @@ public: * @param[in] key Key name in database. * @param[in] unit Desired units */ - template - inline TYPE getScalar( const std::string& key, const Units& unit = Units() ) const; - + template + inline TYPE getScalar(const std::string &key, + const Units &unit = Units()) const; /// @copydoc Database::getScalar(const std::string&,const Units&) const - template - inline TYPE getScalar( const std::string& key, const std::string& unit ) const; - + template + inline TYPE getScalar(const std::string &key, + const std::string &unit) const; /** * Get the scalar entry from the database with the specified key @@ -143,16 +134,14 @@ public: * @param[in] value Default value * @param[in] unit Desired units */ - template - inline TYPE getWithDefault( - const std::string& key, const TYPE& value, const Units& unit = Units() ) const; - + template + inline TYPE getWithDefault(const std::string &key, const TYPE &value, + const Units &unit = Units()) const; /// @copydoc Database::getWithDefault(const std::string&,const TYPE&,const Units&) const - template - inline TYPE getWithDefault( - const std::string& key, const TYPE& value, const std::string& unit ) const; - + template + inline TYPE getWithDefault(const std::string &key, const TYPE &value, + const std::string &unit) const; /** * Put the scalar entry into the database with the specified key name. @@ -160,9 +149,9 @@ public: * @param value Value to store * @param unit Desired units */ - template - inline void putScalar( const std::string& key, const TYPE& value, const Units& unit = Units() ); - + template + inline void putScalar(const std::string &key, const TYPE &value, + const Units &unit = Units()); /** * Put the scalar entry into the database with the specified key name. @@ -170,9 +159,9 @@ public: * @param value Value to store * @param unit Desired units */ - template - inline void putScalar( const std::string& key, const TYPE& value, const std::string& unit ); - + template + inline void putScalar(const std::string &key, const TYPE &value, + const std::string &unit); /** * Get the vector entries from the database with the specified key @@ -183,14 +172,14 @@ public: * @param key Key name in database. * @param unit Desired units */ - template - std::vector getVector( const std::string& key, const Units& unit = Units() ) const; - + template + std::vector getVector(const std::string &key, + const Units &unit = Units()) const; /// @copydoc Database::getVector(const std::string&,const Units&) const - template - inline std::vector getVector( const std::string& key, const std::string& unit ) const; - + template + inline std::vector getVector(const std::string &key, + const std::string &unit) const; /** * Put the vector entries into the database with the specified key @@ -202,16 +191,14 @@ public: * @param data Data to store * @param unit Desired units */ - template - void putVector( - const std::string& key, const std::vector& data, const Units& unit = Units() ); - + template + void putVector(const std::string &key, const std::vector &data, + const Units &unit = Units()); /// @copydoc Database::putVector(const std::string&,const std::vector&,const Units&) - template - inline void putVector( - const std::string& key, const std::vector& data, const std::string& unit ); - + template + inline void putVector(const std::string &key, const std::vector &data, + const std::string &unit); /** * Get the data for a key in the database. If the specified key @@ -220,7 +207,7 @@ public: * * @param key Key name in database. */ - std::shared_ptr getData( const std::string& key ); + std::shared_ptr getData(const std::string &key); /** * Get the data for a key in the database. If the specified key @@ -229,8 +216,7 @@ public: * * @param key Key name in database. */ - std::shared_ptr getData( const std::string& key ) const; - + std::shared_ptr getData(const std::string &key) const; /** * Put the data for a key in the database. @@ -238,17 +224,13 @@ public: * @param key Key name in database. * @param data Data to store */ - void putData( const std::string& key, std::shared_ptr data ); - + void putData(const std::string &key, std::shared_ptr data); // Check if the key is a database object - bool isDatabase( const std::string& key ) const; - + bool isDatabase(const std::string &key) const; // Check if the entry can be stored as the given type - template - bool isType( const std::string& key ) const; - + template bool isType(const std::string &key) const; /** * Get the database for a key in the database. If the specified key @@ -257,7 +239,7 @@ public: * * @param key Key name in database. */ - std::shared_ptr getDatabase( const std::string& key ); + std::shared_ptr getDatabase(const std::string &key); /** * Get the database for a key in the database. If the specified key @@ -266,8 +248,7 @@ public: * * @param key Key name in database. */ - std::shared_ptr getDatabase( const std::string& key ) const; - + std::shared_ptr getDatabase(const std::string &key) const; /** * Get the database for a key in the database. If the specified key @@ -277,36 +258,32 @@ public: * @param key Key name in database. * @param db Database to store */ - void putDatabase( const std::string& key, std::shared_ptr db ); - + void putDatabase(const std::string &key, std::shared_ptr db); /** * Print the data to a stream * @param os Output stream * @param indent Indenting to use before each line */ - virtual void print( std::ostream& os, const std::string& indent = "" ) const override; - + virtual void print(std::ostream &os, + const std::string &indent = "") const override; //! Print the type virtual std::string type() const override { return "database"; } - /** * Print the data to a string * @return Output string */ - std::string print( const std::string& indent = "" ) const; - + std::string print(const std::string &indent = "") const; protected: std::map> d_data; // Function to load a database from a buffer - static size_t loadDatabase( const char* buffer, Database& db ); + static size_t loadDatabase(const char *buffer, Database &db); }; - #include "common/Database.hpp" #endif diff --git a/common/Database.hpp b/common/Database.hpp index cc3899aa..a2162b25 100644 --- a/common/Database.hpp +++ b/common/Database.hpp @@ -38,66 +38,58 @@ #include - /******************************************************************** * Basic classes for primative data types * ********************************************************************/ -class EmptyKeyData : public KeyData -{ +class EmptyKeyData : public KeyData { public: EmptyKeyData() {} virtual ~EmptyKeyData() {} - virtual std::shared_ptr clone() const override - { + virtual std::shared_ptr clone() const override { return std::make_shared(); } - virtual void print( std::ostream& os, const std::string& = "" ) const override - { + virtual void print(std::ostream &os, + const std::string & = "") const override { os << std::endl; } virtual std::string type() const override { return ""; } }; -class KeyDataDouble : public KeyData -{ +class KeyDataDouble : public KeyData { public: KeyDataDouble() {} - explicit KeyDataDouble( const std::vector& data, const Units& unit ) - : d_data( data ), d_unit( unit ) - { - } + explicit KeyDataDouble(const std::vector &data, const Units &unit) + : d_data(data), d_unit(unit) {} virtual ~KeyDataDouble() {} - virtual std::shared_ptr clone() const override - { - return std::make_shared( d_data, d_unit ); + virtual std::shared_ptr clone() const override { + return std::make_shared(d_data, d_unit); } - virtual void print( std::ostream& os, const std::string& indent = "" ) const override; + virtual void print(std::ostream &os, + const std::string &indent = "") const override; virtual std::string type() const override { return "double"; } - static std::tuple read( const std::string& ); - static double readValue( const std::string& ); + static std::tuple read(const std::string &); + static double readValue(const std::string &); public: std::vector d_data; Units d_unit; }; -class KeyDataBool : public KeyData -{ +class KeyDataBool : public KeyData { public: KeyDataBool() {} - explicit KeyDataBool( const std::vector& data ) : d_data( data ) {} + explicit KeyDataBool(const std::vector &data) : d_data(data) {} virtual ~KeyDataBool() {} - virtual std::shared_ptr clone() const override - { - return std::make_shared( d_data ); + virtual std::shared_ptr clone() const override { + return std::make_shared(d_data); } - virtual void print( std::ostream& os, const std::string& indent = "" ) const override - { + virtual void print(std::ostream &os, + const std::string &indent = "") const override { os << indent; - for ( size_t i = 0; i < d_data.size(); i++ ) { - if ( i > 0 ) { + for (size_t i = 0; i < d_data.size(); i++) { + if (i > 0) { os << ", "; } - if ( d_data[i] ) { + if (d_data[i]) { os << "true"; } else { os << "false"; @@ -108,21 +100,20 @@ public: virtual std::string type() const override { return "bool"; } std::vector d_data; }; -class KeyDataString : public KeyData -{ +class KeyDataString : public KeyData { public: KeyDataString() {} - explicit KeyDataString( const std::vector& data ) : d_data( data ) {} + explicit KeyDataString(const std::vector &data) + : d_data(data) {} virtual ~KeyDataString() {} - virtual std::shared_ptr clone() const override - { - return std::make_shared( d_data ); + virtual std::shared_ptr clone() const override { + return std::make_shared(d_data); } - virtual void print( std::ostream& os, const std::string& indent = "" ) const override - { + virtual void print(std::ostream &os, + const std::string &indent = "") const override { os << indent; - for ( size_t i = 0; i < d_data.size(); i++ ) { - if ( i > 0 ) { + for (size_t i = 0; i < d_data.size(); i++) { + if (i > 0) { os << ", "; } os << '"' << d_data[i] << '"'; @@ -133,73 +124,66 @@ public: std::vector d_data; }; - /******************************************************************** * Get a vector * ********************************************************************/ -template -inline std::vector Database::getVector( - const std::string& key, const std::string& unit ) const -{ - return getVector( key, Units( unit ) ); +template +inline std::vector Database::getVector(const std::string &key, + const std::string &unit) const { + return getVector(key, Units(unit)); } -template -inline void Database::putVector( - const std::string& key, const std::vector& data, const std::string& unit ) -{ - putVector( key, data, Units( unit ) ); +template +inline void Database::putVector(const std::string &key, + const std::vector &data, + const std::string &unit) { + putVector(key, data, Units(unit)); } - /******************************************************************** * Get a scalar * ********************************************************************/ -template -inline TYPE Database::getScalar( const std::string& key, const Units& unit ) const -{ - const std::vector& data = getVector( key, unit ); - if ( data.size() != 1 ) { +template +inline TYPE Database::getScalar(const std::string &key, + const Units &unit) const { + const std::vector &data = getVector(key, unit); + if (data.size() != 1) { char msg[1000]; - sprintf( msg, "Variable %s is not a scalar", key.c_str() ); - ERROR( msg ); + sprintf(msg, "Variable %s is not a scalar", key.c_str()); + ERROR(msg); } return data[0]; } -template -inline TYPE Database::getWithDefault( - const std::string& key, const TYPE& value, const Units& unit ) const -{ - if ( !keyExists( key ) ) +template +inline TYPE Database::getWithDefault(const std::string &key, const TYPE &value, + const Units &unit) const { + if (!keyExists(key)) return value; - return getScalar( key, unit ); + return getScalar(key, unit); } -template -inline void Database::putScalar( const std::string& key, const TYPE& data, const Units& unit ) -{ - putVector( key, std::vector( 1, data ), unit ); +template +inline void Database::putScalar(const std::string &key, const TYPE &data, + const Units &unit) { + putVector(key, std::vector(1, data), unit); } -template -inline TYPE Database::getScalar( const std::string& key, const std::string& unit ) const -{ - return getScalar( key, Units( unit ) ); +template +inline TYPE Database::getScalar(const std::string &key, + const std::string &unit) const { + return getScalar(key, Units(unit)); } -template -inline TYPE Database::getWithDefault( - const std::string& key, const TYPE& value, const std::string& unit ) const -{ - return getWithDefault( key, value, Units( unit ) ); +template +inline TYPE Database::getWithDefault(const std::string &key, const TYPE &value, + const std::string &unit) const { + return getWithDefault(key, value, Units(unit)); } -template -inline void Database::putScalar( const std::string& key, const TYPE& data, const std::string& unit ) -{ - putScalar( key, data, Units( unit ) ); +template +inline void Database::putScalar(const std::string &key, const TYPE &data, + const std::string &unit) { + putScalar(key, data, Units(unit)); } -template -inline void putVector( - const std::string& key, const std::vector& data, const std::string& unit ) -{ - putVector( key, data, Units( unit ) ); +template +inline void putVector(const std::string &key, const std::vector &data, + const std::string &unit) { + putVector(key, data, Units(unit)); } - #endif diff --git a/common/Domain.cpp b/common/Domain.cpp index 23b6bf2c..2e808411 100644 --- a/common/Domain.cpp +++ b/common/Domain.cpp @@ -22,7 +22,7 @@ #include #include #include -#include // std::exception +#include // std::exception #include #include "common/Domain.h" @@ -32,1033 +32,1152 @@ #include "common/Communication.h" // Inline function to read line without a return argument -static inline void fgetl( char * str, int num, FILE * stream ) -{ - char* ptr = fgets( str, num, stream ); - if ( 0 ) {char *temp = (char *)&ptr; temp++;} +static inline void fgetl(char *str, int num, FILE *stream) { + char *ptr = fgets(str, num, stream); + if (0) { + char *temp = (char *)&ptr; + temp++; + } } /******************************************************** * Constructors * ********************************************************/ -Domain::Domain( int nx, int ny, int nz, int rnk, int npx, int npy, int npz, - double lx, double ly, double lz, int BC): - database(nullptr), Nx(0), Ny(0), Nz(0), - Lx(0), Ly(0), Lz(0), Volume(0), BoundaryCondition(0), voxel_length(1), - Comm( Utilities::MPI( MPI_COMM_WORLD).dup() ), - inlet_layers_x(0), inlet_layers_y(0), inlet_layers_z(0), - inlet_layers_phase(1),outlet_layers_phase(2) -{ - NULL_USE( rnk ); - NULL_USE( npy ); - NULL_USE( npz ); - // set up the neighbor ranks +Domain::Domain(int nx, int ny, int nz, int rnk, int npx, int npy, int npz, + double lx, double ly, double lz, int BC) + : database(nullptr), Nx(0), Ny(0), Nz(0), Lx(0), Ly(0), Lz(0), Volume(0), + BoundaryCondition(0), voxel_length(1), + Comm(Utilities::MPI(MPI_COMM_WORLD).dup()), inlet_layers_x(0), + inlet_layers_y(0), inlet_layers_z(0), inlet_layers_phase(1), + outlet_layers_phase(2) { + NULL_USE(rnk); + NULL_USE(npy); + NULL_USE(npz); + // set up the neighbor ranks int myrank = Comm.getRank(); - rank_info = RankInfoStruct( myrank, rank_info.nx, rank_info.ny, rank_info.nz ); - - Comm.barrier(); - - auto db = std::make_shared( ); - db->putScalar( "BC", BC ); - db->putVector( "nproc", { npx, npx, npx } ); - db->putVector( "n", { nx, ny, nz } ); - db->putScalar( "nspheres", 0 ); - db->putVector( "L", { lx, ly, lz } ); - initialize( db ); + rank_info = + RankInfoStruct(myrank, rank_info.nx, rank_info.ny, rank_info.nz); + + Comm.barrier(); + + auto db = std::make_shared(); + db->putScalar("BC", BC); + db->putVector("nproc", {npx, npx, npx}); + db->putVector("n", {nx, ny, nz}); + db->putScalar("nspheres", 0); + db->putVector("L", {lx, ly, lz}); + initialize(db); } -Domain::Domain( std::shared_ptr db, const Utilities::MPI& Communicator): - database(db), Nx(0), Ny(0), Nz(0), - Lx(0), Ly(0), Lz(0), Volume(0), BoundaryCondition(0), - inlet_layers_x(0), inlet_layers_y(0), inlet_layers_z(0), - outlet_layers_x(0), outlet_layers_y(0), outlet_layers_z(0), - inlet_layers_phase(1),outlet_layers_phase(2) -{ +Domain::Domain(std::shared_ptr db, const Utilities::MPI &Communicator) + : database(db), Nx(0), Ny(0), Nz(0), Lx(0), Ly(0), Lz(0), Volume(0), + BoundaryCondition(0), inlet_layers_x(0), inlet_layers_y(0), + inlet_layers_z(0), outlet_layers_x(0), outlet_layers_y(0), + outlet_layers_z(0), inlet_layers_phase(1), outlet_layers_phase(2) { Comm = Communicator.dup(); - // set up the neighbor ranks + // set up the neighbor ranks int myrank = Comm.getRank(); - initialize( db ); - rank_info = RankInfoStruct( myrank, rank_info.nx, rank_info.ny, rank_info.nz ); + initialize(db); + rank_info = + RankInfoStruct(myrank, rank_info.nx, rank_info.ny, rank_info.nz); Comm.barrier(); } - /******************************************************** * Destructor * ********************************************************/ -Domain::~Domain() -{ -} - +Domain::~Domain() {} /******************************************************** * Initialization * ********************************************************/ -void Domain::initialize( std::shared_ptr db ) -{ +void Domain::initialize(std::shared_ptr db) { d_db = db; auto nproc = d_db->getVector("nproc"); auto n = d_db->getVector("n"); - ASSERT( n.size() == 3u ); - ASSERT( nproc.size() == 3u ); + ASSERT(n.size() == 3u); + ASSERT(nproc.size() == 3u); int nx = n[0]; int ny = n[1]; int nz = n[2]; - - if (d_db->keyExists( "InletLayers" )){ - auto InletCount = d_db->getVector( "InletLayers" ); - inlet_layers_x = InletCount[0]; - inlet_layers_y = InletCount[1]; - inlet_layers_z = InletCount[2]; - } - if (d_db->keyExists( "OutletLayers" )){ - auto OutletCount = d_db->getVector( "OutletLayers" ); - outlet_layers_x = OutletCount[0]; - outlet_layers_y = OutletCount[1]; - outlet_layers_z = OutletCount[2]; - } - if (d_db->keyExists( "InletLayersPhase" )){ - inlet_layers_phase = d_db->getScalar( "InletLayersPhase" ); - } - if (d_db->keyExists( "OutletLayersPhase" )){ - outlet_layers_phase = d_db->getScalar( "OutletLayersPhase" ); - } + + if (d_db->keyExists("InletLayers")) { + auto InletCount = d_db->getVector("InletLayers"); + inlet_layers_x = InletCount[0]; + inlet_layers_y = InletCount[1]; + inlet_layers_z = InletCount[2]; + } + if (d_db->keyExists("OutletLayers")) { + auto OutletCount = d_db->getVector("OutletLayers"); + outlet_layers_x = OutletCount[0]; + outlet_layers_y = OutletCount[1]; + outlet_layers_z = OutletCount[2]; + } + if (d_db->keyExists("InletLayersPhase")) { + inlet_layers_phase = d_db->getScalar("InletLayersPhase"); + } + if (d_db->keyExists("OutletLayersPhase")) { + outlet_layers_phase = d_db->getScalar("OutletLayersPhase"); + } voxel_length = 1.0; - if (d_db->keyExists( "voxel_length" )){ - voxel_length = d_db->getScalar("voxel_length"); + if (d_db->keyExists("voxel_length")) { + voxel_length = d_db->getScalar("voxel_length"); + } else if (d_db->keyExists("L")) { + auto Length = d_db->getVector("L"); + Lx = Length[0]; + Ly = Length[1]; + Lz = Length[2]; + voxel_length = Lx / (nx * nproc[0]); } - else if (d_db->keyExists( "L" )){ - auto Length = d_db->getVector("L"); - Lx = Length[0]; - Ly = Length[1]; - Lz = Length[2]; - voxel_length = Lx/(nx*nproc[0]); - } - Lx = nx*nproc[0]*voxel_length; - Ly = ny*nproc[1]*voxel_length; - Lz = nz*nproc[2]*voxel_length; - Nx = nx+2; - Ny = ny+2; - Nz = nz+2; + Lx = nx * nproc[0] * voxel_length; + Ly = ny * nproc[1] * voxel_length; + Lz = nz * nproc[2] * voxel_length; + Nx = nx + 2; + Ny = ny + 2; + Nz = nz + 2; // Initialize ranks int myrank = Comm.getRank(); - rank_info = RankInfoStruct(myrank,nproc[0],nproc[1],nproc[2]); - // inlet layers only apply to lower part of domain - if (rank_info.ix > 0) inlet_layers_x = 0; - if (rank_info.jy > 0) inlet_layers_y = 0; - if (rank_info.kz > 0) inlet_layers_z = 0; - // outlet layers only apply to top part of domain - if (rank_info.ix < nproc[0]-1 ) outlet_layers_x = 0; - if (rank_info.jy < nproc[1]-1) outlet_layers_y = 0; - if (rank_info.kz < nproc[2]-1) outlet_layers_z = 0; + rank_info = RankInfoStruct(myrank, nproc[0], nproc[1], nproc[2]); + // inlet layers only apply to lower part of domain + if (rank_info.ix > 0) + inlet_layers_x = 0; + if (rank_info.jy > 0) + inlet_layers_y = 0; + if (rank_info.kz > 0) + inlet_layers_z = 0; + // outlet layers only apply to top part of domain + if (rank_info.ix < nproc[0] - 1) + outlet_layers_x = 0; + if (rank_info.jy < nproc[1] - 1) + outlet_layers_y = 0; + if (rank_info.kz < nproc[2] - 1) + outlet_layers_z = 0; // Fill remaining variables - N = Nx*Ny*Nz; - Volume = nx*ny*nz*nproc[0]*nproc[1]*nproc[2]*1.0; + N = Nx * Ny * Nz; + Volume = nx * ny * nz * nproc[0] * nproc[1] * nproc[2] * 1.0; - if (myrank==0) printf("voxel length = %f micron \n", voxel_length); + if (myrank == 0) + printf("voxel length = %f micron \n", voxel_length); - id = std::vector( N, 0 ); - BoundaryCondition = d_db->getScalar("BC"); + id = std::vector(N, 0); + BoundaryCondition = d_db->getScalar("BC"); int nprocs = Comm.getSize(); - INSIST(nprocs == nproc[0]*nproc[1]*nproc[2],"Fatal error in processor count!"); + INSIST(nprocs == nproc[0] * nproc[1] * nproc[2], + "Fatal error in processor count!"); } - /******************************************************** * Get send/recv lists * ********************************************************/ -const std::vector& Domain::getRecvList( const char* dir ) const -{ - if ( dir[0] == 'x' ) { - if ( dir[1] == 0 ) +const std::vector &Domain::getRecvList(const char *dir) const { + if (dir[0] == 'x') { + if (dir[1] == 0) return recvList_x; - else if ( dir[1] == 'y' ) + else if (dir[1] == 'y') return recvList_xy; - else if ( dir[1] == 'Y' ) + else if (dir[1] == 'Y') return recvList_xY; - else if ( dir[1] == 'z' ) + else if (dir[1] == 'z') return recvList_xz; - else if ( dir[1] == 'Z' ) + else if (dir[1] == 'Z') return recvList_xZ; - } else if ( dir[0] == 'y' ) { - if ( dir[1] == 0 ) + } else if (dir[0] == 'y') { + if (dir[1] == 0) return recvList_y; - else if ( dir[1] == 'z' ) + else if (dir[1] == 'z') return recvList_yz; - else if ( dir[1] == 'Z' ) + else if (dir[1] == 'Z') return recvList_yZ; - } else if ( dir[0] == 'z' ) { - if ( dir[1] == 0 ) + } else if (dir[0] == 'z') { + if (dir[1] == 0) return recvList_z; - } else if ( dir[0] == 'X' ) { - if ( dir[1] == 0 ) + } else if (dir[0] == 'X') { + if (dir[1] == 0) return recvList_X; - else if ( dir[1] == 'y' ) + else if (dir[1] == 'y') return recvList_Xy; - else if ( dir[1] == 'Y' ) + else if (dir[1] == 'Y') return recvList_XY; - else if ( dir[1] == 'z' ) + else if (dir[1] == 'z') return recvList_Xz; - else if ( dir[1] == 'Z' ) + else if (dir[1] == 'Z') return recvList_XZ; - } else if ( dir[0] == 'Y' ) { - if ( dir[1] == 0 ) + } else if (dir[0] == 'Y') { + if (dir[1] == 0) return recvList_Y; - else if ( dir[1] == 'z' ) + else if (dir[1] == 'z') return recvList_Yz; - else if ( dir[1] == 'Z' ) + else if (dir[1] == 'Z') return recvList_YZ; - } else if ( dir[0] == 'Z' ) { - if ( dir[1] == 0 ) + } else if (dir[0] == 'Z') { + if (dir[1] == 0) return recvList_Z; } throw std::logic_error("Internal error"); } -const std::vector& Domain::getSendList( const char* dir ) const -{ - if ( dir[0] == 'x' ) { - if ( dir[1] == 0 ) +const std::vector &Domain::getSendList(const char *dir) const { + if (dir[0] == 'x') { + if (dir[1] == 0) return sendList_x; - else if ( dir[1] == 'y' ) + else if (dir[1] == 'y') return sendList_xy; - else if ( dir[1] == 'Y' ) + else if (dir[1] == 'Y') return sendList_xY; - else if ( dir[1] == 'z' ) + else if (dir[1] == 'z') return sendList_xz; - else if ( dir[1] == 'Z' ) + else if (dir[1] == 'Z') return sendList_xZ; - } else if ( dir[0] == 'y' ) { - if ( dir[1] == 0 ) + } else if (dir[0] == 'y') { + if (dir[1] == 0) return sendList_y; - else if ( dir[1] == 'z' ) + else if (dir[1] == 'z') return sendList_yz; - else if ( dir[1] == 'Z' ) + else if (dir[1] == 'Z') return sendList_yZ; - } else if ( dir[0] == 'z' ) { - if ( dir[1] == 0 ) + } else if (dir[0] == 'z') { + if (dir[1] == 0) return sendList_z; - } else if ( dir[0] == 'X' ) { - if ( dir[1] == 0 ) + } else if (dir[0] == 'X') { + if (dir[1] == 0) return sendList_X; - else if ( dir[1] == 'y' ) + else if (dir[1] == 'y') return sendList_Xy; - else if ( dir[1] == 'Y' ) + else if (dir[1] == 'Y') return sendList_XY; - else if ( dir[1] == 'z' ) + else if (dir[1] == 'z') return sendList_Xz; - else if ( dir[1] == 'Z' ) + else if (dir[1] == 'Z') return sendList_XZ; - } else if ( dir[0] == 'Y' ) { - if ( dir[1] == 0 ) + } else if (dir[0] == 'Y') { + if (dir[1] == 0) return sendList_Y; - else if ( dir[1] == 'z' ) + else if (dir[1] == 'z') return sendList_Yz; - else if ( dir[1] == 'Z' ) + else if (dir[1] == 'Z') return sendList_YZ; - } else if ( dir[0] == 'Z' ) { - if ( dir[1] == 0 ) + } else if (dir[0] == 'Z') { + if (dir[1] == 0) return sendList_Z; } throw std::logic_error("Internal error"); } - /******************************************************** * Decomp * ********************************************************/ -void Domain::Decomp( const std::string& Filename ) -{ - //....................................................................... - // Reading the domain information file - //....................................................................... - int rank_offset = 0; - int RANK = rank(); - int nprocs, nprocx, nprocy, nprocz, nx, ny, nz; - int64_t global_Nx,global_Ny,global_Nz; - int64_t i,j,k,n; - int64_t xStart,yStart,zStart; - int checkerSize; - bool USE_CHECKER = false; - //int inlet_layers_x, inlet_layers_y, inlet_layers_z; - //int outlet_layers_x, outlet_layers_y, outlet_layers_z; - xStart=yStart=zStart=0; - inlet_layers_x = 0; - inlet_layers_y = 0; - inlet_layers_z = 0; - outlet_layers_x = 0; - outlet_layers_y = 0; - outlet_layers_z = 0; - inlet_layers_phase=1; - outlet_layers_phase=2; - checkerSize = 32; +void Domain::Decomp(const std::string &Filename) { + //....................................................................... + // Reading the domain information file + //....................................................................... + int rank_offset = 0; + int RANK = rank(); + int nprocs, nprocx, nprocy, nprocz, nx, ny, nz; + int64_t global_Nx, global_Ny, global_Nz; + int64_t i, j, k, n; + int64_t xStart, yStart, zStart; + int checkerSize; + bool USE_CHECKER = false; + //int inlet_layers_x, inlet_layers_y, inlet_layers_z; + //int outlet_layers_x, outlet_layers_y, outlet_layers_z; + xStart = yStart = zStart = 0; + inlet_layers_x = 0; + inlet_layers_y = 0; + inlet_layers_z = 0; + outlet_layers_x = 0; + outlet_layers_y = 0; + outlet_layers_z = 0; + inlet_layers_phase = 1; + outlet_layers_phase = 2; + checkerSize = 32; - // Read domain parameters - //auto Filename = database->getScalar( "Filename" ); - //auto L = database->getVector( "L" ); - auto size = database->getVector( "n" ); - auto SIZE = database->getVector( "N" ); - auto nproc = database->getVector( "nproc" ); - if (database->keyExists( "offset" )){ - auto offset = database->getVector( "offset" ); - xStart = offset[0]; - yStart = offset[1]; - zStart = offset[2]; - } - if (database->keyExists( "InletLayers" )){ - auto InletCount = database->getVector( "InletLayers" ); - inlet_layers_x = InletCount[0]; - inlet_layers_y = InletCount[1]; - inlet_layers_z = InletCount[2]; - } - if (database->keyExists( "OutletLayers" )){ - auto OutletCount = database->getVector( "OutletLayers" ); - outlet_layers_x = OutletCount[0]; - outlet_layers_y = OutletCount[1]; - outlet_layers_z = OutletCount[2]; - } - if (database->keyExists( "checkerSize" )){ - checkerSize = database->getScalar( "checkerSize" ); - USE_CHECKER = true; - } - else { - checkerSize = SIZE[0]; - } - if (database->keyExists( "InletLayersPhase" )){ - inlet_layers_phase = database->getScalar( "InletLayersPhase" ); - } - if (database->keyExists( "OutletLayersPhase" )){ - outlet_layers_phase = database->getScalar( "OutletLayersPhase" ); - } - auto ReadValues = database->getVector( "ReadValues" ); - auto WriteValues = database->getVector( "WriteValues" ); - auto ReadType = database->getScalar( "ReadType" ); - - if (ReadType == "8bit"){ - } - else if (ReadType == "16bit"){ - } - else{ - //printf("INPUT ERROR: Valid ReadType are 8bit, 16bit \n"); - ReadType = "8bit"; - } - nx = size[0]; - ny = size[1]; - nz = size[2]; - nprocx = nproc[0]; - nprocy = nproc[1]; - nprocz = nproc[2]; - global_Nx = SIZE[0]; - global_Ny = SIZE[1]; - global_Nz = SIZE[2]; - nprocs=nprocx*nprocy*nprocz; - char *SegData = NULL; - - if (RANK==0){ - printf("Input media: %s\n",Filename.c_str()); - printf("Relabeling %lu values\n",ReadValues.size()); - for (size_t idx=0; idxgetScalar( "Filename" ); + //auto L = database->getVector( "L" ); + auto size = database->getVector("n"); + auto SIZE = database->getVector("N"); + auto nproc = database->getVector("nproc"); + if (database->keyExists("offset")) { + auto offset = database->getVector("offset"); + xStart = offset[0]; + yStart = offset[1]; + zStart = offset[2]; + } + if (database->keyExists("InletLayers")) { + auto InletCount = database->getVector("InletLayers"); + inlet_layers_x = InletCount[0]; + inlet_layers_y = InletCount[1]; + inlet_layers_z = InletCount[2]; + } + if (database->keyExists("OutletLayers")) { + auto OutletCount = database->getVector("OutletLayers"); + outlet_layers_x = OutletCount[0]; + outlet_layers_y = OutletCount[1]; + outlet_layers_z = OutletCount[2]; + } + if (database->keyExists("checkerSize")) { + checkerSize = database->getScalar("checkerSize"); + USE_CHECKER = true; + } else { + checkerSize = SIZE[0]; + } + if (database->keyExists("InletLayersPhase")) { + inlet_layers_phase = database->getScalar("InletLayersPhase"); + } + if (database->keyExists("OutletLayersPhase")) { + outlet_layers_phase = database->getScalar("OutletLayersPhase"); + } + auto ReadValues = database->getVector("ReadValues"); + auto WriteValues = database->getVector("WriteValues"); + auto ReadType = database->getScalar("ReadType"); - // Rank=0 reads the entire segmented data and distributes to worker processes - printf("Dimensions of segmented image: %ld x %ld x %ld \n",global_Nx,global_Ny,global_Nz); - int64_t SIZE = global_Nx*global_Ny*global_Nz; - SegData = new char[SIZE]; - if (ReadType == "8bit"){ - printf("Reading 8-bit input data \n"); - FILE *SEGDAT = fopen(Filename.c_str(),"rb"); - if (SEGDAT==NULL) ERROR("Domain.cpp: Error reading segmented data"); - size_t ReadSeg; - ReadSeg=fread(SegData,1,SIZE,SEGDAT); - if (ReadSeg != size_t(SIZE)) printf("Domain.cpp: Error reading segmented data \n"); - fclose(SEGDAT); - } - else if (ReadType == "16bit"){ - printf("Reading 16-bit input data \n"); - short int *InputData; - InputData = new short int[SIZE]; - FILE *SEGDAT = fopen(Filename.c_str(),"rb"); - if (SEGDAT==NULL) ERROR("Domain.cpp: Error reading segmented data"); - size_t ReadSeg; - ReadSeg=fread(InputData,2,SIZE,SEGDAT); - if (ReadSeg != size_t(SIZE)) printf("Domain.cpp: Error reading segmented data \n"); - fclose(SEGDAT); - for (int n=0; n LabelCount(ReadValues.size(),0); - for (int k = 0; k 0){ - // use checkerboard pattern - printf("Checkerboard pattern at x inlet for %i layers \n",inlet_layers_x); - for (int k = 0; k 0){ - printf("Checkerboard pattern at y inlet for %i layers \n",inlet_layers_y); - // use checkerboard pattern - for (int k = 0; k 0){ - printf("Checkerboard pattern at z inlet for %i layers, saturated with phase label=%i \n",inlet_layers_z,inlet_layers_phase); - // use checkerboard pattern - for (int k = zStart; k < zStart+inlet_layers_z; k++){ - for (int j = 0; j LabelCount(ReadValues.size(), 0); + for (int k = 0; k < global_Nz; k++) { + for (int j = 0; j < global_Ny; j++) { + for (int i = 0; i < global_Nx; i++) { + n = k * global_Nx * global_Ny + j * global_Nx + i; + //char locval = loc_id[n]; + signed char locval = SegData[n]; + for (size_t idx = 0; idx < ReadValues.size(); idx++) { + signed char oldvalue = ReadValues[idx]; + signed char newvalue = WriteValues[idx]; + if (locval == oldvalue) { + SegData[n] = newvalue; + LabelCount[idx]++; + idx = ReadValues.size(); + } + } + } + } + } + for (size_t idx = 0; idx < ReadValues.size(); idx++) { + long int label = ReadValues[idx]; + long int count = LabelCount[idx]; + printf("Label=%ld, Count=%ld \n", label, count); + } + if (USE_CHECKER) { + if (inlet_layers_x > 0) { + // use checkerboard pattern + printf("Checkerboard pattern at x inlet for %i layers \n", + inlet_layers_x); + for (int k = 0; k < global_Nz; k++) { + for (int j = 0; j < global_Ny; j++) { + for (int i = xStart; i < xStart + inlet_layers_x; i++) { + if ((j / checkerSize + k / checkerSize) % 2 == 0) { + // void checkers + SegData[k * global_Nx * global_Ny + + j * global_Nx + i] = 2; + } else { + // solid checkers + SegData[k * global_Nx * global_Ny + + j * global_Nx + i] = 0; + } + } + } + } + } - if (outlet_layers_x > 0){ - // use checkerboard pattern - printf("Checkerboard pattern at x outlet for %i layers \n",outlet_layers_x); - for (int k = 0; k 0) { + printf("Checkerboard pattern at y inlet for %i layers \n", + inlet_layers_y); + // use checkerboard pattern + for (int k = 0; k < global_Nz; k++) { + for (int j = yStart; j < yStart + inlet_layers_y; j++) { + for (int i = 0; i < global_Nx; i++) { + if ((i / checkerSize + k / checkerSize) % 2 == 0) { + // void checkers + SegData[k * global_Nx * global_Ny + + j * global_Nx + i] = 2; + } else { + // solid checkers + SegData[k * global_Nx * global_Ny + + j * global_Nx + i] = 0; + } + } + } + } + } - if (outlet_layers_y > 0){ - printf("Checkerboard pattern at y outlet for %i layers \n",outlet_layers_y); - // use checkerboard pattern - for (int k = 0; k 0) { + printf("Checkerboard pattern at z inlet for %i layers, " + "saturated with phase label=%i \n", + inlet_layers_z, inlet_layers_phase); + // use checkerboard pattern + for (int k = zStart; k < zStart + inlet_layers_z; k++) { + for (int j = 0; j < global_Ny; j++) { + for (int i = 0; i < global_Nx; i++) { + if ((i / checkerSize + j / checkerSize) % 2 == 0) { + // void checkers + //SegData[k*global_Nx*global_Ny+j*global_Nx+i] = 2; + SegData[k * global_Nx * global_Ny + + j * global_Nx + i] = inlet_layers_phase; + } else { + // solid checkers + SegData[k * global_Nx * global_Ny + + j * global_Nx + i] = 0; + } + } + } + } + } - } - } - } - } - } + if (outlet_layers_x > 0) { + // use checkerboard pattern + printf("Checkerboard pattern at x outlet for %i layers \n", + outlet_layers_x); + for (int k = 0; k < global_Nz; k++) { + for (int j = 0; j < global_Ny; j++) { + for (int i = xStart + nx * nprocx - outlet_layers_x; + i < xStart + nx * nprocx; i++) { + if ((j / checkerSize + k / checkerSize) % 2 == 0) { + // void checkers + SegData[k * global_Nx * global_Ny + + j * global_Nx + i] = 2; + } else { + // solid checkers + SegData[k * global_Nx * global_Ny + + j * global_Nx + i] = 0; + } + } + } + } + } - if (outlet_layers_z > 0){ - printf("Checkerboard pattern at z outlet for %i layers, saturated with phase label=%i \n",outlet_layers_z,outlet_layers_phase); - // use checkerboard pattern - for (int k = zStart + nz*nprocz - outlet_layers_z; k < zStart + nz*nprocz; k++){ - for (int j = 0; j 0){ - printf("Mixed reflection pattern at z inlet for %i layers, saturated with phase label=%i \n",inlet_layers_z,inlet_layers_phase); - for (int k = zStart; k < zStart+inlet_layers_z; k++){ - for (int j = 0; j 0){ - SegData[k*global_Nx*global_Ny+j*global_Nx+i] = reflection_id; - } - } - } - } - } - if (outlet_layers_z > 0){ - printf("Mixed reflection pattern at z outlet for %i layers, saturated with phase label=%i \n",outlet_layers_z,outlet_layers_phase); - for (int k = zStart + nz*nprocz - outlet_layers_z; k < zStart + nz*nprocz; k++){ - for (int j = 0; j 0){ - SegData[k*global_Nx*global_Ny+j*global_Nx+i] = reflection_id; - } - } - } - } - } - } - } - - // Get the rank info - int64_t N = (nx+2)*(ny+2)*(nz+2); - - // number of sites to use for periodic boundary condition transition zone - int64_t z_transition_size = (nprocz*nz - (global_Nz - zStart))/2; - if (z_transition_size < 0) z_transition_size=0; + if (outlet_layers_y > 0) { + printf("Checkerboard pattern at y outlet for %i layers \n", + outlet_layers_y); + // use checkerboard pattern + for (int k = 0; k < global_Nz; k++) { + for (int j = yStart + ny * nprocy - outlet_layers_y; + j < yStart + ny * nprocy; j++) { + for (int i = 0; i < global_Nx; i++) { + if ((i / checkerSize + k / checkerSize) % 2 == 0) { + // void checkers + SegData[k * global_Nx * global_Ny + + j * global_Nx + i] = 2; + } else { + // solid checkers + SegData[k * global_Nx * global_Ny + + j * global_Nx + i] = 0; + } + } + } + } + } - // Set up the sub-domains - if (RANK==0){ - printf("Distributing subdomains across %i processors \n",nprocs); - printf("Process grid: %i x %i x %i \n",nprocx,nprocy,nprocz); - printf("Subdomain size: %i x %i x %i \n",nx,ny,nz); - printf("Size of transition region: %ld \n", z_transition_size); - auto loc_id = new char [(nx+2)*(ny+2)*(nz+2)]; - for (int kp=0; kp 0) { + printf("Checkerboard pattern at z outlet for %i layers, " + "saturated with phase label=%i \n", + outlet_layers_z, outlet_layers_phase); + // use checkerboard pattern + for (int k = zStart + nz * nprocz - outlet_layers_z; + k < zStart + nz * nprocz; k++) { + for (int j = 0; j < global_Ny; j++) { + for (int i = 0; i < global_Nx; i++) { + if ((i / checkerSize + j / checkerSize) % 2 == 0) { + // void checkers + //SegData[k*global_Nx*global_Ny+j*global_Nx+i] = 2; + SegData[k * global_Nx * global_Ny + + j * global_Nx + i] = + outlet_layers_phase; + } else { + // solid checkers + SegData[k * global_Nx * global_Ny + + j * global_Nx + i] = 0; + } + } + } + } + } + } else { + if (inlet_layers_z > 0) { + printf("Mixed reflection pattern at z inlet for %i layers, " + "saturated with phase label=%i \n", + inlet_layers_z, inlet_layers_phase); + for (int k = zStart; k < zStart + inlet_layers_z; k++) { + for (int j = 0; j < global_Ny; j++) { + for (int i = 0; i < global_Nx; i++) { + signed char local_id = + SegData[k * global_Nx * global_Ny + + j * global_Nx + i]; + signed char reflection_id = + SegData[(zStart + nz * nprocz - 1) * global_Nx * + global_Ny + + j * global_Nx + i]; + if (local_id < 1 && reflection_id > 0) { + SegData[k * global_Nx * global_Ny + + j * global_Nx + i] = reflection_id; + } + } + } + } + } + if (outlet_layers_z > 0) { + printf("Mixed reflection pattern at z outlet for %i layers, " + "saturated with phase label=%i \n", + outlet_layers_z, outlet_layers_phase); + for (int k = zStart + nz * nprocz - outlet_layers_z; + k < zStart + nz * nprocz; k++) { + for (int j = 0; j < global_Ny; j++) { + for (int i = 0; i < global_Nx; i++) { + signed char local_id = + SegData[k * global_Nx * global_Ny + + j * global_Nx + i]; + signed char reflection_id = + SegData[zStart * global_Nx * global_Ny + + j * global_Nx + i]; + if (local_id < 1 && reflection_id > 0) { + SegData[k * global_Nx * global_Ny + + j * global_Nx + i] = reflection_id; + } + } + } + } + } + } + } + + // Get the rank info + int64_t N = (nx + 2) * (ny + 2) * (nz + 2); + + // number of sites to use for periodic boundary condition transition zone + int64_t z_transition_size = (nprocz * nz - (global_Nz - zStart)) / 2; + if (z_transition_size < 0) + z_transition_size = 0; + + // Set up the sub-domains + if (RANK == 0) { + printf("Distributing subdomains across %i processors \n", nprocs); + printf("Process grid: %i x %i x %i \n", nprocx, nprocy, nprocz); + printf("Subdomain size: %i x %i x %i \n", nx, ny, nz); + printf("Size of transition region: %ld \n", z_transition_size); + auto loc_id = new char[(nx + 2) * (ny + 2) * (nz + 2)]; + for (int kp = 0; kp < nprocz; kp++) { + for (int jp = 0; jp < nprocy; jp++) { + for (int ip = 0; ip < nprocx; ip++) { + // rank of the process that gets this subdomain + int rnk = kp * nprocx * nprocy + jp * nprocx + ip; + // Pack and send the subdomain for rnk + for (k = 0; k < nz + 2; k++) { + for (j = 0; j < ny + 2; j++) { + for (i = 0; i < nx + 2; i++) { + int64_t x = xStart + ip * nx + i - 1; + int64_t y = yStart + jp * ny + j - 1; + // int64_t z = zStart + kp*nz + k-1; + int64_t z = zStart + kp * nz + k - 1 - + z_transition_size; + if (x < xStart) + x = xStart; + if (!(x < global_Nx)) + x = global_Nx - 1; + if (y < yStart) + y = yStart; + if (!(y < global_Ny)) + y = global_Ny - 1; + if (z < zStart) + z = zStart; + if (!(z < global_Nz)) + z = global_Nz - 1; + int64_t nlocal = + k * (nx + 2) * (ny + 2) + j * (nx + 2) + i; + int64_t nglobal = z * global_Nx * global_Ny + + y * global_Nx + x; + loc_id[nlocal] = SegData[nglobal]; + } + } + } + if (rnk == 0) { + for (k = 0; k < nz + 2; k++) { + for (j = 0; j < ny + 2; j++) { + for (i = 0; i < nx + 2; i++) { + int nlocal = k * (nx + 2) * (ny + 2) + + j * (nx + 2) + i; + id[nlocal] = loc_id[nlocal]; + } + } + } + } else { + //printf("Sending data to process %i \n", rnk); + Comm.send(loc_id, N, rnk, 15); + } + // Write the data for this rank data + char LocalRankFilename[40]; + sprintf(LocalRankFilename, "ID.%05i", rnk + rank_offset); + FILE *ID = fopen(LocalRankFilename, "wb"); + fwrite(loc_id, 1, (nx + 2) * (ny + 2) * (nz + 2), ID); + fclose(ID); + } + } + } + delete[] loc_id; + } else { + // Recieve the subdomain from rank = 0 + //printf("Ready to recieve data %i at process %i \n", N,rank); + Comm.recv(id.data(), N, 0, 15); + } + Comm.barrier(); ComputePorosity(); - delete [] SegData; + delete[] SegData; } -void Domain::ComputePorosity(){ - // Compute the porosity - double sum; - double sum_local=0.0; - double iVol_global = 1.0/(1.0*(Nx-2)*(Ny-2)*(Nz-2)*nprocx()*nprocy()*nprocz()); - if (BoundaryCondition > 0 && BoundaryCondition !=5) - iVol_global = 1.0/(1.0*(Nx-2)*nprocx()*(Ny-2)*nprocy()*((Nz-2)*nprocz()-inlet_layers_z - outlet_layers_z)); - //......................................................... - for (int k=inlet_layers_z+1; k 0){ - sum_local+=1.0; +void Domain::ComputePorosity() { + // Compute the porosity + double sum; + double sum_local = 0.0; + double iVol_global = 1.0 / (1.0 * (Nx - 2) * (Ny - 2) * (Nz - 2) * + nprocx() * nprocy() * nprocz()); + if (BoundaryCondition > 0 && BoundaryCondition != 5) + iVol_global = + 1.0 / (1.0 * (Nx - 2) * nprocx() * (Ny - 2) * nprocy() * + ((Nz - 2) * nprocz() - inlet_layers_z - outlet_layers_z)); + //......................................................... + for (int k = inlet_layers_z + 1; k < Nz - outlet_layers_z - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + int n = k * Nx * Ny + j * Nx + i; + if (id[n] > 0) { + sum_local += 1.0; } } } } sum = Comm.sumReduce(sum_local); - porosity = sum*iVol_global; - if (rank()==0) printf("Media porosity = %f \n",porosity); + porosity = sum * iVol_global; + if (rank() == 0) + printf("Media porosity = %f \n", porosity); //......................................................... - } -void Domain::AggregateLabels( const std::string& filename ){ - - int nx = Nx; - int ny = Ny; - int nz = Nz; - - int npx = nprocx(); - int npy = nprocy(); - int npz = nprocz(); - - int ipx = iproc(); - int ipy = jproc(); - int ipz = kproc(); - - int nprocs = nprocx()*nprocy()*nprocz(); - - int full_nx = npx*(nx-2); - int full_ny = npy*(ny-2); - int full_nz = npz*(nz-2); - int local_size = (nx-2)*(ny-2)*(nz-2); - long int full_size = long(full_nx)*long(full_ny)*long(full_nz); - - auto LocalID = new signed char [local_size]; - - //printf("aggregate labels: local size=%i, global size = %i",local_size, full_size); - // assign the ID for the local sub-region - for (int k=1; k 0){ - // Counts for the six faces - if (i==1) sendCount_x++; - if (j==1) sendCount_y++; - if (k==1) sendCount_z++; - if (i==Nx-2) sendCount_X++; - if (j==Ny-2) sendCount_Y++; - if (k==Nz-2) sendCount_Z++; - // Counts for the twelve edges - if (i==1 && j==1) sendCount_xy++; - if (i==1 && j==Ny-2) sendCount_xY++; - if (i==Nx-2 && j==1) sendCount_Xy++; - if (i==Nx-2 && j==Ny-2) sendCount_XY++; +void Domain::CommInit() { + int i, j, k, n; + int sendtag = 21; + int recvtag = 21; + //...................................................................................... + int sendCount_x, sendCount_y, sendCount_z, sendCount_X, sendCount_Y, + sendCount_Z; + int sendCount_xy, sendCount_yz, sendCount_xz, sendCount_Xy, sendCount_Yz, + sendCount_xZ; + int sendCount_xY, sendCount_yZ, sendCount_Xz, sendCount_XY, sendCount_YZ, + sendCount_XZ; + sendCount_x = sendCount_y = sendCount_z = sendCount_X = sendCount_Y = + sendCount_Z = 0; + sendCount_xy = sendCount_yz = sendCount_xz = sendCount_Xy = sendCount_Yz = + sendCount_xZ = 0; + sendCount_xY = sendCount_yZ = sendCount_Xz = sendCount_XY = sendCount_YZ = + sendCount_XZ = 0; + //...................................................................................... + for (k = 1; k < Nz - 1; k++) { + for (j = 1; j < Ny - 1; j++) { + for (i = 1; i < Nx - 1; i++) { + // Check the phase ID + if (id[k * Nx * Ny + j * Nx + i] > 0) { + // Counts for the six faces + if (i == 1) + sendCount_x++; + if (j == 1) + sendCount_y++; + if (k == 1) + sendCount_z++; + if (i == Nx - 2) + sendCount_X++; + if (j == Ny - 2) + sendCount_Y++; + if (k == Nz - 2) + sendCount_Z++; + // Counts for the twelve edges + if (i == 1 && j == 1) + sendCount_xy++; + if (i == 1 && j == Ny - 2) + sendCount_xY++; + if (i == Nx - 2 && j == 1) + sendCount_Xy++; + if (i == Nx - 2 && j == Ny - 2) + sendCount_XY++; - if (i==1 && k==1) sendCount_xz++; - if (i==1 && k==Nz-2) sendCount_xZ++; - if (i==Nx-2 && k==1) sendCount_Xz++; - if (i==Nx-2 && k==Nz-2) sendCount_XZ++; + if (i == 1 && k == 1) + sendCount_xz++; + if (i == 1 && k == Nz - 2) + sendCount_xZ++; + if (i == Nx - 2 && k == 1) + sendCount_Xz++; + if (i == Nx - 2 && k == Nz - 2) + sendCount_XZ++; - if (j==1 && k==1) sendCount_yz++; - if (j==1 && k==Nz-2) sendCount_yZ++; - if (j==Ny-2 && k==1) sendCount_Yz++; - if (j==Ny-2 && k==Nz-2) sendCount_YZ++; - } - } - } - } + if (j == 1 && k == 1) + sendCount_yz++; + if (j == 1 && k == Nz - 2) + sendCount_yZ++; + if (j == Ny - 2 && k == 1) + sendCount_Yz++; + if (j == Ny - 2 && k == Nz - 2) + sendCount_YZ++; + } + } + } + } - // allocate send lists - sendList_x.resize( sendCount_x, 0 ); - sendList_y.resize( sendCount_y, 0 ); - sendList_z.resize( sendCount_z, 0 ); - sendList_X.resize( sendCount_X, 0 ); - sendList_Y.resize( sendCount_Y, 0 ); - sendList_Z.resize( sendCount_Z, 0 ); - sendList_xy.resize( sendCount_xy, 0 ); - sendList_yz.resize( sendCount_yz, 0 ); - sendList_xz.resize( sendCount_xz, 0 ); - sendList_Xy.resize( sendCount_Xy, 0 ); - sendList_Yz.resize( sendCount_Yz, 0 ); - sendList_xZ.resize( sendCount_xZ, 0 ); - sendList_xY.resize( sendCount_xY, 0 ); - sendList_yZ.resize( sendCount_yZ, 0 ); - sendList_Xz.resize( sendCount_Xz, 0 ); - sendList_XY.resize( sendCount_XY, 0 ); - sendList_YZ.resize( sendCount_YZ, 0 ); - sendList_XZ.resize( sendCount_XZ, 0 ); - // Populate the send list - sendCount_x = sendCount_y = sendCount_z = sendCount_X = sendCount_Y = sendCount_Z = 0; - sendCount_xy = sendCount_yz = sendCount_xz = sendCount_Xy = sendCount_Yz = sendCount_xZ = 0; - sendCount_xY = sendCount_yZ = sendCount_Xz = sendCount_XY = sendCount_YZ = sendCount_XZ = 0; - for (k=1; k 0){ - // Counts for the six faces - if (i==1) sendList_x[sendCount_x++]=n; - if (j==1) sendList_y[sendCount_y++]=n; - if (k==1) sendList_z[sendCount_z++]=n; - if (i==Nx-2) sendList_X[sendCount_X++]=n; - if (j==Ny-2) sendList_Y[sendCount_Y++]=n; - if (k==Nz-2) sendList_Z[sendCount_Z++]=n; - // Counts for the twelve edges - if (i==1 && j==1) sendList_xy[sendCount_xy++]=n; - if (i==1 && j==Ny-2) sendList_xY[sendCount_xY++]=n; - if (i==Nx-2 && j==1) sendList_Xy[sendCount_Xy++]=n; - if (i==Nx-2 && j==Ny-2) sendList_XY[sendCount_XY++]=n; + // allocate send lists + sendList_x.resize(sendCount_x, 0); + sendList_y.resize(sendCount_y, 0); + sendList_z.resize(sendCount_z, 0); + sendList_X.resize(sendCount_X, 0); + sendList_Y.resize(sendCount_Y, 0); + sendList_Z.resize(sendCount_Z, 0); + sendList_xy.resize(sendCount_xy, 0); + sendList_yz.resize(sendCount_yz, 0); + sendList_xz.resize(sendCount_xz, 0); + sendList_Xy.resize(sendCount_Xy, 0); + sendList_Yz.resize(sendCount_Yz, 0); + sendList_xZ.resize(sendCount_xZ, 0); + sendList_xY.resize(sendCount_xY, 0); + sendList_yZ.resize(sendCount_yZ, 0); + sendList_Xz.resize(sendCount_Xz, 0); + sendList_XY.resize(sendCount_XY, 0); + sendList_YZ.resize(sendCount_YZ, 0); + sendList_XZ.resize(sendCount_XZ, 0); + // Populate the send list + sendCount_x = sendCount_y = sendCount_z = sendCount_X = sendCount_Y = + sendCount_Z = 0; + sendCount_xy = sendCount_yz = sendCount_xz = sendCount_Xy = sendCount_Yz = + sendCount_xZ = 0; + sendCount_xY = sendCount_yZ = sendCount_Xz = sendCount_XY = sendCount_YZ = + sendCount_XZ = 0; + for (k = 1; k < Nz - 1; k++) { + for (j = 1; j < Ny - 1; j++) { + for (i = 1; i < Nx - 1; i++) { + // Local value to send + n = k * Nx * Ny + j * Nx + i; + if (id[n] > 0) { + // Counts for the six faces + if (i == 1) + sendList_x[sendCount_x++] = n; + if (j == 1) + sendList_y[sendCount_y++] = n; + if (k == 1) + sendList_z[sendCount_z++] = n; + if (i == Nx - 2) + sendList_X[sendCount_X++] = n; + if (j == Ny - 2) + sendList_Y[sendCount_Y++] = n; + if (k == Nz - 2) + sendList_Z[sendCount_Z++] = n; + // Counts for the twelve edges + if (i == 1 && j == 1) + sendList_xy[sendCount_xy++] = n; + if (i == 1 && j == Ny - 2) + sendList_xY[sendCount_xY++] = n; + if (i == Nx - 2 && j == 1) + sendList_Xy[sendCount_Xy++] = n; + if (i == Nx - 2 && j == Ny - 2) + sendList_XY[sendCount_XY++] = n; - if (i==1 && k==1) sendList_xz[sendCount_xz++]=n; - if (i==1 && k==Nz-2) sendList_xZ[sendCount_xZ++]=n; - if (i==Nx-2 && k==1) sendList_Xz[sendCount_Xz++]=n; - if (i==Nx-2 && k==Nz-2) sendList_XZ[sendCount_XZ++]=n; + if (i == 1 && k == 1) + sendList_xz[sendCount_xz++] = n; + if (i == 1 && k == Nz - 2) + sendList_xZ[sendCount_xZ++] = n; + if (i == Nx - 2 && k == 1) + sendList_Xz[sendCount_Xz++] = n; + if (i == Nx - 2 && k == Nz - 2) + sendList_XZ[sendCount_XZ++] = n; - if (j==1 && k==1) sendList_yz[sendCount_yz++]=n; - if (j==1 && k==Nz-2) sendList_yZ[sendCount_yZ++]=n; - if (j==Ny-2 && k==1) sendList_Yz[sendCount_Yz++]=n; - if (j==Ny-2 && k==Nz-2) sendList_YZ[sendCount_YZ++]=n; - } - } - } - } + if (j == 1 && k == 1) + sendList_yz[sendCount_yz++] = n; + if (j == 1 && k == Nz - 2) + sendList_yZ[sendCount_yZ++] = n; + if (j == Ny - 2 && k == 1) + sendList_Yz[sendCount_Yz++] = n; + if (j == Ny - 2 && k == Nz - 2) + sendList_YZ[sendCount_YZ++] = n; + } + } + } + } - //...................................................................................... - int recvCount_x, recvCount_y, recvCount_z, recvCount_X, recvCount_Y, recvCount_Z; - int recvCount_xy, recvCount_yz, recvCount_xz, recvCount_Xy, recvCount_Yz, recvCount_xZ; - int recvCount_xY, recvCount_yZ, recvCount_Xz, recvCount_XY, recvCount_YZ, recvCount_XZ; - req1[0] = Comm.Isend(&sendCount_x,1,rank_x(),sendtag+0); - req2[0] = Comm.Irecv(&recvCount_X,1,rank_X(),recvtag+0); - req1[1] = Comm.Isend(&sendCount_X,1,rank_X(),sendtag+1); - req2[1] = Comm.Irecv(&recvCount_x,1,rank_x(),recvtag+1); - req1[2] = Comm.Isend(&sendCount_y,1,rank_y(),sendtag+2); - req2[2] = Comm.Irecv(&recvCount_Y,1,rank_Y(),recvtag+2); - req1[3] = Comm.Isend(&sendCount_Y,1,rank_Y(),sendtag+3); - req2[3] = Comm.Irecv(&recvCount_y,1,rank_y(),recvtag+3); - req1[4] = Comm.Isend(&sendCount_z,1,rank_z(),sendtag+4); - req2[4] = Comm.Irecv(&recvCount_Z,1,rank_Z(),recvtag+4); - req1[5] = Comm.Isend(&sendCount_Z,1,rank_Z(),sendtag+5); - req2[5] = Comm.Irecv(&recvCount_z,1,rank_z(),recvtag+5); - req1[6] = Comm.Isend(&sendCount_xy,1,rank_xy(),sendtag+6); - req2[6] = Comm.Irecv(&recvCount_XY,1,rank_XY(),recvtag+6); - req1[7] = Comm.Isend(&sendCount_XY,1,rank_XY(),sendtag+7); - req2[7] = Comm.Irecv(&recvCount_xy,1,rank_xy(),recvtag+7); - req1[8] = Comm.Isend(&sendCount_Xy,1,rank_Xy(),sendtag+8); - req2[8] = Comm.Irecv(&recvCount_xY,1,rank_xY(),recvtag+8); - req1[9] = Comm.Isend(&sendCount_xY,1,rank_xY(),sendtag+9); - req2[9] = Comm.Irecv(&recvCount_Xy,1,rank_Xy(),recvtag+9); - req1[10] = Comm.Isend(&sendCount_xz,1,rank_xz(),sendtag+10); - req2[10] = Comm.Irecv(&recvCount_XZ,1,rank_XZ(),recvtag+10); - req1[11] = Comm.Isend(&sendCount_XZ,1,rank_XZ(),sendtag+11); - req2[11] = Comm.Irecv(&recvCount_xz,1,rank_xz(),recvtag+11); - req1[12] = Comm.Isend(&sendCount_Xz,1,rank_Xz(),sendtag+12); - req2[12] = Comm.Irecv(&recvCount_xZ,1,rank_xZ(),recvtag+12); - req1[13] = Comm.Isend(&sendCount_xZ,1,rank_xZ(),sendtag+13); - req2[13] = Comm.Irecv(&recvCount_Xz,1,rank_Xz(),recvtag+13); - req1[14] = Comm.Isend(&sendCount_yz,1,rank_yz(),sendtag+14); - req2[14] = Comm.Irecv(&recvCount_YZ,1,rank_YZ(),recvtag+14); - req1[15] = Comm.Isend(&sendCount_YZ,1,rank_YZ(),sendtag+15); - req2[15] = Comm.Irecv(&recvCount_yz,1,rank_yz(),recvtag+15); - req1[16] = Comm.Isend(&sendCount_Yz,1,rank_Yz(),sendtag+16); - req2[16] = Comm.Irecv(&recvCount_yZ,1,rank_yZ(),recvtag+16); - req1[17] = Comm.Isend(&sendCount_yZ,1,rank_yZ(),sendtag+17); - req2[17] = Comm.Irecv(&recvCount_Yz,1,rank_Yz(),recvtag+17); - Comm.waitAll(18,req1); - Comm.waitAll(18,req2); - Comm.barrier(); - // allocate recv lists - recvList_x.resize( recvCount_x, 0 ); - recvList_y.resize( recvCount_y, 0 ); - recvList_z.resize( recvCount_z, 0 ); - recvList_X.resize( recvCount_X, 0 ); - recvList_Y.resize( recvCount_Y, 0 ); - recvList_Z.resize( recvCount_Z, 0 ); - recvList_xy.resize( recvCount_xy, 0 ); - recvList_yz.resize( recvCount_yz, 0 ); - recvList_xz.resize( recvCount_xz, 0 ); - recvList_Xy.resize( recvCount_Xy, 0 ); - recvList_Yz.resize( recvCount_Yz, 0 ); - recvList_xZ.resize( recvCount_xZ, 0 ); - recvList_xY.resize( recvCount_xY, 0 ); - recvList_yZ.resize( recvCount_yZ, 0 ); - recvList_Xz.resize( recvCount_Xz, 0 ); - recvList_XY.resize( recvCount_XY, 0 ); - recvList_YZ.resize( recvCount_YZ, 0 ); - recvList_XZ.resize( recvCount_XZ, 0 ); - //...................................................................................... - req1[0] = Comm.Isend(sendList_x.data(),sendCount_x,rank_x(),sendtag); - req2[0] = Comm.Irecv(recvList_X.data(),recvCount_X,rank_X(),recvtag); - req1[1] = Comm.Isend(sendList_X.data(),sendCount_X,rank_X(),sendtag); - req2[1] = Comm.Irecv(recvList_x.data(),recvCount_x,rank_x(),recvtag); - req1[2] = Comm.Isend(sendList_y.data(),sendCount_y,rank_y(),sendtag); - req2[2] = Comm.Irecv(recvList_Y.data(),recvCount_Y,rank_Y(),recvtag); - req1[3] = Comm.Isend(sendList_Y.data(),sendCount_Y,rank_Y(),sendtag); - req2[3] = Comm.Irecv(recvList_y.data(),recvCount_y,rank_y(),recvtag); - req1[4] = Comm.Isend(sendList_z.data(),sendCount_z,rank_z(),sendtag); - req2[4] = Comm.Irecv(recvList_Z.data(),recvCount_Z,rank_Z(),recvtag); - req1[5] = Comm.Isend(sendList_Z.data(),sendCount_Z,rank_Z(),sendtag); - req2[5] = Comm.Irecv(recvList_z.data(),recvCount_z,rank_z(),recvtag); - req1[6] = Comm.Isend(sendList_xy.data(),sendCount_xy,rank_xy(),sendtag); - req2[6] = Comm.Irecv(recvList_XY.data(),recvCount_XY,rank_XY(),recvtag); - req1[7] = Comm.Isend(sendList_XY.data(),sendCount_XY,rank_XY(),sendtag); - req2[7] = Comm.Irecv(recvList_xy.data(),recvCount_xy,rank_xy(),recvtag); - req1[8] = Comm.Isend(sendList_Xy.data(),sendCount_Xy,rank_Xy(),sendtag); - req2[8] = Comm.Irecv(recvList_xY.data(),recvCount_xY,rank_xY(),recvtag); - req1[9] = Comm.Isend(sendList_xY.data(),sendCount_xY,rank_xY(),sendtag); - req2[9] = Comm.Irecv(recvList_Xy.data(),recvCount_Xy,rank_Xy(),recvtag); - req1[10] = Comm.Isend(sendList_xz.data(),sendCount_xz,rank_xz(),sendtag); - req2[10] = Comm.Irecv(recvList_XZ.data(),recvCount_XZ,rank_XZ(),recvtag); - req1[11] = Comm.Isend(sendList_XZ.data(),sendCount_XZ,rank_XZ(),sendtag); - req2[11] = Comm.Irecv(recvList_xz.data(),recvCount_xz,rank_xz(),recvtag); - req1[12] = Comm.Isend(sendList_Xz.data(),sendCount_Xz,rank_Xz(),sendtag); - req2[12] = Comm.Irecv(recvList_xZ.data(),recvCount_xZ,rank_xZ(),recvtag); - req1[13] = Comm.Isend(sendList_xZ.data(),sendCount_xZ,rank_xZ(),sendtag); - req2[13] = Comm.Irecv(recvList_Xz.data(),recvCount_Xz,rank_Xz(),recvtag); - req1[14] = Comm.Isend(sendList_yz.data(),sendCount_yz,rank_yz(),sendtag); - req2[14] = Comm.Irecv(recvList_YZ.data(),recvCount_YZ,rank_YZ(),recvtag); - req1[15] = Comm.Isend(sendList_YZ.data(),sendCount_YZ,rank_YZ(),sendtag); - req2[15] = Comm.Irecv(recvList_yz.data(),recvCount_yz,rank_yz(),recvtag); - req1[16] = Comm.Isend(sendList_Yz.data(),sendCount_Yz,rank_Yz(),sendtag); - req2[16] = Comm.Irecv(recvList_yZ.data(),recvCount_yZ,rank_yZ(),recvtag); - req1[17] = Comm.Isend(sendList_yZ.data(),sendCount_yZ,rank_yZ(),sendtag); - req2[17] = Comm.Irecv(recvList_Yz.data(),recvCount_Yz,rank_Yz(),recvtag); - Comm.waitAll(18,req1); - Comm.waitAll(18,req2); - //...................................................................................... - for (int idx=0; idx 0) iVol_global = 1.0/(1.0*(Nx-2)*nprocx()*(Ny-2)*nprocy()*((Nz-2)*nprocz()-6)); - //......................................................... - // If external boundary conditions are applied remove solid - if (BoundaryCondition > 0 && kproc() == 0){ - if (inlet_layers_z < 4) inlet_layers_z=4; - for (int k=0; k 0 && kproc() == nprocz()-1){ - if (outlet_layers_z < 4) outlet_layers_z=4; - for (int k=Nz-outlet_layers_z; k 0){ - sum_local+=1.0; + // Compute the porosity + double sum; + double sum_local = 0.0; + double iVol_global = 1.0 / (1.0 * (Nx - 2) * (Ny - 2) * (Nz - 2) * nprocs); + if (BoundaryCondition > 0) + iVol_global = 1.0 / (1.0 * (Nx - 2) * nprocx() * (Ny - 2) * nprocy() * + ((Nz - 2) * nprocz() - 6)); + //......................................................... + // If external boundary conditions are applied remove solid + if (BoundaryCondition > 0 && kproc() == 0) { + if (inlet_layers_z < 4) + inlet_layers_z = 4; + for (int k = 0; k < inlet_layers_z; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + id[n] = 1; + } + } + } + } + if (BoundaryCondition > 0 && kproc() == nprocz() - 1) { + if (outlet_layers_z < 4) + outlet_layers_z = 4; + for (int k = Nz - outlet_layers_z; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + id[n] = 2; + } + } + } + } + for (int k = inlet_layers_z + 1; k < Nz - outlet_layers_z - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + int n = k * Nx * Ny + j * Nx + i; + if (id[n] > 0) { + sum_local += 1.0; } } } } sum = Comm.sumReduce(sum_local); - porosity = sum*iVol_global; - if (rank()==0) printf("Media porosity = %f \n",porosity); - //......................................................... + porosity = sum * iVol_global; + if (rank() == 0) + printf("Media porosity = %f \n", porosity); + //......................................................... } -int Domain::PoreCount(){ - /* +int Domain::PoreCount() { + /* * count the number of nodes occupied by mobile phases */ - int Npore=0; // number of local pore nodes - for (int k=1;k 0){ + int Npore = 0; // number of local pore nodes + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + int n = k * Nx * Ny + j * Nx + i; + if (id[n] > 0) { Npore++; } } @@ -1067,407 +1186,463 @@ int Domain::PoreCount(){ return Npore; } -void Domain::CommunicateMeshHalo(DoubleArray &Mesh) -{ - int sendtag, recvtag; - sendtag = recvtag = 7; - double *MeshData = Mesh.data(); - // send buffers - auto sendData_x = new double [sendCount("x")]; - auto sendData_y = new double [sendCount("y")]; - auto sendData_z = new double [sendCount("z")]; - auto sendData_X = new double [sendCount("X")]; - auto sendData_Y = new double [sendCount("Y")]; - auto sendData_Z = new double [sendCount("Z")]; - auto sendData_xy = new double [sendCount("xy")]; - auto sendData_yz = new double [sendCount("yz")]; - auto sendData_xz = new double [sendCount("xz")]; - auto sendData_Xy = new double [sendCount("Xy")]; - auto sendData_Yz = new double [sendCount("Yz")]; - auto sendData_xZ = new double [sendCount("xZ")]; - auto sendData_xY = new double [sendCount("xY")]; - auto sendData_yZ = new double [sendCount("yZ")]; - auto sendData_Xz = new double [sendCount("Xz")]; - auto sendData_XY = new double [sendCount("XY")]; - auto sendData_YZ = new double [sendCount("YZ")]; - auto sendData_XZ = new double [sendCount("XZ")]; - // recv buffers - auto recvData_x = new double [recvCount("x")]; - auto recvData_y = new double [recvCount("y")]; - auto recvData_z = new double [recvCount("z")]; - auto recvData_X = new double [recvCount("X")]; - auto recvData_Y = new double [recvCount("Y")]; - auto recvData_Z = new double [recvCount("Z")]; - auto recvData_xy = new double [recvCount("xy")]; - auto recvData_yz = new double [recvCount("yz")]; - auto recvData_xz = new double [recvCount("xz")]; - auto recvData_Xy = new double [recvCount("Xy")]; - auto recvData_xZ = new double [recvCount("xZ")]; - auto recvData_xY = new double [recvCount("xY")]; - auto recvData_yZ = new double [recvCount("yZ")]; - auto recvData_Yz = new double [recvCount("Yz")]; - auto recvData_Xz = new double [recvCount("Xz")]; - auto recvData_XY = new double [recvCount("XY")]; - auto recvData_YZ = new double [recvCount("YZ")]; - auto recvData_XZ = new double [recvCount("XZ")]; - // Pack data - PackMeshData(sendList("x"), sendCount("x"), sendData_x, MeshData); - PackMeshData(sendList("X"), sendCount("X"), sendData_X, MeshData); - PackMeshData(sendList("y"), sendCount("y"), sendData_y, MeshData); - PackMeshData(sendList("Y"), sendCount("Y"), sendData_Y, MeshData); - PackMeshData(sendList("z"), sendCount("z"), sendData_z, MeshData); - PackMeshData(sendList("Z"), sendCount("Z"), sendData_Z, MeshData); - PackMeshData(sendList("xy"), sendCount("xy"), sendData_xy, MeshData); - PackMeshData(sendList("Xy"), sendCount("Xy"), sendData_Xy, MeshData); - PackMeshData(sendList("xY"), sendCount("xY"), sendData_xY, MeshData); - PackMeshData(sendList("XY"), sendCount("XY"), sendData_XY, MeshData); - PackMeshData(sendList("xz"), sendCount("xz"), sendData_xz, MeshData); - PackMeshData(sendList("Xz"), sendCount("Xz"), sendData_Xz, MeshData); - PackMeshData(sendList("xZ"), sendCount("xZ"), sendData_xZ, MeshData); - PackMeshData(sendList("XZ"), sendCount("XZ"), sendData_XZ, MeshData); - PackMeshData(sendList("yz"), sendCount("yz"), sendData_yz, MeshData); - PackMeshData(sendList("Yz"), sendCount("Yz"), sendData_Yz, MeshData); - PackMeshData(sendList("yZ"), sendCount("yZ"), sendData_yZ, MeshData); - PackMeshData(sendList("YZ"), sendCount("YZ"), sendData_YZ, MeshData); - // send/recv - Comm.sendrecv(sendData_x,sendCount("x"),rank_x(),sendtag,recvData_X,recvCount("X"),rank_X(),recvtag); - Comm.sendrecv(sendData_X,sendCount("X"),rank_X(),sendtag,recvData_x,recvCount("x"),rank_x(),recvtag); - Comm.sendrecv(sendData_y,sendCount("y"),rank_y(),sendtag,recvData_Y,recvCount("Y"),rank_Y(),recvtag); - Comm.sendrecv(sendData_Y,sendCount("Y"),rank_Y(),sendtag,recvData_y,recvCount("y"),rank_y(),recvtag); - Comm.sendrecv(sendData_z,sendCount("z"),rank_z(),sendtag,recvData_Z,recvCount("Z"),rank_Z(),recvtag); - Comm.sendrecv(sendData_Z,sendCount("Z"),rank_Z(),sendtag,recvData_z,recvCount("z"),rank_z(),recvtag); - Comm.sendrecv(sendData_xy,sendCount("xy"),rank_xy(),sendtag,recvData_XY,recvCount("XY"),rank_XY(),recvtag); - Comm.sendrecv(sendData_XY,sendCount("XY"),rank_XY(),sendtag,recvData_xy,recvCount("xy"),rank_xy(),recvtag); - Comm.sendrecv(sendData_Xy,sendCount("Xy"),rank_Xy(),sendtag,recvData_xY,recvCount("xY"),rank_xY(),recvtag); - Comm.sendrecv(sendData_xY,sendCount("xY"),rank_xY(),sendtag,recvData_Xy,recvCount("Xy"),rank_Xy(),recvtag); - Comm.sendrecv(sendData_xz,sendCount("xz"),rank_xz(),sendtag,recvData_XZ,recvCount("XZ"),rank_XZ(),recvtag); - Comm.sendrecv(sendData_XZ,sendCount("XZ"),rank_XZ(),sendtag,recvData_xz,recvCount("xz"),rank_xz(),recvtag); - Comm.sendrecv(sendData_Xz,sendCount("Xz"),rank_Xz(),sendtag,recvData_xZ,recvCount("xZ"),rank_xZ(),recvtag); - Comm.sendrecv(sendData_xZ,sendCount("xZ"),rank_xZ(),sendtag,recvData_Xz,recvCount("Xz"),rank_Xz(),recvtag); - Comm.sendrecv(sendData_yz,sendCount("yz"),rank_yz(),sendtag,recvData_YZ,recvCount("YZ"),rank_YZ(),recvtag); - Comm.sendrecv(sendData_YZ,sendCount("YZ"),rank_YZ(),sendtag,recvData_yz,recvCount("yz"),rank_yz(),recvtag); - Comm.sendrecv(sendData_Yz,sendCount("Yz"),rank_Yz(),sendtag,recvData_yZ,recvCount("yZ"),rank_yZ(),recvtag); - Comm.sendrecv(sendData_yZ,sendCount("yZ"),rank_yZ(),sendtag,recvData_Yz,recvCount("Yz"),rank_Yz(),recvtag); - // unpack data - UnpackMeshData(recvList("x"), recvCount("x") ,recvData_x, MeshData); - UnpackMeshData(recvList("X"), recvCount("X") ,recvData_X, MeshData); - UnpackMeshData(recvList("y"), recvCount("y") ,recvData_y, MeshData); - UnpackMeshData(recvList("Y"), recvCount("Y") ,recvData_Y, MeshData); - UnpackMeshData(recvList("z"), recvCount("z") ,recvData_z, MeshData); - UnpackMeshData(recvList("Z"), recvCount("Z") ,recvData_Z, MeshData); - UnpackMeshData(recvList("xy"), recvCount("xy") ,recvData_xy, MeshData); - UnpackMeshData(recvList("Xy"), recvCount("Xy") ,recvData_Xy, MeshData); - UnpackMeshData(recvList("xY"), recvCount("xY") ,recvData_xY, MeshData); - UnpackMeshData(recvList("XY"), recvCount("XY") ,recvData_XY, MeshData); - UnpackMeshData(recvList("xz"), recvCount("xz") ,recvData_xz, MeshData); - UnpackMeshData(recvList("Xz"), recvCount("Xz") ,recvData_Xz, MeshData); - UnpackMeshData(recvList("xZ"), recvCount("xZ") ,recvData_xZ, MeshData); - UnpackMeshData(recvList("XZ"), recvCount("XZ") ,recvData_XZ, MeshData); - UnpackMeshData(recvList("yz"), recvCount("yz") ,recvData_yz, MeshData); - UnpackMeshData(recvList("Yz"), recvCount("Yz") ,recvData_Yz, MeshData); - UnpackMeshData(recvList("yZ"), recvCount("yZ") ,recvData_yZ, MeshData); - UnpackMeshData(recvList("YZ"), recvCount("YZ") ,recvData_YZ, MeshData); - // Free sendData - delete [] sendData_x; delete [] sendData_y; delete [] sendData_z; - delete [] sendData_X; delete [] sendData_Y; delete [] sendData_Z; - delete [] sendData_xy; delete [] sendData_xY; delete [] sendData_Xy; - delete [] sendData_XY; delete [] sendData_xz; delete [] sendData_xZ; - delete [] sendData_Xz; delete [] sendData_XZ; delete [] sendData_yz; - delete [] sendData_yZ; delete [] sendData_Yz; delete [] sendData_YZ; - // Free recvData - delete [] recvData_x; delete [] recvData_y; delete [] recvData_z; - delete [] recvData_X; delete [] recvData_Y; delete [] recvData_Z; - delete [] recvData_xy; delete [] recvData_xY; delete [] recvData_Xy; - delete [] recvData_XY; delete [] recvData_xz; delete [] recvData_xZ; - delete [] recvData_Xz; delete [] recvData_XZ; delete [] recvData_yz; - delete [] recvData_yZ; delete [] recvData_Yz; delete [] recvData_YZ; +void Domain::CommunicateMeshHalo(DoubleArray &Mesh) { + int sendtag, recvtag; + sendtag = recvtag = 7; + double *MeshData = Mesh.data(); + // send buffers + auto sendData_x = new double[sendCount("x")]; + auto sendData_y = new double[sendCount("y")]; + auto sendData_z = new double[sendCount("z")]; + auto sendData_X = new double[sendCount("X")]; + auto sendData_Y = new double[sendCount("Y")]; + auto sendData_Z = new double[sendCount("Z")]; + auto sendData_xy = new double[sendCount("xy")]; + auto sendData_yz = new double[sendCount("yz")]; + auto sendData_xz = new double[sendCount("xz")]; + auto sendData_Xy = new double[sendCount("Xy")]; + auto sendData_Yz = new double[sendCount("Yz")]; + auto sendData_xZ = new double[sendCount("xZ")]; + auto sendData_xY = new double[sendCount("xY")]; + auto sendData_yZ = new double[sendCount("yZ")]; + auto sendData_Xz = new double[sendCount("Xz")]; + auto sendData_XY = new double[sendCount("XY")]; + auto sendData_YZ = new double[sendCount("YZ")]; + auto sendData_XZ = new double[sendCount("XZ")]; + // recv buffers + auto recvData_x = new double[recvCount("x")]; + auto recvData_y = new double[recvCount("y")]; + auto recvData_z = new double[recvCount("z")]; + auto recvData_X = new double[recvCount("X")]; + auto recvData_Y = new double[recvCount("Y")]; + auto recvData_Z = new double[recvCount("Z")]; + auto recvData_xy = new double[recvCount("xy")]; + auto recvData_yz = new double[recvCount("yz")]; + auto recvData_xz = new double[recvCount("xz")]; + auto recvData_Xy = new double[recvCount("Xy")]; + auto recvData_xZ = new double[recvCount("xZ")]; + auto recvData_xY = new double[recvCount("xY")]; + auto recvData_yZ = new double[recvCount("yZ")]; + auto recvData_Yz = new double[recvCount("Yz")]; + auto recvData_Xz = new double[recvCount("Xz")]; + auto recvData_XY = new double[recvCount("XY")]; + auto recvData_YZ = new double[recvCount("YZ")]; + auto recvData_XZ = new double[recvCount("XZ")]; + // Pack data + PackMeshData(sendList("x"), sendCount("x"), sendData_x, MeshData); + PackMeshData(sendList("X"), sendCount("X"), sendData_X, MeshData); + PackMeshData(sendList("y"), sendCount("y"), sendData_y, MeshData); + PackMeshData(sendList("Y"), sendCount("Y"), sendData_Y, MeshData); + PackMeshData(sendList("z"), sendCount("z"), sendData_z, MeshData); + PackMeshData(sendList("Z"), sendCount("Z"), sendData_Z, MeshData); + PackMeshData(sendList("xy"), sendCount("xy"), sendData_xy, MeshData); + PackMeshData(sendList("Xy"), sendCount("Xy"), sendData_Xy, MeshData); + PackMeshData(sendList("xY"), sendCount("xY"), sendData_xY, MeshData); + PackMeshData(sendList("XY"), sendCount("XY"), sendData_XY, MeshData); + PackMeshData(sendList("xz"), sendCount("xz"), sendData_xz, MeshData); + PackMeshData(sendList("Xz"), sendCount("Xz"), sendData_Xz, MeshData); + PackMeshData(sendList("xZ"), sendCount("xZ"), sendData_xZ, MeshData); + PackMeshData(sendList("XZ"), sendCount("XZ"), sendData_XZ, MeshData); + PackMeshData(sendList("yz"), sendCount("yz"), sendData_yz, MeshData); + PackMeshData(sendList("Yz"), sendCount("Yz"), sendData_Yz, MeshData); + PackMeshData(sendList("yZ"), sendCount("yZ"), sendData_yZ, MeshData); + PackMeshData(sendList("YZ"), sendCount("YZ"), sendData_YZ, MeshData); + // send/recv + Comm.sendrecv(sendData_x, sendCount("x"), rank_x(), sendtag, recvData_X, + recvCount("X"), rank_X(), recvtag); + Comm.sendrecv(sendData_X, sendCount("X"), rank_X(), sendtag, recvData_x, + recvCount("x"), rank_x(), recvtag); + Comm.sendrecv(sendData_y, sendCount("y"), rank_y(), sendtag, recvData_Y, + recvCount("Y"), rank_Y(), recvtag); + Comm.sendrecv(sendData_Y, sendCount("Y"), rank_Y(), sendtag, recvData_y, + recvCount("y"), rank_y(), recvtag); + Comm.sendrecv(sendData_z, sendCount("z"), rank_z(), sendtag, recvData_Z, + recvCount("Z"), rank_Z(), recvtag); + Comm.sendrecv(sendData_Z, sendCount("Z"), rank_Z(), sendtag, recvData_z, + recvCount("z"), rank_z(), recvtag); + Comm.sendrecv(sendData_xy, sendCount("xy"), rank_xy(), sendtag, recvData_XY, + recvCount("XY"), rank_XY(), recvtag); + Comm.sendrecv(sendData_XY, sendCount("XY"), rank_XY(), sendtag, recvData_xy, + recvCount("xy"), rank_xy(), recvtag); + Comm.sendrecv(sendData_Xy, sendCount("Xy"), rank_Xy(), sendtag, recvData_xY, + recvCount("xY"), rank_xY(), recvtag); + Comm.sendrecv(sendData_xY, sendCount("xY"), rank_xY(), sendtag, recvData_Xy, + recvCount("Xy"), rank_Xy(), recvtag); + Comm.sendrecv(sendData_xz, sendCount("xz"), rank_xz(), sendtag, recvData_XZ, + recvCount("XZ"), rank_XZ(), recvtag); + Comm.sendrecv(sendData_XZ, sendCount("XZ"), rank_XZ(), sendtag, recvData_xz, + recvCount("xz"), rank_xz(), recvtag); + Comm.sendrecv(sendData_Xz, sendCount("Xz"), rank_Xz(), sendtag, recvData_xZ, + recvCount("xZ"), rank_xZ(), recvtag); + Comm.sendrecv(sendData_xZ, sendCount("xZ"), rank_xZ(), sendtag, recvData_Xz, + recvCount("Xz"), rank_Xz(), recvtag); + Comm.sendrecv(sendData_yz, sendCount("yz"), rank_yz(), sendtag, recvData_YZ, + recvCount("YZ"), rank_YZ(), recvtag); + Comm.sendrecv(sendData_YZ, sendCount("YZ"), rank_YZ(), sendtag, recvData_yz, + recvCount("yz"), rank_yz(), recvtag); + Comm.sendrecv(sendData_Yz, sendCount("Yz"), rank_Yz(), sendtag, recvData_yZ, + recvCount("yZ"), rank_yZ(), recvtag); + Comm.sendrecv(sendData_yZ, sendCount("yZ"), rank_yZ(), sendtag, recvData_Yz, + recvCount("Yz"), rank_Yz(), recvtag); + // unpack data + UnpackMeshData(recvList("x"), recvCount("x"), recvData_x, MeshData); + UnpackMeshData(recvList("X"), recvCount("X"), recvData_X, MeshData); + UnpackMeshData(recvList("y"), recvCount("y"), recvData_y, MeshData); + UnpackMeshData(recvList("Y"), recvCount("Y"), recvData_Y, MeshData); + UnpackMeshData(recvList("z"), recvCount("z"), recvData_z, MeshData); + UnpackMeshData(recvList("Z"), recvCount("Z"), recvData_Z, MeshData); + UnpackMeshData(recvList("xy"), recvCount("xy"), recvData_xy, MeshData); + UnpackMeshData(recvList("Xy"), recvCount("Xy"), recvData_Xy, MeshData); + UnpackMeshData(recvList("xY"), recvCount("xY"), recvData_xY, MeshData); + UnpackMeshData(recvList("XY"), recvCount("XY"), recvData_XY, MeshData); + UnpackMeshData(recvList("xz"), recvCount("xz"), recvData_xz, MeshData); + UnpackMeshData(recvList("Xz"), recvCount("Xz"), recvData_Xz, MeshData); + UnpackMeshData(recvList("xZ"), recvCount("xZ"), recvData_xZ, MeshData); + UnpackMeshData(recvList("XZ"), recvCount("XZ"), recvData_XZ, MeshData); + UnpackMeshData(recvList("yz"), recvCount("yz"), recvData_yz, MeshData); + UnpackMeshData(recvList("Yz"), recvCount("Yz"), recvData_Yz, MeshData); + UnpackMeshData(recvList("yZ"), recvCount("yZ"), recvData_yZ, MeshData); + UnpackMeshData(recvList("YZ"), recvCount("YZ"), recvData_YZ, MeshData); + // Free sendData + delete[] sendData_x; + delete[] sendData_y; + delete[] sendData_z; + delete[] sendData_X; + delete[] sendData_Y; + delete[] sendData_Z; + delete[] sendData_xy; + delete[] sendData_xY; + delete[] sendData_Xy; + delete[] sendData_XY; + delete[] sendData_xz; + delete[] sendData_xZ; + delete[] sendData_Xz; + delete[] sendData_XZ; + delete[] sendData_yz; + delete[] sendData_yZ; + delete[] sendData_Yz; + delete[] sendData_YZ; + // Free recvData + delete[] recvData_x; + delete[] recvData_y; + delete[] recvData_z; + delete[] recvData_X; + delete[] recvData_Y; + delete[] recvData_Z; + delete[] recvData_xy; + delete[] recvData_xY; + delete[] recvData_Xy; + delete[] recvData_XY; + delete[] recvData_xz; + delete[] recvData_xZ; + delete[] recvData_Xz; + delete[] recvData_XZ; + delete[] recvData_yz; + delete[] recvData_yZ; + delete[] recvData_Yz; + delete[] recvData_YZ; } // Ideally stuff below here should be moved somewhere else -- doesn't really belong here -void WriteCheckpoint(const char *FILENAME, const double *cDen, const double *cfq, size_t Np) -{ +void WriteCheckpoint(const char *FILENAME, const double *cDen, + const double *cfq, size_t Np) { double value; - ofstream File(FILENAME,ios::binary); - for (size_t n=0; ngetVector( "n" ); - auto SIZE = database->getVector( "N" ); - auto nproc = database->getVector( "nproc" ); - //TODO currently the funcationality "offset" is disabled as the user-defined input data may have a different size from that of the input domain - if (database->keyExists( "offset" )){ - auto offset = database->getVector( "offset" ); - xStart = offset[0]; - yStart = offset[1]; - zStart = offset[2]; - } - - nx = size[0]; - ny = size[1]; - nz = size[2]; - nprocx = nproc[0]; - nprocy = nproc[1]; - nprocz = nproc[2]; - global_Nx = SIZE[0]; - global_Ny = SIZE[1]; - global_Nz = SIZE[2]; - nprocs=nprocx*nprocy*nprocz; + auto size = database->getVector("n"); + auto SIZE = database->getVector("N"); + auto nproc = database->getVector("nproc"); + //TODO currently the funcationality "offset" is disabled as the user-defined input data may have a different size from that of the input domain + if (database->keyExists("offset")) { + auto offset = database->getVector("offset"); + xStart = offset[0]; + yStart = offset[1]; + zStart = offset[2]; + } - double *SegData = NULL; - if (RANK==0){ - printf("User-defined input file: %s (data type: %s)\n",Filename.c_str(),Datatype.c_str()); - printf("NOTE: currently only BC=0 or 5 supports user-defined input file!\n"); - // Rank=0 reads the entire segmented data and distributes to worker processes - printf("Dimensions of the user-defined input file: %ld x %ld x %ld \n",global_Nx,global_Ny,global_Nz); - int64_t SIZE = global_Nx*global_Ny*global_Nz; + nx = size[0]; + ny = size[1]; + nz = size[2]; + nprocx = nproc[0]; + nprocy = nproc[1]; + nprocz = nproc[2]; + global_Nx = SIZE[0]; + global_Ny = SIZE[1]; + global_Nz = SIZE[2]; + nprocs = nprocx * nprocy * nprocz; - if (Datatype == "double"){ - printf("Reading input data as double precision floating number\n"); - SegData = new double[SIZE]; - FILE *SEGDAT = fopen(Filename.c_str(),"rb"); - if (SEGDAT==NULL) ERROR("Domain.cpp: Error reading user-defined file!\n"); - size_t ReadSeg; - ReadSeg=fread(SegData,8,SIZE,SEGDAT); - if (ReadSeg != size_t(SIZE)) printf("Domain.cpp: Error reading file: %s\n",Filename.c_str()); - fclose(SEGDAT); - } - else{ - ERROR("Error: User-defined input file only supports double-precision floating number!\n"); + double *SegData = NULL; + if (RANK == 0) { + printf("User-defined input file: %s (data type: %s)\n", + Filename.c_str(), Datatype.c_str()); + printf("NOTE: currently only BC=0 or 5 supports user-defined input " + "file!\n"); + // Rank=0 reads the entire segmented data and distributes to worker processes + printf("Dimensions of the user-defined input file: %ld x %ld x %ld \n", + global_Nx, global_Ny, global_Nz); + int64_t SIZE = global_Nx * global_Ny * global_Nz; + + if (Datatype == "double") { + printf("Reading input data as double precision floating number\n"); + SegData = new double[SIZE]; + FILE *SEGDAT = fopen(Filename.c_str(), "rb"); + if (SEGDAT == NULL) + ERROR("Domain.cpp: Error reading user-defined file!\n"); + size_t ReadSeg; + ReadSeg = fread(SegData, 8, SIZE, SEGDAT); + if (ReadSeg != size_t(SIZE)) + printf("Domain.cpp: Error reading file: %s\n", + Filename.c_str()); + fclose(SEGDAT); + } else { + ERROR("Error: User-defined input file only supports " + "double-precision floating number!\n"); } - printf("Read file successfully from %s \n",Filename.c_str()); - } - - // Get the rank info - int64_t N = (nx+2)*(ny+2)*(nz+2); + printf("Read file successfully from %s \n", Filename.c_str()); + } - // number of sites to use for periodic boundary condition transition zone - //int64_t z_transition_size = (nprocz*nz - (global_Nz - zStart))/2; - //if (z_transition_size < 0) z_transition_size=0; - int64_t z_transition_size = 0; + // Get the rank info + int64_t N = (nx + 2) * (ny + 2) * (nz + 2); - //char LocalRankFilename[1000];//just for debug - double *loc_id; - loc_id = new double [(nx+2)*(ny+2)*(nz+2)]; + // number of sites to use for periodic boundary condition transition zone + //int64_t z_transition_size = (nprocz*nz - (global_Nz - zStart))/2; + //if (z_transition_size < 0) z_transition_size=0; + int64_t z_transition_size = 0; - // Set up the sub-domains - if (RANK==0){ + //char LocalRankFilename[1000];//just for debug + double *loc_id; + loc_id = new double[(nx + 2) * (ny + 2) * (nz + 2)]; + + // Set up the sub-domains + if (RANK == 0) { printf("Decomposing user-defined input file\n"); - printf("Distributing subdomains across %i processors \n",nprocs); - printf("Process grid: %i x %i x %i \n",nprocx,nprocy,nprocz); - printf("Subdomain size: %i x %i x %i \n",nx,ny,nz); - printf("Size of transition region: %ld \n", z_transition_size); + printf("Distributing subdomains across %i processors \n", nprocs); + printf("Process grid: %i x %i x %i \n", nprocx, nprocy, nprocz); + printf("Subdomain size: %i x %i x %i \n", nx, ny, nz); + printf("Size of transition region: %ld \n", z_transition_size); - for (int kp=0; kp db, const Utilities::MPI& Communicator); + Domain(std::shared_ptr db, const Utilities::MPI &Communicator); /** * \brief Obsolete constructor */ - Domain( int nx, int ny, int nz, int rnk, int npx, int npy, int npz, - double lx, double ly, double lz, int BC); + Domain(int nx, int ny, int nz, int rnk, int npx, int npy, int npz, + double lx, double ly, double lz, int BC); /** * \brief Empty constructor @@ -86,18 +85,18 @@ public: /** * \brief Copy constructor */ - Domain( const Domain& ) = delete; + Domain(const Domain &) = delete; /** * \brief Assignment operator - */ - Domain& operator=( const Domain& ) = delete; + */ + Domain &operator=(const Domain &) = delete; /** * \brief Destructor - */ + */ ~Domain(); - + /** * \brief Get the database */ @@ -106,37 +105,33 @@ public: /** * \brief Get the domain box */ - inline const Box& getBox() const { return d_box; } + inline const Box &getBox() const { return d_box; } /** * \brief Get local patch */ - inline const Patch& getLocalPatch() const { return *d_localPatch; } + inline const Patch &getLocalPatch() const { return *d_localPatch; } /** * \brief Get all patches - */ - inline const std::vector& getAllPatch() const { return d_patches; } - + */ + inline const std::vector &getAllPatch() const { return d_patches; } private: - /** * \brief initialize from database - */ - void initialize( std::shared_ptr db ); + */ + void initialize(std::shared_ptr db); std::shared_ptr d_db; Box d_box; Patch *d_localPatch; std::vector d_patches; - public: // Public variables (need to create accessors instead) - std::shared_ptr database; - double Lx,Ly,Lz,Volume,voxel_length; - int Nx,Ny,Nz,N; + double Lx, Ly, Lz, Volume, voxel_length; + int Nx, Ny, Nz, N; int inlet_layers_x, inlet_layers_y, inlet_layers_z; int outlet_layers_x, outlet_layers_y, outlet_layers_z; int inlet_layers_phase; //as usual: 1->n, 2->w @@ -144,7 +139,7 @@ public: // Public variables (need to create accessors instead) double porosity; RankInfoStruct rank_info; - Utilities::MPI Comm; // MPI Communicator for this domain + Utilities::MPI Comm; // MPI Communicator for this domain int BoundaryCondition; @@ -153,7 +148,7 @@ public: // Public variables (need to create accessors instead) //********************************** /** * \brief Compute the porosity based on the current domain id file - */ + */ inline double Porosity() const { return porosity; } inline int iproc() const { return rank_info.ix; } inline int jproc() const { return rank_info.jy; } @@ -186,70 +181,78 @@ public: // Public variables (need to create accessors instead) // Get the actual D3Q19 communication counts (based on location of solid phase) // Discrete velocity set symmetry implies the sendcount = recvcount //...................................................................................... - inline int recvCount( const char* dir ) const { return getRecvList( dir ).size(); } - inline int sendCount( const char* dir ) const { return getSendList( dir ).size(); } - inline const int* recvList( const char* dir ) const { return getRecvList( dir ).data(); } - inline const int* sendList( const char* dir ) const { return getSendList( dir ).data(); } + inline int recvCount(const char *dir) const { + return getRecvList(dir).size(); + } + inline int sendCount(const char *dir) const { + return getSendList(dir).size(); + } + inline const int *recvList(const char *dir) const { + return getRecvList(dir).data(); + } + inline const int *sendList(const char *dir) const { + return getSendList(dir).data(); + } - //...................................................................................... + //...................................................................................... // Solid indicator function std::vector id; /** * \brief Read domain IDs from file - */ + */ void ReadIDs(); - + /** * \brief Compute the porosity - */ + */ void ComputePorosity(); - + /** * \brief Read image and perform domain decomposition * @param filename - name of file to read IDs - */ - void Decomp( const std::string& filename ); - + */ + void Decomp(const std::string &filename); + /** * \brief Perform a halo exchange using MPI * @param Mesh - array data that holds scalar values - */ + */ void CommunicateMeshHalo(DoubleArray &Mesh); - + /** * \brief Initialize communication data structures within Domain object. * This routine needs to be called before the communication functionality will work - */ - void CommInit(); - + */ + void CommInit(); + /** * \brief Count number of pore nodes (labels > 1) - */ + */ int PoreCount(); - + /** * \brief Read array data from a file and distribute to local arrays for each MPI process * @param Filename - name of the file to read the data * @param Datatype - data type to use * @param UserData - Array to store the values that are read - */ - void ReadFromFile(const std::string& Filename,const std::string& Datatype, double *UserData); - + */ + void ReadFromFile(const std::string &Filename, const std::string &Datatype, + double *UserData); + /** * \brief Aggregate labels from all MPI processes and write to a file * @param filename - name of the file to write */ - void AggregateLabels( const std::string& filename ); + void AggregateLabels(const std::string &filename); /** * \brief Aggregate user provided array from all MPI processes and write to a single file * @param filename - name of the file to write * @param UserData - array data to aggregate and write */ - void AggregateLabels( const std::string& filename, DoubleArray &UserData ); + void AggregateLabels(const std::string &filename, DoubleArray &UserData); private: - /** * \brief Pack halo data for 8-bit integer * @param list - list of values in the halo @@ -258,7 +261,7 @@ private: * @param ID - 8-bit values on mesh [Nx, Ny, Nz] */ void PackID(int *list, int count, signed char *sendbuf, signed char *ID); - + /** * \brief Unpack halo data for 8-bit integer * @param list - list of values in the halo @@ -267,29 +270,32 @@ private: * @param ID - 8-bit values on mesh [Nx, Ny, Nz] */ void UnpackID(int *list, int count, signed char *recvbuf, signed char *ID); - - //...................................................................................... - MPI_Request req1[18], req2[18]; - //...................................................................................... - std::vector sendList_x, sendList_y, sendList_z, sendList_X, sendList_Y, sendList_Z; - std::vector sendList_xy, sendList_yz, sendList_xz, sendList_Xy, sendList_Yz, sendList_xZ; - std::vector sendList_xY, sendList_yZ, sendList_Xz, sendList_XY, sendList_YZ, sendList_XZ; - //...................................................................................... - std::vector recvList_x, recvList_y, recvList_z, recvList_X, recvList_Y, recvList_Z; - std::vector recvList_xy, recvList_yz, recvList_xz, recvList_Xy, recvList_Yz, recvList_xZ; - std::vector recvList_xY, recvList_yZ, recvList_Xz, recvList_XY, recvList_YZ, recvList_XZ; - //...................................................................................... - const std::vector& getRecvList( const char* dir ) const; - const std::vector& getSendList( const char* dir ) const; + //...................................................................................... + MPI_Request req1[18], req2[18]; + //...................................................................................... + std::vector sendList_x, sendList_y, sendList_z, sendList_X, sendList_Y, + sendList_Z; + std::vector sendList_xy, sendList_yz, sendList_xz, sendList_Xy, + sendList_Yz, sendList_xZ; + std::vector sendList_xY, sendList_yZ, sendList_Xz, sendList_XY, + sendList_YZ, sendList_XZ; + //...................................................................................... + std::vector recvList_x, recvList_y, recvList_z, recvList_X, recvList_Y, + recvList_Z; + std::vector recvList_xy, recvList_yz, recvList_xz, recvList_Xy, + recvList_Yz, recvList_xZ; + std::vector recvList_xY, recvList_yZ, recvList_Xz, recvList_XY, + recvList_YZ, recvList_XZ; + //...................................................................................... + const std::vector &getRecvList(const char *dir) const; + const std::vector &getSendList(const char *dir) const; }; -template class PatchData; - +template class PatchData; enum class DataLocation { CPU, DEVICE }; - /** * \class Patch * @@ -298,44 +304,40 @@ enum class DataLocation { CPU, DEVICE }; */ class Patch { public: - //! Empty constructor Patch() = delete; //! Copy constructor - Patch( const Patch& ) = delete; + Patch(const Patch &) = delete; //! Assignment operator - Patch& operator=( const Patch& ) = delete; + Patch &operator=(const Patch &) = delete; //! Return the box for the patch - inline const Box& getBox() const { return d_box; } + inline const Box &getBox() const { return d_box; } //! Create patch data - template - std::shared_ptr> createPatchData( DataLocation location ) const; + template + std::shared_ptr> + createPatchData(DataLocation location) const; private: Box d_box; int d_owner; Domain *d_domain; - }; - // Class to hold data on a patch -template -class PatchData { +template class PatchData { public: + //! Get the raw data pointer + TYPE *data() { return d_data; } //! Get the raw data pointer - TYPE* data() { return d_data; } - - //! Get the raw data pointer - const TYPE* data() const { return d_data; } + const TYPE *data() const { return d_data; } //! Get the patch - const Patch& getPatch() const { return *d_patch; } + const Patch &getPatch() const { return *d_patch; } //! Start communication void beginCommunication(); @@ -344,20 +346,20 @@ public: void endCommunication(); //! Access ghost values - TYPE operator()( int, int, int ) const; + TYPE operator()(int, int, int) const; //! Copy data from another PatchData - void copy( const PatchData& rhs ); + void copy(const PatchData &rhs); private: DataLocation d_location; const Patch *d_patch; TYPE *d_data; TYPE *d_gcw; - }; -void WriteCheckpoint(const char *FILENAME, const double *cDen, const double *cfq, size_t Np); +void WriteCheckpoint(const char *FILENAME, const double *cDen, + const double *cfq, size_t Np); void ReadCheckpoint(char *FILENAME, double *cDen, double *cfq, size_t Np); diff --git a/common/FunctionTable.cpp b/common/FunctionTable.cpp index 54dc5848..927b0ee4 100644 --- a/common/FunctionTable.cpp +++ b/common/FunctionTable.cpp @@ -1,6 +1,5 @@ #include "FunctionTable.hpp" - /******************************************************** * Random number generation * ********************************************************/ @@ -93,55 +92,36 @@ template<> long double genRand() /******************************************************** * axpy * ********************************************************/ -template<> -void call_axpy( size_t N, const float alpha, const float *x, float *y ) -{ +template <> +void call_axpy(size_t N, const float alpha, const float *x, float *y) { ERROR("Not finished"); } -template<> -void call_axpy( size_t N, const double alpha, const double *x, double *y ) -{ +template <> +void call_axpy(size_t N, const double alpha, const double *x, + double *y) { ERROR("Not finished"); } - /******************************************************** * Multiply two arrays * ********************************************************/ -template<> -void call_gemv( - size_t M, size_t N, double alpha, double beta, const double *A, const double *x, double *y ) -{ +template <> +void call_gemv(size_t M, size_t N, double alpha, double beta, + const double *A, const double *x, double *y) { ERROR("Not finished"); } -template<> -void call_gemv( - size_t M, size_t N, float alpha, float beta, const float *A, const float *x, float *y ) -{ +template <> +void call_gemv(size_t M, size_t N, float alpha, float beta, + const float *A, const float *x, float *y) { ERROR("Not finished"); } -template<> -void call_gemm( size_t M, - size_t N, - size_t K, - double alpha, - double beta, - const double *A, - const double *B, - double *C ) -{ +template <> +void call_gemm(size_t M, size_t N, size_t K, double alpha, double beta, + const double *A, const double *B, double *C) { ERROR("Not finished"); } -template<> -void call_gemm( size_t M, - size_t N, - size_t K, - float alpha, - float beta, - const float *A, - const float *B, - float *C ) -{ +template <> +void call_gemm(size_t M, size_t N, size_t K, float alpha, float beta, + const float *A, const float *B, float *C) { ERROR("Not finished"); } - diff --git a/common/FunctionTable.h b/common/FunctionTable.h index aa857c15..7a45880b 100644 --- a/common/FunctionTable.h +++ b/common/FunctionTable.h @@ -17,28 +17,23 @@ #ifndef included_FunctionTable #define included_FunctionTable - #include "common/ArraySize.h" #include - - /*! * Class FunctionTable is a serial function table class that defines * a series of operations that can be performed on the Array class. * Users can impliment additional versions of the function table that match * the interface to change the behavior of the array class. */ -class FunctionTable final -{ +class FunctionTable final { public: /*! * Initialize the array with random values * @param[in] x The array to operate on */ - template - static void rand( Array &x ); + template static void rand(Array &x); /*! * Perform a reduce operator y = f(x) @@ -49,8 +44,9 @@ public: * ...) * @return The reduction */ - template - static inline TYPE reduce( LAMBDA &op, const Array &A, const TYPE &initialValue ); + template + static inline TYPE reduce(LAMBDA &op, const Array &A, + const TYPE &initialValue); /*! * Perform a reduce operator z = f(x,y) @@ -62,11 +58,10 @@ public: * ...) * @return The reduction */ - template - static inline TYPE reduce( LAMBDA &op, - const Array &A, - const Array &B, - const TYPE &initialValue ); + template + static inline TYPE reduce(LAMBDA &op, const Array &A, + const Array &B, + const TYPE &initialValue); /*! * Perform a element-wise operation y = f(x) @@ -75,8 +70,9 @@ public: * @param[in,out] x The array to operate on * @param[out] y The output array */ - template - static inline void transform( LAMBDA &fun, const Array &x, Array &y ); + template + static inline void transform(LAMBDA &fun, const Array &x, + Array &y); /*! * Perform a element-wise operation z = f(x,y) @@ -86,11 +82,10 @@ public: * @param[in] y The second array * @param[out] z The output array */ - template - static inline void transform( LAMBDA &fun, - const Array &x, - const Array &y, - Array &z ); + template + static inline void transform(LAMBDA &fun, const Array &x, + const Array &y, + Array &z); /*! * Multiply two arrays @@ -98,9 +93,9 @@ public: * @param[in] b The second array * @param[out] c The output array */ - template - static void - multiply( const Array &a, const Array &b, Array &c ); + template + static void multiply(const Array &a, const Array &b, + Array &c); /*! * Perform dgemv/dgemm equavalent operation ( C = alpha*A*B + beta*C ) @@ -110,12 +105,10 @@ public: * @param[in] beta The scalar value alpha * @param[in,out] C The output array C */ - template - static void gemm( const TYPE alpha, - const Array &A, - const Array &B, - const TYPE beta, - Array &C ); + template + static void gemm(const TYPE alpha, const Array &A, + const Array &B, const TYPE beta, + Array &C); /*! * Perform axpy equavalent operation ( y = alpha*x + y ) @@ -123,8 +116,9 @@ public: * @param[in] x The input array x * @param[in,out] y The output array y */ - template - static void axpy( const TYPE alpha, const Array &x, Array &y ); + template + static void axpy(const TYPE alpha, const Array &x, + Array &y); /*! * Check if two arrays are approximately equal @@ -132,24 +126,15 @@ public: * @param[in] B The second array * @param[in] tol The tolerance */ - template - static bool equals( const Array &A, const Array &B, TYPE tol ); - - template - static inline void gemmWrapper( char TRANSA, - char TRANSB, - int M, - int N, - int K, - TYPE alpha, - const TYPE *A, - int LDA, - const TYPE *B, - int LDB, - TYPE beta, - TYPE *C, - int LDC ); + template + static bool equals(const Array &A, const Array &B, + TYPE tol); + template + static inline void gemmWrapper(char TRANSA, char TRANSB, int M, int N, + int K, TYPE alpha, const TYPE *A, int LDA, + const TYPE *B, int LDB, TYPE beta, TYPE *C, + int LDC); /* Specialized Functions */ @@ -158,62 +143,66 @@ public: * @param[in] A The input array * @param[out] B The output array */ - template - static void transformReLU( const Array &A, Array &B ); + template + static void transformReLU(const Array &A, + Array &B); /*! * Perform a element-wise operation B = |A| * @param[in] A The array to operate on * @param[out] B The output array */ - template - static void transformAbs( const Array &A, Array &B ); + template + static void transformAbs(const Array &A, + Array &B); /*! * Perform a element-wise operation B = tanh(A) * @param[in] A The array to operate on * @param[out] B The output array */ - template - static void transformTanh( const Array &A, Array &B ); + template + static void transformTanh(const Array &A, + Array &B); /*! * Perform a element-wise operation B = max(-1 , min(1 , A) ) * @param[in] A The array to operate on * @param[out] B The output array */ - template - static void transformHardTanh( const Array &A, Array &B ); + template + static void transformHardTanh(const Array &A, + Array &B); /*! * Perform a element-wise operation B = 1 / (1 + exp(-A)) * @param[in] A The array to operate on * @param[out] B The output array */ - template - static void transformSigmoid( const Array &A, Array &B ); + template + static void transformSigmoid(const Array &A, + Array &B); /*! * Perform a element-wise operation B = log(exp(A) + 1) * @param[in] A The array to operate on * @param[out] B The output array */ - template - static void transformSoftPlus( const Array &A, Array &B ); + template + static void transformSoftPlus(const Array &A, + Array &B); /*! * Sum the elements of the Array * @param[in] A The array to sum */ - template - static TYPE sum( const Array &A ); + template + static TYPE sum(const Array &A); private: FunctionTable(); - template - static inline void rand( size_t N, T *x ); + template static inline void rand(size_t N, T *x); }; - #endif diff --git a/common/FunctionTable.hpp b/common/FunctionTable.hpp index d83bd6d3..469df732 100644 --- a/common/FunctionTable.hpp +++ b/common/FunctionTable.hpp @@ -41,7 +41,6 @@ #include //#include - /******************************************************** * Random number initialization * ********************************************************/ @@ -57,287 +56,252 @@ inline void FunctionTable::rand( Array &x ) /******************************************************** * Reduction * ********************************************************/ -template -inline TYPE FunctionTable::reduce( LAMBDA &op, const Array &A, const TYPE &initialValue ) -{ - if ( A.length() == 0 ) +template +inline TYPE FunctionTable::reduce(LAMBDA &op, const Array &A, + const TYPE &initialValue) { + if (A.length() == 0) return TYPE(); const TYPE *x = A.data(); - TYPE y = initialValue; - for ( size_t i = 0; i < A.length(); i++ ) - y = op( x[i], y ); + TYPE y = initialValue; + for (size_t i = 0; i < A.length(); i++) + y = op(x[i], y); return y; } -template -inline TYPE FunctionTable::reduce( LAMBDA &op, - const Array &A, - const Array &B, - const TYPE &initialValue ) -{ - ARRAY_ASSERT( A.length() == B.length() ); - if ( A.length() == 0 ) +template +inline TYPE FunctionTable::reduce(LAMBDA &op, const Array &A, + const Array &B, + const TYPE &initialValue) { + ARRAY_ASSERT(A.length() == B.length()); + if (A.length() == 0) return TYPE(); const TYPE *x = A.data(); const TYPE *y = B.data(); - TYPE z = initialValue; - for ( size_t i = 0; i < A.length(); i++ ) - z = op( x[i], y[i], z ); + TYPE z = initialValue; + for (size_t i = 0; i < A.length(); i++) + z = op(x[i], y[i], z); return z; } - /******************************************************** * Unary transformation * ********************************************************/ -template -inline void FunctionTable::transform( LAMBDA &fun, const Array &x, Array &y ) -{ - y.resize( x.size() ); +template +inline void FunctionTable::transform(LAMBDA &fun, const Array &x, + Array &y) { + y.resize(x.size()); const size_t N = x.length(); - for ( size_t i = 0; i < N; i++ ) - y( i ) = fun( x( i ) ); + for (size_t i = 0; i < N; i++) + y(i) = fun(x(i)); } -template -inline void FunctionTable::transform( LAMBDA &fun, - const Array &x, - const Array &y, - Array &z ) -{ - if ( x.size() != y.size() ) - throw std::logic_error( "Sizes of x and y do not match" ); - z.resize( x.size() ); +template +inline void FunctionTable::transform(LAMBDA &fun, const Array &x, + const Array &y, + Array &z) { + if (x.size() != y.size()) + throw std::logic_error("Sizes of x and y do not match"); + z.resize(x.size()); const size_t N = x.length(); - for ( size_t i = 0; i < N; i++ ) - z( i ) = fun( x( i ), y( i ) ); + for (size_t i = 0; i < N; i++) + z(i) = fun(x(i), y(i)); } - /******************************************************** * axpy * ********************************************************/ -template -void call_axpy( size_t N, const TYPE alpha, const TYPE *x, TYPE *y ); -template<> -void call_axpy( size_t N, const float alpha, const float *x, float *y ); -template<> -void call_axpy( size_t N, const double alpha, const double *x, double *y ); -template -void call_axpy( size_t N, const TYPE alpha, const TYPE *x, TYPE *y ) -{ - for ( size_t i = 0; i < N; i++ ) +template +void call_axpy(size_t N, const TYPE alpha, const TYPE *x, TYPE *y); +template <> +void call_axpy(size_t N, const float alpha, const float *x, float *y); +template <> +void call_axpy(size_t N, const double alpha, const double *x, + double *y); +template +void call_axpy(size_t N, const TYPE alpha, const TYPE *x, TYPE *y) { + for (size_t i = 0; i < N; i++) y[i] += alpha * x[i]; } -template -void FunctionTable::axpy( const TYPE alpha, const Array &x, Array &y ) -{ - if ( x.size() != y.size() ) - throw std::logic_error( "Array sizes do not match" ); - call_axpy( x.length(), alpha, x.data(), y.data() ); +template +void FunctionTable::axpy(const TYPE alpha, const Array &x, + Array &y) { + if (x.size() != y.size()) + throw std::logic_error("Array sizes do not match"); + call_axpy(x.length(), alpha, x.data(), y.data()); } - /******************************************************** * Multiply two arrays * ********************************************************/ -template -void call_gemv( size_t M, size_t N, TYPE alpha, TYPE beta, const TYPE *A, const TYPE *x, TYPE *y ); -template<> -void call_gemv( - size_t M, size_t N, double alpha, double beta, const double *A, const double *x, double *y ); -template<> -void call_gemv( - size_t M, size_t N, float alpha, float beta, const float *A, const float *x, float *y ); -template -void call_gemv( size_t M, size_t N, TYPE alpha, TYPE beta, const TYPE *A, const TYPE *x, TYPE *y ) -{ - for ( size_t i = 0; i < M; i++ ) +template +void call_gemv(size_t M, size_t N, TYPE alpha, TYPE beta, const TYPE *A, + const TYPE *x, TYPE *y); +template <> +void call_gemv(size_t M, size_t N, double alpha, double beta, + const double *A, const double *x, double *y); +template <> +void call_gemv(size_t M, size_t N, float alpha, float beta, + const float *A, const float *x, float *y); +template +void call_gemv(size_t M, size_t N, TYPE alpha, TYPE beta, const TYPE *A, + const TYPE *x, TYPE *y) { + for (size_t i = 0; i < M; i++) y[i] = beta * y[i]; - for ( size_t j = 0; j < N; j++ ) { - for ( size_t i = 0; i < M; i++ ) + for (size_t j = 0; j < N; j++) { + for (size_t i = 0; i < M; i++) y[i] += alpha * A[i + j * M] * x[j]; } } -template -void call_gemm( - size_t M, size_t N, size_t K, TYPE alpha, TYPE beta, const TYPE *A, const TYPE *B, TYPE *C ); -template<> -void call_gemm( size_t M, - size_t N, - size_t K, - double alpha, - double beta, - const double *A, - const double *B, - double *C ); -template<> -void call_gemm( size_t M, - size_t N, - size_t K, - float alpha, - float beta, - const float *A, - const float *B, - float *C ); -template -void call_gemm( - size_t M, size_t N, size_t K, TYPE alpha, TYPE beta, const TYPE *A, const TYPE *B, TYPE *C ) -{ - for ( size_t i = 0; i < K * M; i++ ) +template +void call_gemm(size_t M, size_t N, size_t K, TYPE alpha, TYPE beta, + const TYPE *A, const TYPE *B, TYPE *C); +template <> +void call_gemm(size_t M, size_t N, size_t K, double alpha, double beta, + const double *A, const double *B, double *C); +template <> +void call_gemm(size_t M, size_t N, size_t K, float alpha, float beta, + const float *A, const float *B, float *C); +template +void call_gemm(size_t M, size_t N, size_t K, TYPE alpha, TYPE beta, + const TYPE *A, const TYPE *B, TYPE *C) { + for (size_t i = 0; i < K * M; i++) C[i] = beta * C[i]; - for ( size_t k = 0; k < K; k++ ) { - for ( size_t j = 0; j < N; j++ ) { - for ( size_t i = 0; i < M; i++ ) + for (size_t k = 0; k < K; k++) { + for (size_t j = 0; j < N; j++) { + for (size_t i = 0; i < M; i++) C[i + k * M] += alpha * A[i + j * M] * B[j + k * N]; } } } -template -void FunctionTable::gemm( const TYPE alpha, - const Array &a, - const Array &b, - const TYPE beta, - Array &c ) -{ - if ( a.size( 1 ) != b.size( 0 ) ) - throw std::logic_error( "Inner dimensions must match" ); - if ( a.ndim() == 2 && b.ndim() == 1 ) { - call_gemv( a.size( 0 ), a.size( 1 ), alpha, beta, a.data(), b.data(), c.data() ); - } else if ( a.ndim() <= 2 && b.ndim() <= 2 ) { - call_gemm( - a.size( 0 ), a.size( 1 ), b.size( 1 ), alpha, beta, a.data(), b.data(), c.data() ); +template +void FunctionTable::gemm(const TYPE alpha, const Array &a, + const Array &b, const TYPE beta, + Array &c) { + if (a.size(1) != b.size(0)) + throw std::logic_error("Inner dimensions must match"); + if (a.ndim() == 2 && b.ndim() == 1) { + call_gemv(a.size(0), a.size(1), alpha, beta, a.data(), b.data(), + c.data()); + } else if (a.ndim() <= 2 && b.ndim() <= 2) { + call_gemm(a.size(0), a.size(1), b.size(1), alpha, beta, a.data(), + b.data(), c.data()); } else { - throw std::logic_error( "Not finished yet" ); + throw std::logic_error("Not finished yet"); } } -template -void FunctionTable::multiply( const Array &a, - const Array &b, - Array &c ) -{ - if ( a.size( 1 ) != b.size( 0 ) ) - throw std::logic_error( "Inner dimensions must match" ); - if ( a.ndim() == 2 && b.ndim() == 1 ) { - c.resize( a.size( 0 ) ); - call_gemv( a.size( 0 ), a.size( 1 ), 1, 0, a.data(), b.data(), c.data() ); - } else if ( a.ndim() <= 2 && b.ndim() <= 2 ) { - c.resize( a.size( 0 ), b.size( 1 ) ); - call_gemm( - a.size( 0 ), a.size( 1 ), b.size( 1 ), 1, 0, a.data(), b.data(), c.data() ); +template +void FunctionTable::multiply(const Array &a, + const Array &b, Array &c) { + if (a.size(1) != b.size(0)) + throw std::logic_error("Inner dimensions must match"); + if (a.ndim() == 2 && b.ndim() == 1) { + c.resize(a.size(0)); + call_gemv(a.size(0), a.size(1), 1, 0, a.data(), b.data(), + c.data()); + } else if (a.ndim() <= 2 && b.ndim() <= 2) { + c.resize(a.size(0), b.size(1)); + call_gemm(a.size(0), a.size(1), b.size(1), 1, 0, a.data(), + b.data(), c.data()); } else { - throw std::logic_error( "Not finished yet" ); + throw std::logic_error("Not finished yet"); } } - /******************************************************** * Check if two arrays are equal * ********************************************************/ -template +template inline typename std::enable_if::value, bool>::type -FunctionTableCompare( const Array &a, const Array &b, TYPE ) -{ +FunctionTableCompare(const Array &a, const Array &b, + TYPE) { bool pass = true; - if ( a.size() != b.size() ) - throw std::logic_error( "Sizes of x and y do not match" ); - for ( size_t i = 0; i < a.length(); i++ ) - pass = pass && a( i ) == b( i ); + if (a.size() != b.size()) + throw std::logic_error("Sizes of x and y do not match"); + for (size_t i = 0; i < a.length(); i++) + pass = pass && a(i) == b(i); return pass; } -template +template inline typename std::enable_if::value, bool>::type -FunctionTableCompare( const Array &a, const Array &b, TYPE tol ) -{ +FunctionTableCompare(const Array &a, const Array &b, + TYPE tol) { bool pass = true; - if ( a.size() != b.size() ) - throw std::logic_error( "Sizes of x and y do not match" ); - for ( size_t i = 0; i < a.length(); i++ ) - pass = pass && ( std::abs( a( i ) - b( i ) ) < tol ); + if (a.size() != b.size()) + throw std::logic_error("Sizes of x and y do not match"); + for (size_t i = 0; i < a.length(); i++) + pass = pass && (std::abs(a(i) - b(i)) < tol); return pass; } -template -bool FunctionTable::equals( const Array &a, const Array &b, TYPE tol ) -{ - return FunctionTableCompare( a, b, tol ); +template +bool FunctionTable::equals(const Array &a, const Array &b, + TYPE tol) { + return FunctionTableCompare(a, b, tol); } - /******************************************************** * Specialized Functions * ********************************************************/ -template -void FunctionTable::transformReLU( const Array &A, Array &B ) -{ - const auto &fun = []( const TYPE &a ) { return std::max( a, static_cast( 0 ) ); }; - transform( fun, A, B ); -} - -template -void FunctionTable::transformAbs( const Array &A, Array &B ) -{ - B.resize( A.size() ); - const auto &fun = []( const TYPE &a ) { return std::abs( a ); }; - transform( fun, A, B ); -} -template -void FunctionTable::transformTanh( const Array &A, Array &B ) -{ - B.resize( A.size() ); - const auto &fun = []( const TYPE &a ) { return tanh( a ); }; - transform( fun, A, B ); -} - -template -void FunctionTable::transformHardTanh( const Array &A, - Array &B ) -{ - B.resize( A.size() ); - const auto &fun = []( const TYPE &a ) { - return std::max( -static_cast( 1.0 ), std::min( static_cast( 1.0 ), a ) ); +template +void FunctionTable::transformReLU(const Array &A, + Array &B) { + const auto &fun = [](const TYPE &a) { + return std::max(a, static_cast(0)); }; - transform( fun, A, B ); + transform(fun, A, B); } -template -void FunctionTable::transformSigmoid( const Array &A, Array &B ) -{ - B.resize( A.size() ); - const auto &fun = []( const TYPE &a ) { return 1.0 / ( 1.0 + exp( -a ) ); }; - transform( fun, A, B ); +template +void FunctionTable::transformAbs(const Array &A, + Array &B) { + B.resize(A.size()); + const auto &fun = [](const TYPE &a) { return std::abs(a); }; + transform(fun, A, B); +} +template +void FunctionTable::transformTanh(const Array &A, + Array &B) { + B.resize(A.size()); + const auto &fun = [](const TYPE &a) { return tanh(a); }; + transform(fun, A, B); } -template -void FunctionTable::transformSoftPlus( const Array &A, - Array &B ) -{ - B.resize( A.size() ); - const auto &fun = []( const TYPE &a ) { return log1p( exp( a ) ); }; - transform( fun, A, B ); +template +void FunctionTable::transformHardTanh(const Array &A, + Array &B) { + B.resize(A.size()); + const auto &fun = [](const TYPE &a) { + return std::max(-static_cast(1.0), + std::min(static_cast(1.0), a)); + }; + transform(fun, A, B); } -template -TYPE FunctionTable::sum( const Array &A ) -{ - const auto &fun = []( const TYPE &a, const TYPE &b ) { return a + b; }; - return reduce( fun, A, (TYPE) 0 ); +template +void FunctionTable::transformSigmoid(const Array &A, + Array &B) { + B.resize(A.size()); + const auto &fun = [](const TYPE &a) { return 1.0 / (1.0 + exp(-a)); }; + transform(fun, A, B); } -template -inline void FunctionTable::gemmWrapper( char TRANSA, - char TRANSB, - int M, - int N, - int K, - TYPE alpha, - const TYPE *A, - int LDA, - const TYPE *B, - int LDB, - TYPE beta, - TYPE *C, - int LDC ) -{ +template +void FunctionTable::transformSoftPlus(const Array &A, + Array &B) { + B.resize(A.size()); + const auto &fun = [](const TYPE &a) { return log1p(exp(a)); }; + transform(fun, A, B); +} + +template +TYPE FunctionTable::sum(const Array &A) { + const auto &fun = [](const TYPE &a, const TYPE &b) { return a + b; }; + return reduce(fun, A, (TYPE)0); +} + +template +inline void FunctionTable::gemmWrapper(char TRANSA, char TRANSB, int M, int N, + int K, TYPE alpha, const TYPE *A, + int LDA, const TYPE *B, int LDB, + TYPE beta, TYPE *C, int LDC) { ERROR("Not finished"); } - #endif diff --git a/common/MPI.cpp b/common/MPI.cpp index 2b3dc543..e75e242e 100644 --- a/common/MPI.cpp +++ b/common/MPI.cpp @@ -21,22 +21,22 @@ #include #include - // Include OS specific headers #undef USE_WINDOWS #undef USE_LINUX #undef USE_MAC -#if defined( WIN32 ) || defined( _WIN32 ) || defined( WIN64 ) || defined( _WIN64 ) +#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) // We are using windows #define USE_WINDOWS #include #include -#define sched_yield() Sleep( 0 ) -#elif defined( __APPLE__ ) +#define sched_yield() Sleep(0) +#elif defined(__APPLE__) // Using MAC #define USE_MAC #include -#elif defined( __linux ) || defined( __linux__ ) || defined( __unix ) || defined( __posix ) +#elif defined(__linux) || defined(__linux__) || defined(__unix) || \ + defined(__posix) // We are using linux #define USE_LINUX #include @@ -45,7 +45,6 @@ #error Unknown OS #endif - // Convience defines #define MPI_ERROR ERROR #define MPI_ASSERT ASSERT @@ -55,22 +54,18 @@ #define MPI_CLASS_COMM_SELF MPI_COMM_SELF #define MPI_CLASS_COMM_WORLD MPI_COMM_WORLD - // Global variable to track create new unique comms (dup and split) #ifndef USE_MPI MPI_Comm uniqueGlobalComm = 11; #endif - -#if defined( USE_SAMRAI ) && defined( USE_PETSC ) && !defined( USE_MPI ) -int MPI_REQUEST_NULL = 3; +#if defined(USE_SAMRAI) && defined(USE_PETSC) && !defined(USE_MPI) +int MPI_REQUEST_NULL = 3; int MPI_ERR_IN_STATUS = 4; #endif - namespace Utilities { - // Some special structs to work with MPI #ifdef USE_MPI struct IntIntStruct { @@ -91,40 +86,36 @@ struct DoubleIntStruct { }; #endif - // Initialized the static member variables -volatile unsigned int MPI_CLASS::N_MPI_Comm_created = 0; +volatile unsigned int MPI_CLASS::N_MPI_Comm_created = 0; volatile unsigned int MPI_CLASS::N_MPI_Comm_destroyed = 0; -short MPI_CLASS::profile_level = 127; - +short MPI_CLASS::profile_level = 127; // Define a type for use with size_t #ifdef USE_MPI static MPI_Datatype MPI_SIZE_T = 0x0; -static MPI_Datatype getSizeTDataType() -{ +static MPI_Datatype getSizeTDataType() { int size_int, size_long, size_longlong, size_longlong2; - MPI_Type_size( MPI_UNSIGNED, &size_int ); - MPI_Type_size( MPI_UNSIGNED_LONG, &size_long ); - MPI_Type_size( MPI_UNSIGNED_LONG_LONG, &size_longlong ); - MPI_Type_size( MPI_LONG_LONG_INT, &size_longlong2 ); - if ( sizeof( size_t ) == size_int ) { + MPI_Type_size(MPI_UNSIGNED, &size_int); + MPI_Type_size(MPI_UNSIGNED_LONG, &size_long); + MPI_Type_size(MPI_UNSIGNED_LONG_LONG, &size_longlong); + MPI_Type_size(MPI_LONG_LONG_INT, &size_longlong2); + if (sizeof(size_t) == size_int) { return MPI_UNSIGNED; - } else if ( sizeof( size_t ) == size_long ) { + } else if (sizeof(size_t) == size_long) { return MPI_UNSIGNED_LONG; - } else if ( sizeof( size_t ) == size_longlong ) { + } else if (sizeof(size_t) == size_longlong) { return MPI_UNSIGNED_LONG_LONG; - } else if ( sizeof( size_t ) == size_longlong2 ) { - MPI_WARNING( "Using signed long long datatype for size_t in MPI" ); + } else if (sizeof(size_t) == size_longlong2) { + MPI_WARNING("Using signed long long datatype for size_t in MPI"); return MPI_LONG_LONG_INT; // Note: this is not unsigned } else { - MPI_ERROR( "No suitable datatype found" ); + MPI_ERROR("No suitable datatype found"); } return 0; } #endif - // Static data for asyncronous communication without MPI // Note: these routines may not be thread-safe yet #ifndef USE_MPI @@ -134,153 +125,140 @@ struct Isendrecv_struct { int status; // Status: 1-sending, 2-recieving }; std::map global_isendrecv_list; -static MPI_Request getRequest( MPI_Comm comm, int tag ) -{ - MPI_ASSERT( tag >= 0 && tag <= mpi_max_tag ); +static MPI_Request getRequest(MPI_Comm comm, int tag) { + MPI_ASSERT(tag >= 0 && tag <= mpi_max_tag); // Use hashing function: 2^64*0.5*(sqrt(5)-1) - uint64_t a = static_cast( comm ) * 0x9E3779B97F4A7C15; - uint64_t b = static_cast( tag ) * 0x9E3779B97F4A7C15; + uint64_t a = static_cast(comm) * 0x9E3779B97F4A7C15; + uint64_t b = static_cast(tag) * 0x9E3779B97F4A7C15; uint64_t hash = a ^ b; MPI_Request request; - memcpy( &request, &hash, sizeof( MPI_Request ) ); + memcpy(&request, &hash, sizeof(MPI_Request)); return request; } #endif - // Check the mpi error code #ifdef USE_MPI -inline void check_MPI( int error ) -{ - if ( error != MPI_SUCCESS ) - MPI_ERROR( "Error calling MPI routine" ); +inline void check_MPI(int error) { + if (error != MPI_SUCCESS) + MPI_ERROR("Error calling MPI routine"); } #endif - /****************************************************************** * Some helper functions to convert between signed/unsigned types * ******************************************************************/ DISABLE_WARNINGS -static inline constexpr unsigned int offset_int() -{ - return ~static_cast( std::numeric_limits::min() ) + 1; +static inline constexpr unsigned int offset_int() { + return ~static_cast(std::numeric_limits::min()) + 1; } -static inline constexpr unsigned long int offset_long() -{ - return ~static_cast( std::numeric_limits::min() ) + 1; +static inline constexpr unsigned long int offset_long() { + return ~static_cast(std::numeric_limits::min()) + 1; } -static inline constexpr unsigned long long int offset_long_long() -{ - return ~static_cast( std::numeric_limits::min() ) + 1; +static inline constexpr unsigned long long int offset_long_long() { + return ~static_cast( + std::numeric_limits::min()) + + 1; } ENABLE_WARNINGS -static inline unsigned int signed_to_unsigned( int x ) -{ +static inline unsigned int signed_to_unsigned(int x) { const auto offset = offset_int(); - return ( x >= 0 ) ? static_cast( x ) + offset : - offset - static_cast( -x ); + return (x >= 0) ? static_cast(x) + offset + : offset - static_cast(-x); } -static inline unsigned long int signed_to_unsigned( long int x ) -{ +static inline unsigned long int signed_to_unsigned(long int x) { const auto offset = offset_long(); - return ( x >= 0 ) ? static_cast( x ) + offset : - offset - static_cast( -x ); + return (x >= 0) ? static_cast(x) + offset + : offset - static_cast(-x); } -static inline unsigned long long int signed_to_unsigned( long long int x ) -{ +static inline unsigned long long int signed_to_unsigned(long long int x) { const auto offset = offset_long_long(); - return ( x >= 0 ) ? static_cast( x ) + offset : - offset - static_cast( -x ); + return (x >= 0) ? static_cast(x) + offset + : offset - static_cast(-x); } -static inline int unsigned_to_signed( unsigned int x ) -{ +static inline int unsigned_to_signed(unsigned int x) { const auto offset = offset_int(); - return ( x >= offset ) ? static_cast( x - offset ) : -static_cast( offset - x ); + return (x >= offset) ? static_cast(x - offset) + : -static_cast(offset - x); } -static inline long int unsigned_to_signed( unsigned long int x ) -{ +static inline long int unsigned_to_signed(unsigned long int x) { const auto offset = offset_long(); - return ( x >= offset ) ? static_cast( x - offset ) : - -static_cast( offset - x ); + return (x >= offset) ? static_cast(x - offset) + : -static_cast(offset - x); } -static inline long long int unsigned_to_signed( unsigned long long int x ) -{ +static inline long long int unsigned_to_signed(unsigned long long int x) { const auto offset = offset_long_long(); - return ( x >= offset ) ? static_cast( x - offset ) : - -static_cast( offset - x ); + return (x >= offset) ? static_cast(x - offset) + : -static_cast(offset - x); } - /************************************************************************ * Get the MPI version * ************************************************************************/ -std::array MPI_CLASS::version() -{ +std::array MPI_CLASS::version() { #ifdef USE_MPI int MPI_version; int MPI_subversion; - MPI_Get_version( &MPI_version, &MPI_subversion ); - return { MPI_version, MPI_subversion }; + MPI_Get_version(&MPI_version, &MPI_subversion); + return {MPI_version, MPI_subversion}; #else - return { 0, 0 }; + return {0, 0}; #endif } -std::string MPI_CLASS::info() -{ +std::string MPI_CLASS::info() { #ifdef USE_MPI #if MPI_VERSION >= 3 int MPI_version_length = 0; char MPI_version_string[MPI_MAX_LIBRARY_VERSION_STRING]; - MPI_Get_library_version( MPI_version_string, &MPI_version_length ); - if ( MPI_version_length > 0 ) { - std::string MPI_info( MPI_version_string, MPI_version_length ); - size_t pos = MPI_info.find( '\n' ); - while ( pos != std::string::npos ) { - MPI_info.insert( pos + 1, " " ); - pos = MPI_info.find( '\n', pos + 1 ); + MPI_Get_library_version(MPI_version_string, &MPI_version_length); + if (MPI_version_length > 0) { + std::string MPI_info(MPI_version_string, MPI_version_length); + size_t pos = MPI_info.find('\n'); + while (pos != std::string::npos) { + MPI_info.insert(pos + 1, " "); + pos = MPI_info.find('\n', pos + 1); } return MPI_info; } #endif auto tmp = version(); - return std::to_string( tmp[0] ) + "." + std::to_string( tmp[0] ); + return std::to_string(tmp[0]) + "." + std::to_string(tmp[0]); #else return std::string(); #endif } - /************************************************************************ * Functions to get/set the process affinities * ************************************************************************/ -int MPI_CLASS::getNumberOfProcessors() { return std::thread::hardware_concurrency(); } -std::vector MPI_CLASS::getProcessAffinity() -{ +int MPI_CLASS::getNumberOfProcessors() { + return std::thread::hardware_concurrency(); +} +std::vector MPI_CLASS::getProcessAffinity() { std::vector procs; #ifdef USE_LINUX cpu_set_t mask; - int error = sched_getaffinity( getpid(), sizeof( cpu_set_t ), &mask ); - if ( error != 0 ) - MPI_ERROR( "Error getting process affinity" ); - for ( int i = 0; i < (int) sizeof( cpu_set_t ) * CHAR_BIT; i++ ) { - if ( CPU_ISSET( i, &mask ) ) - procs.push_back( i ); + int error = sched_getaffinity(getpid(), sizeof(cpu_set_t), &mask); + if (error != 0) + MPI_ERROR("Error getting process affinity"); + for (int i = 0; i < (int)sizeof(cpu_set_t) * CHAR_BIT; i++) { + if (CPU_ISSET(i, &mask)) + procs.push_back(i); } -#elif defined( USE_MAC ) +#elif defined(USE_MAC) // MAC does not support getting or setting the affinity - printf( "Warning: MAC does not support getting the process affinity\n" ); + printf("Warning: MAC does not support getting the process affinity\n"); procs.clear(); -#elif defined( USE_WINDOWS ) +#elif defined(USE_WINDOWS) HANDLE hProc = GetCurrentProcess(); size_t procMask; size_t sysMask; - PDWORD_PTR procMaskPtr = reinterpret_cast( &procMask ); - PDWORD_PTR sysMaskPtr = reinterpret_cast( &sysMask ); - GetProcessAffinityMask( hProc, procMaskPtr, sysMaskPtr ); - for ( int i = 0; i < (int) sizeof( size_t ) * CHAR_BIT; i++ ) { - if ( ( procMask & 0x1 ) != 0 ) - procs.push_back( i ); + PDWORD_PTR procMaskPtr = reinterpret_cast(&procMask); + PDWORD_PTR sysMaskPtr = reinterpret_cast(&sysMask); + GetProcessAffinityMask(hProc, procMaskPtr, sysMaskPtr); + for (int i = 0; i < (int)sizeof(size_t) * CHAR_BIT; i++) { + if ((procMask & 0x1) != 0) + procs.push_back(i); procMask >>= 1; } #else @@ -288,57 +266,53 @@ std::vector MPI_CLASS::getProcessAffinity() #endif return procs; } -void MPI_CLASS::setProcessAffinity( const std::vector &procs ) -{ +void MPI_CLASS::setProcessAffinity(const std::vector &procs) { #ifdef USE_LINUX cpu_set_t mask; - CPU_ZERO( &mask ); - for ( auto cpu : procs ) - CPU_SET( cpu, &mask ); - int error = sched_setaffinity( getpid(), sizeof( cpu_set_t ), &mask ); - if ( error != 0 ) - MPI_ERROR( "Error setting process affinity" ); -#elif defined( USE_MAC ) + CPU_ZERO(&mask); + for (auto cpu : procs) + CPU_SET(cpu, &mask); + int error = sched_setaffinity(getpid(), sizeof(cpu_set_t), &mask); + if (error != 0) + MPI_ERROR("Error setting process affinity"); +#elif defined(USE_MAC) // MAC does not support getting or setting the affinity - NULL_USE( procs ); -#elif defined( USE_WINDOWS ) + NULL_USE(procs); +#elif defined(USE_WINDOWS) DWORD mask = 0; - for ( size_t i = 0; i < procs.size(); i++ ) - mask |= ( (DWORD) 1 ) << procs[i]; + for (size_t i = 0; i < procs.size(); i++) + mask |= ((DWORD)1) << procs[i]; HANDLE hProc = GetCurrentProcess(); - SetProcessAffinityMask( hProc, mask ); + SetProcessAffinityMask(hProc, mask); #else #error Unknown OS #endif } - /************************************************************************ * Function to check if MPI is active * ************************************************************************/ -bool MPI_CLASS::MPI_active() -{ +bool MPI_CLASS::MPI_active() { #ifdef USE_MPI int initialized = 0, finalized = 0; - MPI_Initialized( &initialized ); - MPI_Finalized( &finalized ); + MPI_Initialized(&initialized); + MPI_Finalized(&finalized); return initialized != 0 && finalized == 0; #else return true; #endif } -MPI_CLASS::ThreadSupport MPI_CLASS::queryThreadSupport() -{ +MPI_CLASS::ThreadSupport MPI_CLASS::queryThreadSupport() { #ifdef USE_MPI int provided = 0; - MPI_Query_thread( &provided ); - if ( provided == MPI_THREAD_SINGLE ) + MPI_Query_thread(&provided); + if (provided == MPI_THREAD_SINGLE) return ThreadSupport::SINGLE; - if ( provided == MPI_THREAD_FUNNELED ) + if (provided == MPI_THREAD_FUNNELED) return ThreadSupport::FUNNELED; - if ( provided == MPI_THREAD_SERIALIZED ) + if (provided == MPI_THREAD_SERIALIZED) return ThreadSupport::SERIALIZED; - if ( provided == MPI_THREAD_MULTIPLE ) + if (provided == MPI_THREAD_MULTIPLE) return ThreadSupport::MULTIPLE; return ThreadSupport::SINGLE; #else @@ -346,747 +320,709 @@ MPI_CLASS::ThreadSupport MPI_CLASS::queryThreadSupport() #endif } - /************************************************************************ * Function to perform a load balance of the given processes * ************************************************************************/ -void MPI_CLASS::balanceProcesses( const MPI_CLASS &globalComm, const int method, - const std::vector &procs, const int N_min_in, const int N_max_in ) -{ +void MPI_CLASS::balanceProcesses(const MPI_CLASS &globalComm, const int method, + const std::vector &procs, + const int N_min_in, const int N_max_in) { // Build the list of processors to use std::vector cpus = procs; - if ( cpus.empty() ) { - for ( int i = 0; i < getNumberOfProcessors(); i++ ) - cpus.push_back( i ); + if (cpus.empty()) { + for (int i = 0; i < getNumberOfProcessors(); i++) + cpus.push_back(i); } // Handle the "easy cases" - if ( method == 1 ) { + if (method == 1) { // Trivial case where we do not need any communication - setProcessAffinity( cpus ); + setProcessAffinity(cpus); return; } // Get the sub-communicator for the current node MPI_CLASS nodeComm = globalComm.splitByNode(); - int N_min = std::min( std::max( N_min_in, 1 ), cpus.size() ); - int N_max = N_max_in; - if ( N_max == -1 ) + int N_min = std::min(std::max(N_min_in, 1), cpus.size()); + int N_max = N_max_in; + if (N_max == -1) N_max = cpus.size(); - N_max = std::min( N_max, cpus.size() ); - MPI_ASSERT( N_max >= N_min ); + N_max = std::min(N_max, cpus.size()); + MPI_ASSERT(N_max >= N_min); // Perform the load balance within the node - if ( method == 2 ) { + if (method == 2) { int N_proc = cpus.size() / nodeComm.getSize(); - N_proc = std::max( N_proc, N_min ); - N_proc = std::min( N_proc, N_max ); - std::vector cpus2( N_proc, -1 ); - for ( int i = 0; i < N_proc; i++ ) - cpus2[i] = cpus[( nodeComm.getRank() * N_proc + i ) % cpus.size()]; - setProcessAffinity( cpus2 ); + N_proc = std::max(N_proc, N_min); + N_proc = std::min(N_proc, N_max); + std::vector cpus2(N_proc, -1); + for (int i = 0; i < N_proc; i++) + cpus2[i] = cpus[(nodeComm.getRank() * N_proc + i) % cpus.size()]; + setProcessAffinity(cpus2); } else { - MPI_ERROR( "Unknown method for load balance" ); + MPI_ERROR("Unknown method for load balance"); } } - /************************************************************************ * Empty constructor * ************************************************************************/ -MPI_CLASS::MPI_CLASS() -{ +MPI_CLASS::MPI_CLASS() { // Initialize the data members to a defaul communicator of self #ifdef USE_MPI communicator = MPI_COMM_NULL; - d_maxTag = 0x7FFFFFFF; + d_maxTag = 0x7FFFFFFF; #else communicator = MPI_CLASS_COMM_NULL; - d_maxTag = mpi_max_tag; + d_maxTag = mpi_max_tag; #endif - d_count = nullptr; - d_manage = false; - comm_rank = 0; - comm_size = 1; - d_isNull = true; - d_currentTag = nullptr; - d_call_abort = true; + d_count = nullptr; + d_manage = false; + comm_rank = 0; + comm_size = 1; + d_isNull = true; + d_currentTag = nullptr; + d_call_abort = true; tmp_alignment = -1; } - /************************************************************************ * Empty deconstructor * ************************************************************************/ MPI_CLASS::~MPI_CLASS() { reset(); } -void MPI_CLASS::reset() -{ +void MPI_CLASS::reset() { // Decrement the count if used int count = -1; - if ( d_count != nullptr ) - count = --( *d_count ); - if ( count == 0 ) { + if (d_count != nullptr) + count = --(*d_count); + if (count == 0) { // We are holding that last reference to the MPI_Comm object, we need to free it - if ( d_manage ) { + if (d_manage) { #ifdef USE_MPI - MPI_Comm_set_errhandler( communicator, MPI_ERRORS_ARE_FATAL ); - int err = MPI_Comm_free( &communicator ); - if ( err != MPI_SUCCESS ) - MPI_ERROR( "Problem free'ing MPI_Comm object" ); + MPI_Comm_set_errhandler(communicator, MPI_ERRORS_ARE_FATAL); + int err = MPI_Comm_free(&communicator); + if (err != MPI_SUCCESS) + MPI_ERROR("Problem free'ing MPI_Comm object"); communicator = MPI_CLASS_COMM_NULL; ++N_MPI_Comm_destroyed; #endif } delete d_count; } - if ( d_currentTag == nullptr ) { + if (d_currentTag == nullptr) { // No tag index - } else if ( d_currentTag[1] > 1 ) { - --( d_currentTag[1] ); + } else if (d_currentTag[1] > 1) { + --(d_currentTag[1]); } else { delete[] d_currentTag; } - d_manage = false; - d_count = nullptr; - comm_rank = 0; - comm_size = 1; - d_maxTag = 0; - d_isNull = true; + d_manage = false; + d_count = nullptr; + comm_rank = 0; + comm_size = 1; + d_maxTag = 0; + d_isNull = true; d_currentTag = nullptr; d_call_abort = true; } - /************************************************************************ * Copy constructors * ************************************************************************/ -MPI_CLASS::MPI_CLASS( const MPI_CLASS &comm ) - : communicator( comm.communicator ), - d_isNull( comm.d_isNull ), - d_manage( comm.d_manage ), - comm_rank( comm.comm_rank ), - comm_size( comm.comm_size ), - d_maxTag( comm.d_maxTag ), - d_currentTag( comm.d_currentTag ) -{ +MPI_CLASS::MPI_CLASS(const MPI_CLASS &comm) + : communicator(comm.communicator), d_isNull(comm.d_isNull), + d_manage(comm.d_manage), comm_rank(comm.comm_rank), + comm_size(comm.comm_size), d_maxTag(comm.d_maxTag), + d_currentTag(comm.d_currentTag) { // Initialize the data members to the existing comm object - if ( d_currentTag != nullptr ) + if (d_currentTag != nullptr) ++d_currentTag[1]; d_call_abort = comm.d_call_abort; // Set and increment the count d_count = comm.d_count; - if ( d_count != nullptr ) - ++( *d_count ); + if (d_count != nullptr) + ++(*d_count); tmp_alignment = -1; } -MPI_CLASS::MPI_CLASS( MPI_CLASS &&rhs ) : MPI_CLASS() -{ - std::swap( communicator, rhs.communicator ); - std::swap( d_isNull, rhs.d_isNull ); - std::swap( d_manage, rhs.d_manage ); - std::swap( d_call_abort, rhs.d_call_abort ); - std::swap( profile_level, rhs.profile_level ); - std::swap( comm_rank, rhs.comm_rank ); - std::swap( comm_size, rhs.comm_size ); - std::swap( d_maxTag, rhs.d_maxTag ); - std::swap( d_currentTag, rhs.d_currentTag ); - std::swap( d_count, rhs.d_count ); - std::swap( tmp_alignment, rhs.tmp_alignment ); +MPI_CLASS::MPI_CLASS(MPI_CLASS &&rhs) : MPI_CLASS() { + std::swap(communicator, rhs.communicator); + std::swap(d_isNull, rhs.d_isNull); + std::swap(d_manage, rhs.d_manage); + std::swap(d_call_abort, rhs.d_call_abort); + std::swap(profile_level, rhs.profile_level); + std::swap(comm_rank, rhs.comm_rank); + std::swap(comm_size, rhs.comm_size); + std::swap(d_maxTag, rhs.d_maxTag); + std::swap(d_currentTag, rhs.d_currentTag); + std::swap(d_count, rhs.d_count); + std::swap(tmp_alignment, rhs.tmp_alignment); } - /************************************************************************ * Assignment operators * ************************************************************************/ -MPI_CLASS &MPI_CLASS::operator=( const MPI_CLASS &comm ) -{ - if ( this == &comm ) // protect against invalid self-assignment +MPI_CLASS &MPI_CLASS::operator=(const MPI_CLASS &comm) { + if (this == &comm) // protect against invalid self-assignment return *this; // Destroy the previous object this->reset(); // Initialize the data members to the existing object this->communicator = comm.communicator; - this->comm_rank = comm.comm_rank; - this->comm_size = comm.comm_size; - this->d_isNull = comm.d_isNull; - this->d_manage = comm.d_manage; - this->d_maxTag = comm.d_maxTag; + this->comm_rank = comm.comm_rank; + this->comm_size = comm.comm_size; + this->d_isNull = comm.d_isNull; + this->d_manage = comm.d_manage; + this->d_maxTag = comm.d_maxTag; this->d_call_abort = comm.d_call_abort; this->d_currentTag = comm.d_currentTag; - if ( this->d_currentTag != nullptr ) - ++( this->d_currentTag[1] ); + if (this->d_currentTag != nullptr) + ++(this->d_currentTag[1]); // Set and increment the count this->d_count = comm.d_count; - if ( this->d_count != nullptr ) - ++( *d_count ); + if (this->d_count != nullptr) + ++(*d_count); this->tmp_alignment = -1; return *this; } -MPI_CLASS &MPI_CLASS::operator=( MPI_CLASS &&rhs ) -{ - if ( this == &rhs ) // protect against invalid self-assignment +MPI_CLASS &MPI_CLASS::operator=(MPI_CLASS &&rhs) { + if (this == &rhs) // protect against invalid self-assignment return *this; - std::swap( communicator, rhs.communicator ); - std::swap( d_isNull, rhs.d_isNull ); - std::swap( d_manage, rhs.d_manage ); - std::swap( d_call_abort, rhs.d_call_abort ); - std::swap( profile_level, rhs.profile_level ); - std::swap( comm_rank, rhs.comm_rank ); - std::swap( comm_size, rhs.comm_size ); - std::swap( d_maxTag, rhs.d_maxTag ); - std::swap( d_currentTag, rhs.d_currentTag ); - std::swap( d_count, rhs.d_count ); - std::swap( tmp_alignment, rhs.tmp_alignment ); + std::swap(communicator, rhs.communicator); + std::swap(d_isNull, rhs.d_isNull); + std::swap(d_manage, rhs.d_manage); + std::swap(d_call_abort, rhs.d_call_abort); + std::swap(profile_level, rhs.profile_level); + std::swap(comm_rank, rhs.comm_rank); + std::swap(comm_size, rhs.comm_size); + std::swap(d_maxTag, rhs.d_maxTag); + std::swap(d_currentTag, rhs.d_currentTag); + std::swap(d_count, rhs.d_count); + std::swap(tmp_alignment, rhs.tmp_alignment); return *this; } - /************************************************************************ * Constructor from existing MPI communicator * ************************************************************************/ -int d_global_currentTag_world1[2] = { 1, 1 }; -int d_global_currentTag_world2[2] = { 1, 1 }; -int d_global_currentTag_self[2] = { 1, 1 }; +int d_global_currentTag_world1[2] = {1, 1}; +int d_global_currentTag_world2[2] = {1, 1}; +int d_global_currentTag_self[2] = {1, 1}; #ifdef USE_MPI -std::atomic_int d_global_count_world1 = { 1 }; -std::atomic_int d_global_count_world2 = { 1 }; -std::atomic_int d_global_count_self = { 1 }; +std::atomic_int d_global_count_world1 = {1}; +std::atomic_int d_global_count_world2 = {1}; +std::atomic_int d_global_count_self = {1}; #endif -MPI_CLASS::MPI_CLASS( MPI_Comm comm, bool manage ) -{ - d_count = nullptr; - d_manage = false; +MPI_CLASS::MPI_CLASS(MPI_Comm comm, bool manage) { + d_count = nullptr; + d_manage = false; tmp_alignment = -1; // Check if we are using our version of comm_world - if ( comm == MPI_CLASS_COMM_WORLD ) { + if (comm == MPI_CLASS_COMM_WORLD) { communicator = MPI_COMM_WORLD; - } else if ( comm == MPI_CLASS_COMM_SELF ) { + } else if (comm == MPI_CLASS_COMM_SELF) { communicator = MPI_COMM_SELF; - } else if ( comm == MPI_CLASS_COMM_NULL ) { + } else if (comm == MPI_CLASS_COMM_NULL) { communicator = MPI_COMM_NULL; } else { communicator = comm; } #ifdef USE_MPI // We are using MPI, use the MPI communicator to initialize the data - if ( communicator != MPI_COMM_NULL ) { + if (communicator != MPI_COMM_NULL) { // Set the MPI_SIZE_T datatype if it has not been set - if ( MPI_SIZE_T == 0x0 ) + if (MPI_SIZE_T == 0x0) MPI_SIZE_T = getSizeTDataType(); // Attach the error handler - StackTrace::setMPIErrorHandler( communicator ); + StackTrace::setMPIErrorHandler(communicator); // Get the communicator properties - MPI_Comm_rank( communicator, &comm_rank ); - MPI_Comm_size( communicator, &comm_size ); + MPI_Comm_rank(communicator, &comm_rank); + MPI_Comm_size(communicator, &comm_size); int flag, *val; - int ierr = MPI_Comm_get_attr( communicator, MPI_TAG_UB, &val, &flag ); - MPI_ASSERT( ierr == MPI_SUCCESS ); - if ( flag == 0 ) { - d_maxTag = 0x7FFFFFFF; // The tag is not a valid attribute (set to 2^31-1) + int ierr = MPI_Comm_get_attr(communicator, MPI_TAG_UB, &val, &flag); + MPI_ASSERT(ierr == MPI_SUCCESS); + if (flag == 0) { + d_maxTag = + 0x7FFFFFFF; // The tag is not a valid attribute (set to 2^31-1) } else { d_maxTag = *val; - if ( d_maxTag < 0 ) { + if (d_maxTag < 0) { d_maxTag = 0x7FFFFFFF; } // The maximum tag is > a signed int (set to 2^31-1) - MPI_INSIST( d_maxTag >= 0x7FFF, "maximum tag size is < MPI standard" ); + MPI_INSIST(d_maxTag >= 0x7FFF, + "maximum tag size is < MPI standard"); } } else { comm_rank = 1; comm_size = 0; - d_maxTag = 0x7FFFFFFF; + d_maxTag = 0x7FFFFFFF; } d_isNull = communicator == MPI_COMM_NULL; - if ( manage && communicator != MPI_COMM_NULL && communicator != MPI_COMM_SELF && - communicator != MPI_COMM_WORLD ) + if (manage && communicator != MPI_COMM_NULL && + communicator != MPI_COMM_SELF && communicator != MPI_COMM_WORLD) d_manage = true; // Create the count (Note: we do not need to worry about thread safety) - if ( communicator == MPI_CLASS_COMM_WORLD ) { + if (communicator == MPI_CLASS_COMM_WORLD) { d_count = &d_global_count_world1; - ++( *d_count ); - } else if ( communicator == MPI_COMM_WORLD ) { + ++(*d_count); + } else if (communicator == MPI_COMM_WORLD) { d_count = &d_global_count_world2; - ++( *d_count ); - } else if ( communicator == MPI_COMM_SELF ) { + ++(*d_count); + } else if (communicator == MPI_COMM_SELF) { d_count = &d_global_count_self; - ++( *d_count ); - } else if ( communicator == MPI_COMM_NULL ) { + ++(*d_count); + } else if (communicator == MPI_COMM_NULL) { d_count = nullptr; } else { - d_count = new std::atomic_int; + d_count = new std::atomic_int; *d_count = 1; } - if ( d_manage ) + if (d_manage) ++N_MPI_Comm_created; #else // We are not using MPI, intialize based on the communicator - NULL_USE( manage ); + NULL_USE(manage); comm_rank = 0; comm_size = 1; - d_maxTag = mpi_max_tag; - d_isNull = communicator == MPI_COMM_NULL; - if ( d_isNull ) + d_maxTag = mpi_max_tag; + d_isNull = communicator == MPI_COMM_NULL; + if (d_isNull) comm_size = 0; #endif - if ( communicator == MPI_CLASS_COMM_WORLD ) { + if (communicator == MPI_CLASS_COMM_WORLD) { d_currentTag = d_global_currentTag_world1; - ++( this->d_currentTag[1] ); - } else if ( communicator == MPI_COMM_WORLD ) { + ++(this->d_currentTag[1]); + } else if (communicator == MPI_COMM_WORLD) { d_currentTag = d_global_currentTag_world2; - ++( this->d_currentTag[1] ); - } else if ( communicator == MPI_COMM_SELF ) { + ++(this->d_currentTag[1]); + } else if (communicator == MPI_COMM_SELF) { d_currentTag = d_global_currentTag_self; - ++( this->d_currentTag[1] ); - } else if ( communicator == MPI_COMM_NULL ) { + ++(this->d_currentTag[1]); + } else if (communicator == MPI_COMM_NULL) { d_currentTag = nullptr; } else { - d_currentTag = new int[2]; - d_currentTag[0] = ( d_maxTag <= 0x10000 ) ? 1 : 0x1FFF; + d_currentTag = new int[2]; + d_currentTag[0] = (d_maxTag <= 0x10000) ? 1 : 0x1FFF; d_currentTag[1] = 1; } d_call_abort = true; } - /************************************************************************ * Return the ranks of the communicator in the global comm * ************************************************************************/ -std::vector MPI_CLASS::globalRanks() const -{ - if ( d_isNull ) +std::vector MPI_CLASS::globalRanks() const { + if (d_isNull) return std::vector(); #ifdef USE_MPI // Get my global rank and size if it has not been set static int globalRank = -1; static int globalSize = -1; - if ( globalRank == -1 && MPI_active() ) { - MPI_Comm_rank( MPI_CLASS_COMM_WORLD, &globalRank ); - MPI_Comm_size( MPI_CLASS_COMM_WORLD, &globalSize ); + if (globalRank == -1 && MPI_active()) { + MPI_Comm_rank(MPI_CLASS_COMM_WORLD, &globalRank); + MPI_Comm_size(MPI_CLASS_COMM_WORLD, &globalSize); } // Check if we are dealing with a serial or global communicator - if ( comm_size == 1 ) - return std::vector( 1, globalRank ); - if ( comm_size == globalSize ) { - std::vector ranks( globalSize ); - for ( int i = 0; i < globalSize; i++ ) + if (comm_size == 1) + return std::vector(1, globalRank); + if (comm_size == globalSize) { + std::vector ranks(globalSize); + for (int i = 0; i < globalSize; i++) ranks[i] = i; return ranks; } // Get the global rank from each rank in the communicator - auto ranks = allGather( globalRank ); - std::sort( ranks.begin(), ranks.end() ); + auto ranks = allGather(globalRank); + std::sort(ranks.begin(), ranks.end()); return ranks; #else - return std::vector( 1, 1 ); + return std::vector(1, 1); #endif } - /************************************************************************ * Generate a random number * ************************************************************************/ -size_t MPI_CLASS::rand() const -{ +size_t MPI_CLASS::rand() const { size_t val = 0; - if ( getRank() == 0 ) { + if (getRank() == 0) { static std::random_device rd; - static std::mt19937 gen( rd() ); + static std::mt19937 gen(rd()); static std::uniform_int_distribution dist; - val = dist( gen ); + val = dist(gen); } - val = bcast( val, 0 ); + val = bcast(val, 0); return val; } - /************************************************************************ * Intersect two communicators * ************************************************************************/ #ifdef USE_MPI -static inline void MPI_Group_free2( MPI_Group *group ) -{ - if ( *group != MPI_GROUP_EMPTY ) { +static inline void MPI_Group_free2(MPI_Group *group) { + if (*group != MPI_GROUP_EMPTY) { // MPICH is fine with free'ing an empty group, OpenMPI crashes - MPI_Group_free( group ); + MPI_Group_free(group); } } -MPI_CLASS MPI_CLASS::intersect( const MPI_CLASS &comm1, const MPI_CLASS &comm2 ) -{ +MPI_CLASS MPI_CLASS::intersect(const MPI_CLASS &comm1, const MPI_CLASS &comm2) { MPI_Group group1 = MPI_GROUP_EMPTY, group2 = MPI_GROUP_EMPTY; - if ( !comm1.isNull() ) { - MPI_Group_free2( &group1 ); - MPI_Comm_group( comm1.communicator, &group1 ); + if (!comm1.isNull()) { + MPI_Group_free2(&group1); + MPI_Comm_group(comm1.communicator, &group1); } - if ( !comm2.isNull() ) { - MPI_Group_free2( &group2 ); - MPI_Comm_group( comm2.communicator, &group2 ); + if (!comm2.isNull()) { + MPI_Group_free2(&group2); + MPI_Comm_group(comm2.communicator, &group2); } MPI_Group group12; - MPI_Group_intersection( group1, group2, &group12 ); + MPI_Group_intersection(group1, group2, &group12); int compare1, compare2; - MPI_Group_compare( group1, group12, &compare1 ); - MPI_Group_compare( group2, group12, &compare2 ); - MPI_CLASS new_comm( MPI_CLASS_COMM_NULL ); + MPI_Group_compare(group1, group12, &compare1); + MPI_Group_compare(group2, group12, &compare2); + MPI_CLASS new_comm(MPI_CLASS_COMM_NULL); int size; - MPI_Group_size( group12, &size ); - if ( compare1 != MPI_UNEQUAL && size != 0 ) { + MPI_Group_size(group12, &size); + if (compare1 != MPI_UNEQUAL && size != 0) { // The intersection matches comm1 new_comm = comm1; - } else if ( compare2 != MPI_UNEQUAL && size != 0 ) { + } else if (compare2 != MPI_UNEQUAL && size != 0) { // The intersection matches comm2 new_comm = comm2; - } else if ( comm1.isNull() ) { + } else if (comm1.isNull()) { // comm1 is null, we can return safely (comm1 is needed for communication) } else { // The intersection is smaller than comm1 or comm2 // Check if the new comm is nullptr for all processors int max_size = 0; - MPI_Allreduce( &size, &max_size, 1, MPI_INT, MPI_MAX, comm1.communicator ); - if ( max_size == 0 ) { + MPI_Allreduce(&size, &max_size, 1, MPI_INT, MPI_MAX, + comm1.communicator); + if (max_size == 0) { // We are dealing with completely disjoint sets - new_comm = MPI_CLASS( MPI_CLASS_COMM_NULL, false ); + new_comm = MPI_CLASS(MPI_CLASS_COMM_NULL, false); } else { // Create the new comm // Note: OpenMPI crashes if the intersection group is EMPTY for any processors // We will set it to SELF for the EMPTY processors, then create a nullptr comm later - if ( group12 == MPI_GROUP_EMPTY ) { - MPI_Group_free2( &group12 ); - MPI_Comm_group( MPI_COMM_SELF, &group12 ); + if (group12 == MPI_GROUP_EMPTY) { + MPI_Group_free2(&group12); + MPI_Comm_group(MPI_COMM_SELF, &group12); } MPI_Comm new_MPI_comm; - MPI_Comm_create( comm1.communicator, group12, &new_MPI_comm ); - if ( size > 0 ) { + MPI_Comm_create(comm1.communicator, group12, &new_MPI_comm); + if (size > 0) { // This is the valid case where we create a new intersection comm - new_comm = MPI_CLASS( new_MPI_comm, true ); + new_comm = MPI_CLASS(new_MPI_comm, true); } else { // We actually want a null comm for this communicator - new_comm = MPI_CLASS( MPI_CLASS_COMM_NULL, false ); - MPI_Comm_free( &new_MPI_comm ); + new_comm = MPI_CLASS(MPI_CLASS_COMM_NULL, false); + MPI_Comm_free(&new_MPI_comm); } } } - MPI_Group_free2( &group1 ); - MPI_Group_free2( &group2 ); - MPI_Group_free2( &group12 ); + MPI_Group_free2(&group1); + MPI_Group_free2(&group2); + MPI_Group_free2(&group12); return new_comm; } #else -MPI_CLASS MPI_CLASS::intersect( const MPI_CLASS &comm1, const MPI_CLASS &comm2 ) -{ - if ( comm1.isNull() || comm2.isNull() ) - return MPI_CLASS( MPI_CLASS_COMM_NULL, false ); - MPI_ASSERT( comm1.comm_size == 1 && comm2.comm_size == 1 ); +MPI_CLASS MPI_CLASS::intersect(const MPI_CLASS &comm1, const MPI_CLASS &comm2) { + if (comm1.isNull() || comm2.isNull()) + return MPI_CLASS(MPI_CLASS_COMM_NULL, false); + MPI_ASSERT(comm1.comm_size == 1 && comm2.comm_size == 1); return comm1; } #endif - /************************************************************************ * Split a comm * ************************************************************************/ -MPI_CLASS MPI_CLASS::split( int color, int key ) const -{ - if ( d_isNull ) { - return MPI_CLASS( MPI_CLASS_COMM_NULL ); - } else if ( comm_size == 1 ) { - if ( color == -1 ) - return MPI_CLASS( MPI_CLASS_COMM_NULL ); +MPI_CLASS MPI_CLASS::split(int color, int key) const { + if (d_isNull) { + return MPI_CLASS(MPI_CLASS_COMM_NULL); + } else if (comm_size == 1) { + if (color == -1) + return MPI_CLASS(MPI_CLASS_COMM_NULL); return dup(); } MPI_Comm new_MPI_comm = MPI_CLASS_COMM_NULL; #ifdef USE_MPI // USE MPI to split the communicator - if ( color == -1 ) { - check_MPI( MPI_Comm_split( communicator, MPI_UNDEFINED, key, &new_MPI_comm ) ); + if (color == -1) { + check_MPI( + MPI_Comm_split(communicator, MPI_UNDEFINED, key, &new_MPI_comm)); } else { - check_MPI( MPI_Comm_split( communicator, color, key, &new_MPI_comm ) ); + check_MPI(MPI_Comm_split(communicator, color, key, &new_MPI_comm)); } #endif // Create the new object - NULL_USE( key ); - MPI_CLASS new_comm( new_MPI_comm, true ); + NULL_USE(key); + MPI_CLASS new_comm(new_MPI_comm, true); new_comm.d_call_abort = d_call_abort; return new_comm; } -MPI_CLASS MPI_CLASS::splitByNode( int key ) const -{ +MPI_CLASS MPI_CLASS::splitByNode(int key) const { // Check if we are dealing with a single processor (trivial case) - if ( comm_size == 1 ) - return this->split( 0, 0 ); + if (comm_size == 1) + return this->split(0, 0); // Get the node name std::string name = MPI_CLASS::getNodeName(); // Gather the names from all ranks - std::vector list( comm_size ); - allGather( name, &list[0] ); + std::vector list(comm_size); + allGather(name, &list[0]); // Create the colors - std::vector color( comm_size, -1 ); + std::vector color(comm_size, -1); color[0] = 0; - for ( int i = 1; i < comm_size; i++ ) { + for (int i = 1; i < comm_size; i++) { const std::string tmp1 = list[i]; - for ( int j = 0; j < i; j++ ) { + for (int j = 0; j < i; j++) { const std::string tmp2 = list[j]; - if ( tmp1 == tmp2 ) { + if (tmp1 == tmp2) { color[i] = color[j]; break; } color[i] = color[i - 1] + 1; } } - MPI_CLASS new_comm = this->split( color[comm_rank], key ); + MPI_CLASS new_comm = this->split(color[comm_rank], key); return new_comm; } - /************************************************************************ * Duplicate an exisiting comm object * ************************************************************************/ -MPI_CLASS MPI_CLASS::dup() const -{ - if ( d_isNull ) - return MPI_CLASS( MPI_CLASS_COMM_NULL ); +MPI_CLASS MPI_CLASS::dup() const { + if (d_isNull) + return MPI_CLASS(MPI_CLASS_COMM_NULL); MPI_Comm new_MPI_comm = communicator; -#if defined( USE_MPI ) || defined( USE_PETSC ) +#if defined(USE_MPI) || defined(USE_PETSC) // USE MPI to duplicate the communicator - MPI_Comm_dup( communicator, &new_MPI_comm ); + MPI_Comm_dup(communicator, &new_MPI_comm); #else new_MPI_comm = uniqueGlobalComm; uniqueGlobalComm++; #endif // Create the new comm object - MPI_CLASS new_comm( new_MPI_comm, true ); - new_comm.d_isNull = d_isNull; + MPI_CLASS new_comm(new_MPI_comm, true); + new_comm.d_isNull = d_isNull; new_comm.d_call_abort = d_call_abort; return new_comm; } - /************************************************************************ * Get the node name * ************************************************************************/ -std::string MPI_CLASS::getNodeName() -{ +std::string MPI_CLASS::getNodeName() { #ifdef USE_MPI int length; char name[MPI_MAX_PROCESSOR_NAME + 1]; - memset( name, 0, MPI_MAX_PROCESSOR_NAME + 1 ); - MPI_Get_processor_name( name, &length ); - return std::string( name ); + memset(name, 0, MPI_MAX_PROCESSOR_NAME + 1); + MPI_Get_processor_name(name, &length); + return std::string(name); #else return "Node0"; #endif } - /************************************************************************ * Overload operator == * ************************************************************************/ -bool MPI_CLASS::operator==( const MPI_CLASS &comm ) const -{ +bool MPI_CLASS::operator==(const MPI_CLASS &comm) const { return communicator == comm.communicator; } - /************************************************************************ * Overload operator != * ************************************************************************/ -bool MPI_CLASS::operator!=( const MPI_CLASS &comm ) const -{ +bool MPI_CLASS::operator!=(const MPI_CLASS &comm) const { return communicator != comm.communicator; } - /************************************************************************ * Overload operator < * ************************************************************************/ -bool MPI_CLASS::operator<( const MPI_CLASS &comm ) const -{ - MPI_ASSERT( !this->d_isNull && !comm.d_isNull ); +bool MPI_CLASS::operator<(const MPI_CLASS &comm) const { + MPI_ASSERT(!this->d_isNull && !comm.d_isNull); bool flag = true; // First check if either communicator is NULL - if ( this->d_isNull ) + if (this->d_isNull) return false; - if ( comm.d_isNull ) + if (comm.d_isNull) flag = false; // Use compare to check if the comms are equal - if ( compare( comm ) != 0 ) + if (compare(comm) != 0) return false; // Check that the size of the other communicator is > the current communicator size - if ( comm_size >= comm.comm_size ) + if (comm_size >= comm.comm_size) flag = false; // Check the union of the communicator groups // this is < comm iff this group is a subgroup of comm's group #ifdef USE_MPI - MPI_Group group1 = MPI_GROUP_EMPTY, group2 = MPI_GROUP_EMPTY, group12 = MPI_GROUP_EMPTY; - if ( !d_isNull ) - MPI_Comm_group( communicator, &group1 ); - if ( !comm.d_isNull ) - MPI_Comm_group( comm.communicator, &group2 ); - MPI_Group_union( group1, group2, &group12 ); + MPI_Group group1 = MPI_GROUP_EMPTY, group2 = MPI_GROUP_EMPTY, + group12 = MPI_GROUP_EMPTY; + if (!d_isNull) + MPI_Comm_group(communicator, &group1); + if (!comm.d_isNull) + MPI_Comm_group(comm.communicator, &group2); + MPI_Group_union(group1, group2, &group12); int compare; - MPI_Group_compare( group2, group12, &compare ); - if ( compare == MPI_UNEQUAL ) + MPI_Group_compare(group2, group12, &compare); + if (compare == MPI_UNEQUAL) flag = false; - MPI_Group_free( &group1 ); - MPI_Group_free( &group2 ); - MPI_Group_free( &group12 ); + MPI_Group_free(&group1); + MPI_Group_free(&group2); + MPI_Group_free(&group12); #endif // Perform a global reduce of the flag (equivalent to all operation) - return allReduce( flag ); + return allReduce(flag); } - /************************************************************************ * Overload operator <= * ************************************************************************/ -bool MPI_CLASS::operator<=( const MPI_CLASS &comm ) const -{ - MPI_ASSERT( !this->d_isNull && !comm.d_isNull ); +bool MPI_CLASS::operator<=(const MPI_CLASS &comm) const { + MPI_ASSERT(!this->d_isNull && !comm.d_isNull); bool flag = true; // First check if either communicator is NULL - if ( this->d_isNull ) + if (this->d_isNull) return false; - if ( comm.d_isNull ) + if (comm.d_isNull) flag = false; #ifdef USE_MPI int world_size = 0; - MPI_Comm_size( MPI_COMM_WORLD, &world_size ); - if ( comm.getSize() == world_size ) + MPI_Comm_size(MPI_COMM_WORLD, &world_size); + if (comm.getSize() == world_size) return true; - if ( getSize() == 1 && !comm.d_isNull ) + if (getSize() == 1 && !comm.d_isNull) return true; #endif // Use compare to check if the comms are equal - if ( compare( comm ) != 0 ) + if (compare(comm) != 0) return true; // Check that the size of the other communicator is > the current communicator size // this is <= comm iff this group is a subgroup of comm's group - if ( comm_size > comm.comm_size ) + if (comm_size > comm.comm_size) flag = false; // Check the unnion of the communicator groups #ifdef USE_MPI MPI_Group group1, group2, group12; - MPI_Comm_group( communicator, &group1 ); - MPI_Comm_group( comm.communicator, &group2 ); - MPI_Group_union( group1, group2, &group12 ); + MPI_Comm_group(communicator, &group1); + MPI_Comm_group(comm.communicator, &group2); + MPI_Group_union(group1, group2, &group12); int compare; - MPI_Group_compare( group2, group12, &compare ); - if ( compare == MPI_UNEQUAL ) + MPI_Group_compare(group2, group12, &compare); + if (compare == MPI_UNEQUAL) flag = false; - MPI_Group_free( &group1 ); - MPI_Group_free( &group2 ); - MPI_Group_free( &group12 ); + MPI_Group_free(&group1); + MPI_Group_free(&group2); + MPI_Group_free(&group12); #endif // Perform a global reduce of the flag (equivalent to all operation) - return allReduce( flag ); + return allReduce(flag); } - /************************************************************************ * Overload operator > * ************************************************************************/ -bool MPI_CLASS::operator>( const MPI_CLASS &comm ) const -{ +bool MPI_CLASS::operator>(const MPI_CLASS &comm) const { bool flag = true; // First check if either communicator is NULL - if ( this->d_isNull ) + if (this->d_isNull) return false; - if ( comm.d_isNull ) + if (comm.d_isNull) flag = false; // Use compare to check if the comms are equal - if ( compare( comm ) != 0 ) + if (compare(comm) != 0) return false; // Check that the size of the other communicator is > the current communicator size - if ( comm_size <= comm.comm_size ) + if (comm_size <= comm.comm_size) flag = false; // Check the unnion of the communicator groups // this is > comm iff comm's group is a subgroup of this group #ifdef USE_MPI - MPI_Group group1 = MPI_GROUP_EMPTY, group2 = MPI_GROUP_EMPTY, group12 = MPI_GROUP_EMPTY; - if ( !d_isNull ) - MPI_Comm_group( communicator, &group1 ); - if ( !comm.d_isNull ) - MPI_Comm_group( comm.communicator, &group2 ); - MPI_Group_union( group1, group2, &group12 ); + MPI_Group group1 = MPI_GROUP_EMPTY, group2 = MPI_GROUP_EMPTY, + group12 = MPI_GROUP_EMPTY; + if (!d_isNull) + MPI_Comm_group(communicator, &group1); + if (!comm.d_isNull) + MPI_Comm_group(comm.communicator, &group2); + MPI_Group_union(group1, group2, &group12); int compare; - MPI_Group_compare( group1, group12, &compare ); - if ( compare == MPI_UNEQUAL ) + MPI_Group_compare(group1, group12, &compare); + if (compare == MPI_UNEQUAL) flag = false; - MPI_Group_free( &group1 ); - MPI_Group_free( &group2 ); - MPI_Group_free( &group12 ); + MPI_Group_free(&group1); + MPI_Group_free(&group2); + MPI_Group_free(&group12); #endif // Perform a global reduce of the flag (equivalent to all operation) - return allReduce( flag ); + return allReduce(flag); } - /************************************************************************ * Overload operator >= * ************************************************************************/ -bool MPI_CLASS::operator>=( const MPI_CLASS &comm ) const -{ +bool MPI_CLASS::operator>=(const MPI_CLASS &comm) const { bool flag = true; // First check if either communicator is NULL - if ( this->d_isNull ) + if (this->d_isNull) return false; - if ( comm.d_isNull ) + if (comm.d_isNull) flag = false; #ifdef USE_MPI int world_size = 0; - MPI_Comm_size( MPI_COMM_WORLD, &world_size ); - if ( getSize() == world_size ) + MPI_Comm_size(MPI_COMM_WORLD, &world_size); + if (getSize() == world_size) return true; - if ( comm.getSize() == 1 && !comm.d_isNull ) + if (comm.getSize() == 1 && !comm.d_isNull) return true; #endif // Use compare to check if the comms are equal - if ( compare( comm ) != 0 ) + if (compare(comm) != 0) return true; // Check that the size of the other communicator is > the current communicator size - if ( comm_size < comm.comm_size ) + if (comm_size < comm.comm_size) flag = false; // Check the unnion of the communicator groups // this is >= comm iff comm's group is a subgroup of this group #ifdef USE_MPI - MPI_Group group1 = MPI_GROUP_EMPTY, group2 = MPI_GROUP_EMPTY, group12 = MPI_GROUP_EMPTY; - if ( !d_isNull ) - MPI_Comm_group( communicator, &group1 ); - if ( !comm.d_isNull ) - MPI_Comm_group( comm.communicator, &group2 ); - MPI_Group_union( group1, group2, &group12 ); + MPI_Group group1 = MPI_GROUP_EMPTY, group2 = MPI_GROUP_EMPTY, + group12 = MPI_GROUP_EMPTY; + if (!d_isNull) + MPI_Comm_group(communicator, &group1); + if (!comm.d_isNull) + MPI_Comm_group(comm.communicator, &group2); + MPI_Group_union(group1, group2, &group12); int compare; - MPI_Group_compare( group1, group12, &compare ); - if ( compare == MPI_UNEQUAL ) + MPI_Group_compare(group1, group12, &compare); + if (compare == MPI_UNEQUAL) flag = false; - MPI_Group_free( &group1 ); - MPI_Group_free( &group2 ); - MPI_Group_free( &group12 ); + MPI_Group_free(&group1); + MPI_Group_free(&group2); + MPI_Group_free(&group12); #endif // Perform a global reduce of the flag (equivalent to all operation) - return allReduce( flag ); + return allReduce(flag); } - /************************************************************************ * Compare two comm objects * ************************************************************************/ -int MPI_CLASS::compare( const MPI_CLASS &comm ) const -{ - if ( communicator == comm.communicator ) +int MPI_CLASS::compare(const MPI_CLASS &comm) const { + if (communicator == comm.communicator) return 1; #ifdef USE_MPI - if ( d_isNull || comm.d_isNull ) + if (d_isNull || comm.d_isNull) return 0; int result; - check_MPI( MPI_Comm_compare( communicator, comm.communicator, &result ) ); - if ( result == MPI_IDENT ) + check_MPI(MPI_Comm_compare(communicator, comm.communicator, &result)); + if (result == MPI_IDENT) return 2; - else if ( result == MPI_CONGRUENT ) + else if (result == MPI_CONGRUENT) return 3; - else if ( result == MPI_SIMILAR ) + else if (result == MPI_SIMILAR) return 4; - else if ( result == MPI_UNEQUAL ) + else if (result == MPI_UNEQUAL) return 0; - MPI_ERROR( "Unknown results from comm compare" ); + MPI_ERROR("Unknown results from comm compare"); #else - if ( comm.communicator == MPI_COMM_NULL || communicator == MPI_COMM_NULL ) + if (comm.communicator == MPI_COMM_NULL || communicator == MPI_COMM_NULL) return 0; else return 3; @@ -1094,45 +1030,43 @@ int MPI_CLASS::compare( const MPI_CLASS &comm ) const return 0; } - /************************************************************************ * Abort the program. * ************************************************************************/ -void MPI_CLASS::setCallAbortInSerialInsteadOfExit( bool flag ) { d_call_abort = flag; } -void MPI_CLASS::abort() const -{ +void MPI_CLASS::setCallAbortInSerialInsteadOfExit(bool flag) { + d_call_abort = flag; +} +void MPI_CLASS::abort() const { #ifdef USE_MPI MPI_Comm comm = communicator; - if ( comm == MPI_COMM_NULL ) + if (comm == MPI_COMM_NULL) comm = MPI_COMM_WORLD; - if ( !MPI_active() ) { + if (!MPI_active()) { // MPI is not availible - exit( -1 ); - } else if ( comm_size > 1 ) { - MPI_Abort( comm, -1 ); - } else if ( d_call_abort ) { - MPI_Abort( comm, -1 ); + exit(-1); + } else if (comm_size > 1) { + MPI_Abort(comm, -1); + } else if (d_call_abort) { + MPI_Abort(comm, -1); } else { - exit( -1 ); + exit(-1); } #else - exit( -1 ); + exit(-1); #endif } - /************************************************************************ * newTag * ************************************************************************/ -int MPI_CLASS::newTag() -{ +int MPI_CLASS::newTag() { #ifdef USE_MPI // Syncronize the processes to ensure all ranks enter this call // Needed so the count will match barrier(); // Return and increment the tag - int tag = ( *d_currentTag )++; - MPI_INSIST( tag <= d_maxTag, "Maximum number of tags exceeded\n" ); + int tag = (*d_currentTag)++; + MPI_INSIST(tag <= d_maxTag, "Maximum number of tags exceeded\n"); return tag; #else static int globalCurrentTag = 1; @@ -1140,2320 +1074,2311 @@ int MPI_CLASS::newTag() #endif } - /************************************************************************ * allReduce * ************************************************************************/ -bool MPI_CLASS::allReduce( const bool value ) const -{ +bool MPI_CLASS::allReduce(const bool value) const { bool ret = value; - if ( comm_size > 1 ) { + if (comm_size > 1) { #ifdef USE_MPI - MPI_Allreduce( - (void *) &value, (void *) &ret, 1, MPI_UNSIGNED_CHAR, MPI_MIN, communicator ); + MPI_Allreduce((void *)&value, (void *)&ret, 1, MPI_UNSIGNED_CHAR, + MPI_MIN, communicator); #else - MPI_ERROR( "This shouldn't be possible" ); + MPI_ERROR("This shouldn't be possible"); #endif } return ret; } - /************************************************************************ * anyReduce * ************************************************************************/ -bool MPI_CLASS::anyReduce( const bool value ) const -{ +bool MPI_CLASS::anyReduce(const bool value) const { bool ret = value; - if ( comm_size > 1 ) { + if (comm_size > 1) { #ifdef USE_MPI - MPI_Allreduce( - (void *) &value, (void *) &ret, 1, MPI_UNSIGNED_CHAR, MPI_MAX, communicator ); + MPI_Allreduce((void *)&value, (void *)&ret, 1, MPI_UNSIGNED_CHAR, + MPI_MAX, communicator); #else - MPI_ERROR( "This shouldn't be possible" ); + MPI_ERROR("This shouldn't be possible"); #endif } return ret; } - /************************************************************************ * call_sumReduce * * Note: these specializations are only called when using MPI. * ************************************************************************/ #ifdef USE_MPI // unsigned char -template<> -void MPI_CLASS::call_sumReduce( - const unsigned char *send, unsigned char *recv, const int n ) const -{ - PROFILE_START( "sumReduce1", profile_level ); - MPI_Allreduce( (void *) send, (void *) recv, n, MPI_UNSIGNED_CHAR, MPI_SUM, communicator ); - PROFILE_STOP( "sumReduce1", profile_level ); +template <> +void MPI_CLASS::call_sumReduce(const unsigned char *send, + unsigned char *recv, + const int n) const { + PROFILE_START("sumReduce1", profile_level); + MPI_Allreduce((void *)send, (void *)recv, n, MPI_UNSIGNED_CHAR, MPI_SUM, + communicator); + PROFILE_STOP("sumReduce1", profile_level); } -template<> -void MPI_CLASS::call_sumReduce( unsigned char *x, const int n ) const -{ - PROFILE_START( "sumReduce2", profile_level ); +template <> +void MPI_CLASS::call_sumReduce(unsigned char *x, + const int n) const { + PROFILE_START("sumReduce2", profile_level); auto send = x; auto recv = new unsigned char[n]; - MPI_Allreduce( send, recv, n, MPI_UNSIGNED_CHAR, MPI_SUM, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_UNSIGNED_CHAR, MPI_SUM, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; - PROFILE_STOP( "sumReduce2", profile_level ); + PROFILE_STOP("sumReduce2", profile_level); } // char -template<> -void MPI_CLASS::call_sumReduce( const char *send, char *recv, const int n ) const -{ - PROFILE_START( "sumReduce1", profile_level ); - MPI_Allreduce( (void *) send, (void *) recv, n, MPI_SIGNED_CHAR, MPI_SUM, communicator ); - PROFILE_STOP( "sumReduce1", profile_level ); +template <> +void MPI_CLASS::call_sumReduce(const char *send, char *recv, + const int n) const { + PROFILE_START("sumReduce1", profile_level); + MPI_Allreduce((void *)send, (void *)recv, n, MPI_SIGNED_CHAR, MPI_SUM, + communicator); + PROFILE_STOP("sumReduce1", profile_level); } -template<> -void MPI_CLASS::call_sumReduce( char *x, const int n ) const -{ - PROFILE_START( "sumReduce2", profile_level ); +template <> void MPI_CLASS::call_sumReduce(char *x, const int n) const { + PROFILE_START("sumReduce2", profile_level); auto send = x; auto recv = new char[n]; - MPI_Allreduce( send, recv, n, MPI_SIGNED_CHAR, MPI_SUM, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_SIGNED_CHAR, MPI_SUM, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; - PROFILE_STOP( "sumReduce2", profile_level ); + PROFILE_STOP("sumReduce2", profile_level); } // unsigned int -template<> -void MPI_CLASS::call_sumReduce( - const unsigned int *send, unsigned int *recv, const int n ) const -{ - PROFILE_START( "sumReduce1", profile_level ); - MPI_Allreduce( (void *) send, (void *) recv, n, MPI_UNSIGNED, MPI_SUM, communicator ); - PROFILE_STOP( "sumReduce1", profile_level ); +template <> +void MPI_CLASS::call_sumReduce(const unsigned int *send, + unsigned int *recv, + const int n) const { + PROFILE_START("sumReduce1", profile_level); + MPI_Allreduce((void *)send, (void *)recv, n, MPI_UNSIGNED, MPI_SUM, + communicator); + PROFILE_STOP("sumReduce1", profile_level); } -template<> -void MPI_CLASS::call_sumReduce( unsigned int *x, const int n ) const -{ - PROFILE_START( "sumReduce2", profile_level ); +template <> +void MPI_CLASS::call_sumReduce(unsigned int *x, + const int n) const { + PROFILE_START("sumReduce2", profile_level); auto send = x; auto recv = new unsigned int[n]; - MPI_Allreduce( send, recv, n, MPI_UNSIGNED, MPI_SUM, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_UNSIGNED, MPI_SUM, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; - PROFILE_STOP( "sumReduce2", profile_level ); + PROFILE_STOP("sumReduce2", profile_level); } // int -template<> -void MPI_CLASS::call_sumReduce( const int *send, int *recv, const int n ) const -{ - PROFILE_START( "sumReduce1", profile_level ); - MPI_Allreduce( (void *) send, (void *) recv, n, MPI_INT, MPI_SUM, communicator ); - PROFILE_STOP( "sumReduce1", profile_level ); +template <> +void MPI_CLASS::call_sumReduce(const int *send, int *recv, + const int n) const { + PROFILE_START("sumReduce1", profile_level); + MPI_Allreduce((void *)send, (void *)recv, n, MPI_INT, MPI_SUM, + communicator); + PROFILE_STOP("sumReduce1", profile_level); } -template<> -void MPI_CLASS::call_sumReduce( int *x, const int n ) const -{ - PROFILE_START( "sumReduce2", profile_level ); +template <> void MPI_CLASS::call_sumReduce(int *x, const int n) const { + PROFILE_START("sumReduce2", profile_level); auto send = x; auto recv = new int[n]; - MPI_Allreduce( send, recv, n, MPI_INT, MPI_SUM, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_INT, MPI_SUM, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; - PROFILE_STOP( "sumReduce2", profile_level ); + PROFILE_STOP("sumReduce2", profile_level); } // long int -template<> -void MPI_CLASS::call_sumReduce( const long int *send, long int *recv, const int n ) const -{ - PROFILE_START( "sumReduce1", profile_level ); - MPI_Allreduce( (void *) send, (void *) recv, n, MPI_LONG, MPI_SUM, communicator ); - PROFILE_STOP( "sumReduce1", profile_level ); +template <> +void MPI_CLASS::call_sumReduce(const long int *send, long int *recv, + const int n) const { + PROFILE_START("sumReduce1", profile_level); + MPI_Allreduce((void *)send, (void *)recv, n, MPI_LONG, MPI_SUM, + communicator); + PROFILE_STOP("sumReduce1", profile_level); } -template<> -void MPI_CLASS::call_sumReduce( long int *x, const int n ) const -{ - PROFILE_START( "sumReduce2", profile_level ); +template <> +void MPI_CLASS::call_sumReduce(long int *x, const int n) const { + PROFILE_START("sumReduce2", profile_level); auto send = x; auto recv = new long int[n]; - MPI_Allreduce( send, recv, n, MPI_LONG, MPI_SUM, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_LONG, MPI_SUM, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; - PROFILE_STOP( "sumReduce2", profile_level ); + PROFILE_STOP("sumReduce2", profile_level); } // unsigned long int -template<> -void MPI_CLASS::call_sumReduce( - const unsigned long *send, unsigned long *recv, const int n ) const -{ - PROFILE_START( "sumReduce1", profile_level ); - MPI_Allreduce( (void *) send, (void *) recv, n, MPI_UNSIGNED_LONG, MPI_SUM, communicator ); - PROFILE_STOP( "sumReduce1", profile_level ); +template <> +void MPI_CLASS::call_sumReduce(const unsigned long *send, + unsigned long *recv, + const int n) const { + PROFILE_START("sumReduce1", profile_level); + MPI_Allreduce((void *)send, (void *)recv, n, MPI_UNSIGNED_LONG, MPI_SUM, + communicator); + PROFILE_STOP("sumReduce1", profile_level); } -template<> -void MPI_CLASS::call_sumReduce( unsigned long *x, const int n ) const -{ - PROFILE_START( "sumReduce2", profile_level ); +template <> +void MPI_CLASS::call_sumReduce(unsigned long *x, + const int n) const { + PROFILE_START("sumReduce2", profile_level); auto send = x; auto recv = new unsigned long int[n]; - MPI_Allreduce( send, recv, n, MPI_UNSIGNED_LONG, MPI_SUM, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_UNSIGNED_LONG, MPI_SUM, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; - PROFILE_STOP( "sumReduce2", profile_level ); + PROFILE_STOP("sumReduce2", profile_level); } // size_t #ifdef USE_WINDOWS -template<> -void MPI_CLASS::call_sumReduce( const size_t *send, size_t *recv, const int n ) const -{ - MPI_ASSERT( MPI_SIZE_T != 0 ); - PROFILE_START( "sumReduce1", profile_level ); - MPI_Allreduce( (void *) send, (void *) recv, n, MPI_SIZE_T, MPI_SUM, communicator ); - PROFILE_STOP( "sumReduce1", profile_level ); +template <> +void MPI_CLASS::call_sumReduce(const size_t *send, size_t *recv, + const int n) const { + MPI_ASSERT(MPI_SIZE_T != 0); + PROFILE_START("sumReduce1", profile_level); + MPI_Allreduce((void *)send, (void *)recv, n, MPI_SIZE_T, MPI_SUM, + communicator); + PROFILE_STOP("sumReduce1", profile_level); } -template<> -void MPI_CLASS::call_sumReduce( size_t *x, const int n ) const -{ - MPI_ASSERT( MPI_SIZE_T != 0 ); - PROFILE_START( "sumReduce2", profile_level ); +template <> +void MPI_CLASS::call_sumReduce(size_t *x, const int n) const { + MPI_ASSERT(MPI_SIZE_T != 0); + PROFILE_START("sumReduce2", profile_level); auto send = x; auto recv = new size_t[n]; - MPI_Allreduce( (void *) send, (void *) recv, n, MPI_SIZE_T, MPI_SUM, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce((void *)send, (void *)recv, n, MPI_SIZE_T, MPI_SUM, + communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; - PROFILE_STOP( "sumReduce2", profile_level ); + PROFILE_STOP("sumReduce2", profile_level); } #endif // float -template<> -void MPI_CLASS::call_sumReduce( const float *send, float *recv, const int n ) const -{ - PROFILE_START( "sumReduce1", profile_level ); - MPI_Allreduce( (void *) send, (void *) recv, n, MPI_FLOAT, MPI_SUM, communicator ); - PROFILE_STOP( "sumReduce1", profile_level ); +template <> +void MPI_CLASS::call_sumReduce(const float *send, float *recv, + const int n) const { + PROFILE_START("sumReduce1", profile_level); + MPI_Allreduce((void *)send, (void *)recv, n, MPI_FLOAT, MPI_SUM, + communicator); + PROFILE_STOP("sumReduce1", profile_level); } -template<> -void MPI_CLASS::call_sumReduce( float *x, const int n ) const -{ - PROFILE_START( "sumReduce2", profile_level ); +template <> void MPI_CLASS::call_sumReduce(float *x, const int n) const { + PROFILE_START("sumReduce2", profile_level); auto send = x; auto recv = new float[n]; - MPI_Allreduce( send, recv, n, MPI_FLOAT, MPI_SUM, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_FLOAT, MPI_SUM, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; - PROFILE_STOP( "sumReduce2", profile_level ); + PROFILE_STOP("sumReduce2", profile_level); } // double -template<> -void MPI_CLASS::call_sumReduce( const double *send, double *recv, const int n ) const -{ - PROFILE_START( "sumReduce1", profile_level ); - MPI_Allreduce( (void *) send, (void *) recv, n, MPI_DOUBLE, MPI_SUM, communicator ); - PROFILE_STOP( "sumReduce1", profile_level ); +template <> +void MPI_CLASS::call_sumReduce(const double *send, double *recv, + const int n) const { + PROFILE_START("sumReduce1", profile_level); + MPI_Allreduce((void *)send, (void *)recv, n, MPI_DOUBLE, MPI_SUM, + communicator); + PROFILE_STOP("sumReduce1", profile_level); } -template<> -void MPI_CLASS::call_sumReduce( double *x, const int n ) const -{ - PROFILE_START( "sumReduce2", profile_level ); +template <> +void MPI_CLASS::call_sumReduce(double *x, const int n) const { + PROFILE_START("sumReduce2", profile_level); auto send = x; auto recv = new double[n]; - MPI_Allreduce( send, recv, n, MPI_DOUBLE, MPI_SUM, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_DOUBLE, MPI_SUM, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; - PROFILE_STOP( "sumReduce2", profile_level ); + PROFILE_STOP("sumReduce2", profile_level); } // std::complex -template<> +template <> void MPI_CLASS::call_sumReduce>( - const std::complex *x, std::complex *y, const int n ) const -{ - PROFILE_START( "sumReduce1", profile_level ); + const std::complex *x, std::complex *y, const int n) const { + PROFILE_START("sumReduce1", profile_level); auto send = new double[2 * n]; auto recv = new double[2 * n]; - for ( int i = 0; i < n; i++ ) { - send[2 * i + 0] = real( x[i] ); - send[2 * i + 1] = imag( x[i] ); + for (int i = 0; i < n; i++) { + send[2 * i + 0] = real(x[i]); + send[2 * i + 1] = imag(x[i]); } - MPI_Allreduce( (void *) send, (void *) recv, 2 * n, MPI_DOUBLE, MPI_SUM, communicator ); - for ( int i = 0; i < n; i++ ) - y[i] = std::complex( recv[2 * i + 0], recv[2 * i + 1] ); + MPI_Allreduce((void *)send, (void *)recv, 2 * n, MPI_DOUBLE, MPI_SUM, + communicator); + for (int i = 0; i < n; i++) + y[i] = std::complex(recv[2 * i + 0], recv[2 * i + 1]); delete[] send; delete[] recv; - PROFILE_STOP( "sumReduce1", profile_level ); + PROFILE_STOP("sumReduce1", profile_level); } -template<> -void MPI_CLASS::call_sumReduce>( std::complex *x, const int n ) const -{ - PROFILE_START( "sumReduce2", profile_level ); +template <> +void MPI_CLASS::call_sumReduce>(std::complex *x, + const int n) const { + PROFILE_START("sumReduce2", profile_level); auto send = new double[2 * n]; auto recv = new double[2 * n]; - for ( int i = 0; i < n; i++ ) { - send[2 * i + 0] = real( x[i] ); - send[2 * i + 1] = imag( x[i] ); + for (int i = 0; i < n; i++) { + send[2 * i + 0] = real(x[i]); + send[2 * i + 1] = imag(x[i]); } - MPI_Allreduce( send, recv, 2 * n, MPI_DOUBLE, MPI_SUM, communicator ); - for ( int i = 0; i < n; i++ ) - x[i] = std::complex( recv[2 * i + 0], recv[2 * i + 1] ); + MPI_Allreduce(send, recv, 2 * n, MPI_DOUBLE, MPI_SUM, communicator); + for (int i = 0; i < n; i++) + x[i] = std::complex(recv[2 * i + 0], recv[2 * i + 1]); delete[] send; delete[] recv; - PROFILE_STOP( "sumReduce2", profile_level ); + PROFILE_STOP("sumReduce2", profile_level); } #endif - /************************************************************************ * call_minReduce * * Note: these specializations are only called when using MPI. * ************************************************************************/ #ifdef USE_MPI // unsigned char -template<> -void MPI_CLASS::call_minReduce( - const unsigned char *send, unsigned char *recv, const int n, int *comm_rank_of_min ) const -{ - if ( comm_rank_of_min == nullptr ) { - PROFILE_START( "minReduce1", profile_level ); - MPI_Allreduce( (void *) send, (void *) recv, n, MPI_UNSIGNED_CHAR, MPI_MIN, communicator ); - PROFILE_STOP( "minReduce1", profile_level ); +template <> +void MPI_CLASS::call_minReduce(const unsigned char *send, + unsigned char *recv, const int n, + int *comm_rank_of_min) const { + if (comm_rank_of_min == nullptr) { + PROFILE_START("minReduce1", profile_level); + MPI_Allreduce((void *)send, (void *)recv, n, MPI_UNSIGNED_CHAR, MPI_MIN, + communicator); + PROFILE_STOP("minReduce1", profile_level); } else { auto tmp = new int[n]; - for ( int i = 0; i < n; i++ ) + for (int i = 0; i < n; i++) tmp[i] = send[i]; - call_minReduce( tmp, n, comm_rank_of_min ); - for ( int i = 0; i < n; i++ ) - recv[i] = static_cast( tmp[i] ); + call_minReduce(tmp, n, comm_rank_of_min); + for (int i = 0; i < n; i++) + recv[i] = static_cast(tmp[i]); delete[] tmp; } } -template<> -void MPI_CLASS::call_minReduce( - unsigned char *x, const int n, int *comm_rank_of_min ) const -{ - if ( comm_rank_of_min == nullptr ) { - PROFILE_START( "minReduce2", profile_level ); +template <> +void MPI_CLASS::call_minReduce(unsigned char *x, const int n, + int *comm_rank_of_min) const { + if (comm_rank_of_min == nullptr) { + PROFILE_START("minReduce2", profile_level); auto send = x; auto recv = new unsigned char[n]; - MPI_Allreduce( send, recv, n, MPI_UNSIGNED_CHAR, MPI_MIN, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_UNSIGNED_CHAR, MPI_MIN, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; - PROFILE_STOP( "minReduce2", profile_level ); + PROFILE_STOP("minReduce2", profile_level); } else { auto tmp = new int[n]; - for ( int i = 0; i < n; i++ ) + for (int i = 0; i < n; i++) tmp[i] = x[i]; - call_minReduce( tmp, n, comm_rank_of_min ); - for ( int i = 0; i < n; i++ ) - x[i] = static_cast( tmp[i] ); + call_minReduce(tmp, n, comm_rank_of_min); + for (int i = 0; i < n; i++) + x[i] = static_cast(tmp[i]); delete[] tmp; } } // char -template<> -void MPI_CLASS::call_minReduce( - const char *send, char *recv, const int n, int *comm_rank_of_min ) const -{ - if ( comm_rank_of_min == nullptr ) { - PROFILE_START( "minReduce1", profile_level ); - MPI_Allreduce( (void *) send, (void *) recv, n, MPI_SIGNED_CHAR, MPI_MIN, communicator ); - PROFILE_STOP( "minReduce1", profile_level ); +template <> +void MPI_CLASS::call_minReduce(const char *send, char *recv, const int n, + int *comm_rank_of_min) const { + if (comm_rank_of_min == nullptr) { + PROFILE_START("minReduce1", profile_level); + MPI_Allreduce((void *)send, (void *)recv, n, MPI_SIGNED_CHAR, MPI_MIN, + communicator); + PROFILE_STOP("minReduce1", profile_level); } else { auto tmp = new int[n]; - for ( int i = 0; i < n; i++ ) + for (int i = 0; i < n; i++) tmp[i] = send[i]; - call_minReduce( tmp, n, comm_rank_of_min ); - for ( int i = 0; i < n; i++ ) - recv[i] = static_cast( tmp[i] ); + call_minReduce(tmp, n, comm_rank_of_min); + for (int i = 0; i < n; i++) + recv[i] = static_cast(tmp[i]); delete[] tmp; } } -template<> -void MPI_CLASS::call_minReduce( char *x, const int n, int *comm_rank_of_min ) const -{ - if ( comm_rank_of_min == nullptr ) { - PROFILE_START( "minReduce2", profile_level ); +template <> +void MPI_CLASS::call_minReduce(char *x, const int n, + int *comm_rank_of_min) const { + if (comm_rank_of_min == nullptr) { + PROFILE_START("minReduce2", profile_level); auto send = x; auto recv = new char[n]; - MPI_Allreduce( send, recv, n, MPI_SIGNED_CHAR, MPI_MIN, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_SIGNED_CHAR, MPI_MIN, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; - PROFILE_STOP( "minReduce2", profile_level ); + PROFILE_STOP("minReduce2", profile_level); } else { auto tmp = new int[n]; - for ( int i = 0; i < n; i++ ) + for (int i = 0; i < n; i++) tmp[i] = x[i]; - call_minReduce( tmp, n, comm_rank_of_min ); - for ( int i = 0; i < n; i++ ) - x[i] = static_cast( tmp[i] ); + call_minReduce(tmp, n, comm_rank_of_min); + for (int i = 0; i < n; i++) + x[i] = static_cast(tmp[i]); delete[] tmp; } } // unsigned int -template<> -void MPI_CLASS::call_minReduce( - const unsigned int *send, unsigned int *recv, const int n, int *comm_rank_of_min ) const -{ - if ( comm_rank_of_min == nullptr ) { - PROFILE_START( "minReduce1", profile_level ); - MPI_Allreduce( (void *) send, (void *) recv, n, MPI_UNSIGNED, MPI_MIN, communicator ); - PROFILE_STOP( "minReduce1", profile_level ); +template <> +void MPI_CLASS::call_minReduce(const unsigned int *send, + unsigned int *recv, const int n, + int *comm_rank_of_min) const { + if (comm_rank_of_min == nullptr) { + PROFILE_START("minReduce1", profile_level); + MPI_Allreduce((void *)send, (void *)recv, n, MPI_UNSIGNED, MPI_MIN, + communicator); + PROFILE_STOP("minReduce1", profile_level); } else { auto tmp = new int[n]; - for ( int i = 0; i < n; i++ ) - tmp[i] = unsigned_to_signed( send[i] ); - call_minReduce( tmp, n, comm_rank_of_min ); - for ( int i = 0; i < n; i++ ) - recv[i] = signed_to_unsigned( tmp[i] ); + for (int i = 0; i < n; i++) + tmp[i] = unsigned_to_signed(send[i]); + call_minReduce(tmp, n, comm_rank_of_min); + for (int i = 0; i < n; i++) + recv[i] = signed_to_unsigned(tmp[i]); delete[] tmp; } } -template<> -void MPI_CLASS::call_minReduce( - unsigned int *x, const int n, int *comm_rank_of_min ) const -{ - if ( comm_rank_of_min == nullptr ) { - PROFILE_START( "minReduce2", profile_level ); +template <> +void MPI_CLASS::call_minReduce(unsigned int *x, const int n, + int *comm_rank_of_min) const { + if (comm_rank_of_min == nullptr) { + PROFILE_START("minReduce2", profile_level); auto send = x; auto recv = new unsigned int[n]; - MPI_Allreduce( send, recv, n, MPI_UNSIGNED, MPI_MIN, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_UNSIGNED, MPI_MIN, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; - PROFILE_STOP( "minReduce2", profile_level ); + PROFILE_STOP("minReduce2", profile_level); } else { auto tmp = new int[n]; - for ( int i = 0; i < n; i++ ) - tmp[i] = unsigned_to_signed( x[i] ); - call_minReduce( tmp, n, comm_rank_of_min ); - for ( int i = 0; i < n; i++ ) - x[i] = signed_to_unsigned( tmp[i] ); + for (int i = 0; i < n; i++) + tmp[i] = unsigned_to_signed(x[i]); + call_minReduce(tmp, n, comm_rank_of_min); + for (int i = 0; i < n; i++) + x[i] = signed_to_unsigned(tmp[i]); delete[] tmp; } } // int -template<> -void MPI_CLASS::call_minReduce( - const int *x, int *y, const int n, int *comm_rank_of_min ) const -{ - PROFILE_START( "minReduce1", profile_level ); - if ( comm_rank_of_min == nullptr ) { - MPI_Allreduce( (void *) x, (void *) y, n, MPI_INT, MPI_MIN, communicator ); +template <> +void MPI_CLASS::call_minReduce(const int *x, int *y, const int n, + int *comm_rank_of_min) const { + PROFILE_START("minReduce1", profile_level); + if (comm_rank_of_min == nullptr) { + MPI_Allreduce((void *)x, (void *)y, n, MPI_INT, MPI_MIN, communicator); } else { auto recv = new IntIntStruct[n]; auto send = new IntIntStruct[n]; - for ( int i = 0; i < n; ++i ) { + for (int i = 0; i < n; ++i) { send[i].j = x[i]; send[i].i = comm_rank; } - MPI_Allreduce( send, recv, n, MPI_2INT, MPI_MINLOC, communicator ); - for ( int i = 0; i < n; ++i ) { - y[i] = recv[i].j; + MPI_Allreduce(send, recv, n, MPI_2INT, MPI_MINLOC, communicator); + for (int i = 0; i < n; ++i) { + y[i] = recv[i].j; comm_rank_of_min[i] = recv[i].i; } delete[] recv; delete[] send; } - PROFILE_STOP( "minReduce1", profile_level ); + PROFILE_STOP("minReduce1", profile_level); } -template<> -void MPI_CLASS::call_minReduce( int *x, const int n, int *comm_rank_of_min ) const -{ - PROFILE_START( "minReduce2", profile_level ); - if ( comm_rank_of_min == nullptr ) { +template <> +void MPI_CLASS::call_minReduce(int *x, const int n, + int *comm_rank_of_min) const { + PROFILE_START("minReduce2", profile_level); + if (comm_rank_of_min == nullptr) { auto send = x; auto recv = new int[n]; - MPI_Allreduce( send, recv, n, MPI_INT, MPI_MIN, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_INT, MPI_MIN, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; } else { auto recv = new IntIntStruct[n]; auto send = new IntIntStruct[n]; - for ( int i = 0; i < n; ++i ) { + for (int i = 0; i < n; ++i) { send[i].j = x[i]; send[i].i = comm_rank; } - MPI_Allreduce( send, recv, n, MPI_2INT, MPI_MINLOC, communicator ); - for ( int i = 0; i < n; ++i ) { - x[i] = recv[i].j; + MPI_Allreduce(send, recv, n, MPI_2INT, MPI_MINLOC, communicator); + for (int i = 0; i < n; ++i) { + x[i] = recv[i].j; comm_rank_of_min[i] = recv[i].i; } delete[] recv; delete[] send; } - PROFILE_STOP( "minReduce2", profile_level ); + PROFILE_STOP("minReduce2", profile_level); } // unsigned long int -template<> -void MPI_CLASS::call_minReduce( const unsigned long int *send, - unsigned long int *recv, const int n, int *comm_rank_of_min ) const -{ - if ( comm_rank_of_min == nullptr ) { - PROFILE_START( "minReduce1", profile_level ); - MPI_Allreduce( (void *) send, (void *) recv, n, MPI_UNSIGNED_LONG, MPI_MIN, communicator ); - PROFILE_STOP( "minReduce1", profile_level ); +template <> +void MPI_CLASS::call_minReduce(const unsigned long int *send, + unsigned long int *recv, + const int n, + int *comm_rank_of_min) const { + if (comm_rank_of_min == nullptr) { + PROFILE_START("minReduce1", profile_level); + MPI_Allreduce((void *)send, (void *)recv, n, MPI_UNSIGNED_LONG, MPI_MIN, + communicator); + PROFILE_STOP("minReduce1", profile_level); } else { auto tmp = new long int[n]; - for ( int i = 0; i < n; i++ ) - tmp[i] = unsigned_to_signed( send[i] ); - call_minReduce( tmp, n, comm_rank_of_min ); - for ( int i = 0; i < n; i++ ) - recv[i] = signed_to_unsigned( tmp[i] ); + for (int i = 0; i < n; i++) + tmp[i] = unsigned_to_signed(send[i]); + call_minReduce(tmp, n, comm_rank_of_min); + for (int i = 0; i < n; i++) + recv[i] = signed_to_unsigned(tmp[i]); delete[] tmp; } } -template<> -void MPI_CLASS::call_minReduce( - unsigned long int *x, const int n, int *comm_rank_of_min ) const -{ - if ( comm_rank_of_min == nullptr ) { - PROFILE_START( "minReduce2", profile_level ); +template <> +void MPI_CLASS::call_minReduce(unsigned long int *x, + const int n, + int *comm_rank_of_min) const { + if (comm_rank_of_min == nullptr) { + PROFILE_START("minReduce2", profile_level); auto send = x; auto recv = new unsigned long int[n]; - MPI_Allreduce( send, recv, n, MPI_UNSIGNED_LONG, MPI_MIN, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_UNSIGNED_LONG, MPI_MIN, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; - PROFILE_STOP( "minReduce2", profile_level ); + PROFILE_STOP("minReduce2", profile_level); } else { auto tmp = new long int[n]; - for ( int i = 0; i < n; i++ ) - tmp[i] = unsigned_to_signed( x[i] ); - call_minReduce( tmp, n, comm_rank_of_min ); - for ( int i = 0; i < n; i++ ) - x[i] = signed_to_unsigned( tmp[i] ); + for (int i = 0; i < n; i++) + tmp[i] = unsigned_to_signed(x[i]); + call_minReduce(tmp, n, comm_rank_of_min); + for (int i = 0; i < n; i++) + x[i] = signed_to_unsigned(tmp[i]); delete[] tmp; } } // long int -template<> -void MPI_CLASS::call_minReduce( - const long int *x, long int *y, const int n, int *comm_rank_of_min ) const -{ - PROFILE_START( "minReduce1", profile_level ); - if ( comm_rank_of_min == nullptr ) { - MPI_Allreduce( (void *) x, (void *) y, n, MPI_LONG, MPI_MIN, communicator ); +template <> +void MPI_CLASS::call_minReduce(const long int *x, long int *y, + const int n, + int *comm_rank_of_min) const { + PROFILE_START("minReduce1", profile_level); + if (comm_rank_of_min == nullptr) { + MPI_Allreduce((void *)x, (void *)y, n, MPI_LONG, MPI_MIN, communicator); } else { auto recv = new LongIntStruct[n]; auto send = new LongIntStruct[n]; - for ( int i = 0; i < n; ++i ) { + for (int i = 0; i < n; ++i) { send[i].j = x[i]; send[i].i = comm_rank; } - MPI_Allreduce( send, recv, n, MPI_LONG_INT, MPI_MINLOC, communicator ); - for ( int i = 0; i < n; ++i ) { - y[i] = recv[i].j; + MPI_Allreduce(send, recv, n, MPI_LONG_INT, MPI_MINLOC, communicator); + for (int i = 0; i < n; ++i) { + y[i] = recv[i].j; comm_rank_of_min[i] = recv[i].i; } delete[] recv; delete[] send; } - PROFILE_STOP( "minReduce1", profile_level ); + PROFILE_STOP("minReduce1", profile_level); } -template<> -void MPI_CLASS::call_minReduce( long int *x, const int n, int *comm_rank_of_min ) const -{ - PROFILE_START( "minReduce2", profile_level ); - if ( comm_rank_of_min == nullptr ) { +template <> +void MPI_CLASS::call_minReduce(long int *x, const int n, + int *comm_rank_of_min) const { + PROFILE_START("minReduce2", profile_level); + if (comm_rank_of_min == nullptr) { auto send = x; auto recv = new long int[n]; - MPI_Allreduce( send, recv, n, MPI_LONG, MPI_MIN, communicator ); - for ( long int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_LONG, MPI_MIN, communicator); + for (long int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; } else { auto recv = new LongIntStruct[n]; auto send = new LongIntStruct[n]; - for ( int i = 0; i < n; ++i ) { + for (int i = 0; i < n; ++i) { send[i].j = x[i]; send[i].i = comm_rank; } - MPI_Allreduce( send, recv, n, MPI_LONG_INT, MPI_MINLOC, communicator ); - for ( int i = 0; i < n; ++i ) { - x[i] = recv[i].j; + MPI_Allreduce(send, recv, n, MPI_LONG_INT, MPI_MINLOC, communicator); + for (int i = 0; i < n; ++i) { + x[i] = recv[i].j; comm_rank_of_min[i] = recv[i].i; } delete[] recv; delete[] send; } - PROFILE_STOP( "minReduce2", profile_level ); + PROFILE_STOP("minReduce2", profile_level); } // unsigned long long int -template<> -void MPI_CLASS::call_minReduce( const unsigned long long int *send, - unsigned long long int *recv, const int n, int *comm_rank_of_min ) const -{ - PROFILE_START( "minReduce1", profile_level ); - if ( comm_rank_of_min == nullptr ) { +template <> +void MPI_CLASS::call_minReduce( + const unsigned long long int *send, unsigned long long int *recv, + const int n, int *comm_rank_of_min) const { + PROFILE_START("minReduce1", profile_level); + if (comm_rank_of_min == nullptr) { auto x = new long long int[n]; auto y = new long long int[n]; - for ( int i = 0; i < n; i++ ) - x[i] = unsigned_to_signed( send[i] ); - MPI_Allreduce( (void *) x, (void *) y, n, MPI_LONG_LONG_INT, MPI_MIN, communicator ); - for ( int i = 0; i < n; i++ ) - recv[i] = signed_to_unsigned( y[i] ); + for (int i = 0; i < n; i++) + x[i] = unsigned_to_signed(send[i]); + MPI_Allreduce((void *)x, (void *)y, n, MPI_LONG_LONG_INT, MPI_MIN, + communicator); + for (int i = 0; i < n; i++) + recv[i] = signed_to_unsigned(y[i]); delete[] x; delete[] y; } else { - printf( "minReduce will use double\n" ); + printf("minReduce will use double\n"); auto tmp = new double[n]; - for ( int i = 0; i < n; i++ ) - tmp[i] = static_cast( send[i] ); - call_minReduce( tmp, n, comm_rank_of_min ); - for ( int i = 0; i < n; i++ ) - recv[i] = static_cast( tmp[i] ); + for (int i = 0; i < n; i++) + tmp[i] = static_cast(send[i]); + call_minReduce(tmp, n, comm_rank_of_min); + for (int i = 0; i < n; i++) + recv[i] = static_cast(tmp[i]); delete[] tmp; } - PROFILE_STOP( "minReduce1", profile_level ); + PROFILE_STOP("minReduce1", profile_level); } -template<> +template <> void MPI_CLASS::call_minReduce( - unsigned long long int *x, const int n, int *comm_rank_of_min ) const -{ + unsigned long long int *x, const int n, int *comm_rank_of_min) const { auto recv = new unsigned long long int[n]; - call_minReduce( x, recv, n, comm_rank_of_min ); - for ( int i = 0; i < n; i++ ) + call_minReduce(x, recv, n, comm_rank_of_min); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; } // long long int -template<> -void MPI_CLASS::call_minReduce( - const long long int *x, long long int *y, const int n, int *comm_rank_of_min ) const -{ - PROFILE_START( "minReduce1", profile_level ); - if ( comm_rank_of_min == nullptr ) { - MPI_Allreduce( (void *) x, (void *) y, n, MPI_LONG_LONG_INT, MPI_MIN, communicator ); +template <> +void MPI_CLASS::call_minReduce(const long long int *x, + long long int *y, const int n, + int *comm_rank_of_min) const { + PROFILE_START("minReduce1", profile_level); + if (comm_rank_of_min == nullptr) { + MPI_Allreduce((void *)x, (void *)y, n, MPI_LONG_LONG_INT, MPI_MIN, + communicator); } else { - printf( "minReduce will use double\n" ); + printf("minReduce will use double\n"); auto tmp = new double[n]; - for ( int i = 0; i < n; i++ ) - tmp[i] = static_cast( x[i] ); - call_minReduce( tmp, n, comm_rank_of_min ); - for ( int i = 0; i < n; i++ ) - y[i] = static_cast( tmp[i] ); + for (int i = 0; i < n; i++) + tmp[i] = static_cast(x[i]); + call_minReduce(tmp, n, comm_rank_of_min); + for (int i = 0; i < n; i++) + y[i] = static_cast(tmp[i]); delete[] tmp; } - PROFILE_STOP( "minReduce1", profile_level ); + PROFILE_STOP("minReduce1", profile_level); } -template<> -void MPI_CLASS::call_minReduce( - long long int *x, const int n, int *comm_rank_of_min ) const -{ +template <> +void MPI_CLASS::call_minReduce(long long int *x, const int n, + int *comm_rank_of_min) const { auto recv = new long long int[n]; - call_minReduce( x, recv, n, comm_rank_of_min ); - for ( int i = 0; i < n; i++ ) - x[i] = signed_to_unsigned( recv[i] ); + call_minReduce(x, recv, n, comm_rank_of_min); + for (int i = 0; i < n; i++) + x[i] = signed_to_unsigned(recv[i]); delete[] recv; } // float -template<> -void MPI_CLASS::call_minReduce( - const float *x, float *y, const int n, int *comm_rank_of_min ) const -{ - PROFILE_START( "minReduce1", profile_level ); - if ( comm_rank_of_min == nullptr ) { - MPI_Allreduce( (void *) x, (void *) y, n, MPI_INT, MPI_MIN, communicator ); +template <> +void MPI_CLASS::call_minReduce(const float *x, float *y, const int n, + int *comm_rank_of_min) const { + PROFILE_START("minReduce1", profile_level); + if (comm_rank_of_min == nullptr) { + MPI_Allreduce((void *)x, (void *)y, n, MPI_INT, MPI_MIN, communicator); } else { auto recv = new FloatIntStruct[n]; auto send = new FloatIntStruct[n]; - for ( int i = 0; i < n; ++i ) { + for (int i = 0; i < n; ++i) { send[i].f = x[i]; send[i].i = comm_rank; } - MPI_Allreduce( send, recv, n, MPI_FLOAT_INT, MPI_MINLOC, communicator ); - for ( int i = 0; i < n; ++i ) { - y[i] = recv[i].f; + MPI_Allreduce(send, recv, n, MPI_FLOAT_INT, MPI_MINLOC, communicator); + for (int i = 0; i < n; ++i) { + y[i] = recv[i].f; comm_rank_of_min[i] = recv[i].i; } delete[] recv; delete[] send; } - PROFILE_STOP( "minReduce1", profile_level ); + PROFILE_STOP("minReduce1", profile_level); } -template<> -void MPI_CLASS::call_minReduce( float *x, const int n, int *comm_rank_of_min ) const -{ - PROFILE_START( "minReduce2", profile_level ); - if ( comm_rank_of_min == nullptr ) { +template <> +void MPI_CLASS::call_minReduce(float *x, const int n, + int *comm_rank_of_min) const { + PROFILE_START("minReduce2", profile_level); + if (comm_rank_of_min == nullptr) { auto send = x; auto recv = new float[n]; - MPI_Allreduce( send, recv, n, MPI_FLOAT, MPI_MIN, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_FLOAT, MPI_MIN, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; } else { auto recv = new FloatIntStruct[n]; auto send = new FloatIntStruct[n]; - for ( int i = 0; i < n; ++i ) { + for (int i = 0; i < n; ++i) { send[i].f = x[i]; send[i].i = comm_rank; } - MPI_Allreduce( send, recv, n, MPI_FLOAT_INT, MPI_MINLOC, communicator ); - for ( int i = 0; i < n; ++i ) { - x[i] = recv[i].f; + MPI_Allreduce(send, recv, n, MPI_FLOAT_INT, MPI_MINLOC, communicator); + for (int i = 0; i < n; ++i) { + x[i] = recv[i].f; comm_rank_of_min[i] = recv[i].i; } delete[] recv; delete[] send; } - PROFILE_STOP( "minReduce2", profile_level ); + PROFILE_STOP("minReduce2", profile_level); } // double -template<> -void MPI_CLASS::call_minReduce( - const double *x, double *y, const int n, int *comm_rank_of_min ) const -{ - PROFILE_START( "minReduce1", profile_level ); - if ( comm_rank_of_min == nullptr ) { - MPI_Allreduce( (void *) x, (void *) y, n, MPI_DOUBLE, MPI_MIN, communicator ); +template <> +void MPI_CLASS::call_minReduce(const double *x, double *y, const int n, + int *comm_rank_of_min) const { + PROFILE_START("minReduce1", profile_level); + if (comm_rank_of_min == nullptr) { + MPI_Allreduce((void *)x, (void *)y, n, MPI_DOUBLE, MPI_MIN, + communicator); } else { auto recv = new DoubleIntStruct[n]; auto send = new DoubleIntStruct[n]; - for ( int i = 0; i < n; ++i ) { + for (int i = 0; i < n; ++i) { send[i].d = x[i]; send[i].i = comm_rank; } - MPI_Allreduce( send, recv, n, MPI_DOUBLE_INT, MPI_MINLOC, communicator ); - for ( int i = 0; i < n; ++i ) { - y[i] = recv[i].d; + MPI_Allreduce(send, recv, n, MPI_DOUBLE_INT, MPI_MINLOC, communicator); + for (int i = 0; i < n; ++i) { + y[i] = recv[i].d; comm_rank_of_min[i] = recv[i].i; } delete[] recv; delete[] send; } - PROFILE_STOP( "minReduce1", profile_level ); + PROFILE_STOP("minReduce1", profile_level); } -template<> -void MPI_CLASS::call_minReduce( double *x, const int n, int *comm_rank_of_min ) const -{ - PROFILE_START( "minReduce2", profile_level ); - if ( comm_rank_of_min == nullptr ) { +template <> +void MPI_CLASS::call_minReduce(double *x, const int n, + int *comm_rank_of_min) const { + PROFILE_START("minReduce2", profile_level); + if (comm_rank_of_min == nullptr) { auto send = x; auto recv = new double[n]; - MPI_Allreduce( send, recv, n, MPI_DOUBLE, MPI_MIN, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_DOUBLE, MPI_MIN, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; } else { auto recv = new DoubleIntStruct[n]; auto send = new DoubleIntStruct[n]; - for ( int i = 0; i < n; ++i ) { + for (int i = 0; i < n; ++i) { send[i].d = x[i]; send[i].i = comm_rank; } - MPI_Allreduce( send, recv, n, MPI_DOUBLE_INT, MPI_MINLOC, communicator ); - for ( int i = 0; i < n; ++i ) { - x[i] = recv[i].d; + MPI_Allreduce(send, recv, n, MPI_DOUBLE_INT, MPI_MINLOC, communicator); + for (int i = 0; i < n; ++i) { + x[i] = recv[i].d; comm_rank_of_min[i] = recv[i].i; } delete[] recv; delete[] send; } - PROFILE_STOP( "minReduce2", profile_level ); + PROFILE_STOP("minReduce2", profile_level); } #endif - /************************************************************************ * call_maxReduce * * Note: these specializations are only called when using MPI. * ************************************************************************/ #ifdef USE_MPI // unsigned char -template<> -void MPI_CLASS::call_maxReduce( - const unsigned char *send, unsigned char *recv, const int n, int *comm_rank_of_max ) const -{ - if ( comm_rank_of_max == nullptr ) { - PROFILE_START( "maxReduce1", profile_level ); - MPI_Allreduce( (void *) send, (void *) recv, n, MPI_UNSIGNED_CHAR, MPI_MAX, communicator ); - PROFILE_STOP( "maxReduce1", profile_level ); +template <> +void MPI_CLASS::call_maxReduce(const unsigned char *send, + unsigned char *recv, const int n, + int *comm_rank_of_max) const { + if (comm_rank_of_max == nullptr) { + PROFILE_START("maxReduce1", profile_level); + MPI_Allreduce((void *)send, (void *)recv, n, MPI_UNSIGNED_CHAR, MPI_MAX, + communicator); + PROFILE_STOP("maxReduce1", profile_level); } else { auto tmp = new int[n]; - for ( int i = 0; i < n; i++ ) + for (int i = 0; i < n; i++) tmp[i] = send[i]; - call_maxReduce( tmp, n, comm_rank_of_max ); - for ( int i = 0; i < n; i++ ) - recv[i] = static_cast( tmp[i] ); + call_maxReduce(tmp, n, comm_rank_of_max); + for (int i = 0; i < n; i++) + recv[i] = static_cast(tmp[i]); delete[] tmp; } } -template<> -void MPI_CLASS::call_maxReduce( - unsigned char *x, const int n, int *comm_rank_of_max ) const -{ - if ( comm_rank_of_max == nullptr ) { - PROFILE_START( "maxReduce2", profile_level ); +template <> +void MPI_CLASS::call_maxReduce(unsigned char *x, const int n, + int *comm_rank_of_max) const { + if (comm_rank_of_max == nullptr) { + PROFILE_START("maxReduce2", profile_level); auto send = x; auto recv = new unsigned char[n]; - MPI_Allreduce( send, recv, n, MPI_UNSIGNED_CHAR, MPI_MAX, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_UNSIGNED_CHAR, MPI_MAX, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; - PROFILE_STOP( "maxReduce2", profile_level ); + PROFILE_STOP("maxReduce2", profile_level); } else { auto tmp = new int[n]; - for ( int i = 0; i < n; i++ ) + for (int i = 0; i < n; i++) tmp[i] = x[i]; - call_maxReduce( tmp, n, comm_rank_of_max ); - for ( int i = 0; i < n; i++ ) - x[i] = static_cast( tmp[i] ); + call_maxReduce(tmp, n, comm_rank_of_max); + for (int i = 0; i < n; i++) + x[i] = static_cast(tmp[i]); delete[] tmp; } } // char -template<> -void MPI_CLASS::call_maxReduce( - const char *send, char *recv, const int n, int *comm_rank_of_max ) const -{ - if ( comm_rank_of_max == nullptr ) { - PROFILE_START( "maxReduce1", profile_level ); - MPI_Allreduce( (void *) send, (void *) recv, n, MPI_SIGNED_CHAR, MPI_MAX, communicator ); - PROFILE_STOP( "maxReduce1", profile_level ); +template <> +void MPI_CLASS::call_maxReduce(const char *send, char *recv, const int n, + int *comm_rank_of_max) const { + if (comm_rank_of_max == nullptr) { + PROFILE_START("maxReduce1", profile_level); + MPI_Allreduce((void *)send, (void *)recv, n, MPI_SIGNED_CHAR, MPI_MAX, + communicator); + PROFILE_STOP("maxReduce1", profile_level); } else { auto tmp = new int[n]; - for ( int i = 0; i < n; i++ ) + for (int i = 0; i < n; i++) tmp[i] = send[i]; - call_maxReduce( tmp, n, comm_rank_of_max ); - for ( int i = 0; i < n; i++ ) - recv[i] = static_cast( tmp[i] ); + call_maxReduce(tmp, n, comm_rank_of_max); + for (int i = 0; i < n; i++) + recv[i] = static_cast(tmp[i]); delete[] tmp; } } -template<> -void MPI_CLASS::call_maxReduce( char *x, const int n, int *comm_rank_of_max ) const -{ - if ( comm_rank_of_max == nullptr ) { - PROFILE_START( "maxReduce2", profile_level ); +template <> +void MPI_CLASS::call_maxReduce(char *x, const int n, + int *comm_rank_of_max) const { + if (comm_rank_of_max == nullptr) { + PROFILE_START("maxReduce2", profile_level); auto send = x; auto recv = new char[n]; - MPI_Allreduce( send, recv, n, MPI_SIGNED_CHAR, MPI_MAX, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_SIGNED_CHAR, MPI_MAX, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; - PROFILE_STOP( "maxReduce2", profile_level ); + PROFILE_STOP("maxReduce2", profile_level); } else { auto tmp = new int[n]; - for ( int i = 0; i < n; i++ ) + for (int i = 0; i < n; i++) tmp[i] = x[i]; - call_maxReduce( tmp, n, comm_rank_of_max ); - for ( int i = 0; i < n; i++ ) - x[i] = static_cast( tmp[i] ); + call_maxReduce(tmp, n, comm_rank_of_max); + for (int i = 0; i < n; i++) + x[i] = static_cast(tmp[i]); delete[] tmp; } } // unsigned int -template<> -void MPI_CLASS::call_maxReduce( - const unsigned int *send, unsigned int *recv, const int n, int *comm_rank_of_max ) const -{ - if ( comm_rank_of_max == nullptr ) { - PROFILE_START( "maxReduce1", profile_level ); - MPI_Allreduce( (void *) send, (void *) recv, n, MPI_UNSIGNED, MPI_MAX, communicator ); - PROFILE_STOP( "maxReduce1", profile_level ); +template <> +void MPI_CLASS::call_maxReduce(const unsigned int *send, + unsigned int *recv, const int n, + int *comm_rank_of_max) const { + if (comm_rank_of_max == nullptr) { + PROFILE_START("maxReduce1", profile_level); + MPI_Allreduce((void *)send, (void *)recv, n, MPI_UNSIGNED, MPI_MAX, + communicator); + PROFILE_STOP("maxReduce1", profile_level); } else { auto tmp = new int[n]; - for ( int i = 0; i < n; i++ ) - tmp[i] = unsigned_to_signed( send[i] ); - call_maxReduce( tmp, n, comm_rank_of_max ); - for ( int i = 0; i < n; i++ ) - recv[i] = signed_to_unsigned( tmp[i] ); + for (int i = 0; i < n; i++) + tmp[i] = unsigned_to_signed(send[i]); + call_maxReduce(tmp, n, comm_rank_of_max); + for (int i = 0; i < n; i++) + recv[i] = signed_to_unsigned(tmp[i]); delete[] tmp; } } -template<> -void MPI_CLASS::call_maxReduce( - unsigned int *x, const int n, int *comm_rank_of_max ) const -{ - if ( comm_rank_of_max == nullptr ) { - PROFILE_START( "maxReduce2", profile_level ); +template <> +void MPI_CLASS::call_maxReduce(unsigned int *x, const int n, + int *comm_rank_of_max) const { + if (comm_rank_of_max == nullptr) { + PROFILE_START("maxReduce2", profile_level); auto send = x; auto recv = new unsigned int[n]; - MPI_Allreduce( send, recv, n, MPI_UNSIGNED, MPI_MAX, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_UNSIGNED, MPI_MAX, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; - PROFILE_STOP( "maxReduce2", profile_level ); + PROFILE_STOP("maxReduce2", profile_level); } else { auto tmp = new int[n]; - for ( int i = 0; i < n; i++ ) - tmp[i] = unsigned_to_signed( x[i] ); - call_maxReduce( tmp, n, comm_rank_of_max ); - for ( int i = 0; i < n; i++ ) - x[i] = signed_to_unsigned( tmp[i] ); + for (int i = 0; i < n; i++) + tmp[i] = unsigned_to_signed(x[i]); + call_maxReduce(tmp, n, comm_rank_of_max); + for (int i = 0; i < n; i++) + x[i] = signed_to_unsigned(tmp[i]); delete[] tmp; } } // int -template<> -void MPI_CLASS::call_maxReduce( - const int *x, int *y, const int n, int *comm_rank_of_max ) const -{ - PROFILE_START( "maxReduce1", profile_level ); - if ( comm_rank_of_max == nullptr ) { - MPI_Allreduce( (void *) x, (void *) y, n, MPI_INT, MPI_MAX, communicator ); +template <> +void MPI_CLASS::call_maxReduce(const int *x, int *y, const int n, + int *comm_rank_of_max) const { + PROFILE_START("maxReduce1", profile_level); + if (comm_rank_of_max == nullptr) { + MPI_Allreduce((void *)x, (void *)y, n, MPI_INT, MPI_MAX, communicator); } else { auto recv = new IntIntStruct[n]; auto send = new IntIntStruct[n]; - for ( int i = 0; i < n; ++i ) { + for (int i = 0; i < n; ++i) { send[i].j = x[i]; send[i].i = comm_rank; } - MPI_Allreduce( send, recv, n, MPI_2INT, MPI_MAXLOC, communicator ); - for ( int i = 0; i < n; ++i ) { - y[i] = recv[i].j; + MPI_Allreduce(send, recv, n, MPI_2INT, MPI_MAXLOC, communicator); + for (int i = 0; i < n; ++i) { + y[i] = recv[i].j; comm_rank_of_max[i] = recv[i].i; } delete[] recv; delete[] send; } - PROFILE_STOP( "maxReduce1", profile_level ); + PROFILE_STOP("maxReduce1", profile_level); } -template<> -void MPI_CLASS::call_maxReduce( int *x, const int n, int *comm_rank_of_max ) const -{ - PROFILE_START( "maxReduce2", profile_level ); - if ( comm_rank_of_max == nullptr ) { +template <> +void MPI_CLASS::call_maxReduce(int *x, const int n, + int *comm_rank_of_max) const { + PROFILE_START("maxReduce2", profile_level); + if (comm_rank_of_max == nullptr) { int *send = x; auto recv = new int[n]; - MPI_Allreduce( send, recv, n, MPI_INT, MPI_MAX, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_INT, MPI_MAX, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; } else { auto recv = new IntIntStruct[n]; auto send = new IntIntStruct[n]; - for ( int i = 0; i < n; ++i ) { + for (int i = 0; i < n; ++i) { send[i].j = x[i]; send[i].i = comm_rank; } - MPI_Allreduce( send, recv, n, MPI_2INT, MPI_MAXLOC, communicator ); - for ( int i = 0; i < n; ++i ) { - x[i] = recv[i].j; + MPI_Allreduce(send, recv, n, MPI_2INT, MPI_MAXLOC, communicator); + for (int i = 0; i < n; ++i) { + x[i] = recv[i].j; comm_rank_of_max[i] = recv[i].i; } delete[] recv; delete[] send; } - PROFILE_STOP( "maxReduce2", profile_level ); + PROFILE_STOP("maxReduce2", profile_level); } // long int -template<> -void MPI_CLASS::call_maxReduce( - const long int *x, long int *y, const int n, int *comm_rank_of_max ) const -{ - PROFILE_START( "maxReduce1", profile_level ); - if ( comm_rank_of_max == nullptr ) { - MPI_Allreduce( (void *) x, (void *) y, n, MPI_LONG, MPI_MAX, communicator ); +template <> +void MPI_CLASS::call_maxReduce(const long int *x, long int *y, + const int n, + int *comm_rank_of_max) const { + PROFILE_START("maxReduce1", profile_level); + if (comm_rank_of_max == nullptr) { + MPI_Allreduce((void *)x, (void *)y, n, MPI_LONG, MPI_MAX, communicator); } else { auto recv = new LongIntStruct[n]; auto send = new LongIntStruct[n]; - for ( int i = 0; i < n; ++i ) { + for (int i = 0; i < n; ++i) { send[i].j = x[i]; send[i].i = comm_rank; } - MPI_Allreduce( send, recv, n, MPI_LONG_INT, MPI_MAXLOC, communicator ); - for ( int i = 0; i < n; ++i ) { - y[i] = recv[i].j; + MPI_Allreduce(send, recv, n, MPI_LONG_INT, MPI_MAXLOC, communicator); + for (int i = 0; i < n; ++i) { + y[i] = recv[i].j; comm_rank_of_max[i] = recv[i].i; } delete[] recv; delete[] send; } - PROFILE_STOP( "maxReduce1", profile_level ); + PROFILE_STOP("maxReduce1", profile_level); } -template<> -void MPI_CLASS::call_maxReduce( long int *x, const int n, int *comm_rank_of_max ) const -{ - PROFILE_START( "maxReduce2", profile_level ); - if ( comm_rank_of_max == nullptr ) { +template <> +void MPI_CLASS::call_maxReduce(long int *x, const int n, + int *comm_rank_of_max) const { + PROFILE_START("maxReduce2", profile_level); + if (comm_rank_of_max == nullptr) { auto send = x; auto recv = new long int[n]; - MPI_Allreduce( send, recv, n, MPI_LONG, MPI_MAX, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_LONG, MPI_MAX, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; } else { auto recv = new LongIntStruct[n]; auto send = new LongIntStruct[n]; - for ( int i = 0; i < n; ++i ) { + for (int i = 0; i < n; ++i) { send[i].j = x[i]; send[i].i = comm_rank; } - MPI_Allreduce( send, recv, n, MPI_LONG_INT, MPI_MAXLOC, communicator ); - for ( int i = 0; i < n; ++i ) { - x[i] = recv[i].j; + MPI_Allreduce(send, recv, n, MPI_LONG_INT, MPI_MAXLOC, communicator); + for (int i = 0; i < n; ++i) { + x[i] = recv[i].j; comm_rank_of_max[i] = recv[i].i; } delete[] recv; delete[] send; } - PROFILE_STOP( "maxReduce2", profile_level ); + PROFILE_STOP("maxReduce2", profile_level); } // unsigned long int -template<> -void MPI_CLASS::call_maxReduce( const unsigned long int *send, - unsigned long int *recv, const int n, int *comm_rank_of_max ) const -{ - if ( comm_rank_of_max == nullptr ) { - PROFILE_START( "maxReduce1", profile_level ); - MPI_Allreduce( (void *) send, (void *) recv, n, MPI_UNSIGNED_LONG, MPI_MAX, communicator ); - PROFILE_STOP( "maxReduce1", profile_level ); +template <> +void MPI_CLASS::call_maxReduce(const unsigned long int *send, + unsigned long int *recv, + const int n, + int *comm_rank_of_max) const { + if (comm_rank_of_max == nullptr) { + PROFILE_START("maxReduce1", profile_level); + MPI_Allreduce((void *)send, (void *)recv, n, MPI_UNSIGNED_LONG, MPI_MAX, + communicator); + PROFILE_STOP("maxReduce1", profile_level); } else { auto tmp = new long int[n]; - for ( int i = 0; i < n; i++ ) - tmp[i] = unsigned_to_signed( send[i] ); - call_maxReduce( tmp, n, comm_rank_of_max ); - for ( int i = 0; i < n; i++ ) - recv[i] = signed_to_unsigned( tmp[i] ); + for (int i = 0; i < n; i++) + tmp[i] = unsigned_to_signed(send[i]); + call_maxReduce(tmp, n, comm_rank_of_max); + for (int i = 0; i < n; i++) + recv[i] = signed_to_unsigned(tmp[i]); delete[] tmp; } } -template<> -void MPI_CLASS::call_maxReduce( - unsigned long int *x, const int n, int *comm_rank_of_max ) const -{ - if ( comm_rank_of_max == nullptr ) { - PROFILE_START( "maxReduce2", profile_level ); +template <> +void MPI_CLASS::call_maxReduce(unsigned long int *x, + const int n, + int *comm_rank_of_max) const { + if (comm_rank_of_max == nullptr) { + PROFILE_START("maxReduce2", profile_level); auto send = x; auto recv = new unsigned long int[n]; - MPI_Allreduce( send, recv, n, MPI_UNSIGNED_LONG, MPI_MAX, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_UNSIGNED_LONG, MPI_MAX, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; - PROFILE_STOP( "maxReduce2", profile_level ); + PROFILE_STOP("maxReduce2", profile_level); } else { auto tmp = new long int[n]; - for ( int i = 0; i < n; i++ ) - tmp[i] = unsigned_to_signed( x[i] ); - call_maxReduce( tmp, n, comm_rank_of_max ); - for ( int i = 0; i < n; i++ ) - x[i] = signed_to_unsigned( tmp[i] ); + for (int i = 0; i < n; i++) + tmp[i] = unsigned_to_signed(x[i]); + call_maxReduce(tmp, n, comm_rank_of_max); + for (int i = 0; i < n; i++) + x[i] = signed_to_unsigned(tmp[i]); delete[] tmp; } } // unsigned long long int -template<> -void MPI_CLASS::call_maxReduce( const unsigned long long int *send, - unsigned long long int *recv, const int n, int *comm_rank_of_max ) const -{ - PROFILE_START( "maxReduce1", profile_level ); - if ( comm_rank_of_max == nullptr ) { +template <> +void MPI_CLASS::call_maxReduce( + const unsigned long long int *send, unsigned long long int *recv, + const int n, int *comm_rank_of_max) const { + PROFILE_START("maxReduce1", profile_level); + if (comm_rank_of_max == nullptr) { auto x = new long long int[n]; auto y = new long long int[n]; - for ( int i = 0; i < n; i++ ) - x[i] = unsigned_to_signed( send[i] ); - MPI_Allreduce( (void *) x, (void *) y, n, MPI_LONG_LONG_INT, MPI_MAX, communicator ); - for ( int i = 0; i < n; i++ ) - recv[i] = signed_to_unsigned( y[i] ); + for (int i = 0; i < n; i++) + x[i] = unsigned_to_signed(send[i]); + MPI_Allreduce((void *)x, (void *)y, n, MPI_LONG_LONG_INT, MPI_MAX, + communicator); + for (int i = 0; i < n; i++) + recv[i] = signed_to_unsigned(y[i]); delete[] x; delete[] y; } else { - printf( "maxReduce will use double\n" ); + printf("maxReduce will use double\n"); auto tmp = new double[n]; - for ( int i = 0; i < n; i++ ) - tmp[i] = static_cast( send[i] ); - call_maxReduce( tmp, n, comm_rank_of_max ); - for ( int i = 0; i < n; i++ ) - recv[i] = static_cast( tmp[i] ); + for (int i = 0; i < n; i++) + tmp[i] = static_cast(send[i]); + call_maxReduce(tmp, n, comm_rank_of_max); + for (int i = 0; i < n; i++) + recv[i] = static_cast(tmp[i]); delete[] tmp; } - PROFILE_STOP( "maxReduce1", profile_level ); + PROFILE_STOP("maxReduce1", profile_level); } -template<> +template <> void MPI_CLASS::call_maxReduce( - unsigned long long int *x, const int n, int *comm_rank_of_max ) const -{ + unsigned long long int *x, const int n, int *comm_rank_of_max) const { auto recv = new unsigned long long int[n]; - call_maxReduce( x, recv, n, comm_rank_of_max ); - for ( int i = 0; i < n; i++ ) + call_maxReduce(x, recv, n, comm_rank_of_max); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; } // long long int -template<> -void MPI_CLASS::call_maxReduce( - const long long int *x, long long int *y, const int n, int *comm_rank_of_max ) const -{ - PROFILE_START( "maxReduce1", profile_level ); - if ( comm_rank_of_max == nullptr ) { - MPI_Allreduce( (void *) x, (void *) y, n, MPI_LONG_LONG_INT, MPI_MAX, communicator ); +template <> +void MPI_CLASS::call_maxReduce(const long long int *x, + long long int *y, const int n, + int *comm_rank_of_max) const { + PROFILE_START("maxReduce1", profile_level); + if (comm_rank_of_max == nullptr) { + MPI_Allreduce((void *)x, (void *)y, n, MPI_LONG_LONG_INT, MPI_MAX, + communicator); } else { - printf( "maxReduce will use double\n" ); + printf("maxReduce will use double\n"); auto tmp = new double[n]; - for ( int i = 0; i < n; i++ ) - tmp[i] = static_cast( x[i] ); - call_maxReduce( tmp, n, comm_rank_of_max ); - for ( int i = 0; i < n; i++ ) - y[i] = static_cast( tmp[i] ); + for (int i = 0; i < n; i++) + tmp[i] = static_cast(x[i]); + call_maxReduce(tmp, n, comm_rank_of_max); + for (int i = 0; i < n; i++) + y[i] = static_cast(tmp[i]); delete[] tmp; } - PROFILE_STOP( "maxReduce1", profile_level ); + PROFILE_STOP("maxReduce1", profile_level); } -template<> -void MPI_CLASS::call_maxReduce( - long long int *x, const int n, int *comm_rank_of_max ) const -{ +template <> +void MPI_CLASS::call_maxReduce(long long int *x, const int n, + int *comm_rank_of_max) const { auto recv = new long long int[n]; - call_maxReduce( x, recv, n, comm_rank_of_max ); - for ( int i = 0; i < n; i++ ) - x[i] = signed_to_unsigned( recv[i] ); + call_maxReduce(x, recv, n, comm_rank_of_max); + for (int i = 0; i < n; i++) + x[i] = signed_to_unsigned(recv[i]); delete[] recv; } // float -template<> -void MPI_CLASS::call_maxReduce( - const float *x, float *y, const int n, int *comm_rank_of_max ) const -{ - PROFILE_START( "maxReduce1", profile_level ); - if ( comm_rank_of_max == nullptr ) { - MPI_Allreduce( (void *) x, (void *) y, n, MPI_FLOAT, MPI_MAX, communicator ); +template <> +void MPI_CLASS::call_maxReduce(const float *x, float *y, const int n, + int *comm_rank_of_max) const { + PROFILE_START("maxReduce1", profile_level); + if (comm_rank_of_max == nullptr) { + MPI_Allreduce((void *)x, (void *)y, n, MPI_FLOAT, MPI_MAX, + communicator); } else { auto recv = new FloatIntStruct[n]; auto send = new FloatIntStruct[n]; - for ( int i = 0; i < n; ++i ) { + for (int i = 0; i < n; ++i) { send[i].f = x[i]; send[i].i = comm_rank; } - MPI_Allreduce( send, recv, n, MPI_FLOAT_INT, MPI_MAXLOC, communicator ); - for ( int i = 0; i < n; ++i ) { - y[i] = recv[i].f; + MPI_Allreduce(send, recv, n, MPI_FLOAT_INT, MPI_MAXLOC, communicator); + for (int i = 0; i < n; ++i) { + y[i] = recv[i].f; comm_rank_of_max[i] = recv[i].i; } delete[] recv; delete[] send; } - PROFILE_STOP( "maxReduce1", profile_level ); + PROFILE_STOP("maxReduce1", profile_level); } -template<> -void MPI_CLASS::call_maxReduce( float *x, const int n, int *comm_rank_of_max ) const -{ - PROFILE_START( "maxReduce2", profile_level ); - if ( comm_rank_of_max == nullptr ) { +template <> +void MPI_CLASS::call_maxReduce(float *x, const int n, + int *comm_rank_of_max) const { + PROFILE_START("maxReduce2", profile_level); + if (comm_rank_of_max == nullptr) { auto send = x; auto recv = new float[n]; - MPI_Allreduce( send, recv, n, MPI_FLOAT, MPI_MAX, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_FLOAT, MPI_MAX, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; } else { auto recv = new FloatIntStruct[n]; auto send = new FloatIntStruct[n]; - for ( int i = 0; i < n; ++i ) { + for (int i = 0; i < n; ++i) { send[i].f = x[i]; send[i].i = comm_rank; } - MPI_Allreduce( send, recv, n, MPI_FLOAT_INT, MPI_MAXLOC, communicator ); - for ( int i = 0; i < n; ++i ) { - x[i] = recv[i].f; + MPI_Allreduce(send, recv, n, MPI_FLOAT_INT, MPI_MAXLOC, communicator); + for (int i = 0; i < n; ++i) { + x[i] = recv[i].f; comm_rank_of_max[i] = recv[i].i; } delete[] recv; delete[] send; } - PROFILE_STOP( "maxReduce2", profile_level ); + PROFILE_STOP("maxReduce2", profile_level); } // double -template<> -void MPI_CLASS::call_maxReduce( - const double *x, double *y, const int n, int *comm_rank_of_max ) const -{ - PROFILE_START( "maxReduce1", profile_level ); - if ( comm_rank_of_max == nullptr ) { - MPI_Allreduce( (void *) x, (void *) y, n, MPI_DOUBLE, MPI_MAX, communicator ); +template <> +void MPI_CLASS::call_maxReduce(const double *x, double *y, const int n, + int *comm_rank_of_max) const { + PROFILE_START("maxReduce1", profile_level); + if (comm_rank_of_max == nullptr) { + MPI_Allreduce((void *)x, (void *)y, n, MPI_DOUBLE, MPI_MAX, + communicator); } else { auto recv = new DoubleIntStruct[n]; auto send = new DoubleIntStruct[n]; - for ( int i = 0; i < n; ++i ) { + for (int i = 0; i < n; ++i) { send[i].d = x[i]; send[i].i = comm_rank; } - MPI_Allreduce( send, recv, n, MPI_DOUBLE_INT, MPI_MAXLOC, communicator ); - for ( int i = 0; i < n; ++i ) { - y[i] = recv[i].d; + MPI_Allreduce(send, recv, n, MPI_DOUBLE_INT, MPI_MAXLOC, communicator); + for (int i = 0; i < n; ++i) { + y[i] = recv[i].d; comm_rank_of_max[i] = recv[i].i; } delete[] recv; delete[] send; } - PROFILE_STOP( "maxReduce1", profile_level ); + PROFILE_STOP("maxReduce1", profile_level); } -template<> -void MPI_CLASS::call_maxReduce( double *x, const int n, int *comm_rank_of_max ) const -{ - PROFILE_START( "maxReduce2", profile_level ); - if ( comm_rank_of_max == nullptr ) { +template <> +void MPI_CLASS::call_maxReduce(double *x, const int n, + int *comm_rank_of_max) const { + PROFILE_START("maxReduce2", profile_level); + if (comm_rank_of_max == nullptr) { auto send = x; auto recv = new double[n]; - MPI_Allreduce( send, recv, n, MPI_DOUBLE, MPI_MAX, communicator ); - for ( int i = 0; i < n; i++ ) + MPI_Allreduce(send, recv, n, MPI_DOUBLE, MPI_MAX, communicator); + for (int i = 0; i < n; i++) x[i] = recv[i]; delete[] recv; } else { auto recv = new DoubleIntStruct[n]; auto send = new DoubleIntStruct[n]; - for ( int i = 0; i < n; ++i ) { + for (int i = 0; i < n; ++i) { send[i].d = x[i]; send[i].i = comm_rank; } - MPI_Allreduce( send, recv, n, MPI_DOUBLE_INT, MPI_MAXLOC, communicator ); - for ( int i = 0; i < n; ++i ) { - x[i] = recv[i].d; + MPI_Allreduce(send, recv, n, MPI_DOUBLE_INT, MPI_MAXLOC, communicator); + for (int i = 0; i < n; ++i) { + x[i] = recv[i].d; comm_rank_of_max[i] = recv[i].i; } delete[] recv; delete[] send; } - PROFILE_STOP( "maxReduce2", profile_level ); + PROFILE_STOP("maxReduce2", profile_level); } #endif - /************************************************************************ * bcast * * Note: these specializations are only called when using MPI. * ************************************************************************/ #ifdef USE_MPI // char -template<> -void MPI_CLASS::call_bcast( unsigned char *x, const int n, const int root ) const -{ - PROFILE_START( "bcast", profile_level ); - MPI_Bcast( x, n, MPI_UNSIGNED_CHAR, root, communicator ); - PROFILE_STOP( "bcast", profile_level ); +template <> +void MPI_CLASS::call_bcast(unsigned char *x, const int n, + const int root) const { + PROFILE_START("bcast", profile_level); + MPI_Bcast(x, n, MPI_UNSIGNED_CHAR, root, communicator); + PROFILE_STOP("bcast", profile_level); } -template<> -void MPI_CLASS::call_bcast( char *x, const int n, const int root ) const -{ - PROFILE_START( "bcast", profile_level ); - MPI_Bcast( x, n, MPI_CHAR, root, communicator ); - PROFILE_STOP( "bcast", profile_level ); +template <> +void MPI_CLASS::call_bcast(char *x, const int n, const int root) const { + PROFILE_START("bcast", profile_level); + MPI_Bcast(x, n, MPI_CHAR, root, communicator); + PROFILE_STOP("bcast", profile_level); } // int -template<> -void MPI_CLASS::call_bcast( unsigned int *x, const int n, const int root ) const -{ - PROFILE_START( "bcast", profile_level ); - MPI_Bcast( x, n, MPI_UNSIGNED, root, communicator ); - PROFILE_STOP( "bcast", profile_level ); +template <> +void MPI_CLASS::call_bcast(unsigned int *x, const int n, + const int root) const { + PROFILE_START("bcast", profile_level); + MPI_Bcast(x, n, MPI_UNSIGNED, root, communicator); + PROFILE_STOP("bcast", profile_level); } -template<> -void MPI_CLASS::call_bcast( int *x, const int n, const int root ) const -{ - PROFILE_START( "bcast", profile_level ); - MPI_Bcast( x, n, MPI_INT, root, communicator ); - PROFILE_STOP( "bcast", profile_level ); +template <> +void MPI_CLASS::call_bcast(int *x, const int n, const int root) const { + PROFILE_START("bcast", profile_level); + MPI_Bcast(x, n, MPI_INT, root, communicator); + PROFILE_STOP("bcast", profile_level); } // float -template<> -void MPI_CLASS::call_bcast( float *x, const int n, const int root ) const -{ - PROFILE_START( "bcast", profile_level ); - MPI_Bcast( x, n, MPI_FLOAT, root, communicator ); - PROFILE_STOP( "bcast", profile_level ); +template <> +void MPI_CLASS::call_bcast(float *x, const int n, const int root) const { + PROFILE_START("bcast", profile_level); + MPI_Bcast(x, n, MPI_FLOAT, root, communicator); + PROFILE_STOP("bcast", profile_level); } // double -template<> -void MPI_CLASS::call_bcast( double *x, const int n, const int root ) const -{ - PROFILE_START( "bcast", profile_level ); - MPI_Bcast( x, n, MPI_DOUBLE, root, communicator ); - PROFILE_STOP( "bcast", profile_level ); +template <> +void MPI_CLASS::call_bcast(double *x, const int n, + const int root) const { + PROFILE_START("bcast", profile_level); + MPI_Bcast(x, n, MPI_DOUBLE, root, communicator); + PROFILE_STOP("bcast", profile_level); } #else // We need a concrete instantiation of bcast(x,n,root); -template<> -void MPI_CLASS::call_bcast( char *, const int, const int ) const -{ -} +template <> +void MPI_CLASS::call_bcast(char *, const int, const int) const {} #endif - /************************************************************************ * Perform a global barrier across all processors. * ************************************************************************/ -void MPI_CLASS::barrier() const -{ +void MPI_CLASS::barrier() const { #ifdef USE_MPI - MPI_Barrier( communicator ); + MPI_Barrier(communicator); #endif } - /************************************************************************ * Send data array to another processor. * * Note: these specializations are only called when using MPI. * ************************************************************************/ #ifdef USE_MPI // char -template<> -void MPI_CLASS::send( - const char *buf, const int length, const int recv_proc_number, int tag ) const -{ +template <> +void MPI_CLASS::send(const char *buf, const int length, + const int recv_proc_number, int tag) const { // Set the tag to 0 if it is < 0 - tag = ( tag >= 0 ) ? tag : 0; - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); + tag = (tag >= 0) ? tag : 0; + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); // Send the data - PROFILE_START( "send", profile_level ); - MPI_Send( (void *) buf, length, MPI_CHAR, recv_proc_number, tag, communicator ); - PROFILE_STOP( "send", profile_level ); + PROFILE_START("send", profile_level); + MPI_Send((void *)buf, length, MPI_CHAR, recv_proc_number, tag, + communicator); + PROFILE_STOP("send", profile_level); } // int -template<> -void MPI_CLASS::send( - const int *buf, const int length, const int recv_proc_number, int tag ) const -{ +template <> +void MPI_CLASS::send(const int *buf, const int length, + const int recv_proc_number, int tag) const { // Set the tag to 0 if it is < 0 - tag = ( tag >= 0 ) ? tag : 0; - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); + tag = (tag >= 0) ? tag : 0; + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); // Send the data - PROFILE_START( "send", profile_level ); - MPI_Send( (void *) buf, length, MPI_INT, recv_proc_number, tag, communicator ); - PROFILE_STOP( "send", profile_level ); + PROFILE_START("send", profile_level); + MPI_Send((void *)buf, length, MPI_INT, recv_proc_number, tag, communicator); + PROFILE_STOP("send", profile_level); } // float -template<> -void MPI_CLASS::send( - const float *buf, const int length, const int recv_proc_number, int tag ) const -{ +template <> +void MPI_CLASS::send(const float *buf, const int length, + const int recv_proc_number, int tag) const { // Set the tag to 0 if it is < 0 - tag = ( tag >= 0 ) ? tag : 0; - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); + tag = (tag >= 0) ? tag : 0; + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); // Send the data - PROFILE_START( "send", profile_level ); - MPI_Send( (void *) buf, length, MPI_FLOAT, recv_proc_number, tag, communicator ); - PROFILE_STOP( "send", profile_level ); + PROFILE_START("send", profile_level); + MPI_Send((void *)buf, length, MPI_FLOAT, recv_proc_number, tag, + communicator); + PROFILE_STOP("send", profile_level); } // double -template<> -void MPI_CLASS::send( - const double *buf, const int length, const int recv_proc_number, int tag ) const -{ +template <> +void MPI_CLASS::send(const double *buf, const int length, + const int recv_proc_number, int tag) const { // Set the tag to 0 if it is < 0 - tag = ( tag >= 0 ) ? tag : 0; - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); + tag = (tag >= 0) ? tag : 0; + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); // Send the data - PROFILE_START( "send", profile_level ); - MPI_Send( (void *) buf, length, MPI_DOUBLE, recv_proc_number, tag, communicator ); - PROFILE_STOP( "send", profile_level ); + PROFILE_START("send", profile_level); + MPI_Send((void *)buf, length, MPI_DOUBLE, recv_proc_number, tag, + communicator); + PROFILE_STOP("send", profile_level); } #else // We need a concrete instantiation of send for use without MPI -template<> -void MPI_CLASS::send( const char *buf, const int length, const int, int tag ) const -{ - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - MPI_INSIST( tag >= 0, "tag must be >= 0" ); - PROFILE_START( "send", profile_level ); - auto id = getRequest( communicator, tag ); - auto it = global_isendrecv_list.find( id ); - MPI_INSIST( it == global_isendrecv_list.end(), - "send must be paired with a previous call to irecv in serial" ); - MPI_ASSERT( it->second.status == 2 ); - memcpy( (char *) it->second.data, buf, length ); - global_isendrecv_list.erase( it ); - PROFILE_START( "send", profile_level ); +template <> +void MPI_CLASS::send(const char *buf, const int length, const int, + int tag) const { + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + MPI_INSIST(tag >= 0, "tag must be >= 0"); + PROFILE_START("send", profile_level); + auto id = getRequest(communicator, tag); + auto it = global_isendrecv_list.find(id); + MPI_INSIST(it == global_isendrecv_list.end(), + "send must be paired with a previous call to irecv in serial"); + MPI_ASSERT(it->second.status == 2); + memcpy((char *)it->second.data, buf, length); + global_isendrecv_list.erase(it); + PROFILE_START("send", profile_level); } #endif - /************************************************************************ * Non-blocking send data array to another processor. * * Note: these specializations are only called when using MPI. * ************************************************************************/ #ifdef USE_MPI // char -template<> -MPI_Request MPI_CLASS::Isend( - const char *buf, const int length, const int recv_proc, const int tag ) const -{ - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - MPI_INSIST( tag >= 0, "tag must be >= 0" ); +template <> +MPI_Request MPI_CLASS::Isend(const char *buf, const int length, + const int recv_proc, const int tag) const { + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + MPI_INSIST(tag >= 0, "tag must be >= 0"); MPI_Request request; - PROFILE_START( "Isend", profile_level ); - MPI_Isend( (void *) buf, length, MPI_CHAR, recv_proc, tag, communicator, &request ); - PROFILE_STOP( "Isend", profile_level ); + PROFILE_START("Isend", profile_level); + MPI_Isend((void *)buf, length, MPI_CHAR, recv_proc, tag, communicator, + &request); + PROFILE_STOP("Isend", profile_level); return request; } // int -template<> -MPI_Request MPI_CLASS::Isend( - const int *buf, const int length, const int recv_proc, const int tag ) const -{ - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - MPI_INSIST( tag >= 0, "tag must be >= 0" ); +template <> +MPI_Request MPI_CLASS::Isend(const int *buf, const int length, + const int recv_proc, const int tag) const { + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + MPI_INSIST(tag >= 0, "tag must be >= 0"); MPI_Request request; - PROFILE_START( "Isend", profile_level ); - MPI_Isend( (void *) buf, length, MPI_INT, recv_proc, tag, communicator, &request ); - PROFILE_STOP( "Isend", profile_level ); + PROFILE_START("Isend", profile_level); + MPI_Isend((void *)buf, length, MPI_INT, recv_proc, tag, communicator, + &request); + PROFILE_STOP("Isend", profile_level); return request; } // float -template<> -MPI_Request MPI_CLASS::Isend( - const float *buf, const int length, const int recv_proc, const int tag ) const -{ - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - MPI_INSIST( tag >= 0, "tag must be >= 0" ); +template <> +MPI_Request MPI_CLASS::Isend(const float *buf, const int length, + const int recv_proc, const int tag) const { + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + MPI_INSIST(tag >= 0, "tag must be >= 0"); MPI_Request request; - PROFILE_START( "Isend", profile_level ); - MPI_Isend( (void *) buf, length, MPI_FLOAT, recv_proc, tag, communicator, &request ); - PROFILE_STOP( "Isend", profile_level ); + PROFILE_START("Isend", profile_level); + MPI_Isend((void *)buf, length, MPI_FLOAT, recv_proc, tag, communicator, + &request); + PROFILE_STOP("Isend", profile_level); return request; } // double -template<> -MPI_Request MPI_CLASS::Isend( - const double *buf, const int length, const int recv_proc, const int tag ) const -{ - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - MPI_INSIST( tag >= 0, "tag must be >= 0" ); +template <> +MPI_Request MPI_CLASS::Isend(const double *buf, const int length, + const int recv_proc, const int tag) const { + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + MPI_INSIST(tag >= 0, "tag must be >= 0"); MPI_Request request; - PROFILE_START( "Isend", profile_level ); - MPI_Isend( (void *) buf, length, MPI_DOUBLE, recv_proc, tag, communicator, &request ); - PROFILE_STOP( "Isend", profile_level ); + PROFILE_START("Isend", profile_level); + MPI_Isend((void *)buf, length, MPI_DOUBLE, recv_proc, tag, communicator, + &request); + PROFILE_STOP("Isend", profile_level); return request; } #else // We need a concrete instantiation of send for use without mpi -template<> -MPI_Request MPI_CLASS::Isend( - const char *buf, const int length, const int, const int tag ) const -{ - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - MPI_INSIST( tag >= 0, "tag must be >= 0" ); - PROFILE_START( "Isend", profile_level ); - auto id = getRequest( communicator, tag ); - auto it = global_isendrecv_list.find( id ); - if ( it == global_isendrecv_list.end() ) { +template <> +MPI_Request MPI_CLASS::Isend(const char *buf, const int length, const int, + const int tag) const { + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + MPI_INSIST(tag >= 0, "tag must be >= 0"); + PROFILE_START("Isend", profile_level); + auto id = getRequest(communicator, tag); + auto it = global_isendrecv_list.find(id); + if (it == global_isendrecv_list.end()) { // We are calling isend first Isendrecv_struct data; - data.data = buf; + data.data = buf; data.status = 1; - global_isendrecv_list.insert( std::pair( id, data ) ); + global_isendrecv_list.insert( + std::pair(id, data)); } else { // We called irecv first - MPI_ASSERT( it->second.status == 2 ); - memcpy( (char *) it->second.data, buf, length ); - global_isendrecv_list.erase( it ); + MPI_ASSERT(it->second.status == 2); + memcpy((char *)it->second.data, buf, length); + global_isendrecv_list.erase(it); } - PROFILE_STOP( "Isend", profile_level ); + PROFILE_STOP("Isend", profile_level); return id; } #endif - /************************************************************************ * Send byte array to another processor. * ************************************************************************/ -void MPI_CLASS::sendBytes( - const void *buf, const int number_bytes, const int recv_proc_number, int tag ) const -{ - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - MPI_INSIST( tag >= 0, "tag must be >= 0" ); - send( (const char *) buf, number_bytes, recv_proc_number, tag ); +void MPI_CLASS::sendBytes(const void *buf, const int number_bytes, + const int recv_proc_number, int tag) const { + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + MPI_INSIST(tag >= 0, "tag must be >= 0"); + send((const char *)buf, number_bytes, recv_proc_number, tag); } - /************************************************************************ * Non-blocking send byte array to another processor. * ************************************************************************/ -MPI_Request MPI_CLASS::IsendBytes( - const void *buf, const int number_bytes, const int recv_proc, const int tag ) const -{ - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - MPI_INSIST( tag >= 0, "tag must be >= 0" ); - return Isend( (const char *) buf, number_bytes, recv_proc, tag ); +MPI_Request MPI_CLASS::IsendBytes(const void *buf, const int number_bytes, + const int recv_proc, const int tag) const { + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + MPI_INSIST(tag >= 0, "tag must be >= 0"); + return Isend((const char *)buf, number_bytes, recv_proc, tag); } - /************************************************************************ * Recieve data array to another processor. * * Note: these specializations are only called when using MPI. * ************************************************************************/ #ifdef USE_MPI // char -template<> -void MPI_CLASS::recv( - char *buf, int &length, const int send_proc_number, const bool get_length, int tag ) const -{ +template <> +void MPI_CLASS::recv(char *buf, int &length, const int send_proc_number, + const bool get_length, int tag) const { // Set the tag to 0 if it is < 0 - tag = ( tag >= 0 ) ? tag : 0; - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - PROFILE_START( "recv", profile_level ); + tag = (tag >= 0) ? tag : 0; + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + PROFILE_START("recv", profile_level); // Get the recieve length if necessary - if ( get_length ) { - int bytes = this->probe( send_proc_number, tag ); - int recv_length = bytes / sizeof( char ); - MPI_INSIST( length >= recv_length, "Recived length is larger than allocated array" ); + if (get_length) { + int bytes = this->probe(send_proc_number, tag); + int recv_length = bytes / sizeof(char); + MPI_INSIST(length >= recv_length, + "Recived length is larger than allocated array"); length = recv_length; } // Send the data MPI_Status status; - MPI_Recv( (void *) buf, length, MPI_CHAR, send_proc_number, tag, communicator, &status ); - PROFILE_STOP( "recv", profile_level ); + MPI_Recv((void *)buf, length, MPI_CHAR, send_proc_number, tag, communicator, + &status); + PROFILE_STOP("recv", profile_level); } // int -template<> -void MPI_CLASS::recv( - int *buf, int &length, const int send_proc_number, const bool get_length, int tag ) const -{ +template <> +void MPI_CLASS::recv(int *buf, int &length, const int send_proc_number, + const bool get_length, int tag) const { // Set the tag to 0 if it is < 0 - tag = ( tag >= 0 ) ? tag : 0; - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - PROFILE_START( "recv", profile_level ); + tag = (tag >= 0) ? tag : 0; + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + PROFILE_START("recv", profile_level); // Get the recieve length if necessary - if ( get_length ) { - int bytes = this->probe( send_proc_number, tag ); - int recv_length = bytes / sizeof( int ); - MPI_INSIST( length >= recv_length, "Recived length is larger than allocated array" ); + if (get_length) { + int bytes = this->probe(send_proc_number, tag); + int recv_length = bytes / sizeof(int); + MPI_INSIST(length >= recv_length, + "Recived length is larger than allocated array"); length = recv_length; } // Send the data MPI_Status status; - MPI_Recv( (void *) buf, length, MPI_INT, send_proc_number, tag, communicator, &status ); - PROFILE_STOP( "recv", profile_level ); + MPI_Recv((void *)buf, length, MPI_INT, send_proc_number, tag, communicator, + &status); + PROFILE_STOP("recv", profile_level); } // float -template<> -void MPI_CLASS::recv( - float *buf, int &length, const int send_proc_number, const bool get_length, int tag ) const -{ +template <> +void MPI_CLASS::recv(float *buf, int &length, const int send_proc_number, + const bool get_length, int tag) const { // Set the tag to 0 if it is < 0 - tag = ( tag >= 0 ) ? tag : 0; - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - PROFILE_START( "recv", profile_level ); + tag = (tag >= 0) ? tag : 0; + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + PROFILE_START("recv", profile_level); // Get the recieve length if necessary - if ( get_length ) { - int bytes = this->probe( send_proc_number, tag ); - int recv_length = bytes / sizeof( float ); - MPI_INSIST( length >= recv_length, "Recived length is larger than allocated array" ); + if (get_length) { + int bytes = this->probe(send_proc_number, tag); + int recv_length = bytes / sizeof(float); + MPI_INSIST(length >= recv_length, + "Recived length is larger than allocated array"); length = recv_length; } // Send the data MPI_Status status; - MPI_Recv( (void *) buf, length, MPI_FLOAT, send_proc_number, tag, communicator, &status ); - PROFILE_STOP( "recv", profile_level ); + MPI_Recv((void *)buf, length, MPI_FLOAT, send_proc_number, tag, + communicator, &status); + PROFILE_STOP("recv", profile_level); } // double -template<> -void MPI_CLASS::recv( - double *buf, int &length, const int send_proc_number, const bool get_length, int tag ) const -{ +template <> +void MPI_CLASS::recv(double *buf, int &length, + const int send_proc_number, const bool get_length, + int tag) const { // Set the tag to 0 if it is < 0 - tag = ( tag >= 0 ) ? tag : 0; - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - PROFILE_START( "recv", profile_level ); + tag = (tag >= 0) ? tag : 0; + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + PROFILE_START("recv", profile_level); // Get the recieve length if necessary - if ( get_length ) { - int bytes = this->probe( send_proc_number, tag ); - int recv_length = bytes / sizeof( double ); - MPI_INSIST( length >= recv_length, "Recived length is larger than allocated array" ); + if (get_length) { + int bytes = this->probe(send_proc_number, tag); + int recv_length = bytes / sizeof(double); + MPI_INSIST(length >= recv_length, + "Recived length is larger than allocated array"); length = recv_length; } // Send the data MPI_Status status; - MPI_Recv( (void *) buf, length, MPI_DOUBLE, send_proc_number, tag, communicator, &status ); - PROFILE_STOP( "recv", profile_level ); + MPI_Recv((void *)buf, length, MPI_DOUBLE, send_proc_number, tag, + communicator, &status); + PROFILE_STOP("recv", profile_level); } #else // We need a concrete instantiation of recv for use without mpi -template<> -void MPI_CLASS::recv( char *buf, int &length, const int, const bool, int tag ) const -{ - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - MPI_INSIST( tag >= 0, "tag must be >= 0" ); - PROFILE_START( "recv", profile_level ); - auto id = getRequest( communicator, tag ); - auto it = global_isendrecv_list.find( id ); - MPI_INSIST( it != global_isendrecv_list.end(), - "recv must be paired with a previous call to isend in serial" ); - MPI_ASSERT( it->second.status == 1 ); - memcpy( buf, it->second.data, length ); - global_isendrecv_list.erase( it ); - PROFILE_STOP( "recv", profile_level ); +template <> +void MPI_CLASS::recv(char *buf, int &length, const int, const bool, + int tag) const { + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + MPI_INSIST(tag >= 0, "tag must be >= 0"); + PROFILE_START("recv", profile_level); + auto id = getRequest(communicator, tag); + auto it = global_isendrecv_list.find(id); + MPI_INSIST(it != global_isendrecv_list.end(), + "recv must be paired with a previous call to isend in serial"); + MPI_ASSERT(it->second.status == 1); + memcpy(buf, it->second.data, length); + global_isendrecv_list.erase(it); + PROFILE_STOP("recv", profile_level); } #endif - /************************************************************************ * Non-blocking recieve data array to another processor. * * Note: these specializations are only called when using MPI. * ************************************************************************/ #ifdef USE_MPI // char -template<> -MPI_Request MPI_CLASS::Irecv( - char *buf, const int length, const int send_proc, const int tag ) const -{ - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - MPI_INSIST( tag >= 0, "tag must be >= 0" ); +template <> +MPI_Request MPI_CLASS::Irecv(char *buf, const int length, + const int send_proc, const int tag) const { + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + MPI_INSIST(tag >= 0, "tag must be >= 0"); MPI_Request request; - PROFILE_START( "Irecv", profile_level ); - MPI_Irecv( (void *) buf, length, MPI_CHAR, send_proc, tag, communicator, &request ); - PROFILE_STOP( "Irecv", profile_level ); + PROFILE_START("Irecv", profile_level); + MPI_Irecv((void *)buf, length, MPI_CHAR, send_proc, tag, communicator, + &request); + PROFILE_STOP("Irecv", profile_level); return request; } // int -template<> -MPI_Request MPI_CLASS::Irecv( - int *buf, const int length, const int send_proc, const int tag ) const -{ - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - MPI_INSIST( tag >= 0, "tag must be >= 0" ); +template <> +MPI_Request MPI_CLASS::Irecv(int *buf, const int length, + const int send_proc, const int tag) const { + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + MPI_INSIST(tag >= 0, "tag must be >= 0"); MPI_Request request; - PROFILE_START( "Irecv", profile_level ); - MPI_Irecv( (void *) buf, length, MPI_INT, send_proc, tag, communicator, &request ); - PROFILE_STOP( "Irecv", profile_level ); + PROFILE_START("Irecv", profile_level); + MPI_Irecv((void *)buf, length, MPI_INT, send_proc, tag, communicator, + &request); + PROFILE_STOP("Irecv", profile_level); return request; } // float -template<> -MPI_Request MPI_CLASS::Irecv( - float *buf, const int length, const int send_proc, const int tag ) const -{ - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - MPI_INSIST( tag >= 0, "tag must be >= 0" ); +template <> +MPI_Request MPI_CLASS::Irecv(float *buf, const int length, + const int send_proc, const int tag) const { + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + MPI_INSIST(tag >= 0, "tag must be >= 0"); MPI_Request request; - PROFILE_START( "Irecv", profile_level ); - MPI_Irecv( (void *) buf, length, MPI_FLOAT, send_proc, tag, communicator, &request ); - PROFILE_STOP( "Irecv", profile_level ); + PROFILE_START("Irecv", profile_level); + MPI_Irecv((void *)buf, length, MPI_FLOAT, send_proc, tag, communicator, + &request); + PROFILE_STOP("Irecv", profile_level); return request; } // double -template<> -MPI_Request MPI_CLASS::Irecv( - double *buf, const int length, const int send_proc, const int tag ) const -{ - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - MPI_INSIST( tag >= 0, "tag must be >= 0" ); +template <> +MPI_Request MPI_CLASS::Irecv(double *buf, const int length, + const int send_proc, const int tag) const { + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + MPI_INSIST(tag >= 0, "tag must be >= 0"); MPI_Request request; - PROFILE_START( "Irecv", profile_level ); - MPI_Irecv( (void *) buf, length, MPI_DOUBLE, send_proc, tag, communicator, &request ); - PROFILE_STOP( "Irecv", profile_level ); + PROFILE_START("Irecv", profile_level); + MPI_Irecv((void *)buf, length, MPI_DOUBLE, send_proc, tag, communicator, + &request); + PROFILE_STOP("Irecv", profile_level); return request; } #else // We need a concrete instantiation of irecv for use without mpi -template<> -MPI_Request MPI_CLASS::Irecv( char *buf, const int length, const int, const int tag ) const -{ - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - MPI_INSIST( tag >= 0, "tag must be >= 0" ); - PROFILE_START( "Irecv", profile_level ); - auto id = getRequest( communicator, tag ); - auto it = global_isendrecv_list.find( id ); - if ( it == global_isendrecv_list.end() ) { +template <> +MPI_Request MPI_CLASS::Irecv(char *buf, const int length, const int, + const int tag) const { + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + MPI_INSIST(tag >= 0, "tag must be >= 0"); + PROFILE_START("Irecv", profile_level); + auto id = getRequest(communicator, tag); + auto it = global_isendrecv_list.find(id); + if (it == global_isendrecv_list.end()) { // We are calling Irecv first Isendrecv_struct data; - data.data = buf; + data.data = buf; data.status = 2; - global_isendrecv_list.insert( std::pair( id, data ) ); + global_isendrecv_list.insert( + std::pair(id, data)); } else { // We called Isend first - MPI_ASSERT( it->second.status == 1 ); - memcpy( buf, it->second.data, length ); - global_isendrecv_list.erase( it ); + MPI_ASSERT(it->second.status == 1); + memcpy(buf, it->second.data, length); + global_isendrecv_list.erase(it); } - PROFILE_STOP( "Irecv", profile_level ); + PROFILE_STOP("Irecv", profile_level); return id; } #endif +/************************************************************************ + * Recieve byte array to another processor. * + ************************************************************************/ +void MPI_CLASS::recvBytes(void *buf, int &number_bytes, const int send_proc, + int tag) const { + recv((char *)buf, number_bytes, send_proc, false, tag); +} /************************************************************************ * Recieve byte array to another processor. * ************************************************************************/ -void MPI_CLASS::recvBytes( void *buf, int &number_bytes, const int send_proc, int tag ) const -{ - recv( (char *) buf, number_bytes, send_proc, false, tag ); +MPI_Request MPI_CLASS::IrecvBytes(void *buf, const int number_bytes, + const int send_proc, const int tag) const { + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + MPI_INSIST(tag >= 0, "tag must be >= 0"); + return Irecv((char *)buf, number_bytes, send_proc, tag); } - -/************************************************************************ - * Recieve byte array to another processor. * - ************************************************************************/ -MPI_Request MPI_CLASS::IrecvBytes( - void *buf, const int number_bytes, const int send_proc, const int tag ) const -{ - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - MPI_INSIST( tag >= 0, "tag must be >= 0" ); - return Irecv( (char *) buf, number_bytes, send_proc, tag ); -} - - /************************************************************************ * sendrecv * ************************************************************************/ -#if defined( USE_MPI ) -template<> -void MPI_CLASS::sendrecv( const char *sendbuf, int sendcount, int dest, int sendtag, - char *recvbuf, int recvcount, int source, int recvtag ) const -{ - PROFILE_START( "sendrecv", profile_level ); - MPI_Sendrecv( sendbuf, sendcount, MPI_CHAR, dest, sendtag, recvbuf, recvcount, MPI_CHAR, source, - recvtag, communicator, MPI_STATUS_IGNORE ); - PROFILE_STOP( "sendrecv", profile_level ); +#if defined(USE_MPI) +template <> +void MPI_CLASS::sendrecv(const char *sendbuf, int sendcount, int dest, + int sendtag, char *recvbuf, int recvcount, + int source, int recvtag) const { + PROFILE_START("sendrecv", profile_level); + MPI_Sendrecv(sendbuf, sendcount, MPI_CHAR, dest, sendtag, recvbuf, + recvcount, MPI_CHAR, source, recvtag, communicator, + MPI_STATUS_IGNORE); + PROFILE_STOP("sendrecv", profile_level); } -template<> -void MPI_CLASS::sendrecv( const int *sendbuf, int sendcount, int dest, int sendtag, - int *recvbuf, int recvcount, int source, int recvtag ) const -{ - PROFILE_START( "sendrecv", profile_level ); - MPI_Sendrecv( sendbuf, sendcount, MPI_INT, dest, sendtag, recvbuf, recvcount, MPI_INT, source, - recvtag, communicator, MPI_STATUS_IGNORE ); - PROFILE_STOP( "sendrecv", profile_level ); +template <> +void MPI_CLASS::sendrecv(const int *sendbuf, int sendcount, int dest, + int sendtag, int *recvbuf, int recvcount, + int source, int recvtag) const { + PROFILE_START("sendrecv", profile_level); + MPI_Sendrecv(sendbuf, sendcount, MPI_INT, dest, sendtag, recvbuf, recvcount, + MPI_INT, source, recvtag, communicator, MPI_STATUS_IGNORE); + PROFILE_STOP("sendrecv", profile_level); } -template<> -void MPI_CLASS::sendrecv( const float *sendbuf, int sendcount, int dest, int sendtag, - float *recvbuf, int recvcount, int source, int recvtag ) const -{ - PROFILE_START( "sendrecv", profile_level ); - MPI_Sendrecv( sendbuf, sendcount, MPI_FLOAT, dest, sendtag, recvbuf, recvcount, MPI_FLOAT, - source, recvtag, communicator, MPI_STATUS_IGNORE ); - PROFILE_STOP( "sendrecv", profile_level ); +template <> +void MPI_CLASS::sendrecv(const float *sendbuf, int sendcount, int dest, + int sendtag, float *recvbuf, int recvcount, + int source, int recvtag) const { + PROFILE_START("sendrecv", profile_level); + MPI_Sendrecv(sendbuf, sendcount, MPI_FLOAT, dest, sendtag, recvbuf, + recvcount, MPI_FLOAT, source, recvtag, communicator, + MPI_STATUS_IGNORE); + PROFILE_STOP("sendrecv", profile_level); } -template<> -void MPI_CLASS::sendrecv( const double *sendbuf, int sendcount, int dest, int sendtag, - double *recvbuf, int recvcount, int source, int recvtag ) const -{ - PROFILE_START( "sendrecv", profile_level ); - MPI_Sendrecv( sendbuf, sendcount, MPI_DOUBLE, dest, sendtag, recvbuf, recvcount, MPI_DOUBLE, - source, recvtag, communicator, MPI_STATUS_IGNORE ); - PROFILE_STOP( "sendrecv", profile_level ); +template <> +void MPI_CLASS::sendrecv(const double *sendbuf, int sendcount, int dest, + int sendtag, double *recvbuf, int recvcount, + int source, int recvtag) const { + PROFILE_START("sendrecv", profile_level); + MPI_Sendrecv(sendbuf, sendcount, MPI_DOUBLE, dest, sendtag, recvbuf, + recvcount, MPI_DOUBLE, source, recvtag, communicator, + MPI_STATUS_IGNORE); + PROFILE_STOP("sendrecv", profile_level); } #endif - /************************************************************************ * allGather * * Note: these specializations are only called when using MPI. * ************************************************************************/ #ifdef USE_MPI // unsigned char -template<> -void MPI_CLASS::call_allGather( - const unsigned char &x_in, unsigned char *x_out ) const -{ - PROFILE_START( "allGather", profile_level ); - MPI_Allgather( - (void *) &x_in, 1, MPI_UNSIGNED_CHAR, (void *) x_out, 1, MPI_UNSIGNED_CHAR, communicator ); - PROFILE_STOP( "allGather", profile_level ); +template <> +void MPI_CLASS::call_allGather(const unsigned char &x_in, + unsigned char *x_out) const { + PROFILE_START("allGather", profile_level); + MPI_Allgather((void *)&x_in, 1, MPI_UNSIGNED_CHAR, (void *)x_out, 1, + MPI_UNSIGNED_CHAR, communicator); + PROFILE_STOP("allGather", profile_level); } -template<> -void MPI_CLASS::call_allGather( const unsigned char *x_in, int size_in, - unsigned char *x_out, int *size_out, int *disp_out ) const -{ - PROFILE_START( "allGatherv", profile_level ); - MPI_Allgatherv( (void *) x_in, size_in, MPI_CHAR, (void *) x_out, size_out, disp_out, MPI_CHAR, - communicator ); - PROFILE_STOP( "allGatherv", profile_level ); +template <> +void MPI_CLASS::call_allGather(const unsigned char *x_in, + int size_in, unsigned char *x_out, + int *size_out, + int *disp_out) const { + PROFILE_START("allGatherv", profile_level); + MPI_Allgatherv((void *)x_in, size_in, MPI_CHAR, (void *)x_out, size_out, + disp_out, MPI_CHAR, communicator); + PROFILE_STOP("allGatherv", profile_level); } // char -template<> -void MPI_CLASS::call_allGather( const char &x_in, char *x_out ) const -{ - PROFILE_START( "allGather", profile_level ); - MPI_Allgather( (void *) &x_in, 1, MPI_CHAR, (void *) x_out, 1, MPI_CHAR, communicator ); - PROFILE_STOP( "allGather", profile_level ); +template <> +void MPI_CLASS::call_allGather(const char &x_in, char *x_out) const { + PROFILE_START("allGather", profile_level); + MPI_Allgather((void *)&x_in, 1, MPI_CHAR, (void *)x_out, 1, MPI_CHAR, + communicator); + PROFILE_STOP("allGather", profile_level); } -template<> -void MPI_CLASS::call_allGather( - const char *x_in, int size_in, char *x_out, int *size_out, int *disp_out ) const -{ - PROFILE_START( "allGatherv", profile_level ); - MPI_Allgatherv( (void *) x_in, size_in, MPI_CHAR, (void *) x_out, size_out, disp_out, MPI_CHAR, - communicator ); - PROFILE_STOP( "allGatherv", profile_level ); +template <> +void MPI_CLASS::call_allGather(const char *x_in, int size_in, char *x_out, + int *size_out, int *disp_out) const { + PROFILE_START("allGatherv", profile_level); + MPI_Allgatherv((void *)x_in, size_in, MPI_CHAR, (void *)x_out, size_out, + disp_out, MPI_CHAR, communicator); + PROFILE_STOP("allGatherv", profile_level); } // unsigned int -template<> -void MPI_CLASS::call_allGather( const unsigned int &x_in, unsigned int *x_out ) const -{ - PROFILE_START( "allGather", profile_level ); - MPI_Allgather( (void *) &x_in, 1, MPI_UNSIGNED, (void *) x_out, 1, MPI_UNSIGNED, communicator ); - PROFILE_STOP( "allGather", profile_level ); +template <> +void MPI_CLASS::call_allGather(const unsigned int &x_in, + unsigned int *x_out) const { + PROFILE_START("allGather", profile_level); + MPI_Allgather((void *)&x_in, 1, MPI_UNSIGNED, (void *)x_out, 1, + MPI_UNSIGNED, communicator); + PROFILE_STOP("allGather", profile_level); } -template<> -void MPI_CLASS::call_allGather( - const unsigned int *x_in, int size_in, unsigned int *x_out, int *size_out, int *disp_out ) const -{ - PROFILE_START( "allGatherv", profile_level ); - MPI_Allgatherv( (void *) x_in, size_in, MPI_UNSIGNED, (void *) x_out, size_out, disp_out, - MPI_UNSIGNED, communicator ); - PROFILE_STOP( "allGatherv", profile_level ); +template <> +void MPI_CLASS::call_allGather(const unsigned int *x_in, + int size_in, unsigned int *x_out, + int *size_out, + int *disp_out) const { + PROFILE_START("allGatherv", profile_level); + MPI_Allgatherv((void *)x_in, size_in, MPI_UNSIGNED, (void *)x_out, size_out, + disp_out, MPI_UNSIGNED, communicator); + PROFILE_STOP("allGatherv", profile_level); } // int -template<> -void MPI_CLASS::call_allGather( const int &x_in, int *x_out ) const -{ - PROFILE_START( "allGather", profile_level ); - MPI_Allgather( (void *) &x_in, 1, MPI_INT, (void *) x_out, 1, MPI_INT, communicator ); - PROFILE_STOP( "allGather", profile_level ); +template <> +void MPI_CLASS::call_allGather(const int &x_in, int *x_out) const { + PROFILE_START("allGather", profile_level); + MPI_Allgather((void *)&x_in, 1, MPI_INT, (void *)x_out, 1, MPI_INT, + communicator); + PROFILE_STOP("allGather", profile_level); } -template<> -void MPI_CLASS::call_allGather( - const int *x_in, int size_in, int *x_out, int *size_out, int *disp_out ) const -{ - PROFILE_START( "allGatherv", profile_level ); - MPI_Allgatherv( (void *) x_in, size_in, MPI_INT, (void *) x_out, size_out, disp_out, MPI_INT, - communicator ); - PROFILE_STOP( "allGatherv", profile_level ); +template <> +void MPI_CLASS::call_allGather(const int *x_in, int size_in, int *x_out, + int *size_out, int *disp_out) const { + PROFILE_START("allGatherv", profile_level); + MPI_Allgatherv((void *)x_in, size_in, MPI_INT, (void *)x_out, size_out, + disp_out, MPI_INT, communicator); + PROFILE_STOP("allGatherv", profile_level); } // unsigned long int -template<> +template <> void MPI_CLASS::call_allGather( - const unsigned long int &x_in, unsigned long int *x_out ) const -{ - PROFILE_START( "allGather", profile_level ); - MPI_Allgather( - (void *) &x_in, 1, MPI_UNSIGNED_LONG, (void *) x_out, 1, MPI_UNSIGNED_LONG, communicator ); - PROFILE_STOP( "allGather", profile_level ); + const unsigned long int &x_in, unsigned long int *x_out) const { + PROFILE_START("allGather", profile_level); + MPI_Allgather((void *)&x_in, 1, MPI_UNSIGNED_LONG, (void *)x_out, 1, + MPI_UNSIGNED_LONG, communicator); + PROFILE_STOP("allGather", profile_level); } -template<> -void MPI_CLASS::call_allGather( const unsigned long int *x_in, int size_in, - unsigned long int *x_out, int *size_out, int *disp_out ) const -{ - PROFILE_START( "allGatherv", profile_level ); - MPI_Allgatherv( (void *) x_in, size_in, MPI_UNSIGNED_LONG, (void *) x_out, size_out, disp_out, - MPI_UNSIGNED_LONG, communicator ); - PROFILE_STOP( "allGatherv", profile_level ); +template <> +void MPI_CLASS::call_allGather(const unsigned long int *x_in, + int size_in, + unsigned long int *x_out, + int *size_out, + int *disp_out) const { + PROFILE_START("allGatherv", profile_level); + MPI_Allgatherv((void *)x_in, size_in, MPI_UNSIGNED_LONG, (void *)x_out, + size_out, disp_out, MPI_UNSIGNED_LONG, communicator); + PROFILE_STOP("allGatherv", profile_level); } // long int -template<> -void MPI_CLASS::call_allGather( const long int &x_in, long int *x_out ) const -{ - PROFILE_START( "allGather", profile_level ); - MPI_Allgather( (void *) &x_in, 1, MPI_LONG, (void *) x_out, 1, MPI_LONG, communicator ); - PROFILE_STOP( "allGather", profile_level ); +template <> +void MPI_CLASS::call_allGather(const long int &x_in, + long int *x_out) const { + PROFILE_START("allGather", profile_level); + MPI_Allgather((void *)&x_in, 1, MPI_LONG, (void *)x_out, 1, MPI_LONG, + communicator); + PROFILE_STOP("allGather", profile_level); } -template<> -void MPI_CLASS::call_allGather( - const long int *x_in, int size_in, long int *x_out, int *size_out, int *disp_out ) const -{ - PROFILE_START( "allGatherv", profile_level ); - MPI_Allgatherv( (void *) x_in, size_in, MPI_LONG, (void *) x_out, size_out, disp_out, MPI_LONG, - communicator ); - PROFILE_STOP( "allGatherv", profile_level ); +template <> +void MPI_CLASS::call_allGather(const long int *x_in, int size_in, + long int *x_out, int *size_out, + int *disp_out) const { + PROFILE_START("allGatherv", profile_level); + MPI_Allgatherv((void *)x_in, size_in, MPI_LONG, (void *)x_out, size_out, + disp_out, MPI_LONG, communicator); + PROFILE_STOP("allGatherv", profile_level); } // float -template<> -void MPI_CLASS::call_allGather( const float &x_in, float *x_out ) const -{ - PROFILE_START( "allGather", profile_level ); - MPI_Allgather( (void *) &x_in, 1, MPI_FLOAT, (void *) x_out, 1, MPI_FLOAT, communicator ); - PROFILE_STOP( "allGather", profile_level ); +template <> +void MPI_CLASS::call_allGather(const float &x_in, float *x_out) const { + PROFILE_START("allGather", profile_level); + MPI_Allgather((void *)&x_in, 1, MPI_FLOAT, (void *)x_out, 1, MPI_FLOAT, + communicator); + PROFILE_STOP("allGather", profile_level); } -template<> -void MPI_CLASS::call_allGather( - const float *x_in, int size_in, float *x_out, int *size_out, int *disp_out ) const -{ - PROFILE_START( "allGatherv", profile_level ); - MPI_Allgatherv( (void *) x_in, size_in, MPI_FLOAT, (void *) x_out, size_out, disp_out, - MPI_FLOAT, communicator ); - PROFILE_STOP( "allGatherv", profile_level ); +template <> +void MPI_CLASS::call_allGather(const float *x_in, int size_in, + float *x_out, int *size_out, + int *disp_out) const { + PROFILE_START("allGatherv", profile_level); + MPI_Allgatherv((void *)x_in, size_in, MPI_FLOAT, (void *)x_out, size_out, + disp_out, MPI_FLOAT, communicator); + PROFILE_STOP("allGatherv", profile_level); } // double -template<> -void MPI_CLASS::call_allGather( const double &x_in, double *x_out ) const -{ - PROFILE_START( "allGather", profile_level ); - MPI_Allgather( (void *) &x_in, 1, MPI_DOUBLE, (void *) x_out, 1, MPI_DOUBLE, communicator ); - PROFILE_STOP( "allGather", profile_level ); +template <> +void MPI_CLASS::call_allGather(const double &x_in, + double *x_out) const { + PROFILE_START("allGather", profile_level); + MPI_Allgather((void *)&x_in, 1, MPI_DOUBLE, (void *)x_out, 1, MPI_DOUBLE, + communicator); + PROFILE_STOP("allGather", profile_level); } -template<> -void MPI_CLASS::call_allGather( - const double *x_in, int size_in, double *x_out, int *size_out, int *disp_out ) const -{ - PROFILE_START( "allGatherv", profile_level ); - MPI_Allgatherv( (void *) x_in, size_in, MPI_DOUBLE, (void *) x_out, size_out, disp_out, - MPI_DOUBLE, communicator ); - PROFILE_STOP( "allGatherv", profile_level ); +template <> +void MPI_CLASS::call_allGather(const double *x_in, int size_in, + double *x_out, int *size_out, + int *disp_out) const { + PROFILE_START("allGatherv", profile_level); + MPI_Allgatherv((void *)x_in, size_in, MPI_DOUBLE, (void *)x_out, size_out, + disp_out, MPI_DOUBLE, communicator); + PROFILE_STOP("allGatherv", profile_level); } #else // We need a concrete instantiation of call_allGather(x_in,size_in,x_out,size_out) -template<> -void MPI_CLASS::call_allGather( const char *, int, char *, int *, int * ) const -{ - MPI_ERROR( "Internal error in communicator (allGather) " ); +template <> +void MPI_CLASS::call_allGather(const char *, int, char *, int *, + int *) const { + MPI_ERROR("Internal error in communicator (allGather) "); } #endif - /************************************************************************ * allToAll * * Note: these specializations are only called when using MPI. * ************************************************************************/ #ifdef USE_MPI -template<> -void MPI_CLASS::allToAll( - const int n, const unsigned char *send, unsigned char *recv ) const -{ - PROFILE_START( "allToAll", profile_level ); - MPI_Alltoall( - (void *) send, n, MPI_UNSIGNED_CHAR, (void *) recv, n, MPI_UNSIGNED_CHAR, communicator ); - PROFILE_STOP( "allToAll", profile_level ); +template <> +void MPI_CLASS::allToAll(const int n, const unsigned char *send, + unsigned char *recv) const { + PROFILE_START("allToAll", profile_level); + MPI_Alltoall((void *)send, n, MPI_UNSIGNED_CHAR, (void *)recv, n, + MPI_UNSIGNED_CHAR, communicator); + PROFILE_STOP("allToAll", profile_level); } -template<> -void MPI_CLASS::allToAll( const int n, const char *send, char *recv ) const -{ - PROFILE_START( "allToAll", profile_level ); - MPI_Alltoall( (void *) send, n, MPI_CHAR, (void *) recv, n, MPI_CHAR, communicator ); - PROFILE_STOP( "allToAll", profile_level ); +template <> +void MPI_CLASS::allToAll(const int n, const char *send, + char *recv) const { + PROFILE_START("allToAll", profile_level); + MPI_Alltoall((void *)send, n, MPI_CHAR, (void *)recv, n, MPI_CHAR, + communicator); + PROFILE_STOP("allToAll", profile_level); } -template<> -void MPI_CLASS::allToAll( - const int n, const unsigned int *send, unsigned int *recv ) const -{ - PROFILE_START( "allToAll", profile_level ); - MPI_Alltoall( (void *) send, n, MPI_UNSIGNED, (void *) recv, n, MPI_UNSIGNED, communicator ); - PROFILE_STOP( "allToAll", profile_level ); +template <> +void MPI_CLASS::allToAll(const int n, const unsigned int *send, + unsigned int *recv) const { + PROFILE_START("allToAll", profile_level); + MPI_Alltoall((void *)send, n, MPI_UNSIGNED, (void *)recv, n, MPI_UNSIGNED, + communicator); + PROFILE_STOP("allToAll", profile_level); } -template<> -void MPI_CLASS::allToAll( const int n, const int *send, int *recv ) const -{ - PROFILE_START( "allToAll", profile_level ); - MPI_Alltoall( (void *) send, n, MPI_INT, (void *) recv, n, MPI_INT, communicator ); - PROFILE_STOP( "allToAll", profile_level ); +template <> +void MPI_CLASS::allToAll(const int n, const int *send, int *recv) const { + PROFILE_START("allToAll", profile_level); + MPI_Alltoall((void *)send, n, MPI_INT, (void *)recv, n, MPI_INT, + communicator); + PROFILE_STOP("allToAll", profile_level); } -template<> -void MPI_CLASS::allToAll( - const int n, const unsigned long int *send, unsigned long int *recv ) const -{ - PROFILE_START( "allToAll", profile_level ); - MPI_Alltoall( - (void *) send, n, MPI_UNSIGNED_LONG, (void *) recv, n, MPI_UNSIGNED_LONG, communicator ); - PROFILE_STOP( "allToAll", profile_level ); +template <> +void MPI_CLASS::allToAll(const int n, + const unsigned long int *send, + unsigned long int *recv) const { + PROFILE_START("allToAll", profile_level); + MPI_Alltoall((void *)send, n, MPI_UNSIGNED_LONG, (void *)recv, n, + MPI_UNSIGNED_LONG, communicator); + PROFILE_STOP("allToAll", profile_level); } -template<> -void MPI_CLASS::allToAll( const int n, const long int *send, long int *recv ) const -{ - PROFILE_START( "allToAll", profile_level ); - MPI_Alltoall( (void *) send, n, MPI_LONG, (void *) recv, n, MPI_LONG, communicator ); - PROFILE_STOP( "allToAll", profile_level ); +template <> +void MPI_CLASS::allToAll(const int n, const long int *send, + long int *recv) const { + PROFILE_START("allToAll", profile_level); + MPI_Alltoall((void *)send, n, MPI_LONG, (void *)recv, n, MPI_LONG, + communicator); + PROFILE_STOP("allToAll", profile_level); } -template<> -void MPI_CLASS::allToAll( const int n, const float *send, float *recv ) const -{ - PROFILE_START( "allToAll", profile_level ); - MPI_Alltoall( (void *) send, n, MPI_FLOAT, (void *) recv, n, MPI_FLOAT, communicator ); - PROFILE_STOP( "allToAll", profile_level ); +template <> +void MPI_CLASS::allToAll(const int n, const float *send, + float *recv) const { + PROFILE_START("allToAll", profile_level); + MPI_Alltoall((void *)send, n, MPI_FLOAT, (void *)recv, n, MPI_FLOAT, + communicator); + PROFILE_STOP("allToAll", profile_level); } -template<> -void MPI_CLASS::allToAll( const int n, const double *send, double *recv ) const -{ - PROFILE_START( "allToAll", profile_level ); - MPI_Alltoall( (void *) send, n, MPI_DOUBLE, (void *) recv, n, MPI_DOUBLE, communicator ); - PROFILE_STOP( "allToAll", profile_level ); +template <> +void MPI_CLASS::allToAll(const int n, const double *send, + double *recv) const { + PROFILE_START("allToAll", profile_level); + MPI_Alltoall((void *)send, n, MPI_DOUBLE, (void *)recv, n, MPI_DOUBLE, + communicator); + PROFILE_STOP("allToAll", profile_level); } #endif - /************************************************************************ * call_allToAll * * Note: these specializations are only called when using MPI. * ************************************************************************/ #ifdef USE_MPI // unsigned char -template<> -void MPI_CLASS::call_allToAll( const unsigned char *send_data, const int send_cnt[], - const int send_disp[], unsigned char *recv_data, const int *recv_cnt, - const int *recv_disp ) const -{ - PROFILE_START( "allToAllv", profile_level ); - MPI_Alltoallv( (void *) send_data, (int *) send_cnt, (int *) send_disp, MPI_UNSIGNED_CHAR, - (void *) recv_data, (int *) recv_cnt, (int *) recv_disp, MPI_UNSIGNED_CHAR, communicator ); - PROFILE_STOP( "allToAllv", profile_level ); +template <> +void MPI_CLASS::call_allToAll( + const unsigned char *send_data, const int send_cnt[], const int send_disp[], + unsigned char *recv_data, const int *recv_cnt, const int *recv_disp) const { + PROFILE_START("allToAllv", profile_level); + MPI_Alltoallv((void *)send_data, (int *)send_cnt, (int *)send_disp, + MPI_UNSIGNED_CHAR, (void *)recv_data, (int *)recv_cnt, + (int *)recv_disp, MPI_UNSIGNED_CHAR, communicator); + PROFILE_STOP("allToAllv", profile_level); } // char -template<> -void MPI_CLASS::call_allToAll( const char *send_data, const int send_cnt[], - const int send_disp[], char *recv_data, const int *recv_cnt, const int *recv_disp ) const -{ - PROFILE_START( "allToAllv", profile_level ); - MPI_Alltoallv( (void *) send_data, (int *) send_cnt, (int *) send_disp, MPI_CHAR, - (void *) recv_data, (int *) recv_cnt, (int *) recv_disp, MPI_CHAR, communicator ); - PROFILE_STOP( "allToAllv", profile_level ); +template <> +void MPI_CLASS::call_allToAll(const char *send_data, const int send_cnt[], + const int send_disp[], char *recv_data, + const int *recv_cnt, + const int *recv_disp) const { + PROFILE_START("allToAllv", profile_level); + MPI_Alltoallv((void *)send_data, (int *)send_cnt, (int *)send_disp, + MPI_CHAR, (void *)recv_data, (int *)recv_cnt, + (int *)recv_disp, MPI_CHAR, communicator); + PROFILE_STOP("allToAllv", profile_level); } // unsigned int -template<> -void MPI_CLASS::call_allToAll( const unsigned int *send_data, const int send_cnt[], - const int send_disp[], unsigned int *recv_data, const int *recv_cnt, - const int *recv_disp ) const -{ - PROFILE_START( "allToAllv", profile_level ); - MPI_Alltoallv( (void *) send_data, (int *) send_cnt, (int *) send_disp, MPI_UNSIGNED, - (void *) recv_data, (int *) recv_cnt, (int *) recv_disp, MPI_UNSIGNED, communicator ); - PROFILE_STOP( "allToAllv", profile_level ); +template <> +void MPI_CLASS::call_allToAll( + const unsigned int *send_data, const int send_cnt[], const int send_disp[], + unsigned int *recv_data, const int *recv_cnt, const int *recv_disp) const { + PROFILE_START("allToAllv", profile_level); + MPI_Alltoallv((void *)send_data, (int *)send_cnt, (int *)send_disp, + MPI_UNSIGNED, (void *)recv_data, (int *)recv_cnt, + (int *)recv_disp, MPI_UNSIGNED, communicator); + PROFILE_STOP("allToAllv", profile_level); } // int -template<> -void MPI_CLASS::call_allToAll( const int *send_data, const int send_cnt[], - const int send_disp[], int *recv_data, const int *recv_cnt, const int *recv_disp ) const -{ - PROFILE_START( "allToAllv", profile_level ); - MPI_Alltoallv( (void *) send_data, (int *) send_cnt, (int *) send_disp, MPI_INT, - (void *) recv_data, (int *) recv_cnt, (int *) recv_disp, MPI_INT, communicator ); - PROFILE_STOP( "allToAllv", profile_level ); +template <> +void MPI_CLASS::call_allToAll(const int *send_data, const int send_cnt[], + const int send_disp[], int *recv_data, + const int *recv_cnt, + const int *recv_disp) const { + PROFILE_START("allToAllv", profile_level); + MPI_Alltoallv((void *)send_data, (int *)send_cnt, (int *)send_disp, MPI_INT, + (void *)recv_data, (int *)recv_cnt, (int *)recv_disp, MPI_INT, + communicator); + PROFILE_STOP("allToAllv", profile_level); } // unsigned long int -template<> -void MPI_CLASS::call_allToAll( const unsigned long int *send_data, - const int send_cnt[], const int send_disp[], unsigned long int *recv_data, const int *recv_cnt, - const int *recv_disp ) const -{ - PROFILE_START( "allToAllv", profile_level ); - MPI_Alltoallv( (void *) send_data, (int *) send_cnt, (int *) send_disp, MPI_UNSIGNED_LONG, - (void *) recv_data, (int *) recv_cnt, (int *) recv_disp, MPI_UNSIGNED_LONG, communicator ); - PROFILE_STOP( "allToAllv", profile_level ); +template <> +void MPI_CLASS::call_allToAll( + const unsigned long int *send_data, const int send_cnt[], + const int send_disp[], unsigned long int *recv_data, const int *recv_cnt, + const int *recv_disp) const { + PROFILE_START("allToAllv", profile_level); + MPI_Alltoallv((void *)send_data, (int *)send_cnt, (int *)send_disp, + MPI_UNSIGNED_LONG, (void *)recv_data, (int *)recv_cnt, + (int *)recv_disp, MPI_UNSIGNED_LONG, communicator); + PROFILE_STOP("allToAllv", profile_level); } // long int -template<> -void MPI_CLASS::call_allToAll( const long int *send_data, const int send_cnt[], - const int send_disp[], long int *recv_data, const int *recv_cnt, const int *recv_disp ) const -{ - PROFILE_START( "allToAllv", profile_level ); - MPI_Alltoallv( (void *) send_data, (int *) send_cnt, (int *) send_disp, MPI_LONG, - (void *) recv_data, (int *) recv_cnt, (int *) recv_disp, MPI_LONG, communicator ); - PROFILE_STOP( "allToAllv", profile_level ); +template <> +void MPI_CLASS::call_allToAll( + const long int *send_data, const int send_cnt[], const int send_disp[], + long int *recv_data, const int *recv_cnt, const int *recv_disp) const { + PROFILE_START("allToAllv", profile_level); + MPI_Alltoallv((void *)send_data, (int *)send_cnt, (int *)send_disp, + MPI_LONG, (void *)recv_data, (int *)recv_cnt, + (int *)recv_disp, MPI_LONG, communicator); + PROFILE_STOP("allToAllv", profile_level); } // float -template<> -void MPI_CLASS::call_allToAll( const float *send_data, const int send_cnt[], - const int send_disp[], float *recv_data, const int *recv_cnt, const int *recv_disp ) const -{ - PROFILE_START( "allToAllv", profile_level ); - MPI_Alltoallv( (void *) send_data, (int *) send_cnt, (int *) send_disp, MPI_FLOAT, - (void *) recv_data, (int *) recv_cnt, (int *) recv_disp, MPI_FLOAT, communicator ); - PROFILE_STOP( "allToAllv", profile_level ); +template <> +void MPI_CLASS::call_allToAll(const float *send_data, + const int send_cnt[], + const int send_disp[], float *recv_data, + const int *recv_cnt, + const int *recv_disp) const { + PROFILE_START("allToAllv", profile_level); + MPI_Alltoallv((void *)send_data, (int *)send_cnt, (int *)send_disp, + MPI_FLOAT, (void *)recv_data, (int *)recv_cnt, + (int *)recv_disp, MPI_FLOAT, communicator); + PROFILE_STOP("allToAllv", profile_level); } // double -template<> -void MPI_CLASS::call_allToAll( const double *send_data, const int send_cnt[], - const int send_disp[], double *recv_data, const int *recv_cnt, const int *recv_disp ) const -{ - PROFILE_START( "allToAllv", profile_level ); - MPI_Alltoallv( (void *) send_data, (int *) send_cnt, (int *) send_disp, MPI_DOUBLE, - (void *) recv_data, (int *) recv_cnt, (int *) recv_disp, MPI_DOUBLE, communicator ); - PROFILE_STOP( "allToAllv", profile_level ); +template <> +void MPI_CLASS::call_allToAll(const double *send_data, + const int send_cnt[], + const int send_disp[], double *recv_data, + const int *recv_cnt, + const int *recv_disp) const { + PROFILE_START("allToAllv", profile_level); + MPI_Alltoallv((void *)send_data, (int *)send_cnt, (int *)send_disp, + MPI_DOUBLE, (void *)recv_data, (int *)recv_cnt, + (int *)recv_disp, MPI_DOUBLE, communicator); + PROFILE_STOP("allToAllv", profile_level); } #else // Default instatiation of unsigned char -template<> -void MPI_CLASS::call_allToAll( - const char *, const int[], const int[], char *, const int *, const int * ) const -{ - MPI_ERROR( "Should not reach this point" ); +template <> +void MPI_CLASS::call_allToAll(const char *, const int[], const int[], + char *, const int *, const int *) const { + MPI_ERROR("Should not reach this point"); } #endif - /************************************************************************ * call_sumScan * * Note: these specializations are only called when using MPI. * ************************************************************************/ #ifdef USE_MPI // unsigned char -template<> -void MPI_CLASS::call_sumScan( - const unsigned char *send, unsigned char *recv, int n ) const -{ - PROFILE_START( "sumScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_UNSIGNED_CHAR, MPI_SUM, communicator ); - PROFILE_STOP( "sumScan", profile_level ); +template <> +void MPI_CLASS::call_sumScan(const unsigned char *send, + unsigned char *recv, int n) const { + PROFILE_START("sumScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_UNSIGNED_CHAR, MPI_SUM, + communicator); + PROFILE_STOP("sumScan", profile_level); } // char -template<> -void MPI_CLASS::call_sumScan( const char *send, char *recv, int n ) const -{ - PROFILE_START( "sumScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_SIGNED_CHAR, MPI_SUM, communicator ); - PROFILE_STOP( "sumScan", profile_level ); +template <> +void MPI_CLASS::call_sumScan(const char *send, char *recv, int n) const { + PROFILE_START("sumScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_SIGNED_CHAR, MPI_SUM, + communicator); + PROFILE_STOP("sumScan", profile_level); } // unsigned int -template<> -void MPI_CLASS::call_sumScan( - const unsigned int *send, unsigned int *recv, int n ) const -{ - PROFILE_START( "sumScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_UNSIGNED, MPI_SUM, communicator ); - PROFILE_STOP( "sumScan", profile_level ); +template <> +void MPI_CLASS::call_sumScan(const unsigned int *send, + unsigned int *recv, int n) const { + PROFILE_START("sumScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_UNSIGNED, MPI_SUM, + communicator); + PROFILE_STOP("sumScan", profile_level); } // int -template<> -void MPI_CLASS::call_sumScan( const int *send, int *recv, int n ) const -{ - PROFILE_START( "sumScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_INT, MPI_SUM, communicator ); - PROFILE_STOP( "sumScan", profile_level ); +template <> +void MPI_CLASS::call_sumScan(const int *send, int *recv, int n) const { + PROFILE_START("sumScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_INT, MPI_SUM, communicator); + PROFILE_STOP("sumScan", profile_level); } // long int -template<> -void MPI_CLASS::call_sumScan( const long int *send, long int *recv, int n ) const -{ - PROFILE_START( "sumScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_LONG, MPI_SUM, communicator ); - PROFILE_STOP( "sumScan", profile_level ); +template <> +void MPI_CLASS::call_sumScan(const long int *send, long int *recv, + int n) const { + PROFILE_START("sumScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_LONG, MPI_SUM, communicator); + PROFILE_STOP("sumScan", profile_level); } // unsigned long int -template<> -void MPI_CLASS::call_sumScan( - const unsigned long *send, unsigned long *recv, int n ) const -{ - PROFILE_START( "sumScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_UNSIGNED_LONG, MPI_SUM, communicator ); - PROFILE_STOP( "sumScan", profile_level ); +template <> +void MPI_CLASS::call_sumScan(const unsigned long *send, + unsigned long *recv, int n) const { + PROFILE_START("sumScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_UNSIGNED_LONG, MPI_SUM, + communicator); + PROFILE_STOP("sumScan", profile_level); } // size_t #ifdef USE_WINDOWS -template<> -void MPI_CLASS::call_sumScan( const size_t *send, size_t *recv, int n ) const -{ - MPI_ASSERT( MPI_SIZE_T != 0 ); - PROFILE_START( "sumScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_SIZE_T, MPI_SUM, communicator ); - PROFILE_STOP( "sumScan", profile_level ); +template <> +void MPI_CLASS::call_sumScan(const size_t *send, size_t *recv, + int n) const { + MPI_ASSERT(MPI_SIZE_T != 0); + PROFILE_START("sumScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_SIZE_T, MPI_SUM, communicator); + PROFILE_STOP("sumScan", profile_level); } #endif // float -template<> -void MPI_CLASS::call_sumScan( const float *send, float *recv, int n ) const -{ - PROFILE_START( "sumScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_FLOAT, MPI_SUM, communicator ); - PROFILE_STOP( "sumScan", profile_level ); +template <> +void MPI_CLASS::call_sumScan(const float *send, float *recv, + int n) const { + PROFILE_START("sumScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_FLOAT, MPI_SUM, communicator); + PROFILE_STOP("sumScan", profile_level); } // double -template<> -void MPI_CLASS::call_sumScan( const double *send, double *recv, int n ) const -{ - PROFILE_START( "sumScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_DOUBLE, MPI_SUM, communicator ); - PROFILE_STOP( "sumScan", profile_level ); +template <> +void MPI_CLASS::call_sumScan(const double *send, double *recv, + int n) const { + PROFILE_START("sumScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_DOUBLE, MPI_SUM, communicator); + PROFILE_STOP("sumScan", profile_level); } // std::complex -template<> +template <> void MPI_CLASS::call_sumScan>( - const std::complex *x, std::complex *y, int n ) const -{ + const std::complex *x, std::complex *y, int n) const { auto send = new double[2 * n]; auto recv = new double[2 * n]; - for ( int i = 0; i < n; i++ ) { - send[2 * i + 0] = real( x[i] ); - send[2 * i + 1] = imag( x[i] ); + for (int i = 0; i < n; i++) { + send[2 * i + 0] = real(x[i]); + send[2 * i + 1] = imag(x[i]); } - MPI_Scan( (void *) send, (void *) recv, 2 * n, MPI_DOUBLE, MPI_SUM, communicator ); - for ( int i = 0; i < n; i++ ) - y[i] = std::complex( recv[2 * i + 0], recv[2 * i + 1] ); + MPI_Scan((void *)send, (void *)recv, 2 * n, MPI_DOUBLE, MPI_SUM, + communicator); + for (int i = 0; i < n; i++) + y[i] = std::complex(recv[2 * i + 0], recv[2 * i + 1]); delete[] send; delete[] recv; } #endif - /************************************************************************ * call_minScan * * Note: these specializations are only called when using MPI. * ************************************************************************/ #ifdef USE_MPI // unsigned char -template<> -void MPI_CLASS::call_minScan( - const unsigned char *send, unsigned char *recv, int n ) const -{ - PROFILE_START( "minScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_UNSIGNED_CHAR, MPI_MIN, communicator ); - PROFILE_STOP( "minScan", profile_level ); +template <> +void MPI_CLASS::call_minScan(const unsigned char *send, + unsigned char *recv, int n) const { + PROFILE_START("minScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_UNSIGNED_CHAR, MPI_MIN, + communicator); + PROFILE_STOP("minScan", profile_level); } // char -template<> -void MPI_CLASS::call_minScan( const char *send, char *recv, int n ) const -{ - PROFILE_START( "minScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_SIGNED_CHAR, MPI_MIN, communicator ); - PROFILE_STOP( "minScan", profile_level ); +template <> +void MPI_CLASS::call_minScan(const char *send, char *recv, int n) const { + PROFILE_START("minScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_SIGNED_CHAR, MPI_MIN, + communicator); + PROFILE_STOP("minScan", profile_level); } // unsigned int -template<> -void MPI_CLASS::call_minScan( - const unsigned int *send, unsigned int *recv, int n ) const -{ - PROFILE_START( "minScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_UNSIGNED, MPI_MIN, communicator ); - PROFILE_STOP( "minScan", profile_level ); +template <> +void MPI_CLASS::call_minScan(const unsigned int *send, + unsigned int *recv, int n) const { + PROFILE_START("minScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_UNSIGNED, MPI_MIN, + communicator); + PROFILE_STOP("minScan", profile_level); } // int -template<> -void MPI_CLASS::call_minScan( const int *send, int *recv, int n ) const -{ - PROFILE_START( "minScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_INT, MPI_MIN, communicator ); - PROFILE_STOP( "minScan", profile_level ); +template <> +void MPI_CLASS::call_minScan(const int *send, int *recv, int n) const { + PROFILE_START("minScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_INT, MPI_MIN, communicator); + PROFILE_STOP("minScan", profile_level); } // unsigned long int -template<> -void MPI_CLASS::call_minScan( - const unsigned long int *send, unsigned long int *recv, int n ) const -{ - PROFILE_START( "minScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_UNSIGNED_LONG, MPI_MIN, communicator ); - PROFILE_STOP( "minScan", profile_level ); +template <> +void MPI_CLASS::call_minScan(const unsigned long int *send, + unsigned long int *recv, + int n) const { + PROFILE_START("minScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_UNSIGNED_LONG, MPI_MIN, + communicator); + PROFILE_STOP("minScan", profile_level); } // long int -template<> -void MPI_CLASS::call_minScan( const long int *send, long int *recv, int n ) const -{ - PROFILE_START( "minScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_LONG, MPI_MIN, communicator ); - PROFILE_STOP( "minScan", profile_level ); +template <> +void MPI_CLASS::call_minScan(const long int *send, long int *recv, + int n) const { + PROFILE_START("minScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_LONG, MPI_MIN, communicator); + PROFILE_STOP("minScan", profile_level); } // size_t #ifdef USE_WINDOWS -template<> -void MPI_CLASS::call_minScan( const size_t *send, size_t *recv, int n ) const -{ - MPI_ASSERT( MPI_SIZE_T != 0 ); - PROFILE_START( "minScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_SIZE_T, MPI_MIN, communicator ); - PROFILE_STOP( "minScan", profile_level ); +template <> +void MPI_CLASS::call_minScan(const size_t *send, size_t *recv, + int n) const { + MPI_ASSERT(MPI_SIZE_T != 0); + PROFILE_START("minScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_SIZE_T, MPI_MIN, communicator); + PROFILE_STOP("minScan", profile_level); } #endif // float -template<> -void MPI_CLASS::call_minScan( const float *send, float *recv, int n ) const -{ - PROFILE_START( "minScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_FLOAT, MPI_MIN, communicator ); - PROFILE_STOP( "minScan", profile_level ); +template <> +void MPI_CLASS::call_minScan(const float *send, float *recv, + int n) const { + PROFILE_START("minScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_FLOAT, MPI_MIN, communicator); + PROFILE_STOP("minScan", profile_level); } // double -template<> -void MPI_CLASS::call_minScan( const double *send, double *recv, int n ) const -{ - PROFILE_START( "minScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_DOUBLE, MPI_MIN, communicator ); - PROFILE_STOP( "minScan", profile_level ); +template <> +void MPI_CLASS::call_minScan(const double *send, double *recv, + int n) const { + PROFILE_START("minScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_DOUBLE, MPI_MIN, communicator); + PROFILE_STOP("minScan", profile_level); } #endif - /************************************************************************ * call_maxScan * * Note: these specializations are only called when using MPI. * ************************************************************************/ #ifdef USE_MPI // unsigned char -template<> -void MPI_CLASS::call_maxScan( - const unsigned char *send, unsigned char *recv, int n ) const -{ - PROFILE_START( "maxScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_UNSIGNED_CHAR, MPI_MAX, communicator ); - PROFILE_STOP( "maxScan", profile_level ); +template <> +void MPI_CLASS::call_maxScan(const unsigned char *send, + unsigned char *recv, int n) const { + PROFILE_START("maxScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_UNSIGNED_CHAR, MPI_MAX, + communicator); + PROFILE_STOP("maxScan", profile_level); } // char -template<> -void MPI_CLASS::call_maxScan( const char *send, char *recv, int n ) const -{ - PROFILE_START( "maxScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_SIGNED_CHAR, MPI_MAX, communicator ); - PROFILE_STOP( "maxScan", profile_level ); +template <> +void MPI_CLASS::call_maxScan(const char *send, char *recv, int n) const { + PROFILE_START("maxScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_SIGNED_CHAR, MPI_MAX, + communicator); + PROFILE_STOP("maxScan", profile_level); } // unsigned int -template<> -void MPI_CLASS::call_maxScan( - const unsigned int *send, unsigned int *recv, int n ) const -{ - PROFILE_START( "maxScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_UNSIGNED, MPI_MAX, communicator ); - PROFILE_STOP( "maxScan", profile_level ); +template <> +void MPI_CLASS::call_maxScan(const unsigned int *send, + unsigned int *recv, int n) const { + PROFILE_START("maxScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_UNSIGNED, MPI_MAX, + communicator); + PROFILE_STOP("maxScan", profile_level); } // int -template<> -void MPI_CLASS::call_maxScan( const int *send, int *recv, int n ) const -{ - PROFILE_START( "maxScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_INT, MPI_MAX, communicator ); - PROFILE_STOP( "maxScan", profile_level ); +template <> +void MPI_CLASS::call_maxScan(const int *send, int *recv, int n) const { + PROFILE_START("maxScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_INT, MPI_MAX, communicator); + PROFILE_STOP("maxScan", profile_level); } // long int -template<> -void MPI_CLASS::call_maxScan( const long int *send, long int *recv, int n ) const -{ - PROFILE_START( "maxScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_LONG, MPI_MAX, communicator ); - PROFILE_STOP( "maxScan", profile_level ); +template <> +void MPI_CLASS::call_maxScan(const long int *send, long int *recv, + int n) const { + PROFILE_START("maxScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_LONG, MPI_MAX, communicator); + PROFILE_STOP("maxScan", profile_level); } // unsigned long int -template<> -void MPI_CLASS::call_maxScan( - const unsigned long int *send, unsigned long int *recv, int n ) const -{ - PROFILE_START( "maxScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_UNSIGNED_LONG, MPI_MAX, communicator ); - PROFILE_STOP( "maxScan", profile_level ); +template <> +void MPI_CLASS::call_maxScan(const unsigned long int *send, + unsigned long int *recv, + int n) const { + PROFILE_START("maxScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_UNSIGNED_LONG, MPI_MAX, + communicator); + PROFILE_STOP("maxScan", profile_level); } // size_t #ifdef USE_WINDOWS -template<> -void MPI_CLASS::call_maxScan( const size_t *send, size_t *recv, int n ) const -{ - MPI_ASSERT( MPI_SIZE_T != 0 ); - PROFILE_START( "maxScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_SIZE_T, MPI_MAX, communicator ); - PROFILE_STOP( "maxScan", profile_level ); +template <> +void MPI_CLASS::call_maxScan(const size_t *send, size_t *recv, + int n) const { + MPI_ASSERT(MPI_SIZE_T != 0); + PROFILE_START("maxScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_SIZE_T, MPI_MAX, communicator); + PROFILE_STOP("maxScan", profile_level); } #endif // float -template<> -void MPI_CLASS::call_maxScan( const float *send, float *recv, int n ) const -{ - PROFILE_START( "maxScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_INT, MPI_MAX, communicator ); - PROFILE_STOP( "maxScan", profile_level ); +template <> +void MPI_CLASS::call_maxScan(const float *send, float *recv, + int n) const { + PROFILE_START("maxScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_INT, MPI_MAX, communicator); + PROFILE_STOP("maxScan", profile_level); } // double -template<> -void MPI_CLASS::call_maxScan( const double *send, double *recv, int n ) const -{ - PROFILE_START( "maxScan", profile_level ); - MPI_Scan( (void *) send, (void *) recv, n, MPI_DOUBLE, MPI_MAX, communicator ); - PROFILE_STOP( "maxScan", profile_level ); +template <> +void MPI_CLASS::call_maxScan(const double *send, double *recv, + int n) const { + PROFILE_START("maxScan", profile_level); + MPI_Scan((void *)send, (void *)recv, n, MPI_DOUBLE, MPI_MAX, communicator); + PROFILE_STOP("maxScan", profile_level); } #endif - /************************************************************************ * Communicate ranks for communication * ************************************************************************/ -std::vector MPI_CLASS::commRanks( const std::vector &ranks ) const -{ +std::vector MPI_CLASS::commRanks(const std::vector &ranks) const { #ifdef USE_MPI // Get a byte array with the ranks to communicate auto data1 = new char[comm_size]; auto data2 = new char[comm_size]; - memset( data1, 0, comm_size ); - memset( data2, 0, comm_size ); - for ( auto &rank : ranks ) + memset(data1, 0, comm_size); + memset(data2, 0, comm_size); + for (auto &rank : ranks) data1[rank] = 1; - MPI_Alltoall( data1, 1, MPI_CHAR, data2, 1, MPI_CHAR, communicator ); + MPI_Alltoall(data1, 1, MPI_CHAR, data2, 1, MPI_CHAR, communicator); int N = 0; - for ( int i = 0; i < comm_size; i++ ) + for (int i = 0; i < comm_size; i++) N += data2[i]; std::vector ranks_out; - ranks_out.reserve( N ); - for ( int i = 0; i < comm_size; i++ ) { - if ( data2[i] ) - ranks_out.push_back( i ); + ranks_out.reserve(N); + for (int i = 0; i < comm_size; i++) { + if (data2[i]) + ranks_out.push_back(i); } delete[] data1; delete[] data2; @@ -3463,16 +3388,14 @@ std::vector MPI_CLASS::commRanks( const std::vector &ranks ) const #endif } - /************************************************************************ * Wait functions * ************************************************************************/ #ifdef USE_MPI -void MPI_CLASS::wait( MPI_Request request ) -{ - PROFILE_START( "wait", profile_level ); +void MPI_CLASS::wait(MPI_Request request) { + PROFILE_START("wait", profile_level); MPI_Status status; - MPI_Wait( &request, &status ); + MPI_Wait(&request, &status); /*int flag = 0; int err = MPI_Test( &request, &flag, &status ); MPI_ASSERT( err == MPI_SUCCESS ); // Check that the first call is valid @@ -3482,16 +3405,15 @@ void MPI_CLASS::wait( MPI_Request request ) // Check if the request has finished MPI_Test( &request, &flag, &status ); }*/ - PROFILE_STOP( "wait", profile_level ); + PROFILE_STOP("wait", profile_level); } -int MPI_CLASS::waitAny( int count, MPI_Request *request ) -{ - if ( count == 0 ) +int MPI_CLASS::waitAny(int count, MPI_Request *request) { + if (count == 0) return -1; - PROFILE_START( "waitAny", profile_level ); - int index = -1; + PROFILE_START("waitAny", profile_level); + int index = -1; auto status = new MPI_Status[count]; - MPI_Waitany( count, request, &index, status ); + MPI_Waitany(count, request, &index, status); /*int flag = 0; int err = MPI_Testany( count, request, &index, &flag, status ); MPI_ASSERT( err == MPI_SUCCESS ); // Check that the first call is valid @@ -3503,16 +3425,15 @@ int MPI_CLASS::waitAny( int count, MPI_Request *request ) } MPI_ASSERT( index >= 0 ); // Check that the index is valid*/ delete[] status; - PROFILE_STOP( "waitAny", profile_level ); + PROFILE_STOP("waitAny", profile_level); return index; } -void MPI_CLASS::waitAll( int count, MPI_Request *request ) -{ - if ( count == 0 ) +void MPI_CLASS::waitAll(int count, MPI_Request *request) { + if (count == 0) return; - PROFILE_START( "waitAll", profile_level ); + PROFILE_START("waitAll", profile_level); auto status = new MPI_Status[count]; - MPI_Waitall( count, request, status ); + MPI_Waitall(count, request, status); /*int flag = 0; int err = MPI_Testall( count, request, &flag, status ); MPI_ASSERT( err == MPI_SUCCESS ); // Check that the first call is valid @@ -3522,18 +3443,17 @@ void MPI_CLASS::waitAll( int count, MPI_Request *request ) // Check if the request has finished MPI_Testall( count, request, &flag, status ); }*/ - PROFILE_STOP( "waitAll", profile_level ); + PROFILE_STOP("waitAll", profile_level); delete[] status; } -std::vector MPI_CLASS::waitSome( int count, MPI_Request *request ) -{ - if ( count == 0 ) +std::vector MPI_CLASS::waitSome(int count, MPI_Request *request) { + if (count == 0) return std::vector(); - PROFILE_START( "waitSome", profile_level ); - std::vector indicies( count, -1 ); + PROFILE_START("waitSome", profile_level); + std::vector indicies(count, -1); auto *status = new MPI_Status[count]; int outcount = 0; - MPI_Waitsome( count, request, &outcount, indicies.data(), status ); + MPI_Waitsome(count, request, &outcount, indicies.data(), status); /*int err = MPI_Testsome( count, request, &outcount, &indicies[0], status ); MPI_ASSERT( err == MPI_SUCCESS ); // Check that the first call is valid MPI_ASSERT( outcount != MPI_UNDEFINED ); // Check that the first call is valid @@ -3543,132 +3463,125 @@ std::vector MPI_CLASS::waitSome( int count, MPI_Request *request ) // Check if the request has finished MPI_Testsome( count, request, &outcount, &indicies[0], status ); }*/ - indicies.resize( outcount ); + indicies.resize(outcount); delete[] status; - PROFILE_STOP( "waitSome", profile_level ); + PROFILE_STOP("waitSome", profile_level); return indicies; } #else -void MPI_CLASS::wait( MPI_Request request ) -{ - PROFILE_START( "wait", profile_level ); - while ( 1 ) { +void MPI_CLASS::wait(MPI_Request request) { + PROFILE_START("wait", profile_level); + while (1) { // Check if the request is in our list - if ( global_isendrecv_list.find( request ) == global_isendrecv_list.end() ) + if (global_isendrecv_list.find(request) == global_isendrecv_list.end()) break; // Put the current thread to sleep to allow other threads to run sched_yield(); } - PROFILE_STOP( "wait", profile_level ); + PROFILE_STOP("wait", profile_level); } -int MPI_CLASS::waitAny( int count, MPI_Request *request ) -{ - if ( count == 0 ) +int MPI_CLASS::waitAny(int count, MPI_Request *request) { + if (count == 0) return -1; - PROFILE_START( "waitAny", profile_level ); + PROFILE_START("waitAny", profile_level); int index = 0; - while ( 1 ) { + while (1) { // Check if the request is in our list bool found_any = false; - for ( int i = 0; i < count; i++ ) { - if ( global_isendrecv_list.find( request[i] ) == global_isendrecv_list.end() ) { + for (int i = 0; i < count; i++) { + if (global_isendrecv_list.find(request[i]) == + global_isendrecv_list.end()) { found_any = true; - index = i; + index = i; } } - if ( found_any ) + if (found_any) break; // Put the current thread to sleep to allow other threads to run sched_yield(); } - PROFILE_STOP( "waitAny", profile_level ); + PROFILE_STOP("waitAny", profile_level); return index; } -void MPI_CLASS::waitAll( int count, MPI_Request *request ) -{ - if ( count == 0 ) +void MPI_CLASS::waitAll(int count, MPI_Request *request) { + if (count == 0) return; - PROFILE_START( "waitAll", profile_level ); - while ( 1 ) { + PROFILE_START("waitAll", profile_level); + while (1) { // Check if the request is in our list bool found_all = true; - for ( int i = 0; i < count; i++ ) { - if ( global_isendrecv_list.find( request[i] ) != global_isendrecv_list.end() ) + for (int i = 0; i < count; i++) { + if (global_isendrecv_list.find(request[i]) != + global_isendrecv_list.end()) found_all = false; } - if ( found_all ) + if (found_all) break; // Put the current thread to sleep to allow other threads to run sched_yield(); } - PROFILE_STOP( "waitAll", profile_level ); + PROFILE_STOP("waitAll", profile_level); } -std::vector MPI_CLASS::waitSome( int count, MPI_Request *request ) -{ - if ( count == 0 ) +std::vector MPI_CLASS::waitSome(int count, MPI_Request *request) { + if (count == 0) return std::vector(); - PROFILE_START( "waitSome", profile_level ); + PROFILE_START("waitSome", profile_level); std::vector indicies; - while ( 1 ) { + while (1) { // Check if the request is in our list - for ( int i = 0; i < count; i++ ) { - if ( global_isendrecv_list.find( request[i] ) == global_isendrecv_list.end() ) - indicies.push_back( i ); + for (int i = 0; i < count; i++) { + if (global_isendrecv_list.find(request[i]) == + global_isendrecv_list.end()) + indicies.push_back(i); } - if ( !indicies.empty() ) + if (!indicies.empty()) break; // Put the current thread to sleep to allow other threads to run sched_yield(); } - PROFILE_STOP( "waitSome", profile_level ); + PROFILE_STOP("waitSome", profile_level); return indicies; } #endif - /************************************************************************ * Probe functions * ************************************************************************/ #ifdef USE_MPI -int MPI_CLASS::Iprobe( int source, int tag ) const -{ - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - MPI_INSIST( tag >= 0, "tag must be >= 0" ); +int MPI_CLASS::Iprobe(int source, int tag) const { + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + MPI_INSIST(tag >= 0, "tag must be >= 0"); MPI_Status status; int flag = 0; - MPI_Iprobe( source, tag, communicator, &flag, &status ); - if ( flag == 0 ) + MPI_Iprobe(source, tag, communicator, &flag, &status); + if (flag == 0) return -1; int count; - MPI_Get_count( &status, MPI_BYTE, &count ); - MPI_ASSERT( count >= 0 ); + MPI_Get_count(&status, MPI_BYTE, &count); + MPI_ASSERT(count >= 0); return count; } -int MPI_CLASS::probe( int source, int tag ) const -{ - MPI_INSIST( tag <= d_maxTag, "Maximum tag value exceeded" ); - MPI_INSIST( tag >= 0, "tag must be >= 0" ); +int MPI_CLASS::probe(int source, int tag) const { + MPI_INSIST(tag <= d_maxTag, "Maximum tag value exceeded"); + MPI_INSIST(tag >= 0, "tag must be >= 0"); MPI_Status status; - MPI_Probe( source, tag, communicator, &status ); + MPI_Probe(source, tag, communicator, &status); int count; - MPI_Get_count( &status, MPI_BYTE, &count ); - MPI_ASSERT( count >= 0 ); + MPI_Get_count(&status, MPI_BYTE, &count); + MPI_ASSERT(count >= 0); return count; } #else -int MPI_CLASS::Iprobe( int, int ) const -{ - MPI_ERROR( "Not implimented for serial codes (Iprobe)" ); +int MPI_CLASS::Iprobe(int, int) const { + MPI_ERROR("Not implimented for serial codes (Iprobe)"); return 0; } -int MPI_CLASS::probe( int, int ) const -{ - MPI_ERROR( "Not implimented for serial codes (probe)" ); +int MPI_CLASS::probe(int, int) const { + MPI_ERROR("Not implimented for serial codes (probe)"); return 0; } #endif - /************************************************************************ * Timer functions * ************************************************************************/ @@ -3676,135 +3589,128 @@ int MPI_CLASS::probe( int, int ) const double MPI_CLASS::time() { return MPI_Wtime(); } double MPI_CLASS::tick() { return MPI_Wtick(); } #else -double MPI_CLASS::time() -{ - auto t = std::chrono::system_clock::now(); - auto ns = std::chrono::duration_cast( t.time_since_epoch() ); +double MPI_CLASS::time() { + auto t = std::chrono::system_clock::now(); + auto ns = std::chrono::duration_cast( + t.time_since_epoch()); return 1e-9 * ns.count(); } -double MPI_CLASS::tick() -{ +double MPI_CLASS::tick() { auto period = std::chrono::system_clock::period(); - return static_cast( period.num ) / static_cast( period.den ); + return static_cast(period.num) / static_cast(period.den); } #endif - /************************************************************************ * Serialize a block of code across MPI processes * ************************************************************************/ -void MPI_CLASS::serializeStart() -{ +void MPI_CLASS::serializeStart() { #ifdef USE_MPI using namespace std::chrono_literals; - if ( comm_rank == 0 ) { + if (comm_rank == 0) { // Start rank 0 immediately } else { // Wait for a message from the previous rank MPI_Request request; MPI_Status status; int flag = false, buf = 0; - MPI_Irecv( &buf, 1, MPI_INT, comm_rank - 1, 5627, MPI_COMM_WORLD, &request ); - while ( !flag ) { - MPI_Test( &request, &flag, &status ); - std::this_thread::sleep_for( 50ms ); + MPI_Irecv(&buf, 1, MPI_INT, comm_rank - 1, 5627, MPI_COMM_WORLD, + &request); + while (!flag) { + MPI_Test(&request, &flag, &status); + std::this_thread::sleep_for(50ms); } } #endif } -void MPI_CLASS::serializeStop() -{ +void MPI_CLASS::serializeStop() { #ifdef USE_MPI using namespace std::chrono_literals; - if ( comm_rank < comm_size - 1 ) { + if (comm_rank < comm_size - 1) { // Send flag to next rank - MPI_Send( &comm_rank, 1, MPI_INT, comm_rank + 1, 5627, MPI_COMM_WORLD ); + MPI_Send(&comm_rank, 1, MPI_INT, comm_rank + 1, 5627, MPI_COMM_WORLD); // Wait for final finished flag int flag = false, buf = 0; MPI_Request request; MPI_Status status; - MPI_Irecv( &buf, 1, MPI_INT, comm_size - 1, 5627, MPI_COMM_WORLD, &request ); - while ( !flag ) { - MPI_Test( &request, &flag, &status ); - std::this_thread::sleep_for( 50ms ); + MPI_Irecv(&buf, 1, MPI_INT, comm_size - 1, 5627, MPI_COMM_WORLD, + &request); + while (!flag) { + MPI_Test(&request, &flag, &status); + std::this_thread::sleep_for(50ms); } } else { // Send final flag to all ranks - for ( int i = 0; i < comm_size - 1; i++ ) - MPI_Send( &comm_rank, 1, MPI_INT, i, 5627, MPI_COMM_WORLD ); + for (int i = 0; i < comm_size - 1; i++) + MPI_Send(&comm_rank, 1, MPI_INT, i, 5627, MPI_COMM_WORLD); } #endif } - /**************************************************************************** * Function to start/stop MPI * ****************************************************************************/ #ifdef USE_MPI static bool called_MPI_Init = false; #endif -bool MPI_CLASS::MPI_Active() -{ +bool MPI_CLASS::MPI_Active() { #ifdef USE_MPI int MPI_initialized, MPI_finialized; - MPI_Initialized( &MPI_initialized ); - MPI_Finalized( &MPI_finialized ); + MPI_Initialized(&MPI_initialized); + MPI_Finalized(&MPI_finialized); return MPI_initialized != 0 && MPI_finialized == 0; #else return false; #endif } -void MPI_CLASS::start_MPI( int argc, char *argv[], int profile_level ) -{ - changeProfileLevel( profile_level ); - NULL_USE( argc ); - NULL_USE( argv ); +void MPI_CLASS::start_MPI(int argc, char *argv[], int profile_level) { + changeProfileLevel(profile_level); + NULL_USE(argc); + NULL_USE(argv); #ifdef USE_MPI - if ( MPI_Active() ) { + if (MPI_Active()) { called_MPI_Init = false; } else { int provided; - int result = MPI_Init_thread( &argc, &argv, MPI_THREAD_MULTIPLE, &provided ); - if ( result != MPI_SUCCESS ) - MPI_ERROR( "Unable to initialize MPI" ); - if ( provided < MPI_THREAD_MULTIPLE ) - std::cerr << "Warning: Failed to start MPI with MPI_THREAD_MULTIPLE\n"; + int result = + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); + if (result != MPI_SUCCESS) + MPI_ERROR("Unable to initialize MPI"); + if (provided < MPI_THREAD_MULTIPLE) + std::cerr + << "Warning: Failed to start MPI with MPI_THREAD_MULTIPLE\n"; called_MPI_Init = true; } #endif } -void MPI_CLASS::stop_MPI() -{ +void MPI_CLASS::stop_MPI() { #ifdef USE_MPI int finalized; - MPI_Finalized( &finalized ); - if ( called_MPI_Init && !finalized ) { - MPI_Barrier( MPI_COMM_WORLD ); + MPI_Finalized(&finalized); + if (called_MPI_Init && !finalized) { + MPI_Barrier(MPI_COMM_WORLD); MPI_Finalize(); called_MPI_Init = true; } #endif } - /**************************************************************************** * Function to perform load balancing * ****************************************************************************/ -MPI MPI::loadBalance( double local, std::vector work ) -{ - MPI_ASSERT( (int) work.size() == getSize() ); - auto perf = allGather( local ); - std::vector I( work.size() ); - for ( size_t i = 0; i < work.size(); i++ ) +MPI MPI::loadBalance(double local, std::vector work) { + MPI_ASSERT((int)work.size() == getSize()); + auto perf = allGather(local); + std::vector I(work.size()); + for (size_t i = 0; i < work.size(); i++) I[i] = i; auto J = I; - quicksort( perf, I ); - quicksort( work, J ); - std::vector key( work.size() ); - for ( size_t i = 0; i < work.size(); i++ ) + quicksort(perf, I); + quicksort(work, J); + std::vector key(work.size()); + for (size_t i = 0; i < work.size(); i++) key[J[i]] = I[i]; - return split( 0, key[getRank()] ); + return split(0, key[getRank()]); } - } // namespace Utilities diff --git a/common/MPI.h b/common/MPI.h index 6e05135d..f8849aaf 100644 --- a/common/MPI.h +++ b/common/MPI.h @@ -22,7 +22,6 @@ redistribution is prohibited. #ifndef included_LBPM_MPI #define included_LBPM_MPI - #include #include #include @@ -31,7 +30,6 @@ redistribution is prohibited. #include #include - // Include mpi.h (or define MPI objects) // clang-format off #ifdef USE_MPI @@ -48,10 +46,8 @@ redistribution is prohibited. #endif // clang-format on - namespace Utilities { - /** * \class MPI * @@ -69,8 +65,7 @@ namespace Utilities { * succeed provided that the size of the data type object is a fixed size on * all processors. sizeof(type) must be the same for all elements and processors. */ -class MPI final -{ +class MPI final { public: enum class ThreadSupport : int { SINGLE, FUNNELED, SERIALIZED, MULTIPLE }; @@ -87,11 +82,9 @@ public: // Constructors */ MPI(); - //! Empty destructor ~MPI(); - /** * \brief Constructor from existing MPI communicator * \details This constructor creates a new communicator from an existing MPI communicator. @@ -104,8 +97,7 @@ public: // Constructors * \param manage Do we want to manage the comm (free the MPI_Comm when this object leaves * scope) */ - MPI( MPI_Comm comm, bool manage = false ); - + MPI(MPI_Comm comm, bool manage = false); /** * \brief Constructor from existing communicator @@ -113,30 +105,26 @@ public: // Constructors * This does not create a new internal MPI_Comm, but uses the existing comm. * \param comm Existing communicator */ - MPI( const MPI &comm ); - + MPI(const MPI &comm); /*! * Move constructor * @param rhs Communicator to copy */ - MPI( MPI &&rhs ); - + MPI(MPI &&rhs); /** * \brief Assignment operator * \details This operator overloads the assignment to correctly copy an communicator * \param comm Existing MPI object */ - MPI &operator=( const MPI &comm ); - + MPI &operator=(const MPI &comm); /*! * Move assignment operator * @param rhs Communicator to copy */ - MPI &operator=( MPI &&rhs ); - + MPI &operator=(MPI &&rhs); /** * \brief Reset the object @@ -144,7 +132,6 @@ public: // Constructors */ void reset(); - public: // Member functions /** * \brief Get the node name @@ -153,18 +140,14 @@ public: // Member functions */ static std::string getNodeName(); - //! Function to return the number of processors available static int getNumberOfProcessors(); - //! Function to return the affinity of the current process static std::vector getProcessAffinity(); - //! Function to set the affinity of the current process - static void setProcessAffinity( const std::vector &procs ); - + static void setProcessAffinity(const std::vector &procs); /** * \brief Load balance the processes within a node @@ -189,22 +172,21 @@ public: // Member functions * processors). * */ - static void balanceProcesses( const MPI &comm = MPI( MPI_COMM_WORLD ), const int method = 1, - const std::vector &procs = std::vector(), const int N_min = 1, - const int N_max = -1 ); - + static void + balanceProcesses(const MPI &comm = MPI(MPI_COMM_WORLD), + const int method = 1, + const std::vector &procs = std::vector(), + const int N_min = 1, const int N_max = -1); //! Query the level of thread support static ThreadSupport queryThreadSupport(); - /** * \brief Generate a random number * \details This generates a random number that is consistent across the comm */ size_t rand() const; - /** * \brief Split an existing communicator * \details This creates a new communicator by splitting an existing communicator. @@ -222,8 +204,7 @@ public: // Member functions * have the relative rank order as they did in their parent group. (See * MPI_Comm_split) */ - MPI split( int color, int key = -1 ) const; - + MPI split(int color, int key = -1) const; /** * \brief Split an existing communicator by node @@ -240,8 +221,7 @@ public: // Member functions * have the relative rank order as they did in their parent group. (See * MPI_Comm_split) */ - MPI splitByNode( int key = -1 ) const; - + MPI splitByNode(int key = -1) const; /** * \brief Duplicate an existing communicator @@ -253,7 +233,6 @@ public: // Member functions */ MPI dup() const; - /** * \brief Create a communicator from the intersection of two communicators * \details This creates a new communicator by intersecting two existing communicators. @@ -265,15 +244,13 @@ public: // Member functions * The communicators partially overlap. This will require communication on the first * communicator. */ - static MPI intersect( const MPI &comm1, const MPI &comm2 ); - + static MPI intersect(const MPI &comm1, const MPI &comm2); /** * Check if the current communicator is NULL */ bool isNull() const { return d_isNull; } - /** * \brief Return the global ranks for the comm * \details This returns a vector which contains the global ranks for each @@ -283,7 +260,6 @@ public: // Member functions */ std::vector globalRanks() const; - /** * Get the current MPI communicator. * Note: The underlying MPI_Comm object may be free'd by the object when it is no @@ -294,15 +270,13 @@ public: // Member functions */ const MPI_Comm &getCommunicator() const { return communicator; } - /** * \brief Overload operator == * \details Overload operator comm1 == comm2. Two MPI objects are == if they share the same * communicator. * Note: this is a local operation. */ - bool operator==( const MPI & ) const; - + bool operator==(const MPI &) const; /** * \brief Overload operator != @@ -310,8 +284,7 @@ public: // Member functions * do not share the same communicator. * Note: this is a local operation. */ - bool operator!=( const MPI & ) const; - + bool operator!=(const MPI &) const; /** * \brief Overload operator < @@ -324,8 +297,7 @@ public: // Member functions * Additionally, all processors on the first object MUST call this routine and will be * synchronized through this call (there is an internalallReduce). */ - bool operator<( const MPI & ) const; - + bool operator<(const MPI &) const; /** * \brief Overload operator <= @@ -337,8 +309,7 @@ public: // Member functions * call this routine and will be synchronized through this call (there is an internal * allReduce). */ - bool operator<=( const MPI & ) const; - + bool operator<=(const MPI &) const; /** * \brief Overload operator > @@ -351,8 +322,7 @@ public: // Member functions * Additionally, all processors on the first object MUST call this routine and will be * synchronized through this call (there is an internal allReduce). */ - bool operator>( const MPI & ) const; - + bool operator>(const MPI &) const; /** * \brief Overload operator >= @@ -365,8 +335,7 @@ public: // Member functions * Additionally, all processors on the first object MUST call this routine and will be * synchronized through this call (there is an internal allReduce). */ - bool operator>=( const MPI & ) const; - + bool operator>=(const MPI &) const; /** * \brief Compare to another communicator @@ -376,8 +345,7 @@ public: // Member functions * 4 if different contexts but similar groups, and 0 otherwise. * Note: this is a local operation. */ - int compare( const MPI & ) const; - + int compare(const MPI &) const; /** * Return the processor rank (identifier) from 0 through the number of @@ -385,19 +353,16 @@ public: // Member functions */ int getRank() const { return comm_rank; } - /** * Return the number of processors. */ int getSize() const { return comm_size; } - /** * Return the maximum tag */ int maxTag() const { return d_maxTag; } - /** * \brief Return a new tag * \details This routine will return an unused tag for communication. @@ -406,7 +371,6 @@ public: // Member functions */ int newTag(); - /** * Call MPI_Abort or exit depending on whether running with one or more * processes and value set by function above, if called. The default is @@ -416,15 +380,13 @@ public: // Member functions */ void abort() const; - /** * Set boolean flag indicating whether exit or abort is called when running * with one processor. Calling this function influences the behavior of * calls to abort(). By default, the flag is true meaning that * abort() will be called. Passing false means exit(-1) will be called. */ - void setCallAbortInSerialInsteadOfExit( bool flag = true ); - + void setCallAbortInSerialInsteadOfExit(bool flag = true); /** * \brief Boolean all reduce @@ -432,8 +394,7 @@ public: // Member functions * It returns true iff all processor are true; * \param value The input value for the all reduce */ - bool allReduce( const bool value ) const; - + bool allReduce(const bool value) const; /** * \brief Boolean any reduce @@ -441,8 +402,7 @@ public: // Member functions * It returns true if any processor is true; * \param value The input value for the all reduce */ - bool anyReduce( const bool value ) const; - + bool anyReduce(const bool value) const; /** * \brief Sum Reduce @@ -450,9 +410,7 @@ public: // Member functions * It returns the sum across all processors; * \param value The input value for the all reduce */ - template - type sumReduce( const type value ) const; - + template type sumReduce(const type value) const; /** * \brief Sum Reduce @@ -462,9 +420,7 @@ public: // Member functions * \param x The input/output array for the reduce * \param n The number of values in the array (must match on all nodes) */ - template - void sumReduce( type *x, const int n = 1 ) const; - + template void sumReduce(type *x, const int n = 1) const; /** * \brief Sum Reduce @@ -475,9 +431,8 @@ public: // Member functions * \param y The output array for the reduce * \param n The number of values in the array (must match on all nodes) */ - template - void sumReduce( const type *x, type *y, const int n = 1 ) const; - + template + void sumReduce(const type *x, type *y, const int n = 1) const; /** * \brief Min Reduce @@ -485,9 +440,7 @@ public: // Member functions * It returns the minimum value across all processors; * \param value The input value for the all reduce */ - template - type minReduce( const type value ) const; - + template type minReduce(const type value) const; /** * \brief Sum Reduce @@ -503,9 +456,8 @@ public: // Member functions * \param rank_of_min Optional array indicating the rank of the processor containing the * minimum value */ - template - void minReduce( type *x, const int n = 1, int *rank_of_min = nullptr ) const; - + template + void minReduce(type *x, const int n = 1, int *rank_of_min = nullptr) const; /** * \brief Sum Reduce @@ -522,9 +474,9 @@ public: // Member functions * \param rank_of_min Optional array indicating the rank of the processor containing the * minimum value */ - template - void minReduce( const type *x, type *y, const int n = 1, int *rank_of_min = nullptr ) const; - + template + void minReduce(const type *x, type *y, const int n = 1, + int *rank_of_min = nullptr) const; /** * \brief Max Reduce @@ -532,9 +484,7 @@ public: // Member functions * It returns the maximum value across all processors; * \param value The input value for the all reduce */ - template - type maxReduce( const type value ) const; - + template type maxReduce(const type value) const; /** * \brief Sum Reduce @@ -550,9 +500,8 @@ public: // Member functions * \param rank_of_max Optional array indicating the rank of the processor containing the * minimum value */ - template - void maxReduce( type *x, const int n = 1, int *rank_of_max = nullptr ) const; - + template + void maxReduce(type *x, const int n = 1, int *rank_of_max = nullptr) const; /** * \brief Sum Reduce @@ -569,9 +518,9 @@ public: // Member functions * \param rank_of_max Optional array indicating the rank of the processor containing the * minimum value */ - template - void maxReduce( const type *x, type *y, const int n = 1, int *rank_of_max = nullptr ) const; - + template + void maxReduce(const type *x, type *y, const int n = 1, + int *rank_of_max = nullptr) const; /** * \brief Scan Sum Reduce @@ -581,9 +530,8 @@ public: // Member functions * \param y The output array for the scan * \param n The number of values in the array (must match on all nodes) */ - template - void sumScan( const type *x, type *y, const int n = 1 ) const; - + template + void sumScan(const type *x, type *y, const int n = 1) const; /** * \brief Scan Min Reduce @@ -593,9 +541,8 @@ public: // Member functions * \param y The output array for the scan * \param n The number of values in the array (must match on all nodes) */ - template - void minScan( const type *x, type *y, const int n = 1 ) const; - + template + void minScan(const type *x, type *y, const int n = 1) const; /** * \brief Scan Max Reduce @@ -605,9 +552,8 @@ public: // Member functions * \param y The output array for the scan * \param n The number of values in the array (must match on all nodes) */ - template - void maxScan( const type *x, type *y, const int n = 1 ) const; - + template + void maxScan(const type *x, type *y, const int n = 1) const; /** * \brief Broadcast @@ -615,9 +561,7 @@ public: // Member functions * \param value The input value for the broadcast. * \param root The processor performing the broadcast */ - template - type bcast( const type &value, const int root ) const; - + template type bcast(const type &value, const int root) const; /** * \brief Broadcast @@ -626,16 +570,14 @@ public: // Member functions * \param n The number of values in the array (must match on all nodes) * \param root The processor performing the broadcast */ - template - void bcast( type *value, const int n, const int root ) const; - + template + void bcast(type *value, const int n, const int root) const; /** * Perform a global barrier across all processors. */ void barrier() const; - /*! * @brief This function sends an MPI message with an array to another processor. * @@ -652,9 +594,9 @@ public: // Member functions * to be sent with this message. Default tag is 0. * The matching recv must share this tag. */ - template - void send( const type *buf, const int length, const int recv, int tag = 0 ) const; - + template + void send(const type *buf, const int length, const int recv, + int tag = 0) const; /*! * @brief This function sends an MPI message with an array of bytes @@ -669,8 +611,8 @@ public: // Member functions * to be sent with this message. Default tag is 0. * The matching recv must share this tag. */ - void sendBytes( const void *buf, const int N_bytes, const int recv, int tag = 0 ) const; - + void sendBytes(const void *buf, const int N_bytes, const int recv, + int tag = 0) const; /*! * @brief This function sends an MPI message with an array @@ -684,10 +626,9 @@ public: // Member functions * @param tag Integer argument specifying an integer tag * to be sent with this message. */ - template - MPI_Request Isend( - const type *buf, const int length, const int recv_proc, const int tag ) const; - + template + MPI_Request Isend(const type *buf, const int length, const int recv_proc, + const int tag) const; /*! * @brief This function sends an MPI message with an array of bytes @@ -701,9 +642,8 @@ public: // Member functions * @param tag Integer argument specifying an integer tag * to be sent with this message. */ - MPI_Request IsendBytes( - const void *buf, const int N_bytes, const int recv_proc, const int tag ) const; - + MPI_Request IsendBytes(const void *buf, const int N_bytes, + const int recv_proc, const int tag) const; /*! * @brief This function receives an MPI message with a data @@ -721,14 +661,12 @@ public: // Member functions * @param tag Optional integer argument specifying a tag which must be matched * by the tag of the incoming message. Default tag is 0. */ - template - inline void recv( type *buf, int length, const int send, int tag ) const - { + template + inline void recv(type *buf, int length, const int send, int tag) const { int length2 = length; - recv( buf, length2, send, false, tag ); + recv(buf, length2, send, false, tag); } - /*! * @brief This function receives an MPI message with a data * array from another processor. @@ -748,9 +686,9 @@ public: // Member functions * @param tag Optional integer argument specifying a tag which must be matched * by the tag of the incoming message. Default tag is 0. */ - template - void recv( type *buf, int &length, const int send, const bool get_length, int tag ) const; - + template + void recv(type *buf, int &length, const int send, const bool get_length, + int tag) const; /*! * @brief This function receives an MPI message with an array of @@ -765,8 +703,7 @@ public: // Member functions * must be matched by the tag of the incoming message. Default * tag is 0. */ - void recvBytes( void *buf, int &N_bytes, const int send, int tag = 0 ) const; - + void recvBytes(void *buf, int &N_bytes, const int send, int tag = 0) const; /*! * @brief This function receives an MPI message with a data @@ -778,9 +715,9 @@ public: // Member functions * @param tag Optional integer argument specifying a tag which must * be matched by the tag of the incoming message. */ - template - MPI_Request Irecv( type *buf, const int length, const int send_proc, const int tag ) const; - + template + MPI_Request Irecv(type *buf, const int length, const int send_proc, + const int tag) const; /*! * @brief This function receives an MPI message with an array of @@ -794,35 +731,30 @@ public: // Member functions * @param tag Integer argument specifying a tag which must * be matched by the tag of the incoming message. */ - MPI_Request IrecvBytes( - void *buf, const int N_bytes, const int send_proc, const int tag ) const; - + MPI_Request IrecvBytes(void *buf, const int N_bytes, const int send_proc, + const int tag) const; /*! * @brief This function sends and recieves data using a blocking call */ - template - void sendrecv( const type *sendbuf, int sendcount, int dest, int sendtag, type *recvbuf, - int recvcount, int source, int recvtag ) const; - + template + void sendrecv(const type *sendbuf, int sendcount, int dest, int sendtag, + type *recvbuf, int recvcount, int source, int recvtag) const; /*! * Each processor sends every other processor a single value. * @param[in] x Input value for allGather * @return Output array for allGather */ - template - std::vector allGather( const type &x ) const; - + template std::vector allGather(const type &x) const; /*! * Each processor sends every other processor an array * @param[in] x Input array for allGather * @return Output array for allGather */ - template - std::vector allGather( const std::vector &x ) const; - + template + std::vector allGather(const std::vector &x) const; /*! * Each processor sends every other processor a single value. @@ -832,9 +764,7 @@ public: // Member functions * @param x_out Output array for allGather (must be preallocated to the size of the * communicator) */ - template - void allGather( const type &x_in, type *x_out ) const; - + template void allGather(const type &x_in, type *x_out) const; /*! * Each processor sends an array of data to all other processors. @@ -861,27 +791,24 @@ public: // Member functions * internally * and the sizes and displacements will be returned (if desired). */ - template - int allGather( const type *send_data, const int send_cnt, type *recv_data, - int *recv_cnt = nullptr, int *recv_disp = nullptr, bool known_recv = false ) const; - + template + int allGather(const type *send_data, const int send_cnt, type *recv_data, + int *recv_cnt = nullptr, int *recv_disp = nullptr, + bool known_recv = false) const; /*! * This function combines sets from different processors to create a single master set * @param set Input/Output std::set for the gather. */ - template - void setGather( std::set &set ) const; - + template void setGather(std::set &set) const; /*! * This function combines std::maps from different processors to create a single master std::map * If two or more ranks share the same key, the lowest rank will be used * @param map Input/Output std::map for the gather. */ - template - void mapGather( std::map &map ) const; - + template + void mapGather(std::map &map) const; /*! * Each processor sends an array of n values to each processor. @@ -894,9 +821,8 @@ public: // Member functions * @param send_data Input array (nxN) * @param recv_data Output array of received values (nxN) */ - template - void allToAll( const int n, const type *send_data, type *recv_data ) const; - + template + void allToAll(const int n, const type *send_data, type *recv_data) const; /*! * Each processor sends an array of data to the different processors. @@ -926,11 +852,11 @@ public: // Member functions * internally * and the sizes and displacements will be returned (if desired). */ - template - int allToAll( const type *send_data, const int send_cnt[], const int send_disp[], - type *recv_data, int *recv_cnt = nullptr, int *recv_disp = nullptr, - bool known_recv = false ) const; - + template + int allToAll(const type *send_data, const int send_cnt[], + const int send_disp[], type *recv_data, + int *recv_cnt = nullptr, int *recv_disp = nullptr, + bool known_recv = false) const; /*! * \brief Send a list of proccesor ids to communicate @@ -942,8 +868,7 @@ public: // Member functions * \param ranks List of ranks that the current rank wants to communicate with * \return List of ranks that want to communicate with the current processor */ - std::vector commRanks( const std::vector &ranks ) const; - + std::vector commRanks(const std::vector &ranks) const; /*! * \brief Wait for a communication to finish @@ -951,8 +876,7 @@ public: // Member functions * Note: this does not require a communicator. * \param request Communication request to wait for (returned for Isend or Irecv) */ - static void wait( MPI_Request request ); - + static void wait(MPI_Request request); /*! * \brief Wait for any communication to finish. @@ -962,8 +886,7 @@ public: // Member functions * \param count Number of communications to check * \param request Array of communication requests to wait for (returned for Isend or Irecv) */ - static int waitAny( int count, MPI_Request *request ); - + static int waitAny(int count, MPI_Request *request); /*! * \brief Wait for all communications to finish. @@ -972,8 +895,7 @@ public: // Member functions * \param count Number of communications to check * \param request Array of communication requests to wait for (returned for Isend or Irecv) */ - static void waitAll( int count, MPI_Request *request ); - + static void waitAll(int count, MPI_Request *request); /*! * \brief Wait for some communications to finish. @@ -983,8 +905,7 @@ public: // Member functions * \param count Number of communications to check * \param request Array of communication requests to wait for (returned for Isend or Irecv) */ - static std::vector waitSome( int count, MPI_Request *request ); - + static std::vector waitSome(int count, MPI_Request *request); /*! * \brief Nonblocking test for a message @@ -995,8 +916,7 @@ public: // Member functions * \param source source rank (-1: any source) * \param tag tag (-1: any tag) */ - int Iprobe( int source = -1, int tag = -1 ) const; - + int Iprobe(int source = -1, int tag = -1) const; /*! * \brief Blocking test for a message @@ -1006,8 +926,7 @@ public: // Member functions * \param source source rank (-1: any source) * \param tag tag (-1: any tag) */ - int probe( int source = -1, int tag = -1 ) const; - + int probe(int source = -1, int tag = -1) const; /*! * \brief Start a serial region @@ -1018,14 +937,12 @@ public: // Member functions */ void serializeStart(); - /*! * \brief Stop a serial region * \details Stop a serial region. See serializeStart for more information. */ void serializeStop(); - /*! * \brief Elapsed time * \details This function returns the elapsed time on the calling processor @@ -1036,21 +953,18 @@ public: // Member functions */ static double time(); - /*! * \brief Timer resolution * \details This function returns the timer resolution used by "time" */ static double tick(); - /*! * \brief Change the level of the internal timers * \details This function changes the level of the timers used to profile MPI * \param level New level of the timers */ - static void changeProfileLevel( int level ) { profile_level = level; } - + static void changeProfileLevel(int level) { profile_level = level; } //! Return the total number of MPI_Comm objects that have been created static size_t MPI_Comm_created() { return N_MPI_Comm_created; } @@ -1068,51 +982,51 @@ public: // Member functions static bool MPI_Active(); //! Start MPI - static void start_MPI( int argc_in, char *argv_in[], int profile_level = 0 ); + static void start_MPI(int argc_in, char *argv_in[], int profile_level = 0); //! Stop MPI static void stop_MPI(); - /*! * \brief Load balance * \details This function will return a new communicator in which the ranks match * the performance and the work load. */ - MPI loadBalance( double localPerformance, std::vector work ); + MPI loadBalance(double localPerformance, std::vector work); private: // Private helper functions for templated MPI operations; - template - void call_sumReduce( type *x, const int n = 1 ) const; - template - void call_sumReduce( const type *x, type *y, const int n = 1 ) const; - template - void call_minReduce( type *x, const int n = 1, int *rank_of_min = nullptr ) const; - template - void call_minReduce( - const type *x, type *y, const int n = 1, int *rank_of_min = nullptr ) const; - template - void call_maxReduce( type *x, const int n = 1, int *rank_of_max = nullptr ) const; - template - void call_maxReduce( - const type *x, type *y, const int n = 1, int *rank_of_max = nullptr ) const; - template - void call_bcast( type *x, const int n, const int root ) const; - template - void call_allGather( const type &x_in, type *x_out ) const; - template - void call_allGather( - const type *x_in, int size_in, type *x_out, int *size_out, int *disp_out ) const; - template - void call_sumScan( const type *x, type *y, int n = 1 ) const; - template - void call_minScan( const type *x, type *y, int n = 1 ) const; - template - void call_maxScan( const type *x, type *y, int n = 1 ) const; - template - void call_allToAll( const type *send_data, const int send_cnt[], const int send_disp[], - type *recv_data, const int *recv_cnt, const int *recv_disp ) const; - + template void call_sumReduce(type *x, const int n = 1) const; + template + void call_sumReduce(const type *x, type *y, const int n = 1) const; + template + void call_minReduce(type *x, const int n = 1, + int *rank_of_min = nullptr) const; + template + void call_minReduce(const type *x, type *y, const int n = 1, + int *rank_of_min = nullptr) const; + template + void call_maxReduce(type *x, const int n = 1, + int *rank_of_max = nullptr) const; + template + void call_maxReduce(const type *x, type *y, const int n = 1, + int *rank_of_max = nullptr) const; + template + void call_bcast(type *x, const int n, const int root) const; + template + void call_allGather(const type &x_in, type *x_out) const; + template + void call_allGather(const type *x_in, int size_in, type *x_out, + int *size_out, int *disp_out) const; + template + void call_sumScan(const type *x, type *y, int n = 1) const; + template + void call_minScan(const type *x, type *y, int n = 1) const; + template + void call_maxScan(const type *x, type *y, int n = 1) const; + template + void call_allToAll(const type *send_data, const int send_cnt[], + const int send_disp[], type *recv_data, + const int *recv_cnt, const int *recv_disp) const; private: // data members // The internal MPI communicator @@ -1157,14 +1071,11 @@ private: // data members static volatile unsigned int N_MPI_Comm_destroyed; }; - } // namespace Utilities - // Include the default instantiations // \cond HIDDEN_SYMBOLS #include "common/MPI.I" // \endcond - #endif diff --git a/common/ReadMicroCT.cpp b/common/ReadMicroCT.cpp index 2209e712..2fc2310c 100644 --- a/common/ReadMicroCT.cpp +++ b/common/ReadMicroCT.cpp @@ -5,112 +5,108 @@ #include - // Read a file into memory -std::vector readFile( const std::string& filename ) -{ - auto fid = fopen( filename.c_str(), "rb" ); - INSIST( fid, "File does not exist: " + filename ); - fseek( fid, 0, SEEK_END ); +std::vector readFile(const std::string &filename) { + auto fid = fopen(filename.c_str(), "rb"); + INSIST(fid, "File does not exist: " + filename); + fseek(fid, 0, SEEK_END); size_t bytes = ftell(fid); - fseek( fid, 0, SEEK_SET ); - std::vector data( bytes ); - size_t bytes2 = fread( data.data(), 1, bytes, fid ); - ASSERT( bytes == bytes2 ); - fclose( fid ); + fseek(fid, 0, SEEK_SET); + std::vector data(bytes); + size_t bytes2 = fread(data.data(), 1, bytes, fid); + ASSERT(bytes == bytes2); + fclose(fid); return data; } - // Decompress a gzip buffer -std::vector gunzip( const std::vector& in ) -{ +std::vector gunzip(const std::vector &in) { z_stream stream; - std::vector out( 1000000 ); - stream.next_in = (Bytef*) in.data(); + std::vector out(1000000); + stream.next_in = (Bytef *)in.data(); stream.avail_in = in.size(); stream.total_in = 0; stream.zalloc = Z_NULL; stream.zfree = Z_NULL; stream.opaque = Z_NULL; - stream.next_out = (Bytef*) out.data(); + stream.next_out = (Bytef *)out.data(); stream.avail_out = out.size(); stream.total_out = 0; - ASSERT( inflateInit2(&stream, 16+MAX_WBITS) == Z_OK ); + ASSERT(inflateInit2(&stream, 16 + MAX_WBITS) == Z_OK); bool finished = inflate(&stream, Z_SYNC_FLUSH) == Z_STREAM_END; - while ( !finished && stream.msg == Z_NULL ) { - out.resize( 2 * out.size() ); - stream.next_out = (Bytef*) &out[stream.total_out]; + while (!finished && stream.msg == Z_NULL) { + out.resize(2 * out.size()); + stream.next_out = (Bytef *)&out[stream.total_out]; stream.avail_out = out.size() - stream.total_out; finished = inflate(&stream, Z_SYNC_FLUSH) == Z_STREAM_END; } - ASSERT( stream.msg == Z_NULL ); - out.resize( stream.total_out ); + ASSERT(stream.msg == Z_NULL); + out.resize(stream.total_out); inflateEnd(&stream); return out; } - // Read the compressed micro CT data -Array readMicroCT( const std::string& filename ) -{ - auto in = readFile( filename ); - auto out = gunzip( in ); - ASSERT( out.size() == 1024*1024*1024 ); - Array data( 1024, 1024, 1024 ); - memcpy( data.data(), out.data(), data.length() ); +Array readMicroCT(const std::string &filename) { + auto in = readFile(filename); + auto out = gunzip(in); + ASSERT(out.size() == 1024 * 1024 * 1024); + Array data(1024, 1024, 1024); + memcpy(data.data(), out.data(), data.length()); return data; } - // Read the compressed micro CT data and distribute -Array readMicroCT( const Database& domain, const Utilities::MPI& comm ) -{ +Array readMicroCT(const Database &domain, const Utilities::MPI &comm) { // Get the local problem info - auto n = domain.getVector( "n" ); + auto n = domain.getVector("n"); int rank = comm.getRank(); - auto nproc = domain.getVector( "nproc" ); - RankInfoStruct rankInfo( rank, nproc[0], nproc[1], nproc[2] ); - + auto nproc = domain.getVector("nproc"); + RankInfoStruct rankInfo(rank, nproc[0], nproc[1], nproc[2]); + // Determine the largest file number to get - int Nfx = ( n[0] * rankInfo.nx + 1023 ) / 1024; - int Nfy = ( n[1] * rankInfo.ny + 1023 ) / 1024; - int Nfz = ( n[2] * rankInfo.nz + 1023 ) / 1024; + int Nfx = (n[0] * rankInfo.nx + 1023) / 1024; + int Nfy = (n[1] * rankInfo.ny + 1023) / 1024; + int Nfz = (n[2] * rankInfo.nz + 1023) / 1024; // Load one of the files if rank < largest file Array data; - RankInfoStruct srcRankInfo( rank, Nfx, Nfy, Nfz ); - if ( srcRankInfo.ix >= 0 ) { - auto filename = domain.getScalar( "Filename" ); - char tmp[100]; - if ( filename.find( "0x_0y_0z.gbd.gz" ) != std::string::npos ) { - sprintf( tmp, "%ix_%iy_%iz.gbd.gz", srcRankInfo.ix, srcRankInfo.jy, srcRankInfo.kz ); - filename = filename.replace( filename.find( "0x_0y_0z.gbd.gz" ), 15, std::string( tmp ) ); - } else if ( filename.find( "x0_y0_z0.gbd.gz" ) != std::string::npos ) { - sprintf( tmp, "x%i_y%i_z%i.gbd.gz", srcRankInfo.ix, srcRankInfo.jy, srcRankInfo.kz ); - filename = filename.replace( filename.find( "x0_y0_z0.gbd.gz" ), 15, std::string( tmp ) ); - } else { - ERROR( "Invalid name for first file" ); - } - data = readMicroCT( filename ); + RankInfoStruct srcRankInfo(rank, Nfx, Nfy, Nfz); + if (srcRankInfo.ix >= 0) { + auto filename = domain.getScalar("Filename"); + char tmp[100]; + if (filename.find("0x_0y_0z.gbd.gz") != std::string::npos) { + sprintf(tmp, "%ix_%iy_%iz.gbd.gz", srcRankInfo.ix, srcRankInfo.jy, + srcRankInfo.kz); + filename = filename.replace(filename.find("0x_0y_0z.gbd.gz"), 15, + std::string(tmp)); + } else if (filename.find("x0_y0_z0.gbd.gz") != std::string::npos) { + sprintf(tmp, "x%i_y%i_z%i.gbd.gz", srcRankInfo.ix, srcRankInfo.jy, + srcRankInfo.kz); + filename = filename.replace(filename.find("x0_y0_z0.gbd.gz"), 15, + std::string(tmp)); + } else { + ERROR("Invalid name for first file"); + } + data = readMicroCT(filename); } // Redistribute the data - data = redistribute( srcRankInfo, data, rankInfo, { n[0], n[1], n[2] }, comm ); + data = redistribute(srcRankInfo, data, rankInfo, {n[0], n[1], n[2]}, comm); - // Relabel the data - auto ReadValues = domain.getVector( "ReadValues" ); - auto WriteValues = domain.getVector( "WriteValues" ); - ASSERT( ReadValues.size() == WriteValues.size() ); + // Relabel the data + auto ReadValues = domain.getVector("ReadValues"); + auto WriteValues = domain.getVector("WriteValues"); + ASSERT(ReadValues.size() == WriteValues.size()); int readMaxValue = 0; - for ( auto v : ReadValues ) - readMaxValue = std::max( data.max()+1, v ); - std::vector map( readMaxValue + 1, -1 ); - for ( size_t i=0; i map(readMaxValue + 1, -1); + for (size_t i = 0; i < ReadValues.size(); i++) map[ReadValues[i]] = WriteValues[i]; - for ( size_t i=0; i= 0 && t <= readMaxValue ); + ASSERT(t >= 0 && t <= readMaxValue); data(i) = map[t]; } diff --git a/common/ReadMicroCT.h b/common/ReadMicroCT.h index c8acc379..066fbe17 100644 --- a/common/ReadMicroCT.h +++ b/common/ReadMicroCT.h @@ -1,16 +1,13 @@ #ifndef READMICROCT_H #define READMICROCT_H - #include "common/Array.h" #include "common/Communication.h" #include "common/Database.h" #include "common/MPI.h" +Array readMicroCT(const std::string &filename); -Array readMicroCT( const std::string& filename ); - -Array readMicroCT( const Database& domain, const Utilities::MPI& comm ); - +Array readMicroCT(const Database &domain, const Utilities::MPI &comm); #endif diff --git a/common/ScaLBL.cpp b/common/ScaLBL.cpp index 5ce44b44..d9d364ef 100644 --- a/common/ScaLBL.cpp +++ b/common/ScaLBL.cpp @@ -18,479 +18,734 @@ #include - -ScaLBL_Communicator::ScaLBL_Communicator(std::shared_ptr Dm){ - //...................................................................................... - Lock=false; // unlock the communicator - //...................................................................................... - // Create a separate copy of the communicator for the device +ScaLBL_Communicator::ScaLBL_Communicator(std::shared_ptr Dm) { + //...................................................................................... + Lock = false; // unlock the communicator + //...................................................................................... + // Create a separate copy of the communicator for the device MPI_COMM_SCALBL = Dm->Comm.dup(); - //...................................................................................... - // Copy the domain size and communication information directly from Dm - Nx = Dm->Nx; - Ny = Dm->Ny; - Nz = Dm->Nz; - N = Nx*Ny*Nz; - next=0; - rank=Dm->rank(); - rank_x=Dm->rank_x(); - rank_y=Dm->rank_y(); - rank_z=Dm->rank_z(); - rank_X=Dm->rank_X(); - rank_Y=Dm->rank_Y(); - rank_Z=Dm->rank_Z(); - rank_xy=Dm->rank_xy(); - rank_XY=Dm->rank_XY(); - rank_xY=Dm->rank_xY(); - rank_Xy=Dm->rank_Xy(); - rank_xz=Dm->rank_xz(); - rank_XZ=Dm->rank_XZ(); - rank_xZ=Dm->rank_xZ(); - rank_Xz=Dm->rank_Xz(); - rank_yz=Dm->rank_yz(); - rank_YZ=Dm->rank_YZ(); - rank_yZ=Dm->rank_yZ(); - rank_Yz=Dm->rank_Yz(); - sendCount_x=Dm->sendCount("x"); - sendCount_y=Dm->sendCount("y"); - sendCount_z=Dm->sendCount("z"); - sendCount_X=Dm->sendCount("X"); - sendCount_Y=Dm->sendCount("Y"); - sendCount_Z=Dm->sendCount("Z"); - sendCount_xy=Dm->sendCount("xy"); - sendCount_yz=Dm->sendCount("yz"); - sendCount_xz=Dm->sendCount("xz"); - sendCount_Xy=Dm->sendCount("Xy"); - sendCount_Yz=Dm->sendCount("Yz"); - sendCount_xZ=Dm->sendCount("xZ"); - sendCount_xY=Dm->sendCount("xY"); - sendCount_yZ=Dm->sendCount("yZ"); - sendCount_Xz=Dm->sendCount("Xz"); - sendCount_XY=Dm->sendCount("XY"); - sendCount_YZ=Dm->sendCount("YZ"); - sendCount_XZ=Dm->sendCount("XZ"); - recvCount_x=Dm->recvCount("x"); - recvCount_y=Dm->recvCount("y"); - recvCount_z=Dm->recvCount("z"); - recvCount_X=Dm->recvCount("X"); - recvCount_Y=Dm->recvCount("Y"); - recvCount_Z=Dm->recvCount("Z"); - recvCount_xy=Dm->recvCount("xy"); - recvCount_yz=Dm->recvCount("yz"); - recvCount_xz=Dm->recvCount("xz"); - recvCount_Xy=Dm->recvCount("Xy"); - recvCount_Yz=Dm->recvCount("Yz"); - recvCount_xZ=Dm->recvCount("xZ"); - recvCount_xY=Dm->recvCount("xY"); - recvCount_yZ=Dm->recvCount("yZ"); - recvCount_Xz=Dm->recvCount("Xz"); - recvCount_XY=Dm->recvCount("XY"); - recvCount_YZ=Dm->recvCount("YZ"); - recvCount_XZ=Dm->recvCount("XZ"); - - iproc = Dm->iproc(); - jproc = Dm->jproc(); - kproc = Dm->kproc(); - nprocx = Dm->nprocx(); - nprocy = Dm->nprocy(); - nprocz = Dm->nprocz(); - BoundaryCondition = Dm->BoundaryCondition; - //...................................................................................... + //...................................................................................... + // Copy the domain size and communication information directly from Dm + Nx = Dm->Nx; + Ny = Dm->Ny; + Nz = Dm->Nz; + N = Nx * Ny * Nz; + next = 0; + rank = Dm->rank(); + rank_x = Dm->rank_x(); + rank_y = Dm->rank_y(); + rank_z = Dm->rank_z(); + rank_X = Dm->rank_X(); + rank_Y = Dm->rank_Y(); + rank_Z = Dm->rank_Z(); + rank_xy = Dm->rank_xy(); + rank_XY = Dm->rank_XY(); + rank_xY = Dm->rank_xY(); + rank_Xy = Dm->rank_Xy(); + rank_xz = Dm->rank_xz(); + rank_XZ = Dm->rank_XZ(); + rank_xZ = Dm->rank_xZ(); + rank_Xz = Dm->rank_Xz(); + rank_yz = Dm->rank_yz(); + rank_YZ = Dm->rank_YZ(); + rank_yZ = Dm->rank_yZ(); + rank_Yz = Dm->rank_Yz(); + sendCount_x = Dm->sendCount("x"); + sendCount_y = Dm->sendCount("y"); + sendCount_z = Dm->sendCount("z"); + sendCount_X = Dm->sendCount("X"); + sendCount_Y = Dm->sendCount("Y"); + sendCount_Z = Dm->sendCount("Z"); + sendCount_xy = Dm->sendCount("xy"); + sendCount_yz = Dm->sendCount("yz"); + sendCount_xz = Dm->sendCount("xz"); + sendCount_Xy = Dm->sendCount("Xy"); + sendCount_Yz = Dm->sendCount("Yz"); + sendCount_xZ = Dm->sendCount("xZ"); + sendCount_xY = Dm->sendCount("xY"); + sendCount_yZ = Dm->sendCount("yZ"); + sendCount_Xz = Dm->sendCount("Xz"); + sendCount_XY = Dm->sendCount("XY"); + sendCount_YZ = Dm->sendCount("YZ"); + sendCount_XZ = Dm->sendCount("XZ"); + recvCount_x = Dm->recvCount("x"); + recvCount_y = Dm->recvCount("y"); + recvCount_z = Dm->recvCount("z"); + recvCount_X = Dm->recvCount("X"); + recvCount_Y = Dm->recvCount("Y"); + recvCount_Z = Dm->recvCount("Z"); + recvCount_xy = Dm->recvCount("xy"); + recvCount_yz = Dm->recvCount("yz"); + recvCount_xz = Dm->recvCount("xz"); + recvCount_Xy = Dm->recvCount("Xy"); + recvCount_Yz = Dm->recvCount("Yz"); + recvCount_xZ = Dm->recvCount("xZ"); + recvCount_xY = Dm->recvCount("xY"); + recvCount_yZ = Dm->recvCount("yZ"); + recvCount_Xz = Dm->recvCount("Xz"); + recvCount_XY = Dm->recvCount("XY"); + recvCount_YZ = Dm->recvCount("YZ"); + recvCount_XZ = Dm->recvCount("XZ"); - ScaLBL_AllocateZeroCopy((void **) &sendbuf_x, 2*5*sendCount_x*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_X, 2*5*sendCount_X*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_y, 2*5*sendCount_y*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_Y, 2*5*sendCount_Y*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_z, 2*5*sendCount_z*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_Z, 2*5*sendCount_Z*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_xy, 2*sendCount_xy*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_xY, 2*sendCount_xY*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_Xy, 2*sendCount_Xy*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_XY, 2*sendCount_XY*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_xz, 2*sendCount_xz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_xZ, 2*sendCount_xZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_Xz, 2*sendCount_Xz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_XZ, 2*sendCount_XZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_yz, 2*sendCount_yz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_yZ, 2*sendCount_yZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_Yz, 2*sendCount_Yz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_YZ, 2*sendCount_YZ*sizeof(double)); // Allocate device memory - //...................................................................................... - ScaLBL_AllocateZeroCopy((void **) &recvbuf_x, 2*5*recvCount_x*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_X, 2*5*recvCount_X*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_y, 2*5*recvCount_y*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_Y, 2*5*recvCount_Y*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_z, 2*5*recvCount_z*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_Z, 2*5*recvCount_Z*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_xy, 2*recvCount_xy*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_xY, 2*recvCount_xY*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_Xy, 2*recvCount_Xy*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_XY, 2*recvCount_XY*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_xz, 2*recvCount_xz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_xZ, 2*recvCount_xZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_Xz, 2*recvCount_Xz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_XZ, 2*recvCount_XZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_yz, 2*recvCount_yz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_yZ, 2*recvCount_yZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_Yz, 2*recvCount_Yz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_YZ, 2*recvCount_YZ*sizeof(double)); // Allocate device memory - //...................................................................................... - ScaLBL_AllocateZeroCopy((void **) &dvcSendList_x, sendCount_x*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcSendList_X, sendCount_X*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcSendList_y, sendCount_y*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcSendList_Y, sendCount_Y*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcSendList_z, sendCount_z*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcSendList_Z, sendCount_Z*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcSendList_xy, sendCount_xy*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcSendList_xY, sendCount_xY*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcSendList_Xy, sendCount_Xy*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcSendList_XY, sendCount_XY*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcSendList_xz, sendCount_xz*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcSendList_xZ, sendCount_xZ*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcSendList_Xz, sendCount_Xz*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcSendList_XZ, sendCount_XZ*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcSendList_yz, sendCount_yz*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcSendList_yZ, sendCount_yZ*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcSendList_Yz, sendCount_Yz*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcSendList_YZ, sendCount_YZ*sizeof(int)); // Allocate device memory - //...................................................................................... - ScaLBL_AllocateZeroCopy((void **) &dvcRecvList_x, recvCount_x*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvList_X, recvCount_X*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvList_y, recvCount_y*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvList_Y, recvCount_Y*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvList_z, recvCount_z*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvList_Z, recvCount_Z*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvList_xy, recvCount_xy*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvList_xY, recvCount_xY*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvList_Xy, recvCount_Xy*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvList_XY, recvCount_XY*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvList_xz, recvCount_xz*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvList_xZ, recvCount_xZ*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvList_Xz, recvCount_Xz*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvList_XZ, recvCount_XZ*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvList_yz, recvCount_yz*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvList_yZ, recvCount_yZ*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvList_Yz, recvCount_Yz*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvList_YZ, recvCount_YZ*sizeof(int)); // Allocate device memory - //...................................................................................... - ScaLBL_AllocateZeroCopy((void **) &dvcRecvDist_x, 5*recvCount_x*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvDist_X, 5*recvCount_X*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvDist_y, 5*recvCount_y*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvDist_Y, 5*recvCount_Y*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvDist_z, 5*recvCount_z*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvDist_Z, 5*recvCount_Z*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvDist_xy, recvCount_xy*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvDist_xY, recvCount_xY*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvDist_Xy, recvCount_Xy*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvDist_XY, recvCount_XY*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvDist_xz, recvCount_xz*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvDist_xZ, recvCount_xZ*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvDist_Xz, recvCount_Xz*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvDist_XZ, recvCount_XZ*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvDist_yz, recvCount_yz*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvDist_yZ, recvCount_yZ*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvDist_Yz, recvCount_Yz*sizeof(int)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &dvcRecvDist_YZ, recvCount_YZ*sizeof(int)); // Allocate device memory - //...................................................................................... + iproc = Dm->iproc(); + jproc = Dm->jproc(); + kproc = Dm->kproc(); + nprocx = Dm->nprocx(); + nprocy = Dm->nprocy(); + nprocz = Dm->nprocz(); + BoundaryCondition = Dm->BoundaryCondition; + //...................................................................................... - //...................................................................................... - ScaLBL_CopyToZeroCopy(dvcSendList_x,Dm->sendList("x"),sendCount_x*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcSendList_X,Dm->sendList("X"),sendCount_X*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcSendList_y,Dm->sendList("y"),sendCount_y*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcSendList_Y,Dm->sendList("Y"),sendCount_Y*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcSendList_z,Dm->sendList("z"),sendCount_z*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcSendList_Z,Dm->sendList("Z"),sendCount_Z*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcSendList_xy,Dm->sendList("xy"),sendCount_xy*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcSendList_XY,Dm->sendList("XY"),sendCount_XY*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcSendList_xY,Dm->sendList("xY"),sendCount_xY*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcSendList_Xy,Dm->sendList("Xy"),sendCount_Xy*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcSendList_xz,Dm->sendList("xz"),sendCount_xz*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcSendList_XZ,Dm->sendList("XZ"),sendCount_XZ*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcSendList_xZ,Dm->sendList("xZ"),sendCount_xZ*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcSendList_Xz,Dm->sendList("Xz"),sendCount_Xz*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcSendList_yz,Dm->sendList("yz"),sendCount_yz*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcSendList_YZ,Dm->sendList("YZ"),sendCount_YZ*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcSendList_yZ,Dm->sendList("yZ"),sendCount_yZ*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcSendList_Yz,Dm->sendList("Yz"),sendCount_Yz*sizeof(int)); - //...................................................................................... - ScaLBL_CopyToZeroCopy(dvcRecvList_x,Dm->recvList("x"),recvCount_x*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcRecvList_X,Dm->recvList("X"),recvCount_X*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcRecvList_y,Dm->recvList("y"),recvCount_y*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcRecvList_Y,Dm->recvList("Y"),recvCount_Y*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcRecvList_z,Dm->recvList("z"),recvCount_z*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcRecvList_Z,Dm->recvList("Z"),recvCount_Z*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcRecvList_xy,Dm->recvList("xy"),recvCount_xy*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcRecvList_XY,Dm->recvList("XY"),recvCount_XY*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcRecvList_xY,Dm->recvList("xY"),recvCount_xY*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcRecvList_Xy,Dm->recvList("Xy"),recvCount_Xy*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcRecvList_xz,Dm->recvList("xz"),recvCount_xz*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcRecvList_XZ,Dm->recvList("XZ"),recvCount_XZ*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcRecvList_xZ,Dm->recvList("xZ"),recvCount_xZ*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcRecvList_Xz,Dm->recvList("Xz"),recvCount_Xz*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcRecvList_yz,Dm->recvList("yz"),recvCount_yz*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcRecvList_YZ,Dm->recvList("YZ"),recvCount_YZ*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcRecvList_yZ,Dm->recvList("yZ"),recvCount_yZ*sizeof(int)); - ScaLBL_CopyToZeroCopy(dvcRecvList_Yz,Dm->recvList("Yz"),recvCount_Yz*sizeof(int)); - //...................................................................................... + ScaLBL_AllocateZeroCopy((void **)&sendbuf_x, + 2 * 5 * sendCount_x * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_X, + 2 * 5 * sendCount_X * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_y, + 2 * 5 * sendCount_y * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_Y, + 2 * 5 * sendCount_Y * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_z, + 2 * 5 * sendCount_z * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_Z, + 2 * 5 * sendCount_Z * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_xy, + 2 * sendCount_xy * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_xY, + 2 * sendCount_xY * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_Xy, + 2 * sendCount_Xy * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_XY, + 2 * sendCount_XY * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_xz, + 2 * sendCount_xz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_xZ, + 2 * sendCount_xZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_Xz, + 2 * sendCount_Xz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_XZ, + 2 * sendCount_XZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_yz, + 2 * sendCount_yz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_yZ, + 2 * sendCount_yZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_Yz, + 2 * sendCount_Yz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_YZ, + 2 * sendCount_YZ * + sizeof(double)); // Allocate device memory + //...................................................................................... + ScaLBL_AllocateZeroCopy((void **)&recvbuf_x, + 2 * 5 * recvCount_x * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_X, + 2 * 5 * recvCount_X * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_y, + 2 * 5 * recvCount_y * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_Y, + 2 * 5 * recvCount_Y * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_z, + 2 * 5 * recvCount_z * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_Z, + 2 * 5 * recvCount_Z * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_xy, + 2 * recvCount_xy * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_xY, + 2 * recvCount_xY * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_Xy, + 2 * recvCount_Xy * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_XY, + 2 * recvCount_XY * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_xz, + 2 * recvCount_xz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_xZ, + 2 * recvCount_xZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_Xz, + 2 * recvCount_Xz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_XZ, + 2 * recvCount_XZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_yz, + 2 * recvCount_yz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_yZ, + 2 * recvCount_yZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_Yz, + 2 * recvCount_Yz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_YZ, + 2 * recvCount_YZ * + sizeof(double)); // Allocate device memory + //...................................................................................... + ScaLBL_AllocateZeroCopy((void **)&dvcSendList_x, + sendCount_x * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcSendList_X, + sendCount_X * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcSendList_y, + sendCount_y * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcSendList_Y, + sendCount_Y * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcSendList_z, + sendCount_z * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcSendList_Z, + sendCount_Z * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcSendList_xy, + sendCount_xy * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcSendList_xY, + sendCount_xY * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcSendList_Xy, + sendCount_Xy * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcSendList_XY, + sendCount_XY * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcSendList_xz, + sendCount_xz * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcSendList_xZ, + sendCount_xZ * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcSendList_Xz, + sendCount_Xz * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcSendList_XZ, + sendCount_XZ * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcSendList_yz, + sendCount_yz * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcSendList_yZ, + sendCount_yZ * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcSendList_Yz, + sendCount_Yz * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcSendList_YZ, + sendCount_YZ * + sizeof(int)); // Allocate device memory + //...................................................................................... + ScaLBL_AllocateZeroCopy((void **)&dvcRecvList_x, + recvCount_x * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvList_X, + recvCount_X * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvList_y, + recvCount_y * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvList_Y, + recvCount_Y * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvList_z, + recvCount_z * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvList_Z, + recvCount_Z * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvList_xy, + recvCount_xy * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvList_xY, + recvCount_xY * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvList_Xy, + recvCount_Xy * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvList_XY, + recvCount_XY * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvList_xz, + recvCount_xz * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvList_xZ, + recvCount_xZ * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvList_Xz, + recvCount_Xz * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvList_XZ, + recvCount_XZ * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvList_yz, + recvCount_yz * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvList_yZ, + recvCount_yZ * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvList_Yz, + recvCount_Yz * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvList_YZ, + recvCount_YZ * + sizeof(int)); // Allocate device memory + //...................................................................................... + ScaLBL_AllocateZeroCopy((void **)&dvcRecvDist_x, + 5 * recvCount_x * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvDist_X, + 5 * recvCount_X * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvDist_y, + 5 * recvCount_y * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvDist_Y, + 5 * recvCount_Y * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvDist_z, + 5 * recvCount_z * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvDist_Z, + 5 * recvCount_Z * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvDist_xy, + recvCount_xy * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvDist_xY, + recvCount_xY * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvDist_Xy, + recvCount_Xy * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvDist_XY, + recvCount_XY * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvDist_xz, + recvCount_xz * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvDist_xZ, + recvCount_xZ * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvDist_Xz, + recvCount_Xz * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvDist_XZ, + recvCount_XZ * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvDist_yz, + recvCount_yz * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvDist_yZ, + recvCount_yZ * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvDist_Yz, + recvCount_Yz * + sizeof(int)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&dvcRecvDist_YZ, + recvCount_YZ * + sizeof(int)); // Allocate device memory + //...................................................................................... - MPI_COMM_SCALBL.barrier(); + //...................................................................................... + ScaLBL_CopyToZeroCopy(dvcSendList_x, Dm->sendList("x"), + sendCount_x * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcSendList_X, Dm->sendList("X"), + sendCount_X * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcSendList_y, Dm->sendList("y"), + sendCount_y * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcSendList_Y, Dm->sendList("Y"), + sendCount_Y * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcSendList_z, Dm->sendList("z"), + sendCount_z * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcSendList_Z, Dm->sendList("Z"), + sendCount_Z * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcSendList_xy, Dm->sendList("xy"), + sendCount_xy * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcSendList_XY, Dm->sendList("XY"), + sendCount_XY * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcSendList_xY, Dm->sendList("xY"), + sendCount_xY * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcSendList_Xy, Dm->sendList("Xy"), + sendCount_Xy * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcSendList_xz, Dm->sendList("xz"), + sendCount_xz * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcSendList_XZ, Dm->sendList("XZ"), + sendCount_XZ * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcSendList_xZ, Dm->sendList("xZ"), + sendCount_xZ * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcSendList_Xz, Dm->sendList("Xz"), + sendCount_Xz * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcSendList_yz, Dm->sendList("yz"), + sendCount_yz * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcSendList_YZ, Dm->sendList("YZ"), + sendCount_YZ * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcSendList_yZ, Dm->sendList("yZ"), + sendCount_yZ * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcSendList_Yz, Dm->sendList("Yz"), + sendCount_Yz * sizeof(int)); + //...................................................................................... + ScaLBL_CopyToZeroCopy(dvcRecvList_x, Dm->recvList("x"), + recvCount_x * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcRecvList_X, Dm->recvList("X"), + recvCount_X * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcRecvList_y, Dm->recvList("y"), + recvCount_y * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcRecvList_Y, Dm->recvList("Y"), + recvCount_Y * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcRecvList_z, Dm->recvList("z"), + recvCount_z * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcRecvList_Z, Dm->recvList("Z"), + recvCount_Z * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcRecvList_xy, Dm->recvList("xy"), + recvCount_xy * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcRecvList_XY, Dm->recvList("XY"), + recvCount_XY * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcRecvList_xY, Dm->recvList("xY"), + recvCount_xY * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcRecvList_Xy, Dm->recvList("Xy"), + recvCount_Xy * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcRecvList_xz, Dm->recvList("xz"), + recvCount_xz * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcRecvList_XZ, Dm->recvList("XZ"), + recvCount_XZ * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcRecvList_xZ, Dm->recvList("xZ"), + recvCount_xZ * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcRecvList_Xz, Dm->recvList("Xz"), + recvCount_Xz * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcRecvList_yz, Dm->recvList("yz"), + recvCount_yz * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcRecvList_YZ, Dm->recvList("YZ"), + recvCount_YZ * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcRecvList_yZ, Dm->recvList("yZ"), + recvCount_yZ * sizeof(int)); + ScaLBL_CopyToZeroCopy(dvcRecvList_Yz, Dm->recvList("Yz"), + recvCount_Yz * sizeof(int)); + //...................................................................................... - //................................................................................... - // Set up the recieve distribution lists - //................................................................................... - //...Map recieve list for the X face: q=2,8,10,12,14 ................................. - D3Q19_MapRecv(-1,0,0, Dm->recvList("X"),0,recvCount_X,dvcRecvDist_X); - D3Q19_MapRecv(-1,-1,0,Dm->recvList("X"),recvCount_X,recvCount_X,dvcRecvDist_X); - D3Q19_MapRecv(-1,1,0, Dm->recvList("X"),2*recvCount_X,recvCount_X,dvcRecvDist_X); - D3Q19_MapRecv(-1,0,-1,Dm->recvList("X"),3*recvCount_X,recvCount_X,dvcRecvDist_X); - D3Q19_MapRecv(-1,0,1, Dm->recvList("X"),4*recvCount_X,recvCount_X,dvcRecvDist_X); - //................................................................................... - //...Map recieve list for the x face: q=1,7,9,11,13.................................. - D3Q19_MapRecv(1,0,0, Dm->recvList("x"),0,recvCount_x,dvcRecvDist_x); - D3Q19_MapRecv(1,1,0, Dm->recvList("x"),recvCount_x,recvCount_x,dvcRecvDist_x); - D3Q19_MapRecv(1,-1,0,Dm->recvList("x"),2*recvCount_x,recvCount_x,dvcRecvDist_x); - D3Q19_MapRecv(1,0,1, Dm->recvList("x"),3*recvCount_x,recvCount_x,dvcRecvDist_x); - D3Q19_MapRecv(1,0,-1,Dm->recvList("x"),4*recvCount_x,recvCount_x,dvcRecvDist_x); - //................................................................................... - //...Map recieve list for the y face: q=4,8,9,16,18 ................................... - D3Q19_MapRecv(0,-1,0, Dm->recvList("Y"),0,recvCount_Y,dvcRecvDist_Y); - D3Q19_MapRecv(-1,-1,0,Dm->recvList("Y"),recvCount_Y,recvCount_Y,dvcRecvDist_Y); - D3Q19_MapRecv(1,-1,0, Dm->recvList("Y"),2*recvCount_Y,recvCount_Y,dvcRecvDist_Y); - D3Q19_MapRecv(0,-1,-1,Dm->recvList("Y"),3*recvCount_Y,recvCount_Y,dvcRecvDist_Y); - D3Q19_MapRecv(0,-1,1, Dm->recvList("Y"),4*recvCount_Y,recvCount_Y,dvcRecvDist_Y); - //................................................................................... - //...Map recieve list for the Y face: q=3,7,10,15,17 .................................. - D3Q19_MapRecv(0,1,0, Dm->recvList("y"),0,recvCount_y,dvcRecvDist_y); - D3Q19_MapRecv(1,1,0, Dm->recvList("y"),recvCount_y,recvCount_y,dvcRecvDist_y); - D3Q19_MapRecv(-1,1,0,Dm->recvList("y"),2*recvCount_y,recvCount_y,dvcRecvDist_y); - D3Q19_MapRecv(0,1,1, Dm->recvList("y"),3*recvCount_y,recvCount_y,dvcRecvDist_y); - D3Q19_MapRecv(0,1,-1,Dm->recvList("y"),4*recvCount_y,recvCount_y,dvcRecvDist_y); - //................................................................................... - //...Map recieve list for the z face<<<6,12,13,16,17).............................................. - D3Q19_MapRecv(0,0,-1, Dm->recvList("Z"),0,recvCount_Z,dvcRecvDist_Z); - D3Q19_MapRecv(-1,0,-1,Dm->recvList("Z"),recvCount_Z,recvCount_Z,dvcRecvDist_Z); - D3Q19_MapRecv(1,0,-1, Dm->recvList("Z"),2*recvCount_Z,recvCount_Z,dvcRecvDist_Z); - D3Q19_MapRecv(0,-1,-1,Dm->recvList("Z"),3*recvCount_Z,recvCount_Z,dvcRecvDist_Z); - D3Q19_MapRecv(0,1,-1, Dm->recvList("Z"),4*recvCount_Z,recvCount_Z,dvcRecvDist_Z); - //...Map recieve list for the Z face<<<5,11,14,15,18).............................................. - D3Q19_MapRecv(0,0,1, Dm->recvList("z"),0,recvCount_z,dvcRecvDist_z); - D3Q19_MapRecv(1,0,1, Dm->recvList("z"),recvCount_z,recvCount_z,dvcRecvDist_z); - D3Q19_MapRecv(-1,0,1,Dm->recvList("z"),2*recvCount_z,recvCount_z,dvcRecvDist_z); - D3Q19_MapRecv(0,1,1, Dm->recvList("z"),3*recvCount_z,recvCount_z,dvcRecvDist_z); - D3Q19_MapRecv(0,-1,1,Dm->recvList("z"),4*recvCount_z,recvCount_z,dvcRecvDist_z); - //.................................................................................. - //...Map recieve list for the xy edge <<<8)................................ - D3Q19_MapRecv(-1,-1,0,Dm->recvList("XY"),0,recvCount_XY,dvcRecvDist_XY); - //...Map recieve list for the Xy edge <<<9)................................ - D3Q19_MapRecv(1,-1,0,Dm->recvList("xY"),0,recvCount_xY,dvcRecvDist_xY); - //...Map recieve list for the xY edge <<<10)................................ - D3Q19_MapRecv(-1,1,0,Dm->recvList("Xy"),0,recvCount_Xy,dvcRecvDist_Xy); - //...Map recieve list for the XY edge <<<7)................................ - D3Q19_MapRecv(1,1,0,Dm->recvList("xy"),0,recvCount_xy,dvcRecvDist_xy); - //...Map recieve list for the xz edge <<<12)................................ - D3Q19_MapRecv(-1,0,-1,Dm->recvList("XZ"),0,recvCount_XZ,dvcRecvDist_XZ); - //...Map recieve list for the xZ edge <<<14)................................ - D3Q19_MapRecv(-1,0,1,Dm->recvList("Xz"),0,recvCount_Xz,dvcRecvDist_Xz); - //...Map recieve list for the Xz edge <<<13)................................ - D3Q19_MapRecv(1,0,-1,Dm->recvList("xZ"),0,recvCount_xZ,dvcRecvDist_xZ); - //...Map recieve list for the XZ edge <<<11)................................ - D3Q19_MapRecv(1,0,1,Dm->recvList("xz"),0,recvCount_xz,dvcRecvDist_xz); - //...Map recieve list for the yz edge <<<16)................................ - D3Q19_MapRecv(0,-1,-1,Dm->recvList("YZ"),0,recvCount_YZ,dvcRecvDist_YZ); - //...Map recieve list for the yZ edge <<<18)................................ - D3Q19_MapRecv(0,-1,1,Dm->recvList("Yz"),0,recvCount_Yz,dvcRecvDist_Yz); - //...Map recieve list for the Yz edge <<<17)................................ - D3Q19_MapRecv(0,1,-1,Dm->recvList("yZ"),0,recvCount_yZ,dvcRecvDist_yZ); - //...Map recieve list for the YZ edge <<<15)................................ - D3Q19_MapRecv(0,1,1,Dm->recvList("yz"),0,recvCount_yz,dvcRecvDist_yz); - //................................................................................... + MPI_COMM_SCALBL.barrier(); - //...................................................................................... - MPI_COMM_SCALBL.barrier(); - ScaLBL_DeviceBarrier(); - //...................................................................................... - SendCount = sendCount_x+sendCount_X+sendCount_y+sendCount_Y+sendCount_z+sendCount_Z+ - sendCount_xy+sendCount_Xy+sendCount_xY+sendCount_XY+ - sendCount_xZ+sendCount_Xz+sendCount_xZ+sendCount_XZ+ - sendCount_yz+sendCount_Yz+sendCount_yZ+sendCount_YZ; + //................................................................................... + // Set up the recieve distribution lists + //................................................................................... + //...Map recieve list for the X face: q=2,8,10,12,14 ................................. + D3Q19_MapRecv(-1, 0, 0, Dm->recvList("X"), 0, recvCount_X, dvcRecvDist_X); + D3Q19_MapRecv(-1, -1, 0, Dm->recvList("X"), recvCount_X, recvCount_X, + dvcRecvDist_X); + D3Q19_MapRecv(-1, 1, 0, Dm->recvList("X"), 2 * recvCount_X, recvCount_X, + dvcRecvDist_X); + D3Q19_MapRecv(-1, 0, -1, Dm->recvList("X"), 3 * recvCount_X, recvCount_X, + dvcRecvDist_X); + D3Q19_MapRecv(-1, 0, 1, Dm->recvList("X"), 4 * recvCount_X, recvCount_X, + dvcRecvDist_X); + //................................................................................... + //...Map recieve list for the x face: q=1,7,9,11,13.................................. + D3Q19_MapRecv(1, 0, 0, Dm->recvList("x"), 0, recvCount_x, dvcRecvDist_x); + D3Q19_MapRecv(1, 1, 0, Dm->recvList("x"), recvCount_x, recvCount_x, + dvcRecvDist_x); + D3Q19_MapRecv(1, -1, 0, Dm->recvList("x"), 2 * recvCount_x, recvCount_x, + dvcRecvDist_x); + D3Q19_MapRecv(1, 0, 1, Dm->recvList("x"), 3 * recvCount_x, recvCount_x, + dvcRecvDist_x); + D3Q19_MapRecv(1, 0, -1, Dm->recvList("x"), 4 * recvCount_x, recvCount_x, + dvcRecvDist_x); + //................................................................................... + //...Map recieve list for the y face: q=4,8,9,16,18 ................................... + D3Q19_MapRecv(0, -1, 0, Dm->recvList("Y"), 0, recvCount_Y, dvcRecvDist_Y); + D3Q19_MapRecv(-1, -1, 0, Dm->recvList("Y"), recvCount_Y, recvCount_Y, + dvcRecvDist_Y); + D3Q19_MapRecv(1, -1, 0, Dm->recvList("Y"), 2 * recvCount_Y, recvCount_Y, + dvcRecvDist_Y); + D3Q19_MapRecv(0, -1, -1, Dm->recvList("Y"), 3 * recvCount_Y, recvCount_Y, + dvcRecvDist_Y); + D3Q19_MapRecv(0, -1, 1, Dm->recvList("Y"), 4 * recvCount_Y, recvCount_Y, + dvcRecvDist_Y); + //................................................................................... + //...Map recieve list for the Y face: q=3,7,10,15,17 .................................. + D3Q19_MapRecv(0, 1, 0, Dm->recvList("y"), 0, recvCount_y, dvcRecvDist_y); + D3Q19_MapRecv(1, 1, 0, Dm->recvList("y"), recvCount_y, recvCount_y, + dvcRecvDist_y); + D3Q19_MapRecv(-1, 1, 0, Dm->recvList("y"), 2 * recvCount_y, recvCount_y, + dvcRecvDist_y); + D3Q19_MapRecv(0, 1, 1, Dm->recvList("y"), 3 * recvCount_y, recvCount_y, + dvcRecvDist_y); + D3Q19_MapRecv(0, 1, -1, Dm->recvList("y"), 4 * recvCount_y, recvCount_y, + dvcRecvDist_y); + //................................................................................... + //...Map recieve list for the z face<<<6,12,13,16,17).............................................. + D3Q19_MapRecv(0, 0, -1, Dm->recvList("Z"), 0, recvCount_Z, dvcRecvDist_Z); + D3Q19_MapRecv(-1, 0, -1, Dm->recvList("Z"), recvCount_Z, recvCount_Z, + dvcRecvDist_Z); + D3Q19_MapRecv(1, 0, -1, Dm->recvList("Z"), 2 * recvCount_Z, recvCount_Z, + dvcRecvDist_Z); + D3Q19_MapRecv(0, -1, -1, Dm->recvList("Z"), 3 * recvCount_Z, recvCount_Z, + dvcRecvDist_Z); + D3Q19_MapRecv(0, 1, -1, Dm->recvList("Z"), 4 * recvCount_Z, recvCount_Z, + dvcRecvDist_Z); + //...Map recieve list for the Z face<<<5,11,14,15,18).............................................. + D3Q19_MapRecv(0, 0, 1, Dm->recvList("z"), 0, recvCount_z, dvcRecvDist_z); + D3Q19_MapRecv(1, 0, 1, Dm->recvList("z"), recvCount_z, recvCount_z, + dvcRecvDist_z); + D3Q19_MapRecv(-1, 0, 1, Dm->recvList("z"), 2 * recvCount_z, recvCount_z, + dvcRecvDist_z); + D3Q19_MapRecv(0, 1, 1, Dm->recvList("z"), 3 * recvCount_z, recvCount_z, + dvcRecvDist_z); + D3Q19_MapRecv(0, -1, 1, Dm->recvList("z"), 4 * recvCount_z, recvCount_z, + dvcRecvDist_z); + //.................................................................................. + //...Map recieve list for the xy edge <<<8)................................ + D3Q19_MapRecv(-1, -1, 0, Dm->recvList("XY"), 0, recvCount_XY, + dvcRecvDist_XY); + //...Map recieve list for the Xy edge <<<9)................................ + D3Q19_MapRecv(1, -1, 0, Dm->recvList("xY"), 0, recvCount_xY, + dvcRecvDist_xY); + //...Map recieve list for the xY edge <<<10)................................ + D3Q19_MapRecv(-1, 1, 0, Dm->recvList("Xy"), 0, recvCount_Xy, + dvcRecvDist_Xy); + //...Map recieve list for the XY edge <<<7)................................ + D3Q19_MapRecv(1, 1, 0, Dm->recvList("xy"), 0, recvCount_xy, dvcRecvDist_xy); + //...Map recieve list for the xz edge <<<12)................................ + D3Q19_MapRecv(-1, 0, -1, Dm->recvList("XZ"), 0, recvCount_XZ, + dvcRecvDist_XZ); + //...Map recieve list for the xZ edge <<<14)................................ + D3Q19_MapRecv(-1, 0, 1, Dm->recvList("Xz"), 0, recvCount_Xz, + dvcRecvDist_Xz); + //...Map recieve list for the Xz edge <<<13)................................ + D3Q19_MapRecv(1, 0, -1, Dm->recvList("xZ"), 0, recvCount_xZ, + dvcRecvDist_xZ); + //...Map recieve list for the XZ edge <<<11)................................ + D3Q19_MapRecv(1, 0, 1, Dm->recvList("xz"), 0, recvCount_xz, dvcRecvDist_xz); + //...Map recieve list for the yz edge <<<16)................................ + D3Q19_MapRecv(0, -1, -1, Dm->recvList("YZ"), 0, recvCount_YZ, + dvcRecvDist_YZ); + //...Map recieve list for the yZ edge <<<18)................................ + D3Q19_MapRecv(0, -1, 1, Dm->recvList("Yz"), 0, recvCount_Yz, + dvcRecvDist_Yz); + //...Map recieve list for the Yz edge <<<17)................................ + D3Q19_MapRecv(0, 1, -1, Dm->recvList("yZ"), 0, recvCount_yZ, + dvcRecvDist_yZ); + //...Map recieve list for the YZ edge <<<15)................................ + D3Q19_MapRecv(0, 1, 1, Dm->recvList("yz"), 0, recvCount_yz, dvcRecvDist_yz); + //................................................................................... - RecvCount = recvCount_x+recvCount_X+recvCount_y+recvCount_Y+recvCount_z+recvCount_Z+ - recvCount_xy+recvCount_Xy+recvCount_xY+recvCount_XY+ - recvCount_xZ+recvCount_Xz+recvCount_xZ+recvCount_XZ+ - recvCount_yz+recvCount_Yz+recvCount_yZ+recvCount_YZ; + //...................................................................................... + MPI_COMM_SCALBL.barrier(); + ScaLBL_DeviceBarrier(); + //...................................................................................... + SendCount = sendCount_x + sendCount_X + sendCount_y + sendCount_Y + + sendCount_z + sendCount_Z + sendCount_xy + sendCount_Xy + + sendCount_xY + sendCount_XY + sendCount_xZ + sendCount_Xz + + sendCount_xZ + sendCount_XZ + sendCount_yz + sendCount_Yz + + sendCount_yZ + sendCount_YZ; - CommunicationCount = SendCount+RecvCount; - //...................................................................................... + RecvCount = recvCount_x + recvCount_X + recvCount_y + recvCount_Y + + recvCount_z + recvCount_Z + recvCount_xy + recvCount_Xy + + recvCount_xY + recvCount_XY + recvCount_xZ + recvCount_Xz + + recvCount_xZ + recvCount_XZ + recvCount_yz + recvCount_Yz + + recvCount_yZ + recvCount_YZ; + CommunicationCount = SendCount + RecvCount; + //...................................................................................... } +ScaLBL_Communicator::~ScaLBL_Communicator() { -ScaLBL_Communicator::~ScaLBL_Communicator() -{ - - ScaLBL_FreeDeviceMemory( sendbuf_x ); - ScaLBL_FreeDeviceMemory( sendbuf_X ); - ScaLBL_FreeDeviceMemory( sendbuf_y ); - ScaLBL_FreeDeviceMemory( sendbuf_Y ); - ScaLBL_FreeDeviceMemory( sendbuf_z ); - ScaLBL_FreeDeviceMemory( sendbuf_Z ); - ScaLBL_FreeDeviceMemory( sendbuf_xy ); - ScaLBL_FreeDeviceMemory( sendbuf_xY ); - ScaLBL_FreeDeviceMemory( sendbuf_Xy ); - ScaLBL_FreeDeviceMemory( sendbuf_XY ); - ScaLBL_FreeDeviceMemory( sendbuf_xz ); - ScaLBL_FreeDeviceMemory( sendbuf_xZ ); - ScaLBL_FreeDeviceMemory( sendbuf_Xz ); - ScaLBL_FreeDeviceMemory( sendbuf_XZ ); - ScaLBL_FreeDeviceMemory( sendbuf_yz ); - ScaLBL_FreeDeviceMemory( sendbuf_yZ ); - ScaLBL_FreeDeviceMemory( sendbuf_Yz ); - ScaLBL_FreeDeviceMemory( sendbuf_YZ ); - ScaLBL_FreeDeviceMemory( recvbuf_x ); - ScaLBL_FreeDeviceMemory( recvbuf_X ); - ScaLBL_FreeDeviceMemory( recvbuf_y ); - ScaLBL_FreeDeviceMemory( recvbuf_Y ); - ScaLBL_FreeDeviceMemory( recvbuf_z ); - ScaLBL_FreeDeviceMemory( recvbuf_Z ); - ScaLBL_FreeDeviceMemory( recvbuf_xy ); - ScaLBL_FreeDeviceMemory( recvbuf_xY ); - ScaLBL_FreeDeviceMemory( recvbuf_Xy ); - ScaLBL_FreeDeviceMemory( recvbuf_XY ); - ScaLBL_FreeDeviceMemory( recvbuf_xz ); - ScaLBL_FreeDeviceMemory( recvbuf_xZ ); - ScaLBL_FreeDeviceMemory( recvbuf_Xz ); - ScaLBL_FreeDeviceMemory( recvbuf_XZ ); - ScaLBL_FreeDeviceMemory( recvbuf_yz ); - ScaLBL_FreeDeviceMemory( recvbuf_yZ ); - ScaLBL_FreeDeviceMemory( recvbuf_Yz ); - ScaLBL_FreeDeviceMemory( recvbuf_YZ ); - ScaLBL_FreeDeviceMemory( dvcSendList_x ); - ScaLBL_FreeDeviceMemory( dvcSendList_X ); - ScaLBL_FreeDeviceMemory( dvcSendList_y ); - ScaLBL_FreeDeviceMemory( dvcSendList_Y ); - ScaLBL_FreeDeviceMemory( dvcSendList_z ); - ScaLBL_FreeDeviceMemory( dvcSendList_Z ); - ScaLBL_FreeDeviceMemory( dvcSendList_xy ); - ScaLBL_FreeDeviceMemory( dvcSendList_xY ); - ScaLBL_FreeDeviceMemory( dvcSendList_Xy ); - ScaLBL_FreeDeviceMemory( dvcSendList_XY ); - ScaLBL_FreeDeviceMemory( dvcSendList_xz ); - ScaLBL_FreeDeviceMemory( dvcSendList_xZ ); - ScaLBL_FreeDeviceMemory( dvcSendList_Xz ); - ScaLBL_FreeDeviceMemory( dvcSendList_XZ ); - ScaLBL_FreeDeviceMemory( dvcSendList_yz ); - ScaLBL_FreeDeviceMemory( dvcSendList_yZ ); - ScaLBL_FreeDeviceMemory( dvcSendList_Yz ); - ScaLBL_FreeDeviceMemory( dvcSendList_YZ ); - ScaLBL_FreeDeviceMemory( dvcRecvList_x ); - ScaLBL_FreeDeviceMemory( dvcRecvList_X ); - ScaLBL_FreeDeviceMemory( dvcRecvList_y ); - ScaLBL_FreeDeviceMemory( dvcRecvList_Y ); - ScaLBL_FreeDeviceMemory( dvcRecvList_z ); - ScaLBL_FreeDeviceMemory( dvcRecvList_Z ); - ScaLBL_FreeDeviceMemory( dvcRecvList_xy ); - ScaLBL_FreeDeviceMemory( dvcRecvList_xY ); - ScaLBL_FreeDeviceMemory( dvcRecvList_Xy ); - ScaLBL_FreeDeviceMemory( dvcRecvList_XY ); - ScaLBL_FreeDeviceMemory( dvcRecvList_xz ); - ScaLBL_FreeDeviceMemory( dvcRecvList_xZ ); - ScaLBL_FreeDeviceMemory( dvcRecvList_Xz ); - ScaLBL_FreeDeviceMemory( dvcRecvList_XZ ); - ScaLBL_FreeDeviceMemory( dvcRecvList_yz ); - ScaLBL_FreeDeviceMemory( dvcRecvList_yZ ); - ScaLBL_FreeDeviceMemory( dvcRecvList_Yz ); - ScaLBL_FreeDeviceMemory( dvcRecvList_YZ ); - ScaLBL_FreeDeviceMemory( dvcRecvDist_x ); - ScaLBL_FreeDeviceMemory( dvcRecvDist_X ); - ScaLBL_FreeDeviceMemory( dvcRecvDist_y ); - ScaLBL_FreeDeviceMemory( dvcRecvDist_Y ); - ScaLBL_FreeDeviceMemory( dvcRecvDist_z ); - ScaLBL_FreeDeviceMemory( dvcRecvDist_Z ); - ScaLBL_FreeDeviceMemory( dvcRecvDist_xy ); - ScaLBL_FreeDeviceMemory( dvcRecvDist_xY ); - ScaLBL_FreeDeviceMemory( dvcRecvDist_Xy ); - ScaLBL_FreeDeviceMemory( dvcRecvDist_XY ); - ScaLBL_FreeDeviceMemory( dvcRecvDist_xz ); - ScaLBL_FreeDeviceMemory( dvcRecvDist_xZ ); - ScaLBL_FreeDeviceMemory( dvcRecvDist_Xz ); - ScaLBL_FreeDeviceMemory( dvcRecvDist_XZ ); - ScaLBL_FreeDeviceMemory( dvcRecvDist_yz ); - ScaLBL_FreeDeviceMemory( dvcRecvDist_yZ ); - ScaLBL_FreeDeviceMemory( dvcRecvDist_Yz ); - ScaLBL_FreeDeviceMemory( dvcRecvDist_YZ ); + ScaLBL_FreeDeviceMemory(sendbuf_x); + ScaLBL_FreeDeviceMemory(sendbuf_X); + ScaLBL_FreeDeviceMemory(sendbuf_y); + ScaLBL_FreeDeviceMemory(sendbuf_Y); + ScaLBL_FreeDeviceMemory(sendbuf_z); + ScaLBL_FreeDeviceMemory(sendbuf_Z); + ScaLBL_FreeDeviceMemory(sendbuf_xy); + ScaLBL_FreeDeviceMemory(sendbuf_xY); + ScaLBL_FreeDeviceMemory(sendbuf_Xy); + ScaLBL_FreeDeviceMemory(sendbuf_XY); + ScaLBL_FreeDeviceMemory(sendbuf_xz); + ScaLBL_FreeDeviceMemory(sendbuf_xZ); + ScaLBL_FreeDeviceMemory(sendbuf_Xz); + ScaLBL_FreeDeviceMemory(sendbuf_XZ); + ScaLBL_FreeDeviceMemory(sendbuf_yz); + ScaLBL_FreeDeviceMemory(sendbuf_yZ); + ScaLBL_FreeDeviceMemory(sendbuf_Yz); + ScaLBL_FreeDeviceMemory(sendbuf_YZ); + ScaLBL_FreeDeviceMemory(recvbuf_x); + ScaLBL_FreeDeviceMemory(recvbuf_X); + ScaLBL_FreeDeviceMemory(recvbuf_y); + ScaLBL_FreeDeviceMemory(recvbuf_Y); + ScaLBL_FreeDeviceMemory(recvbuf_z); + ScaLBL_FreeDeviceMemory(recvbuf_Z); + ScaLBL_FreeDeviceMemory(recvbuf_xy); + ScaLBL_FreeDeviceMemory(recvbuf_xY); + ScaLBL_FreeDeviceMemory(recvbuf_Xy); + ScaLBL_FreeDeviceMemory(recvbuf_XY); + ScaLBL_FreeDeviceMemory(recvbuf_xz); + ScaLBL_FreeDeviceMemory(recvbuf_xZ); + ScaLBL_FreeDeviceMemory(recvbuf_Xz); + ScaLBL_FreeDeviceMemory(recvbuf_XZ); + ScaLBL_FreeDeviceMemory(recvbuf_yz); + ScaLBL_FreeDeviceMemory(recvbuf_yZ); + ScaLBL_FreeDeviceMemory(recvbuf_Yz); + ScaLBL_FreeDeviceMemory(recvbuf_YZ); + ScaLBL_FreeDeviceMemory(dvcSendList_x); + ScaLBL_FreeDeviceMemory(dvcSendList_X); + ScaLBL_FreeDeviceMemory(dvcSendList_y); + ScaLBL_FreeDeviceMemory(dvcSendList_Y); + ScaLBL_FreeDeviceMemory(dvcSendList_z); + ScaLBL_FreeDeviceMemory(dvcSendList_Z); + ScaLBL_FreeDeviceMemory(dvcSendList_xy); + ScaLBL_FreeDeviceMemory(dvcSendList_xY); + ScaLBL_FreeDeviceMemory(dvcSendList_Xy); + ScaLBL_FreeDeviceMemory(dvcSendList_XY); + ScaLBL_FreeDeviceMemory(dvcSendList_xz); + ScaLBL_FreeDeviceMemory(dvcSendList_xZ); + ScaLBL_FreeDeviceMemory(dvcSendList_Xz); + ScaLBL_FreeDeviceMemory(dvcSendList_XZ); + ScaLBL_FreeDeviceMemory(dvcSendList_yz); + ScaLBL_FreeDeviceMemory(dvcSendList_yZ); + ScaLBL_FreeDeviceMemory(dvcSendList_Yz); + ScaLBL_FreeDeviceMemory(dvcSendList_YZ); + ScaLBL_FreeDeviceMemory(dvcRecvList_x); + ScaLBL_FreeDeviceMemory(dvcRecvList_X); + ScaLBL_FreeDeviceMemory(dvcRecvList_y); + ScaLBL_FreeDeviceMemory(dvcRecvList_Y); + ScaLBL_FreeDeviceMemory(dvcRecvList_z); + ScaLBL_FreeDeviceMemory(dvcRecvList_Z); + ScaLBL_FreeDeviceMemory(dvcRecvList_xy); + ScaLBL_FreeDeviceMemory(dvcRecvList_xY); + ScaLBL_FreeDeviceMemory(dvcRecvList_Xy); + ScaLBL_FreeDeviceMemory(dvcRecvList_XY); + ScaLBL_FreeDeviceMemory(dvcRecvList_xz); + ScaLBL_FreeDeviceMemory(dvcRecvList_xZ); + ScaLBL_FreeDeviceMemory(dvcRecvList_Xz); + ScaLBL_FreeDeviceMemory(dvcRecvList_XZ); + ScaLBL_FreeDeviceMemory(dvcRecvList_yz); + ScaLBL_FreeDeviceMemory(dvcRecvList_yZ); + ScaLBL_FreeDeviceMemory(dvcRecvList_Yz); + ScaLBL_FreeDeviceMemory(dvcRecvList_YZ); + ScaLBL_FreeDeviceMemory(dvcRecvDist_x); + ScaLBL_FreeDeviceMemory(dvcRecvDist_X); + ScaLBL_FreeDeviceMemory(dvcRecvDist_y); + ScaLBL_FreeDeviceMemory(dvcRecvDist_Y); + ScaLBL_FreeDeviceMemory(dvcRecvDist_z); + ScaLBL_FreeDeviceMemory(dvcRecvDist_Z); + ScaLBL_FreeDeviceMemory(dvcRecvDist_xy); + ScaLBL_FreeDeviceMemory(dvcRecvDist_xY); + ScaLBL_FreeDeviceMemory(dvcRecvDist_Xy); + ScaLBL_FreeDeviceMemory(dvcRecvDist_XY); + ScaLBL_FreeDeviceMemory(dvcRecvDist_xz); + ScaLBL_FreeDeviceMemory(dvcRecvDist_xZ); + ScaLBL_FreeDeviceMemory(dvcRecvDist_Xz); + ScaLBL_FreeDeviceMemory(dvcRecvDist_XZ); + ScaLBL_FreeDeviceMemory(dvcRecvDist_yz); + ScaLBL_FreeDeviceMemory(dvcRecvDist_yZ); + ScaLBL_FreeDeviceMemory(dvcRecvDist_Yz); + ScaLBL_FreeDeviceMemory(dvcRecvDist_YZ); } -double ScaLBL_Communicator::GetPerformance(int *NeighborList, double *fq, int Np){ - /* EACH MPI PROCESS GETS ITS OWN MEASUREMENT*/ - /* use MRT kernels to check performance without communication / synchronization */ - int TIMESTEPS=500; - double RLX_SETA=1.0; - double RLX_SETB = 8.f*(2.f-RLX_SETA)/(8.f-RLX_SETA); - double FX = 0.0; - double FY = 0.0; - double FZ = 0.0; +double ScaLBL_Communicator::GetPerformance(int *NeighborList, double *fq, + int Np) { + /* EACH MPI PROCESS GETS ITS OWN MEASUREMENT*/ + /* use MRT kernels to check performance without communication / synchronization */ + int TIMESTEPS = 500; + double RLX_SETA = 1.0; + double RLX_SETB = 8.f * (2.f - RLX_SETA) / (8.f - RLX_SETA); + double FX = 0.0; + double FY = 0.0; + double FZ = 0.0; ScaLBL_D3Q19_Init(fq, Np); - //.......create and start timer............ - Barrier(); + //.......create and start timer............ + Barrier(); auto t1 = std::chrono::system_clock::now(); - for (int t=0; t( t2 - t1 ).count(); - double cputime = 0.5*diff/TIMESTEPS; - // Performance obtained from each node - double MLUPS = double(Np)/cputime/1000000; - return MLUPS; + Barrier(); + // Compute the walltime per timestep + double diff = std::chrono::duration(t2 - t1).count(); + double cputime = 0.5 * diff / TIMESTEPS; + // Performance obtained from each node + double MLUPS = double(Np) / cputime / 1000000; + return MLUPS; +} +int ScaLBL_Communicator::LastExterior() { return next; } +int ScaLBL_Communicator::FirstInterior() { return first_interior; } +int ScaLBL_Communicator::LastInterior() { return last_interior; } -} -int ScaLBL_Communicator::LastExterior(){ - return next; -} -int ScaLBL_Communicator::FirstInterior(){ - return first_interior; -} -int ScaLBL_Communicator::LastInterior(){ - return last_interior; +void ScaLBL_Communicator::D3Q19_MapRecv(int Cqx, int Cqy, int Cqz, + const int *list, int start, int count, + int *d3q19_recvlist) { + int i, j, k, n, nn, idx; + int *ReturnDist; + ReturnDist = new int[count]; + + for (idx = 0; idx < count; idx++) { + + // Get the value from the list -- note that n is the index is from the send (non-local) process + n = list[idx]; // if (rank == 0) printf("@ rank:%d n=%d\n",rank,n); + // Get the 3-D indices from the send process + k = n / (Nx * Ny); + j = (n - Nx * Ny * k) / Nx; + i = n - Nx * Ny * k - Nx * j; + // if (rank ==0) printf("@ Get 3D indices from the send process: i=%d, j=%d, k=%d\n",i,j,k); + + // Streaming for the non-local distribution + i += Cqx; + j += Cqy; + k += Cqz; + // if (rank == 0) printf("@ Streaming for the non-local distribution: i=%d, j=%d, k=%d\n",i,j,k); + + // Compute 1D index for the neighbor and save + nn = k * Nx * Ny + j * Nx + i; + // if (rank == 0) printf("@ rank:%d: neighbor=%d\n",rank,nn); + ReturnDist[idx] = nn; + } + + // Return updated version to the device + ScaLBL_CopyToDevice(&d3q19_recvlist[start], ReturnDist, + count * sizeof(int)); + + // clean up the work arrays + delete[] ReturnDist; } -void ScaLBL_Communicator::D3Q19_MapRecv(int Cqx, int Cqy, int Cqz, const int *list, int start, int count, - int *d3q19_recvlist){ - int i,j,k,n,nn,idx; - int * ReturnDist; - ReturnDist=new int [count]; - - for (idx=0; idx 0) - Map(i,j,k) = -2; // this label is for parallel communication sites - else - Map(i,j,k) = -1; // this label is for solid bounce-back sites - } - } - } - - //printf("Exterior... \n"); - - // ********* Exterior ********** - // Step 1/2: Index the outer walls of the grid only - idx=0; next=0; - for (k=1; k 0){ - // Counts for the six faces - if (i>0 && i<=width) Map(n)=idx++; - else if (j>0 && j<=width) Map(n)=idx++; - else if (k>0 && k<=width) Map(n)=idx++; - else if (i>Nx-width-2 && iNy-width-2 && jNz-width-2 && k 0 ){ - Map(n) = idx++; - //neighborList[idx++] = n; // index of self in regular layout - } - } - } - } - last_interior=idx; - - Np = (last_interior/16 + 1)*16; - //printf(" Np=%i \n",Np); - - // Now use Map to determine the neighbors for each lattice direction - for (k=1;k Np) printf("ScaLBL_Communicator::MemoryOptimizedLayout: Map(%i,%i,%i) = %i > %i \n",i,j,k,Map(i,j,k),Np); - else if (!(idx<0)){ - // store the idx associated with each neighbor - // store idx for self if neighbor is in solid or out of domain - //D3Q19 = {{1,0,0},{-1,0,0} - // {0,1,0},{0,-1,0} - // {0,0,1},{0,0,-1}, - // {1,1,0},{-1,-1,0}, - // {1,-1,0},{-1,1,0}, - // {1,0,1},{-1,0,-1}, - // {1,0,-1},{-1,0,1}, - // {0,1,1},{0,-1,-1}, - // {0,1,-1},{0,-1,1}}; - int neighbor; // cycle through the neighbors of lattice site idx - neighbor=Map(i-1,j,k); - if (neighbor<0) neighborList[idx]=idx + 2*Np; - else neighborList[idx]=neighbor + 1*Np; - - neighbor=Map(i+1,j,k); - if (neighbor<0) neighborList[Np+idx] = idx + 1*Np; - else neighborList[Np+idx]= neighbor + 2*Np; - - neighbor=Map(i,j-1,k); - if (neighbor<0) neighborList[2*Np+idx]=idx + 4*Np; - else neighborList[2*Np+idx]=neighbor + 3*Np; - - neighbor=Map(i,j+1,k); - if (neighbor<0) neighborList[3*Np+idx]=idx + 3*Np; - else neighborList[3*Np+idx]=neighbor + 4*Np; - - neighbor=Map(i,j,k-1); - if (neighbor<0) neighborList[4*Np+idx]=idx + 6*Np; - else neighborList[4*Np+idx]=neighbor + 5*Np; - - neighbor=Map(i,j,k+1); - if (neighbor<0) neighborList[5*Np+idx]=idx + 5*Np; - else neighborList[5*Np+idx]=neighbor + 6*Np; - - neighbor=Map(i-1,j-1,k); - if (neighbor<0) neighborList[6*Np+idx]=idx + 8*Np; - else neighborList[6*Np+idx]=neighbor + 7*Np; - - neighbor=Map(i+1,j+1,k); - if (neighbor<0) neighborList[7*Np+idx]=idx + 7*Np; - else neighborList[7*Np+idx]=neighbor+8*Np; - - neighbor=Map(i-1,j+1,k); - if (neighbor<0) neighborList[8*Np+idx]=idx + 10*Np; - else neighborList[8*Np+idx]=neighbor + 9*Np; - - neighbor=Map(i+1,j-1,k); - if (neighbor<0) neighborList[9*Np+idx]=idx + 9*Np; - else neighborList[9*Np+idx]=neighbor + 10*Np; - - neighbor=Map(i-1,j,k-1); - if (neighbor<0) neighborList[10*Np+idx]=idx + 12*Np; - else neighborList[10*Np+idx]=neighbor + 11*Np; - - neighbor=Map(i+1,j,k+1); - if (neighbor<0) neighborList[11*Np+idx]=idx + 11*Np; - else neighborList[11*Np+idx]=neighbor + 12*Np; - - neighbor=Map(i-1,j,k+1); - if (neighbor<0) neighborList[12*Np+idx]=idx + 14*Np; - else neighborList[12*Np+idx]=neighbor + 13*Np; - - neighbor=Map(i+1,j,k-1); - if (neighbor<0) neighborList[13*Np+idx]=idx + 13*Np; - else neighborList[13*Np+idx]=neighbor + 14*Np; - - neighbor=Map(i,j-1,k-1); - if (neighbor<0) neighborList[14*Np+idx]=idx + 16*Np; - else neighborList[14*Np+idx]=neighbor + 15*Np; - - neighbor=Map(i,j+1,k+1); - if (neighbor<0) neighborList[15*Np+idx]=idx + 15*Np; - else neighborList[15*Np+idx]=neighbor + 16*Np; - - neighbor=Map(i,j-1,k+1); - if (neighbor<0) neighborList[16*Np+idx]=idx + 18*Np; - else neighborList[16*Np+idx]=neighbor + 17*Np; - - neighbor=Map(i,j+1,k-1); - if (neighbor<0) neighborList[17*Np+idx]=idx + 17*Np; - else neighborList[17*Np+idx]=neighbor + 18*Np; - } - } - } - } - - //for (idx=0; idx 0) + Map(i, j, k) = + -2; // this label is for parallel communication sites + else + Map(i, j, k) = + -1; // this label is for solid bounce-back sites + } + } + } + + //printf("Exterior... \n"); + + // ********* Exterior ********** + // Step 1/2: Index the outer walls of the grid only + idx = 0; + next = 0; + for (k = 1; k < Nz - 1; k++) { + for (j = 1; j < Ny - 1; j++) { + for (i = 1; i < Nx - 1; i++) { + // domain interior + Map(i, j, k) = -1; + // Local index + n = k * Nx * Ny + j * Nx + i; + if (id[n] > 0) { + // Counts for the six faces + if (i > 0 && i <= width) + Map(n) = idx++; + else if (j > 0 && j <= width) + Map(n) = idx++; + else if (k > 0 && k <= width) + Map(n) = idx++; + else if (i > Nx - width - 2 && i < Nx - 1) + Map(n) = idx++; + else if (j > Ny - width - 2 && j < Ny - 1) + Map(n) = idx++; + else if (k > Nz - width - 2 && k < Nz - 1) + Map(n) = idx++; + } + } + } + } + next = idx; + + // ********* Interior ********** + // align the next read + first_interior = (next / 16 + 1) * 16; + idx = first_interior; + // Step 2/2: Next loop over the domain interior in block-cyclic fashion + for (k = width + 1; k < Nz - width - 1; k++) { + for (j = width + 1; j < Ny - width - 1; j++) { + for (i = width + 1; i < Nx - width - 1; i++) { + // Local index (regular layout) + n = k * Nx * Ny + j * Nx + i; + if (id[n] > 0) { + Map(n) = idx++; + //neighborList[idx++] = n; // index of self in regular layout + } + } + } + } + last_interior = idx; + + Np = (last_interior / 16 + 1) * 16; + //printf(" Np=%i \n",Np); + + // Now use Map to determine the neighbors for each lattice direction + for (k = 1; k < Nz - 1; k++) { + for (j = 1; j < Ny - 1; j++) { + for (i = 1; i < Nx - 1; i++) { + n = k * Nx * Ny + j * Nx + i; + idx = Map(i, j, k); + if (idx > Np) + printf("ScaLBL_Communicator::MemoryOptimizedLayout: " + "Map(%i,%i,%i) = %i > %i \n", + i, j, k, Map(i, j, k), Np); + else if (!(idx < 0)) { + // store the idx associated with each neighbor + // store idx for self if neighbor is in solid or out of domain + //D3Q19 = {{1,0,0},{-1,0,0} + // {0,1,0},{0,-1,0} + // {0,0,1},{0,0,-1}, + // {1,1,0},{-1,-1,0}, + // {1,-1,0},{-1,1,0}, + // {1,0,1},{-1,0,-1}, + // {1,0,-1},{-1,0,1}, + // {0,1,1},{0,-1,-1}, + // {0,1,-1},{0,-1,1}}; + int neighbor; // cycle through the neighbors of lattice site idx + neighbor = Map(i - 1, j, k); + if (neighbor < 0) + neighborList[idx] = idx + 2 * Np; + else + neighborList[idx] = neighbor + 1 * Np; + + neighbor = Map(i + 1, j, k); + if (neighbor < 0) + neighborList[Np + idx] = idx + 1 * Np; + else + neighborList[Np + idx] = neighbor + 2 * Np; + + neighbor = Map(i, j - 1, k); + if (neighbor < 0) + neighborList[2 * Np + idx] = idx + 4 * Np; + else + neighborList[2 * Np + idx] = neighbor + 3 * Np; + + neighbor = Map(i, j + 1, k); + if (neighbor < 0) + neighborList[3 * Np + idx] = idx + 3 * Np; + else + neighborList[3 * Np + idx] = neighbor + 4 * Np; + + neighbor = Map(i, j, k - 1); + if (neighbor < 0) + neighborList[4 * Np + idx] = idx + 6 * Np; + else + neighborList[4 * Np + idx] = neighbor + 5 * Np; + + neighbor = Map(i, j, k + 1); + if (neighbor < 0) + neighborList[5 * Np + idx] = idx + 5 * Np; + else + neighborList[5 * Np + idx] = neighbor + 6 * Np; + + neighbor = Map(i - 1, j - 1, k); + if (neighbor < 0) + neighborList[6 * Np + idx] = idx + 8 * Np; + else + neighborList[6 * Np + idx] = neighbor + 7 * Np; + + neighbor = Map(i + 1, j + 1, k); + if (neighbor < 0) + neighborList[7 * Np + idx] = idx + 7 * Np; + else + neighborList[7 * Np + idx] = neighbor + 8 * Np; + + neighbor = Map(i - 1, j + 1, k); + if (neighbor < 0) + neighborList[8 * Np + idx] = idx + 10 * Np; + else + neighborList[8 * Np + idx] = neighbor + 9 * Np; + + neighbor = Map(i + 1, j - 1, k); + if (neighbor < 0) + neighborList[9 * Np + idx] = idx + 9 * Np; + else + neighborList[9 * Np + idx] = neighbor + 10 * Np; + + neighbor = Map(i - 1, j, k - 1); + if (neighbor < 0) + neighborList[10 * Np + idx] = idx + 12 * Np; + else + neighborList[10 * Np + idx] = neighbor + 11 * Np; + + neighbor = Map(i + 1, j, k + 1); + if (neighbor < 0) + neighborList[11 * Np + idx] = idx + 11 * Np; + else + neighborList[11 * Np + idx] = neighbor + 12 * Np; + + neighbor = Map(i - 1, j, k + 1); + if (neighbor < 0) + neighborList[12 * Np + idx] = idx + 14 * Np; + else + neighborList[12 * Np + idx] = neighbor + 13 * Np; + + neighbor = Map(i + 1, j, k - 1); + if (neighbor < 0) + neighborList[13 * Np + idx] = idx + 13 * Np; + else + neighborList[13 * Np + idx] = neighbor + 14 * Np; + + neighbor = Map(i, j - 1, k - 1); + if (neighbor < 0) + neighborList[14 * Np + idx] = idx + 16 * Np; + else + neighborList[14 * Np + idx] = neighbor + 15 * Np; + + neighbor = Map(i, j + 1, k + 1); + if (neighbor < 0) + neighborList[15 * Np + idx] = idx + 15 * Np; + else + neighborList[15 * Np + idx] = neighbor + 16 * Np; + + neighbor = Map(i, j - 1, k + 1); + if (neighbor < 0) + neighborList[16 * Np + idx] = idx + 18 * Np; + else + neighborList[16 * Np + idx] = neighbor + 17 * Np; + + neighbor = Map(i, j + 1, k - 1); + if (neighbor < 0) + neighborList[17 * Np + idx] = idx + 17 * Np; + else + neighborList[17 * Np + idx] = neighbor + 18 * Np; + } + } + } + } + + //for (idx=0; idx 0 && kproc == 0){ - // don't unpack little z - //...Packing for Z face(5,11,14,15,18)................................ - ScaLBL_D3Q7_Unpack(5,dvcRecvDist_Z,0,recvCount_Z,recvbuf_Z,Aq,N); - ScaLBL_D3Q7_Unpack(5,dvcRecvDist_Z,recvCount_Z,recvCount_Z,recvbuf_Z,Bq,N); - } - else if (BoundaryCondition > 0 && kproc == nprocz-1){ - // don't unpack big z - //...Packing for z face(6,12,13,16,17)................................ - ScaLBL_D3Q7_Unpack(6,dvcRecvDist_z,0,recvCount_z,recvbuf_z,Aq,N); - ScaLBL_D3Q7_Unpack(6,dvcRecvDist_z,recvCount_z,recvCount_z,recvbuf_z,Bq,N); - } - else { - //...Packing for z face(6,12,13,16,17)................................ - ScaLBL_D3Q7_Unpack(6,dvcRecvDist_z,0,recvCount_z,recvbuf_z,Aq,N); - ScaLBL_D3Q7_Unpack(6,dvcRecvDist_z,recvCount_z,recvCount_z,recvbuf_z,Bq,N); - //...Packing for Z face(5,11,14,15,18)................................ - ScaLBL_D3Q7_Unpack(5,dvcRecvDist_Z,0,recvCount_Z,recvbuf_Z,Aq,N); - ScaLBL_D3Q7_Unpack(5,dvcRecvDist_Z,recvCount_Z,recvCount_Z,recvbuf_Z,Bq,N); - } - - //................................................................................... - Lock=false; // unlock the communicator after communications complete - //................................................................................... + if (BoundaryCondition > 0 && kproc == 0) { + // don't unpack little z + //...Packing for Z face(5,11,14,15,18)................................ + ScaLBL_D3Q7_Unpack(5, dvcRecvDist_Z, 0, recvCount_Z, recvbuf_Z, Aq, N); + ScaLBL_D3Q7_Unpack(5, dvcRecvDist_Z, recvCount_Z, recvCount_Z, + recvbuf_Z, Bq, N); + } else if (BoundaryCondition > 0 && kproc == nprocz - 1) { + // don't unpack big z + //...Packing for z face(6,12,13,16,17)................................ + ScaLBL_D3Q7_Unpack(6, dvcRecvDist_z, 0, recvCount_z, recvbuf_z, Aq, N); + ScaLBL_D3Q7_Unpack(6, dvcRecvDist_z, recvCount_z, recvCount_z, + recvbuf_z, Bq, N); + } else { + //...Packing for z face(6,12,13,16,17)................................ + ScaLBL_D3Q7_Unpack(6, dvcRecvDist_z, 0, recvCount_z, recvbuf_z, Aq, N); + ScaLBL_D3Q7_Unpack(6, dvcRecvDist_z, recvCount_z, recvCount_z, + recvbuf_z, Bq, N); + //...Packing for Z face(5,11,14,15,18)................................ + ScaLBL_D3Q7_Unpack(5, dvcRecvDist_Z, 0, recvCount_Z, recvbuf_Z, Aq, N); + ScaLBL_D3Q7_Unpack(5, dvcRecvDist_Z, recvCount_Z, recvCount_Z, + recvbuf_Z, Bq, N); + } + //................................................................................... + Lock = false; // unlock the communicator after communications complete + //................................................................................... } -void ScaLBL_Communicator::SendD3Q7AA(double *Aq, int Component){ +void ScaLBL_Communicator::SendD3Q7AA(double *Aq, int Component) { - // NOTE: the center distribution f0 must NOT be at the start of feven, provide offset to start of f2 - if (Lock==true){ - ERROR("ScaLBL Error (SendD3Q7): ScaLBL_Communicator is locked -- did you forget to match Send/Recv calls?"); - } - else{ - Lock=true; - } - // assign tag of 19 to D3Q19 communication - sendtag = recvtag = 7; - ScaLBL_DeviceBarrier(); - // Pack the distributions - //...Packing for x face(2,8,10,12,14)................................ - ScaLBL_D3Q19_Pack(2,dvcSendList_x,0,sendCount_x,sendbuf_x,&Aq[Component*7*N],N); - req1[0] = MPI_COMM_SCALBL.Isend(sendbuf_x, sendCount_x,rank_x,sendtag); - req2[0] = MPI_COMM_SCALBL.Irecv(recvbuf_X, recvCount_X,rank_X,recvtag); - - //...Packing for X face(1,7,9,11,13)................................ - ScaLBL_D3Q19_Pack(1,dvcSendList_X,0,sendCount_X,sendbuf_X,&Aq[Component*7*N],N); - req1[1] = MPI_COMM_SCALBL.Isend(sendbuf_X, sendCount_X,rank_X,sendtag); - req2[1] = MPI_COMM_SCALBL.Irecv(recvbuf_x, recvCount_x,rank_x,recvtag); - - //...Packing for y face(4,8,9,16,18)................................. - ScaLBL_D3Q19_Pack(4,dvcSendList_y,0,sendCount_y,sendbuf_y,&Aq[Component*7*N],N); - req1[2] = MPI_COMM_SCALBL.Isend(sendbuf_y, sendCount_y,rank_y,sendtag); - req2[2] = MPI_COMM_SCALBL.Irecv(recvbuf_Y, recvCount_Y,rank_Y,recvtag); - - //...Packing for Y face(3,7,10,15,17)................................. - ScaLBL_D3Q19_Pack(3,dvcSendList_Y,0,sendCount_Y,sendbuf_Y,&Aq[Component*7*N],N); - req1[3] = MPI_COMM_SCALBL.Isend(sendbuf_Y, sendCount_Y,rank_Y,sendtag); - req2[3] = MPI_COMM_SCALBL.Irecv(recvbuf_y, recvCount_y,rank_y,recvtag); - - //...Packing for z face(6,12,13,16,17)................................ - ScaLBL_D3Q19_Pack(6,dvcSendList_z,0,sendCount_z,sendbuf_z,&Aq[Component*7*N],N); - req1[4] = MPI_COMM_SCALBL.Isend(sendbuf_z, sendCount_z,rank_z,sendtag); - req2[4] = MPI_COMM_SCALBL.Irecv(recvbuf_Z, recvCount_Z,rank_Z,recvtag); - - //...Packing for Z face(5,11,14,15,18)................................ - ScaLBL_D3Q19_Pack(5,dvcSendList_Z,0,sendCount_Z,sendbuf_Z,&Aq[Component*7*N],N); - req1[5] = MPI_COMM_SCALBL.Isend(sendbuf_Z, sendCount_Z,rank_Z,sendtag); - req2[5] = MPI_COMM_SCALBL.Irecv(recvbuf_z, recvCount_z,rank_z,recvtag); + // NOTE: the center distribution f0 must NOT be at the start of feven, provide offset to start of f2 + if (Lock == true) { + ERROR("ScaLBL Error (SendD3Q7): ScaLBL_Communicator is locked -- did " + "you forget to match Send/Recv calls?"); + } else { + Lock = true; + } + // assign tag of 19 to D3Q19 communication + sendtag = recvtag = 7; + ScaLBL_DeviceBarrier(); + // Pack the distributions + //...Packing for x face(2,8,10,12,14)................................ + ScaLBL_D3Q19_Pack(2, dvcSendList_x, 0, sendCount_x, sendbuf_x, + &Aq[Component * 7 * N], N); + req1[0] = MPI_COMM_SCALBL.Isend(sendbuf_x, sendCount_x, rank_x, sendtag); + req2[0] = MPI_COMM_SCALBL.Irecv(recvbuf_X, recvCount_X, rank_X, recvtag); + + //...Packing for X face(1,7,9,11,13)................................ + ScaLBL_D3Q19_Pack(1, dvcSendList_X, 0, sendCount_X, sendbuf_X, + &Aq[Component * 7 * N], N); + req1[1] = MPI_COMM_SCALBL.Isend(sendbuf_X, sendCount_X, rank_X, sendtag); + req2[1] = MPI_COMM_SCALBL.Irecv(recvbuf_x, recvCount_x, rank_x, recvtag); + + //...Packing for y face(4,8,9,16,18)................................. + ScaLBL_D3Q19_Pack(4, dvcSendList_y, 0, sendCount_y, sendbuf_y, + &Aq[Component * 7 * N], N); + req1[2] = MPI_COMM_SCALBL.Isend(sendbuf_y, sendCount_y, rank_y, sendtag); + req2[2] = MPI_COMM_SCALBL.Irecv(recvbuf_Y, recvCount_Y, rank_Y, recvtag); + + //...Packing for Y face(3,7,10,15,17)................................. + ScaLBL_D3Q19_Pack(3, dvcSendList_Y, 0, sendCount_Y, sendbuf_Y, + &Aq[Component * 7 * N], N); + req1[3] = MPI_COMM_SCALBL.Isend(sendbuf_Y, sendCount_Y, rank_Y, sendtag); + req2[3] = MPI_COMM_SCALBL.Irecv(recvbuf_y, recvCount_y, rank_y, recvtag); + + //...Packing for z face(6,12,13,16,17)................................ + ScaLBL_D3Q19_Pack(6, dvcSendList_z, 0, sendCount_z, sendbuf_z, + &Aq[Component * 7 * N], N); + req1[4] = MPI_COMM_SCALBL.Isend(sendbuf_z, sendCount_z, rank_z, sendtag); + req2[4] = MPI_COMM_SCALBL.Irecv(recvbuf_Z, recvCount_Z, rank_Z, recvtag); + + //...Packing for Z face(5,11,14,15,18)................................ + ScaLBL_D3Q19_Pack(5, dvcSendList_Z, 0, sendCount_Z, sendbuf_Z, + &Aq[Component * 7 * N], N); + req1[5] = MPI_COMM_SCALBL.Isend(sendbuf_Z, sendCount_Z, rank_Z, sendtag); + req2[5] = MPI_COMM_SCALBL.Irecv(recvbuf_z, recvCount_z, rank_z, recvtag); } +void ScaLBL_Communicator::RecvD3Q7AA(double *Aq, int Component) { -void ScaLBL_Communicator::RecvD3Q7AA(double *Aq, int Component){ + // NOTE: the center distribution f0 must NOT be at the start of feven, provide offset to start of f2 + //................................................................................... + // Wait for completion of D3Q19 communication + MPI_COMM_SCALBL.waitAll(6, req1); + MPI_COMM_SCALBL.waitAll(6, req2); + ScaLBL_DeviceBarrier(); - // NOTE: the center distribution f0 must NOT be at the start of feven, provide offset to start of f2 - //................................................................................... - // Wait for completion of D3Q19 communication - MPI_COMM_SCALBL.waitAll(6,req1); - MPI_COMM_SCALBL.waitAll(6,req2); - ScaLBL_DeviceBarrier(); + //................................................................................... + // NOTE: AA Routine writes to opposite + // Unpack the distributions on the device + //................................................................................... + //...Unpacking for x face(2,8,10,12,14)................................ + ScaLBL_D3Q7_Unpack(2, dvcRecvDist_x, 0, recvCount_x, recvbuf_x, + &Aq[Component * 7 * N], N); + //................................................................................... + //...Packing for X face(1,7,9,11,13)................................ + ScaLBL_D3Q7_Unpack(1, dvcRecvDist_X, 0, recvCount_X, recvbuf_X, + &Aq[Component * 7 * N], N); + //................................................................................... + //...Packing for y face(4,8,9,16,18)................................. + ScaLBL_D3Q7_Unpack(4, dvcRecvDist_y, 0, recvCount_y, recvbuf_y, + &Aq[Component * 7 * N], N); + //................................................................................... + //...Packing for Y face(3,7,10,15,17)................................. + ScaLBL_D3Q7_Unpack(3, dvcRecvDist_Y, 0, recvCount_Y, recvbuf_Y, + &Aq[Component * 7 * N], N); + //................................................................................... - //................................................................................... - // NOTE: AA Routine writes to opposite - // Unpack the distributions on the device - //................................................................................... - //...Unpacking for x face(2,8,10,12,14)................................ - ScaLBL_D3Q7_Unpack(2,dvcRecvDist_x,0,recvCount_x,recvbuf_x,&Aq[Component*7*N],N); - //................................................................................... - //...Packing for X face(1,7,9,11,13)................................ - ScaLBL_D3Q7_Unpack(1,dvcRecvDist_X,0,recvCount_X,recvbuf_X,&Aq[Component*7*N],N); - //................................................................................... - //...Packing for y face(4,8,9,16,18)................................. - ScaLBL_D3Q7_Unpack(4,dvcRecvDist_y,0,recvCount_y,recvbuf_y,&Aq[Component*7*N],N); - //................................................................................... - //...Packing for Y face(3,7,10,15,17)................................. - ScaLBL_D3Q7_Unpack(3,dvcRecvDist_Y,0,recvCount_Y,recvbuf_Y,&Aq[Component*7*N],N); - //................................................................................... - - if (BoundaryCondition > 0){ - if (kproc != 0){ - //...Packing for z face(6,12,13,16,17)................................ - ScaLBL_D3Q7_Unpack(6,dvcRecvDist_z,0,recvCount_z,recvbuf_z,&Aq[Component*7*N],N); - } - if (kproc != nprocz-1){ - //...Packing for Z face(5,11,14,15,18)................................ - ScaLBL_D3Q7_Unpack(5,dvcRecvDist_Z,0,recvCount_Z,recvbuf_Z,&Aq[Component*7*N],N); - } - } - else { - //...Packing for z face(6,12,13,16,17)................................ - ScaLBL_D3Q7_Unpack(6,dvcRecvDist_z,0,recvCount_z,recvbuf_z,&Aq[Component*7*N],N); - //...Packing for Z face(5,11,14,15,18)................................ - ScaLBL_D3Q7_Unpack(5,dvcRecvDist_Z,0,recvCount_Z,recvbuf_Z,&Aq[Component*7*N],N); - } - - //................................................................................... - Lock=false; // unlock the communicator after communications complete - //................................................................................... + if (BoundaryCondition > 0) { + if (kproc != 0) { + //...Packing for z face(6,12,13,16,17)................................ + ScaLBL_D3Q7_Unpack(6, dvcRecvDist_z, 0, recvCount_z, recvbuf_z, + &Aq[Component * 7 * N], N); + } + if (kproc != nprocz - 1) { + //...Packing for Z face(5,11,14,15,18)................................ + ScaLBL_D3Q7_Unpack(5, dvcRecvDist_Z, 0, recvCount_Z, recvbuf_Z, + &Aq[Component * 7 * N], N); + } + } else { + //...Packing for z face(6,12,13,16,17)................................ + ScaLBL_D3Q7_Unpack(6, dvcRecvDist_z, 0, recvCount_z, recvbuf_z, + &Aq[Component * 7 * N], N); + //...Packing for Z face(5,11,14,15,18)................................ + ScaLBL_D3Q7_Unpack(5, dvcRecvDist_Z, 0, recvCount_Z, recvbuf_Z, + &Aq[Component * 7 * N], N); + } + //................................................................................... + Lock = false; // unlock the communicator after communications complete + //................................................................................... } -void ScaLBL_Communicator::TriSendD3Q7AA(double *Aq, double *Bq, double *Cq){ +void ScaLBL_Communicator::TriSendD3Q7AA(double *Aq, double *Bq, double *Cq) { - // NOTE: the center distribution f0 must NOT be at the start of feven, provide offset to start of f2 - if (Lock==true){ - ERROR("ScaLBL Error (SendD3Q19): ScaLBL_Communicator is locked -- did you forget to match Send/Recv calls?"); - } - else{ - Lock=true; - } - // assign tag of 19 to D3Q19 communication - sendtag = recvtag = 15; - ScaLBL_DeviceBarrier(); - // Pack the distributions - //...Packing for x face(2,8,10,12,14)................................ - ScaLBL_D3Q19_Pack(2,dvcSendList_x,0,sendCount_x,sendbuf_x,Aq,N); - ScaLBL_D3Q19_Pack(2,dvcSendList_x,sendCount_x,sendCount_x,sendbuf_x,Bq,N); - ScaLBL_D3Q19_Pack(2,dvcSendList_x,2*sendCount_x,sendCount_x,sendbuf_x,Cq,N); - //...Packing for X face(1,7,9,11,13)................................ - ScaLBL_D3Q19_Pack(1,dvcSendList_X,0,sendCount_X,sendbuf_X,Aq,N); - ScaLBL_D3Q19_Pack(1,dvcSendList_X,sendCount_X,sendCount_X,sendbuf_X,Bq,N); - ScaLBL_D3Q19_Pack(1,dvcSendList_X,2*sendCount_X,sendCount_X,sendbuf_X,Cq,N); - //...Packing for y face(4,8,9,16,18)................................. - ScaLBL_D3Q19_Pack(4,dvcSendList_y,0,sendCount_y,sendbuf_y,Aq,N); - ScaLBL_D3Q19_Pack(4,dvcSendList_y,sendCount_y,sendCount_y,sendbuf_y,Bq,N); - ScaLBL_D3Q19_Pack(4,dvcSendList_y,2*sendCount_y,sendCount_y,sendbuf_y,Cq,N); - //...Packing for Y face(3,7,10,15,17)................................. - ScaLBL_D3Q19_Pack(3,dvcSendList_Y,0,sendCount_Y,sendbuf_Y,Aq,N); - ScaLBL_D3Q19_Pack(3,dvcSendList_Y,sendCount_Y,sendCount_Y,sendbuf_Y,Bq,N); - ScaLBL_D3Q19_Pack(3,dvcSendList_Y,2*sendCount_Y,sendCount_Y,sendbuf_Y,Cq,N); - //...Packing for z face(6,12,13,16,17)................................ - ScaLBL_D3Q19_Pack(6,dvcSendList_z,0,sendCount_z,sendbuf_z,Aq,N); - ScaLBL_D3Q19_Pack(6,dvcSendList_z,sendCount_z,sendCount_z,sendbuf_z,Bq,N); - ScaLBL_D3Q19_Pack(6,dvcSendList_z,2*sendCount_z,sendCount_z,sendbuf_z,Cq,N); - //...Packing for Z face(5,11,14,15,18)................................ - ScaLBL_D3Q19_Pack(5,dvcSendList_Z,0,sendCount_Z,sendbuf_Z,Aq,N); - ScaLBL_D3Q19_Pack(5,dvcSendList_Z,sendCount_Z,sendCount_Z,sendbuf_Z,Bq,N); - ScaLBL_D3Q19_Pack(5,dvcSendList_Z,2*sendCount_Z,sendCount_Z,sendbuf_Z,Cq,N); - - //................................................................................... - // Send all the distributions - req1[0] = MPI_COMM_SCALBL.Isend(sendbuf_x, 3*sendCount_x,rank_x,sendtag); - req2[0] = MPI_COMM_SCALBL.Irecv(recvbuf_X, 3*recvCount_X,rank_X,recvtag); - req1[1] = MPI_COMM_SCALBL.Isend(sendbuf_X, 3*sendCount_X,rank_X,sendtag); - req2[1] = MPI_COMM_SCALBL.Irecv(recvbuf_x, 3*recvCount_x,rank_x,recvtag); - req1[2] = MPI_COMM_SCALBL.Isend(sendbuf_y, 3*sendCount_y,rank_y,sendtag); - req2[2] = MPI_COMM_SCALBL.Irecv(recvbuf_Y, 3*recvCount_Y,rank_Y,recvtag); - req1[3] = MPI_COMM_SCALBL.Isend(sendbuf_Y, 3*sendCount_Y,rank_Y,sendtag); - req2[3] = MPI_COMM_SCALBL.Irecv(recvbuf_y, 3*recvCount_y,rank_y,recvtag); - req1[4] = MPI_COMM_SCALBL.Isend(sendbuf_z, 3*sendCount_z,rank_z,sendtag); - req2[4] = MPI_COMM_SCALBL.Irecv(recvbuf_Z, 3*recvCount_Z,rank_Z,recvtag); - req1[5] = MPI_COMM_SCALBL.Isend(sendbuf_Z, 3*sendCount_Z,rank_Z,sendtag); - req2[5] = MPI_COMM_SCALBL.Irecv(recvbuf_z, 3*recvCount_z,rank_z,recvtag); + // NOTE: the center distribution f0 must NOT be at the start of feven, provide offset to start of f2 + if (Lock == true) { + ERROR("ScaLBL Error (SendD3Q19): ScaLBL_Communicator is locked -- did " + "you forget to match Send/Recv calls?"); + } else { + Lock = true; + } + // assign tag of 19 to D3Q19 communication + sendtag = recvtag = 15; + ScaLBL_DeviceBarrier(); + // Pack the distributions + //...Packing for x face(2,8,10,12,14)................................ + ScaLBL_D3Q19_Pack(2, dvcSendList_x, 0, sendCount_x, sendbuf_x, Aq, N); + ScaLBL_D3Q19_Pack(2, dvcSendList_x, sendCount_x, sendCount_x, sendbuf_x, Bq, + N); + ScaLBL_D3Q19_Pack(2, dvcSendList_x, 2 * sendCount_x, sendCount_x, sendbuf_x, + Cq, N); + //...Packing for X face(1,7,9,11,13)................................ + ScaLBL_D3Q19_Pack(1, dvcSendList_X, 0, sendCount_X, sendbuf_X, Aq, N); + ScaLBL_D3Q19_Pack(1, dvcSendList_X, sendCount_X, sendCount_X, sendbuf_X, Bq, + N); + ScaLBL_D3Q19_Pack(1, dvcSendList_X, 2 * sendCount_X, sendCount_X, sendbuf_X, + Cq, N); + //...Packing for y face(4,8,9,16,18)................................. + ScaLBL_D3Q19_Pack(4, dvcSendList_y, 0, sendCount_y, sendbuf_y, Aq, N); + ScaLBL_D3Q19_Pack(4, dvcSendList_y, sendCount_y, sendCount_y, sendbuf_y, Bq, + N); + ScaLBL_D3Q19_Pack(4, dvcSendList_y, 2 * sendCount_y, sendCount_y, sendbuf_y, + Cq, N); + //...Packing for Y face(3,7,10,15,17)................................. + ScaLBL_D3Q19_Pack(3, dvcSendList_Y, 0, sendCount_Y, sendbuf_Y, Aq, N); + ScaLBL_D3Q19_Pack(3, dvcSendList_Y, sendCount_Y, sendCount_Y, sendbuf_Y, Bq, + N); + ScaLBL_D3Q19_Pack(3, dvcSendList_Y, 2 * sendCount_Y, sendCount_Y, sendbuf_Y, + Cq, N); + //...Packing for z face(6,12,13,16,17)................................ + ScaLBL_D3Q19_Pack(6, dvcSendList_z, 0, sendCount_z, sendbuf_z, Aq, N); + ScaLBL_D3Q19_Pack(6, dvcSendList_z, sendCount_z, sendCount_z, sendbuf_z, Bq, + N); + ScaLBL_D3Q19_Pack(6, dvcSendList_z, 2 * sendCount_z, sendCount_z, sendbuf_z, + Cq, N); + //...Packing for Z face(5,11,14,15,18)................................ + ScaLBL_D3Q19_Pack(5, dvcSendList_Z, 0, sendCount_Z, sendbuf_Z, Aq, N); + ScaLBL_D3Q19_Pack(5, dvcSendList_Z, sendCount_Z, sendCount_Z, sendbuf_Z, Bq, + N); + ScaLBL_D3Q19_Pack(5, dvcSendList_Z, 2 * sendCount_Z, sendCount_Z, sendbuf_Z, + Cq, N); + //................................................................................... + // Send all the distributions + req1[0] = + MPI_COMM_SCALBL.Isend(sendbuf_x, 3 * sendCount_x, rank_x, sendtag); + req2[0] = + MPI_COMM_SCALBL.Irecv(recvbuf_X, 3 * recvCount_X, rank_X, recvtag); + req1[1] = + MPI_COMM_SCALBL.Isend(sendbuf_X, 3 * sendCount_X, rank_X, sendtag); + req2[1] = + MPI_COMM_SCALBL.Irecv(recvbuf_x, 3 * recvCount_x, rank_x, recvtag); + req1[2] = + MPI_COMM_SCALBL.Isend(sendbuf_y, 3 * sendCount_y, rank_y, sendtag); + req2[2] = + MPI_COMM_SCALBL.Irecv(recvbuf_Y, 3 * recvCount_Y, rank_Y, recvtag); + req1[3] = + MPI_COMM_SCALBL.Isend(sendbuf_Y, 3 * sendCount_Y, rank_Y, sendtag); + req2[3] = + MPI_COMM_SCALBL.Irecv(recvbuf_y, 3 * recvCount_y, rank_y, recvtag); + req1[4] = + MPI_COMM_SCALBL.Isend(sendbuf_z, 3 * sendCount_z, rank_z, sendtag); + req2[4] = + MPI_COMM_SCALBL.Irecv(recvbuf_Z, 3 * recvCount_Z, rank_Z, recvtag); + req1[5] = + MPI_COMM_SCALBL.Isend(sendbuf_Z, 3 * sendCount_Z, rank_Z, sendtag); + req2[5] = + MPI_COMM_SCALBL.Irecv(recvbuf_z, 3 * recvCount_z, rank_z, recvtag); } +void ScaLBL_Communicator::TriRecvD3Q7AA(double *Aq, double *Bq, double *Cq) { -void ScaLBL_Communicator::TriRecvD3Q7AA(double *Aq, double *Bq, double *Cq){ + // NOTE: the center distribution f0 must NOT be at the start of feven, provide offset to start of f2 + //................................................................................... + // Wait for completion of D3Q19 communication + MPI_COMM_SCALBL.waitAll(6, req1); + MPI_COMM_SCALBL.waitAll(6, req2); + ScaLBL_DeviceBarrier(); - // NOTE: the center distribution f0 must NOT be at the start of feven, provide offset to start of f2 - //................................................................................... - // Wait for completion of D3Q19 communication - MPI_COMM_SCALBL.waitAll(6,req1); - MPI_COMM_SCALBL.waitAll(6,req2); - ScaLBL_DeviceBarrier(); + //................................................................................... + // NOTE: AA Routine writes to opposite + // Unpack the distributions on the device + //................................................................................... + //...Unpacking for x face(2,8,10,12,14)................................ + ScaLBL_D3Q7_Unpack(2, dvcRecvDist_x, 0, recvCount_x, recvbuf_x, Aq, N); + ScaLBL_D3Q7_Unpack(2, dvcRecvDist_x, recvCount_x, recvCount_x, recvbuf_x, + Bq, N); + ScaLBL_D3Q7_Unpack(2, dvcRecvDist_x, 2 * recvCount_x, recvCount_x, + recvbuf_x, Cq, N); + //................................................................................... + //...Packing for X face(1,7,9,11,13)................................ + ScaLBL_D3Q7_Unpack(1, dvcRecvDist_X, 0, recvCount_X, recvbuf_X, Aq, N); + ScaLBL_D3Q7_Unpack(1, dvcRecvDist_X, recvCount_X, recvCount_X, recvbuf_X, + Bq, N); + ScaLBL_D3Q7_Unpack(1, dvcRecvDist_X, 2 * recvCount_X, recvCount_X, + recvbuf_X, Cq, N); + //................................................................................... + //...Packing for y face(4,8,9,16,18)................................. + ScaLBL_D3Q7_Unpack(4, dvcRecvDist_y, 0, recvCount_y, recvbuf_y, Aq, N); + ScaLBL_D3Q7_Unpack(4, dvcRecvDist_y, recvCount_y, recvCount_y, recvbuf_y, + Bq, N); + ScaLBL_D3Q7_Unpack(4, dvcRecvDist_y, 2 * recvCount_y, recvCount_y, + recvbuf_y, Cq, N); + //................................................................................... + //...Packing for Y face(3,7,10,15,17)................................. + ScaLBL_D3Q7_Unpack(3, dvcRecvDist_Y, 0, recvCount_Y, recvbuf_Y, Aq, N); + ScaLBL_D3Q7_Unpack(3, dvcRecvDist_Y, recvCount_Y, recvCount_Y, recvbuf_Y, + Bq, N); + ScaLBL_D3Q7_Unpack(3, dvcRecvDist_Y, 2 * recvCount_Y, recvCount_Y, + recvbuf_Y, Cq, N); + //................................................................................... - //................................................................................... - // NOTE: AA Routine writes to opposite - // Unpack the distributions on the device - //................................................................................... - //...Unpacking for x face(2,8,10,12,14)................................ - ScaLBL_D3Q7_Unpack(2,dvcRecvDist_x,0,recvCount_x,recvbuf_x,Aq,N); - ScaLBL_D3Q7_Unpack(2,dvcRecvDist_x,recvCount_x,recvCount_x,recvbuf_x,Bq,N); - ScaLBL_D3Q7_Unpack(2,dvcRecvDist_x,2*recvCount_x,recvCount_x,recvbuf_x,Cq,N); - //................................................................................... - //...Packing for X face(1,7,9,11,13)................................ - ScaLBL_D3Q7_Unpack(1,dvcRecvDist_X,0,recvCount_X,recvbuf_X,Aq,N); - ScaLBL_D3Q7_Unpack(1,dvcRecvDist_X,recvCount_X,recvCount_X,recvbuf_X,Bq,N); - ScaLBL_D3Q7_Unpack(1,dvcRecvDist_X,2*recvCount_X,recvCount_X,recvbuf_X,Cq,N); - //................................................................................... - //...Packing for y face(4,8,9,16,18)................................. - ScaLBL_D3Q7_Unpack(4,dvcRecvDist_y,0,recvCount_y,recvbuf_y,Aq,N); - ScaLBL_D3Q7_Unpack(4,dvcRecvDist_y,recvCount_y,recvCount_y,recvbuf_y,Bq,N); - ScaLBL_D3Q7_Unpack(4,dvcRecvDist_y,2*recvCount_y,recvCount_y,recvbuf_y,Cq,N); - //................................................................................... - //...Packing for Y face(3,7,10,15,17)................................. - ScaLBL_D3Q7_Unpack(3,dvcRecvDist_Y,0,recvCount_Y,recvbuf_Y,Aq,N); - ScaLBL_D3Q7_Unpack(3,dvcRecvDist_Y,recvCount_Y,recvCount_Y,recvbuf_Y,Bq,N); - ScaLBL_D3Q7_Unpack(3,dvcRecvDist_Y,2*recvCount_Y,recvCount_Y,recvbuf_Y,Cq,N); - //................................................................................... - - if (BoundaryCondition > 0 && kproc == 0){ - // don't unpack little z - //...Packing for Z face(5,11,14,15,18)................................ - ScaLBL_D3Q7_Unpack(5,dvcRecvDist_Z,0,recvCount_Z,recvbuf_Z,Aq,N); - ScaLBL_D3Q7_Unpack(5,dvcRecvDist_Z,recvCount_Z,recvCount_Z,recvbuf_Z,Bq,N); - ScaLBL_D3Q7_Unpack(5,dvcRecvDist_Z,2*recvCount_Z,recvCount_Z,recvbuf_Z,Cq,N); - } - else if (BoundaryCondition > 0 && kproc == nprocz-1){ - // don't unpack big z - //...Packing for z face(6,12,13,16,17)................................ - ScaLBL_D3Q7_Unpack(6,dvcRecvDist_z,0,recvCount_z,recvbuf_z,Aq,N); - ScaLBL_D3Q7_Unpack(6,dvcRecvDist_z,recvCount_z,recvCount_z,recvbuf_z,Bq,N); - ScaLBL_D3Q7_Unpack(6,dvcRecvDist_z,2*recvCount_z,recvCount_z,recvbuf_z,Cq,N); - } - else { - //...Packing for z face(6,12,13,16,17)................................ - ScaLBL_D3Q7_Unpack(6,dvcRecvDist_z,0,recvCount_z,recvbuf_z,Aq,N); - ScaLBL_D3Q7_Unpack(6,dvcRecvDist_z,recvCount_z,recvCount_z,recvbuf_z,Bq,N); - ScaLBL_D3Q7_Unpack(6,dvcRecvDist_z,2*recvCount_z,recvCount_z,recvbuf_z,Cq,N); - //...Packing for Z face(5,11,14,15,18)................................ - ScaLBL_D3Q7_Unpack(5,dvcRecvDist_Z,0,recvCount_Z,recvbuf_Z,Aq,N); - ScaLBL_D3Q7_Unpack(5,dvcRecvDist_Z,recvCount_Z,recvCount_Z,recvbuf_Z,Bq,N); - ScaLBL_D3Q7_Unpack(5,dvcRecvDist_Z,2*recvCount_Z,recvCount_Z,recvbuf_Z,Cq,N); - } - - //................................................................................... - Lock=false; // unlock the communicator after communications complete - //................................................................................... + if (BoundaryCondition > 0 && kproc == 0) { + // don't unpack little z + //...Packing for Z face(5,11,14,15,18)................................ + ScaLBL_D3Q7_Unpack(5, dvcRecvDist_Z, 0, recvCount_Z, recvbuf_Z, Aq, N); + ScaLBL_D3Q7_Unpack(5, dvcRecvDist_Z, recvCount_Z, recvCount_Z, + recvbuf_Z, Bq, N); + ScaLBL_D3Q7_Unpack(5, dvcRecvDist_Z, 2 * recvCount_Z, recvCount_Z, + recvbuf_Z, Cq, N); + } else if (BoundaryCondition > 0 && kproc == nprocz - 1) { + // don't unpack big z + //...Packing for z face(6,12,13,16,17)................................ + ScaLBL_D3Q7_Unpack(6, dvcRecvDist_z, 0, recvCount_z, recvbuf_z, Aq, N); + ScaLBL_D3Q7_Unpack(6, dvcRecvDist_z, recvCount_z, recvCount_z, + recvbuf_z, Bq, N); + ScaLBL_D3Q7_Unpack(6, dvcRecvDist_z, 2 * recvCount_z, recvCount_z, + recvbuf_z, Cq, N); + } else { + //...Packing for z face(6,12,13,16,17)................................ + ScaLBL_D3Q7_Unpack(6, dvcRecvDist_z, 0, recvCount_z, recvbuf_z, Aq, N); + ScaLBL_D3Q7_Unpack(6, dvcRecvDist_z, recvCount_z, recvCount_z, + recvbuf_z, Bq, N); + ScaLBL_D3Q7_Unpack(6, dvcRecvDist_z, 2 * recvCount_z, recvCount_z, + recvbuf_z, Cq, N); + //...Packing for Z face(5,11,14,15,18)................................ + ScaLBL_D3Q7_Unpack(5, dvcRecvDist_Z, 0, recvCount_Z, recvbuf_Z, Aq, N); + ScaLBL_D3Q7_Unpack(5, dvcRecvDist_Z, recvCount_Z, recvCount_Z, + recvbuf_Z, Bq, N); + ScaLBL_D3Q7_Unpack(5, dvcRecvDist_Z, 2 * recvCount_Z, recvCount_Z, + recvbuf_Z, Cq, N); + } + //................................................................................... + Lock = false; // unlock the communicator after communications complete + //................................................................................... } +void ScaLBL_Communicator::SendHalo(double *data) { + //................................................................................... + if (Lock == true) { + ERROR("ScaLBL Error (SendHalo): ScaLBL_Communicator is locked -- did " + "you forget to match Send/Recv calls?"); + } else { + Lock = true; + } + ScaLBL_DeviceBarrier(); + //................................................................................... + sendtag = recvtag = 1; + //................................................................................... + ScaLBL_Scalar_Pack(dvcSendList_x, sendCount_x, sendbuf_x, data, N); + ScaLBL_Scalar_Pack(dvcSendList_y, sendCount_y, sendbuf_y, data, N); + ScaLBL_Scalar_Pack(dvcSendList_z, sendCount_z, sendbuf_z, data, N); + ScaLBL_Scalar_Pack(dvcSendList_X, sendCount_X, sendbuf_X, data, N); + ScaLBL_Scalar_Pack(dvcSendList_Y, sendCount_Y, sendbuf_Y, data, N); + ScaLBL_Scalar_Pack(dvcSendList_Z, sendCount_Z, sendbuf_Z, data, N); + ScaLBL_Scalar_Pack(dvcSendList_xy, sendCount_xy, sendbuf_xy, data, N); + ScaLBL_Scalar_Pack(dvcSendList_xY, sendCount_xY, sendbuf_xY, data, N); + ScaLBL_Scalar_Pack(dvcSendList_Xy, sendCount_Xy, sendbuf_Xy, data, N); + ScaLBL_Scalar_Pack(dvcSendList_XY, sendCount_XY, sendbuf_XY, data, N); + ScaLBL_Scalar_Pack(dvcSendList_xz, sendCount_xz, sendbuf_xz, data, N); + ScaLBL_Scalar_Pack(dvcSendList_xZ, sendCount_xZ, sendbuf_xZ, data, N); + ScaLBL_Scalar_Pack(dvcSendList_Xz, sendCount_Xz, sendbuf_Xz, data, N); + ScaLBL_Scalar_Pack(dvcSendList_XZ, sendCount_XZ, sendbuf_XZ, data, N); + ScaLBL_Scalar_Pack(dvcSendList_yz, sendCount_yz, sendbuf_yz, data, N); + ScaLBL_Scalar_Pack(dvcSendList_yZ, sendCount_yZ, sendbuf_yZ, data, N); + ScaLBL_Scalar_Pack(dvcSendList_Yz, sendCount_Yz, sendbuf_Yz, data, N); + ScaLBL_Scalar_Pack(dvcSendList_YZ, sendCount_YZ, sendbuf_YZ, data, N); + //................................................................................... + // Send / Recv all the phase indcator field values + //................................................................................... -void ScaLBL_Communicator::SendHalo(double *data){ - //................................................................................... - if (Lock==true){ - ERROR("ScaLBL Error (SendHalo): ScaLBL_Communicator is locked -- did you forget to match Send/Recv calls?"); - } - else{ - Lock=true; - } - ScaLBL_DeviceBarrier(); - //................................................................................... - sendtag = recvtag = 1; - //................................................................................... - ScaLBL_Scalar_Pack(dvcSendList_x, sendCount_x,sendbuf_x, data, N); - ScaLBL_Scalar_Pack(dvcSendList_y, sendCount_y,sendbuf_y, data, N); - ScaLBL_Scalar_Pack(dvcSendList_z, sendCount_z,sendbuf_z, data, N); - ScaLBL_Scalar_Pack(dvcSendList_X, sendCount_X,sendbuf_X, data, N); - ScaLBL_Scalar_Pack(dvcSendList_Y, sendCount_Y,sendbuf_Y, data, N); - ScaLBL_Scalar_Pack(dvcSendList_Z, sendCount_Z,sendbuf_Z, data, N); - ScaLBL_Scalar_Pack(dvcSendList_xy, sendCount_xy,sendbuf_xy, data, N); - ScaLBL_Scalar_Pack(dvcSendList_xY, sendCount_xY,sendbuf_xY, data, N); - ScaLBL_Scalar_Pack(dvcSendList_Xy, sendCount_Xy,sendbuf_Xy, data, N); - ScaLBL_Scalar_Pack(dvcSendList_XY, sendCount_XY,sendbuf_XY, data, N); - ScaLBL_Scalar_Pack(dvcSendList_xz, sendCount_xz,sendbuf_xz, data, N); - ScaLBL_Scalar_Pack(dvcSendList_xZ, sendCount_xZ,sendbuf_xZ, data, N); - ScaLBL_Scalar_Pack(dvcSendList_Xz, sendCount_Xz,sendbuf_Xz, data, N); - ScaLBL_Scalar_Pack(dvcSendList_XZ, sendCount_XZ,sendbuf_XZ, data, N); - ScaLBL_Scalar_Pack(dvcSendList_yz, sendCount_yz,sendbuf_yz, data, N); - ScaLBL_Scalar_Pack(dvcSendList_yZ, sendCount_yZ,sendbuf_yZ, data, N); - ScaLBL_Scalar_Pack(dvcSendList_Yz, sendCount_Yz,sendbuf_Yz, data, N); - ScaLBL_Scalar_Pack(dvcSendList_YZ, sendCount_YZ,sendbuf_YZ, data, N); - //................................................................................... - // Send / Recv all the phase indcator field values - //................................................................................... - - req1[0] = MPI_COMM_SCALBL.Isend(sendbuf_x, sendCount_x,rank_x,sendtag); - req2[0] = MPI_COMM_SCALBL.Irecv(recvbuf_X, recvCount_X,rank_X,recvtag); - req1[1] = MPI_COMM_SCALBL.Isend(sendbuf_X, sendCount_X,rank_X,sendtag); - req2[1] = MPI_COMM_SCALBL.Irecv(recvbuf_x, recvCount_x,rank_x,recvtag); - req1[2] = MPI_COMM_SCALBL.Isend(sendbuf_y, sendCount_y,rank_y,sendtag); - req2[2] = MPI_COMM_SCALBL.Irecv(recvbuf_Y, recvCount_Y,rank_Y,recvtag); - req1[3] = MPI_COMM_SCALBL.Isend(sendbuf_Y, sendCount_Y,rank_Y,sendtag); - req2[3] = MPI_COMM_SCALBL.Irecv(recvbuf_y, recvCount_y,rank_y,recvtag); - req1[4] = MPI_COMM_SCALBL.Isend(sendbuf_z, sendCount_z,rank_z,sendtag); - req2[4] = MPI_COMM_SCALBL.Irecv(recvbuf_Z, recvCount_Z,rank_Z,recvtag); - req1[5] = MPI_COMM_SCALBL.Isend(sendbuf_Z, sendCount_Z,rank_Z,sendtag); - req2[5] = MPI_COMM_SCALBL.Irecv(recvbuf_z, recvCount_z,rank_z,recvtag); - req1[6] = MPI_COMM_SCALBL.Isend(sendbuf_xy, sendCount_xy,rank_xy,sendtag); - req2[6] = MPI_COMM_SCALBL.Irecv(recvbuf_XY, recvCount_XY,rank_XY,recvtag); - req1[7] = MPI_COMM_SCALBL.Isend(sendbuf_XY, sendCount_XY,rank_XY,sendtag); - req2[7] = MPI_COMM_SCALBL.Irecv(recvbuf_xy, recvCount_xy,rank_xy,recvtag); - req1[8] = MPI_COMM_SCALBL.Isend(sendbuf_Xy, sendCount_Xy,rank_Xy,sendtag); - req2[8] = MPI_COMM_SCALBL.Irecv(recvbuf_xY, recvCount_xY,rank_xY,recvtag); - req1[9] = MPI_COMM_SCALBL.Isend(sendbuf_xY, sendCount_xY,rank_xY,sendtag); - req2[9] = MPI_COMM_SCALBL.Irecv(recvbuf_Xy, recvCount_Xy,rank_Xy,recvtag); - req1[10] = MPI_COMM_SCALBL.Isend(sendbuf_xz, sendCount_xz,rank_xz,sendtag); - req2[10] = MPI_COMM_SCALBL.Irecv(recvbuf_XZ, recvCount_XZ,rank_XZ,recvtag); - req1[11] = MPI_COMM_SCALBL.Isend(sendbuf_XZ, sendCount_XZ,rank_XZ,sendtag); - req2[11] = MPI_COMM_SCALBL.Irecv(recvbuf_xz, recvCount_xz,rank_xz,recvtag); - req1[12] = MPI_COMM_SCALBL.Isend(sendbuf_Xz, sendCount_Xz,rank_Xz,sendtag); - req2[12] = MPI_COMM_SCALBL.Irecv(recvbuf_xZ, recvCount_xZ,rank_xZ,recvtag); - req1[13] = MPI_COMM_SCALBL.Isend(sendbuf_xZ, sendCount_xZ,rank_xZ,sendtag); - req2[13] = MPI_COMM_SCALBL.Irecv(recvbuf_Xz, recvCount_Xz,rank_Xz,recvtag); - req1[14] = MPI_COMM_SCALBL.Isend(sendbuf_yz, sendCount_yz,rank_yz,sendtag); - req2[14] = MPI_COMM_SCALBL.Irecv(recvbuf_YZ, recvCount_YZ,rank_YZ,recvtag); - req1[15] = MPI_COMM_SCALBL.Isend(sendbuf_YZ, sendCount_YZ,rank_YZ,sendtag); - req2[15] = MPI_COMM_SCALBL.Irecv(recvbuf_yz, recvCount_yz,rank_yz,recvtag); - req1[16] = MPI_COMM_SCALBL.Isend(sendbuf_Yz, sendCount_Yz,rank_Yz,sendtag); - req2[16] = MPI_COMM_SCALBL.Irecv(recvbuf_yZ, recvCount_yZ,rank_yZ,recvtag); - req1[17] = MPI_COMM_SCALBL.Isend(sendbuf_yZ, sendCount_yZ,rank_yZ,sendtag); - req2[17] = MPI_COMM_SCALBL.Irecv(recvbuf_Yz, recvCount_Yz,rank_Yz,recvtag); - //................................................................................... + req1[0] = MPI_COMM_SCALBL.Isend(sendbuf_x, sendCount_x, rank_x, sendtag); + req2[0] = MPI_COMM_SCALBL.Irecv(recvbuf_X, recvCount_X, rank_X, recvtag); + req1[1] = MPI_COMM_SCALBL.Isend(sendbuf_X, sendCount_X, rank_X, sendtag); + req2[1] = MPI_COMM_SCALBL.Irecv(recvbuf_x, recvCount_x, rank_x, recvtag); + req1[2] = MPI_COMM_SCALBL.Isend(sendbuf_y, sendCount_y, rank_y, sendtag); + req2[2] = MPI_COMM_SCALBL.Irecv(recvbuf_Y, recvCount_Y, rank_Y, recvtag); + req1[3] = MPI_COMM_SCALBL.Isend(sendbuf_Y, sendCount_Y, rank_Y, sendtag); + req2[3] = MPI_COMM_SCALBL.Irecv(recvbuf_y, recvCount_y, rank_y, recvtag); + req1[4] = MPI_COMM_SCALBL.Isend(sendbuf_z, sendCount_z, rank_z, sendtag); + req2[4] = MPI_COMM_SCALBL.Irecv(recvbuf_Z, recvCount_Z, rank_Z, recvtag); + req1[5] = MPI_COMM_SCALBL.Isend(sendbuf_Z, sendCount_Z, rank_Z, sendtag); + req2[5] = MPI_COMM_SCALBL.Irecv(recvbuf_z, recvCount_z, rank_z, recvtag); + req1[6] = MPI_COMM_SCALBL.Isend(sendbuf_xy, sendCount_xy, rank_xy, sendtag); + req2[6] = MPI_COMM_SCALBL.Irecv(recvbuf_XY, recvCount_XY, rank_XY, recvtag); + req1[7] = MPI_COMM_SCALBL.Isend(sendbuf_XY, sendCount_XY, rank_XY, sendtag); + req2[7] = MPI_COMM_SCALBL.Irecv(recvbuf_xy, recvCount_xy, rank_xy, recvtag); + req1[8] = MPI_COMM_SCALBL.Isend(sendbuf_Xy, sendCount_Xy, rank_Xy, sendtag); + req2[8] = MPI_COMM_SCALBL.Irecv(recvbuf_xY, recvCount_xY, rank_xY, recvtag); + req1[9] = MPI_COMM_SCALBL.Isend(sendbuf_xY, sendCount_xY, rank_xY, sendtag); + req2[9] = MPI_COMM_SCALBL.Irecv(recvbuf_Xy, recvCount_Xy, rank_Xy, recvtag); + req1[10] = + MPI_COMM_SCALBL.Isend(sendbuf_xz, sendCount_xz, rank_xz, sendtag); + req2[10] = + MPI_COMM_SCALBL.Irecv(recvbuf_XZ, recvCount_XZ, rank_XZ, recvtag); + req1[11] = + MPI_COMM_SCALBL.Isend(sendbuf_XZ, sendCount_XZ, rank_XZ, sendtag); + req2[11] = + MPI_COMM_SCALBL.Irecv(recvbuf_xz, recvCount_xz, rank_xz, recvtag); + req1[12] = + MPI_COMM_SCALBL.Isend(sendbuf_Xz, sendCount_Xz, rank_Xz, sendtag); + req2[12] = + MPI_COMM_SCALBL.Irecv(recvbuf_xZ, recvCount_xZ, rank_xZ, recvtag); + req1[13] = + MPI_COMM_SCALBL.Isend(sendbuf_xZ, sendCount_xZ, rank_xZ, sendtag); + req2[13] = + MPI_COMM_SCALBL.Irecv(recvbuf_Xz, recvCount_Xz, rank_Xz, recvtag); + req1[14] = + MPI_COMM_SCALBL.Isend(sendbuf_yz, sendCount_yz, rank_yz, sendtag); + req2[14] = + MPI_COMM_SCALBL.Irecv(recvbuf_YZ, recvCount_YZ, rank_YZ, recvtag); + req1[15] = + MPI_COMM_SCALBL.Isend(sendbuf_YZ, sendCount_YZ, rank_YZ, sendtag); + req2[15] = + MPI_COMM_SCALBL.Irecv(recvbuf_yz, recvCount_yz, rank_yz, recvtag); + req1[16] = + MPI_COMM_SCALBL.Isend(sendbuf_Yz, sendCount_Yz, rank_Yz, sendtag); + req2[16] = + MPI_COMM_SCALBL.Irecv(recvbuf_yZ, recvCount_yZ, rank_yZ, recvtag); + req1[17] = + MPI_COMM_SCALBL.Isend(sendbuf_yZ, sendCount_yZ, rank_yZ, sendtag); + req2[17] = + MPI_COMM_SCALBL.Irecv(recvbuf_Yz, recvCount_Yz, rank_Yz, recvtag); + //................................................................................... } -void ScaLBL_Communicator::RecvHalo(double *data){ +void ScaLBL_Communicator::RecvHalo(double *data) { - //................................................................................... - MPI_COMM_SCALBL.waitAll(18,req1); - MPI_COMM_SCALBL.waitAll(18,req2); - ScaLBL_DeviceBarrier(); - //................................................................................... - //................................................................................... - ScaLBL_Scalar_Unpack(dvcRecvList_x, recvCount_x,recvbuf_x, data, N); - ScaLBL_Scalar_Unpack(dvcRecvList_y, recvCount_y,recvbuf_y, data, N); - ScaLBL_Scalar_Unpack(dvcRecvList_z, recvCount_z,recvbuf_z, data, N); - ScaLBL_Scalar_Unpack(dvcRecvList_X, recvCount_X,recvbuf_X, data, N); - ScaLBL_Scalar_Unpack(dvcRecvList_Y, recvCount_Y,recvbuf_Y, data, N); - ScaLBL_Scalar_Unpack(dvcRecvList_Z, recvCount_Z,recvbuf_Z, data, N); - ScaLBL_Scalar_Unpack(dvcRecvList_xy, recvCount_xy,recvbuf_xy, data, N); - ScaLBL_Scalar_Unpack(dvcRecvList_xY, recvCount_xY,recvbuf_xY, data, N); - ScaLBL_Scalar_Unpack(dvcRecvList_Xy, recvCount_Xy,recvbuf_Xy, data, N); - ScaLBL_Scalar_Unpack(dvcRecvList_XY, recvCount_XY,recvbuf_XY, data, N); - ScaLBL_Scalar_Unpack(dvcRecvList_xz, recvCount_xz,recvbuf_xz, data, N); - ScaLBL_Scalar_Unpack(dvcRecvList_xZ, recvCount_xZ,recvbuf_xZ, data, N); - ScaLBL_Scalar_Unpack(dvcRecvList_Xz, recvCount_Xz,recvbuf_Xz, data, N); - ScaLBL_Scalar_Unpack(dvcRecvList_XZ, recvCount_XZ,recvbuf_XZ, data, N); - ScaLBL_Scalar_Unpack(dvcRecvList_yz, recvCount_yz,recvbuf_yz, data, N); - ScaLBL_Scalar_Unpack(dvcRecvList_yZ, recvCount_yZ,recvbuf_yZ, data, N); - ScaLBL_Scalar_Unpack(dvcRecvList_Yz, recvCount_Yz,recvbuf_Yz, data, N); - ScaLBL_Scalar_Unpack(dvcRecvList_YZ, recvCount_YZ,recvbuf_YZ, data, N); - //................................................................................... - Lock=false; // unlock the communicator after communications complete - //................................................................................... + //................................................................................... + MPI_COMM_SCALBL.waitAll(18, req1); + MPI_COMM_SCALBL.waitAll(18, req2); + ScaLBL_DeviceBarrier(); + //................................................................................... + //................................................................................... + ScaLBL_Scalar_Unpack(dvcRecvList_x, recvCount_x, recvbuf_x, data, N); + ScaLBL_Scalar_Unpack(dvcRecvList_y, recvCount_y, recvbuf_y, data, N); + ScaLBL_Scalar_Unpack(dvcRecvList_z, recvCount_z, recvbuf_z, data, N); + ScaLBL_Scalar_Unpack(dvcRecvList_X, recvCount_X, recvbuf_X, data, N); + ScaLBL_Scalar_Unpack(dvcRecvList_Y, recvCount_Y, recvbuf_Y, data, N); + ScaLBL_Scalar_Unpack(dvcRecvList_Z, recvCount_Z, recvbuf_Z, data, N); + ScaLBL_Scalar_Unpack(dvcRecvList_xy, recvCount_xy, recvbuf_xy, data, N); + ScaLBL_Scalar_Unpack(dvcRecvList_xY, recvCount_xY, recvbuf_xY, data, N); + ScaLBL_Scalar_Unpack(dvcRecvList_Xy, recvCount_Xy, recvbuf_Xy, data, N); + ScaLBL_Scalar_Unpack(dvcRecvList_XY, recvCount_XY, recvbuf_XY, data, N); + ScaLBL_Scalar_Unpack(dvcRecvList_xz, recvCount_xz, recvbuf_xz, data, N); + ScaLBL_Scalar_Unpack(dvcRecvList_xZ, recvCount_xZ, recvbuf_xZ, data, N); + ScaLBL_Scalar_Unpack(dvcRecvList_Xz, recvCount_Xz, recvbuf_Xz, data, N); + ScaLBL_Scalar_Unpack(dvcRecvList_XZ, recvCount_XZ, recvbuf_XZ, data, N); + ScaLBL_Scalar_Unpack(dvcRecvList_yz, recvCount_yz, recvbuf_yz, data, N); + ScaLBL_Scalar_Unpack(dvcRecvList_yZ, recvCount_yZ, recvbuf_yZ, data, N); + ScaLBL_Scalar_Unpack(dvcRecvList_Yz, recvCount_Yz, recvbuf_Yz, data, N); + ScaLBL_Scalar_Unpack(dvcRecvList_YZ, recvCount_YZ, recvbuf_YZ, data, N); + //................................................................................... + Lock = false; // unlock the communicator after communications complete + //................................................................................... } -void ScaLBL_Communicator::RegularLayout(IntArray map, const double *data, DoubleArray ®data){ - // Gets data from the device and stores in regular layout - int i,j,k,idx; - int Nx = map.size(0); - int Ny = map.size(1); - int Nz = map.size(2); +void ScaLBL_Communicator::RegularLayout(IntArray map, const double *data, + DoubleArray ®data) { + // Gets data from the device and stores in regular layout + int i, j, k, idx; + int Nx = map.size(0); + int Ny = map.size(1); + int Nz = map.size(2); - // initialize the array - regdata.fill(0.f); - - double *TmpDat; - double value; - TmpDat = new double [N]; - ScaLBL_CopyToHost(&TmpDat[0],&data[0], N*sizeof(double)); - for (k=0; k Dm); + ScaLBL_Communicator(std::shared_ptr Dm); - /** + /** *\brief Destructor */ - ~ScaLBL_Communicator(); - //...................................................................................... - unsigned long int CommunicationCount,SendCount,RecvCount; - int Nx,Ny,Nz,N; - int n_bb_d3q7, n_bb_d3q19; - int BoundaryCondition; - - int next; - int first_interior,last_interior; - //...................................................................................... - // Set up for D319 distributions - // - determines how much memory is allocated - // - buffers are reused to send D3Q7 distributions and halo exchange as needed - //...................................................................................... - // Buffers to store data sent and recieved by this MPI process - double *sendbuf_x, *sendbuf_y, *sendbuf_z, *sendbuf_X, *sendbuf_Y, *sendbuf_Z; - double *sendbuf_xy, *sendbuf_yz, *sendbuf_xz, *sendbuf_Xy, *sendbuf_Yz, *sendbuf_xZ; - double *sendbuf_xY, *sendbuf_yZ, *sendbuf_Xz, *sendbuf_XY, *sendbuf_YZ, *sendbuf_XZ; - double *recvbuf_x, *recvbuf_y, *recvbuf_z, *recvbuf_X, *recvbuf_Y, *recvbuf_Z; - double *recvbuf_xy, *recvbuf_yz, *recvbuf_xz, *recvbuf_Xy, *recvbuf_Yz, *recvbuf_xZ; - double *recvbuf_xY, *recvbuf_yZ, *recvbuf_Xz, *recvbuf_XY, *recvbuf_YZ, *recvbuf_XZ; - //...................................................................................... + ~ScaLBL_Communicator(); + //...................................................................................... + unsigned long int CommunicationCount, SendCount, RecvCount; + int Nx, Ny, Nz, N; + int n_bb_d3q7, n_bb_d3q19; + int BoundaryCondition; - int LastExterior(); - int FirstInterior(); - int LastInterior(); - - double GetPerformance(int *NeighborList, double *fq, int Np); - int MemoryOptimizedLayoutAA(IntArray &Map, int *neighborList, signed char *id, int Np, int width); - void Barrier(){ - ScaLBL_DeviceBarrier(); - MPI_COMM_SCALBL.barrier(); - }; - void SendD3Q19AA(double *dist); - void RecvD3Q19AA(double *dist); - void SendD3Q7AA(double *fq, int Component); - void RecvD3Q7AA(double *fq, int Component); - void BiSendD3Q7AA(double *Aq, double *Bq); - void BiRecvD3Q7AA(double *Aq, double *Bq); - void TriSendD3Q7AA(double *Aq, double *Bq, double *Cq); - void TriRecvD3Q7AA(double *Aq, double *Bq, double *Cq); - void SendHalo(double *data); - void RecvHalo(double *data); - void RecvGrad(double *Phi, double *Gradient); - void RegularLayout(IntArray map, const double *data, DoubleArray ®data); - void SetupBounceBackList(IntArray &Map, signed char *id, int Np, bool SlippingVelBC=false); + int next; + int first_interior, last_interior; + //...................................................................................... + // Set up for D319 distributions + // - determines how much memory is allocated + // - buffers are reused to send D3Q7 distributions and halo exchange as needed + //...................................................................................... + // Buffers to store data sent and recieved by this MPI process + double *sendbuf_x, *sendbuf_y, *sendbuf_z, *sendbuf_X, *sendbuf_Y, + *sendbuf_Z; + double *sendbuf_xy, *sendbuf_yz, *sendbuf_xz, *sendbuf_Xy, *sendbuf_Yz, + *sendbuf_xZ; + double *sendbuf_xY, *sendbuf_yZ, *sendbuf_Xz, *sendbuf_XY, *sendbuf_YZ, + *sendbuf_XZ; + double *recvbuf_x, *recvbuf_y, *recvbuf_z, *recvbuf_X, *recvbuf_Y, + *recvbuf_Z; + double *recvbuf_xy, *recvbuf_yz, *recvbuf_xz, *recvbuf_Xy, *recvbuf_Yz, + *recvbuf_xZ; + double *recvbuf_xY, *recvbuf_yZ, *recvbuf_Xz, *recvbuf_XY, *recvbuf_YZ, + *recvbuf_XZ; + //...................................................................................... + + int LastExterior(); + int FirstInterior(); + int LastInterior(); + + double GetPerformance(int *NeighborList, double *fq, int Np); + int MemoryOptimizedLayoutAA(IntArray &Map, int *neighborList, + signed char *id, int Np, int width); + void Barrier() { + ScaLBL_DeviceBarrier(); + MPI_COMM_SCALBL.barrier(); + }; + void SendD3Q19AA(double *dist); + void RecvD3Q19AA(double *dist); + void SendD3Q7AA(double *fq, int Component); + void RecvD3Q7AA(double *fq, int Component); + void BiSendD3Q7AA(double *Aq, double *Bq); + void BiRecvD3Q7AA(double *Aq, double *Bq); + void TriSendD3Q7AA(double *Aq, double *Bq, double *Cq); + void TriRecvD3Q7AA(double *Aq, double *Bq, double *Cq); + void SendHalo(double *data); + void RecvHalo(double *data); + void RecvGrad(double *Phi, double *Gradient); + void RegularLayout(IntArray map, const double *data, DoubleArray ®data); + void SetupBounceBackList(IntArray &Map, signed char *id, int Np, + bool SlippingVelBC = false); void SolidDirichletD3Q7(double *fq, double *BoundaryValue); void SolidNeumannD3Q7(double *fq, double *BoundaryValue); - void SolidSlippingVelocityBCD3Q19(double *fq, double *zeta_potential, double *ElectricField, double *SolidGrad, - double epslion_LB, double tau, double rho0, double den_scale,double h, double time_conv); + void SolidSlippingVelocityBCD3Q19(double *fq, double *zeta_potential, + double *ElectricField, double *SolidGrad, + double epslion_LB, double tau, + double rho0, double den_scale, double h, + double time_conv); // Routines to set boundary conditions void Color_BC_z(int *Map, double *Phi, double *Den, double vA, double vB); void Color_BC_Z(int *Map, double *Phi, double *Den, double vA, double vB); - void D3Q19_Pressure_BC_z(int *neighborList, double *fq, double din, int time); - void D3Q19_Pressure_BC_Z(int *neighborList, double *fq, double dout, int time); + void D3Q19_Pressure_BC_z(int *neighborList, double *fq, double din, + int time); + void D3Q19_Pressure_BC_Z(int *neighborList, double *fq, double dout, + int time); void D3Q19_Reflection_BC_z(double *fq); void D3Q19_Reflection_BC_Z(double *fq); - double D3Q19_Flux_BC_z(int *neighborList, double *fq, double flux, int time); - void D3Q7_Poisson_Potential_BC_z(int *neighborList, double *fq, double Vin, int time); - void D3Q7_Poisson_Potential_BC_Z(int *neighborList, double *fq, double Vout, int time); + double D3Q19_Flux_BC_z(int *neighborList, double *fq, double flux, + int time); + void D3Q7_Poisson_Potential_BC_z(int *neighborList, double *fq, double Vin, + int time); + void D3Q7_Poisson_Potential_BC_Z(int *neighborList, double *fq, double Vout, + int time); void Poisson_D3Q7_BC_z(int *Map, double *Psi, double Vin); void Poisson_D3Q7_BC_Z(int *Map, double *Psi, double Vout); - void D3Q7_Ion_Concentration_BC_z(int *neighborList, double *fq, double Cin, int time); - void D3Q7_Ion_Concentration_BC_Z(int *neighborList, double *fq, double Cout, int time); - void D3Q7_Ion_Flux_Diff_BC_z(int *neighborList, double *fq, double Cin, double tau, double *VelocityZ, int time); - void D3Q7_Ion_Flux_Diff_BC_Z(int *neighborList, double *fq, double Cout, double tau, double *VelocityZ, int time); - void D3Q7_Ion_Flux_DiffAdvc_BC_z(int *neighborList, double *fq, double Cin, double tau, double *VelocityZ, int time); - void D3Q7_Ion_Flux_DiffAdvc_BC_Z(int *neighborList, double *fq, double Cout, double tau, double *VelocityZ, int time); - void D3Q7_Ion_Flux_DiffAdvcElec_BC_z(int *neighborList,double *fq,double Cin,double tau,double *VelocityZ,double *ElectricField_Z,double Di,double zi,double Vt, int time); - void D3Q7_Ion_Flux_DiffAdvcElec_BC_Z(int *neighborList,double *fq,double Cout,double tau,double *VelocityZ,double *ElectricField_Z,double Di,double zi,double Vt, int time); - void GreyscaleSC_BC_z(int *Map, double *DenA, double *DenB, double vA, double vB); - void GreyscaleSC_BC_Z(int *Map, double *DenA, double *DenB, double vA, double vB); - void GreyscaleSC_Pressure_BC_z(int *neighborList, double *fqA, double *fqB, double dinA, double dinB, int time); - void GreyscaleSC_Pressure_BC_Z(int *neighborList, double *fqA, double *fqB, double doutA, double doutB, int time); + void D3Q7_Ion_Concentration_BC_z(int *neighborList, double *fq, double Cin, + int time); + void D3Q7_Ion_Concentration_BC_Z(int *neighborList, double *fq, double Cout, + int time); + void D3Q7_Ion_Flux_Diff_BC_z(int *neighborList, double *fq, double Cin, + double tau, double *VelocityZ, int time); + void D3Q7_Ion_Flux_Diff_BC_Z(int *neighborList, double *fq, double Cout, + double tau, double *VelocityZ, int time); + void D3Q7_Ion_Flux_DiffAdvc_BC_z(int *neighborList, double *fq, double Cin, + double tau, double *VelocityZ, int time); + void D3Q7_Ion_Flux_DiffAdvc_BC_Z(int *neighborList, double *fq, double Cout, + double tau, double *VelocityZ, int time); + void D3Q7_Ion_Flux_DiffAdvcElec_BC_z(int *neighborList, double *fq, + double Cin, double tau, + double *VelocityZ, + double *ElectricField_Z, double Di, + double zi, double Vt, int time); + void D3Q7_Ion_Flux_DiffAdvcElec_BC_Z(int *neighborList, double *fq, + double Cout, double tau, + double *VelocityZ, + double *ElectricField_Z, double Di, + double zi, double Vt, int time); + void GreyscaleSC_BC_z(int *Map, double *DenA, double *DenB, double vA, + double vB); + void GreyscaleSC_BC_Z(int *Map, double *DenA, double *DenB, double vA, + double vB); + void GreyscaleSC_Pressure_BC_z(int *neighborList, double *fqA, double *fqB, + double dinA, double dinB, int time); + void GreyscaleSC_Pressure_BC_Z(int *neighborList, double *fqA, double *fqB, + double doutA, double doutB, int time); // Debugging and unit testing functions void PrintD3Q19(); private: - void D3Q19_MapRecv(int Cqx, int Cqy, int Cqz, const int *list, int start, int count, int *d3q19_recvlist); + void D3Q19_MapRecv(int Cqx, int Cqy, int Cqz, const int *list, int start, + int count, int *d3q19_recvlist); - bool Lock; // use Lock to make sure only one call at a time to protect data in transit - // only one set of Send requests can be active at any time (per instance) - int i,j,k,n; + bool + Lock; // use Lock to make sure only one call at a time to protect data in transit + // only one set of Send requests can be active at any time (per instance) + int i, j, k, n; - int iproc,jproc,kproc; - int nprocx,nprocy,nprocz; - int sendtag,recvtag; - // Give the object it's own MPI communicator - RankInfoStruct rank_info; - Utilities::MPI MPI_COMM_SCALBL; // MPI Communicator for this domain - MPI_Request req1[18],req2[18]; - //...................................................................................... - // MPI ranks for all 18 neighbors - //...................................................................................... - // These variables are all private to prevent external things from modifying them!! - //...................................................................................... - int rank; - int rank_x,rank_y,rank_z,rank_X,rank_Y,rank_Z; - int rank_xy,rank_XY,rank_xY,rank_Xy; - int rank_xz,rank_XZ,rank_xZ,rank_Xz; - int rank_yz,rank_YZ,rank_yZ,rank_Yz; - //...................................................................................... - //...................................................................................... - int sendCount_x, sendCount_y, sendCount_z, sendCount_X, sendCount_Y, sendCount_Z; - int sendCount_xy, sendCount_yz, sendCount_xz, sendCount_Xy, sendCount_Yz, sendCount_xZ; - int sendCount_xY, sendCount_yZ, sendCount_Xz, sendCount_XY, sendCount_YZ, sendCount_XZ; - //...................................................................................... + int iproc, jproc, kproc; + int nprocx, nprocy, nprocz; + int sendtag, recvtag; + // Give the object it's own MPI communicator + RankInfoStruct rank_info; + Utilities::MPI MPI_COMM_SCALBL; // MPI Communicator for this domain + MPI_Request req1[18], req2[18]; + //...................................................................................... + // MPI ranks for all 18 neighbors + //...................................................................................... + // These variables are all private to prevent external things from modifying them!! + //...................................................................................... + int rank; + int rank_x, rank_y, rank_z, rank_X, rank_Y, rank_Z; + int rank_xy, rank_XY, rank_xY, rank_Xy; + int rank_xz, rank_XZ, rank_xZ, rank_Xz; + int rank_yz, rank_YZ, rank_yZ, rank_Yz; + //...................................................................................... + //...................................................................................... + int sendCount_x, sendCount_y, sendCount_z, sendCount_X, sendCount_Y, + sendCount_Z; + int sendCount_xy, sendCount_yz, sendCount_xz, sendCount_Xy, sendCount_Yz, + sendCount_xZ; + int sendCount_xY, sendCount_yZ, sendCount_Xz, sendCount_XY, sendCount_YZ, + sendCount_XZ; + //...................................................................................... - int recvCount_x, recvCount_y, recvCount_z, recvCount_X, recvCount_Y, recvCount_Z; - int recvCount_xy, recvCount_yz, recvCount_xz, recvCount_Xy, recvCount_Yz, recvCount_xZ; - int recvCount_xY, recvCount_yZ, recvCount_Xz, recvCount_XY, recvCount_YZ, recvCount_XZ; - //...................................................................................... - // Send buffers that reside on the compute device - int *dvcSendList_x, *dvcSendList_y, *dvcSendList_z, *dvcSendList_X, *dvcSendList_Y, *dvcSendList_Z; - int *dvcSendList_xy, *dvcSendList_yz, *dvcSendList_xz, *dvcSendList_Xy, *dvcSendList_Yz, *dvcSendList_xZ; - int *dvcSendList_xY, *dvcSendList_yZ, *dvcSendList_Xz, *dvcSendList_XY, *dvcSendList_YZ, *dvcSendList_XZ; - // Recieve buffers that reside on the compute device - int *dvcRecvList_x, *dvcRecvList_y, *dvcRecvList_z, *dvcRecvList_X, *dvcRecvList_Y, *dvcRecvList_Z; - int *dvcRecvList_xy, *dvcRecvList_yz, *dvcRecvList_xz, *dvcRecvList_Xy, *dvcRecvList_Yz, *dvcRecvList_xZ; - int *dvcRecvList_xY, *dvcRecvList_yZ, *dvcRecvList_Xz, *dvcRecvList_XY, *dvcRecvList_YZ, *dvcRecvList_XZ; - // Recieve buffers for the distributions - int *dvcRecvDist_x, *dvcRecvDist_y, *dvcRecvDist_z, *dvcRecvDist_X, *dvcRecvDist_Y, *dvcRecvDist_Z; - int *dvcRecvDist_xy, *dvcRecvDist_yz, *dvcRecvDist_xz, *dvcRecvDist_Xy, *dvcRecvDist_Yz, *dvcRecvDist_xZ; - int *dvcRecvDist_xY, *dvcRecvDist_yZ, *dvcRecvDist_Xz, *dvcRecvDist_XY, *dvcRecvDist_YZ, *dvcRecvDist_XZ; - //...................................................................................... - int *bb_dist; - int *bb_interactions; + int recvCount_x, recvCount_y, recvCount_z, recvCount_X, recvCount_Y, + recvCount_Z; + int recvCount_xy, recvCount_yz, recvCount_xz, recvCount_Xy, recvCount_Yz, + recvCount_xZ; + int recvCount_xY, recvCount_yZ, recvCount_Xz, recvCount_XY, recvCount_YZ, + recvCount_XZ; + //...................................................................................... + // Send buffers that reside on the compute device + int *dvcSendList_x, *dvcSendList_y, *dvcSendList_z, *dvcSendList_X, + *dvcSendList_Y, *dvcSendList_Z; + int *dvcSendList_xy, *dvcSendList_yz, *dvcSendList_xz, *dvcSendList_Xy, + *dvcSendList_Yz, *dvcSendList_xZ; + int *dvcSendList_xY, *dvcSendList_yZ, *dvcSendList_Xz, *dvcSendList_XY, + *dvcSendList_YZ, *dvcSendList_XZ; + // Recieve buffers that reside on the compute device + int *dvcRecvList_x, *dvcRecvList_y, *dvcRecvList_z, *dvcRecvList_X, + *dvcRecvList_Y, *dvcRecvList_Z; + int *dvcRecvList_xy, *dvcRecvList_yz, *dvcRecvList_xz, *dvcRecvList_Xy, + *dvcRecvList_Yz, *dvcRecvList_xZ; + int *dvcRecvList_xY, *dvcRecvList_yZ, *dvcRecvList_Xz, *dvcRecvList_XY, + *dvcRecvList_YZ, *dvcRecvList_XZ; + // Recieve buffers for the distributions + int *dvcRecvDist_x, *dvcRecvDist_y, *dvcRecvDist_z, *dvcRecvDist_X, + *dvcRecvDist_Y, *dvcRecvDist_Z; + int *dvcRecvDist_xy, *dvcRecvDist_yz, *dvcRecvDist_xz, *dvcRecvDist_Xy, + *dvcRecvDist_Yz, *dvcRecvDist_xZ; + int *dvcRecvDist_xY, *dvcRecvDist_yZ, *dvcRecvDist_Xz, *dvcRecvDist_XY, + *dvcRecvDist_YZ, *dvcRecvDist_XZ; + //...................................................................................... + int *bb_dist; + int *bb_interactions; int *fluid_boundary; double *lattice_weight; float *lattice_cx, *lattice_cy, *lattice_cz; - //...................................................................................... - + //...................................................................................... }; - #endif diff --git a/common/SpherePack.cpp b/common/SpherePack.cpp index 557ee95c..12551238 100644 --- a/common/SpherePack.cpp +++ b/common/SpherePack.cpp @@ -47,245 +47,277 @@ #include "common/SpherePack.h" // Inline function to read line without a return argument -static inline void fgetl( char * str, int num, FILE * stream ) -{ - char* ptr = fgets( str, num, stream ); - if ( 0 ) {char *temp = (char *)&ptr; temp++;} +static inline void fgetl(char *str, int num, FILE *stream) { + char *ptr = fgets(str, num, stream); + if (0) { + char *temp = (char *)&ptr; + temp++; + } } -void WriteLocalSolidID(char *FILENAME, char *ID, int N) -{ - char value; - ofstream File(FILENAME,ios::binary); - for (int n=0; nNx) imin = Nx; - if (imax<0) imax = 0; - if (imax>Nx) imax = Nx; - if (jmin<0) jmin = 0; - if (jmin>Ny) jmin = Ny; - if (jmax<0) jmax = 0; - if (jmax>Ny) jmax = Ny; - if (kmin<0) kmin = 0; - if (kmin>Nz) kmin = Nz; - if (kmax<0) kmax = 0; - if (kmax>Nz) kmax = Nz; - // Loop over the domain for this sphere (may be null) - for (i=imin;i Nx) + imin = Nx; + if (imax < 0) + imax = 0; + if (imax > Nx) + imax = Nx; + if (jmin < 0) + jmin = 0; + if (jmin > Ny) + jmin = Ny; + if (jmax < 0) + jmax = 0; + if (jmax > Ny) + jmax = Ny; + if (kmin < 0) + kmin = 0; + if (kmin > Nz) + kmin = Nz; + if (kmax < 0) + kmax = 0; + if (kmax > Nz) + kmax = Nz; + // Loop over the domain for this sphere (may be null) + for (i = imin; i < imax; i++) { + for (j = jmin; j < jmax; j++) { + for (k = kmin; k < kmax; k++) { + // Initialize ID value to 'fluid (=1)' + x = i * hx; + y = j * hy; + z = k * hz; + value = 1; + // if inside sphere, set to zero + if ((cx - x) * (cx - x) + (cy - y) * (cy - y) + + (cz - z) * (cz - z) < + r * r) { + value = 0; + } + // get the position in the list + n = k * Nx * Ny + j * Nx + i; + if (ID[n] != 0) { + ID[n] = value; + } + } + } + } + } } -void SignedDistance(double *Distance, int nspheres, double *List_cx, double *List_cy, double *List_cz, double *List_rad, - double Lx, double Ly, double Lz, int Nx, int Ny, int Nz, - int iproc, int jproc, int kproc, int nprocx, int nprocy, int nprocz) -{ - // Use sphere lists to determine which nodes are in porespace - // Write out binary file for nodes - int N = Nx*Ny*Nz; // Domain size, including the halo - double hx,hy,hz; - double x,y,z; - double cx,cy,cz,r; - int imin,imax,jmin,jmax,kmin,kmax; - int p,i,j,k,n; - //............................................ - double min_x,min_y,min_z; - double distance; - //............................................ - // Lattice spacing for the entire domain - // It should generally be true that hx=hy=hz - // Otherwise, you will end up with ellipsoids - hx = Lx/((Nx-2)*nprocx-1); - hy = Ly/((Ny-2)*nprocy-1); - hz = Lz/((Nz-2)*nprocz-1); - //............................................ - // Get maximum and minimum for this domain - // Halo is included ! - min_x = double(iproc*(Nx-2)-1)*hx; - min_y = double(jproc*(Ny-2)-1)*hy; - min_z = double(kproc*(Nz-2)-1)*hz; - //............................................ +void SignedDistance(double *Distance, int nspheres, double *List_cx, + double *List_cy, double *List_cz, double *List_rad, + double Lx, double Ly, double Lz, int Nx, int Ny, int Nz, + int iproc, int jproc, int kproc, int nprocx, int nprocy, + int nprocz) { + // Use sphere lists to determine which nodes are in porespace + // Write out binary file for nodes + int N = Nx * Ny * Nz; // Domain size, including the halo + double hx, hy, hz; + double x, y, z; + double cx, cy, cz, r; + int imin, imax, jmin, jmax, kmin, kmax; + int p, i, j, k, n; + //............................................ + double min_x, min_y, min_z; + double distance; + //............................................ + // Lattice spacing for the entire domain + // It should generally be true that hx=hy=hz + // Otherwise, you will end up with ellipsoids + hx = Lx / ((Nx - 2) * nprocx - 1); + hy = Ly / ((Ny - 2) * nprocy - 1); + hz = Lz / ((Nz - 2) * nprocz - 1); + //............................................ + // Get maximum and minimum for this domain + // Halo is included ! + min_x = double(iproc * (Nx - 2) - 1) * hx; + min_y = double(jproc * (Ny - 2) - 1) * hy; + min_z = double(kproc * (Nz - 2) - 1) * hz; + //............................................ - //............................................ - // Pre-initialize Distance - for (n=0;nNx) imin = Nx; - if (imax<0) imax = 0; - if (imax>Nx) imax = Nx; - if (jmin<0) jmin = 0; - if (jmin>Ny) jmin = Ny; - if (jmax<0) jmax = 0; - if (jmax>Ny) jmax = Ny; - if (kmin<0) kmin = 0; - if (kmin>Nz) kmin = Nz; - if (kmax<0) kmax = 0; - if (kmax>Nz) kmax = Nz; - // Loop over the domain for this sphere (may be null) - for (i=imin;i Nx) + imin = Nx; + if (imax < 0) + imax = 0; + if (imax > Nx) + imax = Nx; + if (jmin < 0) + jmin = 0; + if (jmin > Ny) + jmin = Ny; + if (jmax < 0) + jmax = 0; + if (jmax > Ny) + jmax = Ny; + if (kmin < 0) + kmin = 0; + if (kmin > Nz) + kmin = Nz; + if (kmax < 0) + kmax = 0; + if (kmax > Nz) + kmax = Nz; + // Loop over the domain for this sphere (may be null) + for (i = imin; i < imax; i++) { + for (j = jmin; j < jmax; j++) { + for (k = kmin; k < kmax; k++) { + // x,y,z is distance in physical units + x = i * hx; + y = j * hy; + z = k * hz; + // if inside sphere, set to zero + // get the position in the list + n = k * Nx * Ny + j * Nx + i; + // Compute the distance + distance = sqrt((cx - x) * (cx - x) + (cy - y) * (cy - y) + + (cz - z) * (cz - z)) - + r; + // Assign the minimum distance + if (distance < Distance[n]) + Distance[n] = distance; + } + } + } + } - } - } - } - } - - // Map the distance to lattice units - for (n=0; n #include - #define pout std::cout #define printp printf - /******************************************************************** * Constructor/Destructor * ********************************************************************/ -UnitTest::UnitTest() -{ +UnitTest::UnitTest() { #ifdef USE_MPI comm = MPI_COMM_WORLD; #endif } UnitTest::~UnitTest() { reset(); } -void UnitTest::reset() -{ +void UnitTest::reset() { mutex.lock(); // Clear the data forcing a reallocation - std::vector().swap( pass_messages ); - std::vector().swap( fail_messages ); - std::vector().swap( expected_fail_messages ); + std::vector().swap(pass_messages); + std::vector().swap(fail_messages); + std::vector().swap(expected_fail_messages); mutex.unlock(); } - /******************************************************************** * Add a pass, fail, expected failure message in a thread-safe way * ********************************************************************/ -void UnitTest::passes( const std::string &in ) -{ +void UnitTest::passes(const std::string &in) { mutex.lock(); - pass_messages.push_back( in ); + pass_messages.push_back(in); mutex.unlock(); } -void UnitTest::failure( const std::string &in ) -{ +void UnitTest::failure(const std::string &in) { mutex.lock(); - fail_messages.push_back( in ); + fail_messages.push_back(in); mutex.unlock(); } -void UnitTest::expected_failure( const std::string &in ) -{ +void UnitTest::expected_failure(const std::string &in) { mutex.lock(); - expected_fail_messages.push_back( in ); + expected_fail_messages.push_back(in); mutex.unlock(); } - /******************************************************************** * Print a global report * * Note: only rank 0 will print, all messages will be aggregated * ********************************************************************/ -inline std::vector UnitTest::allGather( int value ) const -{ +inline std::vector UnitTest::allGather(int value) const { int size = getSize(); - std::vector data( size, value ); + std::vector data(size, value); #ifdef USE_MPI - if ( size > 1 ) - MPI_Allgather( &value, 1, MPI_INT, data.data(), 1, MPI_INT, comm ); + if (size > 1) + MPI_Allgather(&value, 1, MPI_INT, data.data(), 1, MPI_INT, comm); #endif return data; } -inline void UnitTest::barrier() const -{ +inline void UnitTest::barrier() const { #ifdef USE_MPI - if ( getSize() > 1 ) - MPI_Barrier( comm ); + if (getSize() > 1) + MPI_Barrier(comm); #endif } -static inline void print_messages( const std::vector> &messages ) -{ - if ( messages.size() > 1 ) { - for ( size_t i = 0; i < messages.size(); i++ ) { - if ( !messages[i].empty() ) { - printp( " Proccessor %i:\n", static_cast( i ) ); - for ( const auto &j : messages[i] ) +static inline void +print_messages(const std::vector> &messages) { + if (messages.size() > 1) { + for (size_t i = 0; i < messages.size(); i++) { + if (!messages[i].empty()) { + printp(" Proccessor %i:\n", static_cast(i)); + for (const auto &j : messages[i]) pout << " " << j << std::endl; } } } else { - for ( const auto &j : messages[0] ) + for (const auto &j : messages[0]) pout << " " << j << std::endl; } } -void UnitTest::report( const int level0 ) const -{ +void UnitTest::report(const int level0) const { mutex.lock(); int size = getSize(); int rank = getRank(); // Broadcast the print level from rank 0 int level = level0; #ifdef USE_MPI - if ( getSize() > 1 ) - MPI_Bcast( &level, 1, MPI_INT, 0, comm ); + if (getSize() > 1) + MPI_Bcast(&level, 1, MPI_INT, 0, comm); #endif - if ( level < 0 || level > 2 ) - ERROR( "Invalid print level" ); + if (level < 0 || level > 2) + ERROR("Invalid print level"); // Perform a global all gather to get the number of failures per processor - auto N_pass = allGather( pass_messages.size() ); - auto N_fail = allGather( fail_messages.size() ); - auto N_expected_fail = allGather( expected_fail_messages.size() ); - int N_pass_tot = 0; - int N_fail_tot = 0; + auto N_pass = allGather(pass_messages.size()); + auto N_fail = allGather(fail_messages.size()); + auto N_expected_fail = allGather(expected_fail_messages.size()); + int N_pass_tot = 0; + int N_fail_tot = 0; int N_expected_fail_tot = 0; - for ( int i = 0; i < size; i++ ) { + for (int i = 0; i < size; i++) { N_pass_tot += N_pass[i]; N_fail_tot += N_fail[i]; N_expected_fail_tot += N_expected_fail[i]; } // Send all messages to rank 0 (if needed) - std::vector> pass_messages_rank( size ); - std::vector> fail_messages_rank( size ); - std::vector> expected_fail_rank( size ); + std::vector> pass_messages_rank(size); + std::vector> fail_messages_rank(size); + std::vector> expected_fail_rank(size); // Get the pass messages - if ( ( level == 1 && N_pass_tot <= 20 ) || level == 2 ) - pass_messages_rank = UnitTest::gatherMessages( pass_messages, 1 ); + if ((level == 1 && N_pass_tot <= 20) || level == 2) + pass_messages_rank = UnitTest::gatherMessages(pass_messages, 1); // Get the fail messages - if ( level == 1 || level == 2 ) - fail_messages_rank = UnitTest::gatherMessages( fail_messages, 2 ); + if (level == 1 || level == 2) + fail_messages_rank = UnitTest::gatherMessages(fail_messages, 2); // Get the expected_fail messages - if ( ( level == 1 && N_expected_fail_tot <= 50 ) || level == 2 ) - expected_fail_rank = UnitTest::gatherMessages( expected_fail_messages, 2 ); + if ((level == 1 && N_expected_fail_tot <= 50) || level == 2) + expected_fail_rank = + UnitTest::gatherMessages(expected_fail_messages, 2); // Print the results of all messages (only rank 0 will print) - if ( rank == 0 ) { + if (rank == 0) { pout << std::endl; // Print the passed tests pout << "Tests passed" << std::endl; - if ( level == 0 || ( level == 1 && N_pass_tot > 20 ) ) { + if (level == 0 || (level == 1 && N_pass_tot > 20)) { // We want to print a summary - if ( size > 8 ) { + if (size > 8) { // Print 1 summary for all processors - printp( " %i tests passed (use report level 2 for more detail)\n", N_pass_tot ); + printp(" %i tests passed (use report level 2 for more " + "detail)\n", + N_pass_tot); } else { // Print a summary for each processor - for ( int i = 0; i < size; i++ ) - printp( " %i tests passed (proc %i) (use report level 2 for more detail)\n", - N_pass[i], i ); + for (int i = 0; i < size; i++) + printp(" %i tests passed (proc %i) (use report level 2 " + "for more detail)\n", + N_pass[i], i); } } else { // We want to print all messages - for ( int i = 0; i < size; i++ ) - ASSERT( (int) pass_messages_rank[i].size() == N_pass[i] ); - print_messages( pass_messages_rank ); + for (int i = 0; i < size; i++) + ASSERT((int)pass_messages_rank[i].size() == N_pass[i]); + print_messages(pass_messages_rank); } pout << std::endl; // Print the tests that failed pout << "Tests failed" << std::endl; - if ( level == 0 ) { + if (level == 0) { // We want to print a summary - if ( size > 8 ) { + if (size > 8) { // Print 1 summary for all processors - printp( " %i tests failed (use report level 2 for more detail)\n", N_fail_tot ); + printp(" %i tests failed (use report level 2 for more " + "detail)\n", + N_fail_tot); } else { // Print a summary for each processor - for ( int i = 0; i < size; i++ ) - printp( " %i tests failed (proc %i) (use report level 2 for more detail)\n", - N_fail[i], i ); + for (int i = 0; i < size; i++) + printp(" %i tests failed (proc %i) (use report level 2 " + "for more detail)\n", + N_fail[i], i); } } else { // We want to print all messages - for ( int i = 0; i < size; i++ ) - ASSERT( (int) fail_messages_rank[i].size() == N_fail[i] ); - print_messages( fail_messages_rank ); + for (int i = 0; i < size; i++) + ASSERT((int)fail_messages_rank[i].size() == N_fail[i]); + print_messages(fail_messages_rank); } pout << std::endl; // Print the tests that expected failed pout << "Tests expected failed" << std::endl; - if ( level == 0 || ( level == 1 && N_expected_fail_tot > 50 ) ) { + if (level == 0 || (level == 1 && N_expected_fail_tot > 50)) { // We want to print a summary - if ( size > 8 ) { + if (size > 8) { // Print 1 summary for all processors - printp( " %i tests expected failed (use report level 2 for more detail)\n", - N_expected_fail_tot ); + printp(" %i tests expected failed (use report level 2 for " + "more detail)\n", + N_expected_fail_tot); } else { // Print a summary for each processor - for ( int i = 0; i < size; i++ ) - printp( " %i tests expected failed (proc %i) (use report level 2 for more " - "detail)\n", - N_expected_fail[i], i ); + for (int i = 0; i < size; i++) + printp(" %i tests expected failed (proc %i) (use " + "report level 2 for more " + "detail)\n", + N_expected_fail[i], i); } } else { // We want to print all messages - for ( int i = 0; i < size; i++ ) - ASSERT( (int) expected_fail_rank[i].size() == N_expected_fail[i] ); - print_messages( expected_fail_rank ); + for (int i = 0; i < size; i++) + ASSERT((int)expected_fail_rank[i].size() == N_expected_fail[i]); + print_messages(expected_fail_rank); } pout << std::endl; } // Add a barrier to synchronize all processors (rank 0 is much slower) barrier(); - Utilities::sleep_ms( 10 ); // Need a brief pause to allow any printing to finish + Utilities::sleep_ms( + 10); // Need a brief pause to allow any printing to finish mutex.unlock(); } - /******************************************************************** * Gather the messages to rank 0 * ********************************************************************/ -std::vector> UnitTest::gatherMessages( - const std::vector &local_messages, int tag ) const -{ +std::vector> +UnitTest::gatherMessages(const std::vector &local_messages, + int tag) const { const int rank = getRank(); const int size = getSize(); - std::vector> messages( size ); - if ( rank == 0 ) { + std::vector> messages(size); + if (rank == 0) { // Rank 0 should receive all messages - for ( int i = 0; i < size; i++ ) { - if ( i == 0 ) + for (int i = 0; i < size; i++) { + if (i == 0) messages[i] = local_messages; else - messages[i] = unpack_message_stream( i, tag ); + messages[i] = unpack_message_stream(i, tag); } } else { // All other ranks send their message (use non-blocking communication) - pack_message_stream( local_messages, 0, tag ); + pack_message_stream(local_messages, 0, tag); } return messages; } - /******************************************************************** * Pack and send the given messages * ********************************************************************/ -void UnitTest::pack_message_stream( - const std::vector &messages, const int rank, const int tag ) const -{ +void UnitTest::pack_message_stream(const std::vector &messages, + const int rank, const int tag) const { #ifdef USE_MPI // Get the size of the messages - auto N_messages = (int) messages.size(); - auto *msg_size = new int[N_messages]; + auto N_messages = (int)messages.size(); + auto *msg_size = new int[N_messages]; int msg_size_tot = 0; - for ( int i = 0; i < N_messages; i++ ) { - msg_size[i] = (int) messages[i].size(); + for (int i = 0; i < N_messages; i++) { + msg_size[i] = (int)messages[i].size(); msg_size_tot += msg_size[i]; } // Allocate space for the message stream - size_t size_data = ( N_messages + 1 ) * sizeof( int ) + msg_size_tot; - auto *data = new char[size_data]; + size_t size_data = (N_messages + 1) * sizeof(int) + msg_size_tot; + auto *data = new char[size_data]; // Pack the message stream - memcpy( data, &N_messages, sizeof( int ) ); - memcpy( &data[sizeof( int )], msg_size, N_messages * sizeof( int ) ); - size_t k = ( N_messages + 1 ) * sizeof( int ); - for ( int i = 0; i < N_messages; i++ ) { - messages[i].copy( &data[k], msg_size[i] ); + memcpy(data, &N_messages, sizeof(int)); + memcpy(&data[sizeof(int)], msg_size, N_messages * sizeof(int)); + size_t k = (N_messages + 1) * sizeof(int); + for (int i = 0; i < N_messages; i++) { + messages[i].copy(&data[k], msg_size[i]); k += msg_size[i]; } // Send the message stream (using a non-blocking send) MPI_Request request; - MPI_Isend( data, size_data, MPI_CHAR, rank, tag, comm, &request ); + MPI_Isend(data, size_data, MPI_CHAR, rank, tag, comm, &request); // Wait for the communication to send and free the temporary memory MPI_Status status; - MPI_Wait( &request, &status ); + MPI_Wait(&request, &status); delete[] data; delete[] msg_size; #else - NULL_USE( messages ); - NULL_USE( rank ); - NULL_USE( tag ); + NULL_USE(messages); + NULL_USE(rank); + NULL_USE(tag); #endif } - /******************************************************************** * Receive and unpack a message stream * ********************************************************************/ -std::vector UnitTest::unpack_message_stream( const int rank, const int tag ) const -{ +std::vector UnitTest::unpack_message_stream(const int rank, + const int tag) const { #ifdef USE_MPI // Probe the message to get the message size MPI_Status status; - MPI_Probe( rank, tag, comm, &status ); + MPI_Probe(rank, tag, comm, &status); int size_data = -1; - MPI_Get_count( &status, MPI_BYTE, &size_data ); - ASSERT( size_data >= 0 ); + MPI_Get_count(&status, MPI_BYTE, &size_data); + ASSERT(size_data >= 0); // Allocate memory to receive the data auto *data = new char[size_data]; // receive the data (using a non-blocking receive) MPI_Request request; - MPI_Irecv( data, size_data, MPI_CHAR, rank, tag, comm, &request ); + MPI_Irecv(data, size_data, MPI_CHAR, rank, tag, comm, &request); // Wait for the communication to be received - MPI_Wait( &request, &status ); + MPI_Wait(&request, &status); // Unpack the message stream int N_messages = 0; - memcpy( &N_messages, data, sizeof( int ) ); - if ( N_messages == 0 ) { + memcpy(&N_messages, data, sizeof(int)); + if (N_messages == 0) { delete[] data; return std::vector(); } - std::vector msg_size( N_messages ); - std::vector messages( N_messages ); - memcpy( msg_size.data(), &data[sizeof( int )], N_messages * sizeof( int ) ); - int k = ( N_messages + 1 ) * sizeof( int ); - for ( int i = 0; i < N_messages; i++ ) { - messages[i] = std::string( &data[k], msg_size[i] ); + std::vector msg_size(N_messages); + std::vector messages(N_messages); + memcpy(msg_size.data(), &data[sizeof(int)], N_messages * sizeof(int)); + int k = (N_messages + 1) * sizeof(int); + for (int i = 0; i < N_messages; i++) { + messages[i] = std::string(&data[k], msg_size[i]); k += msg_size[i]; } delete[] data; return messages; #else - NULL_USE( rank ); - NULL_USE( tag ); + NULL_USE(rank); + NULL_USE(tag); return std::vector(); #endif } - /******************************************************************** * Other functions * ********************************************************************/ -int UnitTest::getRank() const -{ +int UnitTest::getRank() const { int rank = 0; #ifdef USE_MPI int flag = 0; - MPI_Initialized( &flag ); - if ( flag ) - MPI_Comm_rank( comm, &rank ); + MPI_Initialized(&flag); + if (flag) + MPI_Comm_rank(comm, &rank); #endif return rank; } -int UnitTest::getSize() const -{ +int UnitTest::getSize() const { int size = 1; #ifdef USE_MPI int flag = 0; - MPI_Initialized( &flag ); - if ( flag ) - MPI_Comm_size( comm, &size ); + MPI_Initialized(&flag); + if (flag) + MPI_Comm_size(comm, &size); #endif return size; } -size_t UnitTest::NumPassGlobal() const -{ +size_t UnitTest::NumPassGlobal() const { size_t num = pass_messages.size(); #ifdef USE_MPI - if ( getSize() > 1 ) { - auto send = static_cast( num ); - int sum = 0; - MPI_Allreduce( &send, &sum, 1, MPI_INT, MPI_SUM, comm ); - num = static_cast( sum ); + if (getSize() > 1) { + auto send = static_cast(num); + int sum = 0; + MPI_Allreduce(&send, &sum, 1, MPI_INT, MPI_SUM, comm); + num = static_cast(sum); } #endif return num; } -size_t UnitTest::NumFailGlobal() const -{ +size_t UnitTest::NumFailGlobal() const { size_t num = fail_messages.size(); #ifdef USE_MPI - if ( getSize() > 1 ) { - auto send = static_cast( num ); - int sum = 0; - MPI_Allreduce( &send, &sum, 1, MPI_INT, MPI_SUM, comm ); - num = static_cast( sum ); + if (getSize() > 1) { + auto send = static_cast(num); + int sum = 0; + MPI_Allreduce(&send, &sum, 1, MPI_INT, MPI_SUM, comm); + num = static_cast(sum); } #endif return num; } -size_t UnitTest::NumExpectedFailGlobal() const -{ +size_t UnitTest::NumExpectedFailGlobal() const { size_t num = expected_fail_messages.size(); #ifdef USE_MPI - if ( getSize() > 1 ) { - auto send = static_cast( num ); - int sum = 0; - MPI_Allreduce( &send, &sum, 1, MPI_INT, MPI_SUM, comm ); - num = static_cast( sum ); + if (getSize() > 1) { + auto send = static_cast(num); + int sum = 0; + MPI_Allreduce(&send, &sum, 1, MPI_INT, MPI_SUM, comm); + num = static_cast(sum); } #endif return num; diff --git a/common/UnitTest.h b/common/UnitTest.h index 339bd65e..693c9c72 100644 --- a/common/UnitTest.h +++ b/common/UnitTest.h @@ -25,7 +25,6 @@ #include "mpi.h" #endif - /*! * @brief Class UnitTest is simple utility for running unit tests. * It provides basic routines for tracing success or failure of tests, @@ -44,8 +43,7 @@ * \endcode */ -class UnitTest -{ +class UnitTest { public: //! Constructor UnitTest(); @@ -54,13 +52,13 @@ public: virtual ~UnitTest(); //! Indicate a passed test (thread-safe) - virtual void passes( const std::string &in ); + virtual void passes(const std::string &in); //! Indicate a failed test (thread-safe) - virtual void failure( const std::string &in ); + virtual void failure(const std::string &in); //! Indicate an expected failed test (thread-safe) - virtual void expected_failure( const std::string &in ); + virtual void expected_failure(const std::string &in); //! Return the number of passed tests locally virtual size_t NumPassLocal() const { return pass_messages.size(); } @@ -69,7 +67,9 @@ public: virtual size_t NumFailLocal() const { return fail_messages.size(); } //! Return the number of expected failed tests locally - virtual size_t NumExpectedFailLocal() const { return expected_fail_messages.size(); } + virtual size_t NumExpectedFailLocal() const { + return expected_fail_messages.size(); + } //! Return the number of passed tests locally virtual size_t NumPassGlobal() const; @@ -98,7 +98,7 @@ public: * failed tests (if <=50) or the number passed otherwise. * 2: Report all passed, failed, and expected failed tests. */ - virtual void report( const int level = 1 ) const; + virtual void report(const int level = 1) const; //! Clear the messages void reset(); @@ -114,23 +114,24 @@ protected: private: // Make the copy constructor private - UnitTest( const UnitTest & ) {} + UnitTest(const UnitTest &) {} // Function to pack the messages into a single data stream and send to the given processor // Note: This function does not return until the message stream has been sent - void pack_message_stream( - const std::vector &messages, const int rank, const int tag ) const; + void pack_message_stream(const std::vector &messages, + const int rank, const int tag) const; // Function to unpack the messages from a single data stream // Note: This function does not return until the message stream has been received - std::vector unpack_message_stream( const int rank, const int tag ) const; + std::vector unpack_message_stream(const int rank, + const int tag) const; // Helper functions inline void barrier() const; - inline std::vector allGather( int value ) const; - inline std::vector> gatherMessages( - const std::vector &local_messages, int tag ) const; + inline std::vector allGather(int value) const; + inline std::vector> + gatherMessages(const std::vector &local_messages, + int tag) const; }; - #endif diff --git a/common/Units.cpp b/common/Units.cpp index f6e9b79a..13b2d1e3 100644 --- a/common/Units.cpp +++ b/common/Units.cpp @@ -21,134 +21,126 @@ #include #include - constexpr double Units::d_pow10[22]; constexpr char Units::d_prefixSymbol[]; - /******************************************************************** * Constructors * ********************************************************************/ -Units::Units() : d_prefix( UnitPrefix::unknown ), d_unit( UnitValue::unknown ) {} -Units::Units( UnitPrefix p, UnitValue u ) : d_prefix( p ), d_unit( u ) {} -Units::Units( const std::string& unit ) - : d_prefix( UnitPrefix::unknown ), d_unit( UnitValue::unknown ) -{ +Units::Units() : d_prefix(UnitPrefix::unknown), d_unit(UnitValue::unknown) {} +Units::Units(UnitPrefix p, UnitValue u) : d_prefix(p), d_unit(u) {} +Units::Units(const std::string &unit) + : d_prefix(UnitPrefix::unknown), d_unit(UnitValue::unknown) { // Parse the string to get it into a more friendly format auto tmp = unit; - tmp.erase( std::remove( tmp.begin(), tmp.end(), ' ' ), tmp.end() ); + tmp.erase(std::remove(tmp.begin(), tmp.end(), ' '), tmp.end()); // Check if the character '-' is present indicating a seperation between the prefix and unit - size_t index = tmp.find( '-' ); - if ( index != std::string::npos ) { - d_prefix = getUnitPrefix( tmp.substr( 0, index ) ); - d_unit = getUnitValue( tmp.substr( index + 1 ) ); + size_t index = tmp.find('-'); + if (index != std::string::npos) { + d_prefix = getUnitPrefix(tmp.substr(0, index)); + d_unit = getUnitValue(tmp.substr(index + 1)); } else { - if ( tmp.size() <= 1 ) { + if (tmp.size() <= 1) { d_prefix = UnitPrefix::none; - d_unit = getUnitValue( tmp ); - } else if ( tmp.substr( 0, 2 ) == "da" ) { + d_unit = getUnitValue(tmp); + } else if (tmp.substr(0, 2) == "da") { d_prefix = UnitPrefix::deca; - d_unit = getUnitValue( tmp.substr( 2 ) ); + d_unit = getUnitValue(tmp.substr(2)); } else { - d_prefix = getUnitPrefix( tmp.substr( 0, 1 ) ); - d_unit = getUnitValue( tmp.substr( 1 ) ); - if ( d_prefix == UnitPrefix::unknown || d_unit == UnitValue::unknown ) { + d_prefix = getUnitPrefix(tmp.substr(0, 1)); + d_unit = getUnitValue(tmp.substr(1)); + if (d_prefix == UnitPrefix::unknown || + d_unit == UnitValue::unknown) { d_prefix = UnitPrefix::none; - d_unit = getUnitValue( tmp ); + d_unit = getUnitValue(tmp); } } } } - /******************************************************************** * Get prefix * ********************************************************************/ -Units::UnitPrefix Units::getUnitPrefix( const std::string& str ) noexcept -{ +Units::UnitPrefix Units::getUnitPrefix(const std::string &str) noexcept { Units::UnitPrefix value = UnitPrefix::unknown; - if ( str.empty() ) { + if (str.empty()) { value = UnitPrefix::none; - } else if ( str == "yotta" || str == "Y" ) { + } else if (str == "yotta" || str == "Y") { value = UnitPrefix::yotta; - } else if ( str == "zetta" || str == "Z" ) { + } else if (str == "zetta" || str == "Z") { value = UnitPrefix::zetta; - } else if ( str == "exa" || str == "E" ) { + } else if (str == "exa" || str == "E") { value = UnitPrefix::exa; - } else if ( str == "peta" || str == "P" ) { + } else if (str == "peta" || str == "P") { value = UnitPrefix::peta; - } else if ( str == "tera" || str == "T" ) { + } else if (str == "tera" || str == "T") { value = UnitPrefix::tera; - } else if ( str == "giga" || str == "G" ) { + } else if (str == "giga" || str == "G") { value = UnitPrefix::giga; - } else if ( str == "mega" || str == "M" ) { + } else if (str == "mega" || str == "M") { value = UnitPrefix::mega; - } else if ( str == "kilo" || str == "k" ) { + } else if (str == "kilo" || str == "k") { value = UnitPrefix::kilo; - } else if ( str == "hecto" || str == "h" ) { + } else if (str == "hecto" || str == "h") { value = UnitPrefix::hecto; - } else if ( str == "deca" || str == "da" ) { + } else if (str == "deca" || str == "da") { value = UnitPrefix::deca; - } else if ( str == "deci" || str == "d" ) { + } else if (str == "deci" || str == "d") { value = UnitPrefix::deci; - } else if ( str == "centi" || str == "c" ) { + } else if (str == "centi" || str == "c") { value = UnitPrefix::centi; - } else if ( str == "milli" || str == "m" ) { + } else if (str == "milli" || str == "m") { value = UnitPrefix::milli; - } else if ( str == "micro" || str == "u" ) { + } else if (str == "micro" || str == "u") { value = UnitPrefix::micro; - } else if ( str == "nano" || str == "n" ) { + } else if (str == "nano" || str == "n") { value = UnitPrefix::nano; - } else if ( str == "pico" || str == "p" ) { + } else if (str == "pico" || str == "p") { value = UnitPrefix::pico; - } else if ( str == "femto" || str == "f" ) { + } else if (str == "femto" || str == "f") { value = UnitPrefix::femto; - } else if ( str == "atto" || str == "a" ) { + } else if (str == "atto" || str == "a") { value = UnitPrefix::atto; - } else if ( str == "zepto" || str == "z" ) { + } else if (str == "zepto" || str == "z") { value = UnitPrefix::zepto; - } else if ( str == "yocto" || str == "y" ) { + } else if (str == "yocto" || str == "y") { value = UnitPrefix::yocto; } return value; } - /******************************************************************** * Get unit value * ********************************************************************/ -Units::UnitValue Units::getUnitValue( const std::string& str ) noexcept -{ +Units::UnitValue Units::getUnitValue(const std::string &str) noexcept { Units::UnitValue value = UnitValue::unknown; - if ( str == "meter" || str == "m" ) { + if (str == "meter" || str == "m") { value = UnitValue::meter; - } else if ( str == "gram" || str == "g" ) { + } else if (str == "gram" || str == "g") { value = UnitValue::gram; - } else if ( str == "second" || str == "s" ) { + } else if (str == "second" || str == "s") { value = UnitValue::second; - } else if ( str == "ampere" || str == "A" ) { + } else if (str == "ampere" || str == "A") { value = UnitValue::ampere; - } else if ( str == "kelvin" || str == "K" ) { + } else if (str == "kelvin" || str == "K") { value = UnitValue::kelvin; - } else if ( str == "joule" || str == "J" ) { + } else if (str == "joule" || str == "J") { value = UnitValue::joule; - } else if ( str == "ergs" || str == "erg" ) { + } else if (str == "ergs" || str == "erg") { value = UnitValue::erg; - } else if ( str == "degree" || str == "degrees" ) { + } else if (str == "degree" || str == "degrees") { value = UnitValue::degree; - } else if ( str == "radian" || str == "radians" ) { + } else if (str == "radian" || str == "radians") { value = UnitValue::radian; } return value; } - /******************************************************************** * Get unit type * ********************************************************************/ -Units::UnitType Units::getUnitType( UnitValue u ) noexcept -{ - switch ( u ) { +Units::UnitType Units::getUnitType(UnitValue u) noexcept { + switch (u) { case UnitValue::meter: return UnitType::length; case UnitValue::gram: @@ -170,72 +162,66 @@ Units::UnitType Units::getUnitType( UnitValue u ) noexcept } } - /******************************************************************** * Convert to another unit system * ********************************************************************/ -double Units::convert( const Units& rhs ) const noexcept -{ - if ( this->operator==( rhs ) ) +double Units::convert(const Units &rhs) const noexcept { + if (this->operator==(rhs)) return 1; // Convert the prefix - double cp = convert( d_prefix ) / convert( rhs.d_prefix ); - if ( d_unit == rhs.d_unit ) + double cp = convert(d_prefix) / convert(rhs.d_prefix); + if (d_unit == rhs.d_unit) return cp; // Only need to convert prefix // Convert the unit - if ( getUnitType( d_unit ) != getUnitType( rhs.d_unit ) ) + if (getUnitType(d_unit) != getUnitType(rhs.d_unit)) return 0; // Invalid conversion double cu = 0; - if ( d_unit == UnitValue::joule && rhs.d_unit == UnitValue::erg ) + if (d_unit == UnitValue::joule && rhs.d_unit == UnitValue::erg) cu = 1e7; - else if ( d_unit == UnitValue::erg && rhs.d_unit == UnitValue::joule ) + else if (d_unit == UnitValue::erg && rhs.d_unit == UnitValue::joule) cu = 1e-7; - else if ( d_unit == UnitValue::degree && rhs.d_unit == UnitValue::radian ) + else if (d_unit == UnitValue::degree && rhs.d_unit == UnitValue::radian) cu = 0.017453292519943; - else if ( d_unit == UnitValue::radian && rhs.d_unit == UnitValue::degree ) + else if (d_unit == UnitValue::radian && rhs.d_unit == UnitValue::degree) cu = 57.295779513082323; // Return the total conversion return cp * cu; } - /******************************************************************** * Write a string for the units * ********************************************************************/ -std::string Units::str() const -{ - ASSERT( !isNull() ); - return std::string( str( d_prefix ).data() ) + str( d_unit ); +std::string Units::str() const { + ASSERT(!isNull()); + return std::string(str(d_prefix).data()) + str(d_unit); } -std::array Units::str( UnitPrefix p ) noexcept -{ +std::array Units::str(UnitPrefix p) noexcept { std::array str; - str[0] = d_prefixSymbol[static_cast( p )]; + str[0] = d_prefixSymbol[static_cast(p)]; str[1] = 0; str[2] = 0; - if ( p == UnitPrefix::deca ) + if (p == UnitPrefix::deca) str[1] = 'a'; return str; } -std::string Units::str( UnitValue u ) -{ - if ( u == UnitValue::meter ) { +std::string Units::str(UnitValue u) { + if (u == UnitValue::meter) { return "m"; - } else if ( u == UnitValue::gram ) { + } else if (u == UnitValue::gram) { return "g"; - } else if ( u == UnitValue::second ) { + } else if (u == UnitValue::second) { return "s"; - } else if ( u == UnitValue::ampere ) { + } else if (u == UnitValue::ampere) { return "A"; - } else if ( u == UnitValue::kelvin ) { + } else if (u == UnitValue::kelvin) { return "K"; - } else if ( u == UnitValue::joule ) { + } else if (u == UnitValue::joule) { return "J"; - } else if ( u == UnitValue::erg ) { + } else if (u == UnitValue::erg) { return "erg"; - } else if ( u == UnitValue::degree ) { + } else if (u == UnitValue::degree) { return "degree"; - } else if ( u == UnitValue::radian ) { + } else if (u == UnitValue::radian) { return "radian"; } return "unknown"; diff --git a/common/Units.h b/common/Units.h index 2ae503cd..cdbd25ca 100644 --- a/common/Units.h +++ b/common/Units.h @@ -24,34 +24,32 @@ #include #include - //! Unit system class -class Units final -{ +class Units final { public: //! Enum to hold prefix enum class UnitPrefix : int8_t { - yocto = 0, - zepto = 1, - atto = 2, - femto = 3, - pico = 4, - nano = 5, - micro = 6, - milli = 7, - centi = 8, - deci = 9, - none = 10, - deca = 11, - hecto = 12, - kilo = 13, - mega = 14, - giga = 15, - tera = 16, - peta = 17, - exa = 18, - zetta = 19, - yotta = 20, + yocto = 0, + zepto = 1, + atto = 2, + femto = 3, + pico = 4, + nano = 5, + micro = 6, + milli = 7, + centi = 8, + deci = 9, + none = 10, + deca = 11, + hecto = 12, + kilo = 13, + mega = 14, + giga = 15, + tera = 16, + peta = 17, + exa = 18, + zetta = 19, + yotta = 20, unknown = 21 }; @@ -81,16 +79,15 @@ public: unknown }; - public: //! Constructor Units(); //! Constructor - explicit Units( const std::string& unit ); + explicit Units(const std::string &unit); //! Constructor - explicit Units( UnitPrefix, UnitValue ); + explicit Units(UnitPrefix, UnitValue); //! Get the prefix inline UnitPrefix getPrefix() const noexcept { return d_prefix; } @@ -99,57 +96,57 @@ public: inline UnitValue getUnit() const noexcept { return d_unit; } //! Get the unit - inline UnitType getUnitType() const noexcept { return getUnitType( d_unit ); } + inline UnitType getUnitType() const noexcept { return getUnitType(d_unit); } //! Get the unit - static UnitType getUnitType( UnitValue ) noexcept; + static UnitType getUnitType(UnitValue) noexcept; //! Get the prefix from a string - static UnitPrefix getUnitPrefix( const std::string& ) noexcept; + static UnitPrefix getUnitPrefix(const std::string &) noexcept; //! Get the unit value from a string - static UnitValue getUnitValue( const std::string& ) noexcept; + static UnitValue getUnitValue(const std::string &) noexcept; //! Convert to the given unit system - double convert( const Units& ) const noexcept; + double convert(const Units &) const noexcept; //! Convert a prefix to a scalar - static inline double convert( UnitPrefix x ) noexcept - { - return d_pow10[static_cast( x )]; + static inline double convert(UnitPrefix x) noexcept { + return d_pow10[static_cast(x)]; } //! Get a string representation of the units std::string str() const; //! Get a string representation for the prefix - static std::array str( UnitPrefix ) noexcept; + static std::array str(UnitPrefix) noexcept; //! Get a string representation for the unit value - static std::string str( UnitValue ); + static std::string str(UnitValue); //! Operator == - inline bool operator==( const Units& rhs ) const noexcept - { + inline bool operator==(const Units &rhs) const noexcept { return d_prefix == rhs.d_prefix && d_unit == rhs.d_unit; } //! Operator != - inline bool operator!=( const Units& rhs ) const noexcept - { + inline bool operator!=(const Units &rhs) const noexcept { return d_prefix != rhs.d_prefix || d_unit != rhs.d_unit; } //! Check if unit is null - bool isNull() const { return d_prefix == UnitPrefix::unknown || d_unit == UnitValue::unknown; } + bool isNull() const { + return d_prefix == UnitPrefix::unknown || d_unit == UnitValue::unknown; + } protected: UnitPrefix d_prefix; UnitValue d_unit; private: - constexpr static double d_pow10[22] = { 1e-24, 1e-21, 1e-18, 1e-15, 1e-12, 1e-9, 1e-6, 1e-3, - 1e-2, 0.1, 1, 10, 100, 1000, 1e6, 1e9, 1e12, 1e15, 1e18, 1e21, 1e24, 0 }; + constexpr static double d_pow10[22] = { + 1e-24, 1e-21, 1e-18, 1e-15, 1e-12, 1e-9, 1e-6, 1e-3, 1e-2, 0.1, 1, + 10, 100, 1000, 1e6, 1e9, 1e12, 1e15, 1e18, 1e21, 1e24, 0}; constexpr static char d_prefixSymbol[] = "yzafpnumcd\0dhkMGTPEZYu"; }; diff --git a/common/Utilities.cpp b/common/Utilities.cpp index 2b4806d6..ddcc9e8d 100644 --- a/common/Utilities.cpp +++ b/common/Utilities.cpp @@ -31,7 +31,6 @@ #include #include - // OS specific includes / definitions // clang-format off #if defined( WIN32 ) || defined( _WIN32 ) || defined( WIN64 ) || defined( _WIN64 ) @@ -45,162 +44,150 @@ #endif // clang-format on - // Mutex for Utility functions static std::mutex Utilities_mutex; - /**************************************************************************** * Function to perform the default startup/shutdown sequences * ****************************************************************************/ -void Utilities::startup( int argc, char **argv, bool multiple ) -{ - NULL_USE( argc ); - NULL_USE( argv ); +void Utilities::startup(int argc, char **argv, bool multiple) { + NULL_USE(argc); + NULL_USE(argv); // Disable OpenMP - Utilities::setenv( "OMP_NUM_THREADS", "1" ); - Utilities::setenv( "MKL_NUM_THREADS", "1" ); + Utilities::setenv("OMP_NUM_THREADS", "1"); + Utilities::setenv("MKL_NUM_THREADS", "1"); // Start MPI #ifdef USE_MPI - if ( multiple ) { + if (multiple) { int provided; - MPI_Init_thread( &argc, &argv, MPI_THREAD_MULTIPLE, &provided ); - if ( provided < MPI_THREAD_MULTIPLE ) { + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); + if (provided < MPI_THREAD_MULTIPLE) { int rank; - MPI_Comm_rank( MPI_COMM_WORLD, &rank ); - if ( rank == 0 ) - std::cerr << "Warning: Failed to start MPI with necessary thread support, thread support will be disabled" << std::endl; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 0) + std::cerr << "Warning: Failed to start MPI with necessary " + "thread support, thread support will be disabled" + << std::endl; } - StackTrace::globalCallStackInitialize( MPI_COMM_WORLD ); + StackTrace::globalCallStackInitialize(MPI_COMM_WORLD); } else { - MPI_Init( &argc, &argv ); + MPI_Init(&argc, &argv); } #endif // Set the error handlers - Utilities::setAbortBehavior( true, 3 ); + Utilities::setAbortBehavior(true, 3); Utilities::setErrorHandlers(); } -void Utilities::shutdown() -{ +void Utilities::shutdown() { // Clear the error handlers Utilities::clearErrorHandlers(); StackTrace::clearSignals(); StackTrace::clearSymbols(); int rank = 0; #ifdef USE_MPI - MPI_Comm_rank( MPI_COMM_WORLD, &rank ); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); StackTrace::globalCallStackFinalize(); - MPI_Barrier( MPI_COMM_WORLD ); + MPI_Barrier(MPI_COMM_WORLD); MPI_Finalize(); #endif #ifdef USE_TIMER PROFILE_DISABLE(); auto memory = MemoryApp::getMemoryStats(); - if ( rank == 0 && memory.N_new > memory.N_delete ) - MemoryApp::print( std::cout ); + if (rank == 0 && memory.N_new > memory.N_delete) + MemoryApp::print(std::cout); #endif } - /**************************************************************************** * Function to set an environemental variable * ****************************************************************************/ -void Utilities::setenv( const std::string &name, const std::string &value ) -{ +void Utilities::setenv(const std::string &name, const std::string &value) { Utilities_mutex.lock(); -#if defined( USE_LINUX ) || defined( USE_MAC ) +#if defined(USE_LINUX) || defined(USE_MAC) bool pass = false; - if ( !value.empty() ) - pass = ::setenv( name.data(), value.data(), 1 ) == 0; + if (!value.empty()) + pass = ::setenv(name.data(), value.data(), 1) == 0; else - pass = ::unsetenv( name.data() ) == 0; -#elif defined( USE_WINDOWS ) - bool pass = SetEnvironmentVariable( name.data(), value.data() ) != 0; + pass = ::unsetenv(name.data()) == 0; +#elif defined(USE_WINDOWS) + bool pass = SetEnvironmentVariable(name.data(), value.data()) != 0; #else #error Unknown OS #endif Utilities_mutex.unlock(); - if ( !pass ) { + if (!pass) { char msg[1024]; - if ( !value.empty() ) - sprintf( - msg, "Error setting enviornmental variable: %s=%s\n", name.data(), value.data() ); + if (!value.empty()) + sprintf(msg, "Error setting enviornmental variable: %s=%s\n", + name.data(), value.data()); else - sprintf( msg, "Error clearing enviornmental variable: %s\n", name.data() ); - ERROR( msg ); + sprintf(msg, "Error clearing enviornmental variable: %s\n", + name.data()); + ERROR(msg); } } -std::string Utilities::getenv( const std::string &name ) -{ +std::string Utilities::getenv(const std::string &name) { std::string var; Utilities_mutex.lock(); - auto tmp = std::getenv( name.data() ); - if ( tmp ) - var = std::string( tmp ); + auto tmp = std::getenv(name.data()); + if (tmp) + var = std::string(tmp); Utilities_mutex.unlock(); return var; } - /**************************************************************************** * Factor a number into it's prime factors * ****************************************************************************/ -std::vector Utilities::factor(size_t number) -{ - if ( number<=3 ) - return std::vector(1,(int)number); +std::vector Utilities::factor(size_t number) { + if (number <= 3) + return std::vector(1, (int)number); size_t i, n, n_max; bool factor_found; // Compute the maximum number of factors int N_primes_max = 1; n = number; - while (n >>= 1) ++N_primes_max; - // Initialize n, factors + while (n >>= 1) + ++N_primes_max; + // Initialize n, factors n = number; std::vector factors; factors.reserve(N_primes_max); - while ( 1 ) { + while (1) { // Check if n is a trivial prime number - if ( n==2 || n==3 || n==5 ) { - factors.push_back( (int) n ); + if (n == 2 || n == 3 || n == 5) { + factors.push_back((int)n); break; - } + } // Check if n is divisible by 2 - if ( n%2 == 0 ) { - factors.push_back( 2 ); - n/=2; + if (n % 2 == 0) { + factors.push_back(2); + n /= 2; continue; - } + } // Check each odd number until a factor is reached - n_max = (size_t) floor(sqrt((double) n)); + n_max = (size_t)floor(sqrt((double)n)); factor_found = false; - for (i=3; i<=n_max; i+=2) { - if ( n%i == 0 ) { - factors.push_back( i ); - n/=i; + for (i = 3; i <= n_max; i += 2) { + if (n % i == 0) { + factors.push_back(i); + n /= i; factor_found = true; break; - } + } } - if ( factor_found ) + if (factor_found) continue; // No factors were found, the number must be prime - factors.push_back( (int) n ); + factors.push_back((int)n); break; } // Sort the factors - std::sort( factors.begin(), factors.end() ); + std::sort(factors.begin(), factors.end()); return factors; } - /**************************************************************************** * Dummy function to prevent compiler from optimizing away variable * ****************************************************************************/ -void Utilities::nullUse( void* data ) -{ - NULL_USE(data); -} - - - +void Utilities::nullUse(void *data) { NULL_USE(data); } diff --git a/common/Utilities.h b/common/Utilities.h index 21e65284..e9567535 100644 --- a/common/Utilities.h +++ b/common/Utilities.h @@ -22,10 +22,8 @@ #include "StackTrace/Utilities.h" - namespace Utilities { - // Functions inherited from StackTrace::Utilities using StackTrace::Utilities::abort; using StackTrace::Utilities::cause_segfault; @@ -35,11 +33,10 @@ using StackTrace::Utilities::getMemoryUsage; using StackTrace::Utilities::getSystemMemory; using StackTrace::Utilities::setAbortBehavior; using StackTrace::Utilities::setErrorHandlers; -using StackTrace::Utilities::tick; -using StackTrace::Utilities::time; using StackTrace::Utilities::sleep_ms; using StackTrace::Utilities::sleep_s; - +using StackTrace::Utilities::tick; +using StackTrace::Utilities::time; /*! * \brief Start MPI, error handlers @@ -48,7 +45,7 @@ using StackTrace::Utilities::sleep_s; * \param argv argv from main * \param multiple Intialize mpi with MPI_THREAD_MULTIPLE support? */ -void startup( int argc, char **argv, bool multiple=true ); +void startup(int argc, char **argv, bool multiple = true); /*! * \brief Stop MPI, error handlers @@ -56,51 +53,41 @@ void startup( int argc, char **argv, bool multiple=true ); */ void shutdown(); - /*! * Get an environmental variable * @param name The name of the environmental variable * @return The value of the enviornmental variable */ -std::string getenv( const std::string &name ); - +std::string getenv(const std::string &name); /*! * Set an environmental variable * @param name The name of the environmental variable * @param value The value to set */ -void setenv( const std::string &name, const std::string &value ); - +void setenv(const std::string &name, const std::string &value); //! std::string version of sprintf -inline std::string stringf( const char *format, ... ); - +inline std::string stringf(const char *format, ...); //! Factor a number into it's prime factors std::vector factor(size_t number); - //! Null use function -void nullUse( void* ); - +void nullUse(void *); } // namespace Utilities - #include "common/UtilityMacros.h" - // stringf -inline std::string Utilities::stringf( const char *format, ... ) -{ +inline std::string Utilities::stringf(const char *format, ...) { va_list ap; - va_start( ap, format ); + va_start(ap, format); char tmp[4096]; - vsprintf( tmp, format, ap ); - va_end( ap ); - return std::string( tmp ); + vsprintf(tmp, format, ap); + va_end(ap); + return std::string(tmp); } - #endif diff --git a/common/Utilities.hpp b/common/Utilities.hpp index bcdc4057..e84c62c8 100644 --- a/common/Utilities.hpp +++ b/common/Utilities.hpp @@ -1,108 +1,104 @@ #ifndef included_Utilities_hpp #define included_Utilities_hpp - #include "Utilities.h" #include - namespace Utilities { - /************************************************************************ * templated quicksort routines * ************************************************************************/ -template -void quicksort( std::vector &x ) -{ - if ( x.size() <= 1u ) +template void quicksort(std::vector &x) { + if (x.size() <= 1u) return; T *arr = &x[0]; bool test; long int i, ir, j, jstack, k, l, istack[100]; T a, tmp_a; jstack = 0; - l = 0; - ir = x.size() - 1; - while ( 1 ) { - if ( ir - l < 7 ) { // Insertion sort when subarray small enough. - for ( j = l + 1; j <= ir; j++ ) { - a = arr[j]; + l = 0; + ir = x.size() - 1; + while (1) { + if (ir - l < 7) { // Insertion sort when subarray small enough. + for (j = l + 1; j <= ir; j++) { + a = arr[j]; test = true; - for ( i = j - 1; i >= 0; i-- ) { - if ( arr[i] < a ) { + for (i = j - 1; i >= 0; i--) { + if (arr[i] < a) { arr[i + 1] = a; - test = false; + test = false; break; } arr[i + 1] = arr[i]; } - if ( test ) { - i = l - 1; + if (test) { + i = l - 1; arr[i + 1] = a; } } - if ( jstack == 0 ) + if (jstack == 0) return; - ir = istack[jstack]; // Pop stack and begin a new round of partitioning. - l = istack[jstack - 1]; + ir = istack + [jstack]; // Pop stack and begin a new round of partitioning. + l = istack[jstack - 1]; jstack -= 2; } else { - k = ( l + ir ) / 2; // Choose median of left, center and right elements as partitioning - // element a. Also rearrange so that a(l) < a(l+1) < a(ir). - tmp_a = arr[k]; - arr[k] = arr[l + 1]; + k = (l + ir) / + 2; // Choose median of left, center and right elements as partitioning + // element a. Also rearrange so that a(l) < a(l+1) < a(ir). + tmp_a = arr[k]; + arr[k] = arr[l + 1]; arr[l + 1] = tmp_a; - if ( arr[l] > arr[ir] ) { - tmp_a = arr[l]; - arr[l] = arr[ir]; + if (arr[l] > arr[ir]) { + tmp_a = arr[l]; + arr[l] = arr[ir]; arr[ir] = tmp_a; } - if ( arr[l + 1] > arr[ir] ) { - tmp_a = arr[l + 1]; + if (arr[l + 1] > arr[ir]) { + tmp_a = arr[l + 1]; arr[l + 1] = arr[ir]; - arr[ir] = tmp_a; + arr[ir] = tmp_a; } - if ( arr[l] > arr[l + 1] ) { - tmp_a = arr[l]; - arr[l] = arr[l + 1]; + if (arr[l] > arr[l + 1]) { + tmp_a = arr[l]; + arr[l] = arr[l + 1]; arr[l + 1] = tmp_a; } // Scan up to find element > a j = ir; a = arr[l + 1]; // Partitioning element. - for ( i = l + 2; i <= ir; i++ ) { - if ( arr[i] < a ) + for (i = l + 2; i <= ir; i++) { + if (arr[i] < a) continue; - while ( arr[j] > a ) // Scan down to find element < a. + while (arr[j] > a) // Scan down to find element < a. j--; - if ( j < i ) - break; // Pointers crossed. Exit with partitioning complete. - tmp_a = arr[i]; // Exchange elements of both arrays. + if (j < i) + break; // Pointers crossed. Exit with partitioning complete. + tmp_a = arr[i]; // Exchange elements of both arrays. arr[i] = arr[j]; arr[j] = tmp_a; } arr[l + 1] = arr[j]; // Insert partitioning element in both arrays. - arr[j] = a; + arr[j] = a; jstack += 2; // Push pointers to larger subarray on stack, process smaller subarray immediately. - if ( ir - i + 1 >= j - l ) { - istack[jstack] = ir; + if (ir - i + 1 >= j - l) { + istack[jstack] = ir; istack[jstack - 1] = i; - ir = j - 1; + ir = j - 1; } else { - istack[jstack] = j - 1; + istack[jstack] = j - 1; istack[jstack - 1] = l; - l = i; + l = i; } } } } -template -void quicksort( std::vector &x, std::vector &y ) -{ - if ( x.size() <= 1u ) +template +void quicksort(std::vector &x, std::vector &y) { + if (x.size() <= 1u) return; T1 *arr = &x[0]; T2 *brr = &y[0]; @@ -111,124 +107,123 @@ void quicksort( std::vector &x, std::vector &y ) T1 a, tmp_a; T2 b, tmp_b; jstack = 0; - l = 0; - ir = x.size() - 1; - while ( 1 ) { - if ( ir - l < 7 ) { // Insertion sort when subarray small enough. - for ( j = l + 1; j <= ir; j++ ) { - a = arr[j]; - b = brr[j]; + l = 0; + ir = x.size() - 1; + while (1) { + if (ir - l < 7) { // Insertion sort when subarray small enough. + for (j = l + 1; j <= ir; j++) { + a = arr[j]; + b = brr[j]; test = true; - for ( i = j - 1; i >= 0; i-- ) { - if ( arr[i] < a ) { + for (i = j - 1; i >= 0; i--) { + if (arr[i] < a) { arr[i + 1] = a; brr[i + 1] = b; - test = false; + test = false; break; } arr[i + 1] = arr[i]; brr[i + 1] = brr[i]; } - if ( test ) { - i = l - 1; + if (test) { + i = l - 1; arr[i + 1] = a; brr[i + 1] = b; } } - if ( jstack == 0 ) + if (jstack == 0) return; - ir = istack[jstack]; // Pop stack and begin a new round of partitioning. - l = istack[jstack - 1]; + ir = istack + [jstack]; // Pop stack and begin a new round of partitioning. + l = istack[jstack - 1]; jstack -= 2; } else { - k = ( l + ir ) / 2; // Choose median of left, center and right elements as partitioning - // element a. Also rearrange so that a(l) ? a(l+1) ? a(ir). - tmp_a = arr[k]; - arr[k] = arr[l + 1]; + k = (l + ir) / + 2; // Choose median of left, center and right elements as partitioning + // element a. Also rearrange so that a(l) ? a(l+1) ? a(ir). + tmp_a = arr[k]; + arr[k] = arr[l + 1]; arr[l + 1] = tmp_a; - tmp_b = brr[k]; - brr[k] = brr[l + 1]; + tmp_b = brr[k]; + brr[k] = brr[l + 1]; brr[l + 1] = tmp_b; - if ( arr[l] > arr[ir] ) { - tmp_a = arr[l]; - arr[l] = arr[ir]; + if (arr[l] > arr[ir]) { + tmp_a = arr[l]; + arr[l] = arr[ir]; arr[ir] = tmp_a; - tmp_b = brr[l]; - brr[l] = brr[ir]; + tmp_b = brr[l]; + brr[l] = brr[ir]; brr[ir] = tmp_b; } - if ( arr[l + 1] > arr[ir] ) { - tmp_a = arr[l + 1]; + if (arr[l + 1] > arr[ir]) { + tmp_a = arr[l + 1]; arr[l + 1] = arr[ir]; - arr[ir] = tmp_a; - tmp_b = brr[l + 1]; + arr[ir] = tmp_a; + tmp_b = brr[l + 1]; brr[l + 1] = brr[ir]; - brr[ir] = tmp_b; + brr[ir] = tmp_b; } - if ( arr[l] > arr[l + 1] ) { - tmp_a = arr[l]; - arr[l] = arr[l + 1]; + if (arr[l] > arr[l + 1]) { + tmp_a = arr[l]; + arr[l] = arr[l + 1]; arr[l + 1] = tmp_a; - tmp_b = brr[l]; - brr[l] = brr[l + 1]; + tmp_b = brr[l]; + brr[l] = brr[l + 1]; brr[l + 1] = tmp_b; } // Scan up to find element > a j = ir; a = arr[l + 1]; // Partitioning element. b = brr[l + 1]; - for ( i = l + 2; i <= ir; i++ ) { - if ( arr[i] < a ) + for (i = l + 2; i <= ir; i++) { + if (arr[i] < a) continue; - while ( arr[j] > a ) // Scan down to find element < a. + while (arr[j] > a) // Scan down to find element < a. j--; - if ( j < i ) - break; // Pointers crossed. Exit with partitioning complete. - tmp_a = arr[i]; // Exchange elements of both arrays. + if (j < i) + break; // Pointers crossed. Exit with partitioning complete. + tmp_a = arr[i]; // Exchange elements of both arrays. arr[i] = arr[j]; arr[j] = tmp_a; - tmp_b = brr[i]; + tmp_b = brr[i]; brr[i] = brr[j]; brr[j] = tmp_b; } arr[l + 1] = arr[j]; // Insert partitioning element in both arrays. - arr[j] = a; + arr[j] = a; brr[l + 1] = brr[j]; - brr[j] = b; + brr[j] = b; jstack += 2; // Push pointers to larger subarray on stack, process smaller subarray immediately. - if ( ir - i + 1 >= j - l ) { - istack[jstack] = ir; + if (ir - i + 1 >= j - l) { + istack[jstack] = ir; istack[jstack - 1] = i; - ir = j - 1; + ir = j - 1; } else { - istack[jstack] = j - 1; + istack[jstack] = j - 1; istack[jstack - 1] = l; - l = i; + l = i; } } } } -template -void unique( std::vector &x ) -{ - if ( x.size() <= 1 ) +template void unique(std::vector &x) { + if (x.size() <= 1) return; // First perform a quicksort - quicksort( x ); + quicksort(x); // Next remove duplicate entries size_t pos = 1; - for ( size_t i = 1; i < x.size(); i++ ) { - if ( x[i] != x[pos - 1] ) { + for (size_t i = 1; i < x.size(); i++) { + if (x[i] != x[pos - 1]) { x[pos] = x[i]; pos++; } } - if ( pos < x.size() ) - x.resize( pos ); + if (pos < x.size()) + x.resize(pos); } - -} +} // namespace Utilities #endif diff --git a/common/UtilityMacros.h b/common/UtilityMacros.h index 185acbc5..1e1d6fed 100644 --- a/common/UtilityMacros.h +++ b/common/UtilityMacros.h @@ -24,7 +24,6 @@ #include #include - /*! \defgroup Macros Set of utility macro functions * \details These functions are a list of C++ macros that are used * for common operations, including checking for errors. @@ -32,7 +31,6 @@ * @{ */ - /*! \def NULL_STATEMENT * \brief A null statement * \details A statement that does nothing, for insure++ make it something @@ -40,33 +38,31 @@ */ #ifndef NULL_STATEMENT #ifdef __INSURE__ -#define NULL_STATEMENT \ - do { \ - if ( 0 ) \ - int nullstatement = 0 \ - } while ( 0 ) +#define NULL_STATEMENT \ + do { \ + if (0) \ + int nullstatement = 0 \ + } while (0) #else #define NULL_STATEMENT #endif #endif - /*! \def NULL_USE(variable) * \brief A null use of a variable * \details A null use of a variable, use to avoid GNU compiler warnings about unused variables. * \param variable Variable to pretend to use */ #ifndef NULL_USE -#define NULL_USE( variable ) \ - do { \ - if ( 0 ) { \ - auto temp = (char *) &variable; \ - temp++; \ - } \ - } while ( 0 ) +#define NULL_USE(variable) \ + do { \ + if (0) { \ + auto temp = (char *)&variable; \ + temp++; \ + } \ + } while (0) #endif - /*! \def ERROR(MSG) * \brief Throw error * \details Throw an error exception from within any C++ source code. The @@ -74,25 +70,23 @@ * line number of the abort are also printed. * \param MSG Error message to print */ -#define ERROR(MSG) \ - do { \ - ::Utilities::abort( MSG, __FILE__, __LINE__ ); \ - } while ( 0 ) - +#define ERROR(MSG) \ + do { \ + ::Utilities::abort(MSG, __FILE__, __LINE__); \ + } while (0) /*! \def WARNING(MSG) * \brief Print a warning * \details Print a warning without exit. Print file and line number of the warning. * \param MSG Warning message to print */ -#define WARNING(MSG) \ - do { \ - std::stringstream tboxos; \ - tboxos << MSG << std::ends; \ - printf("WARNING: %s\n Warning called in %s on line %i\n", \ - tboxos.str().c_str(),__FILE__,__LINE__); \ - }while(0) - +#define WARNING(MSG) \ + do { \ + std::stringstream tboxos; \ + tboxos << MSG << std::ends; \ + printf("WARNING: %s\n Warning called in %s on line %i\n", \ + tboxos.str().c_str(), __FILE__, __LINE__); \ + } while (0) /*! \def ASSERT(EXP) * \brief Assert error @@ -102,15 +96,14 @@ * The file and line number of the abort are printed along with the stack trace (if availible). * \param EXP Expression to evaluate */ -#define ASSERT(EXP) \ - do { \ - if ( !(EXP) ) { \ - std::stringstream tboxos; \ - tboxos << "Failed assertion: " << #EXP << std::ends; \ - ::Utilities::abort(tboxos.str(), __FILE__, __LINE__); \ - } \ - }while(0) - +#define ASSERT(EXP) \ + do { \ + if (!(EXP)) { \ + std::stringstream tboxos; \ + tboxos << "Failed assertion: " << #EXP << std::ends; \ + ::Utilities::abort(tboxos.str(), __FILE__, __LINE__); \ + } \ + } while (0) /*! \def INSIST(EXP,MSG) * \brief Insist error @@ -121,15 +114,15 @@ * \param EXP Expression to evaluate * \param MSG Debug message to print */ -#define INSIST(EXP,MSG) do { \ - if ( !(EXP) ) { \ - std::stringstream tboxos; \ - tboxos << "Failed insist: " << #EXP << std::endl; \ - tboxos << "Message: " << MSG << std::ends; \ - ::Utilities::abort(tboxos.str(), __FILE__, __LINE__); \ - } \ -}while(0) - +#define INSIST(EXP, MSG) \ + do { \ + if (!(EXP)) { \ + std::stringstream tboxos; \ + tboxos << "Failed insist: " << #EXP << std::endl; \ + tboxos << "Message: " << MSG << std::ends; \ + ::Utilities::abort(tboxos.str(), __FILE__, __LINE__); \ + } \ + } while (0) /** * Macro for use when assertions are to be included @@ -143,12 +136,11 @@ * \param EXP Expression to evaluate */ #ifdef DEBUG_CHECK_ASSERTIONS - #define CHECK_ASSERT(EXP) ASSERT(EXP) +#define CHECK_ASSERT(EXP) ASSERT(EXP) #else - #define CHECK_ASSERT(EXP) +#define CHECK_ASSERT(EXP) #endif - /*! \def DISABLE_WARNINGS * \brief Reenable warnings * \details This will re-enable warnings after a call to DIASABLE_WARNINGS @@ -190,9 +182,6 @@ #endif // clang-format on - - /*! @} */ - #endif diff --git a/common/WideHalo.cpp b/common/WideHalo.cpp index ca82473f..0368740a 100644 --- a/common/WideHalo.cpp +++ b/common/WideHalo.cpp @@ -3,338 +3,543 @@ This class implements support for halo widths larger than 1 */ #include "common/WideHalo.h" -ScaLBLWideHalo_Communicator::ScaLBLWideHalo_Communicator(std::shared_ptr Dm, int width) -{ - //...................................................................................... - Lock=false; // unlock the communicator - //...................................................................................... - // Create a separate copy of the communicator for the device +ScaLBLWideHalo_Communicator::ScaLBLWideHalo_Communicator( + std::shared_ptr Dm, int width) { + //...................................................................................... + Lock = false; // unlock the communicator + //...................................................................................... + // Create a separate copy of the communicator for the device MPI_COMM_SCALBL = Dm->Comm.dup(); - //...................................................................................... - // Copy the domain size and communication information directly from Dm - Nx = Dm->Nx; - Ny = Dm->Ny; - Nz = Dm->Nz; - N = Nx*Ny*Nz; - Nxh = Nx + 2*(width - 1); - Nyh = Ny + 2*(width - 1); - Nzh = Nz + 2*(width - 1); - Nh = Nxh*Nyh*Nzh; - - Map.resize(Nx,Ny,Nz); - - rank=Dm->rank(); - iproc = Dm->iproc(); - jproc = Dm->jproc(); - kproc = Dm->kproc(); - nprocx = Dm->nprocx(); - nprocy = Dm->nprocy(); - nprocz = Dm->nprocz(); - rank_info = RankInfoStruct(rank,nprocx,nprocy,nprocz); - rank = rank_info.rank[1][1][1]; - rank_X = rank_info.rank[2][1][1]; - rank_x = rank_info.rank[0][1][1]; - rank_Y = rank_info.rank[1][2][1]; - rank_y = rank_info.rank[1][0][1]; - rank_Z = rank_info.rank[1][1][2]; - rank_z = rank_info.rank[1][1][0]; - rank_XY = rank_info.rank[2][2][1]; - rank_xy = rank_info.rank[0][0][1]; - rank_Xy = rank_info.rank[2][0][1]; - rank_xY = rank_info.rank[0][2][1]; - rank_XZ = rank_info.rank[2][1][2]; - rank_xz = rank_info.rank[0][1][0]; - rank_Xz = rank_info.rank[2][1][0]; - rank_xZ = rank_info.rank[0][1][2]; - rank_YZ = rank_info.rank[1][2][2]; - rank_yz = rank_info.rank[1][0][0]; - rank_Yz = rank_info.rank[1][2][0]; - rank_yZ = rank_info.rank[1][0][2]; - rank_XYz = rank_info.rank[2][2][0]; - rank_xyz = rank_info.rank[0][0][0]; - rank_Xyz = rank_info.rank[2][0][0]; - rank_xYz = rank_info.rank[0][2][0]; - rank_XYZ = rank_info.rank[2][2][2]; - rank_xyZ = rank_info.rank[0][0][2]; - rank_XyZ = rank_info.rank[2][0][2]; - rank_xYZ = rank_info.rank[0][2][2]; - MPI_COMM_SCALBL.barrier(); - - /* Fill in communications patterns for the lists */ - /* Send lists */ - sendCount_x =getHaloBlock(width,2*width,width,Nyh-width,width,Nzh-width,dvcSendList_x); - sendCount_X =getHaloBlock(Nxh-2*width,Nxh-width,width,Nyh-width,width,Nzh-width,dvcSendList_X); - sendCount_y =getHaloBlock(width,Nxh-width,width,2*width,width,Nzh-width,dvcSendList_y); - sendCount_Y =getHaloBlock(width,Nxh-width,Nyh-2*width,Nyh-width,width,Nzh-width,dvcSendList_Y); - sendCount_z =getHaloBlock(width,Nxh-width,width,Nyh-width,width,2*width,dvcSendList_z); - sendCount_Z =getHaloBlock(width,Nxh-width,width,Nyh-width,Nzh-2*width,Nzh-width,dvcSendList_Z); - // xy - sendCount_xy =getHaloBlock(width,2*width,width,2*width,width,Nzh-width,dvcSendList_xy); - sendCount_xY =getHaloBlock(width,2*width,Nyh-2*width,Nyh-width,width,Nzh-width,dvcSendList_xY); - sendCount_Xy =getHaloBlock(Nxh-2*width,Nxh-width,width,2*width,width,Nzh-width,dvcSendList_Xy); - sendCount_XY =getHaloBlock(Nxh-2*width,Nxh-width,Nyh-2*width,Nyh-width,width,Nzh-width,dvcSendList_XY); - // xz - sendCount_xz =getHaloBlock(width,2*width,width,Nyh-width,width,2*width,dvcSendList_xz); - sendCount_xZ =getHaloBlock(width,2*width,width,Nyh-width,Nzh-2*width,Nzh-width,dvcSendList_xZ); - sendCount_Xz =getHaloBlock(Nxh-2*width,Nxh-width,width,Nyh-width,width,2*width,dvcSendList_Xz); - sendCount_XZ =getHaloBlock(Nxh-2*width,Nxh-width,width,Nyh-width,Nzh-2*width,Nzh-width,dvcSendList_XZ); - // yz - sendCount_yz =getHaloBlock(width,Nxh-width,width,2*width,width,2*width,dvcSendList_yz); - sendCount_yZ =getHaloBlock(width,Nxh-width,width,2*width,Nzh-2*width,Nzh-width,dvcSendList_yZ); - sendCount_Yz =getHaloBlock(width,Nxh-width,Nyh-2*width,Nyh-width,width,2*width,dvcSendList_Yz); - sendCount_YZ =getHaloBlock(width,Nxh-width,Nyh-2*width,Nyh-width,Nzh-2*width,Nzh-width,dvcSendList_YZ); - // xyz - sendCount_xyz =getHaloBlock(width,2*width,width,2*width,width,2*width,dvcSendList_xyz); - sendCount_xyZ =getHaloBlock(width,2*width,width,2*width,Nzh-2*width,Nzh-width,dvcSendList_xyZ); - sendCount_xYz =getHaloBlock(width,2*width,Nyh-2*width,Nyh-width,width,2*width,dvcSendList_xYz); - sendCount_xYZ =getHaloBlock(width,2*width,Nyh-2*width,Nyh-width,Nzh-2*width,Nzh-width,dvcSendList_xYZ); - sendCount_Xyz =getHaloBlock(Nxh-2*width,Nxh-width,width,2*width,width,2*width,dvcSendList_Xyz); - sendCount_XyZ =getHaloBlock(Nxh-2*width,Nxh-width,width,2*width,Nzh-2*width,Nzh-width,dvcSendList_XyZ); - sendCount_XYz =getHaloBlock(Nxh-2*width,Nxh-width,Nyh-2*width,Nyh-width,width,2*width,dvcSendList_XYz); - sendCount_XYZ =getHaloBlock(Nxh-2*width,Nxh-width,Nyh-2*width,Nyh-width,Nzh-2*width,Nzh-width,dvcSendList_XYZ); - - /* Recv lists */ - recvCount_x =getHaloBlock(0,width,width,Nyh-width,width,Nzh-width,dvcRecvList_x); - recvCount_X =getHaloBlock(Nxh-width,Nxh,width,Nyh-width,width,Nzh-width,dvcRecvList_X); - recvCount_y =getHaloBlock(width,Nxh-width,0,width,width,Nzh-width,dvcRecvList_y); - recvCount_Y =getHaloBlock(width,Nxh-width,Nyh-width,Nyh,width,Nzh-width,dvcRecvList_Y); - recvCount_z =getHaloBlock(width,Nxh-width,width,Nyh-width,0,width,dvcRecvList_z); - recvCount_Z =getHaloBlock(width,Nxh-width,width,Nyh-width,Nzh-width,Nzh,dvcRecvList_Z); - //xy - recvCount_xy =getHaloBlock(0,width,0,width,width,Nzh-width,dvcRecvList_xy); - recvCount_xY =getHaloBlock(0,width,Nyh-width,Nyh,width,Nzh-width,dvcRecvList_xY); - recvCount_Xy =getHaloBlock(Nxh-width,Nxh,0,width,width,Nzh-width,dvcRecvList_Xy); - recvCount_XY =getHaloBlock(Nxh-width,Nxh,Nyh-width,Nyh,width,Nzh-width,dvcRecvList_XY); - //xz - recvCount_xz =getHaloBlock(0,width,width,Nyh-width,0,width,dvcRecvList_xz); - recvCount_xZ =getHaloBlock(0,width,width,Nyh-width,Nzh-width,Nzh,dvcRecvList_xZ); - recvCount_Xz =getHaloBlock(Nxh-width,Nxh,width,Nyh-width,0,width,dvcRecvList_Xz); - recvCount_XZ =getHaloBlock(Nxh-width,Nxh,width,Nyh-width,Nzh-width,Nzh,dvcRecvList_XZ); - //yz - recvCount_yz =getHaloBlock(width,Nxh-width,0,width,0,width,dvcRecvList_yz); - recvCount_yZ =getHaloBlock(width,Nxh-width,0,width,Nzh-width,Nzh,dvcRecvList_yZ); - recvCount_Yz =getHaloBlock(width,Nxh-width,Nyh-width,Nyh,0,width,dvcRecvList_Yz); - recvCount_YZ =getHaloBlock(width,Nxh-width,Nyh-width,Nyh,Nzh-width,Nzh,dvcRecvList_YZ); - //xyz - recvCount_xyz =getHaloBlock(0,width,0,width,0,width,dvcRecvList_xyz); - recvCount_xyZ =getHaloBlock(0,width,0,width,Nzh-width,Nzh,dvcRecvList_xyZ); - recvCount_xYz =getHaloBlock(0,width,Nyh-width,Nyh,0,width,dvcRecvList_xYz); - recvCount_xYZ =getHaloBlock(0,width,Nyh-width,Nyh,Nzh-width,Nzh,dvcRecvList_xYZ); - recvCount_Xyz =getHaloBlock(Nxh-width,Nxh,0,width,0,width,dvcRecvList_Xyz); - recvCount_XyZ =getHaloBlock(Nxh-width,Nxh,0,width,Nzh-width,Nzh,dvcRecvList_XyZ); - recvCount_XYz =getHaloBlock(Nxh-width,Nxh,Nyh-width,Nyh,0,width,dvcRecvList_XYz); - recvCount_XYZ =getHaloBlock(Nxh-width,Nxh,Nyh-width,Nyh,Nzh-width,Nzh,dvcRecvList_XYZ); + //...................................................................................... + // Copy the domain size and communication information directly from Dm + Nx = Dm->Nx; + Ny = Dm->Ny; + Nz = Dm->Nz; + N = Nx * Ny * Nz; + Nxh = Nx + 2 * (width - 1); + Nyh = Ny + 2 * (width - 1); + Nzh = Nz + 2 * (width - 1); + Nh = Nxh * Nyh * Nzh; - //...................................................................................... - ScaLBL_AllocateZeroCopy((void **) &sendbuf_x, sendCount_x*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_X, sendCount_X*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_y, sendCount_y*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_Y, sendCount_Y*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_z, sendCount_z*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_Z, sendCount_Z*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_xy, sendCount_xy*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_xY, sendCount_xY*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_Xy, sendCount_Xy*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_XY, sendCount_XY*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_xz, sendCount_xz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_xZ, sendCount_xZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_Xz, sendCount_Xz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_XZ, sendCount_XZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_yz, sendCount_yz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_yZ, sendCount_yZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_Yz, sendCount_Yz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_YZ, sendCount_YZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_xyz, sendCount_xyz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_xYz, sendCount_xYz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_Xyz, sendCount_Xyz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_XYz, sendCount_XYz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_xyZ, sendCount_xyZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_xYZ, sendCount_xYZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_XyZ, sendCount_XyZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &sendbuf_XYZ, sendCount_XYZ*sizeof(double)); // Allocate device memory - //...................................................................................... - ScaLBL_AllocateZeroCopy((void **) &recvbuf_x, recvCount_x*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_X, recvCount_X*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_y, recvCount_y*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_Y, recvCount_Y*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_z, recvCount_z*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_Z, recvCount_Z*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_xy, recvCount_xy*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_xY, recvCount_xY*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_Xy, recvCount_Xy*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_XY, recvCount_XY*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_xz, recvCount_xz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_xZ, recvCount_xZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_Xz, recvCount_Xz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_XZ, recvCount_XZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_yz, recvCount_yz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_yZ, recvCount_yZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_Yz, recvCount_Yz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_YZ, recvCount_YZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_xyz, recvCount_xyz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_xYz, recvCount_xYz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_Xyz, recvCount_Xyz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_XYz, recvCount_XYz*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_xyZ, recvCount_xyZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_xYZ, recvCount_xYZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_XyZ, recvCount_XyZ*sizeof(double)); // Allocate device memory - ScaLBL_AllocateZeroCopy((void **) &recvbuf_XYZ, recvCount_XYZ*sizeof(double)); // Allocate device memory - - /* Set up a map to the halo width=1 data structure */ - for (k=width; krank(); + iproc = Dm->iproc(); + jproc = Dm->jproc(); + kproc = Dm->kproc(); + nprocx = Dm->nprocx(); + nprocy = Dm->nprocy(); + nprocz = Dm->nprocz(); + rank_info = RankInfoStruct(rank, nprocx, nprocy, nprocz); + rank = rank_info.rank[1][1][1]; + rank_X = rank_info.rank[2][1][1]; + rank_x = rank_info.rank[0][1][1]; + rank_Y = rank_info.rank[1][2][1]; + rank_y = rank_info.rank[1][0][1]; + rank_Z = rank_info.rank[1][1][2]; + rank_z = rank_info.rank[1][1][0]; + rank_XY = rank_info.rank[2][2][1]; + rank_xy = rank_info.rank[0][0][1]; + rank_Xy = rank_info.rank[2][0][1]; + rank_xY = rank_info.rank[0][2][1]; + rank_XZ = rank_info.rank[2][1][2]; + rank_xz = rank_info.rank[0][1][0]; + rank_Xz = rank_info.rank[2][1][0]; + rank_xZ = rank_info.rank[0][1][2]; + rank_YZ = rank_info.rank[1][2][2]; + rank_yz = rank_info.rank[1][0][0]; + rank_Yz = rank_info.rank[1][2][0]; + rank_yZ = rank_info.rank[1][0][2]; + rank_XYz = rank_info.rank[2][2][0]; + rank_xyz = rank_info.rank[0][0][0]; + rank_Xyz = rank_info.rank[2][0][0]; + rank_xYz = rank_info.rank[0][2][0]; + rank_XYZ = rank_info.rank[2][2][2]; + rank_xyZ = rank_info.rank[0][0][2]; + rank_XyZ = rank_info.rank[2][0][2]; + rank_xYZ = rank_info.rank[0][2][2]; + MPI_COMM_SCALBL.barrier(); + + /* Fill in communications patterns for the lists */ + /* Send lists */ + sendCount_x = getHaloBlock(width, 2 * width, width, Nyh - width, width, + Nzh - width, dvcSendList_x); + sendCount_X = getHaloBlock(Nxh - 2 * width, Nxh - width, width, Nyh - width, + width, Nzh - width, dvcSendList_X); + sendCount_y = getHaloBlock(width, Nxh - width, width, 2 * width, width, + Nzh - width, dvcSendList_y); + sendCount_Y = getHaloBlock(width, Nxh - width, Nyh - 2 * width, Nyh - width, + width, Nzh - width, dvcSendList_Y); + sendCount_z = getHaloBlock(width, Nxh - width, width, Nyh - width, width, + 2 * width, dvcSendList_z); + sendCount_Z = getHaloBlock(width, Nxh - width, width, Nyh - width, + Nzh - 2 * width, Nzh - width, dvcSendList_Z); + // xy + sendCount_xy = getHaloBlock(width, 2 * width, width, 2 * width, width, + Nzh - width, dvcSendList_xy); + sendCount_xY = getHaloBlock(width, 2 * width, Nyh - 2 * width, Nyh - width, + width, Nzh - width, dvcSendList_xY); + sendCount_Xy = getHaloBlock(Nxh - 2 * width, Nxh - width, width, 2 * width, + width, Nzh - width, dvcSendList_Xy); + sendCount_XY = + getHaloBlock(Nxh - 2 * width, Nxh - width, Nyh - 2 * width, Nyh - width, + width, Nzh - width, dvcSendList_XY); + // xz + sendCount_xz = getHaloBlock(width, 2 * width, width, Nyh - width, width, + 2 * width, dvcSendList_xz); + sendCount_xZ = getHaloBlock(width, 2 * width, width, Nyh - width, + Nzh - 2 * width, Nzh - width, dvcSendList_xZ); + sendCount_Xz = getHaloBlock(Nxh - 2 * width, Nxh - width, width, + Nyh - width, width, 2 * width, dvcSendList_Xz); + sendCount_XZ = + getHaloBlock(Nxh - 2 * width, Nxh - width, width, Nyh - width, + Nzh - 2 * width, Nzh - width, dvcSendList_XZ); + // yz + sendCount_yz = getHaloBlock(width, Nxh - width, width, 2 * width, width, + 2 * width, dvcSendList_yz); + sendCount_yZ = getHaloBlock(width, Nxh - width, width, 2 * width, + Nzh - 2 * width, Nzh - width, dvcSendList_yZ); + sendCount_Yz = getHaloBlock(width, Nxh - width, Nyh - 2 * width, + Nyh - width, width, 2 * width, dvcSendList_Yz); + sendCount_YZ = + getHaloBlock(width, Nxh - width, Nyh - 2 * width, Nyh - width, + Nzh - 2 * width, Nzh - width, dvcSendList_YZ); + // xyz + sendCount_xyz = getHaloBlock(width, 2 * width, width, 2 * width, width, + 2 * width, dvcSendList_xyz); + sendCount_xyZ = getHaloBlock(width, 2 * width, width, 2 * width, + Nzh - 2 * width, Nzh - width, dvcSendList_xyZ); + sendCount_xYz = getHaloBlock(width, 2 * width, Nyh - 2 * width, Nyh - width, + width, 2 * width, dvcSendList_xYz); + sendCount_xYZ = getHaloBlock(width, 2 * width, Nyh - 2 * width, Nyh - width, + Nzh - 2 * width, Nzh - width, dvcSendList_xYZ); + sendCount_Xyz = getHaloBlock(Nxh - 2 * width, Nxh - width, width, 2 * width, + width, 2 * width, dvcSendList_Xyz); + sendCount_XyZ = getHaloBlock(Nxh - 2 * width, Nxh - width, width, 2 * width, + Nzh - 2 * width, Nzh - width, dvcSendList_XyZ); + sendCount_XYz = + getHaloBlock(Nxh - 2 * width, Nxh - width, Nyh - 2 * width, Nyh - width, + width, 2 * width, dvcSendList_XYz); + sendCount_XYZ = + getHaloBlock(Nxh - 2 * width, Nxh - width, Nyh - 2 * width, Nyh - width, + Nzh - 2 * width, Nzh - width, dvcSendList_XYZ); + + /* Recv lists */ + recvCount_x = getHaloBlock(0, width, width, Nyh - width, width, Nzh - width, + dvcRecvList_x); + recvCount_X = getHaloBlock(Nxh - width, Nxh, width, Nyh - width, width, + Nzh - width, dvcRecvList_X); + recvCount_y = getHaloBlock(width, Nxh - width, 0, width, width, Nzh - width, + dvcRecvList_y); + recvCount_Y = getHaloBlock(width, Nxh - width, Nyh - width, Nyh, width, + Nzh - width, dvcRecvList_Y); + recvCount_z = getHaloBlock(width, Nxh - width, width, Nyh - width, 0, width, + dvcRecvList_z); + recvCount_Z = getHaloBlock(width, Nxh - width, width, Nyh - width, + Nzh - width, Nzh, dvcRecvList_Z); + //xy + recvCount_xy = + getHaloBlock(0, width, 0, width, width, Nzh - width, dvcRecvList_xy); + recvCount_xY = getHaloBlock(0, width, Nyh - width, Nyh, width, Nzh - width, + dvcRecvList_xY); + recvCount_Xy = getHaloBlock(Nxh - width, Nxh, 0, width, width, Nzh - width, + dvcRecvList_Xy); + recvCount_XY = getHaloBlock(Nxh - width, Nxh, Nyh - width, Nyh, width, + Nzh - width, dvcRecvList_XY); + //xz + recvCount_xz = + getHaloBlock(0, width, width, Nyh - width, 0, width, dvcRecvList_xz); + recvCount_xZ = getHaloBlock(0, width, width, Nyh - width, Nzh - width, Nzh, + dvcRecvList_xZ); + recvCount_Xz = getHaloBlock(Nxh - width, Nxh, width, Nyh - width, 0, width, + dvcRecvList_Xz); + recvCount_XZ = getHaloBlock(Nxh - width, Nxh, width, Nyh - width, + Nzh - width, Nzh, dvcRecvList_XZ); + //yz + recvCount_yz = + getHaloBlock(width, Nxh - width, 0, width, 0, width, dvcRecvList_yz); + recvCount_yZ = getHaloBlock(width, Nxh - width, 0, width, Nzh - width, Nzh, + dvcRecvList_yZ); + recvCount_Yz = getHaloBlock(width, Nxh - width, Nyh - width, Nyh, 0, width, + dvcRecvList_Yz); + recvCount_YZ = getHaloBlock(width, Nxh - width, Nyh - width, Nyh, + Nzh - width, Nzh, dvcRecvList_YZ); + //xyz + recvCount_xyz = getHaloBlock(0, width, 0, width, 0, width, dvcRecvList_xyz); + recvCount_xyZ = + getHaloBlock(0, width, 0, width, Nzh - width, Nzh, dvcRecvList_xyZ); + recvCount_xYz = + getHaloBlock(0, width, Nyh - width, Nyh, 0, width, dvcRecvList_xYz); + recvCount_xYZ = getHaloBlock(0, width, Nyh - width, Nyh, Nzh - width, Nzh, + dvcRecvList_xYZ); + recvCount_Xyz = + getHaloBlock(Nxh - width, Nxh, 0, width, 0, width, dvcRecvList_Xyz); + recvCount_XyZ = getHaloBlock(Nxh - width, Nxh, 0, width, Nzh - width, Nzh, + dvcRecvList_XyZ); + recvCount_XYz = getHaloBlock(Nxh - width, Nxh, Nyh - width, Nyh, 0, width, + dvcRecvList_XYz); + recvCount_XYZ = getHaloBlock(Nxh - width, Nxh, Nyh - width, Nyh, + Nzh - width, Nzh, dvcRecvList_XYZ); + + //...................................................................................... + ScaLBL_AllocateZeroCopy((void **)&sendbuf_x, + sendCount_x * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_X, + sendCount_X * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_y, + sendCount_y * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_Y, + sendCount_Y * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_z, + sendCount_z * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_Z, + sendCount_Z * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_xy, + sendCount_xy * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_xY, + sendCount_xY * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_Xy, + sendCount_Xy * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_XY, + sendCount_XY * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_xz, + sendCount_xz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_xZ, + sendCount_xZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_Xz, + sendCount_Xz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_XZ, + sendCount_XZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_yz, + sendCount_yz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_yZ, + sendCount_yZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_Yz, + sendCount_Yz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_YZ, + sendCount_YZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_xyz, + sendCount_xyz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_xYz, + sendCount_xYz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_Xyz, + sendCount_Xyz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_XYz, + sendCount_XYz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_xyZ, + sendCount_xyZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_xYZ, + sendCount_xYZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_XyZ, + sendCount_XyZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&sendbuf_XYZ, + sendCount_XYZ * + sizeof(double)); // Allocate device memory + //...................................................................................... + ScaLBL_AllocateZeroCopy((void **)&recvbuf_x, + recvCount_x * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_X, + recvCount_X * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_y, + recvCount_y * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_Y, + recvCount_Y * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_z, + recvCount_z * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_Z, + recvCount_Z * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_xy, + recvCount_xy * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_xY, + recvCount_xY * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_Xy, + recvCount_Xy * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_XY, + recvCount_XY * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_xz, + recvCount_xz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_xZ, + recvCount_xZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_Xz, + recvCount_Xz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_XZ, + recvCount_XZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_yz, + recvCount_yz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_yZ, + recvCount_yZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_Yz, + recvCount_Yz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_YZ, + recvCount_YZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_xyz, + recvCount_xyz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_xYz, + recvCount_xYz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_Xyz, + recvCount_Xyz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_XYz, + recvCount_XYz * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_xyZ, + recvCount_xyZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_xYZ, + recvCount_xYZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_XyZ, + recvCount_XyZ * + sizeof(double)); // Allocate device memory + ScaLBL_AllocateZeroCopy((void **)&recvbuf_XYZ, + recvCount_XYZ * + sizeof(double)); // Allocate device memory + + /* Set up a map to the halo width=1 data structure */ + for (k = width; k < Nzh - width; k++) { + for (j = width; j < Nyh - width; j++) { + for (i = width; i < Nxh - width; i++) { + int idx = k * Nxh * Nyh + j * Nxh + i; + Map(i - width + 1, j - width + 1, k - width + 1) = idx; + } + } + } } -void ScaLBLWideHalo_Communicator::Send(double *data){ - //................................................................................... - if (Lock==true){ - ERROR("ScaLBL Error (SendHalo): ScaLBLWideHalo_Communicator is locked -- did you forget to match Send/Recv calls?"); - } - else{ - Lock=true; - } - ScaLBL_DeviceBarrier(); - //................................................................................... - sendtag = recvtag = 1; - //................................................................................... - ScaLBL_Scalar_Pack(dvcSendList_x, sendCount_x,sendbuf_x, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_y, sendCount_y,sendbuf_y, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_z, sendCount_z,sendbuf_z, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_X, sendCount_X,sendbuf_X, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_Y, sendCount_Y,sendbuf_Y, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_Z, sendCount_Z,sendbuf_Z, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_xy, sendCount_xy,sendbuf_xy, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_xY, sendCount_xY,sendbuf_xY, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_Xy, sendCount_Xy,sendbuf_Xy, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_XY, sendCount_XY,sendbuf_XY, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_xz, sendCount_xz,sendbuf_xz, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_xZ, sendCount_xZ,sendbuf_xZ, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_Xz, sendCount_Xz,sendbuf_Xz, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_XZ, sendCount_XZ,sendbuf_XZ, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_yz, sendCount_yz,sendbuf_yz, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_yZ, sendCount_yZ,sendbuf_yZ, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_Yz, sendCount_Yz,sendbuf_Yz, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_YZ, sendCount_YZ,sendbuf_YZ, data, Nh); - /* corners */ - ScaLBL_Scalar_Pack(dvcSendList_xyz, sendCount_xyz,sendbuf_xyz, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_xyZ, sendCount_xyZ,sendbuf_xyZ, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_xYz, sendCount_xYz,sendbuf_xYz, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_xYZ, sendCount_xYZ,sendbuf_xYZ, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_Xyz, sendCount_Xyz,sendbuf_Xyz, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_XyZ, sendCount_XyZ,sendbuf_XyZ, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_XYz, sendCount_XYz,sendbuf_XYz, data, Nh); - ScaLBL_Scalar_Pack(dvcSendList_XYZ, sendCount_XYZ,sendbuf_XYZ, data, Nh); - //................................................................................... - // Send / Recv all the phase indcator field values - //................................................................................... - req1[0] = MPI_COMM_SCALBL.Isend(sendbuf_x,sendCount_x,rank_x,sendtag+0); - req2[0] = MPI_COMM_SCALBL.Irecv(recvbuf_X,recvCount_X,rank_X,recvtag+0); - req1[1] = MPI_COMM_SCALBL.Isend(sendbuf_X,sendCount_X,rank_X,sendtag+1); - req2[1] = MPI_COMM_SCALBL.Irecv(recvbuf_x,recvCount_x,rank_x,recvtag+1); - req1[2] = MPI_COMM_SCALBL.Isend(sendbuf_y,sendCount_y,rank_y,sendtag+2); - req2[2] = MPI_COMM_SCALBL.Irecv(recvbuf_Y,recvCount_Y,rank_Y,recvtag+2); - req1[3] = MPI_COMM_SCALBL.Isend(sendbuf_Y,sendCount_Y,rank_Y,sendtag+3); - req2[3] = MPI_COMM_SCALBL.Irecv(recvbuf_y,recvCount_y,rank_y,recvtag+3); - req1[4] = MPI_COMM_SCALBL.Isend(sendbuf_z,sendCount_z,rank_z,sendtag+4); - req2[4] = MPI_COMM_SCALBL.Irecv(recvbuf_Z,recvCount_Z,rank_Z,recvtag+4); - req1[5] = MPI_COMM_SCALBL.Isend(sendbuf_Z,sendCount_Z,rank_Z,sendtag+5); - req2[5] = MPI_COMM_SCALBL.Irecv(recvbuf_z,recvCount_z,rank_z,recvtag+5); - req1[6] = MPI_COMM_SCALBL.Isend(sendbuf_xy,sendCount_xy,rank_xy,sendtag+6); - req2[6] = MPI_COMM_SCALBL.Irecv(recvbuf_XY,recvCount_XY,rank_XY,recvtag+6); - req1[7] = MPI_COMM_SCALBL.Isend(sendbuf_XY,sendCount_XY,rank_XY,sendtag+7); - req2[7] = MPI_COMM_SCALBL.Irecv(recvbuf_xy,recvCount_xy,rank_xy,recvtag+7); - req1[8] = MPI_COMM_SCALBL.Isend(sendbuf_Xy,sendCount_Xy,rank_Xy,sendtag+8); - req2[8] = MPI_COMM_SCALBL.Irecv(recvbuf_xY,recvCount_xY,rank_xY,recvtag+8); - req1[9] = MPI_COMM_SCALBL.Isend(sendbuf_xY,sendCount_xY,rank_xY,sendtag+9); - req2[9] = MPI_COMM_SCALBL.Irecv(recvbuf_Xy,recvCount_Xy,rank_Xy,recvtag+9); - req1[10] = MPI_COMM_SCALBL.Isend(sendbuf_xz,sendCount_xz,rank_xz,sendtag+10); - req2[10] = MPI_COMM_SCALBL.Irecv(recvbuf_XZ,recvCount_XZ,rank_XZ,recvtag+10); - req1[11] = MPI_COMM_SCALBL.Isend(sendbuf_XZ,sendCount_XZ,rank_XZ,sendtag+11); - req2[11] = MPI_COMM_SCALBL.Irecv(recvbuf_xz,recvCount_xz,rank_xz,recvtag+11); - req1[12] = MPI_COMM_SCALBL.Isend(sendbuf_Xz,sendCount_Xz,rank_Xz,sendtag+12); - req2[12] = MPI_COMM_SCALBL.Irecv(recvbuf_xZ,recvCount_xZ,rank_xZ,recvtag+12); - req1[13] = MPI_COMM_SCALBL.Isend(sendbuf_xZ,sendCount_xZ,rank_xZ,sendtag+13); - req2[13] = MPI_COMM_SCALBL.Irecv(recvbuf_Xz,recvCount_Xz,rank_Xz,recvtag+13); - req1[14] = MPI_COMM_SCALBL.Isend(sendbuf_yz,sendCount_yz,rank_yz,sendtag+14); - req2[14] = MPI_COMM_SCALBL.Irecv(recvbuf_YZ,recvCount_YZ,rank_YZ,recvtag+14); - req1[15] = MPI_COMM_SCALBL.Isend(sendbuf_YZ,sendCount_YZ,rank_YZ,sendtag+15); - req2[15] = MPI_COMM_SCALBL.Irecv(recvbuf_yz,recvCount_yz,rank_yz,recvtag+15); - req1[16] = MPI_COMM_SCALBL.Isend(sendbuf_Yz,sendCount_Yz,rank_Yz,sendtag+16); - req2[16] = MPI_COMM_SCALBL.Irecv(recvbuf_yZ,recvCount_yZ,rank_yZ,recvtag+16); - req1[17] = MPI_COMM_SCALBL.Isend(sendbuf_yZ,sendCount_yZ,rank_yZ,sendtag+17); - req2[17] = MPI_COMM_SCALBL.Irecv(recvbuf_Yz,recvCount_Yz,rank_Yz,recvtag+17); - /* Corners */ - req1[18] = MPI_COMM_SCALBL.Isend(sendbuf_xyz,sendCount_xyz,rank_xyz,sendtag+18); - req2[18] = MPI_COMM_SCALBL.Irecv(recvbuf_XYZ,recvCount_XYZ,rank_XYZ,recvtag+18); - req1[19] = MPI_COMM_SCALBL.Isend(sendbuf_XYz,sendCount_XYz,rank_XYz,sendtag+19); - req2[19] = MPI_COMM_SCALBL.Irecv(recvbuf_xyZ,recvCount_xyZ,rank_xyZ,recvtag+19); - req1[20] = MPI_COMM_SCALBL.Isend(sendbuf_Xyz,sendCount_Xyz,rank_Xyz,sendtag+20); - req2[20] = MPI_COMM_SCALBL.Irecv(recvbuf_xYZ,recvCount_xYZ,rank_xYZ,recvtag+20); - req1[21] = MPI_COMM_SCALBL.Isend(sendbuf_xYz,sendCount_xYz,rank_xYz,sendtag+21); - req2[21] = MPI_COMM_SCALBL.Irecv(recvbuf_XyZ,recvCount_XyZ,rank_XyZ,recvtag+21); - req1[22] = MPI_COMM_SCALBL.Isend(sendbuf_xyZ,sendCount_xyZ,rank_xyZ,sendtag+22); - req2[22] = MPI_COMM_SCALBL.Irecv(recvbuf_XYz,recvCount_XYz,rank_XYz,recvtag+22); - req1[23] = MPI_COMM_SCALBL.Isend(sendbuf_XYZ,sendCount_XYZ,rank_XYZ,sendtag+23); - req2[23] = MPI_COMM_SCALBL.Irecv(recvbuf_xyz,recvCount_xyz,rank_xyz,recvtag+23); - req1[24] = MPI_COMM_SCALBL.Isend(sendbuf_XyZ,sendCount_XyZ,rank_XyZ,sendtag+24); - req2[24] = MPI_COMM_SCALBL.Irecv(recvbuf_xYz,recvCount_xYz,rank_xYz,recvtag+24); - req1[25] = MPI_COMM_SCALBL.Isend(sendbuf_xYZ,sendCount_xYZ,rank_xYZ,sendtag+25); - req2[25] = MPI_COMM_SCALBL.Irecv(recvbuf_Xyz,recvCount_Xyz,rank_Xyz,recvtag+25); - //................................................................................... - +void ScaLBLWideHalo_Communicator::Send(double *data) { + //................................................................................... + if (Lock == true) { + ERROR("ScaLBL Error (SendHalo): ScaLBLWideHalo_Communicator is locked " + "-- did you forget to match Send/Recv calls?"); + } else { + Lock = true; + } + ScaLBL_DeviceBarrier(); + //................................................................................... + sendtag = recvtag = 1; + //................................................................................... + ScaLBL_Scalar_Pack(dvcSendList_x, sendCount_x, sendbuf_x, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_y, sendCount_y, sendbuf_y, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_z, sendCount_z, sendbuf_z, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_X, sendCount_X, sendbuf_X, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_Y, sendCount_Y, sendbuf_Y, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_Z, sendCount_Z, sendbuf_Z, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_xy, sendCount_xy, sendbuf_xy, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_xY, sendCount_xY, sendbuf_xY, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_Xy, sendCount_Xy, sendbuf_Xy, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_XY, sendCount_XY, sendbuf_XY, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_xz, sendCount_xz, sendbuf_xz, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_xZ, sendCount_xZ, sendbuf_xZ, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_Xz, sendCount_Xz, sendbuf_Xz, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_XZ, sendCount_XZ, sendbuf_XZ, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_yz, sendCount_yz, sendbuf_yz, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_yZ, sendCount_yZ, sendbuf_yZ, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_Yz, sendCount_Yz, sendbuf_Yz, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_YZ, sendCount_YZ, sendbuf_YZ, data, Nh); + /* corners */ + ScaLBL_Scalar_Pack(dvcSendList_xyz, sendCount_xyz, sendbuf_xyz, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_xyZ, sendCount_xyZ, sendbuf_xyZ, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_xYz, sendCount_xYz, sendbuf_xYz, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_xYZ, sendCount_xYZ, sendbuf_xYZ, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_Xyz, sendCount_Xyz, sendbuf_Xyz, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_XyZ, sendCount_XyZ, sendbuf_XyZ, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_XYz, sendCount_XYz, sendbuf_XYz, data, Nh); + ScaLBL_Scalar_Pack(dvcSendList_XYZ, sendCount_XYZ, sendbuf_XYZ, data, Nh); + //................................................................................... + // Send / Recv all the phase indcator field values + //................................................................................... + req1[0] = + MPI_COMM_SCALBL.Isend(sendbuf_x, sendCount_x, rank_x, sendtag + 0); + req2[0] = + MPI_COMM_SCALBL.Irecv(recvbuf_X, recvCount_X, rank_X, recvtag + 0); + req1[1] = + MPI_COMM_SCALBL.Isend(sendbuf_X, sendCount_X, rank_X, sendtag + 1); + req2[1] = + MPI_COMM_SCALBL.Irecv(recvbuf_x, recvCount_x, rank_x, recvtag + 1); + req1[2] = + MPI_COMM_SCALBL.Isend(sendbuf_y, sendCount_y, rank_y, sendtag + 2); + req2[2] = + MPI_COMM_SCALBL.Irecv(recvbuf_Y, recvCount_Y, rank_Y, recvtag + 2); + req1[3] = + MPI_COMM_SCALBL.Isend(sendbuf_Y, sendCount_Y, rank_Y, sendtag + 3); + req2[3] = + MPI_COMM_SCALBL.Irecv(recvbuf_y, recvCount_y, rank_y, recvtag + 3); + req1[4] = + MPI_COMM_SCALBL.Isend(sendbuf_z, sendCount_z, rank_z, sendtag + 4); + req2[4] = + MPI_COMM_SCALBL.Irecv(recvbuf_Z, recvCount_Z, rank_Z, recvtag + 4); + req1[5] = + MPI_COMM_SCALBL.Isend(sendbuf_Z, sendCount_Z, rank_Z, sendtag + 5); + req2[5] = + MPI_COMM_SCALBL.Irecv(recvbuf_z, recvCount_z, rank_z, recvtag + 5); + req1[6] = + MPI_COMM_SCALBL.Isend(sendbuf_xy, sendCount_xy, rank_xy, sendtag + 6); + req2[6] = + MPI_COMM_SCALBL.Irecv(recvbuf_XY, recvCount_XY, rank_XY, recvtag + 6); + req1[7] = + MPI_COMM_SCALBL.Isend(sendbuf_XY, sendCount_XY, rank_XY, sendtag + 7); + req2[7] = + MPI_COMM_SCALBL.Irecv(recvbuf_xy, recvCount_xy, rank_xy, recvtag + 7); + req1[8] = + MPI_COMM_SCALBL.Isend(sendbuf_Xy, sendCount_Xy, rank_Xy, sendtag + 8); + req2[8] = + MPI_COMM_SCALBL.Irecv(recvbuf_xY, recvCount_xY, rank_xY, recvtag + 8); + req1[9] = + MPI_COMM_SCALBL.Isend(sendbuf_xY, sendCount_xY, rank_xY, sendtag + 9); + req2[9] = + MPI_COMM_SCALBL.Irecv(recvbuf_Xy, recvCount_Xy, rank_Xy, recvtag + 9); + req1[10] = + MPI_COMM_SCALBL.Isend(sendbuf_xz, sendCount_xz, rank_xz, sendtag + 10); + req2[10] = + MPI_COMM_SCALBL.Irecv(recvbuf_XZ, recvCount_XZ, rank_XZ, recvtag + 10); + req1[11] = + MPI_COMM_SCALBL.Isend(sendbuf_XZ, sendCount_XZ, rank_XZ, sendtag + 11); + req2[11] = + MPI_COMM_SCALBL.Irecv(recvbuf_xz, recvCount_xz, rank_xz, recvtag + 11); + req1[12] = + MPI_COMM_SCALBL.Isend(sendbuf_Xz, sendCount_Xz, rank_Xz, sendtag + 12); + req2[12] = + MPI_COMM_SCALBL.Irecv(recvbuf_xZ, recvCount_xZ, rank_xZ, recvtag + 12); + req1[13] = + MPI_COMM_SCALBL.Isend(sendbuf_xZ, sendCount_xZ, rank_xZ, sendtag + 13); + req2[13] = + MPI_COMM_SCALBL.Irecv(recvbuf_Xz, recvCount_Xz, rank_Xz, recvtag + 13); + req1[14] = + MPI_COMM_SCALBL.Isend(sendbuf_yz, sendCount_yz, rank_yz, sendtag + 14); + req2[14] = + MPI_COMM_SCALBL.Irecv(recvbuf_YZ, recvCount_YZ, rank_YZ, recvtag + 14); + req1[15] = + MPI_COMM_SCALBL.Isend(sendbuf_YZ, sendCount_YZ, rank_YZ, sendtag + 15); + req2[15] = + MPI_COMM_SCALBL.Irecv(recvbuf_yz, recvCount_yz, rank_yz, recvtag + 15); + req1[16] = + MPI_COMM_SCALBL.Isend(sendbuf_Yz, sendCount_Yz, rank_Yz, sendtag + 16); + req2[16] = + MPI_COMM_SCALBL.Irecv(recvbuf_yZ, recvCount_yZ, rank_yZ, recvtag + 16); + req1[17] = + MPI_COMM_SCALBL.Isend(sendbuf_yZ, sendCount_yZ, rank_yZ, sendtag + 17); + req2[17] = + MPI_COMM_SCALBL.Irecv(recvbuf_Yz, recvCount_Yz, rank_Yz, recvtag + 17); + /* Corners */ + req1[18] = MPI_COMM_SCALBL.Isend(sendbuf_xyz, sendCount_xyz, rank_xyz, + sendtag + 18); + req2[18] = MPI_COMM_SCALBL.Irecv(recvbuf_XYZ, recvCount_XYZ, rank_XYZ, + recvtag + 18); + req1[19] = MPI_COMM_SCALBL.Isend(sendbuf_XYz, sendCount_XYz, rank_XYz, + sendtag + 19); + req2[19] = MPI_COMM_SCALBL.Irecv(recvbuf_xyZ, recvCount_xyZ, rank_xyZ, + recvtag + 19); + req1[20] = MPI_COMM_SCALBL.Isend(sendbuf_Xyz, sendCount_Xyz, rank_Xyz, + sendtag + 20); + req2[20] = MPI_COMM_SCALBL.Irecv(recvbuf_xYZ, recvCount_xYZ, rank_xYZ, + recvtag + 20); + req1[21] = MPI_COMM_SCALBL.Isend(sendbuf_xYz, sendCount_xYz, rank_xYz, + sendtag + 21); + req2[21] = MPI_COMM_SCALBL.Irecv(recvbuf_XyZ, recvCount_XyZ, rank_XyZ, + recvtag + 21); + req1[22] = MPI_COMM_SCALBL.Isend(sendbuf_xyZ, sendCount_xyZ, rank_xyZ, + sendtag + 22); + req2[22] = MPI_COMM_SCALBL.Irecv(recvbuf_XYz, recvCount_XYz, rank_XYz, + recvtag + 22); + req1[23] = MPI_COMM_SCALBL.Isend(sendbuf_XYZ, sendCount_XYZ, rank_XYZ, + sendtag + 23); + req2[23] = MPI_COMM_SCALBL.Irecv(recvbuf_xyz, recvCount_xyz, rank_xyz, + recvtag + 23); + req1[24] = MPI_COMM_SCALBL.Isend(sendbuf_XyZ, sendCount_XyZ, rank_XyZ, + sendtag + 24); + req2[24] = MPI_COMM_SCALBL.Irecv(recvbuf_xYz, recvCount_xYz, rank_xYz, + recvtag + 24); + req1[25] = MPI_COMM_SCALBL.Isend(sendbuf_xYZ, sendCount_xYZ, rank_xYZ, + sendtag + 25); + req2[25] = MPI_COMM_SCALBL.Irecv(recvbuf_Xyz, recvCount_Xyz, rank_Xyz, + recvtag + 25); + //................................................................................... } +ScaLBLWideHalo_Communicator::~ScaLBLWideHalo_Communicator() {} +void ScaLBLWideHalo_Communicator::Recv(double *data) { -ScaLBLWideHalo_Communicator::~ScaLBLWideHalo_Communicator() -{ + //................................................................................... + Utilities::MPI::waitAll(26, req1); + Utilities::MPI::waitAll(26, req2); + ScaLBL_DeviceBarrier(); + //................................................................................... + //printf("Ready to unpack %i to x\n",recvCount_x); + //printf(" print first 10 values...\n"); + //for (int idx=0; idx<10; idx++) printf(" recvBuf[%i]=%f \n",idx,recvbuf_x[idx]); + ScaLBL_Scalar_Unpack(dvcRecvList_x, recvCount_x, recvbuf_x, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_y, recvCount_y, recvbuf_y, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_X, recvCount_X, recvbuf_X, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_Y, recvCount_Y, recvbuf_Y, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_xy, recvCount_xy, recvbuf_xy, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_xY, recvCount_xY, recvbuf_xY, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_Xy, recvCount_Xy, recvbuf_Xy, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_XY, recvCount_XY, recvbuf_XY, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_z, recvCount_z, recvbuf_z, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_xz, recvCount_xz, recvbuf_xz, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_Xz, recvCount_Xz, recvbuf_Xz, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_yz, recvCount_yz, recvbuf_yz, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_Yz, recvCount_Yz, recvbuf_Yz, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_Z, recvCount_Z, recvbuf_Z, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_xZ, recvCount_xZ, recvbuf_xZ, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_XZ, recvCount_XZ, recvbuf_XZ, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_yZ, recvCount_yZ, recvbuf_yZ, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_YZ, recvCount_YZ, recvbuf_YZ, data, Nh); + /* corners */ + ScaLBL_Scalar_Unpack(dvcRecvList_xyz, recvCount_xyz, recvbuf_xyz, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_xYz, recvCount_xYz, recvbuf_xYz, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_xyZ, recvCount_xyZ, recvbuf_xyZ, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_xYZ, recvCount_xYZ, recvbuf_xYZ, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_Xyz, recvCount_Xyz, recvbuf_Xyz, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_XYz, recvCount_XYz, recvbuf_XYz, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_XyZ, recvCount_XyZ, recvbuf_XyZ, data, Nh); + ScaLBL_Scalar_Unpack(dvcRecvList_XYZ, recvCount_XYZ, recvbuf_XYZ, data, Nh); + //................................................................................... + Lock = false; // unlock the communicator after communications complete + //................................................................................... } -void ScaLBLWideHalo_Communicator::Recv(double *data){ - - //................................................................................... - Utilities::MPI::waitAll(26,req1); - Utilities::MPI::waitAll(26,req2); - ScaLBL_DeviceBarrier(); - //................................................................................... - //printf("Ready to unpack %i to x\n",recvCount_x); - //printf(" print first 10 values...\n"); - //for (int idx=0; idx<10; idx++) printf(" recvBuf[%i]=%f \n",idx,recvbuf_x[idx]); - ScaLBL_Scalar_Unpack(dvcRecvList_x, recvCount_x,recvbuf_x, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_y, recvCount_y,recvbuf_y, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_X, recvCount_X,recvbuf_X, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_Y, recvCount_Y,recvbuf_Y, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_xy, recvCount_xy,recvbuf_xy, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_xY, recvCount_xY,recvbuf_xY, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_Xy, recvCount_Xy,recvbuf_Xy, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_XY, recvCount_XY,recvbuf_XY, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_z, recvCount_z,recvbuf_z, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_xz, recvCount_xz,recvbuf_xz, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_Xz, recvCount_Xz,recvbuf_Xz, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_yz, recvCount_yz,recvbuf_yz, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_Yz, recvCount_Yz,recvbuf_Yz, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_Z, recvCount_Z,recvbuf_Z, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_xZ, recvCount_xZ,recvbuf_xZ, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_XZ, recvCount_XZ,recvbuf_XZ, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_yZ, recvCount_yZ,recvbuf_yZ, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_YZ, recvCount_YZ,recvbuf_YZ, data, Nh); - /* corners */ - ScaLBL_Scalar_Unpack(dvcRecvList_xyz, recvCount_xyz,recvbuf_xyz, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_xYz, recvCount_xYz,recvbuf_xYz, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_xyZ, recvCount_xyZ,recvbuf_xyZ, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_xYZ, recvCount_xYZ,recvbuf_xYZ, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_Xyz, recvCount_Xyz,recvbuf_Xyz, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_XYz, recvCount_XYz,recvbuf_XYz, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_XyZ, recvCount_XyZ,recvbuf_XyZ, data, Nh); - ScaLBL_Scalar_Unpack(dvcRecvList_XYZ, recvCount_XYZ,recvbuf_XYZ, data, Nh); - //................................................................................... - Lock=false; // unlock the communicator after communications complete - //................................................................................... - -} - diff --git a/common/WideHalo.h b/common/WideHalo.h index 5c9fcedf..28455f10 100644 --- a/common/WideHalo.h +++ b/common/WideHalo.h @@ -6,110 +6,130 @@ This class implements support for halo widths larger than 1 #include "common/ScaLBL.h" #include "common/MPI.h" -class ScaLBLWideHalo_Communicator{ +class ScaLBLWideHalo_Communicator { public: - //...................................................................................... - ScaLBLWideHalo_Communicator(std::shared_ptr Dm, int width); - ~ScaLBLWideHalo_Communicator(); - //...................................................................................... - //MPI_Comm MPI_COMM_SCALBL; // MPI Communicator - Utilities::MPI MPI_COMM_SCALBL; - unsigned long int CommunicationCount,SendCount,RecvCount; - int Nx,Ny,Nz,N; // original domain structure - int Nxh,Nyh,Nzh,Nh; // with wide halo - DoubleArray Map; // map to regular halo - int first_interior,last_interior; - //...................................................................................... - // Set up for D3Q19 distributions -- all 27 neighbors are needed - //...................................................................................... - // Buffers to store data sent and recieved by this MPI process - double *sendbuf_x, *sendbuf_y, *sendbuf_z, *sendbuf_X, *sendbuf_Y, *sendbuf_Z; - double *sendbuf_xy, *sendbuf_yz, *sendbuf_xz, *sendbuf_Xy, *sendbuf_Yz, *sendbuf_xZ; - double *sendbuf_xY, *sendbuf_yZ, *sendbuf_Xz, *sendbuf_XY, *sendbuf_YZ, *sendbuf_XZ; - double *sendbuf_xyz, *sendbuf_Xyz, *sendbuf_xYz, *sendbuf_XYz; - double *sendbuf_xyZ, *sendbuf_XyZ, *sendbuf_xYZ, *sendbuf_XYZ; - double *recvbuf_x, *recvbuf_y, *recvbuf_z, *recvbuf_X, *recvbuf_Y, *recvbuf_Z; - double *recvbuf_xy, *recvbuf_yz, *recvbuf_xz, *recvbuf_Xy, *recvbuf_Yz, *recvbuf_xZ; - double *recvbuf_xY, *recvbuf_yZ, *recvbuf_Xz, *recvbuf_XY, *recvbuf_YZ, *recvbuf_XZ; - double *recvbuf_xyz, *recvbuf_Xyz, *recvbuf_xYz, *recvbuf_XYz; - double *recvbuf_xyZ, *recvbuf_XyZ, *recvbuf_xYZ, *recvbuf_XYZ; - //...................................................................................... - int LastExterior(); - int FirstInterior(); - int LastInterior(); - - void Send(double *data); - void Recv(double *data); + //...................................................................................... + ScaLBLWideHalo_Communicator(std::shared_ptr Dm, int width); + ~ScaLBLWideHalo_Communicator(); + //...................................................................................... + //MPI_Comm MPI_COMM_SCALBL; // MPI Communicator + Utilities::MPI MPI_COMM_SCALBL; + unsigned long int CommunicationCount, SendCount, RecvCount; + int Nx, Ny, Nz, N; // original domain structure + int Nxh, Nyh, Nzh, Nh; // with wide halo + DoubleArray Map; // map to regular halo + int first_interior, last_interior; + //...................................................................................... + // Set up for D3Q19 distributions -- all 27 neighbors are needed + //...................................................................................... + // Buffers to store data sent and recieved by this MPI process + double *sendbuf_x, *sendbuf_y, *sendbuf_z, *sendbuf_X, *sendbuf_Y, + *sendbuf_Z; + double *sendbuf_xy, *sendbuf_yz, *sendbuf_xz, *sendbuf_Xy, *sendbuf_Yz, + *sendbuf_xZ; + double *sendbuf_xY, *sendbuf_yZ, *sendbuf_Xz, *sendbuf_XY, *sendbuf_YZ, + *sendbuf_XZ; + double *sendbuf_xyz, *sendbuf_Xyz, *sendbuf_xYz, *sendbuf_XYz; + double *sendbuf_xyZ, *sendbuf_XyZ, *sendbuf_xYZ, *sendbuf_XYZ; + double *recvbuf_x, *recvbuf_y, *recvbuf_z, *recvbuf_X, *recvbuf_Y, + *recvbuf_Z; + double *recvbuf_xy, *recvbuf_yz, *recvbuf_xz, *recvbuf_Xy, *recvbuf_Yz, + *recvbuf_xZ; + double *recvbuf_xY, *recvbuf_yZ, *recvbuf_Xz, *recvbuf_XY, *recvbuf_YZ, + *recvbuf_XZ; + double *recvbuf_xyz, *recvbuf_Xyz, *recvbuf_xYz, *recvbuf_XYz; + double *recvbuf_xyZ, *recvbuf_XyZ, *recvbuf_xYZ, *recvbuf_XYZ; + //...................................................................................... + int LastExterior(); + int FirstInterior(); + int LastInterior(); - // Debugging and unit testing functions - void PrintDebug(); + void Send(double *data); + void Recv(double *data); + + // Debugging and unit testing functions + void PrintDebug(); private: - bool Lock; // use Lock to make sure only one call at a time to protect data in transit - // only one set of Send requests can be active at any time (per instance) - int i,j,k,n; - int iproc,jproc,kproc; - int nprocx,nprocy,nprocz; - int sendtag,recvtag; - // Give the object it's own MPI communicator - RankInfoStruct rank_info; - MPI_Request req1[26],req2[26]; - //...................................................................................... - // MPI ranks for all 18 neighbors - //...................................................................................... - // These variables are all private to prevent external things from modifying them!! - //...................................................................................... - int rank; - int rank_x,rank_y,rank_z,rank_X,rank_Y,rank_Z; - int rank_xy,rank_XY,rank_xY,rank_Xy; - int rank_xz,rank_XZ,rank_xZ,rank_Xz; - int rank_yz,rank_YZ,rank_yZ,rank_Yz; - int rank_xyz,rank_Xyz,rank_xYz,rank_XYz; - int rank_xyZ,rank_XyZ,rank_xYZ,rank_XYZ; - //...................................................................................... - //...................................................................................... - int sendCount_x, sendCount_y, sendCount_z, sendCount_X, sendCount_Y, sendCount_Z; - int sendCount_xy, sendCount_yz, sendCount_xz, sendCount_Xy, sendCount_Yz, sendCount_xZ; - int sendCount_xY, sendCount_yZ, sendCount_Xz, sendCount_XY, sendCount_YZ, sendCount_XZ; - int sendCount_xyz,sendCount_Xyz,sendCount_xYz,sendCount_XYz; - int sendCount_xyZ,sendCount_XyZ,sendCount_xYZ,sendCount_XYZ; - //...................................................................................... - int recvCount_x, recvCount_y, recvCount_z, recvCount_X, recvCount_Y, recvCount_Z; - int recvCount_xy, recvCount_yz, recvCount_xz, recvCount_Xy, recvCount_Yz, recvCount_xZ; - int recvCount_xY, recvCount_yZ, recvCount_Xz, recvCount_XY, recvCount_YZ, recvCount_XZ; - int recvCount_xyz,recvCount_Xyz,recvCount_xYz,recvCount_XYz; - int recvCount_xyZ,recvCount_XyZ,recvCount_xYZ,recvCount_XYZ; - //...................................................................................... - // Send buffers that reside on the compute device - int *dvcSendList_x, *dvcSendList_y, *dvcSendList_z, *dvcSendList_X, *dvcSendList_Y, *dvcSendList_Z; - int *dvcSendList_xy, *dvcSendList_yz, *dvcSendList_xz, *dvcSendList_Xy, *dvcSendList_Yz, *dvcSendList_xZ; - int *dvcSendList_xY, *dvcSendList_yZ, *dvcSendList_Xz, *dvcSendList_XY, *dvcSendList_YZ, *dvcSendList_XZ; - int *dvcSendList_xyz,*dvcSendList_Xyz,*dvcSendList_xYz,*dvcSendList_XYz; - int *dvcSendList_xyZ,*dvcSendList_XyZ,*dvcSendList_xYZ,*dvcSendList_XYZ; - // Recieve buffers that reside on the compute device - int *dvcRecvList_x, *dvcRecvList_y, *dvcRecvList_z, *dvcRecvList_X, *dvcRecvList_Y, *dvcRecvList_Z; - int *dvcRecvList_xy, *dvcRecvList_yz, *dvcRecvList_xz, *dvcRecvList_Xy, *dvcRecvList_Yz, *dvcRecvList_xZ; - int *dvcRecvList_xY, *dvcRecvList_yZ, *dvcRecvList_Xz, *dvcRecvList_XY, *dvcRecvList_YZ, *dvcRecvList_XZ; - int *dvcRecvList_xyz,*dvcRecvList_Xyz,*dvcRecvList_xYz,*dvcRecvList_XYz; - int *dvcRecvList_xyZ,*dvcRecvList_XyZ,*dvcRecvList_xYZ,*dvcRecvList_XYZ; - //...................................................................................... - - inline int getHaloBlock(int imin, int imax, int jmin, int jmax, int kmin, int kmax, int *& dvcList){ - int count = 0; - int *List; - List = new int [(imax-imin)*(jmax-jmin)*(kmax-kmin)]; - for (k=kmin; k. */ -extern "C" void ScaLBL_D3Q19_AAeven_BGK(double *dist, int start, int finish, int Np, double rlx, double Fx, double Fy, double Fz){ - // conserved momemnts - double rho,ux,uy,uz,uu; - // non-conserved moments - double f0,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18; +extern "C" void ScaLBL_D3Q19_AAeven_BGK(double *dist, int start, int finish, + int Np, double rlx, double Fx, + double Fy, double Fz) { + // conserved momemnts + double rho, ux, uy, uz, uu; + // non-conserved moments + double f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, + f16, f17, f18; - for (int n=start; n 10Np => odd part of dist) - f1 = dist[nr1]; // reading the f1 data into register fq + for (int n = start; n < finish; n++) { - nr2 = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) - f2 = dist[nr2]; // reading the f2 data into register fq + // q=0 + f0 = dist[n]; + // q=1 + nr1 = neighborList[n]; // neighbor 2 ( > 10Np => odd part of dist) + f1 = dist[nr1]; // reading the f1 data into register fq - // q=3 - nr3 = neighborList[n+2*Np]; // neighbor 4 - f3 = dist[nr3]; + nr2 = neighborList[n + Np]; // neighbor 1 ( < 10Np => even part of dist) + f2 = dist[nr2]; // reading the f2 data into register fq - // q = 4 - nr4 = neighborList[n+3*Np]; // neighbor 3 - f4 = dist[nr4]; + // q=3 + nr3 = neighborList[n + 2 * Np]; // neighbor 4 + f3 = dist[nr3]; - // q=5 - nr5 = neighborList[n+4*Np]; - f5 = dist[nr5]; + // q = 4 + nr4 = neighborList[n + 3 * Np]; // neighbor 3 + f4 = dist[nr4]; - // q = 6 - nr6 = neighborList[n+5*Np]; - f6 = dist[nr6]; - - // q=7 - nr7 = neighborList[n+6*Np]; - f7 = dist[nr7]; + // q=5 + nr5 = neighborList[n + 4 * Np]; + f5 = dist[nr5]; - // q = 8 - nr8 = neighborList[n+7*Np]; - f8 = dist[nr8]; + // q = 6 + nr6 = neighborList[n + 5 * Np]; + f6 = dist[nr6]; - // q=9 - nr9 = neighborList[n+8*Np]; - f9 = dist[nr9]; + // q=7 + nr7 = neighborList[n + 6 * Np]; + f7 = dist[nr7]; - // q = 10 - nr10 = neighborList[n+9*Np]; - f10 = dist[nr10]; + // q = 8 + nr8 = neighborList[n + 7 * Np]; + f8 = dist[nr8]; - // q=11 - nr11 = neighborList[n+10*Np]; - f11 = dist[nr11]; + // q=9 + nr9 = neighborList[n + 8 * Np]; + f9 = dist[nr9]; - // q=12 - nr12 = neighborList[n+11*Np]; - f12 = dist[nr12]; + // q = 10 + nr10 = neighborList[n + 9 * Np]; + f10 = dist[nr10]; - // q=13 - nr13 = neighborList[n+12*Np]; - f13 = dist[nr13]; + // q=11 + nr11 = neighborList[n + 10 * Np]; + f11 = dist[nr11]; - // q=14 - nr14 = neighborList[n+13*Np]; - f14 = dist[nr14]; + // q=12 + nr12 = neighborList[n + 11 * Np]; + f12 = dist[nr12]; - // q=15 - nr15 = neighborList[n+14*Np]; - f15 = dist[nr15]; + // q=13 + nr13 = neighborList[n + 12 * Np]; + f13 = dist[nr13]; - // q=16 - nr16 = neighborList[n+15*Np]; - f16 = dist[nr16]; + // q=14 + nr14 = neighborList[n + 13 * Np]; + f14 = dist[nr14]; - // q=17 - //fq = dist[18*Np+n]; - nr17 = neighborList[n+16*Np]; - f17 = dist[nr17]; + // q=15 + nr15 = neighborList[n + 14 * Np]; + f15 = dist[nr15]; - // q=18 - nr18 = neighborList[n+17*Np]; - f18 = dist[nr18]; + // q=16 + nr16 = neighborList[n + 15 * Np]; + f16 = dist[nr16]; - rho = f0+f2+f1+f4+f3+f6+f5+f8+f7+f10+f9+f12+f11+f14+f13+f16+f15+f18+f17; - ux = f1-f2+f7-f8+f9-f10+f11-f12+f13-f14; - uy = f3-f4+f7-f8-f9+f10+f15-f16+f17-f18; - uz = f5-f6+f11-f12-f13+f14+f15-f16-f17+f18; - uu = 1.5*(ux*ux+uy*uy+uz*uz); + // q=17 + //fq = dist[18*Np+n]; + nr17 = neighborList[n + 16 * Np]; + f17 = dist[nr17]; - // q=0 - dist[n] = f0*(1.0-rlx)+rlx*0.3333333333333333*(1.0-uu); + // q=18 + nr18 = neighborList[n + 17 * Np]; + f18 = dist[nr18]; - // q = 1 - dist[nr2] = f1*(1.0-rlx) + rlx*0.05555555555555555*(rho + 3.0*ux + 4.5*ux*ux - uu) + 0.16666666*Fx; + rho = f0 + f2 + f1 + f4 + f3 + f6 + f5 + f8 + f7 + f10 + f9 + f12 + + f11 + f14 + f13 + f16 + f15 + f18 + f17; + ux = f1 - f2 + f7 - f8 + f9 - f10 + f11 - f12 + f13 - f14; + uy = f3 - f4 + f7 - f8 - f9 + f10 + f15 - f16 + f17 - f18; + uz = f5 - f6 + f11 - f12 - f13 + f14 + f15 - f16 - f17 + f18; + uu = 1.5 * (ux * ux + uy * uy + uz * uz); - // q=2 - dist[nr1] = f2*(1.0-rlx) + rlx*0.05555555555555555*(rho - 3.0*ux + 4.5*ux*ux - uu)- 0.16666666*Fx; + // q=0 + dist[n] = f0 * (1.0 - rlx) + rlx * 0.3333333333333333 * (1.0 - uu); - // q = 3 - dist[nr4] = f3*(1.0-rlx) + - rlx*0.05555555555555555*(rho + 3.0*uy + 4.5*uy*uy - uu) + 0.16666666*Fy; + // q = 1 + dist[nr2] = + f1 * (1.0 - rlx) + + rlx * 0.05555555555555555 * (rho + 3.0 * ux + 4.5 * ux * ux - uu) + + 0.16666666 * Fx; - // q = 4 - dist[nr3] = f4*(1.0-rlx) + - rlx*0.05555555555555555*(rho - 3.0*uy + 4.5*uy*uy - uu)- 0.16666666*Fy; + // q=2 + dist[nr1] = + f2 * (1.0 - rlx) + + rlx * 0.05555555555555555 * (rho - 3.0 * ux + 4.5 * ux * ux - uu) - + 0.16666666 * Fx; - // q = 5 - dist[nr6] = f5*(1.0-rlx) + - rlx*0.05555555555555555*(rho + 3.0*uz + 4.5*uz*uz - uu) + 0.16666666*Fz; + // q = 3 + dist[nr4] = + f3 * (1.0 - rlx) + + rlx * 0.05555555555555555 * (rho + 3.0 * uy + 4.5 * uy * uy - uu) + + 0.16666666 * Fy; - // q = 6 - dist[nr5] = f6*(1.0-rlx) + - rlx*0.05555555555555555*(rho - 3.0*uz + 4.5*uz*uz - uu) - 0.16666666*Fz; + // q = 4 + dist[nr3] = + f4 * (1.0 - rlx) + + rlx * 0.05555555555555555 * (rho - 3.0 * uy + 4.5 * uy * uy - uu) - + 0.16666666 * Fy; - // q = 7 - dist[nr8] = f7*(1.0-rlx) + - rlx*0.02777777777777778*(rho + 3.0*(ux+uy) + 4.5*(ux+uy)*(ux+uy) - uu) + 0.08333333333*(Fx+Fy); + // q = 5 + dist[nr6] = + f5 * (1.0 - rlx) + + rlx * 0.05555555555555555 * (rho + 3.0 * uz + 4.5 * uz * uz - uu) + + 0.16666666 * Fz; - // q = 8 - dist[nr7] = f8*(1.0-rlx) + - rlx*0.02777777777777778*(rho - 3.0*(ux+uy) + 4.5*(ux+uy)*(ux+uy) - uu) - 0.08333333333*(Fx+Fy); + // q = 6 + dist[nr5] = + f6 * (1.0 - rlx) + + rlx * 0.05555555555555555 * (rho - 3.0 * uz + 4.5 * uz * uz - uu) - + 0.16666666 * Fz; - // q = 9 - dist[nr10] = f9*(1.0-rlx) + - rlx*0.02777777777777778*(rho + 3.0*(ux-uy) + 4.5*(ux-uy)*(ux-uy) - uu) + 0.08333333333*(Fx-Fy); + // q = 7 + dist[nr8] = + f7 * (1.0 - rlx) + + rlx * 0.02777777777777778 * + (rho + 3.0 * (ux + uy) + 4.5 * (ux + uy) * (ux + uy) - uu) + + 0.08333333333 * (Fx + Fy); - // q = 10 - dist[nr9] = f10*(1.0-rlx) + - rlx*0.02777777777777778*(rho - 3.0*(ux-uy) + 4.5*(ux-uy)*(ux-uy) - uu) - 0.08333333333*(Fx-Fy); + // q = 8 + dist[nr7] = + f8 * (1.0 - rlx) + + rlx * 0.02777777777777778 * + (rho - 3.0 * (ux + uy) + 4.5 * (ux + uy) * (ux + uy) - uu) - + 0.08333333333 * (Fx + Fy); - // q = 11 - dist[nr12] = f11*(1.0-rlx) + - rlx*0.02777777777777778*(rho + 3.0*(ux+uz) + 4.5*(ux+uz)*(ux+uz) - uu) + 0.08333333333*(Fx+Fz); + // q = 9 + dist[nr10] = + f9 * (1.0 - rlx) + + rlx * 0.02777777777777778 * + (rho + 3.0 * (ux - uy) + 4.5 * (ux - uy) * (ux - uy) - uu) + + 0.08333333333 * (Fx - Fy); - // q = 12 - dist[nr11] = f12*(1.0-rlx) + - rlx*0.02777777777777778*(rho - 3.0*(ux+uz) + 4.5*(ux+uz)*(ux+uz) - uu) - 0.08333333333*(Fx+Fz); + // q = 10 + dist[nr9] = + f10 * (1.0 - rlx) + + rlx * 0.02777777777777778 * + (rho - 3.0 * (ux - uy) + 4.5 * (ux - uy) * (ux - uy) - uu) - + 0.08333333333 * (Fx - Fy); - // q = 13 - dist[nr14] = f13*(1.0-rlx) + - rlx*0.02777777777777778*(rho + 3.0*(ux-uz) + 4.5*(ux-uz)*(ux-uz) - uu) + 0.08333333333*(Fx-Fz); + // q = 11 + dist[nr12] = + f11 * (1.0 - rlx) + + rlx * 0.02777777777777778 * + (rho + 3.0 * (ux + uz) + 4.5 * (ux + uz) * (ux + uz) - uu) + + 0.08333333333 * (Fx + Fz); - // q= 14 - dist[nr13] = f14*(1.0-rlx) + - rlx*0.02777777777777778*(rho - 3.0*(ux-uz) + 4.5*(ux-uz)*(ux-uz) - uu)- 0.08333333333*(Fx-Fz); + // q = 12 + dist[nr11] = + f12 * (1.0 - rlx) + + rlx * 0.02777777777777778 * + (rho - 3.0 * (ux + uz) + 4.5 * (ux + uz) * (ux + uz) - uu) - + 0.08333333333 * (Fx + Fz); - // q = 15 - dist[nr16] = f15*(1.0-rlx) + - rlx*0.02777777777777778*(rho + 3.0*(uy+uz) + 4.5*(uy+uz)*(uy+uz) - uu) + 0.08333333333*(Fy+Fz); + // q = 13 + dist[nr14] = + f13 * (1.0 - rlx) + + rlx * 0.02777777777777778 * + (rho + 3.0 * (ux - uz) + 4.5 * (ux - uz) * (ux - uz) - uu) + + 0.08333333333 * (Fx - Fz); - // q = 16 - dist[nr15] = f16*(1.0-rlx) + - rlx*0.02777777777777778*(rho - 3.0*(uy+uz) + 4.5*(uy+uz)*(uy+uz) - uu) - 0.08333333333*(Fy+Fz); + // q= 14 + dist[nr13] = + f14 * (1.0 - rlx) + + rlx * 0.02777777777777778 * + (rho - 3.0 * (ux - uz) + 4.5 * (ux - uz) * (ux - uz) - uu) - + 0.08333333333 * (Fx - Fz); - // q = 17 - dist[nr18] = f17*(1.0-rlx) + - rlx*0.02777777777777778*(rho + 3.0*(uy-uz) + 4.5*(uy-uz)*(uy-uz) - uu) + 0.08333333333*(Fy-Fz); + // q = 15 + dist[nr16] = + f15 * (1.0 - rlx) + + rlx * 0.02777777777777778 * + (rho + 3.0 * (uy + uz) + 4.5 * (uy + uz) * (uy + uz) - uu) + + 0.08333333333 * (Fy + Fz); - // q = 18 - dist[nr17] = f18*(1.0-rlx) + - rlx*0.02777777777777778*(rho - 3.0*(uy-uz) + 4.5*(uy-uz)*(uy-uz) - uu) - 0.08333333333*(Fy-Fz); + // q = 16 + dist[nr15] = + f16 * (1.0 - rlx) + + rlx * 0.02777777777777778 * + (rho - 3.0 * (uy + uz) + 4.5 * (uy + uz) * (uy + uz) - uu) - + 0.08333333333 * (Fy + Fz); - } + // q = 17 + dist[nr18] = + f17 * (1.0 - rlx) + + rlx * 0.02777777777777778 * + (rho + 3.0 * (uy - uz) + 4.5 * (uy - uz) * (uy - uz) - uu) + + 0.08333333333 * (Fy - Fz); + + // q = 18 + dist[nr17] = + f18 * (1.0 - rlx) + + rlx * 0.02777777777777778 * + (rho - 3.0 * (uy - uz) + 4.5 * (uy - uz) * (uy - uz) - uu) - + 0.08333333333 * (Fy - Fz); + } } diff --git a/cpu/Color.cpp b/cpu/Color.cpp index d1ac8f0d..526bcafa 100644 --- a/cpu/Color.cpp +++ b/cpu/Color.cpp @@ -18,506 +18,590 @@ #define STOKES -extern "C" void ScaLBL_Color_Init(char *ID, double *Den, double *Phi, double das, double dbs, int Nx, int Ny, int Nz) -{ - int n,N; +extern "C" void ScaLBL_Color_Init(char *ID, double *Den, double *Phi, + double das, double dbs, int Nx, int Ny, + int Nz) { + int n, N; - N = Nx*Ny*Nz; + N = Nx * Ny * Nz; - for (n=0; n 0){ + if (id > 0) { - // Retrieve the color gradient - nx = ColorGrad[n]; - ny = ColorGrad[N+n]; - nz = ColorGrad[2*N+n]; - //...........Normalize the Color Gradient................................. - C = sqrt(nx*nx+ny*ny+nz*nz); - if (C==0.0) C=1.0; - nx = nx/C; - ny = ny/C; - nz = nz/C; - //......No color gradient at z-boundary if pressure BC are set............. - // if (pBC && k==0) nx = ny = nz = 0.f; - // if (pBC && k==Nz-1) nx = ny = nz = 0.f; - //........................................................................ - // READ THE DISTRIBUTIONS - // (read from opposite array due to previous swap operation) - //........................................................................ - f2 = distodd[n]; - f4 = distodd[N+n]; - f6 = distodd[2*N+n]; - f8 = distodd[3*N+n]; - f10 = distodd[4*N+n]; - f12 = distodd[5*N+n]; - f14 = distodd[6*N+n]; - f16 = distodd[7*N+n]; - f18 = distodd[8*N+n]; - //........................................................................ - f0 = disteven[n]; - f1 = disteven[N+n]; - f3 = disteven[2*N+n]; - f5 = disteven[3*N+n]; - f7 = disteven[4*N+n]; - f9 = disteven[5*N+n]; - f11 = disteven[6*N+n]; - f13 = disteven[7*N+n]; - f15 = disteven[8*N+n]; - f17 = disteven[9*N+n]; - //........................................................................ - // PERFORM RELAXATION PROCESS - //........................................................................ - //....................compute the moments............................................... - rho = f0+f2+f1+f4+f3+f6+f5+f8+f7+f10+f9+f12+f11+f14+f13+f16+f15+f18+f17; - m1 = -30*f0-11*(f2+f1+f4+f3+f6+f5)+8*(f8+f7+f10+f9+f12+f11+f14+f13+f16+f15+f18 +f17); - m2 = 12*f0-4*(f2+f1 +f4+f3+f6 +f5)+f8+f7+f10+f9+f12+f11+f14+f13+f16+f15+f18+f17; - jx = f1-f2+f7-f8+f9-f10+f11-f12+f13-f14; - m4 = 4*(-f1+f2)+f7-f8+f9-f10+f11-f12+f13-f14; - jy = f3-f4+f7-f8-f9+f10+f15-f16+f17-f18; - m6 = -4*(f3-f4)+f7-f8-f9+f10+f15-f16+f17-f18; - jz = f5-f6+f11-f12-f13+f14+f15-f16-f17+f18; - m8 = -4*(f5-f6)+f11-f12-f13+f14+f15-f16-f17+f18; - m9 = 2*(f1+f2)-f3-f4-f5-f6+f7+f8+f9+f10+f11+f12+f13+f14-2*(f15+f16+f17+f18); - m10 = -4*(f1+f2)+2*(f4+f3+f6+f5)+f8+f7+f10+f9+f12+f11+f14+f13-2*(f16+f15+f18+f17); - m11 = f4+f3-f6-f5+f8+f7+f10+f9-f12-f11-f14-f13; - m12 = -2*(f4+f3-f6-f5)+f8+f7+f10+f9-f12-f11-f14-f13; - m13 = f8+f7-f10-f9; - m14 = f16+f15-f18-f17; - m15 = f12+f11-f14-f13; - m16 = f7-f8+f9-f10-f11+f12-f13+f14; - m17 = -f7+f8+f9-f10+f15-f16+f17-f18; - m18 = f11-f12-f13+f14-f15+f16+f17-f18; - //..........Toelke, Fruediger et. al. 2006............... - if (C == 0.0) nx = ny = nz = 1.0; + // Retrieve the color gradient + nx = ColorGrad[n]; + ny = ColorGrad[N + n]; + nz = ColorGrad[2 * N + n]; + //...........Normalize the Color Gradient................................. + C = sqrt(nx * nx + ny * ny + nz * nz); + if (C == 0.0) + C = 1.0; + nx = nx / C; + ny = ny / C; + nz = nz / C; + //......No color gradient at z-boundary if pressure BC are set............. + // if (pBC && k==0) nx = ny = nz = 0.f; + // if (pBC && k==Nz-1) nx = ny = nz = 0.f; + //........................................................................ + // READ THE DISTRIBUTIONS + // (read from opposite array due to previous swap operation) + //........................................................................ + f2 = distodd[n]; + f4 = distodd[N + n]; + f6 = distodd[2 * N + n]; + f8 = distodd[3 * N + n]; + f10 = distodd[4 * N + n]; + f12 = distodd[5 * N + n]; + f14 = distodd[6 * N + n]; + f16 = distodd[7 * N + n]; + f18 = distodd[8 * N + n]; + //........................................................................ + f0 = disteven[n]; + f1 = disteven[N + n]; + f3 = disteven[2 * N + n]; + f5 = disteven[3 * N + n]; + f7 = disteven[4 * N + n]; + f9 = disteven[5 * N + n]; + f11 = disteven[6 * N + n]; + f13 = disteven[7 * N + n]; + f15 = disteven[8 * N + n]; + f17 = disteven[9 * N + n]; + //........................................................................ + // PERFORM RELAXATION PROCESS + //........................................................................ + //....................compute the moments............................................... + rho = f0 + f2 + f1 + f4 + f3 + f6 + f5 + f8 + f7 + f10 + f9 + f12 + + f11 + f14 + f13 + f16 + f15 + f18 + f17; + m1 = -30 * f0 - 11 * (f2 + f1 + f4 + f3 + f6 + f5) + + 8 * (f8 + f7 + f10 + f9 + f12 + f11 + f14 + f13 + f16 + f15 + + f18 + f17); + m2 = 12 * f0 - 4 * (f2 + f1 + f4 + f3 + f6 + f5) + f8 + f7 + f10 + + f9 + f12 + f11 + f14 + f13 + f16 + f15 + f18 + f17; + jx = f1 - f2 + f7 - f8 + f9 - f10 + f11 - f12 + f13 - f14; + m4 = 4 * (-f1 + f2) + f7 - f8 + f9 - f10 + f11 - f12 + f13 - f14; + jy = f3 - f4 + f7 - f8 - f9 + f10 + f15 - f16 + f17 - f18; + m6 = -4 * (f3 - f4) + f7 - f8 - f9 + f10 + f15 - f16 + f17 - f18; + jz = f5 - f6 + f11 - f12 - f13 + f14 + f15 - f16 - f17 + f18; + m8 = -4 * (f5 - f6) + f11 - f12 - f13 + f14 + f15 - f16 - f17 + f18; + m9 = 2 * (f1 + f2) - f3 - f4 - f5 - f6 + f7 + f8 + f9 + f10 + f11 + + f12 + f13 + f14 - 2 * (f15 + f16 + f17 + f18); + m10 = -4 * (f1 + f2) + 2 * (f4 + f3 + f6 + f5) + f8 + f7 + f10 + + f9 + f12 + f11 + f14 + f13 - 2 * (f16 + f15 + f18 + f17); + m11 = + f4 + f3 - f6 - f5 + f8 + f7 + f10 + f9 - f12 - f11 - f14 - f13; + m12 = -2 * (f4 + f3 - f6 - f5) + f8 + f7 + f10 + f9 - f12 - f11 - + f14 - f13; + m13 = f8 + f7 - f10 - f9; + m14 = f16 + f15 - f18 - f17; + m15 = f12 + f11 - f14 - f13; + m16 = f7 - f8 + f9 - f10 - f11 + f12 - f13 + f14; + m17 = -f7 + f8 + f9 - f10 + f15 - f16 + f17 - f18; + m18 = f11 - f12 - f13 + f14 - f15 + f16 + f17 - f18; + //..........Toelke, Fruediger et. al. 2006............... + if (C == 0.0) + nx = ny = nz = 1.0; #ifdef STOKES - m1 = m1 + rlx_setA*(- 11*rho -alpha*C - m1); - m2 = m2 + rlx_setA*(3*rho - m2); - m4 = m4 + rlx_setB*((-0.6666666666666666*jx)- m4); - m6 = m6 + rlx_setB*((-0.6666666666666666*jy)- m6); - m8 = m8 + rlx_setB*((-0.6666666666666666*jz)- m8); - m9 = m9 + rlx_setA*( 0.5*alpha*C*(2*nx*nx-ny*ny-nz*nz) - m9); - m10 = m10 + rlx_setA*( - m10); - m11 = m11 + rlx_setA*( 0.5*alpha*C*(ny*ny-nz*nz)- m11); - m12 = m12 + rlx_setA*( - m12); - m13 = m13 + rlx_setA*( 0.5*alpha*C*nx*ny - m13); - m14 = m14 + rlx_setA*( 0.5*alpha*C*ny*nz - m14); - m15 = m15 + rlx_setA*( 0.5*alpha*C*nx*nz - m15); - m16 = m16 + rlx_setB*( - m16); - m17 = m17 + rlx_setB*( - m17); - m18 = m18 + rlx_setB*( - m18); + m1 = m1 + rlx_setA * (-11 * rho - alpha * C - m1); + m2 = m2 + rlx_setA * (3 * rho - m2); + m4 = m4 + rlx_setB * ((-0.6666666666666666 * jx) - m4); + m6 = m6 + rlx_setB * ((-0.6666666666666666 * jy) - m6); + m8 = m8 + rlx_setB * ((-0.6666666666666666 * jz) - m8); + m9 = m9 + + rlx_setA * + (0.5 * alpha * C * (2 * nx * nx - ny * ny - nz * nz) - m9); + m10 = m10 + rlx_setA * (-m10); + m11 = + m11 + rlx_setA * (0.5 * alpha * C * (ny * ny - nz * nz) - m11); + m12 = m12 + rlx_setA * (-m12); + m13 = m13 + rlx_setA * (0.5 * alpha * C * nx * ny - m13); + m14 = m14 + rlx_setA * (0.5 * alpha * C * ny * nz - m14); + m15 = m15 + rlx_setA * (0.5 * alpha * C * nx * nz - m15); + m16 = m16 + rlx_setB * (-m16); + m17 = m17 + rlx_setB * (-m17); + m18 = m18 + rlx_setB * (-m18); #else - m1 = m1 + rlx_setA*((19*(jx*jx+jy*jy+jz*jz)/rho - 11*rho) -alpha*C - m1); - m2 = m2 + rlx_setA*((3*rho - 5.5*(jx*jx+jy*jy+jz*jz)/rho)- m2); - m4 = m4 + rlx_setB*((-0.6666666666666666*jx)- m4); - m6 = m6 + rlx_setB*((-0.6666666666666666*jy)- m6); - m8 = m8 + rlx_setB*((-0.6666666666666666*jz)- m8); - m9 = m9 + rlx_setA*(((2*jx*jx-jy*jy-jz*jz)/rho) + 0.5*alpha*C*(2*nx*nx-ny*ny-nz*nz) - m9); - m10 = m10 + rlx_setA*( - m10); - m11 = m11 + rlx_setA*(((jy*jy-jz*jz)/rho) + 0.5*alpha*C*(ny*ny-nz*nz)- m11); - m12 = m12 + rlx_setA*( - m12); - m13 = m13 + rlx_setA*( (jx*jy/rho) + 0.5*alpha*C*nx*ny - m13); - m14 = m14 + rlx_setA*( (jy*jz/rho) + 0.5*alpha*C*ny*nz - m14); - m15 = m15 + rlx_setA*( (jx*jz/rho) + 0.5*alpha*C*nx*nz - m15); - m16 = m16 + rlx_setB*( - m16); - m17 = m17 + rlx_setB*( - m17); - m18 = m18 + rlx_setB*( - m18); + m1 = m1 + rlx_setA * ((19 * (jx * jx + jy * jy + jz * jz) / rho - + 11 * rho) - + alpha * C - m1); + m2 = m2 + rlx_setA * ((3 * rho - + 5.5 * (jx * jx + jy * jy + jz * jz) / rho) - + m2); + m4 = m4 + rlx_setB * ((-0.6666666666666666 * jx) - m4); + m6 = m6 + rlx_setB * ((-0.6666666666666666 * jy) - m6); + m8 = m8 + rlx_setB * ((-0.6666666666666666 * jz) - m8); + m9 = m9 + + rlx_setA * + (((2 * jx * jx - jy * jy - jz * jz) / rho) + + 0.5 * alpha * C * (2 * nx * nx - ny * ny - nz * nz) - m9); + m10 = m10 + rlx_setA * (-m10); + m11 = + m11 + rlx_setA * (((jy * jy - jz * jz) / rho) + + 0.5 * alpha * C * (ny * ny - nz * nz) - m11); + m12 = m12 + rlx_setA * (-m12); + m13 = m13 + rlx_setA * + ((jx * jy / rho) + 0.5 * alpha * C * nx * ny - m13); + m14 = m14 + rlx_setA * + ((jy * jz / rho) + 0.5 * alpha * C * ny * nz - m14); + m15 = m15 + rlx_setA * + ((jx * jz / rho) + 0.5 * alpha * C * nx * nz - m15); + m16 = m16 + rlx_setB * (-m16); + m17 = m17 + rlx_setB * (-m17); + m18 = m18 + rlx_setB * (-m18); #endif - //.................inverse transformation...................................................... - f0 = 0.05263157894736842*rho-0.012531328320802*m1+0.04761904761904762*m2; - f1 = 0.05263157894736842*rho-0.004594820384294068*m1-0.01587301587301587*m2 - +0.1*(jx-m4)+0.0555555555555555555555555*(m9-m10); - f2 = 0.05263157894736842*rho-0.004594820384294068*m1-0.01587301587301587*m2 - +0.1*(m4-jx)+0.0555555555555555555555555*(m9-m10); - f3 = 0.05263157894736842*rho-0.004594820384294068*m1-0.01587301587301587*m2 - +0.1*(jy-m6)+0.02777777777777778*(m10-m9)+0.08333333333333333*(m11-m12); - f4 = 0.05263157894736842*rho-0.004594820384294068*m1-0.01587301587301587*m2 - +0.1*(m6-jy)+0.02777777777777778*(m10-m9)+0.08333333333333333*(m11-m12); - f5 = 0.05263157894736842*rho-0.004594820384294068*m1-0.01587301587301587*m2 - +0.1*(jz-m8)+0.02777777777777778*(m10-m9)+0.08333333333333333*(m12-m11); - f6 = 0.05263157894736842*rho-0.004594820384294068*m1-0.01587301587301587*m2 - +0.1*(m8-jz)+0.02777777777777778*(m10-m9)+0.08333333333333333*(m12-m11); - f7 = 0.05263157894736842*rho+0.003341687552213868*m1+0.003968253968253968*m2+0.1*(jx+jy)+0.025*(m4+m6) - +0.02777777777777778*m9+0.01388888888888889*m10+0.08333333333333333*m11 - +0.04166666666666666*m12+0.25*m13+0.125*(m16-m17); - f8 = 0.05263157894736842*rho+0.003341687552213868*m1+0.003968253968253968*m2-0.1*(jx+jy)-0.025*(m4+m6) - +0.02777777777777778*m9+0.01388888888888889*m10+0.08333333333333333*m11 - +0.04166666666666666*m12+0.25*m13+0.125*(m17-m16); - f9 = 0.05263157894736842*rho+0.003341687552213868*m1+0.003968253968253968*m2+0.1*(jx-jy)+0.025*(m4-m6) - +0.02777777777777778*m9+0.01388888888888889*m10+0.08333333333333333*m11 - +0.04166666666666666*m12-0.25*m13+0.125*(m16+m17); - f10 = 0.05263157894736842*rho+0.003341687552213868*m1+0.003968253968253968*m2+0.1*(jy-jx)+0.025*(m6-m4) - +0.02777777777777778*m9+0.01388888888888889*m10+0.08333333333333333*m11 - +0.04166666666666666*m12-0.25*m13-0.125*(m16+m17); - f11 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2+0.1*(jx+jz)+0.025*(m4+m8) - +0.02777777777777778*m9+0.01388888888888889*m10-0.08333333333333333*m11 - -0.04166666666666666*m12+0.25*m15+0.125*(m18-m16); - f12 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2-0.1*(jx+jz)-0.025*(m4+m8) - +0.02777777777777778*m9+0.01388888888888889*m10-0.08333333333333333*m11 - -0.04166666666666666*m12+0.25*m15+0.125*(m16-m18); - f13 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2+0.1*(jx-jz)+0.025*(m4-m8) - +0.02777777777777778*m9+0.01388888888888889*m10-0.08333333333333333*m11 - -0.04166666666666666*m12-0.25*m15-0.125*(m16+m18); - f14 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2+0.1*(jz-jx)+0.025*(m8-m4) - +0.02777777777777778*m9+0.01388888888888889*m10-0.08333333333333333*m11 - -0.04166666666666666*m12-0.25*m15+0.125*(m16+m18); - f15 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2+0.1*(jy+jz)+0.025*(m6+m8) - -0.0555555555555555555555555*m9-0.02777777777777778*m10+0.25*m14+0.125*(m17-m18); - f16 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2-0.1*(jy+jz)-0.025*(m6+m8) - -0.0555555555555555555555555*m9-0.02777777777777778*m10+0.25*m14+0.125*(m18-m17); - f17 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2+0.1*(jy-jz)+0.025*(m6-m8) - -0.0555555555555555555555555*m9-0.02777777777777778*m10-0.25*m14+0.125*(m17+m18); - f18 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2+0.1*(jz-jy)+0.025*(m8-m6) - -0.0555555555555555555555555*m9-0.02777777777777778*m10-0.25*m14-0.125*(m17+m18); - //....................................................................................................... - // incorporate external force - f1 += 0.16666666*Fx; - f2 -= 0.16666666*Fx; - f3 += 0.16666666*Fy; - f4 -= 0.16666666*Fy; - f5 += 0.16666666*Fz; - f6 -= 0.16666666*Fz; - f7 += 0.08333333333*(Fx+Fy); - f8 -= 0.08333333333*(Fx+Fy); - f9 += 0.08333333333*(Fx-Fy); - f10 -= 0.08333333333*(Fx-Fy); - f11 += 0.08333333333*(Fx+Fz); - f12 -= 0.08333333333*(Fx+Fz); - f13 += 0.08333333333*(Fx-Fz); - f14 -= 0.08333333333*(Fx-Fz); - f15 += 0.08333333333*(Fy+Fz); - f16 -= 0.08333333333*(Fy+Fz); - f17 += 0.08333333333*(Fy-Fz); - f18 -= 0.08333333333*(Fy-Fz); - //*********** WRITE UPDATED VALUES TO MEMORY ****************** - // Write the updated distributions - //....EVEN..................................... - disteven[n] = f0; - disteven[N+n] = f2; - disteven[2*N+n] = f4; - disteven[3*N+n] = f6; - disteven[4*N+n] = f8; - disteven[5*N+n] = f10; - disteven[6*N+n] = f12; - disteven[7*N+n] = f14; - disteven[8*N+n] = f16; - disteven[9*N+n] = f18; - //....ODD...................................... - distodd[n] = f1; - distodd[N+n] = f3; - distodd[2*N+n] = f5; - distodd[3*N+n] = f7; - distodd[4*N+n] = f9; - distodd[5*N+n] = f11; - distodd[6*N+n] = f13; - distodd[7*N+n] = f15; - distodd[8*N+n] = f17; + //.................inverse transformation...................................................... + f0 = 0.05263157894736842 * rho - 0.012531328320802 * m1 + + 0.04761904761904762 * m2; + f1 = 0.05263157894736842 * rho - 0.004594820384294068 * m1 - + 0.01587301587301587 * m2 + 0.1 * (jx - m4) + + 0.0555555555555555555555555 * (m9 - m10); + f2 = 0.05263157894736842 * rho - 0.004594820384294068 * m1 - + 0.01587301587301587 * m2 + 0.1 * (m4 - jx) + + 0.0555555555555555555555555 * (m9 - m10); + f3 = 0.05263157894736842 * rho - 0.004594820384294068 * m1 - + 0.01587301587301587 * m2 + 0.1 * (jy - m6) + + 0.02777777777777778 * (m10 - m9) + + 0.08333333333333333 * (m11 - m12); + f4 = 0.05263157894736842 * rho - 0.004594820384294068 * m1 - + 0.01587301587301587 * m2 + 0.1 * (m6 - jy) + + 0.02777777777777778 * (m10 - m9) + + 0.08333333333333333 * (m11 - m12); + f5 = 0.05263157894736842 * rho - 0.004594820384294068 * m1 - + 0.01587301587301587 * m2 + 0.1 * (jz - m8) + + 0.02777777777777778 * (m10 - m9) + + 0.08333333333333333 * (m12 - m11); + f6 = 0.05263157894736842 * rho - 0.004594820384294068 * m1 - + 0.01587301587301587 * m2 + 0.1 * (m8 - jz) + + 0.02777777777777778 * (m10 - m9) + + 0.08333333333333333 * (m12 - m11); + f7 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jx + jy) + + 0.025 * (m4 + m6) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 + 0.08333333333333333 * m11 + + 0.04166666666666666 * m12 + 0.25 * m13 + 0.125 * (m16 - m17); + f8 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 - 0.1 * (jx + jy) - + 0.025 * (m4 + m6) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 + 0.08333333333333333 * m11 + + 0.04166666666666666 * m12 + 0.25 * m13 + 0.125 * (m17 - m16); + f9 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jx - jy) + + 0.025 * (m4 - m6) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 + 0.08333333333333333 * m11 + + 0.04166666666666666 * m12 - 0.25 * m13 + 0.125 * (m16 + m17); + f10 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jy - jx) + + 0.025 * (m6 - m4) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 + 0.08333333333333333 * m11 + + 0.04166666666666666 * m12 - 0.25 * m13 - 0.125 * (m16 + m17); + f11 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jx + jz) + + 0.025 * (m4 + m8) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 - 0.08333333333333333 * m11 - + 0.04166666666666666 * m12 + 0.25 * m15 + 0.125 * (m18 - m16); + f12 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 - 0.1 * (jx + jz) - + 0.025 * (m4 + m8) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 - 0.08333333333333333 * m11 - + 0.04166666666666666 * m12 + 0.25 * m15 + 0.125 * (m16 - m18); + f13 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jx - jz) + + 0.025 * (m4 - m8) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 - 0.08333333333333333 * m11 - + 0.04166666666666666 * m12 - 0.25 * m15 - 0.125 * (m16 + m18); + f14 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jz - jx) + + 0.025 * (m8 - m4) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 - 0.08333333333333333 * m11 - + 0.04166666666666666 * m12 - 0.25 * m15 + 0.125 * (m16 + m18); + f15 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jy + jz) + + 0.025 * (m6 + m8) - 0.0555555555555555555555555 * m9 - + 0.02777777777777778 * m10 + 0.25 * m14 + 0.125 * (m17 - m18); + f16 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 - 0.1 * (jy + jz) - + 0.025 * (m6 + m8) - 0.0555555555555555555555555 * m9 - + 0.02777777777777778 * m10 + 0.25 * m14 + 0.125 * (m18 - m17); + f17 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jy - jz) + + 0.025 * (m6 - m8) - 0.0555555555555555555555555 * m9 - + 0.02777777777777778 * m10 - 0.25 * m14 + 0.125 * (m17 + m18); + f18 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jz - jy) + + 0.025 * (m8 - m6) - 0.0555555555555555555555555 * m9 - + 0.02777777777777778 * m10 - 0.25 * m14 - 0.125 * (m17 + m18); + //....................................................................................................... + // incorporate external force + f1 += 0.16666666 * Fx; + f2 -= 0.16666666 * Fx; + f3 += 0.16666666 * Fy; + f4 -= 0.16666666 * Fy; + f5 += 0.16666666 * Fz; + f6 -= 0.16666666 * Fz; + f7 += 0.08333333333 * (Fx + Fy); + f8 -= 0.08333333333 * (Fx + Fy); + f9 += 0.08333333333 * (Fx - Fy); + f10 -= 0.08333333333 * (Fx - Fy); + f11 += 0.08333333333 * (Fx + Fz); + f12 -= 0.08333333333 * (Fx + Fz); + f13 += 0.08333333333 * (Fx - Fz); + f14 -= 0.08333333333 * (Fx - Fz); + f15 += 0.08333333333 * (Fy + Fz); + f16 -= 0.08333333333 * (Fy + Fz); + f17 += 0.08333333333 * (Fy - Fz); + f18 -= 0.08333333333 * (Fy - Fz); + //*********** WRITE UPDATED VALUES TO MEMORY ****************** + // Write the updated distributions + //....EVEN..................................... + disteven[n] = f0; + disteven[N + n] = f2; + disteven[2 * N + n] = f4; + disteven[3 * N + n] = f6; + disteven[4 * N + n] = f8; + disteven[5 * N + n] = f10; + disteven[6 * N + n] = f12; + disteven[7 * N + n] = f14; + disteven[8 * N + n] = f16; + disteven[9 * N + n] = f18; + //....ODD...................................... + distodd[n] = f1; + distodd[N + n] = f3; + distodd[2 * N + n] = f5; + distodd[3 * N + n] = f7; + distodd[4 * N + n] = f9; + distodd[5 * N + n] = f11; + distodd[6 * N + n] = f13; + distodd[7 * N + n] = f15; + distodd[8 * N + n] = f17; - //...Store the Velocity.......................... - Velocity[n] = jx; - Velocity[N+n] = jy; - Velocity[2*N+n] = jz; - /* Velocity[3*n] = jx; + //...Store the Velocity.......................... + Velocity[n] = jx; + Velocity[N + n] = jy; + Velocity[2 * N + n] = jz; + /* Velocity[3*n] = jx; Velocity[3*n+1] = jy; Velocity[3*n+2] = jz; */ //...Store the Color Gradient.................... @@ -526,227 +610,261 @@ extern "C" void ColorCollide( char *ID, double *disteven, double *distodd, doubl // ColorGrad[3*n+2] = nz*C; //............................................... //*************************************************************** - } // check if n is in the solid - } // loop over n + } // check if n is in the solid + } // loop over n } -extern "C" void ScaLBL_D3Q19_ColorCollide( char *ID, double *disteven, double *distodd, double *phi, double *ColorGrad, - double *Velocity, int Nx, int Ny, int Nz, double rlx_setA, double rlx_setB, - double alpha, double beta, double Fx, double Fy, double Fz) -{ - - int i,j,k,n,nn,N; - // distributions - double f0,f1,f2,f3,f4,f5,f6,f7,f8,f9; - double f10,f11,f12,f13,f14,f15,f16,f17,f18; +extern "C" void ScaLBL_D3Q19_ColorCollide( + char *ID, double *disteven, double *distodd, double *phi, double *ColorGrad, + double *Velocity, int Nx, int Ny, int Nz, double rlx_setA, double rlx_setB, + double alpha, double beta, double Fx, double Fy, double Fz) { - // non-conserved moments - double m1,m2,m4,m6,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18; - // additional variables needed for computations - double rho,jx,jy,jz,C,nx,ny,nz; + int i, j, k, n, nn, N; + // distributions + double f0, f1, f2, f3, f4, f5, f6, f7, f8, f9; + double f10, f11, f12, f13, f14, f15, f16, f17, f18; - N = Nx*Ny*Nz; - char id; + // non-conserved moments + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; + // additional variables needed for computations + double rho, jx, jy, jz, C, nx, ny, nz; - for (n=0; n 0){ + id = ID[n]; - //.......Back out the 3-D indices for node n.............. - k = n/(Nx*Ny); - j = (n-Nx*Ny*k)/Nx; - i = n-Nx*Ny*k-Nx*j; - //........................................................................ - //........Get 1-D index for this thread.................... - // n = S*blockIdx.x*blockDim.x + s*blockDim.x + threadIdx.x; - //........................................................................ - // COMPUTE THE COLOR GRADIENT - //........................................................................ - //.................Read Phase Indicator Values............................ - //........................................................................ - nn = n-1; // neighbor index (get convention) - if (i-1<0) nn += Nx; // periodic BC along the x-boundary - f1 = phi[nn]; // get neighbor for phi - 1 - //........................................................................ - nn = n+1; // neighbor index (get convention) - if (!(i+1 0) { - /* f2 = distodd[n]; + //.......Back out the 3-D indices for node n.............. + k = n / (Nx * Ny); + j = (n - Nx * Ny * k) / Nx; + i = n - Nx * Ny * k - Nx * j; + //........................................................................ + //........Get 1-D index for this thread.................... + // n = S*blockIdx.x*blockDim.x + s*blockDim.x + threadIdx.x; + //........................................................................ + // COMPUTE THE COLOR GRADIENT + //........................................................................ + //.................Read Phase Indicator Values............................ + //........................................................................ + nn = n - 1; // neighbor index (get convention) + if (i - 1 < 0) + nn += Nx; // periodic BC along the x-boundary + f1 = phi[nn]; // get neighbor for phi - 1 + //........................................................................ + nn = n + 1; // neighbor index (get convention) + if (!(i + 1 < Nx)) + nn -= Nx; // periodic BC along the x-boundary + f2 = phi[nn]; // get neighbor for phi - 2 + //........................................................................ + nn = n - Nx; // neighbor index (get convention) + if (j - 1 < 0) + nn += Nx * Ny; // Perioidic BC along the y-boundary + f3 = phi[nn]; // get neighbor for phi - 3 + //........................................................................ + nn = n + Nx; // neighbor index (get convention) + if (!(j + 1 < Ny)) + nn -= Nx * Ny; // Perioidic BC along the y-boundary + f4 = phi[nn]; // get neighbor for phi - 4 + //........................................................................ + nn = n - Nx * Ny; // neighbor index (get convention) + if (k - 1 < 0) + nn += Nx * Ny * Nz; // Perioidic BC along the z-boundary + f5 = phi[nn]; // get neighbor for phi - 5 + //........................................................................ + nn = n + Nx * Ny; // neighbor index (get convention) + if (!(k + 1 < Nz)) + nn -= Nx * Ny * Nz; // Perioidic BC along the z-boundary + f6 = phi[nn]; // get neighbor for phi - 6 + //........................................................................ + nn = n - Nx - 1; // neighbor index (get convention) + if (i - 1 < 0) + nn += Nx; // periodic BC along the x-boundary + if (j - 1 < 0) + nn += Nx * Ny; // Perioidic BC along the y-boundary + f7 = phi[nn]; // get neighbor for phi - 7 + //........................................................................ + nn = n + Nx + 1; // neighbor index (get convention) + if (!(i + 1 < Nx)) + nn -= Nx; // periodic BC along the x-boundary + if (!(j + 1 < Ny)) + nn -= Nx * Ny; // Perioidic BC along the y-boundary + f8 = phi[nn]; // get neighbor for phi - 8 + //........................................................................ + nn = n + Nx - 1; // neighbor index (get convention) + if (i - 1 < 0) + nn += Nx; // periodic BC along the x-boundary + if (!(j + 1 < Ny)) + nn -= Nx * Ny; // Perioidic BC along the y-boundary + f9 = phi[nn]; // get neighbor for phi - 9 + //........................................................................ + nn = n - Nx + 1; // neighbor index (get convention) + if (!(i + 1 < Nx)) + nn -= Nx; // periodic BC along the x-boundary + if (j - 1 < 0) + nn += Nx * Ny; // Perioidic BC along the y-boundary + f10 = phi[nn]; // get neighbor for phi - 10 + //........................................................................ + nn = n - Nx * Ny - 1; // neighbor index (get convention) + if (i - 1 < 0) + nn += Nx; // periodic BC along the x-boundary + if (k - 1 < 0) + nn += Nx * Ny * Nz; // Perioidic BC along the z-boundary + f11 = phi[nn]; // get neighbor for phi - 11 + //........................................................................ + nn = n + Nx * Ny + 1; // neighbor index (get convention) + if (!(i + 1 < Nx)) + nn -= Nx; // periodic BC along the x-boundary + if (!(k + 1 < Nz)) + nn -= Nx * Ny * Nz; // Perioidic BC along the z-boundary + f12 = phi[nn]; // get neighbor for phi - 12 + //........................................................................ + nn = n + Nx * Ny - 1; // neighbor index (get convention) + if (i - 1 < 0) + nn += Nx; // periodic BC along the x-boundary + if (!(k + 1 < Nz)) + nn -= Nx * Ny * Nz; // Perioidic BC along the z-boundary + f13 = phi[nn]; // get neighbor for phi - 13 + //........................................................................ + nn = n - Nx * Ny + 1; // neighbor index (get convention) + if (!(i + 1 < Nx)) + nn -= Nx; // periodic BC along the x-boundary + if (k - 1 < 0) + nn += Nx * Ny * Nz; // Perioidic BC along the z-boundary + f14 = phi[nn]; // get neighbor for phi - 14 + //........................................................................ + nn = n - Nx * Ny - Nx; // neighbor index (get convention) + if (j - 1 < 0) + nn += Nx * Ny; // Perioidic BC along the y-boundary + if (k - 1 < 0) + nn += Nx * Ny * Nz; // Perioidic BC along the z-boundary + f15 = phi[nn]; // get neighbor for phi - 15 + //........................................................................ + nn = n + Nx * Ny + Nx; // neighbor index (get convention) + if (!(j + 1 < Ny)) + nn -= Nx * Ny; // Perioidic BC along the y-boundary + if (!(k + 1 < Nz)) + nn -= Nx * Ny * Nz; // Perioidic BC along the z-boundary + f16 = phi[nn]; // get neighbor for phi - 16 + //........................................................................ + nn = n + Nx * Ny - Nx; // neighbor index (get convention) + if (j - 1 < 0) + nn += Nx * Ny; // Perioidic BC along the y-boundary + if (!(k + 1 < Nz)) + nn -= Nx * Ny * Nz; // Perioidic BC along the z-boundary + f17 = phi[nn]; // get neighbor for phi - 17 + //........................................................................ + nn = n - Nx * Ny + Nx; // neighbor index (get convention) + if (!(j + 1 < Ny)) + nn -= Nx * Ny; // Perioidic BC along the y-boundary + if (k - 1 < 0) + nn += Nx * Ny * Nz; // Perioidic BC along the z-boundary + f18 = phi[nn]; // get neighbor for phi - 18 + //............Compute the Color Gradient................................... + nx = + -(f1 - f2 + 0.5 * (f7 - f8 + f9 - f10 + f11 - f12 + f13 - f14)); + ny = + -(f3 - f4 + 0.5 * (f7 - f8 - f9 + f10 + f15 - f16 + f17 - f18)); + nz = -(f5 - f6 + + 0.5 * (f11 - f12 - f13 + f14 + f15 - f16 - f17 + f18)); + //...Store the Color Gradient.................... + ColorGrad[n] = nx; + ColorGrad[N + n] = ny; + ColorGrad[2 * N + n] = nz; + //............................................... + //...........Normalize the Color Gradient................................. + C = sqrt(nx * nx + ny * ny + nz * nz); + if (C == 0.0) + C = 1.0; + nx = nx / C; + ny = ny / C; + nz = nz / C; + //......No color gradient at z-boundary if pressure BC are set............. + // if (pBC && k==0) nx = ny = nz = 0.f; + // if (pBC && k==Nz-1) nx = ny = nz = 0.f; + //........................................................................ + // READ THE DISTRIBUTIONS + // (read from opposite array due to previous swap operation) + //........................................................................ + f2 = distodd[n]; + f4 = distodd[N + n]; + f6 = distodd[2 * N + n]; + f0 = disteven[n]; + f1 = disteven[N + n]; + f3 = disteven[2 * N + n]; + f5 = disteven[3 * N + n]; + //........................................................................ + //....................compute the moments............................................... + rho = f0 + f2 + f1 + f4 + f3 + f6 + f5; + m1 = -30 * f0 - 11 * (f2 + f1 + f4 + f3 + f6 + f5); + m2 = 12 * f0 - 4 * (f2 + f1 + f4 + f3 + f6 + f5); + jx = f1 - f2; + m4 = 4 * (-f1 + f2); + jy = f3 - f4; + m6 = -4 * (f3 - f4); + jz = f5 - f6; + m8 = -4 * (f5 - f6); + m9 = 2 * (f1 + f2) - f3 - f4 - f5 - f6; + m10 = -4 * (f1 + f2) + 2 * (f4 + f3 + f6 + f5); + m11 = f4 + f3 - f6 - f5; + m12 = -2 * (f4 + f3 - f6 - f5); + //........................................................................ + f8 = distodd[3 * N + n]; + f10 = distodd[4 * N + n]; + f7 = disteven[4 * N + n]; + f9 = disteven[5 * N + n]; + //........................................................................ + rho += f8 + f7 + f10 + f9; + m1 += 8 * (f8 + f7 + f10 + f9); + m2 += f8 + f7 + f10 + f9; + jx += f7 - f8 + f9 - f10; + m4 += f7 - f8 + f9 - f10; + jy += f7 - f8 - f9 + f10; + m6 += f7 - f8 - f9 + f10; + m9 += f7 + f8 + f9 + f10; + m10 += f8 + f7 + f10 + f9; + m11 += f8 + f7 + f10 + f9; + m12 += f8 + f7 + f10 + f9; + m13 = f8 + f7 - f10 - f9; + m16 = f7 - f8 + f9 - f10; + m17 = -f7 + f8 + f9 - f10; + //........................................................................ + f11 = disteven[6 * N + n]; + f13 = disteven[7 * N + n]; + f12 = distodd[5 * N + n]; + f14 = distodd[6 * N + n]; + //........................................................................ + //........................................................................ + f15 = disteven[8 * N + n]; + f17 = disteven[9 * N + n]; + f16 = distodd[7 * N + n]; + f18 = distodd[8 * N + n]; + //........................................................................ + //....................compute the moments............................................... + rho += f12 + f11 + f14 + f13 + f16 + f15 + f18 + f17; + m1 += 8 * (f12 + f11 + f14 + f13 + f16 + f15 + f18 + f17); + m2 += f12 + f11 + f14 + f13 + f16 + f15 + f18 + f17; + jx += f11 - f12 + f13 - f14; + m4 += f11 - f12 + f13 - f14; + jy += f15 - f16 + f17 - f18; + m6 += f15 - f16 + f17 - f18; + jz += f11 - f12 - f13 + f14 + f15 - f16 - f17 + f18; + m8 += f11 - f12 - f13 + f14 + f15 - f16 - f17 + f18; + m9 += f11 + f12 + f13 + f14 - 2 * (f15 + f16 + f17 + f18); + m10 += f12 + f11 + f14 + f13 - 2 * (f16 + f15 + f18 + f17); + m11 += -f12 - f11 - f14 - f13; + m12 += -f12 - f11 - f14 - f13; + m14 = f16 + f15 - f18 - f17; + m15 = f12 + f11 - f14 - f13; + m16 += -f11 + f12 - f13 + f14; + m17 += f15 - f16 + f17 - f18; + m18 = f11 - f12 - f13 + f14 - f15 + f16 + f17 - f18; + //........................................................................ + + /* f2 = distodd[n]; f4 = distodd[N+n]; f6 = distodd[2*N+n]; f8 = distodd[3*N+n]; @@ -807,244 +925,291 @@ extern "C" void ScaLBL_D3Q19_ColorCollide( char *ID, double *disteven, double *d m17 += f9-f10+f15-f16+f17-f18; m18 = f11-f12-f13+f14-f15+f16+f17-f18; */ //........................................................................ - // PERFORM RELAXATION PROCESS - //........................................................................ - //..........Toelke, Fruediger et. al. 2006............... - if (C == 0.0) nx = ny = nz = 0.0; - m1 = m1 + rlx_setA*((19*(jx*jx+jy*jy+jz*jz)/rho - 11*rho) -alpha*C - m1); - m2 = m2 + rlx_setA*((3*rho - 5.5*(jx*jx+jy*jy+jz*jz)/rho)- m2); - m4 = m4 + rlx_setB*((-0.6666666666666666*jx)- m4); - m6 = m6 + rlx_setB*((-0.6666666666666666*jy)- m6); - m8 = m8 + rlx_setB*((-0.6666666666666666*jz)- m8); - m9 = m9 + rlx_setA*(((2*jx*jx-jy*jy-jz*jz)/rho) + 0.5*alpha*C*(2*nx*nx-ny*ny-nz*nz) - m9); - m10 = m10 + rlx_setA*( - m10); - m11 = m11 + rlx_setA*(((jy*jy-jz*jz)/rho) + 0.5*alpha*C*(ny*ny-nz*nz)- m11); - m12 = m12 + rlx_setA*( - m12); - m13 = m13 + rlx_setA*( (jx*jy/rho) + 0.5*alpha*C*nx*ny - m13); - m14 = m14 + rlx_setA*( (jy*jz/rho) + 0.5*alpha*C*ny*nz - m14); - m15 = m15 + rlx_setA*( (jx*jz/rho) + 0.5*alpha*C*nx*nz - m15); - m16 = m16 + rlx_setB*( - m16); - m17 = m17 + rlx_setB*( - m17); - m18 = m18 + rlx_setB*( - m18); - //.................inverse transformation...................................................... - f0 = 0.05263157894736842*rho-0.012531328320802*m1+0.04761904761904762*m2; - f1 = 0.05263157894736842*rho-0.004594820384294068*m1-0.01587301587301587*m2 - +0.1*(jx-m4)+0.0555555555555555555555555*(m9-m10); - f2 = 0.05263157894736842*rho-0.004594820384294068*m1-0.01587301587301587*m2 - +0.1*(m4-jx)+0.0555555555555555555555555*(m9-m10); - f3 = 0.05263157894736842*rho-0.004594820384294068*m1-0.01587301587301587*m2 - +0.1*(jy-m6)+0.02777777777777778*(m10-m9)+0.08333333333333333*(m11-m12); - f4 = 0.05263157894736842*rho-0.004594820384294068*m1-0.01587301587301587*m2 - +0.1*(m6-jy)+0.02777777777777778*(m10-m9)+0.08333333333333333*(m11-m12); - f5 = 0.05263157894736842*rho-0.004594820384294068*m1-0.01587301587301587*m2 - +0.1*(jz-m8)+0.02777777777777778*(m10-m9)+0.08333333333333333*(m12-m11); - f6 = 0.05263157894736842*rho-0.004594820384294068*m1-0.01587301587301587*m2 - +0.1*(m8-jz)+0.02777777777777778*(m10-m9)+0.08333333333333333*(m12-m11); - f7 = 0.05263157894736842*rho+0.003341687552213868*m1+0.003968253968253968*m2+0.1*(jx+jy)+0.025*(m4+m6) - +0.02777777777777778*m9+0.01388888888888889*m10+0.08333333333333333*m11 - +0.04166666666666666*m12+0.25*m13+0.125*(m16-m17); - f8 = 0.05263157894736842*rho+0.003341687552213868*m1+0.003968253968253968*m2-0.1*(jx+jy)-0.025*(m4+m6) - +0.02777777777777778*m9+0.01388888888888889*m10+0.08333333333333333*m11 - +0.04166666666666666*m12+0.25*m13+0.125*(m17-m16); - f9 = 0.05263157894736842*rho+0.003341687552213868*m1+0.003968253968253968*m2+0.1*(jx-jy)+0.025*(m4-m6) - +0.02777777777777778*m9+0.01388888888888889*m10+0.08333333333333333*m11 - +0.04166666666666666*m12-0.25*m13+0.125*(m16+m17); - f10 = 0.05263157894736842*rho+0.003341687552213868*m1+0.003968253968253968*m2+0.1*(jy-jx)+0.025*(m6-m4) - +0.02777777777777778*m9+0.01388888888888889*m10+0.08333333333333333*m11 - +0.04166666666666666*m12-0.25*m13-0.125*(m16+m17); - f11 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2+0.1*(jx+jz)+0.025*(m4+m8) - +0.02777777777777778*m9+0.01388888888888889*m10-0.08333333333333333*m11 - -0.04166666666666666*m12+0.25*m15+0.125*(m18-m16); - f12 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2-0.1*(jx+jz)-0.025*(m4+m8) - +0.02777777777777778*m9+0.01388888888888889*m10-0.08333333333333333*m11 - -0.04166666666666666*m12+0.25*m15+0.125*(m16-m18); - f13 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2+0.1*(jx-jz)+0.025*(m4-m8) - +0.02777777777777778*m9+0.01388888888888889*m10-0.08333333333333333*m11 - -0.04166666666666666*m12-0.25*m15-0.125*(m16+m18); - f14 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2+0.1*(jz-jx)+0.025*(m8-m4) - +0.02777777777777778*m9+0.01388888888888889*m10-0.08333333333333333*m11 - -0.04166666666666666*m12-0.25*m15+0.125*(m16+m18); - f15 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2+0.1*(jy+jz)+0.025*(m6+m8) - -0.0555555555555555555555555*m9-0.02777777777777778*m10+0.25*m14+0.125*(m17-m18); - f16 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2-0.1*(jy+jz)-0.025*(m6+m8) - -0.0555555555555555555555555*m9-0.02777777777777778*m10+0.25*m14+0.125*(m18-m17); - f17 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2+0.1*(jy-jz)+0.025*(m6-m8) - -0.0555555555555555555555555*m9-0.02777777777777778*m10-0.25*m14+0.125*(m17+m18); - f18 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2+0.1*(jz-jy)+0.025*(m8-m6) - -0.0555555555555555555555555*m9-0.02777777777777778*m10-0.25*m14-0.125*(m17+m18); - //....................................................................................................... - // incorporate external force - f1 += 0.16666666*Fx; - f2 -= 0.16666666*Fx; - f3 += 0.16666666*Fy; - f4 -= 0.16666666*Fy; - f5 += 0.16666666*Fz; - f6 -= 0.16666666*Fz; - f7 += 0.08333333333*(Fx+Fy); - f8 -= 0.08333333333*(Fx+Fy); - f9 += 0.08333333333*(Fx-Fy); - f10 -= 0.08333333333*(Fx-Fy); - f11 += 0.08333333333*(Fx+Fz); - f12 -= 0.08333333333*(Fx+Fz); - f13 += 0.08333333333*(Fx-Fz); - f14 -= 0.08333333333*(Fx-Fz); - f15 += 0.08333333333*(Fy+Fz); - f16 -= 0.08333333333*(Fy+Fz); - f17 += 0.08333333333*(Fy-Fz); - f18 -= 0.08333333333*(Fy-Fz); - //*********** WRITE UPDATED VALUES TO MEMORY ****************** - // Write the updated distributions - //....EVEN..................................... - disteven[n] = f0; - disteven[N+n] = f2; - disteven[2*N+n] = f4; - disteven[3*N+n] = f6; - disteven[4*N+n] = f8; - disteven[5*N+n] = f10; - disteven[6*N+n] = f12; - disteven[7*N+n] = f14; - disteven[8*N+n] = f16; - disteven[9*N+n] = f18; - //....ODD...................................... - distodd[n] = f1; - distodd[N+n] = f3; - distodd[2*N+n] = f5; - distodd[3*N+n] = f7; - distodd[4*N+n] = f9; - distodd[5*N+n] = f11; - distodd[6*N+n] = f13; - distodd[7*N+n] = f15; - distodd[8*N+n] = f17; - //...Store the Velocity.......................... - Velocity[n] = jx; - Velocity[N+n] = jy; - Velocity[2*N+n] = jz; - //*************************************************************** - } // check if n is in the solid - } // loop over n + // PERFORM RELAXATION PROCESS + //........................................................................ + //..........Toelke, Fruediger et. al. 2006............... + if (C == 0.0) + nx = ny = nz = 0.0; + m1 = m1 + rlx_setA * ((19 * (jx * jx + jy * jy + jz * jz) / rho - + 11 * rho) - + alpha * C - m1); + m2 = m2 + rlx_setA * ((3 * rho - + 5.5 * (jx * jx + jy * jy + jz * jz) / rho) - + m2); + m4 = m4 + rlx_setB * ((-0.6666666666666666 * jx) - m4); + m6 = m6 + rlx_setB * ((-0.6666666666666666 * jy) - m6); + m8 = m8 + rlx_setB * ((-0.6666666666666666 * jz) - m8); + m9 = m9 + + rlx_setA * + (((2 * jx * jx - jy * jy - jz * jz) / rho) + + 0.5 * alpha * C * (2 * nx * nx - ny * ny - nz * nz) - m9); + m10 = m10 + rlx_setA * (-m10); + m11 = + m11 + rlx_setA * (((jy * jy - jz * jz) / rho) + + 0.5 * alpha * C * (ny * ny - nz * nz) - m11); + m12 = m12 + rlx_setA * (-m12); + m13 = m13 + rlx_setA * + ((jx * jy / rho) + 0.5 * alpha * C * nx * ny - m13); + m14 = m14 + rlx_setA * + ((jy * jz / rho) + 0.5 * alpha * C * ny * nz - m14); + m15 = m15 + rlx_setA * + ((jx * jz / rho) + 0.5 * alpha * C * nx * nz - m15); + m16 = m16 + rlx_setB * (-m16); + m17 = m17 + rlx_setB * (-m17); + m18 = m18 + rlx_setB * (-m18); + //.................inverse transformation...................................................... + f0 = 0.05263157894736842 * rho - 0.012531328320802 * m1 + + 0.04761904761904762 * m2; + f1 = 0.05263157894736842 * rho - 0.004594820384294068 * m1 - + 0.01587301587301587 * m2 + 0.1 * (jx - m4) + + 0.0555555555555555555555555 * (m9 - m10); + f2 = 0.05263157894736842 * rho - 0.004594820384294068 * m1 - + 0.01587301587301587 * m2 + 0.1 * (m4 - jx) + + 0.0555555555555555555555555 * (m9 - m10); + f3 = 0.05263157894736842 * rho - 0.004594820384294068 * m1 - + 0.01587301587301587 * m2 + 0.1 * (jy - m6) + + 0.02777777777777778 * (m10 - m9) + + 0.08333333333333333 * (m11 - m12); + f4 = 0.05263157894736842 * rho - 0.004594820384294068 * m1 - + 0.01587301587301587 * m2 + 0.1 * (m6 - jy) + + 0.02777777777777778 * (m10 - m9) + + 0.08333333333333333 * (m11 - m12); + f5 = 0.05263157894736842 * rho - 0.004594820384294068 * m1 - + 0.01587301587301587 * m2 + 0.1 * (jz - m8) + + 0.02777777777777778 * (m10 - m9) + + 0.08333333333333333 * (m12 - m11); + f6 = 0.05263157894736842 * rho - 0.004594820384294068 * m1 - + 0.01587301587301587 * m2 + 0.1 * (m8 - jz) + + 0.02777777777777778 * (m10 - m9) + + 0.08333333333333333 * (m12 - m11); + f7 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jx + jy) + + 0.025 * (m4 + m6) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 + 0.08333333333333333 * m11 + + 0.04166666666666666 * m12 + 0.25 * m13 + 0.125 * (m16 - m17); + f8 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 - 0.1 * (jx + jy) - + 0.025 * (m4 + m6) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 + 0.08333333333333333 * m11 + + 0.04166666666666666 * m12 + 0.25 * m13 + 0.125 * (m17 - m16); + f9 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jx - jy) + + 0.025 * (m4 - m6) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 + 0.08333333333333333 * m11 + + 0.04166666666666666 * m12 - 0.25 * m13 + 0.125 * (m16 + m17); + f10 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jy - jx) + + 0.025 * (m6 - m4) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 + 0.08333333333333333 * m11 + + 0.04166666666666666 * m12 - 0.25 * m13 - 0.125 * (m16 + m17); + f11 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jx + jz) + + 0.025 * (m4 + m8) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 - 0.08333333333333333 * m11 - + 0.04166666666666666 * m12 + 0.25 * m15 + 0.125 * (m18 - m16); + f12 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 - 0.1 * (jx + jz) - + 0.025 * (m4 + m8) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 - 0.08333333333333333 * m11 - + 0.04166666666666666 * m12 + 0.25 * m15 + 0.125 * (m16 - m18); + f13 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jx - jz) + + 0.025 * (m4 - m8) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 - 0.08333333333333333 * m11 - + 0.04166666666666666 * m12 - 0.25 * m15 - 0.125 * (m16 + m18); + f14 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jz - jx) + + 0.025 * (m8 - m4) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 - 0.08333333333333333 * m11 - + 0.04166666666666666 * m12 - 0.25 * m15 + 0.125 * (m16 + m18); + f15 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jy + jz) + + 0.025 * (m6 + m8) - 0.0555555555555555555555555 * m9 - + 0.02777777777777778 * m10 + 0.25 * m14 + 0.125 * (m17 - m18); + f16 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 - 0.1 * (jy + jz) - + 0.025 * (m6 + m8) - 0.0555555555555555555555555 * m9 - + 0.02777777777777778 * m10 + 0.25 * m14 + 0.125 * (m18 - m17); + f17 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jy - jz) + + 0.025 * (m6 - m8) - 0.0555555555555555555555555 * m9 - + 0.02777777777777778 * m10 - 0.25 * m14 + 0.125 * (m17 + m18); + f18 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jz - jy) + + 0.025 * (m8 - m6) - 0.0555555555555555555555555 * m9 - + 0.02777777777777778 * m10 - 0.25 * m14 - 0.125 * (m17 + m18); + //....................................................................................................... + // incorporate external force + f1 += 0.16666666 * Fx; + f2 -= 0.16666666 * Fx; + f3 += 0.16666666 * Fy; + f4 -= 0.16666666 * Fy; + f5 += 0.16666666 * Fz; + f6 -= 0.16666666 * Fz; + f7 += 0.08333333333 * (Fx + Fy); + f8 -= 0.08333333333 * (Fx + Fy); + f9 += 0.08333333333 * (Fx - Fy); + f10 -= 0.08333333333 * (Fx - Fy); + f11 += 0.08333333333 * (Fx + Fz); + f12 -= 0.08333333333 * (Fx + Fz); + f13 += 0.08333333333 * (Fx - Fz); + f14 -= 0.08333333333 * (Fx - Fz); + f15 += 0.08333333333 * (Fy + Fz); + f16 -= 0.08333333333 * (Fy + Fz); + f17 += 0.08333333333 * (Fy - Fz); + f18 -= 0.08333333333 * (Fy - Fz); + //*********** WRITE UPDATED VALUES TO MEMORY ****************** + // Write the updated distributions + //....EVEN..................................... + disteven[n] = f0; + disteven[N + n] = f2; + disteven[2 * N + n] = f4; + disteven[3 * N + n] = f6; + disteven[4 * N + n] = f8; + disteven[5 * N + n] = f10; + disteven[6 * N + n] = f12; + disteven[7 * N + n] = f14; + disteven[8 * N + n] = f16; + disteven[9 * N + n] = f18; + //....ODD...................................... + distodd[n] = f1; + distodd[N + n] = f3; + distodd[2 * N + n] = f5; + distodd[3 * N + n] = f7; + distodd[4 * N + n] = f9; + distodd[5 * N + n] = f11; + distodd[6 * N + n] = f13; + distodd[7 * N + n] = f15; + distodd[8 * N + n] = f17; + //...Store the Velocity.......................... + Velocity[n] = jx; + Velocity[N + n] = jy; + Velocity[2 * N + n] = jz; + //*************************************************************** + } // check if n is in the solid + } // loop over n } -extern "C" void ScaLBL_D3Q7_ColorCollideMass(char *ID, double *A_even, double *A_odd, double *B_even, double *B_odd, - double *Den, double *Phi, double *ColorGrad, double *Velocity, double beta, int N, bool pBC) -{ - int n; - char id; - double f0,f1,f2,f3,f4,f5,f6; - double na,nb,nab; // density values - double ux,uy,uz; // flow velocity - double nx,ny,nz,C; // color gradient components - double a1,a2,b1,b2; - double delta; - //double feq[6]; // equilibrium distributions - // Set of Discrete velocities for the D3Q19 Model - //int D3Q7[3][3]={{1,0,0},{0,1,0},{0,0,1}}; +extern "C" void ScaLBL_D3Q7_ColorCollideMass(char *ID, double *A_even, + double *A_odd, double *B_even, + double *B_odd, double *Den, + double *Phi, double *ColorGrad, + double *Velocity, double beta, + int N, bool pBC) { + int n; + char id; + double f0, f1, f2, f3, f4, f5, f6; + double na, nb, nab; // density values + double ux, uy, uz; // flow velocity + double nx, ny, nz, C; // color gradient components + double a1, a2, b1, b2; + double delta; + //double feq[6]; // equilibrium distributions + // Set of Discrete velocities for the D3Q19 Model + //int D3Q7[3][3]={{1,0,0},{0,1,0},{0,0,1}}; - for (n=0; n0)) delta=0; - a1 = na*(0.1111111111111111*(1+4.5*ux))+delta; - b1 = nb*(0.1111111111111111*(1+4.5*ux))-delta; - a2 = na*(0.1111111111111111*(1-4.5*ux))-delta; - b2 = nb*(0.1111111111111111*(1-4.5*ux))+delta; + //.....Load the Color gradient......... + nx = ColorGrad[n]; + ny = ColorGrad[N + n]; + nz = ColorGrad[2 * N + n]; + C = sqrt(nx * nx + ny * ny + nz * nz); + if (C == 0.0) + C = 1.0; + nx = nx / C; + ny = ny / C; + nz = nz / C; + //....Load the flow velocity........... + ux = Velocity[n]; + uy = Velocity[N + n]; + uz = Velocity[2 * N + n]; + //........................................................................ + // READ THE DISTRIBUTIONS + // (read from opposite array due to previous swap operation) + //........................................................................ + f2 = A_odd[n]; + f4 = A_odd[N + n]; + f6 = A_odd[2 * N + n]; + f0 = A_even[n]; + f1 = A_even[N + n]; + f3 = A_even[2 * N + n]; + f5 = A_even[3 * N + n]; + na = f0 + f1 + f2 + f3 + f4 + f5 + f6; + //........................................................................ + f2 = B_odd[n]; + f4 = B_odd[N + n]; + f6 = B_odd[2 * N + n]; + f0 = B_even[n]; + f1 = B_even[N + n]; + f3 = B_even[2 * N + n]; + f5 = B_even[3 * N + n]; + nb = f0 + f1 + f2 + f3 + f4 + f5 + f6; + nab = 1.0 / (na + nb); + //........................................................................ + //....Instantiate the density distributions + // Generate Equilibrium Distributions and stream + // Stationary value - distribution 0 + A_even[n] = 0.3333333333333333 * na; + B_even[n] = 0.3333333333333333 * nb; + // Non-Stationary equilibrium distributions + //feq[0] = 0.1111111111111111*(1+4.5*ux); + //feq[1] = 0.1111111111111111*(1-4.5*ux); + //feq[2] = 0.1111111111111111*(1+4.5*uy); + //feq[3] = 0.1111111111111111*(1-4.5*uy); + //feq[4] = 0.1111111111111111*(1+4.5*uz); + //feq[5] = 0.1111111111111111*(1-4.5*uz); - A_odd[n] = a1; - A_even[N+n] = a2; - B_odd[n] = b1; - B_even[N+n] = b2; - //............................................... - // q = 2 - // Cq = {0,1,0} - delta = beta*na*nb*nab*0.1111111111111111*ny; - if (!(na*nb*nab>0)) delta=0; - a1 = na*(0.1111111111111111*(1+4.5*uy))+delta; - b1 = nb*(0.1111111111111111*(1+4.5*uy))-delta; - a2 = na*(0.1111111111111111*(1-4.5*uy))-delta; - b2 = nb*(0.1111111111111111*(1-4.5*uy))+delta; + //............................................... + // q = 0,2,4 + // Cq = {1,0,0}, {0,1,0}, {0,0,1} + delta = beta * na * nb * nab * 0.1111111111111111 * nx; + if (!(na * nb * nab > 0)) + delta = 0; + a1 = na * (0.1111111111111111 * (1 + 4.5 * ux)) + delta; + b1 = nb * (0.1111111111111111 * (1 + 4.5 * ux)) - delta; + a2 = na * (0.1111111111111111 * (1 - 4.5 * ux)) - delta; + b2 = nb * (0.1111111111111111 * (1 - 4.5 * ux)) + delta; - A_odd[N+n] = a1; - A_even[2*N+n] = a2; - B_odd[N+n] = b1; - B_even[2*N+n] = b2; - //............................................... - // q = 4 - // Cq = {0,0,1} - delta = beta*na*nb*nab*0.1111111111111111*nz; - if (!(na*nb*nab>0)) delta=0; - a1 = na*(0.1111111111111111*(1+4.5*uz))+delta; - b1 = nb*(0.1111111111111111*(1+4.5*uz))-delta; - a2 = na*(0.1111111111111111*(1-4.5*uz))-delta; - b2 = nb*(0.1111111111111111*(1-4.5*uz))+delta; + A_odd[n] = a1; + A_even[N + n] = a2; + B_odd[n] = b1; + B_even[N + n] = b2; + //............................................... + // q = 2 + // Cq = {0,1,0} + delta = beta * na * nb * nab * 0.1111111111111111 * ny; + if (!(na * nb * nab > 0)) + delta = 0; + a1 = na * (0.1111111111111111 * (1 + 4.5 * uy)) + delta; + b1 = nb * (0.1111111111111111 * (1 + 4.5 * uy)) - delta; + a2 = na * (0.1111111111111111 * (1 - 4.5 * uy)) - delta; + b2 = nb * (0.1111111111111111 * (1 - 4.5 * uy)) + delta; - A_odd[2*N+n] = a1; - A_even[3*N+n] = a2; - B_odd[2*N+n] = b1; - B_even[3*N+n] = b2; - //............................................... + A_odd[N + n] = a1; + A_even[2 * N + n] = a2; + B_odd[N + n] = b1; + B_even[2 * N + n] = b2; + //............................................... + // q = 4 + // Cq = {0,0,1} + delta = beta * na * nb * nab * 0.1111111111111111 * nz; + if (!(na * nb * nab > 0)) + delta = 0; + a1 = na * (0.1111111111111111 * (1 + 4.5 * uz)) + delta; + b1 = nb * (0.1111111111111111 * (1 + 4.5 * uz)) - delta; + a2 = na * (0.1111111111111111 * (1 - 4.5 * uz)) - delta; + b2 = nb * (0.1111111111111111 * (1 - 4.5 * uz)) + delta; - /* // Construction and streaming for the components + A_odd[2 * N + n] = a1; + A_even[3 * N + n] = a2; + B_odd[2 * N + n] = b1; + B_even[3 * N + n] = b2; + //............................................... + + /* // Construction and streaming for the components for (idx=0; idx<3; idx++){ //............................................... // Distribution index @@ -1078,1943 +1243,2050 @@ extern "C" void ScaLBL_D3Q7_ColorCollideMass(char *ID, double *A_even, double *A //............................................... } */ - } - } + } + } } //************************************************************************* -extern "C" void DensityStreamD3Q7(char *ID, double *Den, double *Copy, double *Phi, double *ColorGrad, double *Velocity, - double beta, int Nx, int Ny, int Nz, bool pBC, int S) -{ - char id; +extern "C" void DensityStreamD3Q7(char *ID, double *Den, double *Copy, + double *Phi, double *ColorGrad, + double *Velocity, double beta, int Nx, int Ny, + int Nz, bool pBC, int S) { + char id; - int idx; - int in,jn,kn,n,nn,N; - int q,Cqx,Cqy,Cqz; - // int sendLoc; + int idx; + int in, jn, kn, n, nn, N; + int q, Cqx, Cqy, Cqz; + // int sendLoc; - double na,nb; // density values - double ux,uy,uz; // flow velocity - double nx,ny,nz,C; // color gradient components - double a1,a2,b1,b2; - double sp,delta; - double feq[6]; // equilibrium distributions - // Set of Discrete velocities for the D3Q19 Model - int D3Q7[3][3]={{1,0,0},{0,1,0},{0,0,1}}; - N = Nx*Ny*Nz; + double na, nb; // density values + double ux, uy, uz; // flow velocity + double nx, ny, nz, C; // color gradient components + double a1, a2, b1, b2; + double sp, delta; + double feq[6]; // equilibrium distributions + // Set of Discrete velocities for the D3Q19 Model + int D3Q7[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; + N = Nx * Ny * Nz; - for (n=0; n 0 && na+nb > 0.0){ - //.......Back out the 3-D indices for node n.............. - int k = n/(Nx*Ny); - int j = (n-Nx*Ny*k)/Nx; - int i = n-Nx*Ny*k-Nx*j; - //.....Load the Color gradient......... - nx = ColorGrad[n]; - ny = ColorGrad[N+n]; - nz = ColorGrad[2*N+n]; - C = sqrt(nx*nx+ny*ny+nz*nz); - nx = nx/C; - ny = ny/C; - nz = nz/C; - //....Load the flow velocity........... - ux = Velocity[n]; - uy = Velocity[N+n]; - uz = Velocity[2*N+n]; - //....Instantiate the density distributions - // Generate Equilibrium Distributions and stream - // Stationary value - distribution 0 - // Den[2*n] += 0.3333333333333333*na; - // Den[2*n+1] += 0.3333333333333333*nb; - Den[2*n] += 0.3333333333333333*na; - Den[2*n+1] += 0.3333333333333333*nb; - // Non-Stationary equilibrium distributions - feq[0] = 0.1111111111111111*(1+3*ux); - feq[1] = 0.1111111111111111*(1-3*ux); - feq[2] = 0.1111111111111111*(1+3*uy); - feq[3] = 0.1111111111111111*(1-3*uy); - feq[4] = 0.1111111111111111*(1+3*uz); - feq[5] = 0.1111111111111111*(1-3*uz); - // Construction and streaming for the components - for (idx=0; idx<3; idx++){ - // Distribution index - q = 2*idx; - // Associated discrete velocity - Cqx = D3Q7[idx][0]; - Cqy = D3Q7[idx][1]; - Cqz = D3Q7[idx][2]; - // Generate the Equilibrium Distribution - a1 = na*feq[q]; - b1 = nb*feq[q]; - a2 = na*feq[q+1]; - b2 = nb*feq[q+1]; - // Recolor the distributions - if (C > 0.0){ - sp = nx*double(Cqx)+ny*double(Cqy)+nz*double(Cqz); - //if (idx > 2) sp = 0.7071067811865475*sp; - //delta = sp*min( min(a1,a2), min(b1,b2) ); - delta = na*nb/(na+nb)*0.1111111111111111*sp; - //if (a1>0 && b1>0){ - a1 += beta*delta; - a2 -= beta*delta; - b1 -= beta*delta; - b2 += beta*delta; - } + for (n = 0; n < N; n++) { + id = ID[n]; + // Local Density Values + na = Copy[2 * n]; + nb = Copy[2 * n + 1]; + if (id > 0 && na + nb > 0.0) { + //.......Back out the 3-D indices for node n.............. + int k = n / (Nx * Ny); + int j = (n - Nx * Ny * k) / Nx; + int i = n - Nx * Ny * k - Nx * j; + //.....Load the Color gradient......... + nx = ColorGrad[n]; + ny = ColorGrad[N + n]; + nz = ColorGrad[2 * N + n]; + C = sqrt(nx * nx + ny * ny + nz * nz); + nx = nx / C; + ny = ny / C; + nz = nz / C; + //....Load the flow velocity........... + ux = Velocity[n]; + uy = Velocity[N + n]; + uz = Velocity[2 * N + n]; + //....Instantiate the density distributions + // Generate Equilibrium Distributions and stream + // Stationary value - distribution 0 + // Den[2*n] += 0.3333333333333333*na; + // Den[2*n+1] += 0.3333333333333333*nb; + Den[2 * n] += 0.3333333333333333 * na; + Den[2 * n + 1] += 0.3333333333333333 * nb; + // Non-Stationary equilibrium distributions + feq[0] = 0.1111111111111111 * (1 + 3 * ux); + feq[1] = 0.1111111111111111 * (1 - 3 * ux); + feq[2] = 0.1111111111111111 * (1 + 3 * uy); + feq[3] = 0.1111111111111111 * (1 - 3 * uy); + feq[4] = 0.1111111111111111 * (1 + 3 * uz); + feq[5] = 0.1111111111111111 * (1 - 3 * uz); + // Construction and streaming for the components + for (idx = 0; idx < 3; idx++) { + // Distribution index + q = 2 * idx; + // Associated discrete velocity + Cqx = D3Q7[idx][0]; + Cqy = D3Q7[idx][1]; + Cqz = D3Q7[idx][2]; + // Generate the Equilibrium Distribution + a1 = na * feq[q]; + b1 = nb * feq[q]; + a2 = na * feq[q + 1]; + b2 = nb * feq[q + 1]; + // Recolor the distributions + if (C > 0.0) { + sp = nx * double(Cqx) + ny * double(Cqy) + nz * double(Cqz); + //if (idx > 2) sp = 0.7071067811865475*sp; + //delta = sp*min( min(a1,a2), min(b1,b2) ); + delta = na * nb / (na + nb) * 0.1111111111111111 * sp; + //if (a1>0 && b1>0){ + a1 += beta * delta; + a2 -= beta * delta; + b1 -= beta * delta; + b2 += beta * delta; + } - // .......Get the neighbor node.............. - //nn = n + Stride[idx]; - in = i+Cqx; - jn = j+Cqy; - kn = k+Cqz; + // .......Get the neighbor node.............. + //nn = n + Stride[idx]; + in = i + Cqx; + jn = j + Cqy; + kn = k + Cqz; - // Adjust for periodic BC, if necessary - // if (in<0) in+= Nx; - // if (jn<0) jn+= Ny; - // if (kn<0) kn+= Nz; - // if (!(in 0 ){ - // Get the density value (Streaming already performed) - Na = Den[n]; - Nb = Den[N+n]; - Phi[n] = (Na-Nb)/(Na+Nb); - } - } - //................................................................... + if (ID[n] > 0) { + // Get the density value (Streaming already performed) + Na = Den[n]; + Nb = Den[N + n]; + Phi[n] = (Na - Nb) / (Na + Nb); + } + } + //................................................................... } -extern "C" void ScaLBL_SetSlice_z(double *Phi, double value, int Nx, int Ny, int Nz, int Slice){ - int n; - for (n=Slice*Nx*Ny; n<(Slice+1)*Nx*Ny; n++){ - Phi[n] = value; - } +extern "C" void ScaLBL_SetSlice_z(double *Phi, double value, int Nx, int Ny, + int Nz, int Slice) { + int n; + for (n = Slice * Nx * Ny; n < (Slice + 1) * Nx * Ny; n++) { + Phi[n] = value; + } } - //extern "C" void ScaLBL_D3Q19_AAeven_Color(double *dist, double *Aq, double *Bq, double *Den, double *Velocity, // double *ColorGrad, double rhoA, double rhoB, double tauA, double tauB, double alpha, double beta, // double Fx, double Fy, double Fz, int start, int finish, int Np){ -extern "C" void ScaLBL_D3Q19_AAeven_Color(int *Map, double *dist, double *Aq, double *Bq, double *Den, double *Phi, - double *Vel, double rhoA, double rhoB, double tauA, double tauB, double alpha, double beta, - double Fx, double Fy, double Fz, int strideY, int strideZ, int start, int finish, int Np){ +extern "C" void ScaLBL_D3Q19_AAeven_Color( + int *Map, double *dist, double *Aq, double *Bq, double *Den, double *Phi, + double *Vel, double rhoA, double rhoB, double tauA, double tauB, + double alpha, double beta, double Fx, double Fy, double Fz, int strideY, + int strideZ, int start, int finish, int Np) { - int ijk,nn; - double fq; - // conserved momemnts - double rho,jx,jy,jz; - // non-conserved moments - double m1,m2,m4,m6,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18; - double m3,m5,m7; - double nA,nB; // number density - double a1,b1,a2,b2,nAB,delta; - double C,nx,ny,nz; //color gradient magnitude and direction - double ux,uy,uz; - double phi,tau,rho0,rlx_setA,rlx_setB; - - const double mrt_V1=0.05263157894736842; - const double mrt_V2=0.012531328320802; - const double mrt_V3=0.04761904761904762; - const double mrt_V4=0.004594820384294068; - const double mrt_V5=0.01587301587301587; - const double mrt_V6=0.0555555555555555555555555; - const double mrt_V7=0.02777777777777778; - const double mrt_V8=0.08333333333333333; - const double mrt_V9=0.003341687552213868; - const double mrt_V10=0.003968253968253968; - const double mrt_V11=0.01388888888888889; - const double mrt_V12=0.04166666666666666; + int ijk, nn; + double fq; + // conserved momemnts + double rho, jx, jy, jz; + // non-conserved moments + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; + double m3, m5, m7; + double nA, nB; // number density + double a1, b1, a2, b2, nAB, delta; + double C, nx, ny, nz; //color gradient magnitude and direction + double ux, uy, uz; + double phi, tau, rho0, rlx_setA, rlx_setB; + const double mrt_V1 = 0.05263157894736842; + const double mrt_V2 = 0.012531328320802; + const double mrt_V3 = 0.04761904761904762; + const double mrt_V4 = 0.004594820384294068; + const double mrt_V5 = 0.01587301587301587; + const double mrt_V6 = 0.0555555555555555555555555; + const double mrt_V7 = 0.02777777777777778; + const double mrt_V8 = 0.08333333333333333; + const double mrt_V9 = 0.003341687552213868; + const double mrt_V10 = 0.003968253968253968; + const double mrt_V11 = 0.01388888888888889; + const double mrt_V12 = 0.04166666666666666; - for (int n=start; n 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * ux)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * ux)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * ux)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * ux)) + delta; - //............................................... - // q = 0,2,4 - // Cq = {1,0,0}, {0,1,0}, {0,0,1} - delta = beta*nA*nB*nAB*0.1111111111111111*nx; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*ux))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*ux))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*ux))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*ux))+delta; + Aq[1 * Np + n] = a1; + Bq[1 * Np + n] = b1; + Aq[2 * Np + n] = a2; + Bq[2 * Np + n] = b2; - Aq[1*Np+n] = a1; - Bq[1*Np+n] = b1; - Aq[2*Np+n] = a2; - Bq[2*Np+n] = b2; + //............................................... + // q = 2 + // Cq = {0,1,0} + delta = beta * nA * nB * nAB * 0.1111111111111111 * ny; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * uy)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * uy)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * uy)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * uy)) + delta; - //............................................... - // q = 2 - // Cq = {0,1,0} - delta = beta*nA*nB*nAB*0.1111111111111111*ny; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*uy))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*uy))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*uy))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*uy))+delta; + Aq[3 * Np + n] = a1; + Bq[3 * Np + n] = b1; + Aq[4 * Np + n] = a2; + Bq[4 * Np + n] = b2; + //............................................... + // q = 4 + // Cq = {0,0,1} + delta = beta * nA * nB * nAB * 0.1111111111111111 * nz; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * uz)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * uz)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * uz)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * uz)) + delta; - Aq[3*Np+n] = a1; - Bq[3*Np+n] = b1; - Aq[4*Np+n] = a2; - Bq[4*Np+n] = b2; - //............................................... - // q = 4 - // Cq = {0,0,1} - delta = beta*nA*nB*nAB*0.1111111111111111*nz; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*uz))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*uz))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*uz))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*uz))+delta; - - Aq[5*Np+n] = a1; - Bq[5*Np+n] = b1; - Aq[6*Np+n] = a2; - Bq[6*Np+n] = b2; - //............................................... - - } - + Aq[5 * Np + n] = a1; + Bq[5 * Np + n] = b1; + Aq[6 * Np + n] = a2; + Bq[6 * Np + n] = b2; + //............................................... + } } //extern "C" void ScaLBL_D3Q19_AAodd_Color(int *neighborList, double *dist, double *Aq, double *Bq, double *Den, double *Velocity, // double *ColorGrad, double rhoA, double rhoB, double tauA, double tauB, double alpha, double beta, // double Fx, double Fy, double Fz, int start, int finish, int Np){ -extern "C" void ScaLBL_D3Q19_AAodd_Color(int *neighborList, int *Map, double *dist, double *Aq, double *Bq, double *Den, - double *Phi, double *Vel, double rhoA, double rhoB, double tauA, double tauB, double alpha, double beta, - double Fx, double Fy, double Fz, int strideY, int strideZ, int start, int finish, int Np){ - - int nn,ijk,nread; - int nr1,nr2,nr3,nr4,nr5,nr6; - int nr7,nr8,nr9,nr10; - int nr11,nr12,nr13,nr14; - //int nr15,nr16,nr17,nr18; - double fq; - // conserved momemnts - double rho,jx,jy,jz; - // non-conserved moments - double m1,m2,m4,m6,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18; - double m3,m5,m7; - double nA,nB; // number density - double a1,b1,a2,b2,nAB,delta; - double C,nx,ny,nz; //color gradient magnitude and direction - double ux,uy,uz; - double phi,tau,rho0,rlx_setA,rlx_setB; +extern "C" void ScaLBL_D3Q19_AAodd_Color( + int *neighborList, int *Map, double *dist, double *Aq, double *Bq, + double *Den, double *Phi, double *Vel, double rhoA, double rhoB, + double tauA, double tauB, double alpha, double beta, double Fx, double Fy, + double Fz, int strideY, int strideZ, int start, int finish, int Np) { - const double mrt_V1=0.05263157894736842; - const double mrt_V2=0.012531328320802; - const double mrt_V3=0.04761904761904762; - const double mrt_V4=0.004594820384294068; - const double mrt_V5=0.01587301587301587; - const double mrt_V6=0.0555555555555555555555555; - const double mrt_V7=0.02777777777777778; - const double mrt_V8=0.08333333333333333; - const double mrt_V9=0.003341687552213868; - const double mrt_V10=0.003968253968253968; - const double mrt_V11=0.01388888888888889; - const double mrt_V12=0.04166666666666666; + int nn, ijk, nread; + int nr1, nr2, nr3, nr4, nr5, nr6; + int nr7, nr8, nr9, nr10; + int nr11, nr12, nr13, nr14; + //int nr15,nr16,nr17,nr18; + double fq; + // conserved momemnts + double rho, jx, jy, jz; + // non-conserved moments + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; + double m3, m5, m7; + double nA, nB; // number density + double a1, b1, a2, b2, nAB, delta; + double C, nx, ny, nz; //color gradient magnitude and direction + double ux, uy, uz; + double phi, tau, rho0, rlx_setA, rlx_setB; - for (int n=start; n even part of dist) - //fq = dist[nread]; // reading the f2 data into register fq - nr2 = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) - fq = dist[nr2]; // reading the f2 data into register fq - rho += fq; - m1 -= 11.0*(fq); - m2 -= 4.0*(fq); - jx -= fq; - m4 += 4.0*(fq); - m9 += 2.0*(fq); - m10 -= 4.0*(fq); + //...........Normalize the Color Gradient................................. + C = sqrt(nx * nx + ny * ny + nz * nz); + double ColorMag = C; + if (C == 0.0) + ColorMag = 1.0; + nx = nx / ColorMag; + ny = ny / ColorMag; + nz = nz / ColorMag; - // q=3 - //nread = neighborList[n+2*Np]; // neighbor 4 - //fq = dist[nread]; - nr3 = neighborList[n+2*Np]; // neighbor 4 - fq = dist[nr3]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jy = fq; - m6 = -4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 = fq; - m12 = -2.0*fq; + // q=0 + fq = dist[n]; + rho = fq; + m1 = -30.0 * fq; + m2 = 12.0 * fq; - // q = 4 - //nread = neighborList[n+3*Np]; // neighbor 3 - //fq = dist[nread]; - nr4 = neighborList[n+3*Np]; // neighbor 3 - fq = dist[nr4]; - rho+= fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jy -= fq; - m6 += 4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 += fq; - m12 -= 2.0*fq; + // q=1 + //nread = neighborList[n]; // neighbor 2 + //fq = dist[nread]; // reading the f1 data into register fq + nr1 = neighborList[n]; + fq = dist[nr1]; // reading the f1 data into register fq + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jx = fq; + m4 = -4.0 * fq; + m9 = 2.0 * fq; + m10 = -4.0 * fq; - // q=5 - //nread = neighborList[n+4*Np]; - //fq = dist[nread]; - nr5 = neighborList[n+4*Np]; - fq = dist[nr5]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jz = fq; - m8 = -4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 -= fq; - m12 += 2.0*fq; + // f2 = dist[10*Np+n]; + //nread = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) + //fq = dist[nread]; // reading the f2 data into register fq + nr2 = neighborList[n + Np]; // neighbor 1 ( < 10Np => even part of dist) + fq = dist[nr2]; // reading the f2 data into register fq + rho += fq; + m1 -= 11.0 * (fq); + m2 -= 4.0 * (fq); + jx -= fq; + m4 += 4.0 * (fq); + m9 += 2.0 * (fq); + m10 -= 4.0 * (fq); + // q=3 + //nread = neighborList[n+2*Np]; // neighbor 4 + //fq = dist[nread]; + nr3 = neighborList[n + 2 * Np]; // neighbor 4 + fq = dist[nr3]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy = fq; + m6 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 = fq; + m12 = -2.0 * fq; - // q = 6 - //nread = neighborList[n+5*Np]; - //fq = dist[nread]; - nr6 = neighborList[n+5*Np]; - fq = dist[nr6]; - rho+= fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jz -= fq; - m8 += 4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 -= fq; - m12 += 2.0*fq; + // q = 4 + //nread = neighborList[n+3*Np]; // neighbor 3 + //fq = dist[nread]; + nr4 = neighborList[n + 3 * Np]; // neighbor 3 + fq = dist[nr4]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy -= fq; + m6 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 += fq; + m12 -= 2.0 * fq; - // q=7 - //nread = neighborList[n+6*Np]; - //fq = dist[nread]; - nr7 = neighborList[n+6*Np]; - fq = dist[nr7]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jy += fq; - m6 += fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 = fq; - m16 = fq; - m17 = -fq; + // q=5 + //nread = neighborList[n+4*Np]; + //fq = dist[nread]; + nr5 = neighborList[n + 4 * Np]; + fq = dist[nr5]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz = fq; + m8 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; - // q = 8 - //nread = neighborList[n+7*Np]; - //fq = dist[nread]; - nr8 = neighborList[n+7*Np]; - fq = dist[nr8]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jy -= fq; - m6 -= fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 += fq; - m16 -= fq; - m17 += fq; + // q = 6 + //nread = neighborList[n+5*Np]; + //fq = dist[nread]; + nr6 = neighborList[n + 5 * Np]; + fq = dist[nr6]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz -= fq; + m8 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; - // q=9 - //nread = neighborList[n+8*Np]; - //fq = dist[nread]; - nr9 = neighborList[n+8*Np]; - fq = dist[nr9]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jy -= fq; - m6 -= fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 -= fq; - m16 += fq; - m17 += fq; + // q=7 + //nread = neighborList[n+6*Np]; + //fq = dist[nread]; + nr7 = neighborList[n + 6 * Np]; + fq = dist[nr7]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 = fq; + m16 = fq; + m17 = -fq; - // q = 10 - //nread = neighborList[n+9*Np]; - //fq = dist[nread]; - nr10 = neighborList[n+9*Np]; - fq = dist[nr10]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jy += fq; - m6 += fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 -= fq; - m16 -= fq; - m17 -= fq; + // q = 8 + //nread = neighborList[n+7*Np]; + //fq = dist[nread]; + nr8 = neighborList[n + 7 * Np]; + fq = dist[nr8]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 += fq; + m16 -= fq; + m17 += fq; - // q=11 - //nread = neighborList[n+10*Np]; - //fq = dist[nread]; - nr11 = neighborList[n+10*Np]; - fq = dist[nr11]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jz += fq; - m8 += fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 = fq; - m16 -= fq; - m18 = fq; + // q=9 + //nread = neighborList[n+8*Np]; + //fq = dist[nread]; + nr9 = neighborList[n + 8 * Np]; + fq = dist[nr9]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 += fq; + m17 += fq; - // q=12 - //nread = neighborList[n+11*Np]; - //fq = dist[nread]; - nr12 = neighborList[n+11*Np]; - fq = dist[nr12]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jz -= fq; - m8 -= fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 += fq; - m16 += fq; - m18 -= fq; + // q = 10 + //nread = neighborList[n+9*Np]; + //fq = dist[nread]; + nr10 = neighborList[n + 9 * Np]; + fq = dist[nr10]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 -= fq; + m17 -= fq; - // q=13 - //nread = neighborList[n+12*Np]; - //fq = dist[nread]; - nr13 = neighborList[n+12*Np]; - fq = dist[nr13]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jz -= fq; - m8 -= fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 -= fq; - m16 -= fq; - m18 -= fq; + // q=11 + //nread = neighborList[n+10*Np]; + //fq = dist[nread]; + nr11 = neighborList[n + 10 * Np]; + fq = dist[nr11]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 = fq; + m16 -= fq; + m18 = fq; - // q=14 - //nread = neighborList[n+13*Np]; - //fq = dist[nread]; - nr14 = neighborList[n+13*Np]; - fq = dist[nr14]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jz += fq; - m8 += fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 -= fq; - m16 += fq; - m18 += fq; + // q=12 + //nread = neighborList[n+11*Np]; + //fq = dist[nread]; + nr12 = neighborList[n + 11 * Np]; + fq = dist[nr12]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 += fq; + m16 += fq; + m18 -= fq; - // q=15 - nread = neighborList[n+14*Np]; - fq = dist[nread]; - //fq = dist[17*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy += fq; - m6 += fq; - jz += fq; - m8 += fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 = fq; - m17 += fq; - m18 -= fq; + // q=13 + //nread = neighborList[n+12*Np]; + //fq = dist[nread]; + nr13 = neighborList[n + 12 * Np]; + fq = dist[nr13]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 -= fq; + m18 -= fq; - // q=16 - nread = neighborList[n+15*Np]; - fq = dist[nread]; - //fq = dist[8*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy -= fq; - m6 -= fq; - jz -= fq; - m8 -= fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 += fq; - m17 -= fq; - m18 += fq; + // q=14 + //nread = neighborList[n+13*Np]; + //fq = dist[nread]; + nr14 = neighborList[n + 13 * Np]; + fq = dist[nr14]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 += fq; + m18 += fq; - // q=17 - //fq = dist[18*Np+n]; - nread = neighborList[n+16*Np]; - fq = dist[nread]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy += fq; - m6 += fq; - jz -= fq; - m8 -= fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 -= fq; - m17 += fq; - m18 += fq; + // q=15 + nread = neighborList[n + 14 * Np]; + fq = dist[nread]; + //fq = dist[17*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 = fq; + m17 += fq; + m18 -= fq; - // q=18 - nread = neighborList[n+17*Np]; - fq = dist[nread]; - //fq = dist[9*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy -= fq; - m6 -= fq; - jz += fq; - m8 += fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 -= fq; - m17 -= fq; - m18 -= fq; - - //........................................................................ - //..............carry out relaxation process.............................. - //..........Toelke, Fruediger et. al. 2006................................ - if (C == 0.0) nx = ny = nz = 0.0; - m1 = m1 + rlx_setA*((19*(jx*jx+jy*jy+jz*jz)/rho0 - 11*rho) -19*alpha*C - m1); - m2 = m2 + rlx_setA*((3*rho - 5.5*(jx*jx+jy*jy+jz*jz)/rho0)- m2); - m4 = m4 + rlx_setB*((-0.6666666666666666*jx)- m4); - m6 = m6 + rlx_setB*((-0.6666666666666666*jy)- m6); - m8 = m8 + rlx_setB*((-0.6666666666666666*jz)- m8); - m9 = m9 + rlx_setA*(((2*jx*jx-jy*jy-jz*jz)/rho0) + 0.5*alpha*C*(2*nx*nx-ny*ny-nz*nz) - m9); - m10 = m10 + rlx_setA*( - m10); - m11 = m11 + rlx_setA*(((jy*jy-jz*jz)/rho0) + 0.5*alpha*C*(ny*ny-nz*nz)- m11); - m12 = m12 + rlx_setA*( - m12); - m13 = m13 + rlx_setA*( (jx*jy/rho0) + 0.5*alpha*C*nx*ny - m13); - m14 = m14 + rlx_setA*( (jy*jz/rho0) + 0.5*alpha*C*ny*nz - m14); - m15 = m15 + rlx_setA*( (jx*jz/rho0) + 0.5*alpha*C*nx*nz - m15); - m16 = m16 + rlx_setB*( - m16); - m17 = m17 + rlx_setB*( - m17); - m18 = m18 + rlx_setB*( - m18); - //.................inverse transformation...................................................... + // q=16 + nread = neighborList[n + 15 * Np]; + fq = dist[nread]; + //fq = dist[8*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 += fq; + m17 -= fq; + m18 += fq; - // q=0 - fq = mrt_V1*rho-mrt_V2*m1+mrt_V3*m2; - dist[n] = fq; + // q=17 + //fq = dist[18*Np+n]; + nread = neighborList[n + 16 * Np]; + fq = dist[nread]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 += fq; + m18 += fq; - // q = 1 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jx-m4)+mrt_V6*(m9-m10)+0.16666666*Fx; - //nread = neighborList[n+Np]; - dist[nr2] = fq; + // q=18 + nread = neighborList[n + 17 * Np]; + fq = dist[nread]; + //fq = dist[9*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 -= fq; + m18 -= fq; - // q=2 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m4-jx)+mrt_V6*(m9-m10) - 0.16666666*Fx; - //nread = neighborList[n]; - dist[nr1] = fq; + //........................................................................ + //..............carry out relaxation process.............................. + //..........Toelke, Fruediger et. al. 2006................................ + if (C == 0.0) + nx = ny = nz = 0.0; + m1 = m1 + rlx_setA * + ((19 * (jx * jx + jy * jy + jz * jz) / rho0 - 11 * rho) - + 19 * alpha * C - m1); + m2 = m2 + + rlx_setA * + ((3 * rho - 5.5 * (jx * jx + jy * jy + jz * jz) / rho0) - m2); + m4 = m4 + rlx_setB * ((-0.6666666666666666 * jx) - m4); + m6 = m6 + rlx_setB * ((-0.6666666666666666 * jy) - m6); + m8 = m8 + rlx_setB * ((-0.6666666666666666 * jz) - m8); + m9 = + m9 + rlx_setA * + (((2 * jx * jx - jy * jy - jz * jz) / rho0) + + 0.5 * alpha * C * (2 * nx * nx - ny * ny - nz * nz) - m9); + m10 = m10 + rlx_setA * (-m10); + m11 = m11 + rlx_setA * (((jy * jy - jz * jz) / rho0) + + 0.5 * alpha * C * (ny * ny - nz * nz) - m11); + m12 = m12 + rlx_setA * (-m12); + m13 = m13 + + rlx_setA * ((jx * jy / rho0) + 0.5 * alpha * C * nx * ny - m13); + m14 = m14 + + rlx_setA * ((jy * jz / rho0) + 0.5 * alpha * C * ny * nz - m14); + m15 = m15 + + rlx_setA * ((jx * jz / rho0) + 0.5 * alpha * C * nx * nz - m15); + m16 = m16 + rlx_setB * (-m16); + m17 = m17 + rlx_setB * (-m17); + m18 = m18 + rlx_setB * (-m18); + //.................inverse transformation...................................................... - // q = 3 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jy-m6)+mrt_V7*(m10-m9)+mrt_V8*(m11-m12) + 0.16666666*Fy; - //nread = neighborList[n+3*Np]; - dist[nr4] = fq; + // q=0 + fq = mrt_V1 * rho - mrt_V2 * m1 + mrt_V3 * m2; + dist[n] = fq; - // q = 4 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m6-jy)+mrt_V7*(m10-m9)+mrt_V8*(m11-m12) - 0.16666666*Fy; - //nread = neighborList[n+2*Np]; - dist[nr3] = fq; + // q = 1 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jx - m4) + + mrt_V6 * (m9 - m10) + 0.16666666 * Fx; + //nread = neighborList[n+Np]; + dist[nr2] = fq; - // q = 5 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jz-m8)+mrt_V7*(m10-m9)+mrt_V8*(m12-m11) + 0.16666666*Fz; - //nread = neighborList[n+5*Np]; - dist[nr6] = fq; + // q=2 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m4 - jx) + + mrt_V6 * (m9 - m10) - 0.16666666 * Fx; + //nread = neighborList[n]; + dist[nr1] = fq; - // q = 6 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m8-jz)+mrt_V7*(m10-m9)+mrt_V8*(m12-m11) - 0.16666666*Fz; - //nread = neighborList[n+4*Np]; - dist[nr5] = fq; + // q = 3 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jy - m6) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12) + 0.16666666 * Fy; + //nread = neighborList[n+3*Np]; + dist[nr4] = fq; - // q = 7 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jx+jy)+0.025*(m4+m6)+ - mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12+0.25*m13+0.125*(m16-m17) + 0.08333333333*(Fx+Fy); - //nread = neighborList[n+7*Np]; - dist[nr8] = fq; + // q = 4 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m6 - jy) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12) - 0.16666666 * Fy; + //nread = neighborList[n+2*Np]; + dist[nr3] = fq; - // q = 8 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2-0.1*(jx+jy)-0.025*(m4+m6) +mrt_V7*m9+mrt_V11*m10+mrt_V8*m11 - +mrt_V12*m12+0.25*m13+0.125*(m17-m16) - 0.08333333333*(Fx+Fy); - //nread = neighborList[n+6*Np]; - dist[nr7] = fq; + // q = 5 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jz - m8) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11) + 0.16666666 * Fz; + //nread = neighborList[n+5*Np]; + dist[nr6] = fq; - // q = 9 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jx-jy)+0.025*(m4-m6)+ - mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12-0.25*m13+0.125*(m16+m17) + 0.08333333333*(Fx-Fy); - //nread = neighborList[n+9*Np]; - dist[nr10] = fq; + // q = 6 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m8 - jz) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11) - 0.16666666 * Fz; + //nread = neighborList[n+4*Np]; + dist[nr5] = fq; - // q = 10 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jy-jx)+0.025*(m6-m4)+ - mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12-0.25*m13-0.125*(m16+m17)- 0.08333333333*(Fx-Fy); - //nread = neighborList[n+8*Np]; - dist[nr9] = fq; + // q = 7 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jy) + + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m16 - m17) + + 0.08333333333 * (Fx + Fy); + //nread = neighborList[n+7*Np]; + dist[nr8] = fq; - // q = 11 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jx+jz)+0.025*(m4+m8) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12+0.25*m15+0.125*(m18-m16) + 0.08333333333*(Fx+Fz); - //nread = neighborList[n+11*Np]; - dist[nr12] = fq; + // q = 8 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jy) - + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m17 - m16) - + 0.08333333333 * (Fx + Fy); + //nread = neighborList[n+6*Np]; + dist[nr7] = fq; - // q = 12 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2-0.1*(jx+jz)-0.025*(m4+m8)+ - mrt_V7*m9+mrt_V11*m10-mrt_V8*m11-mrt_V12*m12+0.25*m15+0.125*(m16-m18) - 0.08333333333*(Fx+Fz); - //nread = neighborList[n+10*Np]; - dist[nr11]= fq; + // q = 9 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jy) + + 0.025 * (m4 - m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 + 0.125 * (m16 + m17) + + 0.08333333333 * (Fx - Fy); + //nread = neighborList[n+9*Np]; + dist[nr10] = fq; - // q = 13 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jx-jz)+0.025*(m4-m8) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12-0.25*m15-0.125*(m16+m18) + 0.08333333333*(Fx-Fz); - //nread = neighborList[n+13*Np]; - dist[nr14] = fq; + // q = 10 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jx) + + 0.025 * (m6 - m4) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 - 0.125 * (m16 + m17) - + 0.08333333333 * (Fx - Fy); + //nread = neighborList[n+8*Np]; + dist[nr9] = fq; - // q= 14 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jz-jx)+0.025*(m8-m4) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12-0.25*m15+0.125*(m16+m18) - 0.08333333333*(Fx-Fz); - //nread = neighborList[n+12*Np]; - dist[nr13] = fq; + // q = 11 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jz) + + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m18 - m16) + + 0.08333333333 * (Fx + Fz); + //nread = neighborList[n+11*Np]; + dist[nr12] = fq; + // q = 12 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jz) - + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m16 - m18) - + 0.08333333333 * (Fx + Fz); + //nread = neighborList[n+10*Np]; + dist[nr11] = fq; - // q = 15 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jy+jz)+0.025*(m6+m8) - -mrt_V6*m9-mrt_V7*m10+0.25*m14+0.125*(m17-m18) + 0.08333333333*(Fy+Fz); - nread = neighborList[n+15*Np]; - dist[nread] = fq; + // q = 13 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jz) + + 0.025 * (m4 - m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 - 0.125 * (m16 + m18) + + 0.08333333333 * (Fx - Fz); + //nread = neighborList[n+13*Np]; + dist[nr14] = fq; - // q = 16 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2-0.1*(jy+jz)-0.025*(m6+m8) - -mrt_V6*m9-mrt_V7*m10+0.25*m14+0.125*(m18-m17)- 0.08333333333*(Fy+Fz); - nread = neighborList[n+14*Np]; - dist[nread] = fq; + // q= 14 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jx) + + 0.025 * (m8 - m4) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 + 0.125 * (m16 + m18) - + 0.08333333333 * (Fx - Fz); + //nread = neighborList[n+12*Np]; + dist[nr13] = fq; + // q = 15 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy + jz) + + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m17 - m18) + 0.08333333333 * (Fy + Fz); + nread = neighborList[n + 15 * Np]; + dist[nread] = fq; - // q = 17 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jy-jz)+0.025*(m6-m8) - -mrt_V6*m9-mrt_V7*m10-0.25*m14+0.125*(m17+m18) + 0.08333333333*(Fy-Fz); - nread = neighborList[n+17*Np]; - dist[nread] = fq; + // q = 16 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jy + jz) - + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m18 - m17) - 0.08333333333 * (Fy + Fz); + nread = neighborList[n + 14 * Np]; + dist[nread] = fq; - // q = 18 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jz-jy)+0.025*(m8-m6) - -mrt_V6*m9-mrt_V7*m10-0.25*m14-0.125*(m17+m18) - 0.08333333333*(Fy-Fz); - nread = neighborList[n+16*Np]; - dist[nread] = fq; + // q = 17 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jz) + + 0.025 * (m6 - m8) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 + + 0.125 * (m17 + m18) + 0.08333333333 * (Fy - Fz); + nread = neighborList[n + 17 * Np]; + dist[nread] = fq; - // write the velocity - ux = jx / rho0; - uy = jy / rho0; - uz = jz / rho0; - Vel[n] = ux; - Vel[Np+n] = uy; - Vel[2*Np+n] = uz; + // q = 18 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jy) + + 0.025 * (m8 - m6) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 - + 0.125 * (m17 + m18) - 0.08333333333 * (Fy - Fz); + nread = neighborList[n + 16 * Np]; + dist[nread] = fq; - // Instantiate mass transport distributions - // Stationary value - distribution 0 - nAB = 1.0/(nA+nB); - Aq[n] = 0.3333333333333333*nA; - Bq[n] = 0.3333333333333333*nB; + // write the velocity + ux = jx / rho0; + uy = jy / rho0; + uz = jz / rho0; + Vel[n] = ux; + Vel[Np + n] = uy; + Vel[2 * Np + n] = uz; - //............................................... - // q = 0,2,4 - // Cq = {1,0,0}, {0,1,0}, {0,0,1} - delta = beta*nA*nB*nAB*0.1111111111111111*nx; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*ux))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*ux))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*ux))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*ux))+delta; + // Instantiate mass transport distributions + // Stationary value - distribution 0 + nAB = 1.0 / (nA + nB); + Aq[n] = 0.3333333333333333 * nA; + Bq[n] = 0.3333333333333333 * nB; - // q = 1 - //nread = neighborList[n+Np]; - Aq[nr2] = a1; - Bq[nr2] = b1; - // q=2 - //nread = neighborList[n]; - Aq[nr1] = a2; - Bq[nr1] = b2; + //............................................... + // q = 0,2,4 + // Cq = {1,0,0}, {0,1,0}, {0,0,1} + delta = beta * nA * nB * nAB * 0.1111111111111111 * nx; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * ux)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * ux)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * ux)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * ux)) + delta; - //............................................... - // Cq = {0,1,0} - delta = beta*nA*nB*nAB*0.1111111111111111*ny; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*uy))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*uy))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*uy))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*uy))+delta; + // q = 1 + //nread = neighborList[n+Np]; + Aq[nr2] = a1; + Bq[nr2] = b1; + // q=2 + //nread = neighborList[n]; + Aq[nr1] = a2; + Bq[nr1] = b2; - // q = 3 - //nread = neighborList[n+3*Np]; - Aq[nr4] = a1; - Bq[nr4] = b1; - // q = 4 - //nread = neighborList[n+2*Np]; - Aq[nr3] = a2; - Bq[nr3] = b2; + //............................................... + // Cq = {0,1,0} + delta = beta * nA * nB * nAB * 0.1111111111111111 * ny; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * uy)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * uy)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * uy)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * uy)) + delta; - //............................................... - // q = 4 - // Cq = {0,0,1} - delta = beta*nA*nB*nAB*0.1111111111111111*nz; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*uz))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*uz))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*uz))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*uz))+delta; + // q = 3 + //nread = neighborList[n+3*Np]; + Aq[nr4] = a1; + Bq[nr4] = b1; + // q = 4 + //nread = neighborList[n+2*Np]; + Aq[nr3] = a2; + Bq[nr3] = b2; - // q = 5 - //nread = neighborList[n+5*Np]; - Aq[nr6] = a1; - Bq[nr6] = b1; - // q = 6 - //nread = neighborList[n+4*Np]; - Aq[nr5] = a2; - Bq[nr5] = b2; - //............................................... - } + //............................................... + // q = 4 + // Cq = {0,0,1} + delta = beta * nA * nB * nAB * 0.1111111111111111 * nz; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * uz)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * uz)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * uz)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * uz)) + delta; + + // q = 5 + //nread = neighborList[n+5*Np]; + Aq[nr6] = a1; + Bq[nr6] = b1; + // q = 6 + //nread = neighborList[n+4*Np]; + Aq[nr5] = a2; + Bq[nr5] = b2; + //............................................... + } } +extern "C" void ScaLBL_D3Q7_AAodd_Color(int *neighborList, int *Map, double *Aq, + double *Bq, double *Den, double *Phi, + double *ColorGrad, double *Vel, + double rhoA, double rhoB, double beta, + int start, int finish, int Np) { -extern "C" void ScaLBL_D3Q7_AAodd_Color(int *neighborList, int *Map, double *Aq, double *Bq, double *Den, - double *Phi, double *ColorGrad, double *Vel, double rhoA, double rhoB, double beta, int start, int finish, int Np){ + int nr1, nr2, nr3, nr4, nr5, nr6; + double nA, nB; // number density + double a1, b1, a2, b2, nAB, delta; + double C, nx, ny, nz; //color gradient magnitude and direction + double ux, uy, uz; + // Instantiate mass transport distributions + // Stationary value - distribution 0 + for (int n = start; n < finish; n++) { + /* neighbors */ + nr1 = neighborList[n + 0 * Np]; + nr2 = neighborList[n + 1 * Np]; + nr3 = neighborList[n + 2 * Np]; + nr4 = neighborList[n + 3 * Np]; + nr5 = neighborList[n + 4 * Np]; + nr6 = neighborList[n + 5 * Np]; - int nr1,nr2,nr3,nr4,nr5,nr6; - double nA,nB; // number density - double a1,b1,a2,b2,nAB,delta; - double C,nx,ny,nz; //color gradient magnitude and direction - double ux,uy,uz; - // Instantiate mass transport distributions - // Stationary value - distribution 0 - for (int n=start; n0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*ux))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*ux))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*ux))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*ux))+delta; + // read the component number densities + nA = Den[n]; + nB = Den[Np + n]; - // q = 1 - //nread = neighborList[n+Np]; - Aq[nr2] = a1; - Bq[nr2] = b1; - // q=2 - //nread = neighborList[n]; - Aq[nr1] = a2; - Bq[nr1] = b2; + // compute phase indicator field + nAB = 1.0 / (nA + nB); + Aq[n] = 0.3333333333333333 * nA; + Bq[n] = 0.3333333333333333 * nB; - //............................................... - // Cq = {0,1,0} - delta = beta*nA*nB*nAB*0.1111111111111111*ny; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*uy))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*uy))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*uy))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*uy))+delta; + //............................................... + // q = 0,2,4 + // Cq = {1,0,0}, {0,1,0}, {0,0,1} + delta = beta * nA * nB * nAB * 0.1111111111111111 * nx; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * ux)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * ux)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * ux)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * ux)) + delta; - // q = 3 - //nread = neighborList[n+3*Np]; - Aq[nr4] = a1; - Bq[nr4] = b1; - // q = 4 - //nread = neighborList[n+2*Np]; - Aq[nr3] = a2; - Bq[nr3] = b2; + // q = 1 + //nread = neighborList[n+Np]; + Aq[nr2] = a1; + Bq[nr2] = b1; + // q=2 + //nread = neighborList[n]; + Aq[nr1] = a2; + Bq[nr1] = b2; - //............................................... - // q = 4 - // Cq = {0,0,1} - delta = beta*nA*nB*nAB*0.1111111111111111*nz; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*uz))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*uz))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*uz))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*uz))+delta; + //............................................... + // Cq = {0,1,0} + delta = beta * nA * nB * nAB * 0.1111111111111111 * ny; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * uy)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * uy)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * uy)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * uy)) + delta; - // q = 5 - //nread = neighborList[n+5*Np]; - Aq[nr6] = a1; - Bq[nr6] = b1; - // q = 6 - //nread = neighborList[n+4*Np]; - Aq[nr5] = a2; - Bq[nr5] = b2; - //............................................... - } + // q = 3 + //nread = neighborList[n+3*Np]; + Aq[nr4] = a1; + Bq[nr4] = b1; + // q = 4 + //nread = neighborList[n+2*Np]; + Aq[nr3] = a2; + Bq[nr3] = b2; + + //............................................... + // q = 4 + // Cq = {0,0,1} + delta = beta * nA * nB * nAB * 0.1111111111111111 * nz; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * uz)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * uz)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * uz)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * uz)) + delta; + + // q = 5 + //nread = neighborList[n+5*Np]; + Aq[nr6] = a1; + Bq[nr6] = b1; + // q = 6 + //nread = neighborList[n+4*Np]; + Aq[nr5] = a2; + Bq[nr5] = b2; + //............................................... + } } -extern "C" void ScaLBL_D3Q7_AAeven_Color(int *Map, double *Aq, double *Bq, double *Den, - double *Phi, double *ColorGrad, double *Vel, double rhoA, double rhoB, double beta, int start, int finish, int Np){ +extern "C" void ScaLBL_D3Q7_AAeven_Color(int *Map, double *Aq, double *Bq, + double *Den, double *Phi, + double *ColorGrad, double *Vel, + double rhoA, double rhoB, double beta, + int start, int finish, int Np) { - double nA,nB; // number density - double a1,b1,a2,b2,nAB,delta; - double C,nx,ny,nz; //color gradient magnitude and direction - double ux,uy,uz; - // Instantiate mass transport distributions - // Stationary value - distribution 0 - for (int n=start; n0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*ux))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*ux))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*ux))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*ux))+delta; + /* load color gradient */ + nx = ColorGrad[n]; + ny = ColorGrad[Np + n]; + nz = ColorGrad[2 * Np + n]; + C = sqrt(nx * nx + ny * ny + nz * nz); + double ColorMag = C; + if (C == 0.0) + ColorMag = 1.0; + nx = nx / ColorMag; + ny = ny / ColorMag; + nz = nz / ColorMag; - Aq[1*Np+n] = a1; - Bq[1*Np+n] = b1; - Aq[2*Np+n] = a2; - Bq[2*Np+n] = b2; + // read the component number densities + nA = Den[n]; + nB = Den[Np + n]; - //............................................... - // q = 2 - // Cq = {0,1,0} - delta = beta*nA*nB*nAB*0.1111111111111111*ny; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*uy))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*uy))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*uy))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*uy))+delta; + nAB = 1.0 / (nA + nB); + Aq[n] = 0.3333333333333333 * nA; + Bq[n] = 0.3333333333333333 * nB; - Aq[3*Np+n] = a1; - Bq[3*Np+n] = b1; - Aq[4*Np+n] = a2; - Bq[4*Np+n] = b2; - //............................................... - // q = 4 - // Cq = {0,0,1} - delta = beta*nA*nB*nAB*0.1111111111111111*nz; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*uz))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*uz))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*uz))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*uz))+delta; + //............................................... + // q = 0,2,4 + // Cq = {1,0,0}, {0,1,0}, {0,0,1} + delta = beta * nA * nB * nAB * 0.1111111111111111 * nx; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * ux)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * ux)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * ux)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * ux)) + delta; - Aq[5*Np+n] = a1; - Bq[5*Np+n] = b1; - Aq[6*Np+n] = a2; - Bq[6*Np+n] = b2; - //............................................... + Aq[1 * Np + n] = a1; + Bq[1 * Np + n] = b1; + Aq[2 * Np + n] = a2; + Bq[2 * Np + n] = b2; - } + //............................................... + // q = 2 + // Cq = {0,1,0} + delta = beta * nA * nB * nAB * 0.1111111111111111 * ny; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * uy)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * uy)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * uy)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * uy)) + delta; + + Aq[3 * Np + n] = a1; + Bq[3 * Np + n] = b1; + Aq[4 * Np + n] = a2; + Bq[4 * Np + n] = b2; + //............................................... + // q = 4 + // Cq = {0,0,1} + delta = beta * nA * nB * nAB * 0.1111111111111111 * nz; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * uz)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * uz)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * uz)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * uz)) + delta; + + Aq[5 * Np + n] = a1; + Bq[5 * Np + n] = b1; + Aq[6 * Np + n] = a2; + Bq[6 * Np + n] = b2; + //............................................... + } } - -extern "C" void ScaLBL_D3Q7_AAodd_PhaseField(int *neighborList, int *Map, double *Aq, double *Bq, - double *Den, double *Phi, int start, int finish, int Np){ +extern "C" void ScaLBL_D3Q7_AAodd_PhaseField(int *neighborList, int *Map, + double *Aq, double *Bq, + double *Den, double *Phi, + int start, int finish, int Np) { - int idx,nread; - double fq,nA,nB; + int idx, nread; + double fq, nA, nB; - for (int n=start; n 1.f){ - nA = 1.0; nB = 0.f; - } - else if (phi < -1.f){ - nB = 1.0; nA = 0.f; - } - else{ - nA=0.5*(phi+1.f); - nB=0.5*(1.f-phi); - } - Den[idx] = nA; - Den[Np+idx] = nB; - - Aq[idx]=0.3333333333333333*nA; - Aq[Np+idx]=0.1111111111111111*nA; - Aq[2*Np+idx]=0.1111111111111111*nA; - Aq[3*Np+idx]=0.1111111111111111*nA; - Aq[4*Np+idx]=0.1111111111111111*nA; - Aq[5*Np+idx]=0.1111111111111111*nA; - Aq[6*Np+idx]=0.1111111111111111*nA; - - Bq[idx]=0.3333333333333333*nB; - Bq[Np+idx]=0.1111111111111111*nB; - Bq[2*Np+idx]=0.1111111111111111*nB; - Bq[3*Np+idx]=0.1111111111111111*nB; - Bq[4*Np+idx]=0.1111111111111111*nB; - Bq[5*Np+idx]=0.1111111111111111*nB; - Bq[6*Np+idx]=0.1111111111111111*nB; - } + n = Map[idx]; + phi = Phi[n]; + if (phi > 1.f) { + nA = 1.0; + nB = 0.f; + } else if (phi < -1.f) { + nB = 1.0; + nA = 0.f; + } else { + nA = 0.5 * (phi + 1.f); + nB = 0.5 * (1.f - phi); + } + Den[idx] = nA; + Den[Np + idx] = nB; + + Aq[idx] = 0.3333333333333333 * nA; + Aq[Np + idx] = 0.1111111111111111 * nA; + Aq[2 * Np + idx] = 0.1111111111111111 * nA; + Aq[3 * Np + idx] = 0.1111111111111111 * nA; + Aq[4 * Np + idx] = 0.1111111111111111 * nA; + Aq[5 * Np + idx] = 0.1111111111111111 * nA; + Aq[6 * Np + idx] = 0.1111111111111111 * nA; + + Bq[idx] = 0.3333333333333333 * nB; + Bq[Np + idx] = 0.1111111111111111 * nB; + Bq[2 * Np + idx] = 0.1111111111111111 * nB; + Bq[3 * Np + idx] = 0.1111111111111111 * nB; + Bq[4 * Np + idx] = 0.1111111111111111 * nB; + Bq[5 * Np + idx] = 0.1111111111111111 * nB; + Bq[6 * Np + idx] = 0.1111111111111111 * nB; + } } -extern "C" void ScaLBL_CopySlice_z(double *Phi, int Nx, int Ny, int Nz, int Source, int Dest){ - int n; double value; - for (n=0; n -extern "C" void ScaLBL_D3Q19_Pack(int q, int *list, int start, int count, double *sendbuf, double *dist, int N){ - //.................................................................................... - // Pack distribution q into the send buffer for the listed lattice sites - // dist may be even or odd distributions stored by stream layout - //.................................................................................... - int idx,n; - for (idx=0; idx 0){ - //........................................................................ - // Retrieve even distributions from the local node (swap convention) - // f0 = disteven[n]; // Does not particupate in streaming - f1 = distodd[n]; - f3 = distodd[N+n]; - f5 = distodd[2*N+n]; - f7 = distodd[3*N+n]; - f9 = distodd[4*N+n]; - f11 = distodd[5*N+n]; - f13 = distodd[6*N+n]; - f15 = distodd[7*N+n]; - f17 = distodd[8*N+n]; - //........................................................................ + if (ID[n] > 0) { + //........................................................................ + // Retrieve even distributions from the local node (swap convention) + // f0 = disteven[n]; // Does not particupate in streaming + f1 = distodd[n]; + f3 = distodd[N + n]; + f5 = distodd[2 * N + n]; + f7 = distodd[3 * N + n]; + f9 = distodd[4 * N + n]; + f11 = distodd[5 * N + n]; + f13 = distodd[6 * N + n]; + f15 = distodd[7 * N + n]; + f17 = distodd[8 * N + n]; + //........................................................................ - //........................................................................ - // Retrieve odd distributions from neighboring nodes (swap convention) - //........................................................................ - nn = n+1; // neighbor index (pull convention) - if (!(i+1 0){ - distodd[n] = f2; - disteven[N+nn] = f1; - } - //} - //........................................................................ - nn = n+Nx; // neighbor index (pull convention) - if (!(j+1 0){ - distodd[N+n] = f4; - disteven[2*N+nn] = f3; - // } - } - //........................................................................ - nn = n+Nx*Ny; // neighbor index (pull convention) - if (!(k+1 0){ - distodd[2*N+n] = f6; - disteven[3*N+nn] = f5; - // } - } - //........................................................................ - nn = n+Nx+1; // neighbor index (pull convention) - if (!(i+1 0){ - distodd[3*N+n] = f8; - disteven[4*N+nn] = f7; - // } - } - //........................................................................ - nn = n-Nx+1; // neighbor index (pull convention) - if (!(i+1 0){ - distodd[4*N+n] = f10; - disteven[5*N+nn] = f9; - // } - } - //........................................................................ - nn = n+Nx*Ny+1; // neighbor index (pull convention) - if (!(i+1 0){ - distodd[5*N+n] = f12; - disteven[6*N+nn] = f11; - // } - } - //........................................................................ - nn = n-Nx*Ny+1; // neighbor index (pull convention) - if (!(i+1 0){ - distodd[6*N+n] = f14; - disteven[7*N+nn] = f13; - // } - } - //........................................................................ - nn = n+Nx*Ny+Nx; // neighbor index (pull convention) - if (!(j+1 0){ - distodd[7*N+n] = f16; - disteven[8*N+nn] = f15; - // } - } - //........................................................................ - nn = n-Nx*Ny+Nx; // neighbor index (pull convention) - if (!(j+1 0){ - distodd[8*N+n] = f18; - disteven[9*N+nn] = f17; - // } - } - //........................................................................ - - } - } + //........................................................................ + // Retrieve odd distributions from neighboring nodes (swap convention) + //........................................................................ + nn = n + 1; // neighbor index (pull convention) + if (!(i + 1 < Nx)) + nn -= Nx; // periodic BC along the x-boundary + //if (i+1 0) { + distodd[n] = f2; + disteven[N + nn] = f1; + } + //} + //........................................................................ + nn = n + Nx; // neighbor index (pull convention) + if (!(j + 1 < Ny)) + nn -= Nx * Ny; // Perioidic BC along the y-boundary + //if (j+1 0) { + distodd[N + n] = f4; + disteven[2 * N + nn] = f3; + // } + } + //........................................................................ + nn = n + Nx * Ny; // neighbor index (pull convention) + if (!(k + 1 < Nz)) + nn -= Nx * Ny * Nz; // Perioidic BC along the z-boundary + //if (k+1 0) { + distodd[2 * N + n] = f6; + disteven[3 * N + nn] = f5; + // } + } + //........................................................................ + nn = n + Nx + 1; // neighbor index (pull convention) + if (!(i + 1 < Nx)) + nn -= Nx; // periodic BC along the x-boundary + if (!(j + 1 < Ny)) + nn -= Nx * Ny; // Perioidic BC along the y-boundary + //if ((i+1 0) { + distodd[3 * N + n] = f8; + disteven[4 * N + nn] = f7; + // } + } + //........................................................................ + nn = n - Nx + 1; // neighbor index (pull convention) + if (!(i + 1 < Nx)) + nn -= Nx; // periodic BC along the x-boundary + if (j - 1 < 0) + nn += Nx * Ny; // Perioidic BC along the y-boundary + //if (!(i-1<0) && (j+1 0) { + distodd[4 * N + n] = f10; + disteven[5 * N + nn] = f9; + // } + } + //........................................................................ + nn = n + Nx * Ny + 1; // neighbor index (pull convention) + if (!(i + 1 < Nx)) + nn -= Nx; // periodic BC along the x-boundary + if (!(k + 1 < Nz)) + nn -= Nx * Ny * Nz; // Perioidic BC along the z-boundary + //if ( !(i-1<0) && !(k-1<0)){ + f12 = disteven[6 * N + nn]; // pull distribution 11 + if (f12 > 0) { + distodd[5 * N + n] = f12; + disteven[6 * N + nn] = f11; + // } + } + //........................................................................ + nn = n - Nx * Ny + 1; // neighbor index (pull convention) + if (!(i + 1 < Nx)) + nn -= Nx; // periodic BC along the x-boundary + if (k - 1 < 0) + nn += Nx * Ny * Nz; // Perioidic BC along the z-boundary + //if (!(i-1<0) && (k+1 0) { + distodd[6 * N + n] = f14; + disteven[7 * N + nn] = f13; + // } + } + //........................................................................ + nn = n + Nx * Ny + Nx; // neighbor index (pull convention) + if (!(j + 1 < Ny)) + nn -= Nx * Ny; // Perioidic BC along the y-boundary + if (!(k + 1 < Nz)) + nn -= Nx * Ny * Nz; // Perioidic BC along the z-boundary + //if (!(j-1<0) && !(k-1<0)){ + f16 = disteven[8 * N + nn]; // pull neighbor for distribution 15 + if (f16 > 0) { + distodd[7 * N + n] = f16; + disteven[8 * N + nn] = f15; + // } + } + //........................................................................ + nn = n - Nx * Ny + Nx; // neighbor index (pull convention) + if (!(j + 1 < Ny)) + nn -= Nx * Ny; // Perioidic BC along the y-boundary + if (k - 1 < 0) + nn += Nx * Ny * Nz; // Perioidic BC along the z-boundary + //if (!(j-1<0) && (k+1 0) { + distodd[8 * N + n] = f18; + disteven[9 * N + nn] = f17; + // } + } + //........................................................................ + } + } } -extern "C" void ScaLBL_D3Q19_Swap_Compact(int *neighborList, double *disteven, double *distodd, int Np) -{ - int q,n,nn; - double f1,f2; - for (q=0; q<9; q++){ - for (n=0; n 10Np => odd part of dist) + fq = dist[nread]; // reading the f1 data into register fq + //fp = dist[10*Np+n]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jx = fq; + m4 = -4.0 * fq; + m9 = 2.0 * fq; + m10 = -4.0 * fq; - // q=1 - nread = neighborList[n]; // neighbor 2 ( > 10Np => odd part of dist) - fq = dist[nread]; // reading the f1 data into register fq - //fp = dist[10*Np+n]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jx = fq; - m4 = -4.0*fq; - m9 = 2.0*fq; - m10 = -4.0*fq; + // f2 = dist[10*Np+n]; + nread = + neighborList[n + Np]; // neighbor 1 ( < 10Np => even part of dist) + fq = dist[nread]; // reading the f2 data into register fq + //fq = dist[Np+n]; + rho += fq; + m1 -= 11.0 * (fq); + m2 -= 4.0 * (fq); + jx -= fq; + m4 += 4.0 * (fq); + m9 += 2.0 * (fq); + m10 -= 4.0 * (fq); - // f2 = dist[10*Np+n]; - nread = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) - fq = dist[nread]; // reading the f2 data into register fq - //fq = dist[Np+n]; - rho += fq; - m1 -= 11.0*(fq); - m2 -= 4.0*(fq); - jx -= fq; - m4 += 4.0*(fq); - m9 += 2.0*(fq); - m10 -= 4.0*(fq); + // q=3 + nread = neighborList[n + 2 * Np]; // neighbor 4 + fq = dist[nread]; + //fq = dist[11*Np+n]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy = fq; + m6 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 = fq; + m12 = -2.0 * fq; - // q=3 - nread = neighborList[n+2*Np]; // neighbor 4 - fq = dist[nread]; - //fq = dist[11*Np+n]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jy = fq; - m6 = -4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 = fq; - m12 = -2.0*fq; + // q = 4 + nread = neighborList[n + 3 * Np]; // neighbor 3 + fq = dist[nread]; + //fq = dist[2*Np+n]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy -= fq; + m6 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 += fq; + m12 -= 2.0 * fq; - // q = 4 - nread = neighborList[n+3*Np]; // neighbor 3 - fq = dist[nread]; - //fq = dist[2*Np+n]; - rho+= fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jy -= fq; - m6 += 4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 += fq; - m12 -= 2.0*fq; + // q=5 + nread = neighborList[n + 4 * Np]; + fq = dist[nread]; + //fq = dist[12*Np+n]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz = fq; + m8 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; - // q=5 - nread = neighborList[n+4*Np]; - fq = dist[nread]; - //fq = dist[12*Np+n]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jz = fq; - m8 = -4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 -= fq; - m12 += 2.0*fq; + // q = 6 + nread = neighborList[n + 5 * Np]; + fq = dist[nread]; + //fq = dist[3*Np+n]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz -= fq; + m8 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; + // q=7 + nread = neighborList[n + 6 * Np]; + fq = dist[nread]; + //fq = dist[13*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 = fq; + m16 = fq; + m17 = -fq; - // q = 6 - nread = neighborList[n+5*Np]; - fq = dist[nread]; - //fq = dist[3*Np+n]; - rho+= fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jz -= fq; - m8 += 4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 -= fq; - m12 += 2.0*fq; + // q = 8 + nread = neighborList[n + 7 * Np]; + fq = dist[nread]; + //fq = dist[4*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 += fq; + m16 -= fq; + m17 += fq; - // q=7 - nread = neighborList[n+6*Np]; - fq = dist[nread]; - //fq = dist[13*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jy += fq; - m6 += fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 = fq; - m16 = fq; - m17 = -fq; + // q=9 + nread = neighborList[n + 8 * Np]; + fq = dist[nread]; + //fq = dist[14*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 += fq; + m17 += fq; - // q = 8 - nread = neighborList[n+7*Np]; - fq = dist[nread]; - //fq = dist[4*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jy -= fq; - m6 -= fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 += fq; - m16 -= fq; - m17 += fq; + // q = 10 + nread = neighborList[n + 9 * Np]; + fq = dist[nread]; + //fq = dist[5*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 -= fq; + m17 -= fq; - // q=9 - nread = neighborList[n+8*Np]; - fq = dist[nread]; - //fq = dist[14*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jy -= fq; - m6 -= fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 -= fq; - m16 += fq; - m17 += fq; + // q=11 + nread = neighborList[n + 10 * Np]; + fq = dist[nread]; + //fq = dist[15*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 = fq; + m16 -= fq; + m18 = fq; - // q = 10 - nread = neighborList[n+9*Np]; - fq = dist[nread]; - //fq = dist[5*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jy += fq; - m6 += fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 -= fq; - m16 -= fq; - m17 -= fq; + // q=12 + nread = neighborList[n + 11 * Np]; + fq = dist[nread]; + //fq = dist[6*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 += fq; + m16 += fq; + m18 -= fq; - // q=11 - nread = neighborList[n+10*Np]; - fq = dist[nread]; - //fq = dist[15*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jz += fq; - m8 += fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 = fq; - m16 -= fq; - m18 = fq; + // q=13 + nread = neighborList[n + 12 * Np]; + fq = dist[nread]; + //fq = dist[16*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 -= fq; + m18 -= fq; - // q=12 - nread = neighborList[n+11*Np]; - fq = dist[nread]; - //fq = dist[6*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jz -= fq; - m8 -= fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 += fq; - m16 += fq; - m18 -= fq; + // q=14 + nread = neighborList[n + 13 * Np]; + fq = dist[nread]; + //fq = dist[7*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 += fq; + m18 += fq; - // q=13 - nread = neighborList[n+12*Np]; - fq = dist[nread]; - //fq = dist[16*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jz -= fq; - m8 -= fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 -= fq; - m16 -= fq; - m18 -= fq; + // q=15 + nread = neighborList[n + 14 * Np]; + fq = dist[nread]; + //fq = dist[17*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 = fq; + m17 += fq; + m18 -= fq; - // q=14 - nread = neighborList[n+13*Np]; - fq = dist[nread]; - //fq = dist[7*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jz += fq; - m8 += fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 -= fq; - m16 += fq; - m18 += fq; + // q=16 + nread = neighborList[n + 15 * Np]; + fq = dist[nread]; + //fq = dist[8*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 += fq; + m17 -= fq; + m18 += fq; - // q=15 - nread = neighborList[n+14*Np]; - fq = dist[nread]; - //fq = dist[17*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy += fq; - m6 += fq; - jz += fq; - m8 += fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 = fq; - m17 += fq; - m18 -= fq; + // q=17 + //fq = dist[18*Np+n]; + nread = neighborList[n + 16 * Np]; + fq = dist[nread]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 += fq; + m18 += fq; - // q=16 - nread = neighborList[n+15*Np]; - fq = dist[nread]; - //fq = dist[8*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy -= fq; - m6 -= fq; - jz -= fq; - m8 -= fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 += fq; - m17 -= fq; - m18 += fq; + // q=18 + nread = neighborList[n + 17 * Np]; + fq = dist[nread]; + //fq = dist[9*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 -= fq; + m18 -= fq; - // q=17 - //fq = dist[18*Np+n]; - nread = neighborList[n+16*Np]; - fq = dist[nread]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy += fq; - m6 += fq; - jz -= fq; - m8 -= fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 -= fq; - m17 += fq; - m18 += fq; + //..............incorporate external force................................................ + //..............carry out relaxation process............................................... + m1 = m1 + + rlx_setA * + ((19 * (jx * jx + jy * jy + jz * jz) / rho - 11 * rho) - m1); + m2 = m2 + + rlx_setA * + ((3 * rho - 5.5 * (jx * jx + jy * jy + jz * jz) / rho) - m2); + m4 = m4 + rlx_setB * ((-0.6666666666666666 * jx) - m4); + m6 = m6 + rlx_setB * ((-0.6666666666666666 * jy) - m6); + m8 = m8 + rlx_setB * ((-0.6666666666666666 * jz) - m8); + m9 = m9 + rlx_setA * (((2 * jx * jx - jy * jy - jz * jz) / rho) - m9); + m10 = + m10 + + rlx_setA * (-0.5 * ((2 * jx * jx - jy * jy - jz * jz) / rho) - m10); + m11 = m11 + rlx_setA * (((jy * jy - jz * jz) / rho) - m11); + m12 = m12 + rlx_setA * (-0.5 * ((jy * jy - jz * jz) / rho) - m12); + m13 = m13 + rlx_setA * ((jx * jy / rho) - m13); + m14 = m14 + rlx_setA * ((jy * jz / rho) - m14); + m15 = m15 + rlx_setA * ((jx * jz / rho) - m15); + m16 = m16 + rlx_setB * (-m16); + m17 = m17 + rlx_setB * (-m17); + m18 = m18 + rlx_setB * (-m18); + //....................................................................................................... + //.................inverse transformation...................................................... - // q=18 - nread = neighborList[n+17*Np]; - fq = dist[nread]; - //fq = dist[9*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy -= fq; - m6 -= fq; - jz += fq; - m8 += fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 -= fq; - m17 -= fq; - m18 -= fq; + // q=0 + fq = mrt_V1 * rho - mrt_V2 * m1 + mrt_V3 * m2; + dist[n] = fq; - //..............incorporate external force................................................ - //..............carry out relaxation process............................................... - m1 = m1 + rlx_setA*((19*(jx*jx+jy*jy+jz*jz)/rho - 11*rho) - m1); - m2 = m2 + rlx_setA*((3*rho - 5.5*(jx*jx+jy*jy+jz*jz)/rho) - m2); - m4 = m4 + rlx_setB*((-0.6666666666666666*jx) - m4); - m6 = m6 + rlx_setB*((-0.6666666666666666*jy) - m6); - m8 = m8 + rlx_setB*((-0.6666666666666666*jz) - m8); - m9 = m9 + rlx_setA*(((2*jx*jx-jy*jy-jz*jz)/rho) - m9); - m10 = m10 + rlx_setA*(-0.5*((2*jx*jx-jy*jy-jz*jz)/rho) - m10); - m11 = m11 + rlx_setA*(((jy*jy-jz*jz)/rho) - m11); - m12 = m12 + rlx_setA*(-0.5*((jy*jy-jz*jz)/rho) - m12); - m13 = m13 + rlx_setA*((jx*jy/rho) - m13); - m14 = m14 + rlx_setA*((jy*jz/rho) - m14); - m15 = m15 + rlx_setA*((jx*jz/rho) - m15); - m16 = m16 + rlx_setB*( - m16); - m17 = m17 + rlx_setB*( - m17); - m18 = m18 + rlx_setB*( - m18); - //....................................................................................................... - //.................inverse transformation...................................................... + // q = 1 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jx - m4) + + mrt_V6 * (m9 - m10) + 0.16666666 * Fx; + nread = neighborList[n + Np]; + dist[nread] = fq; - // q=0 - fq = mrt_V1*rho-mrt_V2*m1+mrt_V3*m2; - dist[n] = fq; + // q=2 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m4 - jx) + + mrt_V6 * (m9 - m10) - 0.16666666 * Fx; + nread = neighborList[n]; + dist[nread] = fq; - // q = 1 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jx-m4)+mrt_V6*(m9-m10)+0.16666666*Fx; - nread = neighborList[n+Np]; - dist[nread] = fq; + // q = 3 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jy - m6) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12) + 0.16666666 * Fy; + nread = neighborList[n + 3 * Np]; + dist[nread] = fq; - // q=2 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m4-jx)+mrt_V6*(m9-m10) - 0.16666666*Fx; - nread = neighborList[n]; - dist[nread] = fq; + // q = 4 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m6 - jy) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12) - 0.16666666 * Fy; + nread = neighborList[n + 2 * Np]; + dist[nread] = fq; - // q = 3 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jy-m6)+mrt_V7*(m10-m9)+mrt_V8*(m11-m12) + 0.16666666*Fy; - nread = neighborList[n+3*Np]; - dist[nread] = fq; + // q = 5 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jz - m8) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11) + 0.16666666 * Fz; + nread = neighborList[n + 5 * Np]; + dist[nread] = fq; - // q = 4 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m6-jy)+mrt_V7*(m10-m9)+mrt_V8*(m11-m12) - 0.16666666*Fy; - nread = neighborList[n+2*Np]; - dist[nread] = fq; + // q = 6 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m8 - jz) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11) - 0.16666666 * Fz; + nread = neighborList[n + 4 * Np]; + dist[nread] = fq; - // q = 5 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jz-m8)+mrt_V7*(m10-m9)+mrt_V8*(m12-m11) + 0.16666666*Fz; - nread = neighborList[n+5*Np]; - dist[nread] = fq; + // q = 7 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jy) + + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m16 - m17) + + 0.08333333333 * (Fx + Fy); + nread = neighborList[n + 7 * Np]; + dist[nread] = fq; - // q = 6 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m8-jz)+mrt_V7*(m10-m9)+mrt_V8*(m12-m11) - 0.16666666*Fz; - nread = neighborList[n+4*Np]; - dist[nread] = fq; + // q = 8 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jy) - + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m17 - m16) - + 0.08333333333 * (Fx + Fy); + nread = neighborList[n + 6 * Np]; + dist[nread] = fq; - // q = 7 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jx+jy)+0.025*(m4+m6) - +mrt_V7*m9+mrt_V11*m10+mrt_V8*m11 - +mrt_V12*m12+0.25*m13+0.125*(m16-m17) + 0.08333333333*(Fx+Fy); - nread = neighborList[n+7*Np]; - dist[nread] = fq; + // q = 9 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jy) + + 0.025 * (m4 - m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 + 0.125 * (m16 + m17) + + 0.08333333333 * (Fx - Fy); + nread = neighborList[n + 9 * Np]; + dist[nread] = fq; - // q = 8 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2-0.1*(jx+jy)-0.025*(m4+m6) +mrt_V7*m9+mrt_V11*m10+mrt_V8*m11 - +mrt_V12*m12+0.25*m13+0.125*(m17-m16) - 0.08333333333*(Fx+Fy); - nread = neighborList[n+6*Np]; - dist[nread] = fq; + // q = 10 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jx) + + 0.025 * (m6 - m4) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 - 0.125 * (m16 + m17) - + 0.08333333333 * (Fx - Fy); + nread = neighborList[n + 8 * Np]; + dist[nread] = fq; - // q = 9 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jx-jy)+0.025*(m4-m6) - +mrt_V7*m9+mrt_V11*m10+mrt_V8*m11 - +mrt_V12*m12-0.25*m13+0.125*(m16+m17) + 0.08333333333*(Fx-Fy); - nread = neighborList[n+9*Np]; - dist[nread] = fq; + // q = 11 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jz) + + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m18 - m16) + + 0.08333333333 * (Fx + Fz); + nread = neighborList[n + 11 * Np]; + dist[nread] = fq; - // q = 10 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jy-jx)+0.025*(m6-m4) - +mrt_V7*m9+mrt_V11*m10+mrt_V8*m11 - +mrt_V12*m12-0.25*m13-0.125*(m16+m17)- 0.08333333333*(Fx-Fy); - nread = neighborList[n+8*Np]; - dist[nread] = fq; + // q = 12 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jz) - + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m16 - m18) - + 0.08333333333 * (Fx + Fz); + nread = neighborList[n + 10 * Np]; + dist[nread] = fq; - // q = 11 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jx+jz)+0.025*(m4+m8) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12+0.25*m15+0.125*(m18-m16) + 0.08333333333*(Fx+Fz); - nread = neighborList[n+11*Np]; - dist[nread] = fq; + // q = 13 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jz) + + 0.025 * (m4 - m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 - 0.125 * (m16 + m18) + + 0.08333333333 * (Fx - Fz); + nread = neighborList[n + 13 * Np]; + dist[nread] = fq; - // q = 12 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2-0.1*(jx+jz)-0.025*(m4+m8) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12+0.25*m15+0.125*(m16-m18) - 0.08333333333*(Fx+Fz); - nread = neighborList[n+10*Np]; - dist[nread]= fq; + // q= 14 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jx) + + 0.025 * (m8 - m4) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 + 0.125 * (m16 + m18) - + 0.08333333333 * (Fx - Fz); + nread = neighborList[n + 12 * Np]; + dist[nread] = fq; - // q = 13 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jx-jz)+0.025*(m4-m8) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12-0.25*m15-0.125*(m16+m18) + 0.08333333333*(Fx-Fz); - nread = neighborList[n+13*Np]; - dist[nread] = fq; + // q = 15 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy + jz) + + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m17 - m18) + 0.08333333333 * (Fy + Fz); + nread = neighborList[n + 15 * Np]; + dist[nread] = fq; - // q= 14 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jz-jx)+0.025*(m8-m4) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12-0.25*m15+0.125*(m16+m18) - 0.08333333333*(Fx-Fz); - nread = neighborList[n+12*Np]; - dist[nread] = fq; + // q = 16 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jy + jz) - + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m18 - m17) - 0.08333333333 * (Fy + Fz); + nread = neighborList[n + 14 * Np]; + dist[nread] = fq; + // q = 17 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jz) + + 0.025 * (m6 - m8) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 + + 0.125 * (m17 + m18) + 0.08333333333 * (Fy - Fz); + nread = neighborList[n + 17 * Np]; + dist[nread] = fq; - // q = 15 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jy+jz)+0.025*(m6+m8) - -mrt_V6*m9-mrt_V7*m10+0.25*m14+0.125*(m17-m18) + 0.08333333333*(Fy+Fz); - nread = neighborList[n+15*Np]; - dist[nread] = fq; - - // q = 16 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2-0.1*(jy+jz)-0.025*(m6+m8) - -mrt_V6*m9-mrt_V7*m10+0.25*m14+0.125*(m18-m17)- 0.08333333333*(Fy+Fz); - nread = neighborList[n+14*Np]; - dist[nread] = fq; - - - // q = 17 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jy-jz)+0.025*(m6-m8) - -mrt_V6*m9-mrt_V7*m10-0.25*m14+0.125*(m17+m18) + 0.08333333333*(Fy-Fz); - nread = neighborList[n+17*Np]; - dist[nread] = fq; - - // q = 18 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jz-jy)+0.025*(m8-m6) - -mrt_V6*m9-mrt_V7*m10-0.25*m14-0.125*(m17+m18) - 0.08333333333*(Fy-Fz); - nread = neighborList[n+16*Np]; - dist[nread] = fq; - - } + // q = 18 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jy) + + 0.025 * (m8 - m6) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 - + 0.125 * (m17 + m18) - 0.08333333333 * (Fy - Fz); + nread = neighborList[n + 16 * Np]; + dist[nread] = fq; + } } -extern "C" void ScaLBL_D3Q19_AAeven_Compact(char * ID, double *dist, int Np) -{ +extern "C" void ScaLBL_D3Q19_AAeven_Compact(char *ID, double *dist, int Np) { - for (int n=0; n 0){ - value = Den[n]; - f_even[n] = 0.3333333333333333*value; - f_odd[n] = 0.1111111111111111*value; //double(100*n)+1.f; - f_even[N+n] = 0.1111111111111111*value; //double(100*n)+2.f; - f_odd[N+n] = 0.1111111111111111*value; //double(100*n)+3.f; - f_even[2*N+n] = 0.1111111111111111*value; //double(100*n)+4.f; - f_odd[2*N+n] = 0.1111111111111111*value; //double(100*n)+5.f; - f_even[3*N+n] = 0.1111111111111111*value; //double(100*n)+6.f; - } - else{ - for(int q=0; q<3; q++){ - f_even[q*N+n] = -1.0; - f_odd[q*N+n] = -1.0; - } - f_even[3*N+n] = -1.0; - } - } + if (ID[n] > 0) { + value = Den[n]; + f_even[n] = 0.3333333333333333 * value; + f_odd[n] = 0.1111111111111111 * value; //double(100*n)+1.f; + f_even[N + n] = 0.1111111111111111 * value; //double(100*n)+2.f; + f_odd[N + n] = 0.1111111111111111 * value; //double(100*n)+3.f; + f_even[2 * N + n] = 0.1111111111111111 * value; //double(100*n)+4.f; + f_odd[2 * N + n] = 0.1111111111111111 * value; //double(100*n)+5.f; + f_even[3 * N + n] = 0.1111111111111111 * value; //double(100*n)+6.f; + } else { + for (int q = 0; q < 3; q++) { + f_even[q * N + n] = -1.0; + f_odd[q * N + n] = -1.0; + } + f_even[3 * N + n] = -1.0; + } + } } //************************************************************************* -extern "C" void ScaLBL_D3Q7_Swap(char *ID, double *disteven, double *distodd, int Nx, int Ny, int Nz) -{ - int i,j,k,n,nn,N; - // distributions - double f1,f2,f3,f4,f5,f6; - - N = Nx*Ny*Nz; - - for (n=0; n 0){ - //........................................................................ - // Retrieve even distributions from the local node (swap convention) - // f0 = disteven[n]; // Does not particupate in streaming - f1 = distodd[n]; - f3 = distodd[N+n]; - f5 = distodd[2*N+n]; - //........................................................................ - - //........................................................................ - // Retrieve odd distributions from neighboring nodes (swap convention) - //........................................................................ - nn = n+1; // neighbor index (pull convention) - if (!(i+1 0) { + //........................................................................ + // Retrieve even distributions from the local node (swap convention) + // f0 = disteven[n]; // Does not particupate in streaming + f1 = distodd[n]; + f3 = distodd[N + n]; + f5 = distodd[2 * N + n]; + //........................................................................ + + //........................................................................ + // Retrieve odd distributions from neighboring nodes (swap convention) + //........................................................................ + nn = n + 1; // neighbor index (pull convention) + if (!(i + 1 < Nx)) + nn -= Nx; // periodic BC along the x-boundary + //if (i+1 0 ){ - // Read the distributions - f0 = disteven[n]; - f2 = disteven[N+n]; - f4 = disteven[2*N+n]; - f6 = disteven[3*N+n]; - f1 = distodd[n]; - f3 = distodd[N+n]; - f5 = distodd[2*N+n]; - // Compute the density - Den[n] = f0+f1+f2+f3+f4+f5+f6; - } - } +extern "C" void ScaLBL_D3Q7_Density(char *ID, double *disteven, double *distodd, + double *Den, int Nx, int Ny, int Nz) { + char id; + int n; + double f0, f1, f2, f3, f4, f5, f6; + int N = Nx * Ny * Nz; + + for (n = 0; n < N; n++) { + id = ID[n]; + if (id > 0) { + // Read the distributions + f0 = disteven[n]; + f2 = disteven[N + n]; + f4 = disteven[2 * N + n]; + f6 = disteven[3 * N + n]; + f1 = distodd[n]; + f3 = distodd[N + n]; + f5 = distodd[2 * N + n]; + // Compute the density + Den[n] = f0 + f1 + f2 + f3 + f4 + f5 + f6; + } + } } diff --git a/cpu/D3Q7BC.cpp b/cpu/D3Q7BC.cpp index 1c255495..8e31d4ef 100644 --- a/cpu/D3Q7BC.cpp +++ b/cpu/D3Q7BC.cpp @@ -1,801 +1,877 @@ // CPU Functions for D3Q7 Lattice Boltzmann Methods -// Boundary Conditions +// Boundary Conditions -extern "C" void ScaLBL_Solid_Dirichlet_D3Q7(double *dist,double *BoundaryValue,int *BounceBackDist_list,int *BounceBackSolid_list,int N){ +extern "C" void ScaLBL_Solid_Dirichlet_D3Q7(double *dist, double *BoundaryValue, + int *BounceBackDist_list, + int *BounceBackSolid_list, int N) { int idx; - int iq,ib; - double value_b,value_q; - for (idx=0; idx #include -extern "C" int ScaLBL_SetDevice(int rank){ - return 0; +extern "C" int ScaLBL_SetDevice(int rank) { return 0; } + +extern "C" void ScaLBL_AllocateZeroCopy(void **address, size_t size) { + //cudaMalloc(address,size); + (*address) = _mm_malloc(size, 64); + memset(*address, 0, size); + + if (*address == NULL) { + printf("Memory allocation failed! \n"); + } } -extern "C" void ScaLBL_AllocateZeroCopy(void** address, size_t size){ - //cudaMalloc(address,size); - (*address) = _mm_malloc(size,64); - memset(*address,0,size); - - if (*address==NULL){ - printf("Memory allocation failed! \n"); - } +extern "C" void ScaLBL_AllocateDeviceMemory(void **address, size_t size) { + //cudaMalloc(address,size); + (*address) = _mm_malloc(size, 64); + memset(*address, 0, size); + + if (*address == NULL) { + printf("Memory allocation failed! \n"); + } } -extern "C" void ScaLBL_AllocateDeviceMemory(void** address, size_t size){ - //cudaMalloc(address,size); - (*address) = _mm_malloc(size,64); - memset(*address,0,size); - - if (*address==NULL){ - printf("Memory allocation failed! \n"); - } +extern "C" void ScaLBL_FreeDeviceMemory(void *pointer) { _mm_free(pointer); } + +extern "C" void ScaLBL_CopyToDevice(void *dest, const void *source, + size_t size) { + // cudaMemcpy(dest,source,size,cudaMemcpyHostToDevice); + memcpy(dest, source, size); } -extern "C" void ScaLBL_FreeDeviceMemory(void* pointer){ - _mm_free(pointer); +extern "C" void ScaLBL_CopyToHost(void *dest, const void *source, size_t size) { + // cudaMemcpy(dest,source,size,cudaMemcpyDeviceToHost); + memcpy(dest, source, size); } -extern "C" void ScaLBL_CopyToDevice(void* dest, const void* source, size_t size){ -// cudaMemcpy(dest,source,size,cudaMemcpyHostToDevice); - memcpy(dest, source, size); +extern "C" void ScaLBL_CopyToZeroCopy(void *dest, const void *source, + size_t size) { + // cudaMemcpy(dest,source,size,cudaMemcpyDeviceToHost); + memcpy(dest, source, size); } - -extern "C" void ScaLBL_CopyToHost(void* dest, const void* source, size_t size){ -// cudaMemcpy(dest,source,size,cudaMemcpyDeviceToHost); - memcpy(dest, source, size); -} - -extern "C" void ScaLBL_CopyToZeroCopy(void* dest, const void* source, size_t size){ -// cudaMemcpy(dest,source,size,cudaMemcpyDeviceToHost); - memcpy(dest, source, size); -} - -extern "C" void ScaLBL_DeviceBarrier(){ -// cudaDeviceSynchronize(); +extern "C" void ScaLBL_DeviceBarrier() { + // cudaDeviceSynchronize(); } diff --git a/cpu/FreeLee.cpp b/cpu/FreeLee.cpp index a111a2b6..3fe57030 100644 --- a/cpu/FreeLee.cpp +++ b/cpu/FreeLee.cpp @@ -3,2626 +3,4677 @@ #define STOKES -extern "C" void ScaLBL_D3Q19_FreeLeeModel_TwoFluid_Init(double *gqbar, double *mu_phi, double *ColorGrad, double Fx, double Fy, double Fz, int Np) -{ - int n; - double p = 1.0;//NOTE: take initial pressure p=1.0 - double chem; - double cg_x,cg_y,cg_z; +extern "C" void ScaLBL_D3Q19_FreeLeeModel_TwoFluid_Init(double *gqbar, + double *mu_phi, + double *ColorGrad, + double Fx, double Fy, + double Fz, int Np) { + int n; + double p = 1.0; //NOTE: take initial pressure p=1.0 + double chem; + double cg_x, cg_y, cg_z; - for (n=0; n 1.f) phi = 1.0; - if (phi < -1.f) phi = -1.0; - Den[idx] = rhoA + 0.5*(1.0-phi)*(rhoB-rhoA); + n = Map[idx]; + phi = Phi[n]; + if (phi > 1.f) + phi = 1.0; + if (phi < -1.f) + phi = -1.0; + Den[idx] = rhoA + 0.5 * (1.0 - phi) * (rhoB - rhoA); - //compute unit normal of color gradient - nx = ColorGrad[idx+0*Np]; - ny = ColorGrad[idx+1*Np]; - nz = ColorGrad[idx+2*Np]; - cg_mag = sqrt(nx*nx+ny*ny+nz*nz); - double ColorMag_temp = cg_mag; - if (cg_mag==0.0) ColorMag_temp=1.0; - nx = nx/ColorMag_temp; - ny = ny/ColorMag_temp; - nz = nz/ColorMag_temp; + //compute unit normal of color gradient + nx = ColorGrad[idx + 0 * Np]; + ny = ColorGrad[idx + 1 * Np]; + nz = ColorGrad[idx + 2 * Np]; + cg_mag = sqrt(nx * nx + ny * ny + nz * nz); + double ColorMag_temp = cg_mag; + if (cg_mag == 0.0) + ColorMag_temp = 1.0; + nx = nx / ColorMag_temp; + ny = ny / ColorMag_temp; + nz = nz / ColorMag_temp; - theta = M*cs2_inv*2.0*(1-phi*phi)/W; + theta = M * cs2_inv * 2.0 * (1 - phi * phi) / W; - hq[0*Np+idx]=0.3333333333333333*(phi); - hq[1*Np+idx]=0.1111111111111111*(phi+theta*nx); - hq[2*Np+idx]=0.1111111111111111*(phi-theta*nx); - hq[3*Np+idx]=0.1111111111111111*(phi+theta*ny); - hq[4*Np+idx]=0.1111111111111111*(phi-theta*ny); - hq[5*Np+idx]=0.1111111111111111*(phi+theta*nz); - hq[6*Np+idx]=0.1111111111111111*(phi-theta*nz); - - } + hq[0 * Np + idx] = 0.3333333333333333 * (phi); + hq[1 * Np + idx] = 0.1111111111111111 * (phi + theta * nx); + hq[2 * Np + idx] = 0.1111111111111111 * (phi - theta * nx); + hq[3 * Np + idx] = 0.1111111111111111 * (phi + theta * ny); + hq[4 * Np + idx] = 0.1111111111111111 * (phi - theta * ny); + hq[5 * Np + idx] = 0.1111111111111111 * (phi + theta * nz); + hq[6 * Np + idx] = 0.1111111111111111 * (phi - theta * nz); + } } -extern "C" void ScaLBL_D3Q7_AAodd_FreeLeeModel_PhaseField(int *neighborList, int *Map, double *hq, double *Den, double *Phi, - double rhoA, double rhoB, int start, int finish, int Np){ +extern "C" void ScaLBL_D3Q7_AAodd_FreeLeeModel_PhaseField( + int *neighborList, int *Map, double *hq, double *Den, double *Phi, + double rhoA, double rhoB, int start, int finish, int Np) { - int idx,nread; - double fq,phi; + int idx, nread; + double fq, phi; - for (int n=start; n 10Np => odd part of dist) + m1 = dist[nr1]; // reading the f1 data into register fq - //............Compute the Color Gradient................................... - nx = -3.0*1.0/18.0*(m1-m2+0.5*(m7-m8+m9-m10+m11-m12+m13-m14)); - ny = -3.0*1.0/18.0*(m3-m4+0.5*(m7-m8-m9+m10+m15-m16+m17-m18)); - nz = -3.0*1.0/18.0*(m5-m6+0.5*(m11-m12-m13+m14+m15-m16-m17+m18)); - //............Compute the Chemical Potential............................... - chem = 2.0*3.0/18.0*(m1+m2+m3+m4+m5+m6-6*phi+0.5*(m7+m8+m9+m10+m11+m12+m13+m14+m15+m16+m17+m18-12*phi));//intermediate var, i.e. the laplacian - chem = 4.0*beta*phi*(phi+1.0)*(phi-1.0)-kappa*chem; - //............Compute the Mixed Gradient................................... - mgx = -3.0*1.0/18.0*(mm1-mm2+0.5*(mm7-mm8+mm9-mm10+mm11-mm12+mm13-mm14)); - mgy = -3.0*1.0/18.0*(mm3-mm4+0.5*(mm7-mm8-mm9+mm10+mm15-mm16+mm17-mm18)); - mgz = -3.0*1.0/18.0*(mm5-mm6+0.5*(mm11-mm12-mm13+mm14+mm15-mm16-mm17+mm18)); + nr2 = neighborList[n + Np]; // neighbor 1 ( < 10Np => even part of dist) + m2 = dist[nr2]; // reading the f2 data into register fq - // q=0 - m0 = dist[n]; - // q=1 - nr1 = neighborList[n]; // neighbor 2 ( > 10Np => odd part of dist) - m1 = dist[nr1]; // reading the f1 data into register fq + // q=3 + nr3 = neighborList[n + 2 * Np]; // neighbor 4 + m3 = dist[nr3]; - nr2 = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) - m2 = dist[nr2]; // reading the f2 data into register fq + // q = 4 + nr4 = neighborList[n + 3 * Np]; // neighbor 3 + m4 = dist[nr4]; - // q=3 - nr3 = neighborList[n+2*Np]; // neighbor 4 - m3 = dist[nr3]; + // q=5 + nr5 = neighborList[n + 4 * Np]; + m5 = dist[nr5]; - // q = 4 - nr4 = neighborList[n+3*Np]; // neighbor 3 - m4 = dist[nr4]; + // q = 6 + nr6 = neighborList[n + 5 * Np]; + m6 = dist[nr6]; - // q=5 - nr5 = neighborList[n+4*Np]; - m5 = dist[nr5]; + // q=7 + nr7 = neighborList[n + 6 * Np]; + m7 = dist[nr7]; - // q = 6 - nr6 = neighborList[n+5*Np]; - m6 = dist[nr6]; + // q = 8 + nr8 = neighborList[n + 7 * Np]; + m8 = dist[nr8]; - // q=7 - nr7 = neighborList[n+6*Np]; - m7 = dist[nr7]; + // q=9 + nr9 = neighborList[n + 8 * Np]; + m9 = dist[nr9]; - // q = 8 - nr8 = neighborList[n+7*Np]; - m8 = dist[nr8]; + // q = 10 + nr10 = neighborList[n + 9 * Np]; + m10 = dist[nr10]; - // q=9 - nr9 = neighborList[n+8*Np]; - m9 = dist[nr9]; + // q=11 + nr11 = neighborList[n + 10 * Np]; + m11 = dist[nr11]; - // q = 10 - nr10 = neighborList[n+9*Np]; - m10 = dist[nr10]; + // q=12 + nr12 = neighborList[n + 11 * Np]; + m12 = dist[nr12]; - // q=11 - nr11 = neighborList[n+10*Np]; - m11 = dist[nr11]; + // q=13 + nr13 = neighborList[n + 12 * Np]; + m13 = dist[nr13]; - // q=12 - nr12 = neighborList[n+11*Np]; - m12 = dist[nr12]; + // q=14 + nr14 = neighborList[n + 13 * Np]; + m14 = dist[nr14]; - // q=13 - nr13 = neighborList[n+12*Np]; - m13 = dist[nr13]; + // q=15 + nr15 = neighborList[n + 14 * Np]; + m15 = dist[nr15]; - // q=14 - nr14 = neighborList[n+13*Np]; - m14 = dist[nr14]; + // q=16 + nr16 = neighborList[n + 15 * Np]; + m16 = dist[nr16]; - // q=15 - nr15 = neighborList[n+14*Np]; - m15 = dist[nr15]; + // q=17 + nr17 = neighborList[n + 16 * Np]; + m17 = dist[nr17]; - // q=16 - nr16 = neighborList[n+15*Np]; - m16 = dist[nr16]; + // q=18 + nr18 = neighborList[n + 17 * Np]; + m18 = dist[nr18]; - // q=17 - nr17 = neighborList[n+16*Np]; - m17 = dist[nr17]; + //compute fluid velocity + ux = 3.0 / rho0 * + (m1 - m2 + m7 - m8 + m9 - m10 + m11 - m12 + m13 - m14 + + 0.5 * (chem * nx + Fx) / 3.0); + uy = 3.0 / rho0 * + (m3 - m4 + m7 - m8 - m9 + m10 + m15 - m16 + m17 - m18 + + 0.5 * (chem * ny + Fy) / 3.0); + uz = 3.0 / rho0 * + (m5 - m6 + m11 - m12 - m13 + m14 + m15 - m16 - m17 + m18 + + 0.5 * (chem * nz + Fz) / 3.0); + //compute pressure + p = (m0 + m2 + m1 + m4 + m3 + m6 + m5 + m8 + m7 + m10 + m9 + m12 + m11 + + m14 + m13 + m16 + m15 + m18 + m17) + + 0.5 * (rhoA - rhoB) / 2.0 / 3.0 * (ux * nx + uy * ny + uz * nz); - // q=18 - nr18 = neighborList[n+17*Np]; - m18 = dist[nr18]; + //compute equilibrium distributions + feq0 = 0.3333333333333333 * p - + 0.25 * (Fx * ux + Fy * uy + Fz * uz) * + (-0.6666666666666666 + ux * ux + uy * uy + uz * uz) - + 0.16666666666666666 * rho0 * (ux * ux + uy * uy + uz * uz) - + 0.5 * (-(nx * ux) - ny * uy - nz * uz) * + (-0.08333333333333333 * (rhoA - rhoB) * + (ux * ux + uy * uy + uz * uz) + + chem * (0.3333333333333333 - + 0.5 * (ux * ux + uy * uy + uz * uz))); + feq1 = 0.05555555555555555 * p - + 0.08333333333333333 * rho0 * + (-ux * ux + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + uz * uz)) - + 0.125 * (Fx * (-1. + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * ux * ux + + 0.3333333333333333 * + (-2. * ux + ux * ux + uy * uy + uz * uz)) - + 0.0625 * (nx - nx * ux - ny * uy - nz * uz) * + (2 * chem * ux * ux - + 0.3333333333333333 * + ((-rhoA + rhoB) * ux * ux + + 2 * chem * (-2 * ux + ux * ux + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (-2 * ux + ux * ux + + uy * uy + uz * uz))); + feq2 = + 0.05555555555555555 * p - + 0.08333333333333333 * rho0 * + (-ux * ux + + 0.3333333333333333 * (2 * ux + ux * ux + uy * uy + uz * uz)) - + 0.125 * (Fx + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * ux * ux + + 0.3333333333333333 * (2. * ux + ux * ux + uy * uy + uz * uz)) - + 0.0625 * (nx + nx * ux + ny * uy + nz * uz) * + (-2. * chem * ux * ux + + 0.1111111111111111 * + (-4. * chem + + rhoB * (-2. * ux - 1. * ux * ux - 1. * uy * uy - + 1. * uz * uz) + + rhoA * (2. * ux + ux * ux + uy * uy + uz * uz)) + + 0.3333333333333333 * ((-1. * rhoA + rhoB) * ux * ux + + chem * (4. * ux + 2. * ux * ux + + 2. * uy * uy + 2. * uz * uz))); + feq3 = + 0.05555555555555555 * p - + 0.08333333333333333 * rho0 * + (-uy * uy + + 0.3333333333333333 * (ux * ux - 2 * uy + uy * uy + uz * uz)) - + 0.125 * (Fx * ux + Fy * (-1. + uy) + Fz * uz) * + (-0.2222222222222222 - 1. * uy * uy + + 0.3333333333333333 * (ux * ux - 2. * uy + uy * uy + uz * uz)) - + 0.0625 * (ny - nx * ux - ny * uy - nz * uz) * + (2 * chem * uy * uy - + 0.3333333333333333 * + ((-rhoA + rhoB) * uy * uy + + 2 * chem * (ux * ux - 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - + (rhoA - rhoB) * (ux * ux - 2 * uy + uy * uy + uz * uz))); + feq4 = + 0.05555555555555555 * p - + 0.08333333333333333 * rho0 * + (-uy * uy + + 0.3333333333333333 * (ux * ux + 2 * uy + uy * uy + uz * uz)) - + 0.125 * (Fy + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * uy * uy + + 0.3333333333333333 * (ux * ux + 2. * uy + uy * uy + uz * uz)) - + 0.0625 * (ny + nx * ux + ny * uy + nz * uz) * + (-2. * chem * uy * uy + + 0.1111111111111111 * + (-4. * chem + + rhoB * (-1. * ux * ux - 2. * uy - 1. * uy * uy - + 1. * uz * uz) + + rhoA * (ux * ux + 2. * uy + uy * uy + uz * uz)) + + 0.3333333333333333 * ((-1. * rhoA + rhoB) * uy * uy + + chem * (2. * ux * ux + 4. * uy + + 2. * uy * uy + 2. * uz * uz))); + feq5 = + 0.05555555555555555 * p - + 0.08333333333333333 * rho0 * + (-uz * uz + + 0.3333333333333333 * (ux * ux + uy * uy + (-2 + uz) * uz)) - + 0.125 * (Fx * ux + Fy * uy + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * uz * uz + + 0.3333333333333333 * (ux * ux + uy * uy + (-2. + uz) * uz)) - + 0.0625 * (nx * ux + ny * uy + nz * (-1. + uz)) * + (-2. * chem * uz * uz + + 0.1111111111111111 * + (-4. * chem + + rhoB * + (-1. * ux * ux - 1. * uy * uy + (2. - 1. * uz) * uz) + + rhoA * (ux * ux + uy * uy + (-2. + uz) * uz)) + + 0.3333333333333333 * ((-1. * rhoA + rhoB) * uz * uz + + chem * (2. * ux * ux + 2. * uy * uy + + uz * (-4. + 2. * uz)))); + feq6 = 0.05555555555555555 * p - + 0.08333333333333333 * rho0 * + (-uz * uz + + 0.3333333333333333 * (ux * ux + uy * uy + uz * (2 + uz))) - + 0.125 * (Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * uz * uz + + 0.3333333333333333 * (ux * ux + uy * uy + uz * (2. + uz))) - + 0.0625 * (nz + nx * ux + ny * uy + nz * uz) * + (-2. * chem * uz * uz + + 0.1111111111111111 * + (-4. * chem + + rhoB * (-1. * ux * ux - 1. * uy * uy + + (-2. - 1. * uz) * uz) + + rhoA * (ux * ux + uy * uy + uz * (2. + uz))) + + 0.3333333333333333 * ((-1. * rhoA + rhoB) * uz * uz + + chem * (2. * ux * ux + 2. * uy * uy + + uz * (4. + 2. * uz)))); + feq7 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-(ux + uy) * (ux + uy) + + 0.3333333333333333 * + (-2 * ux + ux * ux - 2 * uy + uy * uy + uz * uz)) - + 0.0625 * (Fx * (-1. + ux) + Fy * (-1. + uy) + Fz * uz) * + (-0.2222222222222222 - 1. * ux * ux - 2. * ux * uy - + 1. * uy * uy + + 0.3333333333333333 * + (-2. * ux + ux * ux - 2. * uy + uy * uy + uz * uz)) - + 0.03125 * (nx + ny - nx * ux - ny * uy - nz * uz) * + (2 * chem * (ux + uy) * (ux + uy) + + 0.3333333333333333 * + ((rhoA - rhoB) * (ux + uy) * (ux + uy) - + 2 * chem * + (-2 * ux + ux * ux - 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (-2 * ux + ux * ux - 2 * uy + + uy * uy + uz * uz))); + feq8 = 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-(ux + uy) * (ux + uy) + + 0.3333333333333333 * + (2 * ux + ux * ux + 2 * uy + uy * uy + uz * uz)) - + 0.0625 * (Fx + Fy + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * ux * ux - 2. * ux * uy - + 1. * uy * uy + + 0.3333333333333333 * + (2. * ux + ux * ux + 2. * uy + uy * uy + uz * uz)) - + 0.03125 * (-(nx * (1 + ux)) - ny * (1 + uy) - nz * uz) * + (2 * chem * (ux + uy) * (ux + uy) - + 0.3333333333333333 * + (-((rhoA - rhoB) * (ux + uy) * (ux + uy)) + + 2 * chem * + (2 * ux + ux * ux + 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (2 * ux + ux * ux + 2 * uy + + uy * uy + uz * uz))); + feq9 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-(ux - uy) * (ux - uy) + + 0.3333333333333333 * + (-2 * ux + ux * ux + 2 * uy + uy * uy + uz * uz)) - + 0.0625 * (Fy + Fx * (-1. + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * ux * ux + 2. * ux * uy - + 1. * uy * uy + + 0.3333333333333333 * + (-2. * ux + ux * ux + 2. * uy + uy * uy + uz * uz)) - + 0.03125 * (nx - nx * ux - ny * (1 + uy) - nz * uz) * + (2 * chem * (ux - uy) * (ux - uy) - + 0.3333333333333333 * + (-((rhoA - rhoB) * (ux - uy) * (ux - uy)) + + 2 * chem * + (-2 * ux + ux * ux + 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (-2 * ux + ux * ux + 2 * uy + + uy * uy + uz * uz))); + feq10 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-(ux - uy) * (ux - uy) + + 0.3333333333333333 * + (2 * ux + ux * ux - 2 * uy + uy * uy + uz * uz)) - + 0.0625 * (Fx * (1 + ux) + Fy * (-1. + uy) + Fz * uz) * + (-0.2222222222222222 - 1. * ux * ux + 2. * ux * uy - + 1. * uy * uy + + 0.3333333333333333 * + (2. * ux + ux * ux - 2. * uy + uy * uy + uz * uz)) - + 0.03125 * (ny - nx * (1 + ux) - ny * uy - nz * uz) * + (2 * chem * (ux - uy) * (ux - uy) - + 0.3333333333333333 * + (-((rhoA - rhoB) * (ux - uy) * (ux - uy)) + + 2 * chem * + (2 * ux + ux * ux - 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (2 * ux + ux * ux - 2 * uy + + uy * uy + uz * uz))); + feq11 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-(ux + uz) * (ux + uz) + + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) - + 0.0625 * (Fx * (-1. + ux) + Fy * uy + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * ux * ux - 2. * ux * uz - + 1. * uz * uz + + 0.3333333333333333 * + (-2. * ux + ux * ux + uy * uy + (-2. + uz) * uz)) - + 0.03125 * (nx + nz - nx * ux - ny * uy - nz * uz) * + (2 * chem * (ux + uz) * (ux + uz) + + 0.3333333333333333 * + ((rhoA - rhoB) * (ux + uz) * (ux + uz) - + 2 * chem * + (-2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (-2 * ux + ux * ux + uy * uy + + (-2 + uz) * uz))); + feq12 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-(ux + uz) * (ux + uz) + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + uz * (2 + uz))) - + 0.0625 * (Fx + Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * ux * ux - 2. * ux * uz - + 1. * uz * uz + + 0.3333333333333333 * + (2. * ux + ux * ux + uy * uy + uz * (2. + uz))) - + 0.03125 * (-(nx * (1 + ux)) - ny * uy - nz * (1 + uz)) * + (2 * chem * (ux + uz) * (ux + uz) - + 0.3333333333333333 * + (-((rhoA - rhoB) * (ux + uz) * (ux + uz)) + + 2 * chem * (2 * ux + ux * ux + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (2 * ux + ux * ux + uy * uy + + uz * (2 + uz)))); + feq13 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-(ux - uz) * (ux - uz) + + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + uz * (2 + uz))) - + 0.0625 * (Fz + Fx * (-1. + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * ux * ux + 2. * ux * uz - + 1. * uz * uz + + 0.3333333333333333 * + (-2. * ux + ux * ux + uy * uy + uz * (2. + uz))) - + 0.03125 * (nx - nx * ux - ny * uy - nz * (1 + uz)) * + (2 * chem * (ux - uz) * (ux - uz) - + 0.3333333333333333 * + (-((rhoA - rhoB) * (ux - uz) * (ux - uz)) + + 2 * chem * + (-2 * ux + ux * ux + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (-2 * ux + ux * ux + uy * uy + + uz * (2 + uz)))); + feq14 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-(ux - uz) * (ux - uz) + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) - + 0.0625 * (Fx * (1 + ux) + Fy * uy + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * ux * ux + 2. * ux * uz - + 1. * uz * uz + + 0.3333333333333333 * + (2. * ux + ux * ux + uy * uy + (-2. + uz) * uz)) - + 0.03125 * (nz - nx * (1 + ux) - ny * uy - nz * uz) * + (2 * chem * (ux - uz) * (ux - uz) - + 0.3333333333333333 * + (-((rhoA - rhoB) * (ux - uz) * (ux - uz)) + + 2 * chem * + (2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (2 * ux + ux * ux + uy * uy + + (-2 + uz) * uz))); + feq15 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-(uy + uz) * (uy + uz) + + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + (-2 + uz) * uz)) - + 0.0625 * (Fx * ux + Fy * (-1. + uy) + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * uy * uy - 2. * uy * uz - + 1. * uz * uz + + 0.3333333333333333 * + (ux * ux - 2. * uy + uy * uy + (-2. + uz) * uz)) - + 0.03125 * (ny + nz - nx * ux - ny * uy - nz * uz) * + (2 * chem * (uy + uz) * (uy + uz) + + 0.3333333333333333 * + ((rhoA - rhoB) * (uy + uz) * (uy + uz) - + 2 * chem * + (ux * ux - 2 * uy + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (ux * ux - 2 * uy + uy * uy + + (-2 + uz) * uz))); + feq16 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-(uy + uz) * (uy + uz) + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + uz * (2 + uz))) - + 0.0625 * (Fy + Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * uy * uy - 2. * uy * uz - + 1. * uz * uz + + 0.3333333333333333 * + (ux * ux + 2. * uy + uy * uy + uz * (2. + uz))) - + 0.03125 * (-(nx * ux) - ny * (1 + uy) - nz * (1 + uz)) * + (2 * chem * (uy + uz) * (uy + uz) - + 0.3333333333333333 * + (-((rhoA - rhoB) * (uy + uz) * (uy + uz)) + + 2 * chem * (ux * ux + 2 * uy + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (ux * ux + 2 * uy + uy * uy + + uz * (2 + uz)))); + feq17 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-(uy - uz) * (uy - uz) + + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + uz * (2 + uz))) - + 0.0625 * (Fz + Fx * ux + Fy * (-1. + uy) + Fz * uz) * + (-0.2222222222222222 - 1. * uy * uy + 2. * uy * uz - + 1. * uz * uz + + 0.3333333333333333 * + (ux * ux - 2. * uy + uy * uy + uz * (2. + uz))) - + 0.03125 * (ny - nx * ux - ny * uy - nz * (1 + uz)) * + (2 * chem * (uy - uz) * (uy - uz) - + 0.3333333333333333 * + (-((rhoA - rhoB) * (uy - uz) * (uy - uz)) + + 2 * chem * (ux * ux - 2 * uy + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (ux * ux - 2 * uy + uy * uy + + uz * (2 + uz)))); + feq18 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-(uy - uz) * (uy - uz) + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + (-2 + uz) * uz)) - + 0.0625 * (Fx * ux + Fy * (1 + uy) + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * uy * uy + 2. * uy * uz - + 1. * uz * uz + + 0.3333333333333333 * + (ux * ux + 2. * uy + uy * uy + (-2. + uz) * uz)) - + 0.03125 * (nz - nx * ux - ny * (1 + uy) - nz * uz) * + (2 * chem * (uy - uz) * (uy - uz) - + 0.3333333333333333 * + (-((rhoA - rhoB) * (uy - uz) * (uy - uz)) + + 2 * chem * + (ux * ux + 2 * uy + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (ux * ux + 2 * uy + uy * uy + + (-2 + uz) * uz))); - //compute fluid velocity - ux = 3.0/rho0*(m1-m2+m7-m8+m9-m10+m11-m12+m13-m14+0.5*(chem*nx+Fx)/3.0); - uy = 3.0/rho0*(m3-m4+m7-m8-m9+m10+m15-m16+m17-m18+0.5*(chem*ny+Fy)/3.0); - uz = 3.0/rho0*(m5-m6+m11-m12-m13+m14+m15-m16-m17+m18+0.5*(chem*nz+Fz)/3.0); - //compute pressure - p = (m0+m2+m1+m4+m3+m6+m5+m8+m7+m10+m9+m12+m11+m14+m13+m16+m15+m18+m17) - +0.5*(rhoA-rhoB)/2.0/3.0*(ux*nx+uy*ny+uz*nz); + //------------------------------------------------- BCK collison ------------------------------------------------------------// + // q=0 + dist[n] = + m0 - (m0 - feq0) / tau + + 0.25 * (2 * (Fx * ux + Fy * uy + Fz * uz) * + (-0.6666666666666666 + ux * ux + uy * uy + uz * uz) + + (mgx * ux + mgy * uy + mgz * uz) * + (2 * chem * (ux * ux + uy * uy + uz * uz) + + 0.3333333333333333 * + (-4 * chem + + (rhoA - rhoB) * (ux * ux + uy * uy + uz * uz)))); - //compute equilibrium distributions - feq0 = 0.3333333333333333*p - 0.25*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) - - 0.16666666666666666*rho0*(ux*ux + uy*uy + uz*uz) - 0.5*(-(nx*ux) - ny*uy - nz*uz)* - (-0.08333333333333333*(rhoA - rhoB)*(ux*ux + uy*uy + uz*uz) + chem*(0.3333333333333333 - 0.5*(ux*ux + uy*uy + uz*uz))); - feq1 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-ux*ux + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) - - 0.125*(Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + - 0.3333333333333333*(-2.*ux + ux*ux + uy*uy + uz*uz)) - 0.0625*(nx - nx*ux - ny*uy - nz*uz)* - (2*chem*ux*ux - 0.3333333333333333*((-rhoA + rhoB)*ux*ux + 2*chem*(-2*ux + ux*ux + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*uz))); - feq2 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-ux*ux + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) - - 0.125*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + - 0.3333333333333333*(2.*ux + ux*ux + uy*uy + uz*uz)) - 0.0625*(nx + nx*ux + ny*uy + nz*uz)* - (-2.*chem*ux*ux + 0.1111111111111111*(-4.*chem + rhoB*(-2.*ux - 1.*ux*ux - 1.*uy*uy - 1.*uz*uz) + - rhoA*(2.*ux + ux*ux + uy*uy + uz*uz)) + 0.3333333333333333*((-1.*rhoA + rhoB)*ux*ux + - chem*(4.*ux + 2.*ux*ux + 2.*uy*uy + 2.*uz*uz))); - feq3 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uy*uy + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) - - 0.125*(Fx*ux + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy + - 0.3333333333333333*(ux*ux - 2.*uy + uy*uy + uz*uz)) - 0.0625*(ny - nx*ux - ny*uy - nz*uz)* - (2*chem*uy*uy - 0.3333333333333333*((-rhoA + rhoB)*uy*uy + 2*chem*(ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*uz))); - feq4 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uy*uy + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) - - 0.125*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy + - 0.3333333333333333*(ux*ux + 2.*uy + uy*uy + uz*uz)) - 0.0625*(ny + nx*ux + ny*uy + nz*uz)* - (-2.*chem*uy*uy + 0.1111111111111111*(-4.*chem + rhoB*(-1.*ux*ux - 2.*uy - 1.*uy*uy - 1.*uz*uz) + - rhoA*(ux*ux + 2.*uy + uy*uy + uz*uz)) + 0.3333333333333333*((-1.*rhoA + rhoB)*uy*uy + - chem*(2.*ux*ux + 4.*uy + 2.*uy*uy + 2.*uz*uz))); - feq5 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uz*uz + 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) - - 0.125*(Fx*ux + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*uz*uz + - 0.3333333333333333*(ux*ux + uy*uy + (-2. + uz)*uz)) - 0.0625*(nx*ux + ny*uy + nz*(-1. + uz))* - (-2.*chem*uz*uz + 0.1111111111111111*(-4.*chem + rhoB*(-1.*ux*ux - 1.*uy*uy + (2. - 1.*uz)*uz) + - rhoA*(ux*ux + uy*uy + (-2. + uz)*uz)) + 0.3333333333333333*((-1.*rhoA + rhoB)*uz*uz + - chem*(2.*ux*ux + 2.*uy*uy + uz*(-4. + 2.*uz)))); - feq6 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uz*uz + 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) - - 0.125*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*uz*uz + - 0.3333333333333333*(ux*ux + uy*uy + uz*(2. + uz))) - 0.0625*(nz + nx*ux + ny*uy + nz*uz)* - (-2.*chem*uz*uz + 0.1111111111111111*(-4.*chem + rhoB*(-1.*ux*ux - 1.*uy*uy + (-2. - 1.*uz)*uz) + - rhoA*(ux*ux + uy*uy + uz*(2. + uz))) + 0.3333333333333333*((-1.*rhoA + rhoB)*uz*uz + - chem*(2.*ux*ux + 2.*uy*uy + uz*(4. + 2.*uz)))); - feq7 = 0.027777777777777776*p - 0.041666666666666664*rho0* - (-(ux + uy)*(ux + uy) + 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) - - 0.0625*(Fx*(-1. + ux) + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uy - 1.*uy*uy + - 0.3333333333333333*(-2.*ux + ux*ux - 2.*uy + uy*uy + uz*uz)) - 0.03125*(nx + ny - nx*ux - ny*uy - nz*uz)* - (2*chem*(ux + uy)*(ux + uy) + 0.3333333333333333*((rhoA - rhoB)*(ux + uy)*(ux + uy) - 2*chem*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz))); - feq8 = 0.027777777777777776*p - 0.041666666666666664*rho0* - (-(ux + uy)*(ux + uy) + 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - - 0.0625*(Fx + Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uy - 1.*uy*uy + - 0.3333333333333333*(2.*ux + ux*ux + 2.*uy + uy*uy + uz*uz)) - 0.03125*(-(nx*(1 + ux)) - ny*(1 + uy) - nz*uz)* - (2*chem*(ux + uy)*(ux + uy) - 0.3333333333333333*(-((rhoA - rhoB)*(ux + uy)*(ux + uy)) + - 2*chem*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + 0.1111111111111111* - (4*chem - (rhoA - rhoB)*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz))); - feq9 = 0.027777777777777776*p - 0.041666666666666664*rho0* - (-(ux - uy)*(ux - uy) + 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - - 0.0625*(Fy + Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uy - 1.*uy*uy + - 0.3333333333333333*(-2.*ux + ux*ux + 2.*uy + uy*uy + uz*uz)) - 0.03125*(nx - nx*ux - ny*(1 + uy) - nz*uz)* - (2*chem*(ux - uy)*(ux - uy) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uy)*(ux - uy)) + - 2*chem*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + 0.1111111111111111* - (4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz))); - feq10 = 0.027777777777777776*p - 0.041666666666666664*rho0* - (-(ux - uy)*(ux - uy) + 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) - - 0.0625*(Fx*(1 + ux) + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uy - 1.*uy*uy + - 0.3333333333333333*(2.*ux + ux*ux - 2.*uy + uy*uy + uz*uz)) - 0.03125*(ny - nx*(1 + ux) - ny*uy - nz*uz)* - (2*chem*(ux - uy)*(ux - uy) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uy)*(ux - uy)) + - 2*chem*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + 0.1111111111111111* - (4*chem - (rhoA - rhoB)*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz))); - feq11 = 0.027777777777777776*p - 0.041666666666666664*rho0* - (-(ux + uz)*(ux + uz) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) - - 0.0625*(Fx*(-1. + ux) + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uz - 1.*uz*uz + - 0.3333333333333333*(-2.*ux + ux*ux + uy*uy + (-2. + uz)*uz)) - 0.03125*(nx + nz - nx*ux - ny*uy - nz*uz)* - (2*chem*(ux + uz)*(ux + uz) + 0.3333333333333333*((rhoA - rhoB)*(ux + uz)*(ux + uz) - 2*chem*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz))); - feq12 = 0.027777777777777776*p - 0.041666666666666664*rho0* - (-(ux + uz)*(ux + uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) - - 0.0625*(Fx + Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uz - 1.*uz*uz + - 0.3333333333333333*(2.*ux + ux*ux + uy*uy + uz*(2. + uz))) - 0.03125*(-(nx*(1 + ux)) - ny*uy - nz*(1 + uz))* - (2*chem*(ux + uz)*(ux + uz) - 0.3333333333333333*(-((rhoA - rhoB)*(ux + uz)*(ux + uz)) + - 2*chem*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) + 0.1111111111111111* - (4*chem - (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*(2 + uz)))); - feq13 = 0.027777777777777776*p - 0.041666666666666664*rho0* - (-(ux - uz)*(ux - uz) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) - - 0.0625*(Fz + Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uz - 1.*uz*uz + - 0.3333333333333333*(-2.*ux + ux*ux + uy*uy + uz*(2. + uz))) - 0.03125*(nx - nx*ux - ny*uy - nz*(1 + uz))* - (2*chem*(ux - uz)*(ux - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uz)*(ux - uz)) + - 2*chem*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + 0.1111111111111111* - (4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*(2 + uz)))); - feq14 = 0.027777777777777776*p - 0.041666666666666664*rho0* - (-(ux - uz)*(ux - uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) - - 0.0625*(Fx*(1 + ux) + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uz - 1.*uz*uz + - 0.3333333333333333*(2.*ux + ux*ux + uy*uy + (-2. + uz)*uz)) - 0.03125*(nz - nx*(1 + ux) - ny*uy - nz*uz)* - (2*chem*(ux - uz)*(ux - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uz)*(ux - uz)) + - 2*chem*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + 0.1111111111111111* - (4*chem - (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz))); - feq15 = 0.027777777777777776*p - 0.041666666666666664*rho0* - (-(uy + uz)*(uy + uz) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) - - 0.0625*(Fx*ux + Fy*(-1. + uy) + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*uy*uy - 2.*uy*uz - 1.*uz*uz + - 0.3333333333333333*(ux*ux - 2.*uy + uy*uy + (-2. + uz)*uz)) - 0.03125*(ny + nz - nx*ux - ny*uy - nz*uz)* - (2*chem*(uy + uz)*(uy + uz) + 0.3333333333333333*((rhoA - rhoB)*(uy + uz)*(uy + uz) - 2*chem*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz))); - feq16 = 0.027777777777777776*p - 0.041666666666666664*rho0* - (-(uy + uz)*(uy + uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) - - 0.0625*(Fy + Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy - 2.*uy*uz - 1.*uz*uz + - 0.3333333333333333*(ux*ux + 2.*uy + uy*uy + uz*(2. + uz))) - 0.03125*(-(nx*ux) - ny*(1 + uy) - nz*(1 + uz))* - (2*chem*(uy + uz)*(uy + uz) - 0.3333333333333333*(-((rhoA - rhoB)*(uy + uz)*(uy + uz)) + - 2*chem*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) + 0.1111111111111111* - (4*chem - (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*(2 + uz)))); - feq17 = 0.027777777777777776*p - 0.041666666666666664*rho0* - (-(uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) - - 0.0625*(Fz + Fx*ux + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy + 2.*uy*uz - 1.*uz*uz + - 0.3333333333333333*(ux*ux - 2.*uy + uy*uy + uz*(2. + uz))) - 0.03125*(ny - nx*ux - ny*uy - nz*(1 + uz))* - (2*chem*(uy - uz)*(uy - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(uy - uz)*(uy - uz)) + - 2*chem*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) + 0.1111111111111111* - (4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*(2 + uz)))); - feq18 = 0.027777777777777776*p - 0.041666666666666664*rho0* - (-(uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) - - 0.0625*(Fx*ux + Fy*(1 + uy) + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*uy*uy + 2.*uy*uz - 1.*uz*uz + - 0.3333333333333333*(ux*ux + 2.*uy + uy*uy + (-2. + uz)*uz)) - 0.03125*(nz - nx*ux - ny*(1 + uy) - nz*uz)* - (2*chem*(uy - uz)*(uy - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(uy - uz)*(uy - uz)) + - 2*chem*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + 0.1111111111111111* - (4*chem - (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz))); + // q = 1 + dist[nr2] = + m1 - (m1 - feq1) / tau + + 0.125 * + (2 * (Fx * (-1 + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - ux * ux + + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + uz * uz)) + + (mgx * (-1 + ux) + mgy * uy + mgz * uz) * + (-2 * chem * (ux * ux) + + 0.3333333333333333 * + ((-rhoA + rhoB) * (ux * ux) + + 2 * chem * (-2 * ux + ux * ux + uy * uy + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + (rhoA - rhoB) * (-2 * ux + ux * ux + + uy * uy + uz * uz)))); - //------------------------------------------------- BCK collison ------------------------------------------------------------// - // q=0 - dist[n] = m0 - (m0-feq0)/tau + 0.25*(2*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) + - (mgx*ux + mgy*uy + mgz*uz)*(2*chem*(ux*ux + uy*uy + uz*uz) + - 0.3333333333333333*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + uz*uz)))); + // q=2 + dist[nr1] = + m2 - (m2 - feq2) / tau + + 0.125 * + (2 * (Fx + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - ux * ux + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + uz * uz)) + + (mgx + mgx * ux + mgy * uy + mgz * uz) * + (-2 * chem * (ux * ux) + + 0.3333333333333333 * + ((-rhoA + rhoB) * (ux * ux) + + 2 * chem * (2 * ux + ux * ux + uy * uy + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + (rhoA - rhoB) * (2 * ux + ux * ux + + uy * uy + uz * uz)))); - // q = 1 - dist[nr2] = m1 - (m1-feq1)/tau + 0.125*(2*(Fx*(-1 + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + - 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) + - (mgx*(-1 + ux) + mgy*uy + mgz*uz)*(-2*chem*(ux*ux) + - 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*uz)))); + // q = 3 + dist[nr4] = + m3 - (m3 - feq3) / tau + + 0.125 * + (2 * (Fx * ux + Fy * (-1 + uy) + Fz * uz) * + (-0.2222222222222222 - uy * uy + + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + uz * uz)) + + (mgx * ux + mgy * (-1 + uy) + mgz * uz) * + (-2 * chem * (uy * uy) + + 0.3333333333333333 * + ((-rhoA + rhoB) * (uy * uy) + + 2 * chem * (ux * ux - 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + (rhoA - rhoB) * (ux * ux - 2 * uy + + uy * uy + uz * uz)))); - // q=2 - dist[nr1] = m2 - (m2-feq2)/tau + 0.125*(2*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + - 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) + - (mgx + mgx*ux + mgy*uy + mgz*uz)*(-2*chem*(ux*ux) + - 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(2*ux + ux*ux + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*uz)))); + // q = 4 + dist[nr3] = + m4 - (m4 - feq4) / tau + + 0.125 * + (2 * (Fy + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - uy * uy + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + uz * uz)) + + (mgy + mgx * ux + mgy * uy + mgz * uz) * + (-2 * chem * (uy * uy) + + 0.3333333333333333 * + ((-rhoA + rhoB) * (uy * uy) + + 2 * chem * (ux * ux + 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + (rhoA - rhoB) * (ux * ux + 2 * uy + + uy * uy + uz * uz)))); - // q = 3 - dist[nr4] = m3 - (m3-feq3)/tau + 0.125*(2*(Fx*ux + Fy*(-1 + uy) + Fz*uz)*(-0.2222222222222222 - uy*uy + - 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) + - (mgx*ux + mgy*(-1 + uy) + mgz*uz)*(-2*chem*(uy*uy) + - 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*uz)))); + // q = 5 + dist[nr6] = + m5 - (m5 - feq5) / tau + + 0.125 * + (2 * (Fx * ux + Fy * uy + Fz * (-1 + uz)) * + (-0.2222222222222222 - uz * uz + + 0.3333333333333333 * + (ux * ux + uy * uy + (-2 + uz) * uz)) + + (mgx * ux + mgy * uy + mgz * (-1 + uz)) * + (-2 * chem * (uz * uz) + + 0.3333333333333333 * + ((-rhoA + rhoB) * (uz * uz) + + 2 * chem * (ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (-4 * chem + (rhoA - rhoB) * (ux * ux + uy * uy + + (-2 + uz) * uz)))); - // q = 4 - dist[nr3] = m4 - (m4-feq4)/tau + 0.125*(2*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uy*uy + - 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) + - (mgy + mgx*ux + mgy*uy + mgz*uz)*(-2*chem*(uy*uy) + - 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*uz)))); + // q = 6 + dist[nr5] = + m6 - (m6 - feq6) / tau + + 0.125 * (2 * (Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - uz * uz + + 0.3333333333333333 * + (ux * ux + uy * uy + uz * (2 + uz))) + + (mgz + mgx * ux + mgy * uy + mgz * uz) * + (-2 * chem * (uz * uz) + + 0.3333333333333333 * + ((-rhoA + rhoB) * (uz * uz) + + 2 * chem * (ux * ux + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (-4 * chem + (rhoA - rhoB) * (ux * ux + uy * uy + + uz * (2 + uz))))); - // q = 5 - dist[nr6] = m5 - (m5-feq5)/tau + 0.125*(2*(Fx*ux + Fy*uy + Fz*(-1 + uz))*(-0.2222222222222222 - uz*uz + - 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) + - (mgx*ux + mgy*uy + mgz*(-1 + uz))*(-2*chem*(uz*uz) + - 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + (-2 + uz)*uz)))); + // q = 7 + dist[nr8] = + m7 - (m7 - feq7) / tau + + 0.0625 * (-2 * (Fx * (-1 + ux) + Fy * (-1 + uy) + Fz * uz) * + (0.2222222222222222 + (ux + uy) * (ux + uy) - + 0.3333333333333333 * (-2 * ux + ux * ux - 2 * uy + + uy * uy + uz * uz)) + + (mgx * (-1 + ux) + mgy * (-1 + uy) + mgz * uz) * + (-2 * chem * ((ux + uy) * (ux + uy)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux + uy) * (ux + uy))) + + 2 * chem * + (-2 * ux + ux * ux - 2 * uy + uy * uy + + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * (-2 * ux + ux * ux - 2 * uy + + uy * uy + uz * uz)))); - // q = 6 - dist[nr5] = m6 - (m6-feq6)/tau + 0.125*(2*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uz*uz + - 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) + - (mgz + mgx*ux + mgy*uy + mgz*uz)*(-2*chem*(uz*uz) + - 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + uz*(2 + uz))))); + // q = 8 + dist[nr7] = + m8 - (m8 - feq8) / tau + + 0.0625 * (2 * (Fx + Fy + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (ux + uy) * (ux + uy) + + 0.3333333333333333 * (2 * ux + ux * ux + 2 * uy + + uy * uy + uz * uz)) + + (mgx + mgy + mgx * ux + mgy * uy + mgz * uz) * + (-2 * chem * ((ux + uy) * (ux + uy)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux + uy) * (ux + uy))) + + 2 * chem * + (2 * ux + ux * ux + 2 * uy + uy * uy + + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * (2 * ux + ux * ux + 2 * uy + + uy * uy + uz * uz)))); - // q = 7 - dist[nr8] = m7 - (m7-feq7)/tau + 0.0625*(-2*(Fx*(-1 + ux) + Fy*(-1 + uy) + Fz*uz)* - (0.2222222222222222 + (ux + uy)*(ux + uy) - - 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + - (mgx*(-1 + ux) + mgy*(-1 + uy) + mgz*uz)* - (-2*chem*((ux + uy)*(ux + uy)) + 0.3333333333333333* - (-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)))); + // q = 9 + dist[nr10] = + m9 - (m9 - feq9) / tau + + 0.0625 * (2 * (Fy + Fx * (-1 + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (ux - uy) * (ux - uy) + + 0.3333333333333333 * (-2 * ux + ux * ux + 2 * uy + + uy * uy + uz * uz)) + + (mgy + mgx * (-1 + ux) + mgy * uy + mgz * uz) * + (-2 * chem * ((ux - uy) * (ux - uy)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uy) * (ux - uy))) + + 2 * chem * + (-2 * ux + ux * ux + 2 * uy + uy * uy + + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * (-2 * ux + ux * ux + 2 * uy + + uy * uy + uz * uz)))); - // q = 8 - dist[nr7] = m8 - (m8-feq8)/tau + 0.0625*(2*(Fx + Fy + Fx*ux + Fy*uy + Fz*uz)* - (-0.2222222222222222 - (ux + uy)*(ux + uy) + - 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + - (mgx + mgy + mgx*ux + mgy*uy + mgz*uz)* - (-2*chem*((ux + uy)*(ux + uy)) + 0.3333333333333333* - (-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)))); + // q = 10 + dist[nr9] = + m10 - (m10 - feq10) / tau + + 0.0625 * (2 * (Fx * (1 + ux) + Fy * (-1 + uy) + Fz * uz) * + (-0.2222222222222222 - (ux - uy) * (ux - uy) + + 0.3333333333333333 * (2 * ux + ux * ux - 2 * uy + + uy * uy + uz * uz)) + + (mgx * (1 + ux) + mgy * (-1 + uy) + mgz * uz) * + (-2 * chem * ((ux - uy) * (ux - uy)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uy) * (ux - uy))) + + 2 * chem * + (2 * ux + ux * ux - 2 * uy + uy * uy + + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * (2 * ux + ux * ux - 2 * uy + + uy * uy + uz * uz)))); - // q = 9 - dist[nr10] = m9 - (m9-feq9)/tau + 0.0625*(2*(Fy + Fx*(-1 + ux) + Fy*uy + Fz*uz)* - (-0.2222222222222222 - (ux - uy)*(ux - uy) + - 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + - (mgy + mgx*(-1 + ux) + mgy*uy + mgz*uz)* - (-2*chem*((ux - uy)*(ux - uy)) + 0.3333333333333333* - (-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)))); + // q = 11 + dist[nr12] = + m11 - (m11 - feq11) / tau + + 0.0625 * + (-2 * (Fx * (-1 + ux) + Fy * uy + Fz * (-1 + uz)) * + (0.2222222222222222 + (ux + uz) * (ux + uz) - + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) + + (mgx * (-1 + ux) + mgy * uy + mgz * (-1 + uz)) * + (-2 * chem * ((ux + uz) * (ux + uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux + uz) * (ux + uz))) + + 2 * chem * + (-2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * (-2 * ux + ux * ux + uy * uy + + (-2 + uz) * uz)))); - // q = 10 - dist[nr9] = m10 - (m10-feq10)/tau + 0.0625*(2*(Fx*(1 + ux) + Fy*(-1 + uy) + Fz*uz)* - (-0.2222222222222222 - (ux - uy)*(ux - uy) + - 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + - (mgx*(1 + ux) + mgy*(-1 + uy) + mgz*uz)* - (-2*chem*((ux - uy)*(ux - uy)) + 0.3333333333333333* - (-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)))); + // q = 12 + dist[nr11] = + m12 - (m12 - feq12) / tau + + 0.0625 * + (2 * (Fx + Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (ux + uz) * (ux + uz) + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + uz * (2 + uz))) + + (mgx + mgz + mgx * ux + mgy * uy + mgz * uz) * + (-2 * chem * ((ux + uz) * (ux + uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux + uz) * (ux + uz))) + + 2 * chem * + (2 * ux + ux * ux + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (2 * ux + ux * ux + uy * uy + uz * (2 + uz))))); - // q = 11 - dist[nr12] = m11 - (m11-feq11)/tau + 0.0625*(-2*(Fx*(-1 + ux) + Fy*uy + Fz*(-1 + uz))* - (0.2222222222222222 + (ux + uz)*(ux + uz) - - 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + - (mgx*(-1 + ux) + mgy*uy + mgz*(-1 + uz))* - (-2*chem*((ux + uz)*(ux + uz)) + 0.3333333333333333* - (-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)))); + // q = 13 + dist[nr14] = + m13 - (m13 - feq13) / tau + + 0.0625 * + (2 * (Fz + Fx * (-1 + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (ux - uz) * (ux - uz) + + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + uz * (2 + uz))) + + (mgz + mgx * (-1 + ux) + mgy * uy + mgz * uz) * + (-2 * chem * ((ux - uz) * (ux - uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uz) * (ux - uz))) + + 2 * chem * + (-2 * ux + ux * ux + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (-2 * ux + ux * ux + uy * uy + uz * (2 + uz))))); - // q = 12 - dist[nr11] = m12 - (m12-feq12)/tau + 0.0625*(2*(Fx + Fz + Fx*ux + Fy*uy + Fz*uz)* - (-0.2222222222222222 - (ux + uz)*(ux + uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) - + (mgx + mgz + mgx*ux + mgy*uy + mgz*uz)* - (-2*chem*((ux + uz)*(ux + uz)) + 0.3333333333333333* - (-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*(2 + uz))))); + // q= 14 + dist[nr13] = + m14 - (m14 - feq14) / tau + + 0.0625 * + (2 * (Fx * (1 + ux) + Fy * uy + Fz * (-1 + uz)) * + (-0.2222222222222222 - (ux - uz) * (ux - uz) + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) + + (mgx * (1 + ux) + mgy * uy + mgz * (-1 + uz)) * + (-2 * chem * ((ux - uz) * (ux - uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uz) * (ux - uz))) + + 2 * chem * + (2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)))); - // q = 13 - dist[nr14] = m13 - (m13-feq13)/tau + 0.0625*(2*(Fz + Fx*(-1 + ux) + Fy*uy + Fz*uz)* - (-0.2222222222222222 - (ux - uz)*(ux - uz) + - 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + - (mgz + mgx*(-1 + ux) + mgy*uy + mgz*uz)* - (-2*chem*((ux - uz)*(ux - uz)) + 0.3333333333333333* - (-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))))); + // q = 15 + dist[nr16] = + m15 - (m15 - feq15) / tau + + 0.0625 * + (-2 * (Fx * ux + Fy * (-1 + uy) + Fz * (-1 + uz)) * + (0.2222222222222222 + (uy + uz) * (uy + uz) - + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + (-2 + uz) * uz)) + + (mgx * ux + mgy * (-1 + uy) + mgz * (-1 + uz)) * + (-2 * chem * ((uy + uz) * (uy + uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((uy + uz) * (uy + uz))) + + 2 * chem * + (ux * ux - 2 * uy + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (ux * ux - 2 * uy + uy * uy + (-2 + uz) * uz)))); - // q= 14 - dist[nr13] = m14 - (m14-feq14)/tau + 0.0625*(2*(Fx*(1 + ux) + Fy*uy + Fz*(-1 + uz))* - (-0.2222222222222222 - (ux - uz)*(ux - uz) + - 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + - (mgx*(1 + ux) + mgy*uy + mgz*(-1 + uz))* - (-2*chem*((ux - uz)*(ux - uz)) + 0.3333333333333333* - (-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)))); + // q = 16 + dist[nr15] = + m16 - (m16 - feq16) / tau + + 0.0625 * + (2 * (Fy + Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (uy + uz) * (uy + uz) + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + uz * (2 + uz))) + + (mgy + mgz + mgx * ux + mgy * uy + mgz * uz) * + (-2 * chem * ((uy + uz) * (uy + uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((uy + uz) * (uy + uz))) + + 2 * chem * + (ux * ux + 2 * uy + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (ux * ux + 2 * uy + uy * uy + uz * (2 + uz))))); - // q = 15 - dist[nr16] = m15 - (m15-feq15)/tau + 0.0625*(-2*(Fx*ux + Fy*(-1 + uy) + Fz*(-1 + uz))* - (0.2222222222222222 + (uy + uz)*(uy + uz) - 0.3333333333333333*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) - + (mgx*ux + mgy*(-1 + uy) + mgz*(-1 + uz))* - (-2*chem*((uy + uz)*(uy + uz)) + 0.3333333333333333* - (-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)))); + // q = 17 + dist[nr18] = + m17 - (m17 - feq17) / tau + + 0.0625 * + (2 * (Fz + Fx * ux + Fy * (-1 + uy) + Fz * uz) * + (-0.2222222222222222 - (uy - uz) * (uy - uz) + + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + uz * (2 + uz))) + + (mgz + mgx * ux + mgy * (-1 + uy) + mgz * uz) * + (-2 * chem * ((uy - uz) * (uy - uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((uy - uz) * (uy - uz))) + + 2 * chem * + (ux * ux - 2 * uy + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (ux * ux - 2 * uy + uy * uy + uz * (2 + uz))))); - // q = 16 - dist[nr15] = m16 - (m16-feq16)/tau + 0.0625*(2*(Fy + Fz + Fx*ux + Fy*uy + Fz*uz)* - (-0.2222222222222222 - (uy + uz)*(uy + uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) - + (mgy + mgz + mgx*ux + mgy*uy + mgz*uz)* - (-2*chem*((uy + uz)*(uy + uz)) + 0.3333333333333333* - (-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))))); + // q = 18 + dist[nr17] = + m18 - (m18 - feq18) / tau + + 0.0625 * + (2 * (Fx * ux + Fy * (1 + uy) + Fz * (-1 + uz)) * + (-0.2222222222222222 - (uy - uz) * (uy - uz) + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + (-2 + uz) * uz)) + + (mgx * ux + mgy * (1 + uy) + mgz * (-1 + uz)) * + (-2 * chem * ((uy - uz) * (uy - uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((uy - uz) * (uy - uz))) + + 2 * chem * + (ux * ux + 2 * uy + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (ux * ux + 2 * uy + uy * uy + (-2 + uz) * uz)))); + //----------------------------------------------------------------------------------------------------------------------------------------// - // q = 17 - dist[nr18] = m17 - (m17-feq17)/tau + 0.0625*(2*(Fz + Fx*ux + Fy*(-1 + uy) + Fz*uz)* - (-0.2222222222222222 - (uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) - + (mgz + mgx*ux + mgy*(-1 + uy) + mgz*uz)* - (-2*chem*((uy - uz)*(uy - uz)) + 0.3333333333333333* - (-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))))); - - // q = 18 - dist[nr17] = m18 - (m18-feq18)/tau + 0.0625*(2*(Fx*ux + Fy*(1 + uy) + Fz*(-1 + uz))* - (-0.2222222222222222 - (uy - uz)*(uy - uz) + - 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + - (mgx*ux + mgy*(1 + uy) + mgz*(-1 + uz))* - (-2*chem*((uy - uz)*(uy - uz)) + 0.3333333333333333* - (-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)))); - //----------------------------------------------------------------------------------------------------------------------------------------// - - //Update velocity on device - Vel[0*Np+n] = ux; - Vel[1*Np+n] = uy; - Vel[2*Np+n] = uz; - //Update pressure on device - Pressure[n] = p; - //Update chemical potential on device - mu_phi[n] = chem; - //Update color gradient on device - ColorGrad[0*Np+n] = nx; - ColorGrad[1*Np+n] = ny; - ColorGrad[2*Np+n] = nz; - - } + //Update velocity on device + Vel[0 * Np + n] = ux; + Vel[1 * Np + n] = uy; + Vel[2 * Np + n] = uz; + //Update pressure on device + Pressure[n] = p; + //Update chemical potential on device + mu_phi[n] = chem; + //Update color gradient on device + ColorGrad[0 * Np + n] = nx; + ColorGrad[1 * Np + n] = ny; + ColorGrad[2 * Np + n] = nz; + } } -extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel(int *Map, double *dist, double *Den, double *Phi, double *mu_phi, double *Vel, double *Pressure, double *ColorGrad, - double rhoA, double rhoB, double tauA, double tauB, double kappa, double beta, double W, double Fx, double Fy, double Fz, - int strideY, int strideZ, int start, int finish, int Np){ +extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel( + int *Map, double *dist, double *Den, double *Phi, double *mu_phi, + double *Vel, double *Pressure, double *ColorGrad, double rhoA, double rhoB, + double tauA, double tauB, double kappa, double beta, double W, double Fx, + double Fy, double Fz, int strideY, int strideZ, int start, int finish, + int Np) { - int nn,nn2x,ijk; - //int nr1,nr2,nr3,nr4,nr5,nr6,nr7,nr8,nr9,nr10,nr11,nr12,nr13,nr14,nr15,nr16,nr17,nr18; - double ux,uy,uz;//fluid velocity - double p;//pressure - double chem;//chemical potential - double phi; //phase field - double rho0;//fluid density - // distribution functions - double m1,m2,m4,m6,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18; - double m0,m3,m5,m7; - double mm1,mm2,mm4,mm6,mm8,mm9,mm10,mm11,mm12,mm13,mm14,mm15,mm16,mm17,mm18; - double mm3,mm5,mm7; - double feq0,feq1,feq2,feq3,feq4,feq5,feq6,feq7,feq8,feq9,feq10,feq11,feq12,feq13,feq14,feq15,feq16,feq17,feq18; - double nx,ny,nz;//normal color gradient - double mgx,mgy,mgz;//mixed gradient reaching secondary neighbor + int nn, nn2x, ijk; + //int nr1,nr2,nr3,nr4,nr5,nr6,nr7,nr8,nr9,nr10,nr11,nr12,nr13,nr14,nr15,nr16,nr17,nr18; + double ux, uy, uz; //fluid velocity + double p; //pressure + double chem; //chemical potential + double phi; //phase field + double rho0; //fluid density + // distribution functions + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; + double m0, m3, m5, m7; + double mm1, mm2, mm4, mm6, mm8, mm9, mm10, mm11, mm12, mm13, mm14, mm15, + mm16, mm17, mm18; + double mm3, mm5, mm7; + double feq0, feq1, feq2, feq3, feq4, feq5, feq6, feq7, feq8, feq9, feq10, + feq11, feq12, feq13, feq14, feq15, feq16, feq17, feq18; + double nx, ny, nz; //normal color gradient + double mgx, mgy, mgz; //mixed gradient reaching secondary neighbor - //double f0,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18; - //double h0,h1,h2,h3,h4,h5,h6;//distributions for LB phase field - double tau;//position dependent LB relaxation time for fluid - //double C,theta; - //double M = 2.0/9.0*(tauM-0.5);//diffusivity (or mobility) for the phase field D3Q7 + //double f0,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18; + //double h0,h1,h2,h3,h4,h5,h6;//distributions for LB phase field + double tau; //position dependent LB relaxation time for fluid + //double C,theta; + //double M = 2.0/9.0*(tauM-0.5);//diffusivity (or mobility) for the phase field D3Q7 - for (int n=start; n1.f) phi_temp=1.0; - if (phi<-1.f) phi_temp=-1.0; - - // local relaxation time - tau=tauA + 0.5*(1.0-phi)*(tauB-tauA); - - // COMPUTE THE COLOR GRADIENT - //........................................................................ - //.................Read Phase Indicator Values............................ - //........................................................................ - nn = ijk-1; // neighbor index (get convention) - m2 = Phi[nn]; // get neighbor for phi - 1 - //........................................................................ - nn = ijk+1; // neighbor index (get convention) - m1 = Phi[nn]; // get neighbor for phi - 2 - dirGradC1 = 0.5*(m1-m2); - dirGradC2 = 0.5*(m2-m1); - //........................................................................ - nn = ijk-strideY; // neighbor index (get convention) - m4 = Phi[nn]; // get neighbor for phi - 3 - //........................................................................ - nn = ijk+strideY; // neighbor index (get convention) - m3 = Phi[nn]; // get neighbor for phi - 4 - dirGradC3 = 0.5*(m3-m4); - dirGradC4 = 0.5*(m4-m3); - //........................................................................ - nn = ijk-strideZ; // neighbor index (get convention) - m6 = Phi[nn]; // get neighbor for phi - 5 - //........................................................................ - nn = ijk+strideZ; // neighbor index (get convention) - m5 = Phi[nn]; // get neighbor for phi - 6 - dirGradC5 = 0.5*(m5-m6); - dirGradC6 = 0.5*(m6-m5); - //........................................................................ - nn = ijk-strideY-1; // neighbor index (get convention) - m8 = Phi[nn]; // get neighbor for phi - 7 - //........................................................................ - nn = ijk+strideY+1; // neighbor index (get convention) - m7 = Phi[nn]; // get neighbor for phi - 8 - dirGradC7 = 0.5*(m7-m8); - dirGradC8 = 0.5*(m8-m7); - //........................................................................ - nn = ijk+strideY-1; // neighbor index (get convention) - m10 = Phi[nn]; // get neighbor for phi - 9 - //........................................................................ - nn = ijk-strideY+1; // neighbor index (get convention) - m9 = Phi[nn]; // get neighbor for phi - 10 - dirGradC9 = 0.5*(m9-m10); - dirGradC10 = 0.5*(m10-m9); - //........................................................................ - nn = ijk-strideZ-1; // neighbor index (get convention) - m12 = Phi[nn]; // get neighbor for phi - 11 - //........................................................................ - nn = ijk+strideZ+1; // neighbor index (get convention) - m11 = Phi[nn]; // get neighbor for phi - 12 - dirGradC11 = 0.5*(m11-m12); - dirGradC12 = 0.5*(m12-m11); - //........................................................................ - nn = ijk+strideZ-1; // neighbor index (get convention) - m14 = Phi[nn]; // get neighbor for phi - 13 - //........................................................................ - nn = ijk-strideZ+1; // neighbor index (get convention) - m13 = Phi[nn]; // get neighbor for phi - 14 - dirGradC13 = 0.5*(m13-m14); - dirGradC14 = 0.5*(m14-m13); - //........................................................................ - nn = ijk-strideZ-strideY; // neighbor index (get convention) - m16 = Phi[nn]; // get neighbor for phi - 15 - //........................................................................ - nn = ijk+strideZ+strideY; // neighbor index (get convention) - m15 = Phi[nn]; // get neighbor for phi - 16 - dirGradC15 = 0.5*(m15-m16); - dirGradC16 = 0.5*(m16-m15); - //........................................................................ - nn = ijk+strideZ-strideY; // neighbor index (get convention) - m18 = Phi[nn]; // get neighbor for phi - 17 - //........................................................................ - nn = ijk-strideZ+strideY; // neighbor index (get convention) - m17 = Phi[nn]; // get neighbor for phi - 18 - dirGradC17 = 0.5*(m17-m18); - dirGradC18 = 0.5*(m18-m17); - - // compute mixed difference (Eq.30, A.Fukhari et al. JCP 315(2016) 434-457) - //........................................................................ - nn2x = ijk+2; // neighbor index (get convention) - dirGradM1 = Phi[nn2x]; // get neighbor for phi - 1 - dirGradM1 = 0.25*(-dirGradM1+5.0*m1-3.0*phi-m2); - //........................................................................ - nn2x = ijk-2; // neighbor index (get convention) - dirGradM2 = Phi[nn2x]; // get neighbor for phi - 2 - dirGradM2 = 0.25*(-dirGradM2+5.0*m2-3.0*phi-m1); - //........................................................................ - nn2x = ijk+strideY*2; // neighbor index (get convention) - dirGradM3 = Phi[nn2x]; // get neighbor for phi - 3 - dirGradM3 = 0.25*(-dirGradM3+5.0*m3-3.0*phi-m4); - //........................................................................ - nn2x = ijk-strideY*2; // neighbor index (get convention) - dirGradM4 = Phi[nn2x]; // get neighbor for phi - 4 - dirGradM4 = 0.25*(-dirGradM4+5.0*m4-3.0*phi-m3); - //........................................................................ - nn2x = ijk+strideZ*2; // neighbor index (get convention) - dirGradM5 = Phi[nn2x]; // get neighbor for phi - 5 - dirGradM5 = 0.25*(-dirGradM5+5.0*m5-3.0*phi-m6); - //........................................................................ - nn2x = ijk-strideZ*2; // neighbor index (get convention) - dirGradM6 = Phi[nn2x]; // get neighbor for phi - 6 - dirGradM6 = 0.25*(-dirGradM6+5.0*m6-3.0*phi-m5); - //........................................................................ - nn2x = ijk+strideY*2+2; // neighbor index (get convention) - dirGradM7 = Phi[nn2x]; // get neighbor for phi - 7 - dirGradM7 = 0.25*(-dirGradM7+5.0*m7-3.0*phi-m8); - //........................................................................ - nn2x = ijk-strideY*2-2; // neighbor index (get convention) - dirGradM8 = Phi[nn2x]; // get neighbor for phi - 8 - dirGradM8 = 0.25*(-dirGradM8+5.0*m8-3.0*phi-m7); - //........................................................................ - nn2x = ijk-strideY*2+2; // neighbor index (get convention) - dirGradM9 = Phi[nn2x]; // get neighbor for phi - 9 - dirGradM9 = 0.25*(-dirGradM9+5.0*m9-3.0*phi-m10); - //........................................................................ - nn2x = ijk+strideY*2-2; // neighbor index (get convention) - dirGradM10 = Phi[nn2x]; // get neighbor for phi - 10 - dirGradM10 = 0.25*(-dirGradM10+5.0*m10-3.0*phi-m9); - //........................................................................ - nn2x = ijk+strideZ*2+2; // neighbor index (get convention) - dirGradM11 = Phi[nn2x]; // get neighbor for phi - 11 - dirGradM11 = 0.25*(-dirGradM11+5.0*m11-3.0*phi-m12); - //........................................................................ - nn2x = ijk-strideZ*2-2; // neighbor index (get convention) - dirGradM12 = Phi[nn2x]; // get neighbor for phi - 12 - dirGradM12 = 0.25*(-dirGradM12+5.0*m12-3.0*phi-m11); - //........................................................................ - nn2x = ijk-strideZ*2+2; // neighbor index (get convention) - dirGradM13 = Phi[nn2x]; // get neighbor for phi - 13 - dirGradM13 = 0.25*(-dirGradM13+5.0*m13-3.0*phi-m14); - //........................................................................ - nn2x = ijk+strideZ*2-2; // neighbor index (get convention) - dirGradM14 = Phi[nn2x]; // get neighbor for phi - 14 - dirGradM14 = 0.25*(-dirGradM14+5.0*m14-3.0*phi-m13); - //........................................................................ - nn2x = ijk+strideZ*2+strideY*2; // neighbor index (get convention) - dirGradM15 = Phi[nn2x]; // get neighbor for phi - 15 - dirGradM15 = 0.25*(-dirGradM15+5.0*m15-3.0*phi-m16); - //........................................................................ - nn2x = ijk-strideZ*2-strideY*2; // neighbor index (get convention) - dirGradM16 = Phi[nn2x]; // get neighbor for phi - 16 - dirGradM16 = 0.25*(-dirGradM16+5.0*m16-3.0*phi-m15); - //........................................................................ - nn2x = ijk-strideZ*2+strideY*2; // neighbor index (get convention) - dirGradM17 = Phi[nn2x]; // get neighbor for phi - 17 - dirGradM17 = 0.25*(-dirGradM17+5.0*m17-3.0*phi-m18); - //........................................................................ - nn2x = ijk+strideZ*2-strideY*2; // neighbor index (get convention) - dirGradM18 = Phi[nn2x]; // get neighbor for phi - 18 - dirGradM18 = 0.25*(-dirGradM18+5.0*m18-3.0*phi-m17); - - - //............Compute the Color Gradient................................... - nx = 3.0*1.0/18.0*(dirGradC1-dirGradC2+0.5*(dirGradC7-dirGradC8+dirGradC9-dirGradC10+dirGradC11-dirGradC12+dirGradC13-dirGradC14)); - ny = 3.0*1.0/18.0*(dirGradC3-dirGradC4+0.5*(dirGradC7-dirGradC8-dirGradC9+dirGradC10+dirGradC15-dirGradC16+dirGradC17-dirGradC18)); - nz = 3.0*1.0/18.0*(dirGradC5-dirGradC6+0.5*(dirGradC11-dirGradC12-dirGradC13+dirGradC14+dirGradC15-dirGradC16-dirGradC17+dirGradC18)); - //............Compute the Chemical Potential............................... - //chem = 2.0*3.0/18.0*(m1+m2+m3+m4+m5+m6-6*phi+0.5*(m7+m8+m9+m10+m11+m12+m13+m14+m15+m16+m17+m18-12*phi));//intermediate var, i.e. the laplacian - //chem = 4.0*beta*phi*(phi+1.0)*(phi-1.0)-kappa*chem; - chem = 2.0*3.0/18.0*(m1+m2+m3+m4+m5+m6-6*phi_temp+0.5*(m7+m8+m9+m10+m11+m12+m13+m14+m15+m16+m17+m18-12*phi_temp));//intermediate var, i.e. the laplacian - chem = 4.0*beta*phi_temp*(phi_temp+1.0)*(phi_temp-1.0)-kappa*chem; - //............Compute the Mixed Gradient................................... - mgx = 3.0*1.0/18.0*(dirGradM1-dirGradM2+0.5*(dirGradM7-dirGradM8+dirGradM9-dirGradM10+dirGradM11-dirGradM12+dirGradM13-dirGradM14)); - mgy = 3.0*1.0/18.0*(dirGradM3-dirGradM4+0.5*(dirGradM7-dirGradM8-dirGradM9+dirGradM10+dirGradM15-dirGradM16+dirGradM17-dirGradM18)); - mgz = 3.0*1.0/18.0*(dirGradM5-dirGradM6+0.5*(dirGradM11-dirGradM12-dirGradM13+dirGradM14+dirGradM15-dirGradM16-dirGradM17+dirGradM18)); - - //de-noise color gradient and mixed gradient - C = sqrt(nx*nx+ny*ny+nz*nz); - if (C<1.0e-12) nx=ny=nz=0.0; - double mg_mag = sqrt(mgx*mgx+mgy*mgy+mgz*mgz); - if (mg_mag<1.0e-12) mgx=mgy=mgz=0.0; - //maybe you can also de-noise chemical potential ? within the bulk phase chem should be zero - if (fabs(chem)<1.0e-12) chem=0.0; - - // q=0 - m0 = dist[n]; - // q=1 - nr1 = neighborList[n]; // neighbor 2 ( > 10Np => odd part of dist) - m1 = dist[nr1]; // reading the f1 data into register fq - - nr2 = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) - m2 = dist[nr2]; // reading the f2 data into register fq - - // q=3 - nr3 = neighborList[n+2*Np]; // neighbor 4 - m3 = dist[nr3]; - - // q = 4 - nr4 = neighborList[n+3*Np]; // neighbor 3 - m4 = dist[nr4]; - - // q=5 - nr5 = neighborList[n+4*Np]; - m5 = dist[nr5]; - - // q = 6 - nr6 = neighborList[n+5*Np]; - m6 = dist[nr6]; - - // q=7 - nr7 = neighborList[n+6*Np]; - m7 = dist[nr7]; - - // q = 8 - nr8 = neighborList[n+7*Np]; - m8 = dist[nr8]; - - // q=9 - nr9 = neighborList[n+8*Np]; - m9 = dist[nr9]; - - // q = 10 - nr10 = neighborList[n+9*Np]; - m10 = dist[nr10]; - - // q=11 - nr11 = neighborList[n+10*Np]; - m11 = dist[nr11]; - - // q=12 - nr12 = neighborList[n+11*Np]; - m12 = dist[nr12]; - - // q=13 - nr13 = neighborList[n+12*Np]; - m13 = dist[nr13]; - - // q=14 - nr14 = neighborList[n+13*Np]; - m14 = dist[nr14]; - - // q=15 - nr15 = neighborList[n+14*Np]; - m15 = dist[nr15]; - - // q=16 - nr16 = neighborList[n+15*Np]; - m16 = dist[nr16]; - - // q=17 - nr17 = neighborList[n+16*Np]; - m17 = dist[nr17]; - - // q=18 - nr18 = neighborList[n+17*Np]; - m18 = dist[nr18]; - - //compute fluid velocity - ux = 3.0/rho0*(m1-m2+m7-m8+m9-m10+m11-m12+m13-m14+0.5*(chem*nx+Fx)/1.0); // used to divide by 3.0 think that was wrong - uy = 3.0/rho0*(m3-m4+m7-m8-m9+m10+m15-m16+m17-m18+0.5*(chem*ny+Fy)/1.0); - uz = 3.0/rho0*(m5-m6+m11-m12-m13+m14+m15-m16-m17+m18+0.5*(chem*nz+Fz)/1.0); - //compute pressure - p = (m0+m2+m1+m4+m3+m6+m5+m8+m7+m10+m9+m12+m11+m14+m13+m16+m15+m18+m17) - +0.5*(rhoA-rhoB)/2.0/3.0*(ux*nx+uy*ny+uz*nz); - - //compute equilibrium distributions - feq0 = 0.3333333333333333*p - 0.25*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) - 0.16666666666666666*rho0*(ux*ux + uy*uy + uz*uz) - - 0.5*(-(nx*ux) - ny*uy - nz*uz)*(-0.08333333333333333*(rhoA - rhoB)*(ux*ux + uy*uy + uz*uz) + chem*(0.3333333333333333 - 0.5*(ux*ux + uy*uy + uz*uz))); - feq1 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-(ux*ux) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) - - 0.125*(Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(ux*ux) + 0.3333333333333333*(-2.*ux + ux*ux + uy*uy + uz*uz)) - - 0.0625*(dirGradC1 - nx*ux - ny*uy - nz*uz)*(2*chem*(ux*ux) - 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*uz))); - feq2 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-(ux*ux) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) - - 0.125*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(ux*ux) + 0.3333333333333333*(2.*ux + ux*ux + uy*uy + uz*uz)) - - 0.0625*(dirGradC2 - nx*ux - ny*uy - nz*uz)*(2*chem*(ux*ux) - 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(2*ux + ux*ux + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*uz))); - feq3 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-(uy*uy) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) - - 0.125*(Fx*ux + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*(uy*uy) + 0.3333333333333333*(ux*ux - 2.*uy + uy*uy + uz*uz)) - - 0.0625*(dirGradC3 - nx*ux - ny*uy - nz*uz)*(2*chem*(uy*uy) - 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*uz))); - feq4 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-(uy*uy) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) - - 0.125*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(uy*uy) + 0.3333333333333333*(ux*ux + 2.*uy + uy*uy + uz*uz)) - - 0.0625*(dirGradC4 - nx*ux - ny*uy - nz*uz)*(2*chem*(uy*uy) - 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*uz))); - feq5 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-(uz*uz) + 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) - - 0.125*(Fx*ux + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*(uz*uz) + 0.3333333333333333*(ux*ux + uy*uy + (-2. + uz)*uz)) - - 0.0625*(dirGradC5 - nx*ux - ny*uy - nz*uz)*(2*chem*(uz*uz) - 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux + uy*uy + (-2 + uz)*uz))); - feq6 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-(uz*uz) + 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) - - 0.125*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(uz*uz) + 0.3333333333333333*(ux*ux + uy*uy + uz*(2. + uz))) - - 0.0625*(dirGradC6 - nx*ux - ny*uy - nz*uz)*(2*chem*(uz*uz) - 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux + uy*uy + uz*(2 + uz)))); - feq7 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((ux + uy)*(ux + uy)) + 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) - - 0.0625*(Fx*(-1. + ux) + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*(ux*ux) - 2.*ux*uy - 1.*(uy*uy) + - 0.3333333333333333*(-2.*ux + ux*ux - 2.*uy + uy*uy + uz*uz)) - - 0.03125*(dirGradC7 - nx*ux - ny*uy - nz*uz)*(2*chem*((ux + uy)*(ux + uy)) + - 0.3333333333333333*((rhoA - rhoB)*((ux + uy)*(ux + uy)) - 2*chem*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz))); - feq8 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((ux + uy)*(ux + uy)) + 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - - 0.0625*(Fx + Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(ux*ux) - 2.*ux*uy - 1.*(uy*uy) + - 0.3333333333333333*(2.*ux + ux*ux + 2.*uy + uy*uy + uz*uz)) - - 0.03125*(dirGradC8 - nx*ux - ny*uy - nz*uz)*(2*chem*((ux + uy)*(ux + uy)) - - 0.3333333333333333*(-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz))); - feq9 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((ux - uy)*(ux - uy)) + 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - - 0.0625*(Fy + Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(ux*ux) + 2.*ux*uy - 1.*(uy*uy) + - 0.3333333333333333*(-2.*ux + ux*ux + 2.*uy + uy*uy + uz*uz)) - - 0.03125*(dirGradC9 - nx*ux - ny*uy - nz*uz)*(2*chem*((ux - uy)*(ux - uy)) - - 0.3333333333333333*(-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz))); - feq10 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((ux - uy)*(ux - uy)) + 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) - - 0.0625*(Fx*(1 + ux) + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*(ux*ux) + 2.*ux*uy - 1.*(uy*uy) + - 0.3333333333333333*(2.*ux + ux*ux - 2.*uy + uy*uy + uz*uz)) - - 0.03125*(dirGradC10 - nx*ux - ny*uy - nz*uz)*(2*chem*((ux - uy)*(ux - uy)) - - 0.3333333333333333*(-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz))); - feq11 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((ux + uz)*(ux + uz)) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) - - 0.0625*(Fx*(-1. + ux) + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*(ux*ux) - 2.*ux*uz - 1.*(uz*uz) + - 0.3333333333333333*(-2.*ux + ux*ux + uy*uy + (-2. + uz)*uz)) - - 0.03125*(dirGradC11 - nx*ux - ny*uy - nz*uz)*(2*chem*((ux + uz)*(ux + uz)) + - 0.3333333333333333*((rhoA - rhoB)*((ux + uz)*(ux + uz)) - 2*chem*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz))); - feq12 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((ux + uz)*(ux + uz)) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) - - 0.0625*(Fx + Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(ux*ux) - 2.*ux*uz - 1.*(uz*uz) + - 0.3333333333333333*(2.*ux + ux*ux + uy*uy + uz*(2. + uz))) - - 0.03125*(dirGradC12 - nx*ux - ny*uy - nz*uz)*(2*chem*((ux + uz)*(ux + uz)) - - 0.3333333333333333*(-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*(2 + uz)))); - feq13 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((ux - uz)*(ux - uz)) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) - - 0.0625*(Fz + Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(ux*ux) + 2.*ux*uz - 1.*(uz*uz) + - 0.3333333333333333*(-2.*ux + ux*ux + uy*uy + uz*(2. + uz))) - - 0.03125*(dirGradC13 - nx*ux - ny*uy - nz*uz)*(2*chem*((ux - uz)*(ux - uz)) - - 0.3333333333333333*(-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*(2 + uz)))); - feq14 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((ux - uz)*(ux - uz)) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) - - 0.0625*(Fx*(1 + ux) + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*(ux*ux) + 2.*ux*uz - 1.*(uz*uz) + - 0.3333333333333333*(2.*ux + ux*ux + uy*uy + (-2. + uz)*uz)) - - 0.03125*(dirGradC14 - nx*ux - ny*uy - nz*uz)*(2*chem*((ux - uz)*(ux - uz)) - - 0.3333333333333333*(-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz))); - feq15 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((uy + uz)*(uy + uz)) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) - - 0.0625*(Fx*ux + Fy*(-1. + uy) + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*(uy*uy) - 2.*uy*uz - 1.*(uz*uz) + - 0.3333333333333333*(ux*ux - 2.*uy + uy*uy + (-2. + uz)*uz)) - - 0.03125*(dirGradC15 - nx*ux - ny*uy - nz*uz)*(2*chem*((uy + uz)*(uy + uz)) + - 0.3333333333333333*((rhoA - rhoB)*((uy + uz)*(uy + uz)) - 2*chem*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz))); - feq16 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((uy + uz)*(uy + uz)) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) - - 0.0625*(Fy + Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(uy*uy) - 2.*uy*uz - 1.*(uz*uz) + - 0.3333333333333333*(ux*ux + 2.*uy + uy*uy + uz*(2. + uz))) - - 0.03125*(dirGradC16 - nx*ux - ny*uy - nz*uz)*(2*chem*((uy + uz)*(uy + uz)) - - 0.3333333333333333*(-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*(2 + uz)))); - feq17 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((uy - uz)*(uy - uz)) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) - - 0.0625*(Fz + Fx*ux + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*(uy*uy) + 2.*uy*uz - 1.*(uz*uz) + - 0.3333333333333333*(ux*ux - 2.*uy + uy*uy + uz*(2. + uz))) - - 0.03125*(dirGradC17 - nx*ux - ny*uy - nz*uz)*(2*chem*((uy - uz)*(uy - uz)) - - 0.3333333333333333*(-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*(2 + uz)))); - feq18 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((uy - uz)*(uy - uz)) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) - - 0.0625*(Fx*ux + Fy*(1 + uy) + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*(uy*uy) + 2.*uy*uz - 1.*(uz*uz) + - 0.3333333333333333*(ux*ux + 2.*uy + uy*uy + (-2. + uz)*uz)) - - 0.03125*(dirGradC18 - nx*ux - ny*uy - nz*uz)*(2*chem*((uy - uz)*(uy - uz)) - - 0.3333333333333333*(-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz))); - - //------------------------------------------------- BCK collison ------------------------------------------------------------// - // q=0 - dist[n] = m0 - (m0-feq0)/tau + 0.25*(2*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) + - (mgx*ux + mgy*uy + mgz*uz)*(2*chem*(ux*ux + uy*uy + uz*uz) + 0.3333333333333333*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + uz*uz)))); - - // q = 1 - dist[nr2] = m1 - (m1-feq1)/tau + 0.125*(2*(Fx*(-1 + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) - - (dirGradM1 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*(ux*ux) + 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*uz)))); - - // q=2 - dist[nr1] = m2 - (m2-feq2)/tau + 0.125*(2*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) - - (dirGradM2 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*(ux*ux) + 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(2*ux + ux*ux + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*uz)))); - - // q = 3 - dist[nr4] = m3 - (m3-feq3)/tau + 0.125*(2*(Fx*ux + Fy*(-1 + uy) + Fz*uz)*(-0.2222222222222222 - uy*uy + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) - - (dirGradM3 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*(uy*uy) + 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*uz)))); - - // q = 4 - dist[nr3] = m4 - (m4-feq4)/tau + 0.125*(2*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uy*uy + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) - - (dirGradM4 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*(uy*uy) + 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*uz)))); - - // q = 5 - dist[nr6] = m5 - (m5-feq5)/tau + 0.125*(2*(Fx*ux + Fy*uy + Fz*(-1 + uz))*(-0.2222222222222222 - uz*uz + 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) - - (dirGradM5 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*(uz*uz) + 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + (-2 + uz)*uz)))); - - // q = 6 - dist[nr5] = m6 - (m6-feq6)/tau + 0.125*(2*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uz*uz + 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) - - (dirGradM6 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*(uz*uz) + 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + uz*(2 + uz))))); - - // q = 7 - dist[nr8] = m7 - (m7-feq7)/tau + 0.0625*(-2*(Fx*(-1 + ux) + Fy*(-1 + uy) + Fz*uz)*(0.2222222222222222 + (ux + uy)*(ux + uy) - 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) - - (dirGradM7 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((ux + uy)*(ux + uy)) + - 0.3333333333333333*(-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)))); - - // q = 8 - dist[nr7] = m8 - (m8-feq8)/tau + 0.0625*(2*(Fx + Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - (ux + uy)*(ux + uy) + 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - - (dirGradM8 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((ux + uy)*(ux + uy)) + - 0.3333333333333333*(-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)))); - - // q = 9 - dist[nr10] = m9 - (m9-feq9)/tau + 0.0625*(2*(Fy + Fx*(-1 + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - (ux - uy)*(ux - uy) + 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - - (dirGradM9 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((ux - uy)*(ux - uy)) + - 0.3333333333333333*(-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)))); - - // q = 10 - dist[nr9] = m10 - (m10-feq10)/tau + 0.0625*(2*(Fx*(1 + ux) + Fy*(-1 + uy) + Fz*uz)*(-0.2222222222222222 - (ux - uy)*(ux - uy) + 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) - - (dirGradM10 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((ux - uy)*(ux - uy)) + - 0.3333333333333333*(-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)))); - - // q = 11 - dist[nr12] = m11 - (m11-feq11)/tau + 0.0625*(-2*(Fx*(-1 + ux) + Fy*uy + Fz*(-1 + uz))*(0.2222222222222222 + (ux + uz)*(ux + uz) - 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) - - (dirGradM11 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((ux + uz)*(ux + uz)) + - 0.3333333333333333*(-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)))); - - // q = 12 - dist[nr11] = m12 - (m12-feq12)/tau + 0.0625*(2*(Fx + Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - (ux + uz)*(ux + uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) - - (dirGradM12 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((ux + uz)*(ux + uz)) + - 0.3333333333333333*(-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*(2 + uz))))); - - // q = 13 - dist[nr14] = m13 - (m13-feq13)/tau + 0.0625*(2*(Fz + Fx*(-1 + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - (ux - uz)*(ux - uz) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) - - (dirGradM13 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((ux - uz)*(ux - uz)) + - 0.3333333333333333*(-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))))); - - // q= 14 - dist[nr13] = m14 - (m14-feq14)/tau + 0.0625*(2*(Fx*(1 + ux) + Fy*uy + Fz*(-1 + uz))*(-0.2222222222222222 - (ux - uz)*(ux - uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) - - (dirGradM14 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((ux - uz)*(ux - uz)) + - 0.3333333333333333*(-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)))); - - // q = 15 - dist[nr16] = m15 - (m15-feq15)/tau + 0.0625*(-2*(Fx*ux + Fy*(-1 + uy) + Fz*(-1 + uz))*(0.2222222222222222 + (uy + uz)*(uy + uz) - 0.3333333333333333*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) - - (dirGradM15 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((uy + uz)*(uy + uz)) + - 0.3333333333333333*(-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)))); - - // q = 16 - dist[nr15] = m16 - (m16-feq16)/tau + 0.0625*(2*(Fy + Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - (uy + uz)*(uy + uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) - - (dirGradM16 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((uy + uz)*(uy + uz)) + - 0.3333333333333333*(-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))))); - - // q = 17 - dist[nr18] = m17 - (m17-feq17)/tau + 0.0625*(2*(Fz + Fx*ux + Fy*(-1 + uy) + Fz*uz)*(-0.2222222222222222 - (uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) - - (dirGradM17 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((uy - uz)*(uy - uz)) + - 0.3333333333333333*(-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))))); - - // q = 18 - dist[nr17] = m18 - (m18-feq18)/tau + 0.0625*(2*(Fx*ux + Fy*(1 + uy) + Fz*(-1 + uz))*(-0.2222222222222222 - (uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) - - (dirGradM18 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((uy - uz)*(uy - uz)) + - 0.3333333333333333*(-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)))); - //----------------------------------------------------------------------------------------------------------------------------------------// - - //Update color gradient on device - ColorGrad[0*Np+n] = nx; - ColorGrad[1*Np+n] = ny; - ColorGrad[2*Np+n] = nz; - // ----------------------------- compute phase field evolution ---------------------------------------- - //Normalize the Color Gradient - C = sqrt(nx*nx+ny*ny+nz*nz); - if (C<1.0e-14) nx = ny = nz = 0.0; - //compute surface tension-related parameter - //theta = 4.5*M*2.0*(1-phi*phi)/W; - theta = 4.5*M*2.0*(1-phi_temp*phi_temp)/W; - - //load distributions of phase field - //q=0 - h0 = hq[n]; - //q=1 - h1 = hq[nr1]; - - //q=2 - h2 = hq[nr2]; - - //q=3 - h3 = hq[nr3]; - - //q=4 - h4 = hq[nr4]; - - //q=5 - h5 = hq[nr5]; - - //q=6 - h6 = hq[nr6]; - - //-------------------------------- BGK collison for phase field ---------------------------------// - // q = 0 - hq[n] = h0 - (h0 - 0.3333333333333333*phi)/tauM; - - // q = 1 - hq[nr2] = h1 - (h1 - 0.1111111111111111*nx*theta - phi*(0.1111111111111111 + 0.5*ux))/tauM; - - // q = 2 - hq[nr1] = h2 - (h2 + 0.1111111111111111*nx*theta - phi*(0.1111111111111111 - 0.5*ux))/tauM; - - // q = 3 - hq[nr4] = h3 - (h3 - 0.1111111111111111*ny*theta - phi*(0.1111111111111111 + 0.5*uy))/tauM; - - // q = 4 - hq[nr3] = h4 - (h4 + 0.1111111111111111*ny*theta - phi*(0.1111111111111111 - 0.5*uy))/tauM; - - // q = 5 - hq[nr6] = h5 - (h5 - 0.1111111111111111*nz*theta - phi*(0.1111111111111111 + 0.5*uz))/tauM; - - // q = 6 - hq[nr5] = h6 - (h6 + 0.1111111111111111*nz*theta - phi*(0.1111111111111111 - 0.5*uz))/tauM; - //........................................................................ - - //Update velocity on device - Vel[0*Np+n] = ux; - Vel[1*Np+n] = uy; - Vel[2*Np+n] = uz; - //Update pressure on device - Pressure[n] = p; - //Update chemical potential on device - mu_phi[n] = chem; - - - } +extern "C" void ScaLBL_D3Q19_AAodd_FreeLeeModel_Combined( + int *neighborList, int *Map, double *dist, double *hq, double *Den, + double *Phi, double *mu_phi, double *Vel, double *Pressure, + double *ColorGrad, double rhoA, double rhoB, double tauA, double tauB, + double tauM, double kappa, double beta, double W, double Fx, double Fy, + double Fz, int strideY, int strideZ, int start, int finish, int Np) { + + int nn, nn2x, ijk; + int nr1, nr2, nr3, nr4, nr5, nr6, nr7, nr8, nr9, nr10, nr11, nr12, nr13, + nr14, nr15, nr16, nr17, nr18; + double ux, uy, uz; //fluid velocity + double p; //pressure + double chem; //chemical potential + double phi; //phase field + double rho0; //fluid density + // distribution functions + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; + double m0, m3, m5, m7; + //double mm1,mm2,mm4,mm6,mm8,mm9,mm10,mm11,mm12,mm13,mm14,mm15,mm16,mm17,mm18; + //double mm3,mm5,mm7; + double feq0, feq1, feq2, feq3, feq4, feq5, feq6, feq7, feq8, feq9, feq10, + feq11, feq12, feq13, feq14, feq15, feq16, feq17, feq18; + double nx, ny, nz; //normal color gradient + double mgx, mgy, mgz; //mixed gradient reaching secondary neighbor + double dirGradC1, dirGradC2, dirGradC3, dirGradC4, dirGradC5, dirGradC6, + dirGradC7, dirGradC8, dirGradC9, dirGradC10, dirGradC11, dirGradC12; + double dirGradC13, dirGradC14, dirGradC15, dirGradC16, dirGradC17, + dirGradC18; + double dirGradM1, dirGradM2, dirGradM3, dirGradM4, dirGradM5, dirGradM6, + dirGradM7, dirGradM8, dirGradM9, dirGradM10, dirGradM11, dirGradM12; + double dirGradM13, dirGradM14, dirGradM15, dirGradM16, dirGradM17, + dirGradM18; + + double h0, h1, h2, h3, h4, h5, h6; //distributions for LB phase field + double tau; //position dependent LB relaxation time for fluid + double C, theta; + double M = + 2.0 / 9.0 * + (tauM - 0.5); //diffusivity (or mobility) for the phase field D3Q7 + double phi_temp; + + for (int n = start; n < finish; n++) { + + rho0 = Den[n]; //load density + + // Get the 1D index based on regular data layout + ijk = Map[n]; + phi = Phi[ijk]; // load phase field + phi_temp = phi; + if (phi > 1.f) + phi_temp = 1.0; + if (phi < -1.f) + phi_temp = -1.0; + + // local relaxation time + tau = tauA + 0.5 * (1.0 - phi) * (tauB - tauA); + + // COMPUTE THE COLOR GRADIENT + //........................................................................ + //.................Read Phase Indicator Values............................ + //........................................................................ + nn = ijk - 1; // neighbor index (get convention) + m2 = Phi[nn]; // get neighbor for phi - 1 + //........................................................................ + nn = ijk + 1; // neighbor index (get convention) + m1 = Phi[nn]; // get neighbor for phi - 2 + dirGradC1 = 0.5 * (m1 - m2); + dirGradC2 = 0.5 * (m2 - m1); + //........................................................................ + nn = ijk - strideY; // neighbor index (get convention) + m4 = Phi[nn]; // get neighbor for phi - 3 + //........................................................................ + nn = ijk + strideY; // neighbor index (get convention) + m3 = Phi[nn]; // get neighbor for phi - 4 + dirGradC3 = 0.5 * (m3 - m4); + dirGradC4 = 0.5 * (m4 - m3); + //........................................................................ + nn = ijk - strideZ; // neighbor index (get convention) + m6 = Phi[nn]; // get neighbor for phi - 5 + //........................................................................ + nn = ijk + strideZ; // neighbor index (get convention) + m5 = Phi[nn]; // get neighbor for phi - 6 + dirGradC5 = 0.5 * (m5 - m6); + dirGradC6 = 0.5 * (m6 - m5); + //........................................................................ + nn = ijk - strideY - 1; // neighbor index (get convention) + m8 = Phi[nn]; // get neighbor for phi - 7 + //........................................................................ + nn = ijk + strideY + 1; // neighbor index (get convention) + m7 = Phi[nn]; // get neighbor for phi - 8 + dirGradC7 = 0.5 * (m7 - m8); + dirGradC8 = 0.5 * (m8 - m7); + //........................................................................ + nn = ijk + strideY - 1; // neighbor index (get convention) + m10 = Phi[nn]; // get neighbor for phi - 9 + //........................................................................ + nn = ijk - strideY + 1; // neighbor index (get convention) + m9 = Phi[nn]; // get neighbor for phi - 10 + dirGradC9 = 0.5 * (m9 - m10); + dirGradC10 = 0.5 * (m10 - m9); + //........................................................................ + nn = ijk - strideZ - 1; // neighbor index (get convention) + m12 = Phi[nn]; // get neighbor for phi - 11 + //........................................................................ + nn = ijk + strideZ + 1; // neighbor index (get convention) + m11 = Phi[nn]; // get neighbor for phi - 12 + dirGradC11 = 0.5 * (m11 - m12); + dirGradC12 = 0.5 * (m12 - m11); + //........................................................................ + nn = ijk + strideZ - 1; // neighbor index (get convention) + m14 = Phi[nn]; // get neighbor for phi - 13 + //........................................................................ + nn = ijk - strideZ + 1; // neighbor index (get convention) + m13 = Phi[nn]; // get neighbor for phi - 14 + dirGradC13 = 0.5 * (m13 - m14); + dirGradC14 = 0.5 * (m14 - m13); + //........................................................................ + nn = ijk - strideZ - strideY; // neighbor index (get convention) + m16 = Phi[nn]; // get neighbor for phi - 15 + //........................................................................ + nn = ijk + strideZ + strideY; // neighbor index (get convention) + m15 = Phi[nn]; // get neighbor for phi - 16 + dirGradC15 = 0.5 * (m15 - m16); + dirGradC16 = 0.5 * (m16 - m15); + //........................................................................ + nn = ijk + strideZ - strideY; // neighbor index (get convention) + m18 = Phi[nn]; // get neighbor for phi - 17 + //........................................................................ + nn = ijk - strideZ + strideY; // neighbor index (get convention) + m17 = Phi[nn]; // get neighbor for phi - 18 + dirGradC17 = 0.5 * (m17 - m18); + dirGradC18 = 0.5 * (m18 - m17); + + // compute mixed difference (Eq.30, A.Fukhari et al. JCP 315(2016) 434-457) + //........................................................................ + nn2x = ijk + 2; // neighbor index (get convention) + dirGradM1 = Phi[nn2x]; // get neighbor for phi - 1 + dirGradM1 = 0.25 * (-dirGradM1 + 5.0 * m1 - 3.0 * phi - m2); + //........................................................................ + nn2x = ijk - 2; // neighbor index (get convention) + dirGradM2 = Phi[nn2x]; // get neighbor for phi - 2 + dirGradM2 = 0.25 * (-dirGradM2 + 5.0 * m2 - 3.0 * phi - m1); + //........................................................................ + nn2x = ijk + strideY * 2; // neighbor index (get convention) + dirGradM3 = Phi[nn2x]; // get neighbor for phi - 3 + dirGradM3 = 0.25 * (-dirGradM3 + 5.0 * m3 - 3.0 * phi - m4); + //........................................................................ + nn2x = ijk - strideY * 2; // neighbor index (get convention) + dirGradM4 = Phi[nn2x]; // get neighbor for phi - 4 + dirGradM4 = 0.25 * (-dirGradM4 + 5.0 * m4 - 3.0 * phi - m3); + //........................................................................ + nn2x = ijk + strideZ * 2; // neighbor index (get convention) + dirGradM5 = Phi[nn2x]; // get neighbor for phi - 5 + dirGradM5 = 0.25 * (-dirGradM5 + 5.0 * m5 - 3.0 * phi - m6); + //........................................................................ + nn2x = ijk - strideZ * 2; // neighbor index (get convention) + dirGradM6 = Phi[nn2x]; // get neighbor for phi - 6 + dirGradM6 = 0.25 * (-dirGradM6 + 5.0 * m6 - 3.0 * phi - m5); + //........................................................................ + nn2x = ijk + strideY * 2 + 2; // neighbor index (get convention) + dirGradM7 = Phi[nn2x]; // get neighbor for phi - 7 + dirGradM7 = 0.25 * (-dirGradM7 + 5.0 * m7 - 3.0 * phi - m8); + //........................................................................ + nn2x = ijk - strideY * 2 - 2; // neighbor index (get convention) + dirGradM8 = Phi[nn2x]; // get neighbor for phi - 8 + dirGradM8 = 0.25 * (-dirGradM8 + 5.0 * m8 - 3.0 * phi - m7); + //........................................................................ + nn2x = ijk - strideY * 2 + 2; // neighbor index (get convention) + dirGradM9 = Phi[nn2x]; // get neighbor for phi - 9 + dirGradM9 = 0.25 * (-dirGradM9 + 5.0 * m9 - 3.0 * phi - m10); + //........................................................................ + nn2x = ijk + strideY * 2 - 2; // neighbor index (get convention) + dirGradM10 = Phi[nn2x]; // get neighbor for phi - 10 + dirGradM10 = 0.25 * (-dirGradM10 + 5.0 * m10 - 3.0 * phi - m9); + //........................................................................ + nn2x = ijk + strideZ * 2 + 2; // neighbor index (get convention) + dirGradM11 = Phi[nn2x]; // get neighbor for phi - 11 + dirGradM11 = 0.25 * (-dirGradM11 + 5.0 * m11 - 3.0 * phi - m12); + //........................................................................ + nn2x = ijk - strideZ * 2 - 2; // neighbor index (get convention) + dirGradM12 = Phi[nn2x]; // get neighbor for phi - 12 + dirGradM12 = 0.25 * (-dirGradM12 + 5.0 * m12 - 3.0 * phi - m11); + //........................................................................ + nn2x = ijk - strideZ * 2 + 2; // neighbor index (get convention) + dirGradM13 = Phi[nn2x]; // get neighbor for phi - 13 + dirGradM13 = 0.25 * (-dirGradM13 + 5.0 * m13 - 3.0 * phi - m14); + //........................................................................ + nn2x = ijk + strideZ * 2 - 2; // neighbor index (get convention) + dirGradM14 = Phi[nn2x]; // get neighbor for phi - 14 + dirGradM14 = 0.25 * (-dirGradM14 + 5.0 * m14 - 3.0 * phi - m13); + //........................................................................ + nn2x = + ijk + strideZ * 2 + strideY * 2; // neighbor index (get convention) + dirGradM15 = Phi[nn2x]; // get neighbor for phi - 15 + dirGradM15 = 0.25 * (-dirGradM15 + 5.0 * m15 - 3.0 * phi - m16); + //........................................................................ + nn2x = + ijk - strideZ * 2 - strideY * 2; // neighbor index (get convention) + dirGradM16 = Phi[nn2x]; // get neighbor for phi - 16 + dirGradM16 = 0.25 * (-dirGradM16 + 5.0 * m16 - 3.0 * phi - m15); + //........................................................................ + nn2x = + ijk - strideZ * 2 + strideY * 2; // neighbor index (get convention) + dirGradM17 = Phi[nn2x]; // get neighbor for phi - 17 + dirGradM17 = 0.25 * (-dirGradM17 + 5.0 * m17 - 3.0 * phi - m18); + //........................................................................ + nn2x = + ijk + strideZ * 2 - strideY * 2; // neighbor index (get convention) + dirGradM18 = Phi[nn2x]; // get neighbor for phi - 18 + dirGradM18 = 0.25 * (-dirGradM18 + 5.0 * m18 - 3.0 * phi - m17); + + //............Compute the Color Gradient................................... + nx = 3.0 * 1.0 / 18.0 * + (dirGradC1 - dirGradC2 + + 0.5 * (dirGradC7 - dirGradC8 + dirGradC9 - dirGradC10 + + dirGradC11 - dirGradC12 + dirGradC13 - dirGradC14)); + ny = 3.0 * 1.0 / 18.0 * + (dirGradC3 - dirGradC4 + + 0.5 * (dirGradC7 - dirGradC8 - dirGradC9 + dirGradC10 + + dirGradC15 - dirGradC16 + dirGradC17 - dirGradC18)); + nz = 3.0 * 1.0 / 18.0 * + (dirGradC5 - dirGradC6 + + 0.5 * (dirGradC11 - dirGradC12 - dirGradC13 + dirGradC14 + + dirGradC15 - dirGradC16 - dirGradC17 + dirGradC18)); + //............Compute the Chemical Potential............................... + //chem = 2.0*3.0/18.0*(m1+m2+m3+m4+m5+m6-6*phi+0.5*(m7+m8+m9+m10+m11+m12+m13+m14+m15+m16+m17+m18-12*phi));//intermediate var, i.e. the laplacian + //chem = 4.0*beta*phi*(phi+1.0)*(phi-1.0)-kappa*chem; + chem = 2.0 * 3.0 / 18.0 * + (m1 + m2 + m3 + m4 + m5 + m6 - 6 * phi_temp + + 0.5 * (m7 + m8 + m9 + m10 + m11 + m12 + m13 + m14 + m15 + m16 + + m17 + m18 - + 12 * phi_temp)); //intermediate var, i.e. the laplacian + chem = 4.0 * beta * phi_temp * (phi_temp + 1.0) * (phi_temp - 1.0) - + kappa * chem; + //............Compute the Mixed Gradient................................... + mgx = 3.0 * 1.0 / 18.0 * + (dirGradM1 - dirGradM2 + + 0.5 * (dirGradM7 - dirGradM8 + dirGradM9 - dirGradM10 + + dirGradM11 - dirGradM12 + dirGradM13 - dirGradM14)); + mgy = 3.0 * 1.0 / 18.0 * + (dirGradM3 - dirGradM4 + + 0.5 * (dirGradM7 - dirGradM8 - dirGradM9 + dirGradM10 + + dirGradM15 - dirGradM16 + dirGradM17 - dirGradM18)); + mgz = 3.0 * 1.0 / 18.0 * + (dirGradM5 - dirGradM6 + + 0.5 * (dirGradM11 - dirGradM12 - dirGradM13 + dirGradM14 + + dirGradM15 - dirGradM16 - dirGradM17 + dirGradM18)); + + //de-noise color gradient and mixed gradient + C = sqrt(nx * nx + ny * ny + nz * nz); + if (C < 1.0e-12) + nx = ny = nz = 0.0; + double mg_mag = sqrt(mgx * mgx + mgy * mgy + mgz * mgz); + if (mg_mag < 1.0e-12) + mgx = mgy = mgz = 0.0; + //maybe you can also de-noise chemical potential ? within the bulk phase chem should be zero + if (fabs(chem) < 1.0e-12) + chem = 0.0; + + // q=0 + m0 = dist[n]; + // q=1 + nr1 = neighborList[n]; // neighbor 2 ( > 10Np => odd part of dist) + m1 = dist[nr1]; // reading the f1 data into register fq + + nr2 = neighborList[n + Np]; // neighbor 1 ( < 10Np => even part of dist) + m2 = dist[nr2]; // reading the f2 data into register fq + + // q=3 + nr3 = neighborList[n + 2 * Np]; // neighbor 4 + m3 = dist[nr3]; + + // q = 4 + nr4 = neighborList[n + 3 * Np]; // neighbor 3 + m4 = dist[nr4]; + + // q=5 + nr5 = neighborList[n + 4 * Np]; + m5 = dist[nr5]; + + // q = 6 + nr6 = neighborList[n + 5 * Np]; + m6 = dist[nr6]; + + // q=7 + nr7 = neighborList[n + 6 * Np]; + m7 = dist[nr7]; + + // q = 8 + nr8 = neighborList[n + 7 * Np]; + m8 = dist[nr8]; + + // q=9 + nr9 = neighborList[n + 8 * Np]; + m9 = dist[nr9]; + + // q = 10 + nr10 = neighborList[n + 9 * Np]; + m10 = dist[nr10]; + + // q=11 + nr11 = neighborList[n + 10 * Np]; + m11 = dist[nr11]; + + // q=12 + nr12 = neighborList[n + 11 * Np]; + m12 = dist[nr12]; + + // q=13 + nr13 = neighborList[n + 12 * Np]; + m13 = dist[nr13]; + + // q=14 + nr14 = neighborList[n + 13 * Np]; + m14 = dist[nr14]; + + // q=15 + nr15 = neighborList[n + 14 * Np]; + m15 = dist[nr15]; + + // q=16 + nr16 = neighborList[n + 15 * Np]; + m16 = dist[nr16]; + + // q=17 + nr17 = neighborList[n + 16 * Np]; + m17 = dist[nr17]; + + // q=18 + nr18 = neighborList[n + 17 * Np]; + m18 = dist[nr18]; + + //compute fluid velocity + ux = 3.0 / rho0 * + (m1 - m2 + m7 - m8 + m9 - m10 + m11 - m12 + m13 - m14 + + 0.5 * (chem * nx + Fx) / + 1.0); // used to divide by 3.0 think that was wrong + uy = 3.0 / rho0 * + (m3 - m4 + m7 - m8 - m9 + m10 + m15 - m16 + m17 - m18 + + 0.5 * (chem * ny + Fy) / 1.0); + uz = 3.0 / rho0 * + (m5 - m6 + m11 - m12 - m13 + m14 + m15 - m16 - m17 + m18 + + 0.5 * (chem * nz + Fz) / 1.0); + //compute pressure + p = (m0 + m2 + m1 + m4 + m3 + m6 + m5 + m8 + m7 + m10 + m9 + m12 + m11 + + m14 + m13 + m16 + m15 + m18 + m17) + + 0.5 * (rhoA - rhoB) / 2.0 / 3.0 * (ux * nx + uy * ny + uz * nz); + + //compute equilibrium distributions + feq0 = 0.3333333333333333 * p - + 0.25 * (Fx * ux + Fy * uy + Fz * uz) * + (-0.6666666666666666 + ux * ux + uy * uy + uz * uz) - + 0.16666666666666666 * rho0 * (ux * ux + uy * uy + uz * uz) - + 0.5 * (-(nx * ux) - ny * uy - nz * uz) * + (-0.08333333333333333 * (rhoA - rhoB) * + (ux * ux + uy * uy + uz * uz) + + chem * (0.3333333333333333 - + 0.5 * (ux * ux + uy * uy + uz * uz))); + feq1 = 0.05555555555555555 * p - + 0.08333333333333333 * rho0 * + (-(ux * ux) + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + uz * uz)) - + 0.125 * (Fx * (-1. + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) + + 0.3333333333333333 * + (-2. * ux + ux * ux + uy * uy + uz * uz)) - + 0.0625 * (dirGradC1 - nx * ux - ny * uy - nz * uz) * + (2 * chem * (ux * ux) - + 0.3333333333333333 * + ((-rhoA + rhoB) * (ux * ux) + + 2 * chem * (-2 * ux + ux * ux + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (-2 * ux + ux * ux + + uy * uy + uz * uz))); + feq2 = + 0.05555555555555555 * p - + 0.08333333333333333 * rho0 * + (-(ux * ux) + + 0.3333333333333333 * (2 * ux + ux * ux + uy * uy + uz * uz)) - + 0.125 * (Fx + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) + + 0.3333333333333333 * (2. * ux + ux * ux + uy * uy + uz * uz)) - + 0.0625 * (dirGradC2 - nx * ux - ny * uy - nz * uz) * + (2 * chem * (ux * ux) - + 0.3333333333333333 * + ((-rhoA + rhoB) * (ux * ux) + + 2 * chem * (2 * ux + ux * ux + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - + (rhoA - rhoB) * (2 * ux + ux * ux + uy * uy + uz * uz))); + feq3 = + 0.05555555555555555 * p - + 0.08333333333333333 * rho0 * + (-(uy * uy) + + 0.3333333333333333 * (ux * ux - 2 * uy + uy * uy + uz * uz)) - + 0.125 * (Fx * ux + Fy * (-1. + uy) + Fz * uz) * + (-0.2222222222222222 - 1. * (uy * uy) + + 0.3333333333333333 * (ux * ux - 2. * uy + uy * uy + uz * uz)) - + 0.0625 * (dirGradC3 - nx * ux - ny * uy - nz * uz) * + (2 * chem * (uy * uy) - + 0.3333333333333333 * + ((-rhoA + rhoB) * (uy * uy) + + 2 * chem * (ux * ux - 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - + (rhoA - rhoB) * (ux * ux - 2 * uy + uy * uy + uz * uz))); + feq4 = + 0.05555555555555555 * p - + 0.08333333333333333 * rho0 * + (-(uy * uy) + + 0.3333333333333333 * (ux * ux + 2 * uy + uy * uy + uz * uz)) - + 0.125 * (Fy + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (uy * uy) + + 0.3333333333333333 * (ux * ux + 2. * uy + uy * uy + uz * uz)) - + 0.0625 * (dirGradC4 - nx * ux - ny * uy - nz * uz) * + (2 * chem * (uy * uy) - + 0.3333333333333333 * + ((-rhoA + rhoB) * (uy * uy) + + 2 * chem * (ux * ux + 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - + (rhoA - rhoB) * (ux * ux + 2 * uy + uy * uy + uz * uz))); + feq5 = + 0.05555555555555555 * p - + 0.08333333333333333 * rho0 * + (-(uz * uz) + + 0.3333333333333333 * (ux * ux + uy * uy + (-2 + uz) * uz)) - + 0.125 * (Fx * ux + Fy * uy + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * (uz * uz) + + 0.3333333333333333 * (ux * ux + uy * uy + (-2. + uz) * uz)) - + 0.0625 * (dirGradC5 - nx * ux - ny * uy - nz * uz) * + (2 * chem * (uz * uz) - + 0.3333333333333333 * + ((-rhoA + rhoB) * (uz * uz) + + 2 * chem * (ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (4 * chem - + (rhoA - rhoB) * (ux * ux + uy * uy + (-2 + uz) * uz))); + feq6 = 0.05555555555555555 * p - + 0.08333333333333333 * rho0 * + (-(uz * uz) + + 0.3333333333333333 * (ux * ux + uy * uy + uz * (2 + uz))) - + 0.125 * (Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (uz * uz) + + 0.3333333333333333 * (ux * ux + uy * uy + uz * (2. + uz))) - + 0.0625 * (dirGradC6 - nx * ux - ny * uy - nz * uz) * + (2 * chem * (uz * uz) - + 0.3333333333333333 * + ((-rhoA + rhoB) * (uz * uz) + + 2 * chem * (ux * ux + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (4 * chem - + (rhoA - rhoB) * (ux * ux + uy * uy + uz * (2 + uz)))); + feq7 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((ux + uy) * (ux + uy)) + + 0.3333333333333333 * + (-2 * ux + ux * ux - 2 * uy + uy * uy + uz * uz)) - + 0.0625 * (Fx * (-1. + ux) + Fy * (-1. + uy) + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) - 2. * ux * uy - + 1. * (uy * uy) + + 0.3333333333333333 * + (-2. * ux + ux * ux - 2. * uy + uy * uy + uz * uz)) - + 0.03125 * (dirGradC7 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((ux + uy) * (ux + uy)) + + 0.3333333333333333 * + ((rhoA - rhoB) * ((ux + uy) * (ux + uy)) - + 2 * chem * + (-2 * ux + ux * ux - 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (-2 * ux + ux * ux - 2 * uy + + uy * uy + uz * uz))); + feq8 = 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((ux + uy) * (ux + uy)) + + 0.3333333333333333 * + (2 * ux + ux * ux + 2 * uy + uy * uy + uz * uz)) - + 0.0625 * (Fx + Fy + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) - 2. * ux * uy - + 1. * (uy * uy) + + 0.3333333333333333 * + (2. * ux + ux * ux + 2. * uy + uy * uy + uz * uz)) - + 0.03125 * (dirGradC8 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((ux + uy) * (ux + uy)) - + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux + uy) * (ux + uy))) + + 2 * chem * + (2 * ux + ux * ux + 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (2 * ux + ux * ux + 2 * uy + + uy * uy + uz * uz))); + feq9 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((ux - uy) * (ux - uy)) + + 0.3333333333333333 * + (-2 * ux + ux * ux + 2 * uy + uy * uy + uz * uz)) - + 0.0625 * (Fy + Fx * (-1. + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) + 2. * ux * uy - + 1. * (uy * uy) + + 0.3333333333333333 * + (-2. * ux + ux * ux + 2. * uy + uy * uy + uz * uz)) - + 0.03125 * (dirGradC9 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((ux - uy) * (ux - uy)) - + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uy) * (ux - uy))) + + 2 * chem * + (-2 * ux + ux * ux + 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (-2 * ux + ux * ux + 2 * uy + + uy * uy + uz * uz))); + feq10 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((ux - uy) * (ux - uy)) + + 0.3333333333333333 * + (2 * ux + ux * ux - 2 * uy + uy * uy + uz * uz)) - + 0.0625 * (Fx * (1 + ux) + Fy * (-1. + uy) + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) + 2. * ux * uy - + 1. * (uy * uy) + + 0.3333333333333333 * + (2. * ux + ux * ux - 2. * uy + uy * uy + uz * uz)) - + 0.03125 * (dirGradC10 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((ux - uy) * (ux - uy)) - + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uy) * (ux - uy))) + + 2 * chem * + (2 * ux + ux * ux - 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (2 * ux + ux * ux - 2 * uy + + uy * uy + uz * uz))); + feq11 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((ux + uz) * (ux + uz)) + + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) - + 0.0625 * (Fx * (-1. + ux) + Fy * uy + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * (ux * ux) - 2. * ux * uz - + 1. * (uz * uz) + + 0.3333333333333333 * + (-2. * ux + ux * ux + uy * uy + (-2. + uz) * uz)) - + 0.03125 * (dirGradC11 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((ux + uz) * (ux + uz)) + + 0.3333333333333333 * + ((rhoA - rhoB) * ((ux + uz) * (ux + uz)) - + 2 * chem * + (-2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (-2 * ux + ux * ux + uy * uy + + (-2 + uz) * uz))); + feq12 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((ux + uz) * (ux + uz)) + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + uz * (2 + uz))) - + 0.0625 * (Fx + Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) - 2. * ux * uz - + 1. * (uz * uz) + + 0.3333333333333333 * + (2. * ux + ux * ux + uy * uy + uz * (2. + uz))) - + 0.03125 * (dirGradC12 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((ux + uz) * (ux + uz)) - + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux + uz) * (ux + uz))) + + 2 * chem * (2 * ux + ux * ux + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (2 * ux + ux * ux + uy * uy + + uz * (2 + uz)))); + feq13 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((ux - uz) * (ux - uz)) + + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + uz * (2 + uz))) - + 0.0625 * (Fz + Fx * (-1. + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) + 2. * ux * uz - + 1. * (uz * uz) + + 0.3333333333333333 * + (-2. * ux + ux * ux + uy * uy + uz * (2. + uz))) - + 0.03125 * (dirGradC13 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((ux - uz) * (ux - uz)) - + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uz) * (ux - uz))) + + 2 * chem * + (-2 * ux + ux * ux + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (-2 * ux + ux * ux + uy * uy + + uz * (2 + uz)))); + feq14 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((ux - uz) * (ux - uz)) + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) - + 0.0625 * (Fx * (1 + ux) + Fy * uy + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * (ux * ux) + 2. * ux * uz - + 1. * (uz * uz) + + 0.3333333333333333 * + (2. * ux + ux * ux + uy * uy + (-2. + uz) * uz)) - + 0.03125 * (dirGradC14 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((ux - uz) * (ux - uz)) - + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uz) * (ux - uz))) + + 2 * chem * + (2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (2 * ux + ux * ux + uy * uy + + (-2 + uz) * uz))); + feq15 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((uy + uz) * (uy + uz)) + + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + (-2 + uz) * uz)) - + 0.0625 * (Fx * ux + Fy * (-1. + uy) + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * (uy * uy) - 2. * uy * uz - + 1. * (uz * uz) + + 0.3333333333333333 * + (ux * ux - 2. * uy + uy * uy + (-2. + uz) * uz)) - + 0.03125 * (dirGradC15 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((uy + uz) * (uy + uz)) + + 0.3333333333333333 * + ((rhoA - rhoB) * ((uy + uz) * (uy + uz)) - + 2 * chem * + (ux * ux - 2 * uy + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (ux * ux - 2 * uy + uy * uy + + (-2 + uz) * uz))); + feq16 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((uy + uz) * (uy + uz)) + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + uz * (2 + uz))) - + 0.0625 * (Fy + Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (uy * uy) - 2. * uy * uz - + 1. * (uz * uz) + + 0.3333333333333333 * + (ux * ux + 2. * uy + uy * uy + uz * (2. + uz))) - + 0.03125 * (dirGradC16 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((uy + uz) * (uy + uz)) - + 0.3333333333333333 * + (-((rhoA - rhoB) * ((uy + uz) * (uy + uz))) + + 2 * chem * (ux * ux + 2 * uy + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (ux * ux + 2 * uy + uy * uy + + uz * (2 + uz)))); + feq17 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((uy - uz) * (uy - uz)) + + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + uz * (2 + uz))) - + 0.0625 * (Fz + Fx * ux + Fy * (-1. + uy) + Fz * uz) * + (-0.2222222222222222 - 1. * (uy * uy) + 2. * uy * uz - + 1. * (uz * uz) + + 0.3333333333333333 * + (ux * ux - 2. * uy + uy * uy + uz * (2. + uz))) - + 0.03125 * (dirGradC17 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((uy - uz) * (uy - uz)) - + 0.3333333333333333 * + (-((rhoA - rhoB) * ((uy - uz) * (uy - uz))) + + 2 * chem * (ux * ux - 2 * uy + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (ux * ux - 2 * uy + uy * uy + + uz * (2 + uz)))); + feq18 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((uy - uz) * (uy - uz)) + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + (-2 + uz) * uz)) - + 0.0625 * (Fx * ux + Fy * (1 + uy) + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * (uy * uy) + 2. * uy * uz - + 1. * (uz * uz) + + 0.3333333333333333 * + (ux * ux + 2. * uy + uy * uy + (-2. + uz) * uz)) - + 0.03125 * (dirGradC18 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((uy - uz) * (uy - uz)) - + 0.3333333333333333 * + (-((rhoA - rhoB) * ((uy - uz) * (uy - uz))) + + 2 * chem * + (ux * ux + 2 * uy + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (ux * ux + 2 * uy + uy * uy + + (-2 + uz) * uz))); + + //------------------------------------------------- BCK collison ------------------------------------------------------------// + // q=0 + dist[n] = + m0 - (m0 - feq0) / tau + + 0.25 * (2 * (Fx * ux + Fy * uy + Fz * uz) * + (-0.6666666666666666 + ux * ux + uy * uy + uz * uz) + + (mgx * ux + mgy * uy + mgz * uz) * + (2 * chem * (ux * ux + uy * uy + uz * uz) + + 0.3333333333333333 * + (-4 * chem + + (rhoA - rhoB) * (ux * ux + uy * uy + uz * uz)))); + + // q = 1 + dist[nr2] = + m1 - (m1 - feq1) / tau + + 0.125 * + (2 * (Fx * (-1 + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - ux * ux + + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + uz * uz)) - + (dirGradM1 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * (ux * ux) + + 0.3333333333333333 * + ((-rhoA + rhoB) * (ux * ux) + + 2 * chem * (-2 * ux + ux * ux + uy * uy + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + (rhoA - rhoB) * (-2 * ux + ux * ux + + uy * uy + uz * uz)))); + + // q=2 + dist[nr1] = + m2 - (m2 - feq2) / tau + + 0.125 * + (2 * (Fx + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - ux * ux + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + uz * uz)) - + (dirGradM2 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * (ux * ux) + + 0.3333333333333333 * + ((-rhoA + rhoB) * (ux * ux) + + 2 * chem * (2 * ux + ux * ux + uy * uy + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + (rhoA - rhoB) * (2 * ux + ux * ux + + uy * uy + uz * uz)))); + + // q = 3 + dist[nr4] = + m3 - (m3 - feq3) / tau + + 0.125 * + (2 * (Fx * ux + Fy * (-1 + uy) + Fz * uz) * + (-0.2222222222222222 - uy * uy + + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + uz * uz)) - + (dirGradM3 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * (uy * uy) + + 0.3333333333333333 * + ((-rhoA + rhoB) * (uy * uy) + + 2 * chem * (ux * ux - 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + (rhoA - rhoB) * (ux * ux - 2 * uy + + uy * uy + uz * uz)))); + + // q = 4 + dist[nr3] = + m4 - (m4 - feq4) / tau + + 0.125 * + (2 * (Fy + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - uy * uy + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + uz * uz)) - + (dirGradM4 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * (uy * uy) + + 0.3333333333333333 * + ((-rhoA + rhoB) * (uy * uy) + + 2 * chem * (ux * ux + 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + (rhoA - rhoB) * (ux * ux + 2 * uy + + uy * uy + uz * uz)))); + + // q = 5 + dist[nr6] = + m5 - (m5 - feq5) / tau + + 0.125 * + (2 * (Fx * ux + Fy * uy + Fz * (-1 + uz)) * + (-0.2222222222222222 - uz * uz + + 0.3333333333333333 * + (ux * ux + uy * uy + (-2 + uz) * uz)) - + (dirGradM5 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * (uz * uz) + + 0.3333333333333333 * + ((-rhoA + rhoB) * (uz * uz) + + 2 * chem * (ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (-4 * chem + (rhoA - rhoB) * (ux * ux + uy * uy + + (-2 + uz) * uz)))); + + // q = 6 + dist[nr5] = + m6 - (m6 - feq6) / tau + + 0.125 * (2 * (Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - uz * uz + + 0.3333333333333333 * + (ux * ux + uy * uy + uz * (2 + uz))) - + (dirGradM6 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * (uz * uz) + + 0.3333333333333333 * + ((-rhoA + rhoB) * (uz * uz) + + 2 * chem * (ux * ux + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (-4 * chem + (rhoA - rhoB) * (ux * ux + uy * uy + + uz * (2 + uz))))); + + // q = 7 + dist[nr8] = + m7 - (m7 - feq7) / tau + + 0.0625 * (-2 * (Fx * (-1 + ux) + Fy * (-1 + uy) + Fz * uz) * + (0.2222222222222222 + (ux + uy) * (ux + uy) - + 0.3333333333333333 * (-2 * ux + ux * ux - 2 * uy + + uy * uy + uz * uz)) - + (dirGradM7 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((ux + uy) * (ux + uy)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux + uy) * (ux + uy))) + + 2 * chem * + (-2 * ux + ux * ux - 2 * uy + uy * uy + + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * (-2 * ux + ux * ux - 2 * uy + + uy * uy + uz * uz)))); + + // q = 8 + dist[nr7] = + m8 - (m8 - feq8) / tau + + 0.0625 * (2 * (Fx + Fy + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (ux + uy) * (ux + uy) + + 0.3333333333333333 * (2 * ux + ux * ux + 2 * uy + + uy * uy + uz * uz)) - + (dirGradM8 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((ux + uy) * (ux + uy)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux + uy) * (ux + uy))) + + 2 * chem * + (2 * ux + ux * ux + 2 * uy + uy * uy + + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * (2 * ux + ux * ux + 2 * uy + + uy * uy + uz * uz)))); + + // q = 9 + dist[nr10] = + m9 - (m9 - feq9) / tau + + 0.0625 * (2 * (Fy + Fx * (-1 + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (ux - uy) * (ux - uy) + + 0.3333333333333333 * (-2 * ux + ux * ux + 2 * uy + + uy * uy + uz * uz)) - + (dirGradM9 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((ux - uy) * (ux - uy)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uy) * (ux - uy))) + + 2 * chem * + (-2 * ux + ux * ux + 2 * uy + uy * uy + + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * (-2 * ux + ux * ux + 2 * uy + + uy * uy + uz * uz)))); + + // q = 10 + dist[nr9] = + m10 - (m10 - feq10) / tau + + 0.0625 * (2 * (Fx * (1 + ux) + Fy * (-1 + uy) + Fz * uz) * + (-0.2222222222222222 - (ux - uy) * (ux - uy) + + 0.3333333333333333 * (2 * ux + ux * ux - 2 * uy + + uy * uy + uz * uz)) - + (dirGradM10 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((ux - uy) * (ux - uy)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uy) * (ux - uy))) + + 2 * chem * + (2 * ux + ux * ux - 2 * uy + uy * uy + + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * (2 * ux + ux * ux - 2 * uy + + uy * uy + uz * uz)))); + + // q = 11 + dist[nr12] = + m11 - (m11 - feq11) / tau + + 0.0625 * + (-2 * (Fx * (-1 + ux) + Fy * uy + Fz * (-1 + uz)) * + (0.2222222222222222 + (ux + uz) * (ux + uz) - + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) - + (dirGradM11 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((ux + uz) * (ux + uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux + uz) * (ux + uz))) + + 2 * chem * + (-2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * (-2 * ux + ux * ux + uy * uy + + (-2 + uz) * uz)))); + + // q = 12 + dist[nr11] = + m12 - (m12 - feq12) / tau + + 0.0625 * + (2 * (Fx + Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (ux + uz) * (ux + uz) + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + uz * (2 + uz))) - + (dirGradM12 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((ux + uz) * (ux + uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux + uz) * (ux + uz))) + + 2 * chem * + (2 * ux + ux * ux + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (2 * ux + ux * ux + uy * uy + uz * (2 + uz))))); + + // q = 13 + dist[nr14] = + m13 - (m13 - feq13) / tau + + 0.0625 * + (2 * (Fz + Fx * (-1 + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (ux - uz) * (ux - uz) + + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + uz * (2 + uz))) - + (dirGradM13 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((ux - uz) * (ux - uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uz) * (ux - uz))) + + 2 * chem * + (-2 * ux + ux * ux + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (-2 * ux + ux * ux + uy * uy + uz * (2 + uz))))); + + // q= 14 + dist[nr13] = + m14 - (m14 - feq14) / tau + + 0.0625 * + (2 * (Fx * (1 + ux) + Fy * uy + Fz * (-1 + uz)) * + (-0.2222222222222222 - (ux - uz) * (ux - uz) + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) - + (dirGradM14 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((ux - uz) * (ux - uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uz) * (ux - uz))) + + 2 * chem * + (2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)))); + + // q = 15 + dist[nr16] = + m15 - (m15 - feq15) / tau + + 0.0625 * + (-2 * (Fx * ux + Fy * (-1 + uy) + Fz * (-1 + uz)) * + (0.2222222222222222 + (uy + uz) * (uy + uz) - + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + (-2 + uz) * uz)) - + (dirGradM15 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((uy + uz) * (uy + uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((uy + uz) * (uy + uz))) + + 2 * chem * + (ux * ux - 2 * uy + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (ux * ux - 2 * uy + uy * uy + (-2 + uz) * uz)))); + + // q = 16 + dist[nr15] = + m16 - (m16 - feq16) / tau + + 0.0625 * + (2 * (Fy + Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (uy + uz) * (uy + uz) + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + uz * (2 + uz))) - + (dirGradM16 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((uy + uz) * (uy + uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((uy + uz) * (uy + uz))) + + 2 * chem * + (ux * ux + 2 * uy + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (ux * ux + 2 * uy + uy * uy + uz * (2 + uz))))); + + // q = 17 + dist[nr18] = + m17 - (m17 - feq17) / tau + + 0.0625 * + (2 * (Fz + Fx * ux + Fy * (-1 + uy) + Fz * uz) * + (-0.2222222222222222 - (uy - uz) * (uy - uz) + + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + uz * (2 + uz))) - + (dirGradM17 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((uy - uz) * (uy - uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((uy - uz) * (uy - uz))) + + 2 * chem * + (ux * ux - 2 * uy + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (ux * ux - 2 * uy + uy * uy + uz * (2 + uz))))); + + // q = 18 + dist[nr17] = + m18 - (m18 - feq18) / tau + + 0.0625 * + (2 * (Fx * ux + Fy * (1 + uy) + Fz * (-1 + uz)) * + (-0.2222222222222222 - (uy - uz) * (uy - uz) + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + (-2 + uz) * uz)) - + (dirGradM18 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((uy - uz) * (uy - uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((uy - uz) * (uy - uz))) + + 2 * chem * + (ux * ux + 2 * uy + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (ux * ux + 2 * uy + uy * uy + (-2 + uz) * uz)))); + //----------------------------------------------------------------------------------------------------------------------------------------// + + //Update color gradient on device + ColorGrad[0 * Np + n] = nx; + ColorGrad[1 * Np + n] = ny; + ColorGrad[2 * Np + n] = nz; + // ----------------------------- compute phase field evolution ---------------------------------------- + //Normalize the Color Gradient + C = sqrt(nx * nx + ny * ny + nz * nz); + if (C < 1.0e-14) + nx = ny = nz = 0.0; + //compute surface tension-related parameter + //theta = 4.5*M*2.0*(1-phi*phi)/W; + theta = 4.5 * M * 2.0 * (1 - phi_temp * phi_temp) / W; + + //load distributions of phase field + //q=0 + h0 = hq[n]; + //q=1 + h1 = hq[nr1]; + + //q=2 + h2 = hq[nr2]; + + //q=3 + h3 = hq[nr3]; + + //q=4 + h4 = hq[nr4]; + + //q=5 + h5 = hq[nr5]; + + //q=6 + h6 = hq[nr6]; + + //-------------------------------- BGK collison for phase field ---------------------------------// + // q = 0 + hq[n] = h0 - (h0 - 0.3333333333333333 * phi) / tauM; + + // q = 1 + hq[nr2] = h1 - (h1 - 0.1111111111111111 * nx * theta - + phi * (0.1111111111111111 + 0.5 * ux)) / + tauM; + + // q = 2 + hq[nr1] = h2 - (h2 + 0.1111111111111111 * nx * theta - + phi * (0.1111111111111111 - 0.5 * ux)) / + tauM; + + // q = 3 + hq[nr4] = h3 - (h3 - 0.1111111111111111 * ny * theta - + phi * (0.1111111111111111 + 0.5 * uy)) / + tauM; + + // q = 4 + hq[nr3] = h4 - (h4 + 0.1111111111111111 * ny * theta - + phi * (0.1111111111111111 - 0.5 * uy)) / + tauM; + + // q = 5 + hq[nr6] = h5 - (h5 - 0.1111111111111111 * nz * theta - + phi * (0.1111111111111111 + 0.5 * uz)) / + tauM; + + // q = 6 + hq[nr5] = h6 - (h6 + 0.1111111111111111 * nz * theta - + phi * (0.1111111111111111 - 0.5 * uz)) / + tauM; + //........................................................................ + + //Update velocity on device + Vel[0 * Np + n] = ux; + Vel[1 * Np + n] = uy; + Vel[2 * Np + n] = uz; + //Update pressure on device + Pressure[n] = p; + //Update chemical potential on device + mu_phi[n] = chem; + } } -extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist, double *hq, double *Den, double *Phi, double *mu_phi, double *Vel, double *Pressure, double *ColorGrad, - double rhoA, double rhoB, double tauA, double tauB, double tauM, double kappa, double beta, double W, double Fx, double Fy, double Fz, - int strideY, int strideZ, int start, int finish, int Np){ - - int nn,nn2x,ijk; - double ux,uy,uz;//fluid velocity - double p;//pressure - double chem;//chemical potential - double phi; //phase field - double rho0;//fluid density - // distribution functions - double m1,m2,m4,m6,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18; - double m0,m3,m5,m7; - //double mm1,mm2,mm4,mm6,mm8,mm9,mm10,mm11,mm12,mm13,mm14,mm15,mm16,mm17,mm18; - //double mm3,mm5,mm7; - double feq0,feq1,feq2,feq3,feq4,feq5,feq6,feq7,feq8,feq9,feq10,feq11,feq12,feq13,feq14,feq15,feq16,feq17,feq18; - double nx,ny,nz;//normal color gradient - double mgx,mgy,mgz;//mixed gradient reaching secondary neighbor - double dirGradC1,dirGradC2,dirGradC3,dirGradC4,dirGradC5,dirGradC6,dirGradC7,dirGradC8,dirGradC9,dirGradC10,dirGradC11,dirGradC12; - double dirGradC13,dirGradC14,dirGradC15,dirGradC16,dirGradC17,dirGradC18; - double dirGradM1,dirGradM2,dirGradM3,dirGradM4,dirGradM5,dirGradM6,dirGradM7,dirGradM8,dirGradM9,dirGradM10,dirGradM11,dirGradM12; - double dirGradM13,dirGradM14,dirGradM15,dirGradM16,dirGradM17,dirGradM18; - - double h0,h1,h2,h3,h4,h5,h6;//distributions for LB phase field - double tau;//position dependent LB relaxation time for fluid - double C,theta; - double M = 2.0/9.0*(tauM-0.5);//diffusivity (or mobility) for the phase field D3Q7 - double phi_temp; - - for (int n=start; n1.f) phi_temp=1.0; - if (phi<-1.f) phi_temp=-1.0; - - // local relaxation time - tau=tauA + 0.5*(1.0-phi)*(tauB-tauA); - - // COMPUTE THE COLOR GRADIENT - //........................................................................ - //.................Read Phase Indicator Values............................ - //........................................................................ - nn = ijk-1; // neighbor index (get convention) - m2 = Phi[nn]; // get neighbor for phi - 1 - //........................................................................ - nn = ijk+1; // neighbor index (get convention) - m1 = Phi[nn]; // get neighbor for phi - 2 - dirGradC1 = 0.5*(m1-m2); - dirGradC2 = 0.5*(m2-m1); - //........................................................................ - nn = ijk-strideY; // neighbor index (get convention) - m4 = Phi[nn]; // get neighbor for phi - 3 - //........................................................................ - nn = ijk+strideY; // neighbor index (get convention) - m3 = Phi[nn]; // get neighbor for phi - 4 - dirGradC3 = 0.5*(m3-m4); - dirGradC4 = 0.5*(m4-m3); - //........................................................................ - nn = ijk-strideZ; // neighbor index (get convention) - m6 = Phi[nn]; // get neighbor for phi - 5 - //........................................................................ - nn = ijk+strideZ; // neighbor index (get convention) - m5 = Phi[nn]; // get neighbor for phi - 6 - dirGradC5 = 0.5*(m5-m6); - dirGradC6 = 0.5*(m6-m5); - //........................................................................ - nn = ijk-strideY-1; // neighbor index (get convention) - m8 = Phi[nn]; // get neighbor for phi - 7 - //........................................................................ - nn = ijk+strideY+1; // neighbor index (get convention) - m7 = Phi[nn]; // get neighbor for phi - 8 - dirGradC7 = 0.5*(m7-m8); - dirGradC8 = 0.5*(m8-m7); - //........................................................................ - nn = ijk+strideY-1; // neighbor index (get convention) - m10 = Phi[nn]; // get neighbor for phi - 9 - //........................................................................ - nn = ijk-strideY+1; // neighbor index (get convention) - m9 = Phi[nn]; // get neighbor for phi - 10 - dirGradC9 = 0.5*(m9-m10); - dirGradC10 = 0.5*(m10-m9); - //........................................................................ - nn = ijk-strideZ-1; // neighbor index (get convention) - m12 = Phi[nn]; // get neighbor for phi - 11 - //........................................................................ - nn = ijk+strideZ+1; // neighbor index (get convention) - m11 = Phi[nn]; // get neighbor for phi - 12 - dirGradC11 = 0.5*(m11-m12); - dirGradC12 = 0.5*(m12-m11); - //........................................................................ - nn = ijk+strideZ-1; // neighbor index (get convention) - m14 = Phi[nn]; // get neighbor for phi - 13 - //........................................................................ - nn = ijk-strideZ+1; // neighbor index (get convention) - m13 = Phi[nn]; // get neighbor for phi - 14 - dirGradC13 = 0.5*(m13-m14); - dirGradC14 = 0.5*(m14-m13); - //........................................................................ - nn = ijk-strideZ-strideY; // neighbor index (get convention) - m16 = Phi[nn]; // get neighbor for phi - 15 - //........................................................................ - nn = ijk+strideZ+strideY; // neighbor index (get convention) - m15 = Phi[nn]; // get neighbor for phi - 16 - dirGradC15 = 0.5*(m15-m16); - dirGradC16 = 0.5*(m16-m15); - //........................................................................ - nn = ijk+strideZ-strideY; // neighbor index (get convention) - m18 = Phi[nn]; // get neighbor for phi - 17 - //........................................................................ - nn = ijk-strideZ+strideY; // neighbor index (get convention) - m17 = Phi[nn]; // get neighbor for phi - 18 - dirGradC17 = 0.5*(m17-m18); - dirGradC18 = 0.5*(m18-m17); - - // compute mixed difference (Eq.30, A.Fukhari et al. JCP 315(2016) 434-457) - //........................................................................ - nn2x = ijk+2; // neighbor index (get convention) - dirGradM1 = Phi[nn2x]; // get neighbor for phi - 1 - dirGradM1 = 0.25*(-dirGradM1+5.0*m1-3.0*phi-m2); - //........................................................................ - nn2x = ijk-2; // neighbor index (get convention) - dirGradM2 = Phi[nn2x]; // get neighbor for phi - 2 - dirGradM2 = 0.25*(-dirGradM2+5.0*m2-3.0*phi-m1); - //........................................................................ - nn2x = ijk+strideY*2; // neighbor index (get convention) - dirGradM3 = Phi[nn2x]; // get neighbor for phi - 3 - dirGradM3 = 0.25*(-dirGradM3+5.0*m3-3.0*phi-m4); - //........................................................................ - nn2x = ijk-strideY*2; // neighbor index (get convention) - dirGradM4 = Phi[nn2x]; // get neighbor for phi - 4 - dirGradM4 = 0.25*(-dirGradM4+5.0*m4-3.0*phi-m3); - //........................................................................ - nn2x = ijk+strideZ*2; // neighbor index (get convention) - dirGradM5 = Phi[nn2x]; // get neighbor for phi - 5 - dirGradM5 = 0.25*(-dirGradM5+5.0*m5-3.0*phi-m6); - //........................................................................ - nn2x = ijk-strideZ*2; // neighbor index (get convention) - dirGradM6 = Phi[nn2x]; // get neighbor for phi - 6 - dirGradM6 = 0.25*(-dirGradM6+5.0*m6-3.0*phi-m5); - //........................................................................ - nn2x = ijk+strideY*2+2; // neighbor index (get convention) - dirGradM7 = Phi[nn2x]; // get neighbor for phi - 7 - dirGradM7 = 0.25*(-dirGradM7+5.0*m7-3.0*phi-m8); - //........................................................................ - nn2x = ijk-strideY*2-2; // neighbor index (get convention) - dirGradM8 = Phi[nn2x]; // get neighbor for phi - 8 - dirGradM8 = 0.25*(-dirGradM8+5.0*m8-3.0*phi-m7); - //........................................................................ - nn2x = ijk-strideY*2+2; // neighbor index (get convention) - dirGradM9 = Phi[nn2x]; // get neighbor for phi - 9 - dirGradM9 = 0.25*(-dirGradM9+5.0*m9-3.0*phi-m10); - //........................................................................ - nn2x = ijk+strideY*2-2; // neighbor index (get convention) - dirGradM10 = Phi[nn2x]; // get neighbor for phi - 10 - dirGradM10 = 0.25*(-dirGradM10+5.0*m10-3.0*phi-m9); - //........................................................................ - nn2x = ijk+strideZ*2+2; // neighbor index (get convention) - dirGradM11 = Phi[nn2x]; // get neighbor for phi - 11 - dirGradM11 = 0.25*(-dirGradM11+5.0*m11-3.0*phi-m12); - //........................................................................ - nn2x = ijk-strideZ*2-2; // neighbor index (get convention) - dirGradM12 = Phi[nn2x]; // get neighbor for phi - 12 - dirGradM12 = 0.25*(-dirGradM12+5.0*m12-3.0*phi-m11); - //........................................................................ - nn2x = ijk-strideZ*2+2; // neighbor index (get convention) - dirGradM13 = Phi[nn2x]; // get neighbor for phi - 13 - dirGradM13 = 0.25*(-dirGradM13+5.0*m13-3.0*phi-m14); - //........................................................................ - nn2x = ijk+strideZ*2-2; // neighbor index (get convention) - dirGradM14 = Phi[nn2x]; // get neighbor for phi - 14 - dirGradM14 = 0.25*(-dirGradM14+5.0*m14-3.0*phi-m13); - //........................................................................ - nn2x = ijk+strideZ*2+strideY*2; // neighbor index (get convention) - dirGradM15 = Phi[nn2x]; // get neighbor for phi - 15 - dirGradM15 = 0.25*(-dirGradM15+5.0*m15-3.0*phi-m16); - //........................................................................ - nn2x = ijk-strideZ*2-strideY*2; // neighbor index (get convention) - dirGradM16 = Phi[nn2x]; // get neighbor for phi - 16 - dirGradM16 = 0.25*(-dirGradM16+5.0*m16-3.0*phi-m15); - //........................................................................ - nn2x = ijk-strideZ*2+strideY*2; // neighbor index (get convention) - dirGradM17 = Phi[nn2x]; // get neighbor for phi - 17 - dirGradM17 = 0.25*(-dirGradM17+5.0*m17-3.0*phi-m18); - //........................................................................ - nn2x = ijk+strideZ*2-strideY*2; // neighbor index (get convention) - dirGradM18 = Phi[nn2x]; // get neighbor for phi - 18 - dirGradM18 = 0.25*(-dirGradM18+5.0*m18-3.0*phi-m17); - - - //............Compute the Color Gradient................................... - nx = 3.0*1.0/18.0*(dirGradC1-dirGradC2+0.5*(dirGradC7-dirGradC8+dirGradC9-dirGradC10+dirGradC11-dirGradC12+dirGradC13-dirGradC14)); - ny = 3.0*1.0/18.0*(dirGradC3-dirGradC4+0.5*(dirGradC7-dirGradC8-dirGradC9+dirGradC10+dirGradC15-dirGradC16+dirGradC17-dirGradC18)); - nz = 3.0*1.0/18.0*(dirGradC5-dirGradC6+0.5*(dirGradC11-dirGradC12-dirGradC13+dirGradC14+dirGradC15-dirGradC16-dirGradC17+dirGradC18)); - //............Compute the Chemical Potential............................... - //chem = 2.0*3.0/18.0*(m1+m2+m3+m4+m5+m6-6*phi+0.5*(m7+m8+m9+m10+m11+m12+m13+m14+m15+m16+m17+m18-12*phi));//intermediate var, i.e. the laplacian - //chem = 4.0*beta*phi*(phi+1.0)*(phi-1.0)-kappa*chem; - chem = 2.0*3.0/18.0*(m1+m2+m3+m4+m5+m6-6*phi_temp+0.5*(m7+m8+m9+m10+m11+m12+m13+m14+m15+m16+m17+m18-12*phi_temp));//intermediate var, i.e. the laplacian - chem = 4.0*beta*phi_temp*(phi_temp+1.0)*(phi_temp-1.0)-kappa*chem; - //............Compute the Mixed Gradient................................... - mgx = 3.0*1.0/18.0*(dirGradM1-dirGradM2+0.5*(dirGradM7-dirGradM8+dirGradM9-dirGradM10+dirGradM11-dirGradM12+dirGradM13-dirGradM14)); - mgy = 3.0*1.0/18.0*(dirGradM3-dirGradM4+0.5*(dirGradM7-dirGradM8-dirGradM9+dirGradM10+dirGradM15-dirGradM16+dirGradM17-dirGradM18)); - mgz = 3.0*1.0/18.0*(dirGradM5-dirGradM6+0.5*(dirGradM11-dirGradM12-dirGradM13+dirGradM14+dirGradM15-dirGradM16-dirGradM17+dirGradM18)); - - - //de-noise color gradient and mixed gradient - C = sqrt(nx*nx+ny*ny+nz*nz); - if (C<1.0e-12) nx=ny=nz=0.0; - double mg_mag = sqrt(mgx*mgx+mgy*mgy+mgz*mgz); - if (mg_mag<1.0e-12) mgx=mgy=mgz=0.0; - //maybe you can also de-noise chemical potential ? within the bulk phase chem should be ZERO - if (fabs(chem)<1.0e-12) chem=0.0; - - // q=0 - m0 = dist[n]; - // q=1 - m1 = dist[2*Np+n]; - - // q=2 - m2 = dist[1*Np+n]; - - // q=3 - m3 = dist[4*Np+n]; - - // q = 4 - m4 = dist[3*Np+n]; - - // q=5 - m5 = dist[6*Np+n]; - - // q = 6 - m6 = dist[5*Np+n]; - - // q=7 - m7 = dist[8*Np+n]; - - // q = 8 - m8 = dist[7*Np+n]; - - // q=9 - m9 = dist[10*Np+n]; - - // q = 10 - m10 = dist[9*Np+n]; - - // q=11 - m11 = dist[12*Np+n]; - - // q=12 - m12 = dist[11*Np+n]; - - // q=13 - m13 = dist[14*Np+n]; - - // q=14 - m14 = dist[13*Np+n]; - - // q=15 - m15 = dist[16*Np+n]; - - // q=16 - m16 = dist[15*Np+n]; - - // q=17 - m17 = dist[18*Np+n]; - - // q=18 - m18 = dist[17*Np+n]; - - //compute fluid velocity - ux = 3.0/rho0*(m1-m2+m7-m8+m9-m10+m11-m12+m13-m14+0.5*(chem*nx+Fx)/1.0); - uy = 3.0/rho0*(m3-m4+m7-m8-m9+m10+m15-m16+m17-m18+0.5*(chem*ny+Fy)/1.0); - uz = 3.0/rho0*(m5-m6+m11-m12-m13+m14+m15-m16-m17+m18+0.5*(chem*nz+Fz)/1.0); - //compute pressure - p = (m0+m2+m1+m4+m3+m6+m5+m8+m7+m10+m9+m12+m11+m14+m13+m16+m15+m18+m17) - +0.5*(rhoA-rhoB)/2.0/3.0*(ux*nx+uy*ny+uz*nz); - - //compute equilibrium distributions - feq0 = 0.3333333333333333*p - 0.25*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) - 0.16666666666666666*rho0*(ux*ux + uy*uy + uz*uz) - - 0.5*(-(nx*ux) - ny*uy - nz*uz)*(-0.08333333333333333*(rhoA - rhoB)*(ux*ux + uy*uy + uz*uz) + chem*(0.3333333333333333 - 0.5*(ux*ux + uy*uy + uz*uz))); - feq1 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-(ux*ux) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) - - 0.125*(Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(ux*ux) + 0.3333333333333333*(-2.*ux + ux*ux + uy*uy + uz*uz)) - - 0.0625*(dirGradC1 - nx*ux - ny*uy - nz*uz)*(2*chem*(ux*ux) - 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*uz))); - feq2 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-(ux*ux) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) - - 0.125*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(ux*ux) + 0.3333333333333333*(2.*ux + ux*ux + uy*uy + uz*uz)) - - 0.0625*(dirGradC2 - nx*ux - ny*uy - nz*uz)*(2*chem*(ux*ux) - 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(2*ux + ux*ux + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*uz))); - feq3 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-(uy*uy) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) - - 0.125*(Fx*ux + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*(uy*uy) + 0.3333333333333333*(ux*ux - 2.*uy + uy*uy + uz*uz)) - - 0.0625*(dirGradC3 - nx*ux - ny*uy - nz*uz)*(2*chem*(uy*uy) - 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*uz))); - feq4 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-(uy*uy) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) - - 0.125*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(uy*uy) + 0.3333333333333333*(ux*ux + 2.*uy + uy*uy + uz*uz)) - - 0.0625*(dirGradC4 - nx*ux - ny*uy - nz*uz)*(2*chem*(uy*uy) - 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*uz))); - feq5 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-(uz*uz) + 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) - - 0.125*(Fx*ux + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*(uz*uz) + 0.3333333333333333*(ux*ux + uy*uy + (-2. + uz)*uz)) - - 0.0625*(dirGradC5 - nx*ux - ny*uy - nz*uz)*(2*chem*(uz*uz) - 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux + uy*uy + (-2 + uz)*uz))); - feq6 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-(uz*uz) + 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) - - 0.125*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(uz*uz) + 0.3333333333333333*(ux*ux + uy*uy + uz*(2. + uz))) - - 0.0625*(dirGradC6 - nx*ux - ny*uy - nz*uz)*(2*chem*(uz*uz) - 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux + uy*uy + uz*(2 + uz)))); - feq7 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((ux + uy)*(ux + uy)) + 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) - - 0.0625*(Fx*(-1. + ux) + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*(ux*ux) - 2.*ux*uy - 1.*(uy*uy) + - 0.3333333333333333*(-2.*ux + ux*ux - 2.*uy + uy*uy + uz*uz)) - - 0.03125*(dirGradC7 - nx*ux - ny*uy - nz*uz)*(2*chem*((ux + uy)*(ux + uy)) + - 0.3333333333333333*((rhoA - rhoB)*((ux + uy)*(ux + uy)) - 2*chem*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz))); - feq8 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((ux + uy)*(ux + uy)) + 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - - 0.0625*(Fx + Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(ux*ux) - 2.*ux*uy - 1.*(uy*uy) + - 0.3333333333333333*(2.*ux + ux*ux + 2.*uy + uy*uy + uz*uz)) - - 0.03125*(dirGradC8 - nx*ux - ny*uy - nz*uz)*(2*chem*((ux + uy)*(ux + uy)) - - 0.3333333333333333*(-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz))); - feq9 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((ux - uy)*(ux - uy)) + 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - - 0.0625*(Fy + Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(ux*ux) + 2.*ux*uy - 1.*(uy*uy) + - 0.3333333333333333*(-2.*ux + ux*ux + 2.*uy + uy*uy + uz*uz)) - - 0.03125*(dirGradC9 - nx*ux - ny*uy - nz*uz)*(2*chem*((ux - uy)*(ux - uy)) - - 0.3333333333333333*(-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz))); - feq10 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((ux - uy)*(ux - uy)) + 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) - - 0.0625*(Fx*(1 + ux) + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*(ux*ux) + 2.*ux*uy - 1.*(uy*uy) + - 0.3333333333333333*(2.*ux + ux*ux - 2.*uy + uy*uy + uz*uz)) - - 0.03125*(dirGradC10 - nx*ux - ny*uy - nz*uz)*(2*chem*((ux - uy)*(ux - uy)) - - 0.3333333333333333*(-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz))); - feq11 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((ux + uz)*(ux + uz)) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) - - 0.0625*(Fx*(-1. + ux) + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*(ux*ux) - 2.*ux*uz - 1.*(uz*uz) + - 0.3333333333333333*(-2.*ux + ux*ux + uy*uy + (-2. + uz)*uz)) - - 0.03125*(dirGradC11 - nx*ux - ny*uy - nz*uz)*(2*chem*((ux + uz)*(ux + uz)) + - 0.3333333333333333*((rhoA - rhoB)*((ux + uz)*(ux + uz)) - 2*chem*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz))); - feq12 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((ux + uz)*(ux + uz)) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) - - 0.0625*(Fx + Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(ux*ux) - 2.*ux*uz - 1.*(uz*uz) + - 0.3333333333333333*(2.*ux + ux*ux + uy*uy + uz*(2. + uz))) - - 0.03125*(dirGradC12 - nx*ux - ny*uy - nz*uz)*(2*chem*((ux + uz)*(ux + uz)) - - 0.3333333333333333*(-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*(2 + uz)))); - feq13 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((ux - uz)*(ux - uz)) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) - - 0.0625*(Fz + Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(ux*ux) + 2.*ux*uz - 1.*(uz*uz) + - 0.3333333333333333*(-2.*ux + ux*ux + uy*uy + uz*(2. + uz))) - - 0.03125*(dirGradC13 - nx*ux - ny*uy - nz*uz)*(2*chem*((ux - uz)*(ux - uz)) - - 0.3333333333333333*(-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*(2 + uz)))); - feq14 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((ux - uz)*(ux - uz)) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) - - 0.0625*(Fx*(1 + ux) + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*(ux*ux) + 2.*ux*uz - 1.*(uz*uz) + - 0.3333333333333333*(2.*ux + ux*ux + uy*uy + (-2. + uz)*uz)) - - 0.03125*(dirGradC14 - nx*ux - ny*uy - nz*uz)*(2*chem*((ux - uz)*(ux - uz)) - - 0.3333333333333333*(-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz))); - feq15 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((uy + uz)*(uy + uz)) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) - - 0.0625*(Fx*ux + Fy*(-1. + uy) + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*(uy*uy) - 2.*uy*uz - 1.*(uz*uz) + - 0.3333333333333333*(ux*ux - 2.*uy + uy*uy + (-2. + uz)*uz)) - - 0.03125*(dirGradC15 - nx*ux - ny*uy - nz*uz)*(2*chem*((uy + uz)*(uy + uz)) + - 0.3333333333333333*((rhoA - rhoB)*((uy + uz)*(uy + uz)) - 2*chem*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz))); - feq16 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((uy + uz)*(uy + uz)) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) - - 0.0625*(Fy + Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(uy*uy) - 2.*uy*uz - 1.*(uz*uz) + - 0.3333333333333333*(ux*ux + 2.*uy + uy*uy + uz*(2. + uz))) - - 0.03125*(dirGradC16 - nx*ux - ny*uy - nz*uz)*(2*chem*((uy + uz)*(uy + uz)) - - 0.3333333333333333*(-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*(2 + uz)))); - feq17 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((uy - uz)*(uy - uz)) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) - - 0.0625*(Fz + Fx*ux + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*(uy*uy) + 2.*uy*uz - 1.*(uz*uz) + - 0.3333333333333333*(ux*ux - 2.*uy + uy*uy + uz*(2. + uz))) - - 0.03125*(dirGradC17 - nx*ux - ny*uy - nz*uz)*(2*chem*((uy - uz)*(uy - uz)) - - 0.3333333333333333*(-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*(2 + uz)))); - feq18 = 0.027777777777777776*p - 0.041666666666666664*rho0*(-((uy - uz)*(uy - uz)) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) - - 0.0625*(Fx*ux + Fy*(1 + uy) + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*(uy*uy) + 2.*uy*uz - 1.*(uz*uz) + - 0.3333333333333333*(ux*ux + 2.*uy + uy*uy + (-2. + uz)*uz)) - - 0.03125*(dirGradC18 - nx*ux - ny*uy - nz*uz)*(2*chem*((uy - uz)*(uy - uz)) - - 0.3333333333333333*(-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz))); - - //------------------------------------------------- BCK collison ------------------------------------------------------------// - // q=0 - dist[n] = m0 - (m0-feq0)/tau + 0.25*(2*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) + - (mgx*ux + mgy*uy + mgz*uz)*(2*chem*(ux*ux + uy*uy + uz*uz) + 0.3333333333333333*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + uz*uz)))); - - // q = 1 - dist[1*Np+n] = m1 - (m1-feq1)/tau + 0.125*(2*(Fx*(-1 + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) - - (dirGradM1 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*(ux*ux) + 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*uz)))); - - // q=2 - dist[2*Np+n] = m2 - (m2-feq2)/tau + 0.125*(2*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) - - (dirGradM2 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*(ux*ux) + 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(2*ux + ux*ux + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*uz)))); - - // q = 3 - dist[3*Np+n] = m3 - (m3-feq3)/tau + 0.125*(2*(Fx*ux + Fy*(-1 + uy) + Fz*uz)*(-0.2222222222222222 - uy*uy + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) - - (dirGradM3 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*(uy*uy) + 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*uz)))); - - // q = 4 - dist[4*Np+n] = m4 - (m4-feq4)/tau + 0.125*(2*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uy*uy + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) - - (dirGradM4 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*(uy*uy) + 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*uz)))); - - // q = 5 - dist[5*Np+n] = m5 - (m5-feq5)/tau + 0.125*(2*(Fx*ux + Fy*uy + Fz*(-1 + uz))*(-0.2222222222222222 - uz*uz + 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) - - (dirGradM5 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*(uz*uz) + 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + (-2 + uz)*uz)))); - - // q = 6 - dist[6*Np+n] = m6 - (m6-feq6)/tau + 0.125*(2*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uz*uz + 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) - - (dirGradM6 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*(uz*uz) + 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + uz*(2 + uz))))); - - // q = 7 - dist[7*Np+n] = m7 - (m7-feq7)/tau + 0.0625*(-2*(Fx*(-1 + ux) + Fy*(-1 + uy) + Fz*uz)*(0.2222222222222222 + (ux + uy)*(ux + uy) - 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) - - (dirGradM7 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((ux + uy)*(ux + uy)) + - 0.3333333333333333*(-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)))); - - // q = 8 - dist[8*Np+n] = m8 - (m8-feq8)/tau + 0.0625*(2*(Fx + Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - (ux + uy)*(ux + uy) + 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - - (dirGradM8 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((ux + uy)*(ux + uy)) + - 0.3333333333333333*(-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)))); - - // q = 9 - dist[9*Np+n] = m9 - (m9-feq9)/tau + 0.0625*(2*(Fy + Fx*(-1 + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - (ux - uy)*(ux - uy) + 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - - (dirGradM9 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((ux - uy)*(ux - uy)) + - 0.3333333333333333*(-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)))); - - // q = 10 - dist[10*Np+n] = m10 - (m10-feq10)/tau + 0.0625*(2*(Fx*(1 + ux) + Fy*(-1 + uy) + Fz*uz)*(-0.2222222222222222 - (ux - uy)*(ux - uy) + 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) - - (dirGradM10 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((ux - uy)*(ux - uy)) + - 0.3333333333333333*(-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)))); - - // q = 11 - dist[11*Np+n] = m11 - (m11-feq11)/tau + 0.0625*(-2*(Fx*(-1 + ux) + Fy*uy + Fz*(-1 + uz))*(0.2222222222222222 + (ux + uz)*(ux + uz) - 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) - - (dirGradM11 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((ux + uz)*(ux + uz)) + - 0.3333333333333333*(-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)))); - - // q = 12 - dist[12*Np+n] = m12 - (m12-feq12)/tau + 0.0625*(2*(Fx + Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - (ux + uz)*(ux + uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) - - (dirGradM12 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((ux + uz)*(ux + uz)) + - 0.3333333333333333*(-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*(2 + uz))))); - - // q = 13 - dist[13*Np+n] = m13 - (m13-feq13)/tau + 0.0625*(2*(Fz + Fx*(-1 + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - (ux - uz)*(ux - uz) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) - - (dirGradM13 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((ux - uz)*(ux - uz)) + - 0.3333333333333333*(-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))))); - - // q= 14 - dist[14*Np+n] = m14 - (m14-feq14)/tau + 0.0625*(2*(Fx*(1 + ux) + Fy*uy + Fz*(-1 + uz))*(-0.2222222222222222 - (ux - uz)*(ux - uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) - - (dirGradM14 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((ux - uz)*(ux - uz)) + - 0.3333333333333333*(-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)))); - - // q = 15 - dist[15*Np+n] = m15 - (m15-feq15)/tau + 0.0625*(-2*(Fx*ux + Fy*(-1 + uy) + Fz*(-1 + uz))*(0.2222222222222222 + (uy + uz)*(uy + uz) - 0.3333333333333333*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) - - (dirGradM15 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((uy + uz)*(uy + uz)) + - 0.3333333333333333*(-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)))); - - // q = 16 - dist[16*Np+n] = m16 - (m16-feq16)/tau + 0.0625*(2*(Fy + Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - (uy + uz)*(uy + uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) - - (dirGradM16 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((uy + uz)*(uy + uz)) + - 0.3333333333333333*(-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))))); - - // q = 17 - dist[17*Np+n] = m17 - (m17-feq17)/tau + 0.0625*(2*(Fz + Fx*ux + Fy*(-1 + uy) + Fz*uz)*(-0.2222222222222222 - (uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) - - (dirGradM17 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((uy - uz)*(uy - uz)) + - 0.3333333333333333*(-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))))); - - // q = 18 - dist[18*Np+n] = m18 - (m18-feq18)/tau + 0.0625*(2*(Fx*ux + Fy*(1 + uy) + Fz*(-1 + uz))*(-0.2222222222222222 - (uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) - - (dirGradM18 - mgx*ux - mgy*uy - mgz*uz)*(-2*chem*((uy - uz)*(uy - uz)) + - 0.3333333333333333*(-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + - 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)))); - //----------------------------------------------------------------------------------------------------------------------------------------// - - - //Update color gradient on device - ColorGrad[0*Np+n] = nx; - ColorGrad[1*Np+n] = ny; - ColorGrad[2*Np+n] = nz; - - // ----------------------------- compute phase field evolution ---------------------------------------- - //Normalize the Color Gradient - C = sqrt(nx*nx+ny*ny+nz*nz); - if (C<1.0e-14) nx = ny = nz = 0.0; - //compute surface tension-related parameter - //theta = 4.5*M*2.0*(1-phi*phi)/W; - theta = 4.5*M*2.0*(1-phi_temp*phi_temp)/W; - - //load distributions of phase field - //q=0 - h0 = hq[n]; - //q=1 - h1 = hq[2*Np+n]; - - //q=2 - h2 = hq[1*Np+n]; - - //q=3 - h3 = hq[4*Np+n]; - - //q=4 - h4 = hq[3*Np+n]; - - //q=5 - h5 = hq[6*Np+n]; - - //q=6 - h6 = hq[5*Np+n]; - - //-------------------------------- BGK collison for phase field ---------------------------------// - // q = 0 - hq[n] = h0 - (h0 - 0.3333333333333333*phi)/tauM; - - // q = 1 - hq[1*Np+n] = h1 - (h1 - 0.1111111111111111*nx*theta - phi*(0.1111111111111111 + 0.5*ux))/tauM; - - // q = 2 - hq[2*Np+n] = h2 - (h2 + 0.1111111111111111*nx*theta - phi*(0.1111111111111111 - 0.5*ux))/tauM; - - // q = 3 - hq[3*Np+n] = h3 - (h3 - 0.1111111111111111*ny*theta - phi*(0.1111111111111111 + 0.5*uy))/tauM; - - // q = 4 - hq[4*Np+n] = h4 - (h4 + 0.1111111111111111*ny*theta - phi*(0.1111111111111111 - 0.5*uy))/tauM; - - // q = 5 - hq[5*Np+n] = h5 - (h5 - 0.1111111111111111*nz*theta - phi*(0.1111111111111111 + 0.5*uz))/tauM; - - // q = 6 - hq[6*Np+n] = h6 - (h6 + 0.1111111111111111*nz*theta - phi*(0.1111111111111111 - 0.5*uz))/tauM; - //........................................................................ - - //Update velocity on device - Vel[0*Np+n] = ux; - Vel[1*Np+n] = uy; - Vel[2*Np+n] = uz; - //Update pressure on device - Pressure[n] = p; - //Update chemical potential on device - mu_phi[n] = chem; - - } +extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined( + int *Map, double *dist, double *hq, double *Den, double *Phi, + double *mu_phi, double *Vel, double *Pressure, double *ColorGrad, + double rhoA, double rhoB, double tauA, double tauB, double tauM, + double kappa, double beta, double W, double Fx, double Fy, double Fz, + int strideY, int strideZ, int start, int finish, int Np) { + + int nn, nn2x, ijk; + double ux, uy, uz; //fluid velocity + double p; //pressure + double chem; //chemical potential + double phi; //phase field + double rho0; //fluid density + // distribution functions + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; + double m0, m3, m5, m7; + //double mm1,mm2,mm4,mm6,mm8,mm9,mm10,mm11,mm12,mm13,mm14,mm15,mm16,mm17,mm18; + //double mm3,mm5,mm7; + double feq0, feq1, feq2, feq3, feq4, feq5, feq6, feq7, feq8, feq9, feq10, + feq11, feq12, feq13, feq14, feq15, feq16, feq17, feq18; + double nx, ny, nz; //normal color gradient + double mgx, mgy, mgz; //mixed gradient reaching secondary neighbor + double dirGradC1, dirGradC2, dirGradC3, dirGradC4, dirGradC5, dirGradC6, + dirGradC7, dirGradC8, dirGradC9, dirGradC10, dirGradC11, dirGradC12; + double dirGradC13, dirGradC14, dirGradC15, dirGradC16, dirGradC17, + dirGradC18; + double dirGradM1, dirGradM2, dirGradM3, dirGradM4, dirGradM5, dirGradM6, + dirGradM7, dirGradM8, dirGradM9, dirGradM10, dirGradM11, dirGradM12; + double dirGradM13, dirGradM14, dirGradM15, dirGradM16, dirGradM17, + dirGradM18; + + double h0, h1, h2, h3, h4, h5, h6; //distributions for LB phase field + double tau; //position dependent LB relaxation time for fluid + double C, theta; + double M = + 2.0 / 9.0 * + (tauM - 0.5); //diffusivity (or mobility) for the phase field D3Q7 + double phi_temp; + + for (int n = start; n < finish; n++) { + + rho0 = Den[n]; //load density + + // Get the 1D index based on regular data layout + ijk = Map[n]; + phi = Phi[ijk]; // load phase field + phi_temp = phi; + if (phi > 1.f) + phi_temp = 1.0; + if (phi < -1.f) + phi_temp = -1.0; + + // local relaxation time + tau = tauA + 0.5 * (1.0 - phi) * (tauB - tauA); + + // COMPUTE THE COLOR GRADIENT + //........................................................................ + //.................Read Phase Indicator Values............................ + //........................................................................ + nn = ijk - 1; // neighbor index (get convention) + m2 = Phi[nn]; // get neighbor for phi - 1 + //........................................................................ + nn = ijk + 1; // neighbor index (get convention) + m1 = Phi[nn]; // get neighbor for phi - 2 + dirGradC1 = 0.5 * (m1 - m2); + dirGradC2 = 0.5 * (m2 - m1); + //........................................................................ + nn = ijk - strideY; // neighbor index (get convention) + m4 = Phi[nn]; // get neighbor for phi - 3 + //........................................................................ + nn = ijk + strideY; // neighbor index (get convention) + m3 = Phi[nn]; // get neighbor for phi - 4 + dirGradC3 = 0.5 * (m3 - m4); + dirGradC4 = 0.5 * (m4 - m3); + //........................................................................ + nn = ijk - strideZ; // neighbor index (get convention) + m6 = Phi[nn]; // get neighbor for phi - 5 + //........................................................................ + nn = ijk + strideZ; // neighbor index (get convention) + m5 = Phi[nn]; // get neighbor for phi - 6 + dirGradC5 = 0.5 * (m5 - m6); + dirGradC6 = 0.5 * (m6 - m5); + //........................................................................ + nn = ijk - strideY - 1; // neighbor index (get convention) + m8 = Phi[nn]; // get neighbor for phi - 7 + //........................................................................ + nn = ijk + strideY + 1; // neighbor index (get convention) + m7 = Phi[nn]; // get neighbor for phi - 8 + dirGradC7 = 0.5 * (m7 - m8); + dirGradC8 = 0.5 * (m8 - m7); + //........................................................................ + nn = ijk + strideY - 1; // neighbor index (get convention) + m10 = Phi[nn]; // get neighbor for phi - 9 + //........................................................................ + nn = ijk - strideY + 1; // neighbor index (get convention) + m9 = Phi[nn]; // get neighbor for phi - 10 + dirGradC9 = 0.5 * (m9 - m10); + dirGradC10 = 0.5 * (m10 - m9); + //........................................................................ + nn = ijk - strideZ - 1; // neighbor index (get convention) + m12 = Phi[nn]; // get neighbor for phi - 11 + //........................................................................ + nn = ijk + strideZ + 1; // neighbor index (get convention) + m11 = Phi[nn]; // get neighbor for phi - 12 + dirGradC11 = 0.5 * (m11 - m12); + dirGradC12 = 0.5 * (m12 - m11); + //........................................................................ + nn = ijk + strideZ - 1; // neighbor index (get convention) + m14 = Phi[nn]; // get neighbor for phi - 13 + //........................................................................ + nn = ijk - strideZ + 1; // neighbor index (get convention) + m13 = Phi[nn]; // get neighbor for phi - 14 + dirGradC13 = 0.5 * (m13 - m14); + dirGradC14 = 0.5 * (m14 - m13); + //........................................................................ + nn = ijk - strideZ - strideY; // neighbor index (get convention) + m16 = Phi[nn]; // get neighbor for phi - 15 + //........................................................................ + nn = ijk + strideZ + strideY; // neighbor index (get convention) + m15 = Phi[nn]; // get neighbor for phi - 16 + dirGradC15 = 0.5 * (m15 - m16); + dirGradC16 = 0.5 * (m16 - m15); + //........................................................................ + nn = ijk + strideZ - strideY; // neighbor index (get convention) + m18 = Phi[nn]; // get neighbor for phi - 17 + //........................................................................ + nn = ijk - strideZ + strideY; // neighbor index (get convention) + m17 = Phi[nn]; // get neighbor for phi - 18 + dirGradC17 = 0.5 * (m17 - m18); + dirGradC18 = 0.5 * (m18 - m17); + + // compute mixed difference (Eq.30, A.Fukhari et al. JCP 315(2016) 434-457) + //........................................................................ + nn2x = ijk + 2; // neighbor index (get convention) + dirGradM1 = Phi[nn2x]; // get neighbor for phi - 1 + dirGradM1 = 0.25 * (-dirGradM1 + 5.0 * m1 - 3.0 * phi - m2); + //........................................................................ + nn2x = ijk - 2; // neighbor index (get convention) + dirGradM2 = Phi[nn2x]; // get neighbor for phi - 2 + dirGradM2 = 0.25 * (-dirGradM2 + 5.0 * m2 - 3.0 * phi - m1); + //........................................................................ + nn2x = ijk + strideY * 2; // neighbor index (get convention) + dirGradM3 = Phi[nn2x]; // get neighbor for phi - 3 + dirGradM3 = 0.25 * (-dirGradM3 + 5.0 * m3 - 3.0 * phi - m4); + //........................................................................ + nn2x = ijk - strideY * 2; // neighbor index (get convention) + dirGradM4 = Phi[nn2x]; // get neighbor for phi - 4 + dirGradM4 = 0.25 * (-dirGradM4 + 5.0 * m4 - 3.0 * phi - m3); + //........................................................................ + nn2x = ijk + strideZ * 2; // neighbor index (get convention) + dirGradM5 = Phi[nn2x]; // get neighbor for phi - 5 + dirGradM5 = 0.25 * (-dirGradM5 + 5.0 * m5 - 3.0 * phi - m6); + //........................................................................ + nn2x = ijk - strideZ * 2; // neighbor index (get convention) + dirGradM6 = Phi[nn2x]; // get neighbor for phi - 6 + dirGradM6 = 0.25 * (-dirGradM6 + 5.0 * m6 - 3.0 * phi - m5); + //........................................................................ + nn2x = ijk + strideY * 2 + 2; // neighbor index (get convention) + dirGradM7 = Phi[nn2x]; // get neighbor for phi - 7 + dirGradM7 = 0.25 * (-dirGradM7 + 5.0 * m7 - 3.0 * phi - m8); + //........................................................................ + nn2x = ijk - strideY * 2 - 2; // neighbor index (get convention) + dirGradM8 = Phi[nn2x]; // get neighbor for phi - 8 + dirGradM8 = 0.25 * (-dirGradM8 + 5.0 * m8 - 3.0 * phi - m7); + //........................................................................ + nn2x = ijk - strideY * 2 + 2; // neighbor index (get convention) + dirGradM9 = Phi[nn2x]; // get neighbor for phi - 9 + dirGradM9 = 0.25 * (-dirGradM9 + 5.0 * m9 - 3.0 * phi - m10); + //........................................................................ + nn2x = ijk + strideY * 2 - 2; // neighbor index (get convention) + dirGradM10 = Phi[nn2x]; // get neighbor for phi - 10 + dirGradM10 = 0.25 * (-dirGradM10 + 5.0 * m10 - 3.0 * phi - m9); + //........................................................................ + nn2x = ijk + strideZ * 2 + 2; // neighbor index (get convention) + dirGradM11 = Phi[nn2x]; // get neighbor for phi - 11 + dirGradM11 = 0.25 * (-dirGradM11 + 5.0 * m11 - 3.0 * phi - m12); + //........................................................................ + nn2x = ijk - strideZ * 2 - 2; // neighbor index (get convention) + dirGradM12 = Phi[nn2x]; // get neighbor for phi - 12 + dirGradM12 = 0.25 * (-dirGradM12 + 5.0 * m12 - 3.0 * phi - m11); + //........................................................................ + nn2x = ijk - strideZ * 2 + 2; // neighbor index (get convention) + dirGradM13 = Phi[nn2x]; // get neighbor for phi - 13 + dirGradM13 = 0.25 * (-dirGradM13 + 5.0 * m13 - 3.0 * phi - m14); + //........................................................................ + nn2x = ijk + strideZ * 2 - 2; // neighbor index (get convention) + dirGradM14 = Phi[nn2x]; // get neighbor for phi - 14 + dirGradM14 = 0.25 * (-dirGradM14 + 5.0 * m14 - 3.0 * phi - m13); + //........................................................................ + nn2x = + ijk + strideZ * 2 + strideY * 2; // neighbor index (get convention) + dirGradM15 = Phi[nn2x]; // get neighbor for phi - 15 + dirGradM15 = 0.25 * (-dirGradM15 + 5.0 * m15 - 3.0 * phi - m16); + //........................................................................ + nn2x = + ijk - strideZ * 2 - strideY * 2; // neighbor index (get convention) + dirGradM16 = Phi[nn2x]; // get neighbor for phi - 16 + dirGradM16 = 0.25 * (-dirGradM16 + 5.0 * m16 - 3.0 * phi - m15); + //........................................................................ + nn2x = + ijk - strideZ * 2 + strideY * 2; // neighbor index (get convention) + dirGradM17 = Phi[nn2x]; // get neighbor for phi - 17 + dirGradM17 = 0.25 * (-dirGradM17 + 5.0 * m17 - 3.0 * phi - m18); + //........................................................................ + nn2x = + ijk + strideZ * 2 - strideY * 2; // neighbor index (get convention) + dirGradM18 = Phi[nn2x]; // get neighbor for phi - 18 + dirGradM18 = 0.25 * (-dirGradM18 + 5.0 * m18 - 3.0 * phi - m17); + + //............Compute the Color Gradient................................... + nx = 3.0 * 1.0 / 18.0 * + (dirGradC1 - dirGradC2 + + 0.5 * (dirGradC7 - dirGradC8 + dirGradC9 - dirGradC10 + + dirGradC11 - dirGradC12 + dirGradC13 - dirGradC14)); + ny = 3.0 * 1.0 / 18.0 * + (dirGradC3 - dirGradC4 + + 0.5 * (dirGradC7 - dirGradC8 - dirGradC9 + dirGradC10 + + dirGradC15 - dirGradC16 + dirGradC17 - dirGradC18)); + nz = 3.0 * 1.0 / 18.0 * + (dirGradC5 - dirGradC6 + + 0.5 * (dirGradC11 - dirGradC12 - dirGradC13 + dirGradC14 + + dirGradC15 - dirGradC16 - dirGradC17 + dirGradC18)); + //............Compute the Chemical Potential............................... + //chem = 2.0*3.0/18.0*(m1+m2+m3+m4+m5+m6-6*phi+0.5*(m7+m8+m9+m10+m11+m12+m13+m14+m15+m16+m17+m18-12*phi));//intermediate var, i.e. the laplacian + //chem = 4.0*beta*phi*(phi+1.0)*(phi-1.0)-kappa*chem; + chem = 2.0 * 3.0 / 18.0 * + (m1 + m2 + m3 + m4 + m5 + m6 - 6 * phi_temp + + 0.5 * (m7 + m8 + m9 + m10 + m11 + m12 + m13 + m14 + m15 + m16 + + m17 + m18 - + 12 * phi_temp)); //intermediate var, i.e. the laplacian + chem = 4.0 * beta * phi_temp * (phi_temp + 1.0) * (phi_temp - 1.0) - + kappa * chem; + //............Compute the Mixed Gradient................................... + mgx = 3.0 * 1.0 / 18.0 * + (dirGradM1 - dirGradM2 + + 0.5 * (dirGradM7 - dirGradM8 + dirGradM9 - dirGradM10 + + dirGradM11 - dirGradM12 + dirGradM13 - dirGradM14)); + mgy = 3.0 * 1.0 / 18.0 * + (dirGradM3 - dirGradM4 + + 0.5 * (dirGradM7 - dirGradM8 - dirGradM9 + dirGradM10 + + dirGradM15 - dirGradM16 + dirGradM17 - dirGradM18)); + mgz = 3.0 * 1.0 / 18.0 * + (dirGradM5 - dirGradM6 + + 0.5 * (dirGradM11 - dirGradM12 - dirGradM13 + dirGradM14 + + dirGradM15 - dirGradM16 - dirGradM17 + dirGradM18)); + + //de-noise color gradient and mixed gradient + C = sqrt(nx * nx + ny * ny + nz * nz); + if (C < 1.0e-12) + nx = ny = nz = 0.0; + double mg_mag = sqrt(mgx * mgx + mgy * mgy + mgz * mgz); + if (mg_mag < 1.0e-12) + mgx = mgy = mgz = 0.0; + //maybe you can also de-noise chemical potential ? within the bulk phase chem should be ZERO + if (fabs(chem) < 1.0e-12) + chem = 0.0; + + // q=0 + m0 = dist[n]; + // q=1 + m1 = dist[2 * Np + n]; + + // q=2 + m2 = dist[1 * Np + n]; + + // q=3 + m3 = dist[4 * Np + n]; + + // q = 4 + m4 = dist[3 * Np + n]; + + // q=5 + m5 = dist[6 * Np + n]; + + // q = 6 + m6 = dist[5 * Np + n]; + + // q=7 + m7 = dist[8 * Np + n]; + + // q = 8 + m8 = dist[7 * Np + n]; + + // q=9 + m9 = dist[10 * Np + n]; + + // q = 10 + m10 = dist[9 * Np + n]; + + // q=11 + m11 = dist[12 * Np + n]; + + // q=12 + m12 = dist[11 * Np + n]; + + // q=13 + m13 = dist[14 * Np + n]; + + // q=14 + m14 = dist[13 * Np + n]; + + // q=15 + m15 = dist[16 * Np + n]; + + // q=16 + m16 = dist[15 * Np + n]; + + // q=17 + m17 = dist[18 * Np + n]; + + // q=18 + m18 = dist[17 * Np + n]; + + //compute fluid velocity + ux = 3.0 / rho0 * + (m1 - m2 + m7 - m8 + m9 - m10 + m11 - m12 + m13 - m14 + + 0.5 * (chem * nx + Fx) / 1.0); + uy = 3.0 / rho0 * + (m3 - m4 + m7 - m8 - m9 + m10 + m15 - m16 + m17 - m18 + + 0.5 * (chem * ny + Fy) / 1.0); + uz = 3.0 / rho0 * + (m5 - m6 + m11 - m12 - m13 + m14 + m15 - m16 - m17 + m18 + + 0.5 * (chem * nz + Fz) / 1.0); + //compute pressure + p = (m0 + m2 + m1 + m4 + m3 + m6 + m5 + m8 + m7 + m10 + m9 + m12 + m11 + + m14 + m13 + m16 + m15 + m18 + m17) + + 0.5 * (rhoA - rhoB) / 2.0 / 3.0 * (ux * nx + uy * ny + uz * nz); + + //compute equilibrium distributions + feq0 = 0.3333333333333333 * p - + 0.25 * (Fx * ux + Fy * uy + Fz * uz) * + (-0.6666666666666666 + ux * ux + uy * uy + uz * uz) - + 0.16666666666666666 * rho0 * (ux * ux + uy * uy + uz * uz) - + 0.5 * (-(nx * ux) - ny * uy - nz * uz) * + (-0.08333333333333333 * (rhoA - rhoB) * + (ux * ux + uy * uy + uz * uz) + + chem * (0.3333333333333333 - + 0.5 * (ux * ux + uy * uy + uz * uz))); + feq1 = 0.05555555555555555 * p - + 0.08333333333333333 * rho0 * + (-(ux * ux) + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + uz * uz)) - + 0.125 * (Fx * (-1. + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) + + 0.3333333333333333 * + (-2. * ux + ux * ux + uy * uy + uz * uz)) - + 0.0625 * (dirGradC1 - nx * ux - ny * uy - nz * uz) * + (2 * chem * (ux * ux) - + 0.3333333333333333 * + ((-rhoA + rhoB) * (ux * ux) + + 2 * chem * (-2 * ux + ux * ux + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (-2 * ux + ux * ux + + uy * uy + uz * uz))); + feq2 = + 0.05555555555555555 * p - + 0.08333333333333333 * rho0 * + (-(ux * ux) + + 0.3333333333333333 * (2 * ux + ux * ux + uy * uy + uz * uz)) - + 0.125 * (Fx + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) + + 0.3333333333333333 * (2. * ux + ux * ux + uy * uy + uz * uz)) - + 0.0625 * (dirGradC2 - nx * ux - ny * uy - nz * uz) * + (2 * chem * (ux * ux) - + 0.3333333333333333 * + ((-rhoA + rhoB) * (ux * ux) + + 2 * chem * (2 * ux + ux * ux + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - + (rhoA - rhoB) * (2 * ux + ux * ux + uy * uy + uz * uz))); + feq3 = + 0.05555555555555555 * p - + 0.08333333333333333 * rho0 * + (-(uy * uy) + + 0.3333333333333333 * (ux * ux - 2 * uy + uy * uy + uz * uz)) - + 0.125 * (Fx * ux + Fy * (-1. + uy) + Fz * uz) * + (-0.2222222222222222 - 1. * (uy * uy) + + 0.3333333333333333 * (ux * ux - 2. * uy + uy * uy + uz * uz)) - + 0.0625 * (dirGradC3 - nx * ux - ny * uy - nz * uz) * + (2 * chem * (uy * uy) - + 0.3333333333333333 * + ((-rhoA + rhoB) * (uy * uy) + + 2 * chem * (ux * ux - 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - + (rhoA - rhoB) * (ux * ux - 2 * uy + uy * uy + uz * uz))); + feq4 = + 0.05555555555555555 * p - + 0.08333333333333333 * rho0 * + (-(uy * uy) + + 0.3333333333333333 * (ux * ux + 2 * uy + uy * uy + uz * uz)) - + 0.125 * (Fy + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (uy * uy) + + 0.3333333333333333 * (ux * ux + 2. * uy + uy * uy + uz * uz)) - + 0.0625 * (dirGradC4 - nx * ux - ny * uy - nz * uz) * + (2 * chem * (uy * uy) - + 0.3333333333333333 * + ((-rhoA + rhoB) * (uy * uy) + + 2 * chem * (ux * ux + 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - + (rhoA - rhoB) * (ux * ux + 2 * uy + uy * uy + uz * uz))); + feq5 = + 0.05555555555555555 * p - + 0.08333333333333333 * rho0 * + (-(uz * uz) + + 0.3333333333333333 * (ux * ux + uy * uy + (-2 + uz) * uz)) - + 0.125 * (Fx * ux + Fy * uy + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * (uz * uz) + + 0.3333333333333333 * (ux * ux + uy * uy + (-2. + uz) * uz)) - + 0.0625 * (dirGradC5 - nx * ux - ny * uy - nz * uz) * + (2 * chem * (uz * uz) - + 0.3333333333333333 * + ((-rhoA + rhoB) * (uz * uz) + + 2 * chem * (ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (4 * chem - + (rhoA - rhoB) * (ux * ux + uy * uy + (-2 + uz) * uz))); + feq6 = 0.05555555555555555 * p - + 0.08333333333333333 * rho0 * + (-(uz * uz) + + 0.3333333333333333 * (ux * ux + uy * uy + uz * (2 + uz))) - + 0.125 * (Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (uz * uz) + + 0.3333333333333333 * (ux * ux + uy * uy + uz * (2. + uz))) - + 0.0625 * (dirGradC6 - nx * ux - ny * uy - nz * uz) * + (2 * chem * (uz * uz) - + 0.3333333333333333 * + ((-rhoA + rhoB) * (uz * uz) + + 2 * chem * (ux * ux + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (4 * chem - + (rhoA - rhoB) * (ux * ux + uy * uy + uz * (2 + uz)))); + feq7 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((ux + uy) * (ux + uy)) + + 0.3333333333333333 * + (-2 * ux + ux * ux - 2 * uy + uy * uy + uz * uz)) - + 0.0625 * (Fx * (-1. + ux) + Fy * (-1. + uy) + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) - 2. * ux * uy - + 1. * (uy * uy) + + 0.3333333333333333 * + (-2. * ux + ux * ux - 2. * uy + uy * uy + uz * uz)) - + 0.03125 * (dirGradC7 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((ux + uy) * (ux + uy)) + + 0.3333333333333333 * + ((rhoA - rhoB) * ((ux + uy) * (ux + uy)) - + 2 * chem * + (-2 * ux + ux * ux - 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (-2 * ux + ux * ux - 2 * uy + + uy * uy + uz * uz))); + feq8 = 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((ux + uy) * (ux + uy)) + + 0.3333333333333333 * + (2 * ux + ux * ux + 2 * uy + uy * uy + uz * uz)) - + 0.0625 * (Fx + Fy + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) - 2. * ux * uy - + 1. * (uy * uy) + + 0.3333333333333333 * + (2. * ux + ux * ux + 2. * uy + uy * uy + uz * uz)) - + 0.03125 * (dirGradC8 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((ux + uy) * (ux + uy)) - + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux + uy) * (ux + uy))) + + 2 * chem * + (2 * ux + ux * ux + 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (2 * ux + ux * ux + 2 * uy + + uy * uy + uz * uz))); + feq9 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((ux - uy) * (ux - uy)) + + 0.3333333333333333 * + (-2 * ux + ux * ux + 2 * uy + uy * uy + uz * uz)) - + 0.0625 * (Fy + Fx * (-1. + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) + 2. * ux * uy - + 1. * (uy * uy) + + 0.3333333333333333 * + (-2. * ux + ux * ux + 2. * uy + uy * uy + uz * uz)) - + 0.03125 * (dirGradC9 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((ux - uy) * (ux - uy)) - + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uy) * (ux - uy))) + + 2 * chem * + (-2 * ux + ux * ux + 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (-2 * ux + ux * ux + 2 * uy + + uy * uy + uz * uz))); + feq10 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((ux - uy) * (ux - uy)) + + 0.3333333333333333 * + (2 * ux + ux * ux - 2 * uy + uy * uy + uz * uz)) - + 0.0625 * (Fx * (1 + ux) + Fy * (-1. + uy) + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) + 2. * ux * uy - + 1. * (uy * uy) + + 0.3333333333333333 * + (2. * ux + ux * ux - 2. * uy + uy * uy + uz * uz)) - + 0.03125 * (dirGradC10 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((ux - uy) * (ux - uy)) - + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uy) * (ux - uy))) + + 2 * chem * + (2 * ux + ux * ux - 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (2 * ux + ux * ux - 2 * uy + + uy * uy + uz * uz))); + feq11 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((ux + uz) * (ux + uz)) + + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) - + 0.0625 * (Fx * (-1. + ux) + Fy * uy + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * (ux * ux) - 2. * ux * uz - + 1. * (uz * uz) + + 0.3333333333333333 * + (-2. * ux + ux * ux + uy * uy + (-2. + uz) * uz)) - + 0.03125 * (dirGradC11 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((ux + uz) * (ux + uz)) + + 0.3333333333333333 * + ((rhoA - rhoB) * ((ux + uz) * (ux + uz)) - + 2 * chem * + (-2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (-2 * ux + ux * ux + uy * uy + + (-2 + uz) * uz))); + feq12 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((ux + uz) * (ux + uz)) + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + uz * (2 + uz))) - + 0.0625 * (Fx + Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) - 2. * ux * uz - + 1. * (uz * uz) + + 0.3333333333333333 * + (2. * ux + ux * ux + uy * uy + uz * (2. + uz))) - + 0.03125 * (dirGradC12 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((ux + uz) * (ux + uz)) - + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux + uz) * (ux + uz))) + + 2 * chem * (2 * ux + ux * ux + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (2 * ux + ux * ux + uy * uy + + uz * (2 + uz)))); + feq13 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((ux - uz) * (ux - uz)) + + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + uz * (2 + uz))) - + 0.0625 * (Fz + Fx * (-1. + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) + 2. * ux * uz - + 1. * (uz * uz) + + 0.3333333333333333 * + (-2. * ux + ux * ux + uy * uy + uz * (2. + uz))) - + 0.03125 * (dirGradC13 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((ux - uz) * (ux - uz)) - + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uz) * (ux - uz))) + + 2 * chem * + (-2 * ux + ux * ux + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (-2 * ux + ux * ux + uy * uy + + uz * (2 + uz)))); + feq14 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((ux - uz) * (ux - uz)) + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) - + 0.0625 * (Fx * (1 + ux) + Fy * uy + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * (ux * ux) + 2. * ux * uz - + 1. * (uz * uz) + + 0.3333333333333333 * + (2. * ux + ux * ux + uy * uy + (-2. + uz) * uz)) - + 0.03125 * (dirGradC14 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((ux - uz) * (ux - uz)) - + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uz) * (ux - uz))) + + 2 * chem * + (2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (2 * ux + ux * ux + uy * uy + + (-2 + uz) * uz))); + feq15 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((uy + uz) * (uy + uz)) + + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + (-2 + uz) * uz)) - + 0.0625 * (Fx * ux + Fy * (-1. + uy) + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * (uy * uy) - 2. * uy * uz - + 1. * (uz * uz) + + 0.3333333333333333 * + (ux * ux - 2. * uy + uy * uy + (-2. + uz) * uz)) - + 0.03125 * (dirGradC15 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((uy + uz) * (uy + uz)) + + 0.3333333333333333 * + ((rhoA - rhoB) * ((uy + uz) * (uy + uz)) - + 2 * chem * + (ux * ux - 2 * uy + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (ux * ux - 2 * uy + uy * uy + + (-2 + uz) * uz))); + feq16 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((uy + uz) * (uy + uz)) + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + uz * (2 + uz))) - + 0.0625 * (Fy + Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (uy * uy) - 2. * uy * uz - + 1. * (uz * uz) + + 0.3333333333333333 * + (ux * ux + 2. * uy + uy * uy + uz * (2. + uz))) - + 0.03125 * (dirGradC16 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((uy + uz) * (uy + uz)) - + 0.3333333333333333 * + (-((rhoA - rhoB) * ((uy + uz) * (uy + uz))) + + 2 * chem * (ux * ux + 2 * uy + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (ux * ux + 2 * uy + uy * uy + + uz * (2 + uz)))); + feq17 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((uy - uz) * (uy - uz)) + + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + uz * (2 + uz))) - + 0.0625 * (Fz + Fx * ux + Fy * (-1. + uy) + Fz * uz) * + (-0.2222222222222222 - 1. * (uy * uy) + 2. * uy * uz - + 1. * (uz * uz) + + 0.3333333333333333 * + (ux * ux - 2. * uy + uy * uy + uz * (2. + uz))) - + 0.03125 * (dirGradC17 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((uy - uz) * (uy - uz)) - + 0.3333333333333333 * + (-((rhoA - rhoB) * ((uy - uz) * (uy - uz))) + + 2 * chem * (ux * ux - 2 * uy + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (ux * ux - 2 * uy + uy * uy + + uz * (2 + uz)))); + feq18 = + 0.027777777777777776 * p - + 0.041666666666666664 * rho0 * + (-((uy - uz) * (uy - uz)) + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + (-2 + uz) * uz)) - + 0.0625 * (Fx * ux + Fy * (1 + uy) + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * (uy * uy) + 2. * uy * uz - + 1. * (uz * uz) + + 0.3333333333333333 * + (ux * ux + 2. * uy + uy * uy + (-2. + uz) * uz)) - + 0.03125 * (dirGradC18 - nx * ux - ny * uy - nz * uz) * + (2 * chem * ((uy - uz) * (uy - uz)) - + 0.3333333333333333 * + (-((rhoA - rhoB) * ((uy - uz) * (uy - uz))) + + 2 * chem * + (ux * ux + 2 * uy + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (4 * chem - (rhoA - rhoB) * (ux * ux + 2 * uy + uy * uy + + (-2 + uz) * uz))); + + //------------------------------------------------- BCK collison ------------------------------------------------------------// + // q=0 + dist[n] = + m0 - (m0 - feq0) / tau + + 0.25 * (2 * (Fx * ux + Fy * uy + Fz * uz) * + (-0.6666666666666666 + ux * ux + uy * uy + uz * uz) + + (mgx * ux + mgy * uy + mgz * uz) * + (2 * chem * (ux * ux + uy * uy + uz * uz) + + 0.3333333333333333 * + (-4 * chem + + (rhoA - rhoB) * (ux * ux + uy * uy + uz * uz)))); + + // q = 1 + dist[1 * Np + n] = + m1 - (m1 - feq1) / tau + + 0.125 * + (2 * (Fx * (-1 + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - ux * ux + + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + uz * uz)) - + (dirGradM1 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * (ux * ux) + + 0.3333333333333333 * + ((-rhoA + rhoB) * (ux * ux) + + 2 * chem * (-2 * ux + ux * ux + uy * uy + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + (rhoA - rhoB) * (-2 * ux + ux * ux + + uy * uy + uz * uz)))); + + // q=2 + dist[2 * Np + n] = + m2 - (m2 - feq2) / tau + + 0.125 * + (2 * (Fx + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - ux * ux + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + uz * uz)) - + (dirGradM2 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * (ux * ux) + + 0.3333333333333333 * + ((-rhoA + rhoB) * (ux * ux) + + 2 * chem * (2 * ux + ux * ux + uy * uy + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + (rhoA - rhoB) * (2 * ux + ux * ux + + uy * uy + uz * uz)))); + + // q = 3 + dist[3 * Np + n] = + m3 - (m3 - feq3) / tau + + 0.125 * + (2 * (Fx * ux + Fy * (-1 + uy) + Fz * uz) * + (-0.2222222222222222 - uy * uy + + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + uz * uz)) - + (dirGradM3 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * (uy * uy) + + 0.3333333333333333 * + ((-rhoA + rhoB) * (uy * uy) + + 2 * chem * (ux * ux - 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + (rhoA - rhoB) * (ux * ux - 2 * uy + + uy * uy + uz * uz)))); + + // q = 4 + dist[4 * Np + n] = + m4 - (m4 - feq4) / tau + + 0.125 * + (2 * (Fy + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - uy * uy + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + uz * uz)) - + (dirGradM4 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * (uy * uy) + + 0.3333333333333333 * + ((-rhoA + rhoB) * (uy * uy) + + 2 * chem * (ux * ux + 2 * uy + uy * uy + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + (rhoA - rhoB) * (ux * ux + 2 * uy + + uy * uy + uz * uz)))); + + // q = 5 + dist[5 * Np + n] = + m5 - (m5 - feq5) / tau + + 0.125 * + (2 * (Fx * ux + Fy * uy + Fz * (-1 + uz)) * + (-0.2222222222222222 - uz * uz + + 0.3333333333333333 * + (ux * ux + uy * uy + (-2 + uz) * uz)) - + (dirGradM5 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * (uz * uz) + + 0.3333333333333333 * + ((-rhoA + rhoB) * (uz * uz) + + 2 * chem * (ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (-4 * chem + (rhoA - rhoB) * (ux * ux + uy * uy + + (-2 + uz) * uz)))); + + // q = 6 + dist[6 * Np + n] = + m6 - (m6 - feq6) / tau + + 0.125 * (2 * (Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - uz * uz + + 0.3333333333333333 * + (ux * ux + uy * uy + uz * (2 + uz))) - + (dirGradM6 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * (uz * uz) + + 0.3333333333333333 * + ((-rhoA + rhoB) * (uz * uz) + + 2 * chem * (ux * ux + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (-4 * chem + (rhoA - rhoB) * (ux * ux + uy * uy + + uz * (2 + uz))))); + + // q = 7 + dist[7 * Np + n] = + m7 - (m7 - feq7) / tau + + 0.0625 * (-2 * (Fx * (-1 + ux) + Fy * (-1 + uy) + Fz * uz) * + (0.2222222222222222 + (ux + uy) * (ux + uy) - + 0.3333333333333333 * (-2 * ux + ux * ux - 2 * uy + + uy * uy + uz * uz)) - + (dirGradM7 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((ux + uy) * (ux + uy)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux + uy) * (ux + uy))) + + 2 * chem * + (-2 * ux + ux * ux - 2 * uy + uy * uy + + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * (-2 * ux + ux * ux - 2 * uy + + uy * uy + uz * uz)))); + + // q = 8 + dist[8 * Np + n] = + m8 - (m8 - feq8) / tau + + 0.0625 * (2 * (Fx + Fy + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (ux + uy) * (ux + uy) + + 0.3333333333333333 * (2 * ux + ux * ux + 2 * uy + + uy * uy + uz * uz)) - + (dirGradM8 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((ux + uy) * (ux + uy)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux + uy) * (ux + uy))) + + 2 * chem * + (2 * ux + ux * ux + 2 * uy + uy * uy + + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * (2 * ux + ux * ux + 2 * uy + + uy * uy + uz * uz)))); + + // q = 9 + dist[9 * Np + n] = + m9 - (m9 - feq9) / tau + + 0.0625 * (2 * (Fy + Fx * (-1 + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (ux - uy) * (ux - uy) + + 0.3333333333333333 * (-2 * ux + ux * ux + 2 * uy + + uy * uy + uz * uz)) - + (dirGradM9 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((ux - uy) * (ux - uy)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uy) * (ux - uy))) + + 2 * chem * + (-2 * ux + ux * ux + 2 * uy + uy * uy + + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * (-2 * ux + ux * ux + 2 * uy + + uy * uy + uz * uz)))); + + // q = 10 + dist[10 * Np + n] = + m10 - (m10 - feq10) / tau + + 0.0625 * (2 * (Fx * (1 + ux) + Fy * (-1 + uy) + Fz * uz) * + (-0.2222222222222222 - (ux - uy) * (ux - uy) + + 0.3333333333333333 * (2 * ux + ux * ux - 2 * uy + + uy * uy + uz * uz)) - + (dirGradM10 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((ux - uy) * (ux - uy)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uy) * (ux - uy))) + + 2 * chem * + (2 * ux + ux * ux - 2 * uy + uy * uy + + uz * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * (2 * ux + ux * ux - 2 * uy + + uy * uy + uz * uz)))); + + // q = 11 + dist[11 * Np + n] = + m11 - (m11 - feq11) / tau + + 0.0625 * + (-2 * (Fx * (-1 + ux) + Fy * uy + Fz * (-1 + uz)) * + (0.2222222222222222 + (ux + uz) * (ux + uz) - + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) - + (dirGradM11 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((ux + uz) * (ux + uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux + uz) * (ux + uz))) + + 2 * chem * + (-2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * (-2 * ux + ux * ux + uy * uy + + (-2 + uz) * uz)))); + + // q = 12 + dist[12 * Np + n] = + m12 - (m12 - feq12) / tau + + 0.0625 * + (2 * (Fx + Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (ux + uz) * (ux + uz) + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + uz * (2 + uz))) - + (dirGradM12 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((ux + uz) * (ux + uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux + uz) * (ux + uz))) + + 2 * chem * + (2 * ux + ux * ux + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (2 * ux + ux * ux + uy * uy + uz * (2 + uz))))); + + // q = 13 + dist[13 * Np + n] = + m13 - (m13 - feq13) / tau + + 0.0625 * + (2 * (Fz + Fx * (-1 + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (ux - uz) * (ux - uz) + + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + uz * (2 + uz))) - + (dirGradM13 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((ux - uz) * (ux - uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uz) * (ux - uz))) + + 2 * chem * + (-2 * ux + ux * ux + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (-2 * ux + ux * ux + uy * uy + uz * (2 + uz))))); + + // q= 14 + dist[14 * Np + n] = + m14 - (m14 - feq14) / tau + + 0.0625 * + (2 * (Fx * (1 + ux) + Fy * uy + Fz * (-1 + uz)) * + (-0.2222222222222222 - (ux - uz) * (ux - uz) + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) - + (dirGradM14 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((ux - uz) * (ux - uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((ux - uz) * (ux - uz))) + + 2 * chem * + (2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)))); + + // q = 15 + dist[15 * Np + n] = + m15 - (m15 - feq15) / tau + + 0.0625 * + (-2 * (Fx * ux + Fy * (-1 + uy) + Fz * (-1 + uz)) * + (0.2222222222222222 + (uy + uz) * (uy + uz) - + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + (-2 + uz) * uz)) - + (dirGradM15 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((uy + uz) * (uy + uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((uy + uz) * (uy + uz))) + + 2 * chem * + (ux * ux - 2 * uy + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (ux * ux - 2 * uy + uy * uy + (-2 + uz) * uz)))); + + // q = 16 + dist[16 * Np + n] = + m16 - (m16 - feq16) / tau + + 0.0625 * + (2 * (Fy + Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (uy + uz) * (uy + uz) + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + uz * (2 + uz))) - + (dirGradM16 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((uy + uz) * (uy + uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((uy + uz) * (uy + uz))) + + 2 * chem * + (ux * ux + 2 * uy + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (ux * ux + 2 * uy + uy * uy + uz * (2 + uz))))); + + // q = 17 + dist[17 * Np + n] = + m17 - (m17 - feq17) / tau + + 0.0625 * + (2 * (Fz + Fx * ux + Fy * (-1 + uy) + Fz * uz) * + (-0.2222222222222222 - (uy - uz) * (uy - uz) + + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + uz * (2 + uz))) - + (dirGradM17 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((uy - uz) * (uy - uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((uy - uz) * (uy - uz))) + + 2 * chem * + (ux * ux - 2 * uy + uy * uy + uz * (2 + uz))) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (ux * ux - 2 * uy + uy * uy + uz * (2 + uz))))); + + // q = 18 + dist[18 * Np + n] = + m18 - (m18 - feq18) / tau + + 0.0625 * + (2 * (Fx * ux + Fy * (1 + uy) + Fz * (-1 + uz)) * + (-0.2222222222222222 - (uy - uz) * (uy - uz) + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + (-2 + uz) * uz)) - + (dirGradM18 - mgx * ux - mgy * uy - mgz * uz) * + (-2 * chem * ((uy - uz) * (uy - uz)) + + 0.3333333333333333 * + (-((rhoA - rhoB) * ((uy - uz) * (uy - uz))) + + 2 * chem * + (ux * ux + 2 * uy + uy * uy + (-2 + uz) * uz)) + + 0.1111111111111111 * + (-4 * chem + + (rhoA - rhoB) * + (ux * ux + 2 * uy + uy * uy + (-2 + uz) * uz)))); + //----------------------------------------------------------------------------------------------------------------------------------------// + + //Update color gradient on device + ColorGrad[0 * Np + n] = nx; + ColorGrad[1 * Np + n] = ny; + ColorGrad[2 * Np + n] = nz; + + // ----------------------------- compute phase field evolution ---------------------------------------- + //Normalize the Color Gradient + C = sqrt(nx * nx + ny * ny + nz * nz); + if (C < 1.0e-14) + nx = ny = nz = 0.0; + //compute surface tension-related parameter + //theta = 4.5*M*2.0*(1-phi*phi)/W; + theta = 4.5 * M * 2.0 * (1 - phi_temp * phi_temp) / W; + + //load distributions of phase field + //q=0 + h0 = hq[n]; + //q=1 + h1 = hq[2 * Np + n]; + + //q=2 + h2 = hq[1 * Np + n]; + + //q=3 + h3 = hq[4 * Np + n]; + + //q=4 + h4 = hq[3 * Np + n]; + + //q=5 + h5 = hq[6 * Np + n]; + + //q=6 + h6 = hq[5 * Np + n]; + + //-------------------------------- BGK collison for phase field ---------------------------------// + // q = 0 + hq[n] = h0 - (h0 - 0.3333333333333333 * phi) / tauM; + + // q = 1 + hq[1 * Np + n] = h1 - (h1 - 0.1111111111111111 * nx * theta - + phi * (0.1111111111111111 + 0.5 * ux)) / + tauM; + + // q = 2 + hq[2 * Np + n] = h2 - (h2 + 0.1111111111111111 * nx * theta - + phi * (0.1111111111111111 - 0.5 * ux)) / + tauM; + + // q = 3 + hq[3 * Np + n] = h3 - (h3 - 0.1111111111111111 * ny * theta - + phi * (0.1111111111111111 + 0.5 * uy)) / + tauM; + + // q = 4 + hq[4 * Np + n] = h4 - (h4 + 0.1111111111111111 * ny * theta - + phi * (0.1111111111111111 - 0.5 * uy)) / + tauM; + + // q = 5 + hq[5 * Np + n] = h5 - (h5 - 0.1111111111111111 * nz * theta - + phi * (0.1111111111111111 + 0.5 * uz)) / + tauM; + + // q = 6 + hq[6 * Np + n] = h6 - (h6 + 0.1111111111111111 * nz * theta - + phi * (0.1111111111111111 - 0.5 * uz)) / + tauM; + //........................................................................ + + //Update velocity on device + Vel[0 * Np + n] = ux; + Vel[1 * Np + n] = uy; + Vel[2 * Np + n] = uz; + //Update pressure on device + Pressure[n] = p; + //Update chemical potential on device + mu_phi[n] = chem; + } } - -//extern "C" void ScaLBL_D3Q19_AAodd_FreeLeeModel_Combined(int *neighborList, int *Map, double *dist, double *hq, double *Den, double *Phi, double *mu_phi, double *Vel, double *Pressure, double *ColorGrad, -// double rhoA, double rhoB, double tauA, double tauB, double tauM, double kappa, double beta, double W, double Fx, double Fy, double Fz, +//extern "C" void ScaLBL_D3Q19_AAodd_FreeLeeModel_Combined(int *neighborList, int *Map, double *dist, double *hq, double *Den, double *Phi, double *mu_phi, double *Vel, double *Pressure, double *ColorGrad, +// double rhoA, double rhoB, double tauA, double tauB, double tauM, double kappa, double beta, double W, double Fx, double Fy, double Fz, // int strideY, int strideZ, int start, int finish, int Np){ -// +// // int n,nn,nn2x,ijk; // int nr1,nr2,nr3,nr4,nr5,nr6,nr7,nr8,nr9,nr10,nr11,nr12,nr13,nr14,nr15,nr16,nr17,nr18; -// double ux,uy,uz;//fluid velocity +// double ux,uy,uz;//fluid velocity // double p;//pressure // double chem;//chemical potential // double phi; //phase field @@ -2802,7 +4853,7 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // mgx = -3.0*1.0/18.0*(mm1-mm2+0.5*(mm7-mm8+mm9-mm10+mm11-mm12+mm13-mm14)); // mgy = -3.0*1.0/18.0*(mm3-mm4+0.5*(mm7-mm8-mm9+mm10+mm15-mm16+mm17-mm18)); // mgz = -3.0*1.0/18.0*(mm5-mm6+0.5*(mm11-mm12-mm13+mm14+mm15-mm16-mm17+mm18)); -// +// // //de-noise color gradient and mixed gradient // C = sqrt(nx*nx+ny*ny+nz*nz); // if (C<1.0e-12) nx=ny=nz=0.0; @@ -2835,7 +4886,7 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // // q = 6 // nr6 = neighborList[n+5*Np]; // m6 = dist[nr6]; -// +// // // q=7 // nr7 = neighborList[n+6*Np]; // m7 = dist[nr7]; @@ -2893,216 +4944,216 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // +0.5*(rhoA-rhoB)/2.0/3.0*(ux*nx+uy*ny+uz*nz); // // //compute equilibrium distributions -// feq0 = 0.3333333333333333*p - 0.25*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) - +// feq0 = 0.3333333333333333*p - 0.25*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) - // 0.16666666666666666*rho0*(ux*ux + uy*uy + uz*uz) - 0.5*(-(nx*ux) - ny*uy - nz*uz)* // (-0.08333333333333333*(rhoA - rhoB)*(ux*ux + uy*uy + uz*uz) + chem*(0.3333333333333333 - 0.5*(ux*ux + uy*uy + uz*uz))); -// feq1 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-ux*ux + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) - -// 0.125*(Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + +// feq1 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-ux*ux + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) - +// 0.125*(Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + // 0.3333333333333333*(-2.*ux + ux*ux + uy*uy + uz*uz)) - 0.0625*(nx - nx*ux - ny*uy - nz*uz)* -// (2*chem*ux*ux - 0.3333333333333333*((-rhoA + rhoB)*ux*ux + 2*chem*(-2*ux + ux*ux + uy*uy + uz*uz)) + +// (2*chem*ux*ux - 0.3333333333333333*((-rhoA + rhoB)*ux*ux + 2*chem*(-2*ux + ux*ux + uy*uy + uz*uz)) + // 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*uz))); -// feq2 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-ux*ux + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) - -// 0.125*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + +// feq2 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-ux*ux + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) - +// 0.125*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + // 0.3333333333333333*(2.*ux + ux*ux + uy*uy + uz*uz)) - 0.0625*(nx + nx*ux + ny*uy + nz*uz)* -// (-2.*chem*ux*ux + 0.1111111111111111*(-4.*chem + rhoB*(-2.*ux - 1.*ux*ux - 1.*uy*uy - 1.*uz*uz) + -// rhoA*(2.*ux + ux*ux + uy*uy + uz*uz)) + 0.3333333333333333*((-1.*rhoA + rhoB)*ux*ux + +// (-2.*chem*ux*ux + 0.1111111111111111*(-4.*chem + rhoB*(-2.*ux - 1.*ux*ux - 1.*uy*uy - 1.*uz*uz) + +// rhoA*(2.*ux + ux*ux + uy*uy + uz*uz)) + 0.3333333333333333*((-1.*rhoA + rhoB)*ux*ux + // chem*(4.*ux + 2.*ux*ux + 2.*uy*uy + 2.*uz*uz))); -// feq3 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uy*uy + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) - -// 0.125*(Fx*ux + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy + +// feq3 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uy*uy + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) - +// 0.125*(Fx*ux + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy + // 0.3333333333333333*(ux*ux - 2.*uy + uy*uy + uz*uz)) - 0.0625*(ny - nx*ux - ny*uy - nz*uz)* -// (2*chem*uy*uy - 0.3333333333333333*((-rhoA + rhoB)*uy*uy + 2*chem*(ux*ux - 2*uy + uy*uy + uz*uz)) + +// (2*chem*uy*uy - 0.3333333333333333*((-rhoA + rhoB)*uy*uy + 2*chem*(ux*ux - 2*uy + uy*uy + uz*uz)) + // 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*uz))); -// feq4 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uy*uy + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) - -// 0.125*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy + +// feq4 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uy*uy + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) - +// 0.125*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy + // 0.3333333333333333*(ux*ux + 2.*uy + uy*uy + uz*uz)) - 0.0625*(ny + nx*ux + ny*uy + nz*uz)* -// (-2.*chem*uy*uy + 0.1111111111111111*(-4.*chem + rhoB*(-1.*ux*ux - 2.*uy - 1.*uy*uy - 1.*uz*uz) + -// rhoA*(ux*ux + 2.*uy + uy*uy + uz*uz)) + 0.3333333333333333*((-1.*rhoA + rhoB)*uy*uy + -// chem*(2.*ux*ux + 4.*uy + 2.*uy*uy + 2.*uz*uz))); -// feq5 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uz*uz + 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) - -// 0.125*(Fx*ux + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*uz*uz + +// (-2.*chem*uy*uy + 0.1111111111111111*(-4.*chem + rhoB*(-1.*ux*ux - 2.*uy - 1.*uy*uy - 1.*uz*uz) + +// rhoA*(ux*ux + 2.*uy + uy*uy + uz*uz)) + 0.3333333333333333*((-1.*rhoA + rhoB)*uy*uy + +// chem*(2.*ux*ux + 4.*uy + 2.*uy*uy + 2.*uz*uz))); +// feq5 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uz*uz + 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) - +// 0.125*(Fx*ux + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*uz*uz + // 0.3333333333333333*(ux*ux + uy*uy + (-2. + uz)*uz)) - 0.0625*(nx*ux + ny*uy + nz*(-1. + uz))* -// (-2.*chem*uz*uz + 0.1111111111111111*(-4.*chem + rhoB*(-1.*ux*ux - 1.*uy*uy + (2. - 1.*uz)*uz) + -// rhoA*(ux*ux + uy*uy + (-2. + uz)*uz)) + 0.3333333333333333*((-1.*rhoA + rhoB)*uz*uz + -// chem*(2.*ux*ux + 2.*uy*uy + uz*(-4. + 2.*uz)))); -// feq6 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uz*uz + 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) - -// 0.125*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*uz*uz + +// (-2.*chem*uz*uz + 0.1111111111111111*(-4.*chem + rhoB*(-1.*ux*ux - 1.*uy*uy + (2. - 1.*uz)*uz) + +// rhoA*(ux*ux + uy*uy + (-2. + uz)*uz)) + 0.3333333333333333*((-1.*rhoA + rhoB)*uz*uz + +// chem*(2.*ux*ux + 2.*uy*uy + uz*(-4. + 2.*uz)))); +// feq6 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uz*uz + 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) - +// 0.125*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*uz*uz + // 0.3333333333333333*(ux*ux + uy*uy + uz*(2. + uz))) - 0.0625*(nz + nx*ux + ny*uy + nz*uz)* -// (-2.*chem*uz*uz + 0.1111111111111111*(-4.*chem + rhoB*(-1.*ux*ux - 1.*uy*uy + (-2. - 1.*uz)*uz) + -// rhoA*(ux*ux + uy*uy + uz*(2. + uz))) + 0.3333333333333333*((-1.*rhoA + rhoB)*uz*uz + -// chem*(2.*ux*ux + 2.*uy*uy + uz*(4. + 2.*uz)))); +// (-2.*chem*uz*uz + 0.1111111111111111*(-4.*chem + rhoB*(-1.*ux*ux - 1.*uy*uy + (-2. - 1.*uz)*uz) + +// rhoA*(ux*ux + uy*uy + uz*(2. + uz))) + 0.3333333333333333*((-1.*rhoA + rhoB)*uz*uz + +// chem*(2.*ux*ux + 2.*uy*uy + uz*(4. + 2.*uz)))); // feq7 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(ux + uy)*(ux + uy) + 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) - -// 0.0625*(Fx*(-1. + ux) + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uy - 1.*uy*uy + +// (-(ux + uy)*(ux + uy) + 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) - +// 0.0625*(Fx*(-1. + ux) + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uy - 1.*uy*uy + // 0.3333333333333333*(-2.*ux + ux*ux - 2.*uy + uy*uy + uz*uz)) - 0.03125*(nx + ny - nx*ux - ny*uy - nz*uz)* -// (2*chem*(ux + uy)*(ux + uy) + 0.3333333333333333*((rhoA - rhoB)*(ux + uy)*(ux + uy) - 2*chem*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + -// 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz))); +// (2*chem*(ux + uy)*(ux + uy) + 0.3333333333333333*((rhoA - rhoB)*(ux + uy)*(ux + uy) - 2*chem*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + +// 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz))); // feq8 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(ux + uy)*(ux + uy) + 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - -// 0.0625*(Fx + Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uy - 1.*uy*uy + +// (-(ux + uy)*(ux + uy) + 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - +// 0.0625*(Fx + Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uy - 1.*uy*uy + // 0.3333333333333333*(2.*ux + ux*ux + 2.*uy + uy*uy + uz*uz)) - 0.03125*(-(nx*(1 + ux)) - ny*(1 + uy) - nz*uz)* -// (2*chem*(ux + uy)*(ux + uy) - 0.3333333333333333*(-((rhoA - rhoB)*(ux + uy)*(ux + uy)) + +// (2*chem*(ux + uy)*(ux + uy) - 0.3333333333333333*(-((rhoA - rhoB)*(ux + uy)*(ux + uy)) + // 2*chem*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + 0.1111111111111111* -// (4*chem - (rhoA - rhoB)*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz))); +// (4*chem - (rhoA - rhoB)*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz))); // feq9 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(ux - uy)*(ux - uy) + 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - -// 0.0625*(Fy + Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uy - 1.*uy*uy + +// (-(ux - uy)*(ux - uy) + 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - +// 0.0625*(Fy + Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uy - 1.*uy*uy + // 0.3333333333333333*(-2.*ux + ux*ux + 2.*uy + uy*uy + uz*uz)) - 0.03125*(nx - nx*ux - ny*(1 + uy) - nz*uz)* -// (2*chem*(ux - uy)*(ux - uy) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uy)*(ux - uy)) + +// (2*chem*(ux - uy)*(ux - uy) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uy)*(ux - uy)) + // 2*chem*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + 0.1111111111111111* -// (4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz))); +// (4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz))); // feq10 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(ux - uy)*(ux - uy) + 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) - -// 0.0625*(Fx*(1 + ux) + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uy - 1.*uy*uy + +// (-(ux - uy)*(ux - uy) + 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) - +// 0.0625*(Fx*(1 + ux) + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uy - 1.*uy*uy + // 0.3333333333333333*(2.*ux + ux*ux - 2.*uy + uy*uy + uz*uz)) - 0.03125*(ny - nx*(1 + ux) - ny*uy - nz*uz)* -// (2*chem*(ux - uy)*(ux - uy) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uy)*(ux - uy)) + +// (2*chem*(ux - uy)*(ux - uy) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uy)*(ux - uy)) + // 2*chem*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + 0.1111111111111111* -// (4*chem - (rhoA - rhoB)*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz))); +// (4*chem - (rhoA - rhoB)*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz))); // feq11 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(ux + uz)*(ux + uz) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) - -// 0.0625*(Fx*(-1. + ux) + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uz - 1.*uz*uz + +// (-(ux + uz)*(ux + uz) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) - +// 0.0625*(Fx*(-1. + ux) + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uz - 1.*uz*uz + // 0.3333333333333333*(-2.*ux + ux*ux + uy*uy + (-2. + uz)*uz)) - 0.03125*(nx + nz - nx*ux - ny*uy - nz*uz)* -// (2*chem*(ux + uz)*(ux + uz) + 0.3333333333333333*((rhoA - rhoB)*(ux + uz)*(ux + uz) - 2*chem*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + -// 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz))); +// (2*chem*(ux + uz)*(ux + uz) + 0.3333333333333333*((rhoA - rhoB)*(ux + uz)*(ux + uz) - 2*chem*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + +// 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz))); // feq12 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(ux + uz)*(ux + uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) - -// 0.0625*(Fx + Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uz - 1.*uz*uz + +// (-(ux + uz)*(ux + uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) - +// 0.0625*(Fx + Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uz - 1.*uz*uz + // 0.3333333333333333*(2.*ux + ux*ux + uy*uy + uz*(2. + uz))) - 0.03125*(-(nx*(1 + ux)) - ny*uy - nz*(1 + uz))* -// (2*chem*(ux + uz)*(ux + uz) - 0.3333333333333333*(-((rhoA - rhoB)*(ux + uz)*(ux + uz)) + +// (2*chem*(ux + uz)*(ux + uz) - 0.3333333333333333*(-((rhoA - rhoB)*(ux + uz)*(ux + uz)) + // 2*chem*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) + 0.1111111111111111* -// (4*chem - (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*(2 + uz)))); +// (4*chem - (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*(2 + uz)))); // feq13 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(ux - uz)*(ux - uz) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) - -// 0.0625*(Fz + Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uz - 1.*uz*uz + +// (-(ux - uz)*(ux - uz) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) - +// 0.0625*(Fz + Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uz - 1.*uz*uz + // 0.3333333333333333*(-2.*ux + ux*ux + uy*uy + uz*(2. + uz))) - 0.03125*(nx - nx*ux - ny*uy - nz*(1 + uz))* -// (2*chem*(ux - uz)*(ux - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uz)*(ux - uz)) + +// (2*chem*(ux - uz)*(ux - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uz)*(ux - uz)) + // 2*chem*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + 0.1111111111111111* -// (4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*(2 + uz)))); +// (4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*(2 + uz)))); // feq14 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(ux - uz)*(ux - uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) - -// 0.0625*(Fx*(1 + ux) + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uz - 1.*uz*uz + +// (-(ux - uz)*(ux - uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) - +// 0.0625*(Fx*(1 + ux) + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uz - 1.*uz*uz + // 0.3333333333333333*(2.*ux + ux*ux + uy*uy + (-2. + uz)*uz)) - 0.03125*(nz - nx*(1 + ux) - ny*uy - nz*uz)* -// (2*chem*(ux - uz)*(ux - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uz)*(ux - uz)) + +// (2*chem*(ux - uz)*(ux - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uz)*(ux - uz)) + // 2*chem*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + 0.1111111111111111* -// (4*chem - (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz))); +// (4*chem - (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz))); // feq15 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(uy + uz)*(uy + uz) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) - -// 0.0625*(Fx*ux + Fy*(-1. + uy) + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*uy*uy - 2.*uy*uz - 1.*uz*uz + +// (-(uy + uz)*(uy + uz) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) - +// 0.0625*(Fx*ux + Fy*(-1. + uy) + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*uy*uy - 2.*uy*uz - 1.*uz*uz + // 0.3333333333333333*(ux*ux - 2.*uy + uy*uy + (-2. + uz)*uz)) - 0.03125*(ny + nz - nx*ux - ny*uy - nz*uz)* -// (2*chem*(uy + uz)*(uy + uz) + 0.3333333333333333*((rhoA - rhoB)*(uy + uz)*(uy + uz) - 2*chem*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + -// 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz))); +// (2*chem*(uy + uz)*(uy + uz) + 0.3333333333333333*((rhoA - rhoB)*(uy + uz)*(uy + uz) - 2*chem*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + +// 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz))); // feq16 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(uy + uz)*(uy + uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) - -// 0.0625*(Fy + Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy - 2.*uy*uz - 1.*uz*uz + +// (-(uy + uz)*(uy + uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) - +// 0.0625*(Fy + Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy - 2.*uy*uz - 1.*uz*uz + // 0.3333333333333333*(ux*ux + 2.*uy + uy*uy + uz*(2. + uz))) - 0.03125*(-(nx*ux) - ny*(1 + uy) - nz*(1 + uz))* -// (2*chem*(uy + uz)*(uy + uz) - 0.3333333333333333*(-((rhoA - rhoB)*(uy + uz)*(uy + uz)) + +// (2*chem*(uy + uz)*(uy + uz) - 0.3333333333333333*(-((rhoA - rhoB)*(uy + uz)*(uy + uz)) + // 2*chem*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) + 0.1111111111111111* -// (4*chem - (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*(2 + uz)))); +// (4*chem - (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*(2 + uz)))); // feq17 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) - -// 0.0625*(Fz + Fx*ux + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy + 2.*uy*uz - 1.*uz*uz + +// (-(uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) - +// 0.0625*(Fz + Fx*ux + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy + 2.*uy*uz - 1.*uz*uz + // 0.3333333333333333*(ux*ux - 2.*uy + uy*uy + uz*(2. + uz))) - 0.03125*(ny - nx*ux - ny*uy - nz*(1 + uz))* -// (2*chem*(uy - uz)*(uy - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(uy - uz)*(uy - uz)) + +// (2*chem*(uy - uz)*(uy - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(uy - uz)*(uy - uz)) + // 2*chem*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) + 0.1111111111111111* -// (4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*(2 + uz)))); +// (4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*(2 + uz)))); // feq18 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) - -// 0.0625*(Fx*ux + Fy*(1 + uy) + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*uy*uy + 2.*uy*uz - 1.*uz*uz + +// (-(uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) - +// 0.0625*(Fx*ux + Fy*(1 + uy) + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*uy*uy + 2.*uy*uz - 1.*uz*uz + // 0.3333333333333333*(ux*ux + 2.*uy + uy*uy + (-2. + uz)*uz)) - 0.03125*(nz - nx*ux - ny*(1 + uy) - nz*uz)* -// (2*chem*(uy - uz)*(uy - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(uy - uz)*(uy - uz)) + +// (2*chem*(uy - uz)*(uy - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(uy - uz)*(uy - uz)) + // 2*chem*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + 0.1111111111111111* -// (4*chem - (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz))); +// (4*chem - (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz))); // // //------------------------------------------------- BCK collison ------------------------------------------------------------// // // q=0 -// dist[n] = m0 - (m0-feq0)/tau + 0.25*(2*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) + -// (mgx*ux + mgy*uy + mgz*uz)*(2*chem*(ux*ux + uy*uy + uz*uz) + -// 0.3333333333333333*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + uz*uz)))); +// dist[n] = m0 - (m0-feq0)/tau + 0.25*(2*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) + +// (mgx*ux + mgy*uy + mgz*uz)*(2*chem*(ux*ux + uy*uy + uz*uz) + +// 0.3333333333333333*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + uz*uz)))); // // // q = 1 -// dist[nr2] = m1 - (m1-feq1)/tau + 0.125*(2*(Fx*(-1 + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + -// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) + -// (mgx*(-1 + ux) + mgy*uy + mgz*uz)*(-2*chem*(ux*ux) + -// 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*uz)) + +// dist[nr2] = m1 - (m1-feq1)/tau + 0.125*(2*(Fx*(-1 + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + +// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) + +// (mgx*(-1 + ux) + mgy*uy + mgz*uz)*(-2*chem*(ux*ux) + +// 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*uz)))); // // // q=2 -// dist[nr1] = m2 - (m2-feq2)/tau + 0.125*(2*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + -// 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) + -// (mgx + mgx*ux + mgy*uy + mgz*uz)*(-2*chem*(ux*ux) + -// 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(2*ux + ux*ux + uy*uy + uz*uz)) + +// dist[nr1] = m2 - (m2-feq2)/tau + 0.125*(2*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + +// 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) + +// (mgx + mgx*ux + mgy*uy + mgz*uz)*(-2*chem*(ux*ux) + +// 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(2*ux + ux*ux + uy*uy + uz*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*uz)))); // // // q = 3 -// dist[nr4] = m3 - (m3-feq3)/tau + 0.125*(2*(Fx*ux + Fy*(-1 + uy) + Fz*uz)*(-0.2222222222222222 - uy*uy + -// 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) + -// (mgx*ux + mgy*(-1 + uy) + mgz*uz)*(-2*chem*(uy*uy) + -// 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*uz)) + +// dist[nr4] = m3 - (m3-feq3)/tau + 0.125*(2*(Fx*ux + Fy*(-1 + uy) + Fz*uz)*(-0.2222222222222222 - uy*uy + +// 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) + +// (mgx*ux + mgy*(-1 + uy) + mgz*uz)*(-2*chem*(uy*uy) + +// 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*uz)))); // // // q = 4 -// dist[nr3] = m4 - (m4-feq4)/tau + 0.125*(2*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uy*uy + -// 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) + -// (mgy + mgx*ux + mgy*uy + mgz*uz)*(-2*chem*(uy*uy) + -// 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*uz)) + +// dist[nr3] = m4 - (m4-feq4)/tau + 0.125*(2*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uy*uy + +// 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) + +// (mgy + mgx*ux + mgy*uy + mgz*uz)*(-2*chem*(uy*uy) + +// 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*uz)))); // // // q = 5 -// dist[nr6] = m5 - (m5-feq5)/tau + 0.125*(2*(Fx*ux + Fy*uy + Fz*(-1 + uz))*(-0.2222222222222222 - uz*uz + -// 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) + -// (mgx*ux + mgy*uy + mgz*(-1 + uz))*(-2*chem*(uz*uz) + -// 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + (-2 + uz)*uz)) + +// dist[nr6] = m5 - (m5-feq5)/tau + 0.125*(2*(Fx*ux + Fy*uy + Fz*(-1 + uz))*(-0.2222222222222222 - uz*uz + +// 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) + +// (mgx*ux + mgy*uy + mgz*(-1 + uz))*(-2*chem*(uz*uz) + +// 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + (-2 + uz)*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + (-2 + uz)*uz)))); // // // q = 6 -// dist[nr5] = m6 - (m6-feq6)/tau + 0.125*(2*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uz*uz + -// 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) + -// (mgz + mgx*ux + mgy*uy + mgz*uz)*(-2*chem*(uz*uz) + -// 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + uz*(2 + uz))) + +// dist[nr5] = m6 - (m6-feq6)/tau + 0.125*(2*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uz*uz + +// 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) + +// (mgz + mgx*ux + mgy*uy + mgz*uz)*(-2*chem*(uz*uz) + +// 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + uz*(2 + uz))) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + uz*(2 + uz))))); // // // q = 7 // dist[nr8] = m7 - (m7-feq7)/tau + 0.0625*(-2*(Fx*(-1 + ux) + Fy*(-1 + uy) + Fz*uz)* -// (0.2222222222222222 + (ux + uy)*(ux + uy) - -// 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + +// (0.2222222222222222 + (ux + uy)*(ux + uy) - +// 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + // (mgx*(-1 + ux) + mgy*(-1 + uy) + mgz*uz)* // (-2*chem*((ux + uy)*(ux + uy)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + +// (-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)))); // // // q = 8 // dist[nr7] = m8 - (m8-feq8)/tau + 0.0625*(2*(Fx + Fy + Fx*ux + Fy*uy + Fz*uz)* -// (-0.2222222222222222 - (ux + uy)*(ux + uy) + -// 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + +// (-0.2222222222222222 - (ux + uy)*(ux + uy) + +// 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + // (mgx + mgy + mgx*ux + mgy*uy + mgz*uz)* // (-2*chem*((ux + uy)*(ux + uy)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + +// (-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)))); // // // q = 9 // dist[nr10] = m9 - (m9-feq9)/tau + 0.0625*(2*(Fy + Fx*(-1 + ux) + Fy*uy + Fz*uz)* -// (-0.2222222222222222 - (ux - uy)*(ux - uy) + -// 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + +// (-0.2222222222222222 - (ux - uy)*(ux - uy) + +// 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + // (mgy + mgx*(-1 + ux) + mgy*uy + mgz*uz)* // (-2*chem*((ux - uy)*(ux - uy)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + +// (-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)))); // // // q = 10 // dist[nr9] = m10 - (m10-feq10)/tau + 0.0625*(2*(Fx*(1 + ux) + Fy*(-1 + uy) + Fz*uz)* -// (-0.2222222222222222 - (ux - uy)*(ux - uy) + -// 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + +// (-0.2222222222222222 - (ux - uy)*(ux - uy) + +// 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + // (mgx*(1 + ux) + mgy*(-1 + uy) + mgz*uz)* // (-2*chem*((ux - uy)*(ux - uy)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + +// (-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)))); // // // q = 11 // dist[nr12] = m11 - (m11-feq11)/tau + 0.0625*(-2*(Fx*(-1 + ux) + Fy*uy + Fz*(-1 + uz))* -// (0.2222222222222222 + (ux + uz)*(ux + uz) - -// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + +// (0.2222222222222222 + (ux + uz)*(ux + uz) - +// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + // (mgx*(-1 + ux) + mgy*uy + mgz*(-1 + uz))* // (-2*chem*((ux + uz)*(ux + uz)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + +// (-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)))); // // // q = 12 @@ -3110,25 +5161,25 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // (-0.2222222222222222 - (ux + uz)*(ux + uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) // + (mgx + mgz + mgx*ux + mgy*uy + mgz*uz)* // (-2*chem*((ux + uz)*(ux + uz)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) + +// (-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*(2 + uz))))); // // // q = 13 // dist[nr14] = m13 - (m13-feq13)/tau + 0.0625*(2*(Fz + Fx*(-1 + ux) + Fy*uy + Fz*uz)* -// (-0.2222222222222222 - (ux - uz)*(ux - uz) + -// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + +// (-0.2222222222222222 - (ux - uz)*(ux - uz) + +// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + // (mgz + mgx*(-1 + ux) + mgy*uy + mgz*uz)* // (-2*chem*((ux - uz)*(ux - uz)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + +// (-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))))); // // // q= 14 // dist[nr13] = m14 - (m14-feq14)/tau + 0.0625*(2*(Fx*(1 + ux) + Fy*uy + Fz*(-1 + uz))* -// (-0.2222222222222222 - (ux - uz)*(ux - uz) + -// 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + +// (-0.2222222222222222 - (ux - uz)*(ux - uz) + +// 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + // (mgx*(1 + ux) + mgy*uy + mgz*(-1 + uz))* // (-2*chem*((ux - uz)*(ux - uz)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + +// (-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)))); // // // q = 15 @@ -3136,7 +5187,7 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // (0.2222222222222222 + (uy + uz)*(uy + uz) - 0.3333333333333333*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) // + (mgx*ux + mgy*(-1 + uy) + mgz*(-1 + uz))* // (-2*chem*((uy + uz)*(uy + uz)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + +// (-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)))); // // // q = 16 @@ -3144,7 +5195,7 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // (-0.2222222222222222 - (uy + uz)*(uy + uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) // + (mgy + mgz + mgx*ux + mgy*uy + mgz*uz)* // (-2*chem*((uy + uz)*(uy + uz)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) + +// (-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))))); // // // q = 17 @@ -3152,110 +5203,110 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // (-0.2222222222222222 - (uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) // + (mgz + mgx*ux + mgy*(-1 + uy) + mgz*uz)* // (-2*chem*((uy - uz)*(uy - uz)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) + +// (-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))))); // // // q = 18 // dist[nr17] = m18 - (m18-feq18)/tau + 0.0625*(2*(Fx*ux + Fy*(1 + uy) + Fz*(-1 + uz))* -// (-0.2222222222222222 - (uy - uz)*(uy - uz) + -// 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + +// (-0.2222222222222222 - (uy - uz)*(uy - uz) + +// 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + // (mgx*ux + mgy*(1 + uy) + mgz*(-1 + uz))* // (-2*chem*((uy - uz)*(uy - uz)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + +// (-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)))); // //----------------------------------------------------------------------------------------------------------------------------------------// // //// //------------------------------------------ BCK collison without mixed grad ------------------------------------------------------------// //// // q=0 -//// dist[n] = m0 - (m0-feq0)/tau + 0.25*(2*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) + -//// (nx*ux + ny*uy + nz*uz)*(2*chem*(ux*ux + uy*uy + uz*uz) + -//// 0.3333333333333333*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + uz*uz)))); +//// dist[n] = m0 - (m0-feq0)/tau + 0.25*(2*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) + +//// (nx*ux + ny*uy + nz*uz)*(2*chem*(ux*ux + uy*uy + uz*uz) + +//// 0.3333333333333333*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + uz*uz)))); //// //// // q = 1 -//// dist[nr2] = m1 - (m1-feq1)/tau + 0.125*(2*(Fx*(-1 + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + -//// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) + -//// (nx*(-1 + ux) + ny*uy + nz*uz)*(-2*chem*(ux*ux) + -//// 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*uz)) + +//// dist[nr2] = m1 - (m1-feq1)/tau + 0.125*(2*(Fx*(-1 + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + +//// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) + +//// (nx*(-1 + ux) + ny*uy + nz*uz)*(-2*chem*(ux*ux) + +//// 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*uz)))); //// //// // q=2 -//// dist[nr1] = m2 - (m2-feq2)/tau + 0.125*(2*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + -//// 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) + -//// (nx + nx*ux + ny*uy + nz*uz)*(-2*chem*(ux*ux) + -//// 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(2*ux + ux*ux + uy*uy + uz*uz)) + +//// dist[nr1] = m2 - (m2-feq2)/tau + 0.125*(2*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + +//// 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) + +//// (nx + nx*ux + ny*uy + nz*uz)*(-2*chem*(ux*ux) + +//// 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(2*ux + ux*ux + uy*uy + uz*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*uz)))); //// //// // q = 3 -//// dist[nr4] = m3 - (m3-feq3)/tau + 0.125*(2*(Fx*ux + Fy*(-1 + uy) + Fz*uz)*(-0.2222222222222222 - uy*uy + -//// 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) + -//// (nx*ux + ny*(-1 + uy) + nz*uz)*(-2*chem*(uy*uy) + -//// 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*uz)) + +//// dist[nr4] = m3 - (m3-feq3)/tau + 0.125*(2*(Fx*ux + Fy*(-1 + uy) + Fz*uz)*(-0.2222222222222222 - uy*uy + +//// 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) + +//// (nx*ux + ny*(-1 + uy) + nz*uz)*(-2*chem*(uy*uy) + +//// 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*uz)))); //// //// // q = 4 -//// dist[nr3] = m4 - (m4-feq4)/tau + 0.125*(2*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uy*uy + -//// 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) + -//// (ny + nx*ux + ny*uy + nz*uz)*(-2*chem*(uy*uy) + -//// 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*uz)) + +//// dist[nr3] = m4 - (m4-feq4)/tau + 0.125*(2*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uy*uy + +//// 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) + +//// (ny + nx*ux + ny*uy + nz*uz)*(-2*chem*(uy*uy) + +//// 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*uz)))); //// //// // q = 5 -//// dist[nr6] = m5 - (m5-feq5)/tau + 0.125*(2*(Fx*ux + Fy*uy + Fz*(-1 + uz))*(-0.2222222222222222 - uz*uz + -//// 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) + -//// (nx*ux + ny*uy + nz*(-1 + uz))*(-2*chem*(uz*uz) + -//// 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + (-2 + uz)*uz)) + +//// dist[nr6] = m5 - (m5-feq5)/tau + 0.125*(2*(Fx*ux + Fy*uy + Fz*(-1 + uz))*(-0.2222222222222222 - uz*uz + +//// 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) + +//// (nx*ux + ny*uy + nz*(-1 + uz))*(-2*chem*(uz*uz) + +//// 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + (-2 + uz)*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + (-2 + uz)*uz)))); //// //// // q = 6 -//// dist[nr5] = m6 - (m6-feq6)/tau + 0.125*(2*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uz*uz + -//// 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) + -//// (nz + nx*ux + ny*uy + nz*uz)*(-2*chem*(uz*uz) + -//// 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + uz*(2 + uz))) + +//// dist[nr5] = m6 - (m6-feq6)/tau + 0.125*(2*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uz*uz + +//// 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) + +//// (nz + nx*ux + ny*uy + nz*uz)*(-2*chem*(uz*uz) + +//// 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + uz*(2 + uz))) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + uz*(2 + uz))))); //// //// // q = 7 //// dist[nr8] = m7 - (m7-feq7)/tau + 0.0625*(-2*(Fx*(-1 + ux) + Fy*(-1 + uy) + Fz*uz)* -//// (0.2222222222222222 + (ux + uy)*(ux + uy) - -//// 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + +//// (0.2222222222222222 + (ux + uy)*(ux + uy) - +//// 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + //// (nx*(-1 + ux) + ny*(-1 + uy) + nz*uz)* //// (-2*chem*((ux + uy)*(ux + uy)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + +//// (-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)))); //// //// // q = 8 //// dist[nr7] = m8 - (m8-feq8)/tau + 0.0625*(2*(Fx + Fy + Fx*ux + Fy*uy + Fz*uz)* -//// (-0.2222222222222222 - (ux + uy)*(ux + uy) + -//// 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + +//// (-0.2222222222222222 - (ux + uy)*(ux + uy) + +//// 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + //// (nx + ny + nx*ux + ny*uy + nz*uz)* //// (-2*chem*((ux + uy)*(ux + uy)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + +//// (-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)))); //// //// // q = 9 //// dist[nr10] = m9 - (m9-feq9)/tau + 0.0625*(2*(Fy + Fx*(-1 + ux) + Fy*uy + Fz*uz)* -//// (-0.2222222222222222 - (ux - uy)*(ux - uy) + -//// 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + +//// (-0.2222222222222222 - (ux - uy)*(ux - uy) + +//// 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + //// (ny + nx*(-1 + ux) + ny*uy + nz*uz)* //// (-2*chem*((ux - uy)*(ux - uy)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + +//// (-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)))); //// //// // q = 10 //// dist[nr9] = m10 - (m10-feq10)/tau + 0.0625*(2*(Fx*(1 + ux) + Fy*(-1 + uy) + Fz*uz)* -//// (-0.2222222222222222 - (ux - uy)*(ux - uy) + -//// 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + +//// (-0.2222222222222222 - (ux - uy)*(ux - uy) + +//// 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + //// (nx*(1 + ux) + ny*(-1 + uy) + nz*uz)* //// (-2*chem*((ux - uy)*(ux - uy)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + +//// (-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)))); //// //// // q = 11 //// dist[nr12] = m11 - (m11-feq11)/tau + 0.0625*(-2*(Fx*(-1 + ux) + Fy*uy + Fz*(-1 + uz))* -//// (0.2222222222222222 + (ux + uz)*(ux + uz) - -//// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + +//// (0.2222222222222222 + (ux + uz)*(ux + uz) - +//// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + //// (nx*(-1 + ux) + ny*uy + nz*(-1 + uz))* //// (-2*chem*((ux + uz)*(ux + uz)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + +//// (-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)))); //// //// // q = 12 @@ -3263,25 +5314,25 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist //// (-0.2222222222222222 - (ux + uz)*(ux + uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) //// + (nx + nz + nx*ux + ny*uy + nz*uz)* //// (-2*chem*((ux + uz)*(ux + uz)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) + +//// (-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*(2 + uz))))); //// //// // q = 13 //// dist[nr14] = m13 - (m13-feq13)/tau + 0.0625*(2*(Fz + Fx*(-1 + ux) + Fy*uy + Fz*uz)* -//// (-0.2222222222222222 - (ux - uz)*(ux - uz) + -//// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + +//// (-0.2222222222222222 - (ux - uz)*(ux - uz) + +//// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + //// (nz + nx*(-1 + ux) + ny*uy + nz*uz)* //// (-2*chem*((ux - uz)*(ux - uz)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + +//// (-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))))); //// //// // q= 14 //// dist[nr13] = m14 - (m14-feq14)/tau + 0.0625*(2*(Fx*(1 + ux) + Fy*uy + Fz*(-1 + uz))* -//// (-0.2222222222222222 - (ux - uz)*(ux - uz) + -//// 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + +//// (-0.2222222222222222 - (ux - uz)*(ux - uz) + +//// 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + //// (nx*(1 + ux) + ny*uy + nz*(-1 + uz))* //// (-2*chem*((ux - uz)*(ux - uz)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + +//// (-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)))); //// //// // q = 15 @@ -3289,7 +5340,7 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist //// (0.2222222222222222 + (uy + uz)*(uy + uz) - 0.3333333333333333*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) //// + (nx*ux + ny*(-1 + uy) + nz*(-1 + uz))* //// (-2*chem*((uy + uz)*(uy + uz)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + +//// (-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)))); //// //// // q = 16 @@ -3297,7 +5348,7 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist //// (-0.2222222222222222 - (uy + uz)*(uy + uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) //// + (ny + nz + nx*ux + ny*uy + nz*uz)* //// (-2*chem*((uy + uz)*(uy + uz)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) + +//// (-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))))); //// //// // q = 17 @@ -3305,16 +5356,16 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist //// (-0.2222222222222222 - (uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) //// + (nz + nx*ux + ny*(-1 + uy) + nz*uz)* //// (-2*chem*((uy - uz)*(uy - uz)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) + +//// (-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))))); //// //// // q = 18 //// dist[nr17] = m18 - (m18-feq18)/tau + 0.0625*(2*(Fx*ux + Fy*(1 + uy) + Fz*(-1 + uz))* -//// (-0.2222222222222222 - (uy - uz)*(uy - uz) + -//// 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + +//// (-0.2222222222222222 - (uy - uz)*(uy - uz) + +//// 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + //// (nx*ux + ny*(1 + uy) + nz*(-1 + uz))* //// (-2*chem*((uy - uz)*(uy - uz)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + +//// (-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)))); //// //----------------------------------------------------------------------------------------------------------------------------------------// // @@ -3325,7 +5376,7 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // if (C==0.0) ColorMag=1.0; // nx = nx/ColorMag; // ny = ny/ColorMag; -// nz = nz/ColorMag; +// nz = nz/ColorMag; // //compute surface tension-related parameter // //theta = 4.5*M*2.0*(1-phi*phi)/W; // theta = 4.5*M*2.0*(1-phi_temp*phi_temp)/W; @@ -3334,10 +5385,10 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // //q=0 // h0 = hq[n]; // //q=1 -// h1 = hq[nr1]; +// h1 = hq[nr1]; // // //q=2 -// h2 = hq[nr2]; +// h2 = hq[nr2]; // // //q=3 // h3 = hq[nr3]; @@ -3391,11 +5442,11 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist //} //extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist, double *hq, double *Den, double *Phi, double *mu_phi, double *Vel, double *Pressure, double *ColorGrad, -// double rhoA, double rhoB, double tauA, double tauB, double tauM, double kappa, double beta, double W, double Fx, double Fy, double Fz, +// double rhoA, double rhoB, double tauA, double tauB, double tauM, double kappa, double beta, double W, double Fx, double Fy, double Fz, // int strideY, int strideZ, int start, int finish, int Np){ -// +// // int n,nn,nn2x,ijk; -// double ux,uy,uz;//fluid velocity +// double ux,uy,uz;//fluid velocity // double p;//pressure // double chem;//chemical potential // double phi; //phase field @@ -3575,7 +5626,7 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // mgx = -3.0*1.0/18.0*(mm1-mm2+0.5*(mm7-mm8+mm9-mm10+mm11-mm12+mm13-mm14)); // mgy = -3.0*1.0/18.0*(mm3-mm4+0.5*(mm7-mm8-mm9+mm10+mm15-mm16+mm17-mm18)); // mgz = -3.0*1.0/18.0*(mm5-mm6+0.5*(mm11-mm12-mm13+mm14+mm15-mm16-mm17+mm18)); -// +// // //de-noise color gradient and mixed gradient // C = sqrt(nx*nx+ny*ny+nz*nz); // if (C<1.0e-12) nx=ny=nz=0.0; @@ -3587,10 +5638,10 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // // q=0 // m0 = dist[n]; // // q=1 -// m1 = dist[2*Np+n]; +// m1 = dist[2*Np+n]; // // // q=2 -// m2 = dist[1*Np+n]; +// m2 = dist[1*Np+n]; // // // q=3 // m3 = dist[4*Np+n]; @@ -3603,7 +5654,7 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // // // q = 6 // m6 = dist[5*Np+n]; -// +// // // q=7 // m7 = dist[8*Np+n]; // @@ -3649,216 +5700,216 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // +0.5*(rhoA-rhoB)/2.0/3.0*(ux*nx+uy*ny+uz*nz); // // //compute equilibrium distributions -// feq0 = 0.3333333333333333*p - 0.25*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) - +// feq0 = 0.3333333333333333*p - 0.25*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) - // 0.16666666666666666*rho0*(ux*ux + uy*uy + uz*uz) - 0.5*(-(nx*ux) - ny*uy - nz*uz)* // (-0.08333333333333333*(rhoA - rhoB)*(ux*ux + uy*uy + uz*uz) + chem*(0.3333333333333333 - 0.5*(ux*ux + uy*uy + uz*uz))); -// feq1 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-ux*ux + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) - -// 0.125*(Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + +// feq1 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-ux*ux + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) - +// 0.125*(Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + // 0.3333333333333333*(-2.*ux + ux*ux + uy*uy + uz*uz)) - 0.0625*(nx - nx*ux - ny*uy - nz*uz)* -// (2*chem*ux*ux - 0.3333333333333333*((-rhoA + rhoB)*ux*ux + 2*chem*(-2*ux + ux*ux + uy*uy + uz*uz)) + +// (2*chem*ux*ux - 0.3333333333333333*((-rhoA + rhoB)*ux*ux + 2*chem*(-2*ux + ux*ux + uy*uy + uz*uz)) + // 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*uz))); -// feq2 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-ux*ux + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) - -// 0.125*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + +// feq2 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-ux*ux + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) - +// 0.125*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + // 0.3333333333333333*(2.*ux + ux*ux + uy*uy + uz*uz)) - 0.0625*(nx + nx*ux + ny*uy + nz*uz)* -// (-2.*chem*ux*ux + 0.1111111111111111*(-4.*chem + rhoB*(-2.*ux - 1.*ux*ux - 1.*uy*uy - 1.*uz*uz) + -// rhoA*(2.*ux + ux*ux + uy*uy + uz*uz)) + 0.3333333333333333*((-1.*rhoA + rhoB)*ux*ux + +// (-2.*chem*ux*ux + 0.1111111111111111*(-4.*chem + rhoB*(-2.*ux - 1.*ux*ux - 1.*uy*uy - 1.*uz*uz) + +// rhoA*(2.*ux + ux*ux + uy*uy + uz*uz)) + 0.3333333333333333*((-1.*rhoA + rhoB)*ux*ux + // chem*(4.*ux + 2.*ux*ux + 2.*uy*uy + 2.*uz*uz))); -// feq3 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uy*uy + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) - -// 0.125*(Fx*ux + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy + +// feq3 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uy*uy + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) - +// 0.125*(Fx*ux + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy + // 0.3333333333333333*(ux*ux - 2.*uy + uy*uy + uz*uz)) - 0.0625*(ny - nx*ux - ny*uy - nz*uz)* -// (2*chem*uy*uy - 0.3333333333333333*((-rhoA + rhoB)*uy*uy + 2*chem*(ux*ux - 2*uy + uy*uy + uz*uz)) + +// (2*chem*uy*uy - 0.3333333333333333*((-rhoA + rhoB)*uy*uy + 2*chem*(ux*ux - 2*uy + uy*uy + uz*uz)) + // 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*uz))); -// feq4 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uy*uy + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) - -// 0.125*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy + +// feq4 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uy*uy + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) - +// 0.125*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy + // 0.3333333333333333*(ux*ux + 2.*uy + uy*uy + uz*uz)) - 0.0625*(ny + nx*ux + ny*uy + nz*uz)* -// (-2.*chem*uy*uy + 0.1111111111111111*(-4.*chem + rhoB*(-1.*ux*ux - 2.*uy - 1.*uy*uy - 1.*uz*uz) + -// rhoA*(ux*ux + 2.*uy + uy*uy + uz*uz)) + 0.3333333333333333*((-1.*rhoA + rhoB)*uy*uy + -// chem*(2.*ux*ux + 4.*uy + 2.*uy*uy + 2.*uz*uz))); -// feq5 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uz*uz + 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) - -// 0.125*(Fx*ux + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*uz*uz + +// (-2.*chem*uy*uy + 0.1111111111111111*(-4.*chem + rhoB*(-1.*ux*ux - 2.*uy - 1.*uy*uy - 1.*uz*uz) + +// rhoA*(ux*ux + 2.*uy + uy*uy + uz*uz)) + 0.3333333333333333*((-1.*rhoA + rhoB)*uy*uy + +// chem*(2.*ux*ux + 4.*uy + 2.*uy*uy + 2.*uz*uz))); +// feq5 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uz*uz + 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) - +// 0.125*(Fx*ux + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*uz*uz + // 0.3333333333333333*(ux*ux + uy*uy + (-2. + uz)*uz)) - 0.0625*(nx*ux + ny*uy + nz*(-1. + uz))* -// (-2.*chem*uz*uz + 0.1111111111111111*(-4.*chem + rhoB*(-1.*ux*ux - 1.*uy*uy + (2. - 1.*uz)*uz) + -// rhoA*(ux*ux + uy*uy + (-2. + uz)*uz)) + 0.3333333333333333*((-1.*rhoA + rhoB)*uz*uz + -// chem*(2.*ux*ux + 2.*uy*uy + uz*(-4. + 2.*uz)))); -// feq6 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uz*uz + 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) - -// 0.125*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*uz*uz + +// (-2.*chem*uz*uz + 0.1111111111111111*(-4.*chem + rhoB*(-1.*ux*ux - 1.*uy*uy + (2. - 1.*uz)*uz) + +// rhoA*(ux*ux + uy*uy + (-2. + uz)*uz)) + 0.3333333333333333*((-1.*rhoA + rhoB)*uz*uz + +// chem*(2.*ux*ux + 2.*uy*uy + uz*(-4. + 2.*uz)))); +// feq6 = 0.05555555555555555*p - 0.08333333333333333*rho0*(-uz*uz + 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) - +// 0.125*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*uz*uz + // 0.3333333333333333*(ux*ux + uy*uy + uz*(2. + uz))) - 0.0625*(nz + nx*ux + ny*uy + nz*uz)* -// (-2.*chem*uz*uz + 0.1111111111111111*(-4.*chem + rhoB*(-1.*ux*ux - 1.*uy*uy + (-2. - 1.*uz)*uz) + -// rhoA*(ux*ux + uy*uy + uz*(2. + uz))) + 0.3333333333333333*((-1.*rhoA + rhoB)*uz*uz + -// chem*(2.*ux*ux + 2.*uy*uy + uz*(4. + 2.*uz)))); +// (-2.*chem*uz*uz + 0.1111111111111111*(-4.*chem + rhoB*(-1.*ux*ux - 1.*uy*uy + (-2. - 1.*uz)*uz) + +// rhoA*(ux*ux + uy*uy + uz*(2. + uz))) + 0.3333333333333333*((-1.*rhoA + rhoB)*uz*uz + +// chem*(2.*ux*ux + 2.*uy*uy + uz*(4. + 2.*uz)))); // feq7 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(ux + uy)*(ux + uy) + 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) - -// 0.0625*(Fx*(-1. + ux) + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uy - 1.*uy*uy + +// (-(ux + uy)*(ux + uy) + 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) - +// 0.0625*(Fx*(-1. + ux) + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uy - 1.*uy*uy + // 0.3333333333333333*(-2.*ux + ux*ux - 2.*uy + uy*uy + uz*uz)) - 0.03125*(nx + ny - nx*ux - ny*uy - nz*uz)* -// (2*chem*(ux + uy)*(ux + uy) + 0.3333333333333333*((rhoA - rhoB)*(ux + uy)*(ux + uy) - 2*chem*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + -// 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz))); +// (2*chem*(ux + uy)*(ux + uy) + 0.3333333333333333*((rhoA - rhoB)*(ux + uy)*(ux + uy) - 2*chem*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + +// 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz))); // feq8 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(ux + uy)*(ux + uy) + 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - -// 0.0625*(Fx + Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uy - 1.*uy*uy + +// (-(ux + uy)*(ux + uy) + 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - +// 0.0625*(Fx + Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uy - 1.*uy*uy + // 0.3333333333333333*(2.*ux + ux*ux + 2.*uy + uy*uy + uz*uz)) - 0.03125*(-(nx*(1 + ux)) - ny*(1 + uy) - nz*uz)* -// (2*chem*(ux + uy)*(ux + uy) - 0.3333333333333333*(-((rhoA - rhoB)*(ux + uy)*(ux + uy)) + +// (2*chem*(ux + uy)*(ux + uy) - 0.3333333333333333*(-((rhoA - rhoB)*(ux + uy)*(ux + uy)) + // 2*chem*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + 0.1111111111111111* -// (4*chem - (rhoA - rhoB)*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz))); +// (4*chem - (rhoA - rhoB)*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz))); // feq9 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(ux - uy)*(ux - uy) + 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - -// 0.0625*(Fy + Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uy - 1.*uy*uy + +// (-(ux - uy)*(ux - uy) + 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - +// 0.0625*(Fy + Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uy - 1.*uy*uy + // 0.3333333333333333*(-2.*ux + ux*ux + 2.*uy + uy*uy + uz*uz)) - 0.03125*(nx - nx*ux - ny*(1 + uy) - nz*uz)* -// (2*chem*(ux - uy)*(ux - uy) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uy)*(ux - uy)) + +// (2*chem*(ux - uy)*(ux - uy) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uy)*(ux - uy)) + // 2*chem*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + 0.1111111111111111* -// (4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz))); +// (4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz))); // feq10 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(ux - uy)*(ux - uy) + 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) - -// 0.0625*(Fx*(1 + ux) + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uy - 1.*uy*uy + +// (-(ux - uy)*(ux - uy) + 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) - +// 0.0625*(Fx*(1 + ux) + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uy - 1.*uy*uy + // 0.3333333333333333*(2.*ux + ux*ux - 2.*uy + uy*uy + uz*uz)) - 0.03125*(ny - nx*(1 + ux) - ny*uy - nz*uz)* -// (2*chem*(ux - uy)*(ux - uy) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uy)*(ux - uy)) + +// (2*chem*(ux - uy)*(ux - uy) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uy)*(ux - uy)) + // 2*chem*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + 0.1111111111111111* -// (4*chem - (rhoA - rhoB)*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz))); +// (4*chem - (rhoA - rhoB)*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz))); // feq11 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(ux + uz)*(ux + uz) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) - -// 0.0625*(Fx*(-1. + ux) + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uz - 1.*uz*uz + +// (-(ux + uz)*(ux + uz) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) - +// 0.0625*(Fx*(-1. + ux) + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uz - 1.*uz*uz + // 0.3333333333333333*(-2.*ux + ux*ux + uy*uy + (-2. + uz)*uz)) - 0.03125*(nx + nz - nx*ux - ny*uy - nz*uz)* -// (2*chem*(ux + uz)*(ux + uz) + 0.3333333333333333*((rhoA - rhoB)*(ux + uz)*(ux + uz) - 2*chem*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + -// 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz))); +// (2*chem*(ux + uz)*(ux + uz) + 0.3333333333333333*((rhoA - rhoB)*(ux + uz)*(ux + uz) - 2*chem*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + +// 0.1111111111111111*(4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz))); // feq12 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(ux + uz)*(ux + uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) - -// 0.0625*(Fx + Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uz - 1.*uz*uz + +// (-(ux + uz)*(ux + uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) - +// 0.0625*(Fx + Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux - 2.*ux*uz - 1.*uz*uz + // 0.3333333333333333*(2.*ux + ux*ux + uy*uy + uz*(2. + uz))) - 0.03125*(-(nx*(1 + ux)) - ny*uy - nz*(1 + uz))* -// (2*chem*(ux + uz)*(ux + uz) - 0.3333333333333333*(-((rhoA - rhoB)*(ux + uz)*(ux + uz)) + +// (2*chem*(ux + uz)*(ux + uz) - 0.3333333333333333*(-((rhoA - rhoB)*(ux + uz)*(ux + uz)) + // 2*chem*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) + 0.1111111111111111* -// (4*chem - (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*(2 + uz)))); +// (4*chem - (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*(2 + uz)))); // feq13 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(ux - uz)*(ux - uz) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) - -// 0.0625*(Fz + Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uz - 1.*uz*uz + +// (-(ux - uz)*(ux - uz) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) - +// 0.0625*(Fz + Fx*(-1. + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uz - 1.*uz*uz + // 0.3333333333333333*(-2.*ux + ux*ux + uy*uy + uz*(2. + uz))) - 0.03125*(nx - nx*ux - ny*uy - nz*(1 + uz))* -// (2*chem*(ux - uz)*(ux - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uz)*(ux - uz)) + +// (2*chem*(ux - uz)*(ux - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uz)*(ux - uz)) + // 2*chem*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + 0.1111111111111111* -// (4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*(2 + uz)))); +// (4*chem - (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*(2 + uz)))); // feq14 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(ux - uz)*(ux - uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) - -// 0.0625*(Fx*(1 + ux) + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uz - 1.*uz*uz + +// (-(ux - uz)*(ux - uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) - +// 0.0625*(Fx*(1 + ux) + Fy*uy + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*ux*ux + 2.*ux*uz - 1.*uz*uz + // 0.3333333333333333*(2.*ux + ux*ux + uy*uy + (-2. + uz)*uz)) - 0.03125*(nz - nx*(1 + ux) - ny*uy - nz*uz)* -// (2*chem*(ux - uz)*(ux - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uz)*(ux - uz)) + +// (2*chem*(ux - uz)*(ux - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(ux - uz)*(ux - uz)) + // 2*chem*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + 0.1111111111111111* -// (4*chem - (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz))); +// (4*chem - (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz))); // feq15 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(uy + uz)*(uy + uz) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) - -// 0.0625*(Fx*ux + Fy*(-1. + uy) + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*uy*uy - 2.*uy*uz - 1.*uz*uz + +// (-(uy + uz)*(uy + uz) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) - +// 0.0625*(Fx*ux + Fy*(-1. + uy) + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*uy*uy - 2.*uy*uz - 1.*uz*uz + // 0.3333333333333333*(ux*ux - 2.*uy + uy*uy + (-2. + uz)*uz)) - 0.03125*(ny + nz - nx*ux - ny*uy - nz*uz)* -// (2*chem*(uy + uz)*(uy + uz) + 0.3333333333333333*((rhoA - rhoB)*(uy + uz)*(uy + uz) - 2*chem*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + -// 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz))); +// (2*chem*(uy + uz)*(uy + uz) + 0.3333333333333333*((rhoA - rhoB)*(uy + uz)*(uy + uz) - 2*chem*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + +// 0.1111111111111111*(4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz))); // feq16 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(uy + uz)*(uy + uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) - -// 0.0625*(Fy + Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy - 2.*uy*uz - 1.*uz*uz + +// (-(uy + uz)*(uy + uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) - +// 0.0625*(Fy + Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy - 2.*uy*uz - 1.*uz*uz + // 0.3333333333333333*(ux*ux + 2.*uy + uy*uy + uz*(2. + uz))) - 0.03125*(-(nx*ux) - ny*(1 + uy) - nz*(1 + uz))* -// (2*chem*(uy + uz)*(uy + uz) - 0.3333333333333333*(-((rhoA - rhoB)*(uy + uz)*(uy + uz)) + +// (2*chem*(uy + uz)*(uy + uz) - 0.3333333333333333*(-((rhoA - rhoB)*(uy + uz)*(uy + uz)) + // 2*chem*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) + 0.1111111111111111* -// (4*chem - (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*(2 + uz)))); +// (4*chem - (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*(2 + uz)))); // feq17 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) - -// 0.0625*(Fz + Fx*ux + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy + 2.*uy*uz - 1.*uz*uz + +// (-(uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) - +// 0.0625*(Fz + Fx*ux + Fy*(-1. + uy) + Fz*uz)*(-0.2222222222222222 - 1.*uy*uy + 2.*uy*uz - 1.*uz*uz + // 0.3333333333333333*(ux*ux - 2.*uy + uy*uy + uz*(2. + uz))) - 0.03125*(ny - nx*ux - ny*uy - nz*(1 + uz))* -// (2*chem*(uy - uz)*(uy - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(uy - uz)*(uy - uz)) + +// (2*chem*(uy - uz)*(uy - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(uy - uz)*(uy - uz)) + // 2*chem*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) + 0.1111111111111111* -// (4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*(2 + uz)))); +// (4*chem - (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*(2 + uz)))); // feq18 = 0.027777777777777776*p - 0.041666666666666664*rho0* -// (-(uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) - -// 0.0625*(Fx*ux + Fy*(1 + uy) + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*uy*uy + 2.*uy*uz - 1.*uz*uz + +// (-(uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) - +// 0.0625*(Fx*ux + Fy*(1 + uy) + Fz*(-1. + uz))*(-0.2222222222222222 - 1.*uy*uy + 2.*uy*uz - 1.*uz*uz + // 0.3333333333333333*(ux*ux + 2.*uy + uy*uy + (-2. + uz)*uz)) - 0.03125*(nz - nx*ux - ny*(1 + uy) - nz*uz)* -// (2*chem*(uy - uz)*(uy - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(uy - uz)*(uy - uz)) + +// (2*chem*(uy - uz)*(uy - uz) - 0.3333333333333333*(-((rhoA - rhoB)*(uy - uz)*(uy - uz)) + // 2*chem*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + 0.1111111111111111* -// (4*chem - (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz))); +// (4*chem - (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz))); // // //------------------------------------------------- BCK collison ------------------------------------------------------------// // // q=0 -// dist[n] = m0 - (m0-feq0)/tau + 0.25*(2*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) + -// (mgx*ux + mgy*uy + mgz*uz)*(2*chem*(ux*ux + uy*uy + uz*uz) + +// dist[n] = m0 - (m0-feq0)/tau + 0.25*(2*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) + +// (mgx*ux + mgy*uy + mgz*uz)*(2*chem*(ux*ux + uy*uy + uz*uz) + // 0.3333333333333333*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + uz*uz)))); // // // q = 1 -// dist[1*Np+n] = m1 - (m1-feq1)/tau + 0.125*(2*(Fx*(-1 + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + -// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) + -// (mgx*(-1 + ux) + mgy*uy + mgz*uz)*(-2*chem*(ux*ux) + -// 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*uz)) + +// dist[1*Np+n] = m1 - (m1-feq1)/tau + 0.125*(2*(Fx*(-1 + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + +// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) + +// (mgx*(-1 + ux) + mgy*uy + mgz*uz)*(-2*chem*(ux*ux) + +// 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*uz)))); // // // q=2 -// dist[2*Np+n] = m2 - (m2-feq2)/tau + 0.125*(2*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + -// 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) + -// (mgx + mgx*ux + mgy*uy + mgz*uz)*(-2*chem*(ux*ux) + -// 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(2*ux + ux*ux + uy*uy + uz*uz)) + +// dist[2*Np+n] = m2 - (m2-feq2)/tau + 0.125*(2*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + +// 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) + +// (mgx + mgx*ux + mgy*uy + mgz*uz)*(-2*chem*(ux*ux) + +// 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(2*ux + ux*ux + uy*uy + uz*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*uz)))); // // // q = 3 -// dist[3*Np+n] = m3 - (m3-feq3)/tau + 0.125*(2*(Fx*ux + Fy*(-1 + uy) + Fz*uz)*(-0.2222222222222222 - uy*uy + -// 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) + -// (mgx*ux + mgy*(-1 + uy) + mgz*uz)*(-2*chem*(uy*uy) + -// 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*uz)) + +// dist[3*Np+n] = m3 - (m3-feq3)/tau + 0.125*(2*(Fx*ux + Fy*(-1 + uy) + Fz*uz)*(-0.2222222222222222 - uy*uy + +// 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) + +// (mgx*ux + mgy*(-1 + uy) + mgz*uz)*(-2*chem*(uy*uy) + +// 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*uz)))); // // // q = 4 -// dist[4*Np+n] = m4 - (m4-feq4)/tau + 0.125*(2*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uy*uy + -// 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) + -// (mgy + mgx*ux + mgy*uy + mgz*uz)*(-2*chem*(uy*uy) + -// 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*uz)) + +// dist[4*Np+n] = m4 - (m4-feq4)/tau + 0.125*(2*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uy*uy + +// 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) + +// (mgy + mgx*ux + mgy*uy + mgz*uz)*(-2*chem*(uy*uy) + +// 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*uz)))); // // // q = 5 -// dist[5*Np+n] = m5 - (m5-feq5)/tau + 0.125*(2*(Fx*ux + Fy*uy + Fz*(-1 + uz))*(-0.2222222222222222 - uz*uz + -// 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) + -// (mgx*ux + mgy*uy + mgz*(-1 + uz))*(-2*chem*(uz*uz) + -// 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + (-2 + uz)*uz)) + +// dist[5*Np+n] = m5 - (m5-feq5)/tau + 0.125*(2*(Fx*ux + Fy*uy + Fz*(-1 + uz))*(-0.2222222222222222 - uz*uz + +// 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) + +// (mgx*ux + mgy*uy + mgz*(-1 + uz))*(-2*chem*(uz*uz) + +// 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + (-2 + uz)*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + (-2 + uz)*uz)))); // // // q = 6 -// dist[6*Np+n] = m6 - (m6-feq6)/tau + 0.125*(2*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uz*uz + -// 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) + -// (mgz + mgx*ux + mgy*uy + mgz*uz)*(-2*chem*(uz*uz) + -// 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + uz*(2 + uz))) + +// dist[6*Np+n] = m6 - (m6-feq6)/tau + 0.125*(2*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uz*uz + +// 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) + +// (mgz + mgx*ux + mgy*uy + mgz*uz)*(-2*chem*(uz*uz) + +// 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + uz*(2 + uz))) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + uz*(2 + uz))))); // // // q = 7 // dist[7*Np+n] = m7 - (m7-feq7)/tau + 0.0625*(-2*(Fx*(-1 + ux) + Fy*(-1 + uy) + Fz*uz)* -// (0.2222222222222222 + (ux + uy)*(ux + uy) - -// 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + +// (0.2222222222222222 + (ux + uy)*(ux + uy) - +// 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + // (mgx*(-1 + ux) + mgy*(-1 + uy) + mgz*uz)* // (-2*chem*((ux + uy)*(ux + uy)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + +// (-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)))); // // // q = 8 // dist[8*Np+n] = m8 - (m8-feq8)/tau + 0.0625*(2*(Fx + Fy + Fx*ux + Fy*uy + Fz*uz)* -// (-0.2222222222222222 - (ux + uy)*(ux + uy) + -// 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + +// (-0.2222222222222222 - (ux + uy)*(ux + uy) + +// 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + // (mgx + mgy + mgx*ux + mgy*uy + mgz*uz)* // (-2*chem*((ux + uy)*(ux + uy)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + +// (-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)))); // // // q = 9 // dist[9*Np+n] = m9 - (m9-feq9)/tau + 0.0625*(2*(Fy + Fx*(-1 + ux) + Fy*uy + Fz*uz)* -// (-0.2222222222222222 - (ux - uy)*(ux - uy) + -// 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + +// (-0.2222222222222222 - (ux - uy)*(ux - uy) + +// 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + // (mgy + mgx*(-1 + ux) + mgy*uy + mgz*uz)* // (-2*chem*((ux - uy)*(ux - uy)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + +// (-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)))); // // // q = 10 // dist[10*Np+n] = m10 - (m10-feq10)/tau + 0.0625*(2*(Fx*(1 + ux) + Fy*(-1 + uy) + Fz*uz)* -// (-0.2222222222222222 - (ux - uy)*(ux - uy) + -// 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + +// (-0.2222222222222222 - (ux - uy)*(ux - uy) + +// 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + // (mgx*(1 + ux) + mgy*(-1 + uy) + mgz*uz)* // (-2*chem*((ux - uy)*(ux - uy)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + +// (-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)))); // // // q = 11 // dist[11*Np+n] = m11 - (m11-feq11)/tau + 0.0625*(-2*(Fx*(-1 + ux) + Fy*uy + Fz*(-1 + uz))* -// (0.2222222222222222 + (ux + uz)*(ux + uz) - -// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + +// (0.2222222222222222 + (ux + uz)*(ux + uz) - +// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + // (mgx*(-1 + ux) + mgy*uy + mgz*(-1 + uz))* // (-2*chem*((ux + uz)*(ux + uz)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + +// (-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)))); // // // q = 12 @@ -3866,25 +5917,25 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // (-0.2222222222222222 - (ux + uz)*(ux + uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) // + (mgx + mgz + mgx*ux + mgy*uy + mgz*uz)* // (-2*chem*((ux + uz)*(ux + uz)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) + +// (-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*(2 + uz))))); // // // q = 13 // dist[13*Np+n] = m13 - (m13-feq13)/tau + 0.0625*(2*(Fz + Fx*(-1 + ux) + Fy*uy + Fz*uz)* -// (-0.2222222222222222 - (ux - uz)*(ux - uz) + -// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + +// (-0.2222222222222222 - (ux - uz)*(ux - uz) + +// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + // (mgz + mgx*(-1 + ux) + mgy*uy + mgz*uz)* // (-2*chem*((ux - uz)*(ux - uz)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + +// (-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))))); // // // q= 14 // dist[14*Np+n] = m14 - (m14-feq14)/tau + 0.0625*(2*(Fx*(1 + ux) + Fy*uy + Fz*(-1 + uz))* -// (-0.2222222222222222 - (ux - uz)*(ux - uz) + -// 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + +// (-0.2222222222222222 - (ux - uz)*(ux - uz) + +// 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + // (mgx*(1 + ux) + mgy*uy + mgz*(-1 + uz))* // (-2*chem*((ux - uz)*(ux - uz)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + +// (-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)))); // // // q = 15 @@ -3892,7 +5943,7 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // (0.2222222222222222 + (uy + uz)*(uy + uz) - 0.3333333333333333*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) // + (mgx*ux + mgy*(-1 + uy) + mgz*(-1 + uz))* // (-2*chem*((uy + uz)*(uy + uz)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + +// (-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)))); // // // q = 16 @@ -3900,7 +5951,7 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // (-0.2222222222222222 - (uy + uz)*(uy + uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) // + (mgy + mgz + mgx*ux + mgy*uy + mgz*uz)* // (-2*chem*((uy + uz)*(uy + uz)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) + +// (-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))))); // // // q = 17 @@ -3908,110 +5959,110 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // (-0.2222222222222222 - (uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) // + (mgz + mgx*ux + mgy*(-1 + uy) + mgz*uz)* // (-2*chem*((uy - uz)*(uy - uz)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) + +// (-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))))); // // // q = 18 // dist[18*Np+n] = m18 - (m18-feq18)/tau + 0.0625*(2*(Fx*ux + Fy*(1 + uy) + Fz*(-1 + uz))* -// (-0.2222222222222222 - (uy - uz)*(uy - uz) + -// 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + +// (-0.2222222222222222 - (uy - uz)*(uy - uz) + +// 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + // (mgx*ux + mgy*(1 + uy) + mgz*(-1 + uz))* // (-2*chem*((uy - uz)*(uy - uz)) + 0.3333333333333333* -// (-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + +// (-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + // 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)))); // //----------------------------------------------------------------------------------------------------------------------------------------// // //// //----------------------------------------- BCK collison without mixed grad ------------------------------------------------------------// //// // q=0 -//// dist[n] = m0 - (m0-feq0)/tau + 0.25*(2*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) + -//// (nx*ux + ny*uy + nz*uz)*(2*chem*(ux*ux + uy*uy + uz*uz) + +//// dist[n] = m0 - (m0-feq0)/tau + 0.25*(2*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) + +//// (nx*ux + ny*uy + nz*uz)*(2*chem*(ux*ux + uy*uy + uz*uz) + //// 0.3333333333333333*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + uz*uz)))); //// //// // q = 1 -//// dist[1*Np+n] = m1 - (m1-feq1)/tau + 0.125*(2*(Fx*(-1 + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + -//// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) + -//// (nx*(-1 + ux) + ny*uy + nz*uz)*(-2*chem*(ux*ux) + -//// 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*uz)) + +//// dist[1*Np+n] = m1 - (m1-feq1)/tau + 0.125*(2*(Fx*(-1 + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + +//// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) + +//// (nx*(-1 + ux) + ny*uy + nz*uz)*(-2*chem*(ux*ux) + +//// 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*uz)))); //// //// // q=2 -//// dist[2*Np+n] = m2 - (m2-feq2)/tau + 0.125*(2*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + -//// 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) + -//// (nx + nx*ux + ny*uy + nz*uz)*(-2*chem*(ux*ux) + -//// 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(2*ux + ux*ux + uy*uy + uz*uz)) + +//// dist[2*Np+n] = m2 - (m2-feq2)/tau + 0.125*(2*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + +//// 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) + +//// (nx + nx*ux + ny*uy + nz*uz)*(-2*chem*(ux*ux) + +//// 0.3333333333333333*((-rhoA + rhoB)*(ux*ux) + 2*chem*(2*ux + ux*ux + uy*uy + uz*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*uz)))); //// //// // q = 3 -//// dist[3*Np+n] = m3 - (m3-feq3)/tau + 0.125*(2*(Fx*ux + Fy*(-1 + uy) + Fz*uz)*(-0.2222222222222222 - uy*uy + -//// 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) + -//// (nx*ux + ny*(-1 + uy) + nz*uz)*(-2*chem*(uy*uy) + -//// 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*uz)) + +//// dist[3*Np+n] = m3 - (m3-feq3)/tau + 0.125*(2*(Fx*ux + Fy*(-1 + uy) + Fz*uz)*(-0.2222222222222222 - uy*uy + +//// 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) + +//// (nx*ux + ny*(-1 + uy) + nz*uz)*(-2*chem*(uy*uy) + +//// 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*uz)))); //// //// // q = 4 -//// dist[4*Np+n] = m4 - (m4-feq4)/tau + 0.125*(2*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uy*uy + -//// 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) + -//// (ny + nx*ux + ny*uy + nz*uz)*(-2*chem*(uy*uy) + -//// 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*uz)) + +//// dist[4*Np+n] = m4 - (m4-feq4)/tau + 0.125*(2*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uy*uy + +//// 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) + +//// (ny + nx*ux + ny*uy + nz*uz)*(-2*chem*(uy*uy) + +//// 0.3333333333333333*((-rhoA + rhoB)*(uy*uy) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*uz)))); //// //// // q = 5 -//// dist[5*Np+n] = m5 - (m5-feq5)/tau + 0.125*(2*(Fx*ux + Fy*uy + Fz*(-1 + uz))*(-0.2222222222222222 - uz*uz + -//// 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) + -//// (nx*ux + ny*uy + nz*(-1 + uz))*(-2*chem*(uz*uz) + -//// 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + (-2 + uz)*uz)) + +//// dist[5*Np+n] = m5 - (m5-feq5)/tau + 0.125*(2*(Fx*ux + Fy*uy + Fz*(-1 + uz))*(-0.2222222222222222 - uz*uz + +//// 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) + +//// (nx*ux + ny*uy + nz*(-1 + uz))*(-2*chem*(uz*uz) + +//// 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + (-2 + uz)*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + (-2 + uz)*uz)))); //// //// // q = 6 -//// dist[6*Np+n] = m6 - (m6-feq6)/tau + 0.125*(2*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uz*uz + -//// 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) + -//// (nz + nx*ux + ny*uy + nz*uz)*(-2*chem*(uz*uz) + -//// 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + uz*(2 + uz))) + +//// dist[6*Np+n] = m6 - (m6-feq6)/tau + 0.125*(2*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uz*uz + +//// 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) + +//// (nz + nx*ux + ny*uy + nz*uz)*(-2*chem*(uz*uz) + +//// 0.3333333333333333*((-rhoA + rhoB)*(uz*uz) + 2*chem*(ux*ux + uy*uy + uz*(2 + uz))) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + uy*uy + uz*(2 + uz))))); //// //// // q = 7 //// dist[7*Np+n] = m7 - (m7-feq7)/tau + 0.0625*(-2*(Fx*(-1 + ux) + Fy*(-1 + uy) + Fz*uz)* -//// (0.2222222222222222 + (ux + uy)*(ux + uy) - -//// 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + +//// (0.2222222222222222 + (ux + uy)*(ux + uy) - +//// 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + //// (nx*(-1 + ux) + ny*(-1 + uy) + nz*uz)* //// (-2*chem*((ux + uy)*(ux + uy)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + +//// (-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)))); //// //// // q = 8 //// dist[8*Np+n] = m8 - (m8-feq8)/tau + 0.0625*(2*(Fx + Fy + Fx*ux + Fy*uy + Fz*uz)* -//// (-0.2222222222222222 - (ux + uy)*(ux + uy) + -//// 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + +//// (-0.2222222222222222 - (ux + uy)*(ux + uy) + +//// 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + //// (nx + ny + nx*ux + ny*uy + nz*uz)* //// (-2*chem*((ux + uy)*(ux + uy)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + +//// (-((rhoA - rhoB)*((ux + uy)*(ux + uy))) + 2*chem*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)))); //// //// // q = 9 //// dist[9*Np+n] = m9 - (m9-feq9)/tau + 0.0625*(2*(Fy + Fx*(-1 + ux) + Fy*uy + Fz*uz)* -//// (-0.2222222222222222 - (ux - uy)*(ux - uy) + -//// 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + +//// (-0.2222222222222222 - (ux - uy)*(ux - uy) + +//// 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + //// (ny + nx*(-1 + ux) + ny*uy + nz*uz)* //// (-2*chem*((ux - uy)*(ux - uy)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + +//// (-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)))); //// //// // q = 10 //// dist[10*Np+n] = m10 - (m10-feq10)/tau + 0.0625*(2*(Fx*(1 + ux) + Fy*(-1 + uy) + Fz*uz)* -//// (-0.2222222222222222 - (ux - uy)*(ux - uy) + -//// 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + +//// (-0.2222222222222222 - (ux - uy)*(ux - uy) + +//// 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + //// (nx*(1 + ux) + ny*(-1 + uy) + nz*uz)* //// (-2*chem*((ux - uy)*(ux - uy)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + +//// (-((rhoA - rhoB)*((ux - uy)*(ux - uy))) + 2*chem*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)))); //// //// // q = 11 //// dist[11*Np+n] = m11 - (m11-feq11)/tau + 0.0625*(-2*(Fx*(-1 + ux) + Fy*uy + Fz*(-1 + uz))* -//// (0.2222222222222222 + (ux + uz)*(ux + uz) - -//// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + +//// (0.2222222222222222 + (ux + uz)*(ux + uz) - +//// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + //// (nx*(-1 + ux) + ny*uy + nz*(-1 + uz))* //// (-2*chem*((ux + uz)*(ux + uz)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + +//// (-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)))); //// //// // q = 12 @@ -4019,25 +6070,25 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist //// (-0.2222222222222222 - (ux + uz)*(ux + uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) //// + (nx + nz + nx*ux + ny*uy + nz*uz)* //// (-2*chem*((ux + uz)*(ux + uz)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) + +//// (-((rhoA - rhoB)*((ux + uz)*(ux + uz))) + 2*chem*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + uz*(2 + uz))))); //// //// // q = 13 //// dist[13*Np+n] = m13 - (m13-feq13)/tau + 0.0625*(2*(Fz + Fx*(-1 + ux) + Fy*uy + Fz*uz)* -//// (-0.2222222222222222 - (ux - uz)*(ux - uz) + -//// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + +//// (-0.2222222222222222 - (ux - uz)*(ux - uz) + +//// 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + //// (nz + nx*(-1 + ux) + ny*uy + nz*uz)* //// (-2*chem*((ux - uz)*(ux - uz)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + +//// (-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))))); //// //// // q= 14 //// dist[14*Np+n] = m14 - (m14-feq14)/tau + 0.0625*(2*(Fx*(1 + ux) + Fy*uy + Fz*(-1 + uz))* -//// (-0.2222222222222222 - (ux - uz)*(ux - uz) + -//// 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + +//// (-0.2222222222222222 - (ux - uz)*(ux - uz) + +//// 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + //// (nx*(1 + ux) + ny*uy + nz*(-1 + uz))* //// (-2*chem*((ux - uz)*(ux - uz)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + +//// (-((rhoA - rhoB)*((ux - uz)*(ux - uz))) + 2*chem*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)))); //// //// // q = 15 @@ -4045,7 +6096,7 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist //// (0.2222222222222222 + (uy + uz)*(uy + uz) - 0.3333333333333333*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) //// + (nx*ux + ny*(-1 + uy) + nz*(-1 + uz))* //// (-2*chem*((uy + uz)*(uy + uz)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + +//// (-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)))); //// //// // q = 16 @@ -4053,7 +6104,7 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist //// (-0.2222222222222222 - (uy + uz)*(uy + uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) //// + (ny + nz + nx*ux + ny*uy + nz*uz)* //// (-2*chem*((uy + uz)*(uy + uz)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) + +//// (-((rhoA - rhoB)*((uy + uz)*(uy + uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))))); //// //// // q = 17 @@ -4061,16 +6112,16 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist //// (-0.2222222222222222 - (uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) //// + (nz + nx*ux + ny*(-1 + uy) + nz*uz)* //// (-2*chem*((uy - uz)*(uy - uz)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) + +//// (-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))))); //// //// // q = 18 //// dist[18*Np+n] = m18 - (m18-feq18)/tau + 0.0625*(2*(Fx*ux + Fy*(1 + uy) + Fz*(-1 + uz))* -//// (-0.2222222222222222 - (uy - uz)*(uy - uz) + -//// 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + +//// (-0.2222222222222222 - (uy - uz)*(uy - uz) + +//// 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + //// (nx*ux + ny*(1 + uy) + nz*(-1 + uz))* //// (-2*chem*((uy - uz)*(uy - uz)) + 0.3333333333333333* -//// (-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + +//// (-((rhoA - rhoB)*((uy - uz)*(uy - uz))) + 2*chem*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + //// 0.1111111111111111*(-4*chem + (rhoA - rhoB)*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)))); //// //----------------------------------------------------------------------------------------------------------------------------------------// // @@ -4081,7 +6132,7 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // if (C==0.0) ColorMag=1.0; // nx = nx/ColorMag; // ny = ny/ColorMag; -// nz = nz/ColorMag; +// nz = nz/ColorMag; // //compute surface tension-related parameter // //theta = 4.5*M*2.0*(1-phi*phi)/W; // theta = 4.5*M*2.0*(1-phi_temp*phi_temp)/W; @@ -4090,10 +6141,10 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // //q=0 // h0 = hq[n]; // //q=1 -// h1 = hq[2*Np+n]; +// h1 = hq[2*Np+n]; // // //q=2 -// h2 = hq[1*Np+n]; +// h2 = hq[1*Np+n]; // // //q=3 // h3 = hq[4*Np+n]; @@ -4146,680 +6197,1038 @@ extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(int *Map, double *dist // } //} +extern "C" void ScaLBL_D3Q19_AAodd_FreeLeeModel_SingleFluid_BGK( + int *neighborList, double *dist, double *Vel, double *Pressure, double tau, + double rho0, double Fx, double Fy, double Fz, int start, int finish, + int Np) { -extern "C" void ScaLBL_D3Q19_AAodd_FreeLeeModel_SingleFluid_BGK(int *neighborList, double *dist, double *Vel, double *Pressure, - double tau, double rho0, double Fx, double Fy, double Fz, int start, int finish, int Np){ + int nr1, nr2, nr3, nr4, nr5, nr6, nr7, nr8, nr9, nr10, nr11, nr12, nr13, + nr14, nr15, nr16, nr17, nr18; + double ux, uy, uz; //fluid velocity + double p; //pressure + // distribution functions + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; + double m0, m3, m5, m7; - int nr1,nr2,nr3,nr4,nr5,nr6,nr7,nr8,nr9,nr10,nr11,nr12,nr13,nr14,nr15,nr16,nr17,nr18; - double ux,uy,uz;//fluid velocity - double p;//pressure - // distribution functions - double m1,m2,m4,m6,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18; - double m0,m3,m5,m7; + for (int n = start; n < finish; n++) { - for (int n=start; n 10Np => odd part of dist) + m1 = dist[nr1]; // reading the f1 data into register fq - // q=0 - m0 = dist[n]; - // q=1 - nr1 = neighborList[n]; // neighbor 2 ( > 10Np => odd part of dist) - m1 = dist[nr1]; // reading the f1 data into register fq + nr2 = neighborList[n + Np]; // neighbor 1 ( < 10Np => even part of dist) + m2 = dist[nr2]; // reading the f2 data into register fq - nr2 = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) - m2 = dist[nr2]; // reading the f2 data into register fq + // q=3 + nr3 = neighborList[n + 2 * Np]; // neighbor 4 + m3 = dist[nr3]; - // q=3 - nr3 = neighborList[n+2*Np]; // neighbor 4 - m3 = dist[nr3]; + // q = 4 + nr4 = neighborList[n + 3 * Np]; // neighbor 3 + m4 = dist[nr4]; - // q = 4 - nr4 = neighborList[n+3*Np]; // neighbor 3 - m4 = dist[nr4]; + // q=5 + nr5 = neighborList[n + 4 * Np]; + m5 = dist[nr5]; - // q=5 - nr5 = neighborList[n+4*Np]; - m5 = dist[nr5]; + // q = 6 + nr6 = neighborList[n + 5 * Np]; + m6 = dist[nr6]; - // q = 6 - nr6 = neighborList[n+5*Np]; - m6 = dist[nr6]; + // q=7 + nr7 = neighborList[n + 6 * Np]; + m7 = dist[nr7]; - // q=7 - nr7 = neighborList[n+6*Np]; - m7 = dist[nr7]; + // q = 8 + nr8 = neighborList[n + 7 * Np]; + m8 = dist[nr8]; - // q = 8 - nr8 = neighborList[n+7*Np]; - m8 = dist[nr8]; + // q=9 + nr9 = neighborList[n + 8 * Np]; + m9 = dist[nr9]; - // q=9 - nr9 = neighborList[n+8*Np]; - m9 = dist[nr9]; + // q = 10 + nr10 = neighborList[n + 9 * Np]; + m10 = dist[nr10]; - // q = 10 - nr10 = neighborList[n+9*Np]; - m10 = dist[nr10]; + // q=11 + nr11 = neighborList[n + 10 * Np]; + m11 = dist[nr11]; - // q=11 - nr11 = neighborList[n+10*Np]; - m11 = dist[nr11]; + // q=12 + nr12 = neighborList[n + 11 * Np]; + m12 = dist[nr12]; - // q=12 - nr12 = neighborList[n+11*Np]; - m12 = dist[nr12]; + // q=13 + nr13 = neighborList[n + 12 * Np]; + m13 = dist[nr13]; - // q=13 - nr13 = neighborList[n+12*Np]; - m13 = dist[nr13]; + // q=14 + nr14 = neighborList[n + 13 * Np]; + m14 = dist[nr14]; - // q=14 - nr14 = neighborList[n+13*Np]; - m14 = dist[nr14]; + // q=15 + nr15 = neighborList[n + 14 * Np]; + m15 = dist[nr15]; - // q=15 - nr15 = neighborList[n+14*Np]; - m15 = dist[nr15]; + // q=16 + nr16 = neighborList[n + 15 * Np]; + m16 = dist[nr16]; - // q=16 - nr16 = neighborList[n+15*Np]; - m16 = dist[nr16]; + // q=17 + nr17 = neighborList[n + 16 * Np]; + m17 = dist[nr17]; - // q=17 - nr17 = neighborList[n+16*Np]; - m17 = dist[nr17]; + // q=18 + nr18 = neighborList[n + 17 * Np]; + m18 = dist[nr18]; - // q=18 - nr18 = neighborList[n+17*Np]; - m18 = dist[nr18]; + //compute fluid velocity + ux = 3.0 / rho0 * + (m1 - m2 + m7 - m8 + m9 - m10 + m11 - m12 + m13 - m14 + + 0.5 * (Fx) / 3.0); + uy = 3.0 / rho0 * + (m3 - m4 + m7 - m8 - m9 + m10 + m15 - m16 + m17 - m18 + + 0.5 * (Fy) / 3.0); + uz = 3.0 / rho0 * + (m5 - m6 + m11 - m12 - m13 + m14 + m15 - m16 - m17 + m18 + + 0.5 * (Fz) / 3.0); + //compute pressure + p = (m0 + m2 + m1 + m4 + m3 + m6 + m5 + m8 + m7 + m10 + m9 + m12 + m11 + + m14 + m13 + m16 + m15 + m18 + m17); - //compute fluid velocity - ux = 3.0/rho0*(m1-m2+m7-m8+m9-m10+m11-m12+m13-m14+0.5*(Fx)/3.0); - uy = 3.0/rho0*(m3-m4+m7-m8-m9+m10+m15-m16+m17-m18+0.5*(Fy)/3.0); - uz = 3.0/rho0*(m5-m6+m11-m12-m13+m14+m15-m16-m17+m18+0.5*(Fz)/3.0); - //compute pressure - p = (m0+m2+m1+m4+m3+m6+m5+m8+m7+m10+m9+m12+m11+m14+m13+m16+m15+m18+m17); + //------------------------------------------------- BCK collison ------------------------------------------------------------// + // q=0 + dist[n] = m0 + + 0.5 * (Fx * ux + Fy * uy + Fz * uz) * + (-0.6666666666666666 + ux * ux + uy * uy + uz * uz) - + (m0 - 0.3333333333333333 * p + + 0.25 * (Fx * ux + Fy * uy + Fz * uz) * + (-0.6666666666666666 + ux * ux + uy * uy + uz * uz) + + 0.16666666666666666 * rho0 * (ux * ux + uy * uy + uz * uz)) / + tau; - //------------------------------------------------- BCK collison ------------------------------------------------------------// - // q=0 - dist[n] = m0 + 0.5*(Fx*ux + Fy*uy + Fz*uz)*(-0.6666666666666666 + ux*ux + uy*uy + uz*uz) - - (m0 - 0.3333333333333333*p + 0.25*(Fx*ux + Fy*uy + Fz*uz)* - (-0.6666666666666666 + ux*ux + uy*uy + uz*uz) + 0.16666666666666666*rho0*(ux*ux + uy*uy + uz*uz))/ - tau; + // q = 1 + dist[nr2] = + m1 + + 0.25 * (Fx * (-1 + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - ux * ux + + 0.3333333333333333 * (-2 * ux + ux * ux + uy * uy + uz * uz)) - + (m1 - 0.05555555555555555 * p + + 0.08333333333333333 * rho0 * + (-(ux * ux) + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + uz * uz)) + + 0.125 * (Fx * (-1. + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) + + 0.3333333333333333 * + (-2. * ux + ux * ux + uy * uy + uz * uz))) / + tau; - // q = 1 - dist[nr2] = m1 + 0.25*(Fx*(-1 + ux) + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + - 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) - - (m1 - 0.05555555555555555*p + 0.08333333333333333*rho0* - (-(ux*ux) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*uz)) + - 0.125*(Fx*(-1. + ux) + Fy*uy + Fz*uz)* - (-0.2222222222222222 - 1.*(ux*ux) + 0.3333333333333333*(-2.*ux + ux*ux + uy*uy + uz*uz)))/tau; + // q=2 + dist[nr1] = + m2 + + 0.25 * (Fx + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - ux * ux + + 0.3333333333333333 * (2 * ux + ux * ux + uy * uy + uz * uz)) - + (m2 - 0.05555555555555555 * p + + 0.08333333333333333 * rho0 * + (-(ux * ux) + + 0.3333333333333333 * (2 * ux + ux * ux + uy * uy + uz * uz)) + + 0.125 * (Fx + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) + + 0.3333333333333333 * + (2. * ux + ux * ux + uy * uy + uz * uz))) / + tau; - // q=2 - dist[nr1] = m2 + 0.25*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - ux*ux + - 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) - - (m2 - 0.05555555555555555*p + 0.08333333333333333*rho0* - (-(ux*ux) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*uz)) + - 0.125*(Fx + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(ux*ux) + - 0.3333333333333333*(2.*ux + ux*ux + uy*uy + uz*uz)))/tau; + // q = 3 + dist[nr4] = + m3 + + 0.25 * (Fx * ux + Fy * (-1 + uy) + Fz * uz) * + (-0.2222222222222222 - uy * uy + + 0.3333333333333333 * (ux * ux - 2 * uy + uy * uy + uz * uz)) - + (m3 - 0.05555555555555555 * p + + 0.08333333333333333 * rho0 * + (-(uy * uy) + + 0.3333333333333333 * (ux * ux - 2 * uy + uy * uy + uz * uz)) + + 0.125 * (Fx * ux + Fy * (-1. + uy) + Fz * uz) * + (-0.2222222222222222 - 1. * (uy * uy) + + 0.3333333333333333 * + (ux * ux - 2. * uy + uy * uy + uz * uz))) / + tau; - // q = 3 - dist[nr4] = m3 + 0.25*(Fx*ux + Fy*(-1 + uy) + Fz*uz)*(-0.2222222222222222 - uy*uy + - 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) - - (m3 - 0.05555555555555555*p + 0.08333333333333333*rho0* - (-(uy*uy) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.125*(Fx*ux + Fy*(-1. + uy) + Fz*uz)* - (-0.2222222222222222 - 1.*(uy*uy) + 0.3333333333333333*(ux*ux - 2.*uy + uy*uy + uz*uz)))/tau; + // q = 4 + dist[nr3] = + m4 + + 0.25 * (Fy + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - uy * uy + + 0.3333333333333333 * (ux * ux + 2 * uy + uy * uy + uz * uz)) - + (m4 - 0.05555555555555555 * p + + 0.08333333333333333 * rho0 * + (-(uy * uy) + + 0.3333333333333333 * (ux * ux + 2 * uy + uy * uy + uz * uz)) + + 0.125 * (Fy + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (uy * uy) + + 0.3333333333333333 * + (ux * ux + 2. * uy + uy * uy + uz * uz))) / + tau; - // q = 4 - dist[nr3] = m4 + 0.25*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uy*uy + - 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) - - (m4 - 0.05555555555555555*p + 0.08333333333333333*rho0* - (-(uy*uy) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*uz)) + - 0.125*(Fy + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(uy*uy) + - 0.3333333333333333*(ux*ux + 2.*uy + uy*uy + uz*uz)))/tau; + // q = 5 + dist[nr6] = + m5 + + 0.25 * (Fx * ux + Fy * uy + Fz * (-1 + uz)) * + (-0.2222222222222222 - uz * uz + + 0.3333333333333333 * (ux * ux + uy * uy + (-2 + uz) * uz)) - + (m5 - 0.05555555555555555 * p + + 0.08333333333333333 * rho0 * + (-(uz * uz) + + 0.3333333333333333 * (ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.125 * (Fx * ux + Fy * uy + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * (uz * uz) + + 0.3333333333333333 * (ux * ux + uy * uy + (-2. + uz) * uz))) / + tau; - // q = 5 - dist[nr6] = m5 + 0.25*(Fx*ux + Fy*uy + Fz*(-1 + uz))*(-0.2222222222222222 - uz*uz + - 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) - - (m5 - 0.05555555555555555*p + 0.08333333333333333*rho0* - (-(uz*uz) + 0.3333333333333333*(ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.125*(Fx*ux + Fy*uy + Fz*(-1. + uz))* - (-0.2222222222222222 - 1.*(uz*uz) + 0.3333333333333333*(ux*ux + uy*uy + (-2. + uz)*uz)))/tau; + // q = 6 + dist[nr5] = + m6 + + 0.25 * (Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - uz * uz + + 0.3333333333333333 * (ux * ux + uy * uy + uz * (2 + uz))) - + (m6 - 0.05555555555555555 * p + + 0.08333333333333333 * rho0 * + (-(uz * uz) + + 0.3333333333333333 * (ux * ux + uy * uy + uz * (2 + uz))) + + 0.125 * (Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (uz * uz) + + 0.3333333333333333 * (ux * ux + uy * uy + uz * (2. + uz)))) / + tau; - // q = 6 - dist[nr5] = m6 + 0.25*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - uz*uz + - 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) - - (m6 - 0.05555555555555555*p + 0.08333333333333333*rho0* - (-(uz*uz) + 0.3333333333333333*(ux*ux + uy*uy + uz*(2 + uz))) + - 0.125*(Fz + Fx*ux + Fy*uy + Fz*uz)*(-0.2222222222222222 - 1.*(uz*uz) + - 0.3333333333333333*(ux*ux + uy*uy + uz*(2. + uz))))/tau; + // q = 7 + dist[nr8] = + m7 - + 0.125 * (Fx * (-1 + ux) + Fy * (-1 + uy) + Fz * uz) * + (0.2222222222222222 + (ux + uy) * (ux + uy) - + 0.3333333333333333 * + (-2 * ux + ux * ux - 2 * uy + uy * uy + uz * uz)) - + (m7 - 0.027777777777777776 * p + + 0.041666666666666664 * rho0 * + (-((ux + uy) * (ux + uy)) + + 0.3333333333333333 * + (-2 * ux + ux * ux - 2 * uy + uy * uy + uz * uz)) + + 0.0625 * (Fx * (-1. + ux) + Fy * (-1. + uy) + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) - 2. * ux * uy - + 1. * (uy * uy) + + 0.3333333333333333 * + (-2. * ux + ux * ux - 2. * uy + uy * uy + uz * uz))) / + tau; - // q = 7 - dist[nr8] = m7 - 0.125*(Fx*(-1 + ux) + Fy*(-1 + uy) + Fz*uz)* - (0.2222222222222222 + (ux + uy)*(ux + uy) - 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz))\ - - (m7 - 0.027777777777777776*p + 0.041666666666666664*rho0* - (-((ux + uy)*(ux + uy)) + 0.3333333333333333*(-2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.0625*(Fx*(-1. + ux) + Fy*(-1. + uy) + Fz*uz)* - (-0.2222222222222222 - 1.*(ux*ux) - 2.*ux*uy - 1.*(uy*uy) + - 0.3333333333333333*(-2.*ux + ux*ux - 2.*uy + uy*uy + uz*uz)))/tau; + // q = 8 + dist[nr7] = m8 + + 0.125 * (Fx + Fy + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (ux + uy) * (ux + uy) + + 0.3333333333333333 * + (2 * ux + ux * ux + 2 * uy + uy * uy + uz * uz)) - + (m8 - 0.027777777777777776 * p + + 0.041666666666666664 * rho0 * + (-((ux + uy) * (ux + uy)) + + 0.3333333333333333 * + (2 * ux + ux * ux + 2 * uy + uy * uy + uz * uz)) + + 0.0625 * (Fx + Fy + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) - 2. * ux * uy - + 1. * (uy * uy) + + 0.3333333333333333 * (2. * ux + ux * ux + 2. * uy + + uy * uy + uz * uz))) / + tau; - // q = 8 - dist[nr7] = m8 + 0.125*(Fx + Fy + Fx*ux + Fy*uy + Fz*uz)* - (-0.2222222222222222 - (ux + uy)*(ux + uy) + 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz))\ - - (m8 - 0.027777777777777776*p + 0.041666666666666664*rho0* - (-((ux + uy)*(ux + uy)) + 0.3333333333333333*(2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + - 0.0625*(Fx + Fy + Fx*ux + Fy*uy + Fz*uz)* - (-0.2222222222222222 - 1.*(ux*ux) - 2.*ux*uy - 1.*(uy*uy) + - 0.3333333333333333*(2.*ux + ux*ux + 2.*uy + uy*uy + uz*uz)))/tau; + // q = 9 + dist[nr10] = + m9 + + 0.125 * (Fy + Fx * (-1 + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (ux - uy) * (ux - uy) + + 0.3333333333333333 * + (-2 * ux + ux * ux + 2 * uy + uy * uy + uz * uz)) - + (m9 - 0.027777777777777776 * p + + 0.041666666666666664 * rho0 * + (-((ux - uy) * (ux - uy)) + + 0.3333333333333333 * + (-2 * ux + ux * ux + 2 * uy + uy * uy + uz * uz)) + + 0.0625 * (Fy + Fx * (-1. + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) + 2. * ux * uy - + 1. * (uy * uy) + + 0.3333333333333333 * + (-2. * ux + ux * ux + 2. * uy + uy * uy + uz * uz))) / + tau; - // q = 9 - dist[nr10] = m9 + 0.125*(Fy + Fx*(-1 + ux) + Fy*uy + Fz*uz)* - (-0.2222222222222222 - (ux - uy)*(ux - uy) + 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) - - (m9 - 0.027777777777777776*p + 0.041666666666666664*rho0* - (-((ux - uy)*(ux - uy)) + 0.3333333333333333*(-2*ux + ux*ux + 2*uy + uy*uy + uz*uz)) + - 0.0625*(Fy + Fx*(-1. + ux) + Fy*uy + Fz*uz)* - (-0.2222222222222222 - 1.*(ux*ux) + 2.*ux*uy - 1.*(uy*uy) + - 0.3333333333333333*(-2.*ux + ux*ux + 2.*uy + uy*uy + uz*uz)))/tau; + // q = 10 + dist[nr9] = m10 + + 0.125 * (Fx * (1 + ux) + Fy * (-1 + uy) + Fz * uz) * + (-0.2222222222222222 - (ux - uy) * (ux - uy) + + 0.3333333333333333 * + (2 * ux + ux * ux - 2 * uy + uy * uy + uz * uz)) - + (m10 - 0.027777777777777776 * p + + 0.041666666666666664 * rho0 * + (-((ux - uy) * (ux - uy)) + + 0.3333333333333333 * + (2 * ux + ux * ux - 2 * uy + uy * uy + uz * uz)) + + 0.0625 * (Fx * (1 + ux) + Fy * (-1. + uy) + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) + 2. * ux * uy - + 1. * (uy * uy) + + 0.3333333333333333 * (2. * ux + ux * ux - 2. * uy + + uy * uy + uz * uz))) / + tau; - // q = 10 - dist[nr9] = m10 + 0.125*(Fx*(1 + ux) + Fy*(-1 + uy) + Fz*uz)* - (-0.2222222222222222 - (ux - uy)*(ux - uy) + 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz))\ - - (m10 - 0.027777777777777776*p + 0.041666666666666664*rho0* - (-((ux - uy)*(ux - uy)) + 0.3333333333333333*(2*ux + ux*ux - 2*uy + uy*uy + uz*uz)) + - 0.0625*(Fx*(1 + ux) + Fy*(-1. + uy) + Fz*uz)* - (-0.2222222222222222 - 1.*(ux*ux) + 2.*ux*uy - 1.*(uy*uy) + - 0.3333333333333333*(2.*ux + ux*ux - 2.*uy + uy*uy + uz*uz)))/tau; + // q = 11 + dist[nr12] = m11 - + 0.125 * (Fx * (-1 + ux) + Fy * uy + Fz * (-1 + uz)) * + (0.2222222222222222 + (ux + uz) * (ux + uz) - + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) - + (m11 - 0.027777777777777776 * p + + 0.041666666666666664 * rho0 * + (-((ux + uz) * (ux + uz)) + + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.0625 * (Fx * (-1. + ux) + Fy * uy + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * (ux * ux) - 2. * ux * uz - + 1. * (uz * uz) + + 0.3333333333333333 * (-2. * ux + ux * ux + uy * uy + + (-2. + uz) * uz))) / + tau; - // q = 11 - dist[nr12] = m11 - 0.125*(Fx*(-1 + ux) + Fy*uy + Fz*(-1 + uz))* - (0.2222222222222222 + (ux + uz)*(ux + uz) - 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz))\ - - (m11 - 0.027777777777777776*p + 0.041666666666666664*rho0* - (-((ux + uz)*(ux + uz)) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.0625*(Fx*(-1. + ux) + Fy*uy + Fz*(-1. + uz))* - (-0.2222222222222222 - 1.*(ux*ux) - 2.*ux*uz - 1.*(uz*uz) + - 0.3333333333333333*(-2.*ux + ux*ux + uy*uy + (-2. + uz)*uz)))/tau; + // q = 12 + dist[nr11] = m12 + + 0.125 * (Fx + Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (ux + uz) * (ux + uz) + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + uz * (2 + uz))) - + (m12 - 0.027777777777777776 * p + + 0.041666666666666664 * rho0 * + (-((ux + uz) * (ux + uz)) + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + uz * (2 + uz))) + + 0.0625 * (Fx + Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) - 2. * ux * uz - + 1. * (uz * uz) + + 0.3333333333333333 * (2. * ux + ux * ux + uy * uy + + uz * (2. + uz)))) / + tau; - // q = 12 - dist[nr11] = m12 + 0.125*(Fx + Fz + Fx*ux + Fy*uy + Fz*uz)* - (-0.2222222222222222 - (ux + uz)*(ux + uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*(2 + uz)))\ - - (m12 - 0.027777777777777776*p + 0.041666666666666664*rho0* - (-((ux + uz)*(ux + uz)) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + uz*(2 + uz))) + - 0.0625*(Fx + Fz + Fx*ux + Fy*uy + Fz*uz)* - (-0.2222222222222222 - 1.*(ux*ux) - 2.*ux*uz - 1.*(uz*uz) + - 0.3333333333333333*(2.*ux + ux*ux + uy*uy + uz*(2. + uz))))/tau; + // q = 13 + dist[nr14] = m13 + + 0.125 * (Fz + Fx * (-1 + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (ux - uz) * (ux - uz) + + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + uz * (2 + uz))) - + (m13 - 0.027777777777777776 * p + + 0.041666666666666664 * rho0 * + (-((ux - uz) * (ux - uz)) + + 0.3333333333333333 * + (-2 * ux + ux * ux + uy * uy + uz * (2 + uz))) + + 0.0625 * (Fz + Fx * (-1. + ux) + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (ux * ux) + 2. * ux * uz - + 1. * (uz * uz) + + 0.3333333333333333 * (-2. * ux + ux * ux + uy * uy + + uz * (2. + uz)))) / + tau; - // q = 13 - dist[nr14] = m13 + 0.125*(Fz + Fx*(-1 + ux) + Fy*uy + Fz*uz)* - (-0.2222222222222222 - (ux - uz)*(ux - uz) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz)))\ - - (m13 - 0.027777777777777776*p + 0.041666666666666664*rho0* - (-((ux - uz)*(ux - uz)) + 0.3333333333333333*(-2*ux + ux*ux + uy*uy + uz*(2 + uz))) + - 0.0625*(Fz + Fx*(-1. + ux) + Fy*uy + Fz*uz)* - (-0.2222222222222222 - 1.*(ux*ux) + 2.*ux*uz - 1.*(uz*uz) + - 0.3333333333333333*(-2.*ux + ux*ux + uy*uy + uz*(2. + uz))))/tau; + // q= 14 + dist[nr13] = m14 + + 0.125 * (Fx * (1 + ux) + Fy * uy + Fz * (-1 + uz)) * + (-0.2222222222222222 - (ux - uz) * (ux - uz) + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) - + (m14 - 0.027777777777777776 * p + + 0.041666666666666664 * rho0 * + (-((ux - uz) * (ux - uz)) + + 0.3333333333333333 * + (2 * ux + ux * ux + uy * uy + (-2 + uz) * uz)) + + 0.0625 * (Fx * (1 + ux) + Fy * uy + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * (ux * ux) + 2. * ux * uz - + 1. * (uz * uz) + + 0.3333333333333333 * (2. * ux + ux * ux + uy * uy + + (-2. + uz) * uz))) / + tau; - // q= 14 - dist[nr13] = m14 + 0.125*(Fx*(1 + ux) + Fy*uy + Fz*(-1 + uz))* - (-0.2222222222222222 - (ux - uz)*(ux - uz) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz))\ - - (m14 - 0.027777777777777776*p + 0.041666666666666664*rho0* - (-((ux - uz)*(ux - uz)) + 0.3333333333333333*(2*ux + ux*ux + uy*uy + (-2 + uz)*uz)) + - 0.0625*(Fx*(1 + ux) + Fy*uy + Fz*(-1. + uz))* - (-0.2222222222222222 - 1.*(ux*ux) + 2.*ux*uz - 1.*(uz*uz) + - 0.3333333333333333*(2.*ux + ux*ux + uy*uy + (-2. + uz)*uz)))/tau; + // q = 15 + dist[nr16] = m15 - + 0.125 * (Fx * ux + Fy * (-1 + uy) + Fz * (-1 + uz)) * + (0.2222222222222222 + (uy + uz) * (uy + uz) - + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + (-2 + uz) * uz)) - + (m15 - 0.027777777777777776 * p + + 0.041666666666666664 * rho0 * + (-((uy + uz) * (uy + uz)) + + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + (-2 + uz) * uz)) + + 0.0625 * (Fx * ux + Fy * (-1. + uy) + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * (uy * uy) - 2. * uy * uz - + 1. * (uz * uz) + + 0.3333333333333333 * (ux * ux - 2. * uy + uy * uy + + (-2. + uz) * uz))) / + tau; - // q = 15 - dist[nr16] = m15 - 0.125*(Fx*ux + Fy*(-1 + uy) + Fz*(-1 + uz))* - (0.2222222222222222 + (uy + uz)*(uy + uz) - 0.3333333333333333*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz))\ - - (m15 - 0.027777777777777776*p + 0.041666666666666664*rho0* - (-((uy + uz)*(uy + uz)) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + (-2 + uz)*uz)) + - 0.0625*(Fx*ux + Fy*(-1. + uy) + Fz*(-1. + uz))* - (-0.2222222222222222 - 1.*(uy*uy) - 2.*uy*uz - 1.*(uz*uz) + - 0.3333333333333333*(ux*ux - 2.*uy + uy*uy + (-2. + uz)*uz)))/tau; + // q = 16 + dist[nr15] = m16 + + 0.125 * (Fy + Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - (uy + uz) * (uy + uz) + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + uz * (2 + uz))) - + (m16 - 0.027777777777777776 * p + + 0.041666666666666664 * rho0 * + (-((uy + uz) * (uy + uz)) + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + uz * (2 + uz))) + + 0.0625 * (Fy + Fz + Fx * ux + Fy * uy + Fz * uz) * + (-0.2222222222222222 - 1. * (uy * uy) - 2. * uy * uz - + 1. * (uz * uz) + + 0.3333333333333333 * (ux * ux + 2. * uy + uy * uy + + uz * (2. + uz)))) / + tau; - // q = 16 - dist[nr15] = m16 + 0.125*(Fy + Fz + Fx*ux + Fy*uy + Fz*uz)* - (-0.2222222222222222 - (uy + uz)*(uy + uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*(2 + uz)))\ - - (m16 - 0.027777777777777776*p + 0.041666666666666664*rho0* - (-((uy + uz)*(uy + uz)) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + uz*(2 + uz))) + - 0.0625*(Fy + Fz + Fx*ux + Fy*uy + Fz*uz)* - (-0.2222222222222222 - 1.*(uy*uy) - 2.*uy*uz - 1.*(uz*uz) + - 0.3333333333333333*(ux*ux + 2.*uy + uy*uy + uz*(2. + uz))))/tau; + // q = 17 + dist[nr18] = m17 + + 0.125 * (Fz + Fx * ux + Fy * (-1 + uy) + Fz * uz) * + (-0.2222222222222222 - (uy - uz) * (uy - uz) + + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + uz * (2 + uz))) - + (m17 - 0.027777777777777776 * p + + 0.041666666666666664 * rho0 * + (-((uy - uz) * (uy - uz)) + + 0.3333333333333333 * + (ux * ux - 2 * uy + uy * uy + uz * (2 + uz))) + + 0.0625 * (Fz + Fx * ux + Fy * (-1. + uy) + Fz * uz) * + (-0.2222222222222222 - 1. * (uy * uy) + 2. * uy * uz - + 1. * (uz * uz) + + 0.3333333333333333 * (ux * ux - 2. * uy + uy * uy + + uz * (2. + uz)))) / + tau; - // q = 17 - dist[nr18] = m17 + 0.125*(Fz + Fx*ux + Fy*(-1 + uy) + Fz*uz)* - (-0.2222222222222222 - (uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*(2 + uz)))\ - - (m17 - 0.027777777777777776*p + 0.041666666666666664*rho0* - (-((uy - uz)*(uy - uz)) + 0.3333333333333333*(ux*ux - 2*uy + uy*uy + uz*(2 + uz))) + - 0.0625*(Fz + Fx*ux + Fy*(-1. + uy) + Fz*uz)* - (-0.2222222222222222 - 1.*(uy*uy) + 2.*uy*uz - 1.*(uz*uz) + - 0.3333333333333333*(ux*ux - 2.*uy + uy*uy + uz*(2. + uz))))/tau; + // q = 18 + dist[nr17] = m18 + + 0.125 * (Fx * ux + Fy * (1 + uy) + Fz * (-1 + uz)) * + (-0.2222222222222222 - (uy - uz) * (uy - uz) + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + (-2 + uz) * uz)) - + (m18 - 0.027777777777777776 * p + + 0.041666666666666664 * rho0 * + (-((uy - uz) * (uy - uz)) + + 0.3333333333333333 * + (ux * ux + 2 * uy + uy * uy + (-2 + uz) * uz)) + + 0.0625 * (Fx * ux + Fy * (1 + uy) + Fz * (-1. + uz)) * + (-0.2222222222222222 - 1. * (uy * uy) + 2. * uy * uz - + 1. * (uz * uz) + + 0.3333333333333333 * (ux * ux + 2. * uy + uy * uy + + (-2. + uz) * uz))) / + tau; + //----------------------------------------------------------------------------------------------------------------------------------------// - // q = 18 - dist[nr17] = m18 + 0.125*(Fx*ux + Fy*(1 + uy) + Fz*(-1 + uz))* - (-0.2222222222222222 - (uy - uz)*(uy - uz) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz))\ - - (m18 - 0.027777777777777776*p + 0.041666666666666664*rho0* - (-((uy - uz)*(uy - uz)) + 0.3333333333333333*(ux*ux + 2*uy + uy*uy + (-2 + uz)*uz)) + - 0.0625*(Fx*ux + Fy*(1 + uy) + Fz*(-1. + uz))* - (-0.2222222222222222 - 1.*(uy*uy) + 2.*uy*uz - 1.*(uz*uz) + - 0.3333333333333333*(ux*ux + 2.*uy + uy*uy + (-2. + uz)*uz)))/tau; - //----------------------------------------------------------------------------------------------------------------------------------------// - - - //Update velocity on device - Vel[0*Np+n] = ux; - Vel[1*Np+n] = uy; - Vel[2*Np+n] = uz; - //Update pressure on device - Pressure[n] = p; - } + //Update velocity on device + Vel[0 * Np + n] = ux; + Vel[1 * Np + n] = uy; + Vel[2 * Np + n] = uz; + //Update pressure on device + Pressure[n] = p; + } } -extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_SingleFluid_BGK(double *dist, double *Vel, double *Pressure, - double tau, double rho0, double Fx, double Fy, double Fz, int start, int finish, int Np){ +extern "C" void ScaLBL_D3Q19_AAeven_FreeLeeModel_SingleFluid_BGK( + double *dist, double *Vel, double *Pressure, double tau, double rho0, + double Fx, double Fy, double Fz, int start, int finish, int Np) { - double ux,uy,uz;//fluid velocity - double p;//pressure - // distribution functions - double m1,m2,m4,m6,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18; - double m0,m3,m5,m7; + double ux, uy, uz; //fluid velocity + double p; //pressure + // distribution functions + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; + double m0, m3, m5, m7; - for (int n=start; n -extern "C" void ScaLBL_D3Q19_AAeven_Greyscale(double *dist, int start, int finish, int Np, double rlx, double rlx_eff, double Gx, double Gy, double Gz, - double *Poros,double *Perm, double *Velocity, double *Pressure){ - // conserved momemnts - double rho,vx,vy,vz,v_mag; - double ux,uy,uz,u_mag; +extern "C" void +ScaLBL_D3Q19_AAeven_Greyscale(double *dist, int start, int finish, int Np, + double rlx, double rlx_eff, double Gx, double Gy, + double Gz, double *Poros, double *Perm, + double *Velocity, double *Pressure) { + // conserved momemnts + double rho, vx, vy, vz, v_mag; + double ux, uy, uz, u_mag; double pressure; //double uu; - // non-conserved moments - double f0,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18; - double GeoFun;//geometric function from Guo's PRE 66, 036304 (2002) + // non-conserved moments + double f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, + f16, f17, f18; + double GeoFun; //geometric function from Guo's PRE 66, 036304 (2002) double porosity; - double perm;//voxel permeability - double c0, c1; //Guo's model parameters - double mu_eff = (1.0/rlx_eff-0.5)/3.0;//kinematic viscosity - double Fx, Fy, Fz;//The total body force including Brinkman force and user-specified (Gx,Gy,Gz) + double perm; //voxel permeability + double c0, c1; //Guo's model parameters + double mu_eff = (1.0 / rlx_eff - 0.5) / 3.0; //kinematic viscosity + double Fx, Fy, + Fz; //The total body force including Brinkman force and user-specified (Gx,Gy,Gz) + + for (int n = start; n < finish; n++) { + // q=0 + f0 = dist[n]; + f1 = dist[2 * Np + n]; + f2 = dist[1 * Np + n]; + f3 = dist[4 * Np + n]; + f4 = dist[3 * Np + n]; + f5 = dist[6 * Np + n]; + f6 = dist[5 * Np + n]; + f7 = dist[8 * Np + n]; + f8 = dist[7 * Np + n]; + f9 = dist[10 * Np + n]; + f10 = dist[9 * Np + n]; + f11 = dist[12 * Np + n]; + f12 = dist[11 * Np + n]; + f13 = dist[14 * Np + n]; + f14 = dist[13 * Np + n]; + f15 = dist[16 * Np + n]; + f16 = dist[15 * Np + n]; + f17 = dist[18 * Np + n]; + f18 = dist[17 * Np + n]; - for (int n=start; n 10Np => odd part of dist) - f1 = dist[nr1]; // reading the f1 data into register fq + for (int n = start; n < finish; n++) { - nr2 = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) - f2 = dist[nr2]; // reading the f2 data into register fq + // q=0 + f0 = dist[n]; + // q=1 + nr1 = neighborList[n]; // neighbor 2 ( > 10Np => odd part of dist) + f1 = dist[nr1]; // reading the f1 data into register fq - // q=3 - nr3 = neighborList[n+2*Np]; // neighbor 4 - f3 = dist[nr3]; + nr2 = neighborList[n + Np]; // neighbor 1 ( < 10Np => even part of dist) + f2 = dist[nr2]; // reading the f2 data into register fq - // q = 4 - nr4 = neighborList[n+3*Np]; // neighbor 3 - f4 = dist[nr4]; + // q=3 + nr3 = neighborList[n + 2 * Np]; // neighbor 4 + f3 = dist[nr3]; - // q=5 - nr5 = neighborList[n+4*Np]; - f5 = dist[nr5]; + // q = 4 + nr4 = neighborList[n + 3 * Np]; // neighbor 3 + f4 = dist[nr4]; - // q = 6 - nr6 = neighborList[n+5*Np]; - f6 = dist[nr6]; - - // q=7 - nr7 = neighborList[n+6*Np]; - f7 = dist[nr7]; + // q=5 + nr5 = neighborList[n + 4 * Np]; + f5 = dist[nr5]; - // q = 8 - nr8 = neighborList[n+7*Np]; - f8 = dist[nr8]; + // q = 6 + nr6 = neighborList[n + 5 * Np]; + f6 = dist[nr6]; - // q=9 - nr9 = neighborList[n+8*Np]; - f9 = dist[nr9]; + // q=7 + nr7 = neighborList[n + 6 * Np]; + f7 = dist[nr7]; - // q = 10 - nr10 = neighborList[n+9*Np]; - f10 = dist[nr10]; + // q = 8 + nr8 = neighborList[n + 7 * Np]; + f8 = dist[nr8]; - // q=11 - nr11 = neighborList[n+10*Np]; - f11 = dist[nr11]; + // q=9 + nr9 = neighborList[n + 8 * Np]; + f9 = dist[nr9]; - // q=12 - nr12 = neighborList[n+11*Np]; - f12 = dist[nr12]; + // q = 10 + nr10 = neighborList[n + 9 * Np]; + f10 = dist[nr10]; - // q=13 - nr13 = neighborList[n+12*Np]; - f13 = dist[nr13]; + // q=11 + nr11 = neighborList[n + 10 * Np]; + f11 = dist[nr11]; - // q=14 - nr14 = neighborList[n+13*Np]; - f14 = dist[nr14]; + // q=12 + nr12 = neighborList[n + 11 * Np]; + f12 = dist[nr12]; - // q=15 - nr15 = neighborList[n+14*Np]; - f15 = dist[nr15]; + // q=13 + nr13 = neighborList[n + 12 * Np]; + f13 = dist[nr13]; - // q=16 - nr16 = neighborList[n+15*Np]; - f16 = dist[nr16]; + // q=14 + nr14 = neighborList[n + 13 * Np]; + f14 = dist[nr14]; - // q=17 - //fq = dist[18*Np+n]; - nr17 = neighborList[n+16*Np]; - f17 = dist[nr17]; + // q=15 + nr15 = neighborList[n + 14 * Np]; + f15 = dist[nr15]; - // q=18 - nr18 = neighborList[n+17*Np]; - f18 = dist[nr18]; + // q=16 + nr16 = neighborList[n + 15 * Np]; + f16 = dist[nr16]; + + // q=17 + //fq = dist[18*Np+n]; + nr17 = neighborList[n + 16 * Np]; + f17 = dist[nr17]; + + // q=18 + nr18 = neighborList[n + 17 * Np]; + f18 = dist[nr18]; porosity = Poros[n]; perm = Perm[n]; - c0 = 0.5*(1.0+porosity*0.5*mu_eff/perm); - if (porosity==1.0) c0 = 0.5;//i.e. apparent pore nodes - GeoFun = 1.75/sqrt(150.0*porosity*porosity*porosity); - c1 = porosity*0.5*GeoFun/sqrt(perm); - if (porosity==1.0) c1 = 0.0;//i.e. apparent pore nodes + c0 = 0.5 * (1.0 + porosity * 0.5 * mu_eff / perm); + if (porosity == 1.0) + c0 = 0.5; //i.e. apparent pore nodes + GeoFun = 1.75 / sqrt(150.0 * porosity * porosity * porosity); + c1 = porosity * 0.5 * GeoFun / sqrt(perm); + if (porosity == 1.0) + c1 = 0.0; //i.e. apparent pore nodes - rho = f0+f2+f1+f4+f3+f6+f5+f8+f7+f10+f9+f12+f11+f14+f13+f16+f15+f18+f17; - pressure = rho/porosity/3.0; - vx = (f1-f2+f7-f8+f9-f10+f11-f12+f13-f14)/rho+0.5*porosity*Gx; - vy = (f3-f4+f7-f8-f9+f10+f15-f16+f17-f18)/rho+0.5*porosity*Gy; - vz = (f5-f6+f11-f12-f13+f14+f15-f16-f17+f18)/rho+0.5*porosity*Gz; - v_mag=sqrt(vx*vx+vy*vy+vz*vz); - ux = vx/(c0+sqrt(c0*c0+c1*v_mag)); - uy = vy/(c0+sqrt(c0*c0+c1*v_mag)); - uz = vz/(c0+sqrt(c0*c0+c1*v_mag)); - u_mag=sqrt(ux*ux+uy*uy+uz*uz); + rho = f0 + f2 + f1 + f4 + f3 + f6 + f5 + f8 + f7 + f10 + f9 + f12 + + f11 + f14 + f13 + f16 + f15 + f18 + f17; + pressure = rho / porosity / 3.0; + vx = (f1 - f2 + f7 - f8 + f9 - f10 + f11 - f12 + f13 - f14) / rho + + 0.5 * porosity * Gx; + vy = (f3 - f4 + f7 - f8 - f9 + f10 + f15 - f16 + f17 - f18) / rho + + 0.5 * porosity * Gy; + vz = (f5 - f6 + f11 - f12 - f13 + f14 + f15 - f16 - f17 + f18) / rho + + 0.5 * porosity * Gz; + v_mag = sqrt(vx * vx + vy * vy + vz * vz); + ux = vx / (c0 + sqrt(c0 * c0 + c1 * v_mag)); + uy = vy / (c0 + sqrt(c0 * c0 + c1 * v_mag)); + uz = vz / (c0 + sqrt(c0 * c0 + c1 * v_mag)); + u_mag = sqrt(ux * ux + uy * uy + uz * uz); //Update the total force to include linear (Darcy) and nonlinear (Forchheimer) drags due to the porous medium - Fx = -porosity*mu_eff/perm*ux - porosity*GeoFun/sqrt(perm)*u_mag*ux + porosity*Gx; - Fy = -porosity*mu_eff/perm*uy - porosity*GeoFun/sqrt(perm)*u_mag*uy + porosity*Gy; - Fz = -porosity*mu_eff/perm*uz - porosity*GeoFun/sqrt(perm)*u_mag*uz + porosity*Gz; - if (porosity==1.0){ - Fx=Gx; - Fy=Gy; - Fz=Gz; + Fx = -porosity * mu_eff / perm * ux - + porosity * GeoFun / sqrt(perm) * u_mag * ux + porosity * Gx; + Fy = -porosity * mu_eff / perm * uy - + porosity * GeoFun / sqrt(perm) * u_mag * uy + porosity * Gy; + Fz = -porosity * mu_eff / perm * uz - + porosity * GeoFun / sqrt(perm) * u_mag * uz + porosity * Gz; + if (porosity == 1.0) { + Fx = Gx; + Fy = Gy; + Fz = Gz; } //------------------------ BGK collison where body force has higher-order terms ----------------------------------------------------------// -// // q=0 -// dist[n] = f0*(1.0-rlx) + rlx*0.3333333333333333*rho*(1. - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) -// + 0.3333333333333333*rho*(1. - 0.5*rlx)*(Fx*(0. - (3.*ux)/porosity) + Fy*(0. - (3.*uy)/porosity) + Fz*(0. - (3.*uz)/porosity)); -// -// // q = 1 -// dist[nr2] = f1*(1.0-rlx) + rlx*0.05555555555555555*rho*(1 + 3.*ux + (4.5*ux*ux)/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) -// +0.05555555555555555*rho*(1. - 0.5*rlx)*(Fx*(3. + (6.*ux)/porosity) + Fy*(0. - (3.*uy)/porosity) + Fz*(0. - (3.*uz)/porosity)); -// -// // q=2 -// dist[nr1] = f2*(1.0-rlx) + rlx*0.05555555555555555*rho*(1 - 3.*ux + (4.5*ux*ux)/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) -// +0.05555555555555555*rho*(1. - 0.5*rlx)*(Fx*(-3. + (6.*ux)/porosity) + Fy*(0. - (3.*uy)/porosity) + Fz*(0. - (3.*uz)/porosity)); -// -// // q = 3 -// dist[nr4] = f3*(1.0-rlx) + rlx*0.05555555555555555*rho*(1 + 3.*uy + (4.5*uy*uy)/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) -// +0.05555555555555555*rho*(1. - 0.5*rlx)*(Fx*(0. - (3.*ux)/porosity) + Fy*(3. + (6.*uy)/porosity) + Fz*(0. - (3.*uz)/porosity)); -// -// // q = 4 -// dist[nr3] = f4*(1.0-rlx) + rlx*0.05555555555555555*rho*(1 - 3.*uy + (4.5*uy*uy)/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) -// +0.05555555555555555*rho*(1. - 0.5*rlx)*(Fx*(0. - (3.*ux)/porosity) + Fy*(-3. + (6.*uy)/porosity) + Fz*(0. - (3.*uz)/porosity)); -// -// // q = 5 -// dist[nr6] = f5*(1.0-rlx) + rlx*0.05555555555555555*rho*(1 + 3.*uz + (4.5*uz*uz)/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) -// +0.05555555555555555*rho*(1. - 0.5*rlx)*(Fx*(0. - (3.*ux)/porosity) + Fy*(0. - (3.*uy)/porosity) + Fz*(3. + (6.*uz)/porosity)); -// -// // q = 6 -// dist[nr5] = f6*(1.0-rlx) + rlx*0.05555555555555555*rho*(1 - 3.*uz + (4.5*uz*uz)/porosity - (1.5*(ux*ux+ uy*uy + uz*uz))/porosity) -// +0.05555555555555555*rho*(1. - 0.5*rlx)*(Fx*(0. - (3.*ux)/porosity) + Fy*(0. - (3.*uy)/porosity) + Fz*(-3. + (6.*uz)/porosity)); -// -// // q = 7 -// dist[nr8] = f7*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(ux + uy) + (4.5*(ux + uy)*(ux + uy))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) -// +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(3. - (3.*ux)/porosity + (9.*(ux + uy))/porosity) + Fy*(3. - (3.*uy)/porosity + (9.*(ux + uy))/porosity) + -// Fz*(0. - (3.*uz)/porosity)); -// -// // q = 8 -// dist[nr7] = f8*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(-ux - uy) + (4.5*(-ux - uy)*(-ux - uy))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) -// +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(-3. - (3.*ux)/porosity - (9.*(-ux - uy))/porosity) + Fy*(-3. - (9.*(-ux - uy))/porosity - (3.*uy)/porosity) + -// Fz*(0. - (3.*uz)/porosity)); -// -// // q = 9 -// dist[nr10] = f9*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(ux - uy) + (4.5*(ux - uy)*(ux - uy))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) -// +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(3. - (3.*ux)/porosity + (9.*(ux - uy))/porosity) + Fy*(-3. - (9.*(ux - uy))/porosity - (3.*uy)/porosity) + -// Fz*(0. - (3.*uz)/porosity)); -// -// // q = 10 -// dist[nr9] = f10*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(-ux + uy) + (4.5*(-ux + uy)*(-ux + uy))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) -// +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(-3. - (3.*ux)/porosity - (9.*(-ux + uy))/porosity) + Fy*(3. - (3.*uy)/porosity + (9.*(-ux + uy))/porosity) + -// Fz*(0. - (3.*uz)/porosity)); -// -// // q = 11 -// dist[nr12] = f11*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(ux + uz) + (4.5*(ux + uz)*(ux + uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) -// +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fy*(0. - (3.*uy)/porosity) + Fx*(3. - (3.*ux)/porosity + (9.*(ux + uz))/porosity) + -// Fz*(3. - (3.*uz)/porosity + (9.*(ux + uz))/porosity)); -// -// // q = 12 -// dist[nr11] = f12*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(-ux - uz) + (4.5*(-ux - uz)*(-ux - uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) -// +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fy*(0. - (3.*uy)/porosity) + Fx*(-3. - (3.*ux)/porosity - (9.*(-ux - uz))/porosity) + -// Fz*(-3. - (9.*(-ux - uz))/porosity - (3.*uz)/porosity)); -// -// // q = 13 -// dist[nr14] = f13*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(ux - uz) + (4.5*(ux - uz)*(ux - uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) -// +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fy*(0. - (3.*uy)/porosity) + Fx*(3. - (3.*ux)/porosity + (9.*(ux - uz))/porosity) + -// Fz*(-3. - (9.*(ux - uz))/porosity - (3.*uz)/porosity)); -// -// // q= 14 -// dist[nr13] = f14*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(-ux + uz) + (4.5*(-ux + uz)*(-ux + uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) -// +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fy*(0. - (3.*uy)/porosity) + Fx*(-3. - (3.*ux)/porosity - (9.*(-ux + uz))/porosity) + -// Fz*(3. - (3.*uz)/porosity + (9.*(-ux + uz))/porosity)); -// -// // q = 15 -// dist[nr16] = f15*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(uy + uz) + (4.5*(uy + uz)*(uy + uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) -// +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(0. - (3.*ux)/porosity) + Fy*(3. - (3.*uy)/porosity + (9.*(uy + uz))/porosity) + -// Fz*(3. - (3.*uz)/porosity + (9.*(uy + uz))/porosity)); -// -// // q = 16 -// dist[nr15] = f16*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(-uy - uz) + (4.5*(-uy - uz)*(-uy - uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) -// +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(0. - (3.*ux)/porosity) + Fy*(-3. - (3.*uy)/porosity - (9.*(-uy - uz))/porosity) + -// Fz*(-3. - (9.*(-uy - uz))/porosity - (3.*uz)/porosity)); -// -// // q = 17 -// dist[nr18] = f17*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(uy - uz) + (4.5*(uy - uz)*(uy - uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) -// +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(0. - (3.*ux)/porosity) + Fy*(3. - (3.*uy)/porosity + (9.*(uy - uz))/porosity) + -// Fz*(-3. - (9.*(uy - uz))/porosity - (3.*uz)/porosity)); -// -// // q = 18 -// dist[nr17] = f18*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(-uy + uz) + (4.5*(-uy + uz)*(-uy + uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) -// +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(0. - (3.*ux)/porosity) + Fy*(-3. - (3.*uy)/porosity - (9.*(-uy + uz))/porosity) + -// Fz*(3. - (3.*uz)/porosity + (9.*(-uy + uz))/porosity)); + // // q=0 + // dist[n] = f0*(1.0-rlx) + rlx*0.3333333333333333*rho*(1. - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) + // + 0.3333333333333333*rho*(1. - 0.5*rlx)*(Fx*(0. - (3.*ux)/porosity) + Fy*(0. - (3.*uy)/porosity) + Fz*(0. - (3.*uz)/porosity)); + // + // // q = 1 + // dist[nr2] = f1*(1.0-rlx) + rlx*0.05555555555555555*rho*(1 + 3.*ux + (4.5*ux*ux)/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) + // +0.05555555555555555*rho*(1. - 0.5*rlx)*(Fx*(3. + (6.*ux)/porosity) + Fy*(0. - (3.*uy)/porosity) + Fz*(0. - (3.*uz)/porosity)); + // + // // q=2 + // dist[nr1] = f2*(1.0-rlx) + rlx*0.05555555555555555*rho*(1 - 3.*ux + (4.5*ux*ux)/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) + // +0.05555555555555555*rho*(1. - 0.5*rlx)*(Fx*(-3. + (6.*ux)/porosity) + Fy*(0. - (3.*uy)/porosity) + Fz*(0. - (3.*uz)/porosity)); + // + // // q = 3 + // dist[nr4] = f3*(1.0-rlx) + rlx*0.05555555555555555*rho*(1 + 3.*uy + (4.5*uy*uy)/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) + // +0.05555555555555555*rho*(1. - 0.5*rlx)*(Fx*(0. - (3.*ux)/porosity) + Fy*(3. + (6.*uy)/porosity) + Fz*(0. - (3.*uz)/porosity)); + // + // // q = 4 + // dist[nr3] = f4*(1.0-rlx) + rlx*0.05555555555555555*rho*(1 - 3.*uy + (4.5*uy*uy)/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) + // +0.05555555555555555*rho*(1. - 0.5*rlx)*(Fx*(0. - (3.*ux)/porosity) + Fy*(-3. + (6.*uy)/porosity) + Fz*(0. - (3.*uz)/porosity)); + // + // // q = 5 + // dist[nr6] = f5*(1.0-rlx) + rlx*0.05555555555555555*rho*(1 + 3.*uz + (4.5*uz*uz)/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) + // +0.05555555555555555*rho*(1. - 0.5*rlx)*(Fx*(0. - (3.*ux)/porosity) + Fy*(0. - (3.*uy)/porosity) + Fz*(3. + (6.*uz)/porosity)); + // + // // q = 6 + // dist[nr5] = f6*(1.0-rlx) + rlx*0.05555555555555555*rho*(1 - 3.*uz + (4.5*uz*uz)/porosity - (1.5*(ux*ux+ uy*uy + uz*uz))/porosity) + // +0.05555555555555555*rho*(1. - 0.5*rlx)*(Fx*(0. - (3.*ux)/porosity) + Fy*(0. - (3.*uy)/porosity) + Fz*(-3. + (6.*uz)/porosity)); + // + // // q = 7 + // dist[nr8] = f7*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(ux + uy) + (4.5*(ux + uy)*(ux + uy))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) + // +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(3. - (3.*ux)/porosity + (9.*(ux + uy))/porosity) + Fy*(3. - (3.*uy)/porosity + (9.*(ux + uy))/porosity) + + // Fz*(0. - (3.*uz)/porosity)); + // + // // q = 8 + // dist[nr7] = f8*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(-ux - uy) + (4.5*(-ux - uy)*(-ux - uy))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) + // +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(-3. - (3.*ux)/porosity - (9.*(-ux - uy))/porosity) + Fy*(-3. - (9.*(-ux - uy))/porosity - (3.*uy)/porosity) + + // Fz*(0. - (3.*uz)/porosity)); + // + // // q = 9 + // dist[nr10] = f9*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(ux - uy) + (4.5*(ux - uy)*(ux - uy))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) + // +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(3. - (3.*ux)/porosity + (9.*(ux - uy))/porosity) + Fy*(-3. - (9.*(ux - uy))/porosity - (3.*uy)/porosity) + + // Fz*(0. - (3.*uz)/porosity)); + // + // // q = 10 + // dist[nr9] = f10*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(-ux + uy) + (4.5*(-ux + uy)*(-ux + uy))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) + // +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(-3. - (3.*ux)/porosity - (9.*(-ux + uy))/porosity) + Fy*(3. - (3.*uy)/porosity + (9.*(-ux + uy))/porosity) + + // Fz*(0. - (3.*uz)/porosity)); + // + // // q = 11 + // dist[nr12] = f11*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(ux + uz) + (4.5*(ux + uz)*(ux + uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) + // +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fy*(0. - (3.*uy)/porosity) + Fx*(3. - (3.*ux)/porosity + (9.*(ux + uz))/porosity) + + // Fz*(3. - (3.*uz)/porosity + (9.*(ux + uz))/porosity)); + // + // // q = 12 + // dist[nr11] = f12*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(-ux - uz) + (4.5*(-ux - uz)*(-ux - uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) + // +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fy*(0. - (3.*uy)/porosity) + Fx*(-3. - (3.*ux)/porosity - (9.*(-ux - uz))/porosity) + + // Fz*(-3. - (9.*(-ux - uz))/porosity - (3.*uz)/porosity)); + // + // // q = 13 + // dist[nr14] = f13*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(ux - uz) + (4.5*(ux - uz)*(ux - uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) + // +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fy*(0. - (3.*uy)/porosity) + Fx*(3. - (3.*ux)/porosity + (9.*(ux - uz))/porosity) + + // Fz*(-3. - (9.*(ux - uz))/porosity - (3.*uz)/porosity)); + // + // // q= 14 + // dist[nr13] = f14*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(-ux + uz) + (4.5*(-ux + uz)*(-ux + uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) + // +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fy*(0. - (3.*uy)/porosity) + Fx*(-3. - (3.*ux)/porosity - (9.*(-ux + uz))/porosity) + + // Fz*(3. - (3.*uz)/porosity + (9.*(-ux + uz))/porosity)); + // + // // q = 15 + // dist[nr16] = f15*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(uy + uz) + (4.5*(uy + uz)*(uy + uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) + // +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(0. - (3.*ux)/porosity) + Fy*(3. - (3.*uy)/porosity + (9.*(uy + uz))/porosity) + + // Fz*(3. - (3.*uz)/porosity + (9.*(uy + uz))/porosity)); + // + // // q = 16 + // dist[nr15] = f16*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(-uy - uz) + (4.5*(-uy - uz)*(-uy - uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) + // +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(0. - (3.*ux)/porosity) + Fy*(-3. - (3.*uy)/porosity - (9.*(-uy - uz))/porosity) + + // Fz*(-3. - (9.*(-uy - uz))/porosity - (3.*uz)/porosity)); + // + // // q = 17 + // dist[nr18] = f17*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(uy - uz) + (4.5*(uy - uz)*(uy - uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) + // +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(0. - (3.*ux)/porosity) + Fy*(3. - (3.*uy)/porosity + (9.*(uy - uz))/porosity) + + // Fz*(-3. - (9.*(uy - uz))/porosity - (3.*uz)/porosity)); + // + // // q = 18 + // dist[nr17] = f18*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(-uy + uz) + (4.5*(-uy + uz)*(-uy + uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) + // +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(0. - (3.*ux)/porosity) + Fy*(-3. - (3.*uy)/porosity - (9.*(-uy + uz))/porosity) + + // Fz*(3. - (3.*uz)/porosity + (9.*(-uy + uz))/porosity)); //----------------------------------------------------------------------------------------------------------------------------------------// - - //------------------------ BGK collison where body force has NO higher-order terms ----------------------------------------------------------// - // q=0 - dist[n] = f0*(1.0-rlx) + rlx*0.3333333333333333*rho*(1. - (1.5*(ux*ux + uy*uy + uz*uz))/porosity); + // q=0 + dist[n] = f0 * (1.0 - rlx) + + rlx * 0.3333333333333333 * rho * + (1. - (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity); - // q = 1 - dist[nr2] = f1*(1.0-rlx) + rlx*0.05555555555555555*rho*(1 + 3.*ux + (4.5*ux*ux)/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) - +0.05555555555555555*rho*(1. - 0.5*rlx)*(Fx*(3.)); + // q = 1 + dist[nr2] = f1 * (1.0 - rlx) + + rlx * 0.05555555555555555 * rho * + (1 + 3. * ux + (4.5 * ux * ux) / porosity - + (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity) + + 0.05555555555555555 * rho * (1. - 0.5 * rlx) * (Fx * (3.)); - // q=2 - dist[nr1] = f2*(1.0-rlx) + rlx*0.05555555555555555*rho*(1 - 3.*ux + (4.5*ux*ux)/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) - +0.05555555555555555*rho*(1. - 0.5*rlx)*(Fx*(-3.)); + // q=2 + dist[nr1] = f2 * (1.0 - rlx) + + rlx * 0.05555555555555555 * rho * + (1 - 3. * ux + (4.5 * ux * ux) / porosity - + (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity) + + 0.05555555555555555 * rho * (1. - 0.5 * rlx) * (Fx * (-3.)); - // q = 3 - dist[nr4] = f3*(1.0-rlx) + rlx*0.05555555555555555*rho*(1 + 3.*uy + (4.5*uy*uy)/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) - +0.05555555555555555*rho*(1. - 0.5*rlx)*(Fy*(3.)); + // q = 3 + dist[nr4] = f3 * (1.0 - rlx) + + rlx * 0.05555555555555555 * rho * + (1 + 3. * uy + (4.5 * uy * uy) / porosity - + (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity) + + 0.05555555555555555 * rho * (1. - 0.5 * rlx) * (Fy * (3.)); - // q = 4 - dist[nr3] = f4*(1.0-rlx) + rlx*0.05555555555555555*rho*(1 - 3.*uy + (4.5*uy*uy)/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) - +0.05555555555555555*rho*(1. - 0.5*rlx)*(Fy*(-3.)); + // q = 4 + dist[nr3] = f4 * (1.0 - rlx) + + rlx * 0.05555555555555555 * rho * + (1 - 3. * uy + (4.5 * uy * uy) / porosity - + (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity) + + 0.05555555555555555 * rho * (1. - 0.5 * rlx) * (Fy * (-3.)); - // q = 5 - dist[nr6] = f5*(1.0-rlx) + rlx*0.05555555555555555*rho*(1 + 3.*uz + (4.5*uz*uz)/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) - +0.05555555555555555*rho*(1. - 0.5*rlx)*(Fz*(3.)); + // q = 5 + dist[nr6] = f5 * (1.0 - rlx) + + rlx * 0.05555555555555555 * rho * + (1 + 3. * uz + (4.5 * uz * uz) / porosity - + (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity) + + 0.05555555555555555 * rho * (1. - 0.5 * rlx) * (Fz * (3.)); - // q = 6 - dist[nr5] = f6*(1.0-rlx) + rlx*0.05555555555555555*rho*(1 - 3.*uz + (4.5*uz*uz)/porosity - (1.5*(ux*ux+ uy*uy + uz*uz))/porosity) - +0.05555555555555555*rho*(1. - 0.5*rlx)*(Fz*(-3.)); + // q = 6 + dist[nr5] = f6 * (1.0 - rlx) + + rlx * 0.05555555555555555 * rho * + (1 - 3. * uz + (4.5 * uz * uz) / porosity - + (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity) + + 0.05555555555555555 * rho * (1. - 0.5 * rlx) * (Fz * (-3.)); - // q = 7 - dist[nr8] = f7*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(ux + uy) + (4.5*(ux + uy)*(ux + uy))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) - +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(3.) + Fy*(3.)); + // q = 7 + dist[nr8] = + f7 * (1.0 - rlx) + + rlx * 0.027777777777777776 * rho * + (1 + 3. * (ux + uy) + (4.5 * (ux + uy) * (ux + uy)) / porosity - + (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity) + + 0.027777777777777776 * rho * (1. - 0.5 * rlx) * + (Fx * (3.) + Fy * (3.)); - // q = 8 - dist[nr7] = f8*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(-ux - uy) + (4.5*(-ux - uy)*(-ux - uy))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) - +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(-3.) + Fy*(-3.)); + // q = 8 + dist[nr7] = f8 * (1.0 - rlx) + + rlx * 0.027777777777777776 * rho * + (1 + 3. * (-ux - uy) + + (4.5 * (-ux - uy) * (-ux - uy)) / porosity - + (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity) + + 0.027777777777777776 * rho * (1. - 0.5 * rlx) * + (Fx * (-3.) + Fy * (-3.)); - // q = 9 - dist[nr10] = f9*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(ux - uy) + (4.5*(ux - uy)*(ux - uy))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) - +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(3.) + Fy*(-3.)); + // q = 9 + dist[nr10] = + f9 * (1.0 - rlx) + + rlx * 0.027777777777777776 * rho * + (1 + 3. * (ux - uy) + (4.5 * (ux - uy) * (ux - uy)) / porosity - + (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity) + + 0.027777777777777776 * rho * (1. - 0.5 * rlx) * + (Fx * (3.) + Fy * (-3.)); - // q = 10 - dist[nr9] = f10*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(-ux + uy) + (4.5*(-ux + uy)*(-ux + uy))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) - +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(-3.) + Fy*(3.)); + // q = 10 + dist[nr9] = f10 * (1.0 - rlx) + + rlx * 0.027777777777777776 * rho * + (1 + 3. * (-ux + uy) + + (4.5 * (-ux + uy) * (-ux + uy)) / porosity - + (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity) + + 0.027777777777777776 * rho * (1. - 0.5 * rlx) * + (Fx * (-3.) + Fy * (3.)); - // q = 11 - dist[nr12] = f11*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(ux + uz) + (4.5*(ux + uz)*(ux + uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) - +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(3.) + Fz*(3.)); + // q = 11 + dist[nr12] = + f11 * (1.0 - rlx) + + rlx * 0.027777777777777776 * rho * + (1 + 3. * (ux + uz) + (4.5 * (ux + uz) * (ux + uz)) / porosity - + (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity) + + 0.027777777777777776 * rho * (1. - 0.5 * rlx) * + (Fx * (3.) + Fz * (3.)); - // q = 12 - dist[nr11] = f12*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(-ux - uz) + (4.5*(-ux - uz)*(-ux - uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) - +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(-3.) + Fz*(-3.)); + // q = 12 + dist[nr11] = f12 * (1.0 - rlx) + + rlx * 0.027777777777777776 * rho * + (1 + 3. * (-ux - uz) + + (4.5 * (-ux - uz) * (-ux - uz)) / porosity - + (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity) + + 0.027777777777777776 * rho * (1. - 0.5 * rlx) * + (Fx * (-3.) + Fz * (-3.)); - // q = 13 - dist[nr14] = f13*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(ux - uz) + (4.5*(ux - uz)*(ux - uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) - +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(3.) + Fz*(-3.)); + // q = 13 + dist[nr14] = + f13 * (1.0 - rlx) + + rlx * 0.027777777777777776 * rho * + (1 + 3. * (ux - uz) + (4.5 * (ux - uz) * (ux - uz)) / porosity - + (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity) + + 0.027777777777777776 * rho * (1. - 0.5 * rlx) * + (Fx * (3.) + Fz * (-3.)); - // q= 14 - dist[nr13] = f14*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(-ux + uz) + (4.5*(-ux + uz)*(-ux + uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) - +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fx*(-3.) + Fz*(3.)); + // q= 14 + dist[nr13] = f14 * (1.0 - rlx) + + rlx * 0.027777777777777776 * rho * + (1 + 3. * (-ux + uz) + + (4.5 * (-ux + uz) * (-ux + uz)) / porosity - + (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity) + + 0.027777777777777776 * rho * (1. - 0.5 * rlx) * + (Fx * (-3.) + Fz * (3.)); - // q = 15 - dist[nr16] = f15*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(uy + uz) + (4.5*(uy + uz)*(uy + uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) - +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fy*(3.) + Fz*(3.)); + // q = 15 + dist[nr16] = + f15 * (1.0 - rlx) + + rlx * 0.027777777777777776 * rho * + (1 + 3. * (uy + uz) + (4.5 * (uy + uz) * (uy + uz)) / porosity - + (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity) + + 0.027777777777777776 * rho * (1. - 0.5 * rlx) * + (Fy * (3.) + Fz * (3.)); - // q = 16 - dist[nr15] = f16*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(-uy - uz) + (4.5*(-uy - uz)*(-uy - uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) - +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fy*(-3.) + Fz*(-3.)); + // q = 16 + dist[nr15] = f16 * (1.0 - rlx) + + rlx * 0.027777777777777776 * rho * + (1 + 3. * (-uy - uz) + + (4.5 * (-uy - uz) * (-uy - uz)) / porosity - + (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity) + + 0.027777777777777776 * rho * (1. - 0.5 * rlx) * + (Fy * (-3.) + Fz * (-3.)); - // q = 17 - dist[nr18] = f17*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(uy - uz) + (4.5*(uy - uz)*(uy - uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) - +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fy*(3.) + Fz*(-3.)); + // q = 17 + dist[nr18] = + f17 * (1.0 - rlx) + + rlx * 0.027777777777777776 * rho * + (1 + 3. * (uy - uz) + (4.5 * (uy - uz) * (uy - uz)) / porosity - + (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity) + + 0.027777777777777776 * rho * (1. - 0.5 * rlx) * + (Fy * (3.) + Fz * (-3.)); - // q = 18 - dist[nr17] = f18*(1.0-rlx) + rlx*0.027777777777777776*rho*(1 + 3.*(-uy + uz) + (4.5*(-uy + uz)*(-uy + uz))/porosity - (1.5*(ux*ux + uy*uy + uz*uz))/porosity) - +0.027777777777777776*rho*(1. - 0.5*rlx)*(Fy*(-3.) + Fz*(3.)); + // q = 18 + dist[nr17] = f18 * (1.0 - rlx) + + rlx * 0.027777777777777776 * rho * + (1 + 3. * (-uy + uz) + + (4.5 * (-uy + uz) * (-uy + uz)) / porosity - + (1.5 * (ux * ux + uy * uy + uz * uz)) / porosity) + + 0.027777777777777776 * rho * (1. - 0.5 * rlx) * + (Fy * (-3.) + Fz * (3.)); //-------------------------------------------------------------------------------------------------------------------------------------------// - - //Update velocity on device - Velocity[0*Np+n] = ux; - Velocity[1*Np+n] = uy; - Velocity[2*Np+n] = uz; + Velocity[0 * Np + n] = ux; + Velocity[1 * Np + n] = uy; + Velocity[2 * Np + n] = uz; //Update pressure on device Pressure[n] = pressure; - } + } } - -extern "C" void ScaLBL_D3Q19_AAeven_Greyscale_IMRT(double *dist, int start, int finish, int Np, double rlx, double rlx_eff, double Gx, double Gy, double Gz, - double *Poros,double *Perm, double *Velocity, double Den,double *Pressure){ - double vx,vy,vz,v_mag; - double ux,uy,uz,u_mag; - double pressure;//defined for this incompressible model - // conserved momemnts - double jx,jy,jz; - // non-conserved moments - double m1,m2,m4,m6,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18; +extern "C" void ScaLBL_D3Q19_AAeven_Greyscale_IMRT( + double *dist, int start, int finish, int Np, double rlx, double rlx_eff, + double Gx, double Gy, double Gz, double *Poros, double *Perm, + double *Velocity, double Den, double *Pressure) { + double vx, vy, vz, v_mag; + double ux, uy, uz, u_mag; + double pressure; //defined for this incompressible model + // conserved momemnts + double jx, jy, jz; + // non-conserved moments + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; double fq; - //double f0,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18; - double GeoFun;//geometric function from Guo's PRE 66, 036304 (2002) + //double f0,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18; + double GeoFun; //geometric function from Guo's PRE 66, 036304 (2002) double porosity; - double perm;//voxel permeability - double c0, c1; //Guo's model parameters - double mu_eff = (1.0/rlx_eff-0.5)/3.0;//kinematic viscosity - double Fx, Fy, Fz;//The total body force including Brinkman force and user-specified (Gx,Gy,Gz) + double perm; //voxel permeability + double c0, c1; //Guo's model parameters + double mu_eff = (1.0 / rlx_eff - 0.5) / 3.0; //kinematic viscosity + double Fx, Fy, + Fz; //The total body force including Brinkman force and user-specified (Gx,Gy,Gz) double rlx_setA = rlx; - double rlx_setB = 8.f*(2.f-rlx_setA)/(8.f-rlx_setA); + double rlx_setB = 8.f * (2.f - rlx_setA) / (8.f - rlx_setA); + + const double mrt_V1 = 0.05263157894736842; + const double mrt_V2 = 0.012531328320802; + const double mrt_V3 = 0.04761904761904762; + const double mrt_V4 = 0.004594820384294068; + const double mrt_V5 = 0.01587301587301587; + const double mrt_V6 = 0.0555555555555555555555555; + const double mrt_V7 = 0.02777777777777778; + const double mrt_V8 = 0.08333333333333333; + const double mrt_V9 = 0.003341687552213868; + const double mrt_V10 = 0.003968253968253968; + const double mrt_V11 = 0.01388888888888889; + const double mrt_V12 = 0.04166666666666666; + + for (int n = start; n < finish; n++) { - const double mrt_V1=0.05263157894736842; - const double mrt_V2=0.012531328320802; - const double mrt_V3=0.04761904761904762; - const double mrt_V4=0.004594820384294068; - const double mrt_V5=0.01587301587301587; - const double mrt_V6=0.0555555555555555555555555; - const double mrt_V7=0.02777777777777778; - const double mrt_V8=0.08333333333333333; - const double mrt_V9=0.003341687552213868; - const double mrt_V10=0.003968253968253968; - const double mrt_V11=0.01388888888888889; - const double mrt_V12=0.04166666666666666; - - for (int n=start; n 10Np => odd part of dist) - fq = dist[nread]; // reading the f1 data into register fq - pressure = fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jx = fq; - m4 = -4.0*fq; - m9 = 2.0*fq; - m10 = -4.0*fq; - - // q=2 - nread = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) - fq = dist[nread]; // reading the f2 data into register fq - pressure += fq; - m1 -= 11.0*(fq); - m2 -= 4.0*(fq); - jx -= fq; - m4 += 4.0*(fq); - m9 += 2.0*(fq); - m10 -= 4.0*(fq); - - // q=3 - nread = neighborList[n+2*Np]; // neighbor 4 - fq = dist[nread]; - pressure += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jy = fq; - m6 = -4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 = fq; - m12 = -2.0*fq; - - // q = 4 - nread = neighborList[n+3*Np]; // neighbor 3 - fq = dist[nread]; - pressure += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jy -= fq; - m6 += 4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 += fq; - m12 -= 2.0*fq; - - // q=5 - nread = neighborList[n+4*Np]; - fq = dist[nread]; - pressure += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jz = fq; - m8 = -4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 -= fq; - m12 += 2.0*fq; - - // q = 6 - nread = neighborList[n+5*Np]; - fq = dist[nread]; - pressure += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jz -= fq; - m8 += 4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 -= fq; - m12 += 2.0*fq; - - // q=7 - nread = neighborList[n+6*Np]; - fq = dist[nread]; - pressure += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jy += fq; - m6 += fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 = fq; - m16 = fq; - m17 = -fq; - - // q = 8 - nread = neighborList[n+7*Np]; - fq = dist[nread]; - pressure += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jy -= fq; - m6 -= fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 += fq; - m16 -= fq; - m17 += fq; - - // q=9 - nread = neighborList[n+8*Np]; - - fq = dist[nread]; - pressure += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jy -= fq; - m6 -= fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 -= fq; - m16 += fq; - m17 += fq; - - // q = 10 - nread = neighborList[n+9*Np]; - fq = dist[nread]; - pressure += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jy += fq; - m6 += fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 -= fq; - m16 -= fq; - m17 -= fq; - - // q=11 - nread = neighborList[n+10*Np]; - fq = dist[nread]; - pressure += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jz += fq; - m8 += fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 = fq; - m16 -= fq; - m18 = fq; - - // q=12 - nread = neighborList[n+11*Np]; - fq = dist[nread]; - pressure += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jz -= fq; - m8 -= fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 += fq; - m16 += fq; - m18 -= fq; - - // q=13 - nread = neighborList[n+12*Np]; - fq = dist[nread]; - pressure += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jz -= fq; - m8 -= fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 -= fq; - m16 -= fq; - m18 -= fq; - - // q=14 - nread = neighborList[n+13*Np]; - fq = dist[nread]; - pressure += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jz += fq; - m8 += fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 -= fq; - m16 += fq; - m18 += fq; - - // q=15 - nread = neighborList[n+14*Np]; - fq = dist[nread]; - pressure += fq; - m1 += 8.0*fq; - m2 += fq; - jy += fq; - m6 += fq; - jz += fq; - m8 += fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 = fq; - m17 += fq; - m18 -= fq; - - // q=16 - nread = neighborList[n+15*Np]; - fq = dist[nread]; - pressure += fq; - m1 += 8.0*fq; - m2 += fq; - jy -= fq; - m6 -= fq; - jz -= fq; - m8 -= fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 += fq; - m17 -= fq; - m18 += fq; - - // q=17 - nread = neighborList[n+16*Np]; - fq = dist[nread]; - pressure += fq; - m1 += 8.0*fq; - m2 += fq; - jy += fq; - m6 += fq; - jz -= fq; - m8 -= fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 -= fq; - m17 += fq; - m18 += fq; - - // q=18 - nread = neighborList[n+17*Np]; - fq = dist[nread]; - pressure += fq; - m1 += 8.0*fq; - m2 += fq; - jy -= fq; - m6 -= fq; - jz += fq; - m8 += fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 -= fq; - m17 -= fq; - m18 -= fq; - //---------------------------------------------------------------------// - - porosity = Poros[n]; - perm = Perm[n]; - - c0 = 0.5*(1.0+porosity*0.5*mu_eff/perm); - if (porosity==1.0) c0 = 0.5;//i.e. apparent pore nodes - GeoFun = 1.75/sqrt(150.0*porosity*porosity*porosity); - c1 = porosity*0.5*GeoFun/sqrt(perm); - if (porosity==1.0) c1 = 0.0;//i.e. apparent pore nodes - - vx = jx/Den+0.5*porosity*Gx; - vy = jy/Den+0.5*porosity*Gy; - vz = jz/Den+0.5*porosity*Gz; - v_mag=sqrt(vx*vx+vy*vy+vz*vz); - ux = vx/(c0+sqrt(c0*c0+c1*v_mag)); - uy = vy/(c0+sqrt(c0*c0+c1*v_mag)); - uz = vz/(c0+sqrt(c0*c0+c1*v_mag)); - u_mag=sqrt(ux*ux+uy*uy+uz*uz); - - //Update the total force to include linear (Darcy) and nonlinear (Forchheimer) drags due to the porous medium - Fx = Den*(-porosity*mu_eff/perm*ux - porosity*GeoFun/sqrt(perm)*u_mag*ux + porosity*Gx); - Fy = Den*(-porosity*mu_eff/perm*uy - porosity*GeoFun/sqrt(perm)*u_mag*uy + porosity*Gy); - Fz = Den*(-porosity*mu_eff/perm*uz - porosity*GeoFun/sqrt(perm)*u_mag*uz + porosity*Gz); - if (porosity==1.0){ - Fx=Den*Gx; - Fy=Den*Gy; - Fz=Den*Gz; - } - - //Calculate pressure for Incompressible-MRT model - pressure=0.5/porosity*(pressure-0.5*Den*u_mag*u_mag/porosity); - - //-------------------- IMRT collison where body force has higher-order terms -------------// -// //..............carry out relaxation process............................................... -// m1 = m1 + rlx_setA*((-30*Den+19*Den*(ux*ux+uy*uy+uz*uz)/porosity + 57*pressure*porosity) - m1) -// + (1-0.5*rlx_setA)*38*(Fx*ux+Fy*uy+Fz*uz)/porosity; -// m2 = m2 + rlx_setA*((12*Den - 5.5*Den*(ux*ux+uy*uy+uz*uz)/porosity-27*pressure*porosity) - m2) -// + (1-0.5*rlx_setA)*11*(-Fx*ux-Fy*uy-Fz*uz)/porosity; -// jx = jx + Fx; -// m4 = m4 + rlx_setB*((-0.6666666666666666*ux*Den) - m4) -// + (1-0.5*rlx_setB)*(-0.6666666666666666*Fx); -// jy = jy + Fy; -// m6 = m6 + rlx_setB*((-0.6666666666666666*uy*Den) - m6) -// + (1-0.5*rlx_setB)*(-0.6666666666666666*Fy); -// jz = jz + Fz; -// m8 = m8 + rlx_setB*((-0.6666666666666666*uz*Den) - m8) -// + (1-0.5*rlx_setB)*(-0.6666666666666666*Fz); -// m9 = m9 + rlx_setA*((Den*(2*ux*ux-uy*uy-uz*uz)/porosity) - m9) -// + (1-0.5*rlx_setA)*(4*Fx*ux-2*Fy*uy-2*Fz*uz)/porosity; -// m10 = m10 + rlx_setA*(-0.5*Den*((2*ux*ux-uy*uy-uz*uz)/porosity)- m10) -// + (1-0.5*rlx_setA)*(-2*Fx*ux+Fy*uy+Fz*uz)/porosity; -// m11 = m11 + rlx_setA*((Den*(uy*uy-uz*uz)/porosity) - m11) -// + (1-0.5*rlx_setA)*(2*Fy*uy-2*Fz*uz)/porosity; -// m12 = m12 + rlx_setA*(-0.5*(Den*(uy*uy-uz*uz)/porosity)- m12) -// + (1-0.5*rlx_setA)*(-Fy*uy+Fz*uz)/porosity; -// m13 = m13 + rlx_setA*((Den*ux*uy/porosity) - m13) -// + (1-0.5*rlx_setA)*(Fy*ux+Fx*uy)/porosity; -// m14 = m14 + rlx_setA*((Den*uy*uz/porosity) - m14) -// + (1-0.5*rlx_setA)*(Fz*uy+Fy*uz)/porosity; -// m15 = m15 + rlx_setA*((Den*ux*uz/porosity) - m15) -// + (1-0.5*rlx_setA)*(Fz*ux+Fx*uz)/porosity; -// m16 = m16 + rlx_setB*( - m16); -// m17 = m17 + rlx_setB*( - m17); -// m18 = m18 + rlx_setB*( - m18); -// //....................................................................................................... - - - //-------------------- IMRT collison where body force has NO higher-order terms -------------// - //..............carry out relaxation process............................................... - m1 = m1 + rlx_setA*((-30*Den+19*Den*(ux*ux+uy*uy+uz*uz)/porosity + 57*pressure*porosity) - m1); - m2 = m2 + rlx_setA*((12*Den - 5.5*Den*(ux*ux+uy*uy+uz*uz)/porosity-27*pressure*porosity) - m2); - jx = jx + Fx; - m4 = m4 + rlx_setB*((-0.6666666666666666*ux*Den) - m4) - + (1-0.5*rlx_setB)*(-0.6666666666666666*Fx); - jy = jy + Fy; - m6 = m6 + rlx_setB*((-0.6666666666666666*uy*Den) - m6) - + (1-0.5*rlx_setB)*(-0.6666666666666666*Fy); - jz = jz + Fz; - m8 = m8 + rlx_setB*((-0.6666666666666666*uz*Den) - m8) - + (1-0.5*rlx_setB)*(-0.6666666666666666*Fz); - m9 = m9 + rlx_setA*((Den*(2*ux*ux-uy*uy-uz*uz)/porosity) - m9); - m10 = m10 + rlx_setA*(-0.5*Den*((2*ux*ux-uy*uy-uz*uz)/porosity)- m10); - m11 = m11 + rlx_setA*((Den*(uy*uy-uz*uz)/porosity) - m11); - m12 = m12 + rlx_setA*(-0.5*(Den*(uy*uy-uz*uz)/porosity)- m12); - m13 = m13 + rlx_setA*((Den*ux*uy/porosity) - m13); - m14 = m14 + rlx_setA*((Den*uy*uz/porosity) - m14); - m15 = m15 + rlx_setA*((Den*ux*uz/porosity) - m15); - m16 = m16 + rlx_setB*( - m16); - m17 = m17 + rlx_setB*( - m17); - m18 = m18 + rlx_setB*( - m18); - //....................................................................................................... - - - //.................inverse transformation...................................................... - // q=0 - fq = mrt_V1*Den-mrt_V2*m1+mrt_V3*m2; - dist[n] = fq; - - // q = 1 - fq = mrt_V1*Den-mrt_V4*m1-mrt_V5*m2+0.1*(jx-m4)+mrt_V6*(m9-m10); - nread = neighborList[n+Np]; - dist[nread] = fq; - - // q=2 - fq = mrt_V1*Den-mrt_V4*m1-mrt_V5*m2+0.1*(m4-jx)+mrt_V6*(m9-m10); - nread = neighborList[n]; - dist[nread] = fq; - - // q = 3 - fq = mrt_V1*Den-mrt_V4*m1-mrt_V5*m2+0.1*(jy-m6)+mrt_V7*(m10-m9)+mrt_V8*(m11-m12); - nread = neighborList[n+3*Np]; - dist[nread] = fq; - - // q = 4 - fq = mrt_V1*Den-mrt_V4*m1-mrt_V5*m2+0.1*(m6-jy)+mrt_V7*(m10-m9)+mrt_V8*(m11-m12); - nread = neighborList[n+2*Np]; - dist[nread] = fq; - - // q = 5 - fq = mrt_V1*Den-mrt_V4*m1-mrt_V5*m2+0.1*(jz-m8)+mrt_V7*(m10-m9)+mrt_V8*(m12-m11); - nread = neighborList[n+5*Np]; - dist[nread] = fq; - - // q = 6 - fq = mrt_V1*Den-mrt_V4*m1-mrt_V5*m2+0.1*(m8-jz)+mrt_V7*(m10-m9)+mrt_V8*(m12-m11); - nread = neighborList[n+4*Np]; - dist[nread] = fq; - - // q = 7 - fq = mrt_V1*Den+mrt_V9*m1+mrt_V10*m2+0.1*(jx+jy)+0.025*(m4+m6)+mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12+0.25*m13+0.125*(m16-m17); - nread = neighborList[n+7*Np]; - dist[nread] = fq; - - // q = 8 - fq = mrt_V1*Den+mrt_V9*m1+mrt_V10*m2-0.1*(jx+jy)-0.025*(m4+m6) +mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12+0.25*m13+0.125*(m17-m16); - nread = neighborList[n+6*Np]; - dist[nread] = fq; - - // q = 9 - fq = mrt_V1*Den+mrt_V9*m1+mrt_V10*m2+0.1*(jx-jy)+0.025*(m4-m6)+mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12-0.25*m13+0.125*(m16+m17); - nread = neighborList[n+9*Np]; - dist[nread] = fq; - - // q = 10 - fq = mrt_V1*Den+mrt_V9*m1+mrt_V10*m2+0.1*(jy-jx)+0.025*(m6-m4)+mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12-0.25*m13-0.125*(m16+m17); - nread = neighborList[n+8*Np]; - dist[nread] = fq; - - // q = 11 - fq = mrt_V1*Den+mrt_V9*m1+mrt_V10*m2+0.1*(jx+jz)+0.025*(m4+m8)+mrt_V7*m9+mrt_V11*m10-mrt_V8*m11-mrt_V12*m12+0.25*m15+0.125*(m18-m16); - nread = neighborList[n+11*Np]; - dist[nread] = fq; - - // q = 12 - fq = mrt_V1*Den+mrt_V9*m1+mrt_V10*m2-0.1*(jx+jz)-0.025*(m4+m8)+mrt_V7*m9+mrt_V11*m10-mrt_V8*m11-mrt_V12*m12+0.25*m15+0.125*(m16-m18); - nread = neighborList[n+10*Np]; - dist[nread]= fq; - - // q = 13 - fq = mrt_V1*Den+mrt_V9*m1+mrt_V10*m2+0.1*(jx-jz)+0.025*(m4-m8)+mrt_V7*m9+mrt_V11*m10-mrt_V8*m11-mrt_V12*m12-0.25*m15-0.125*(m16+m18); - nread = neighborList[n+13*Np]; - dist[nread] = fq; - - // q= 14 - fq = mrt_V1*Den+mrt_V9*m1+mrt_V10*m2+0.1*(jz-jx)+0.025*(m8-m4)+mrt_V7*m9+mrt_V11*m10-mrt_V8*m11-mrt_V12*m12-0.25*m15+0.125*(m16+m18); - nread = neighborList[n+12*Np]; - dist[nread] = fq; - - // q = 15 - fq = mrt_V1*Den+mrt_V9*m1+mrt_V10*m2+0.1*(jy+jz)+0.025*(m6+m8)-mrt_V6*m9-mrt_V7*m10+0.25*m14+0.125*(m17-m18); - nread = neighborList[n+15*Np]; - dist[nread] = fq; - - // q = 16 - fq = mrt_V1*Den+mrt_V9*m1+mrt_V10*m2-0.1*(jy+jz)-0.025*(m6+m8)-mrt_V6*m9-mrt_V7*m10+0.25*m14+0.125*(m18-m17); - nread = neighborList[n+14*Np]; - dist[nread] = fq; - - // q = 17 - fq = mrt_V1*Den+mrt_V9*m1+mrt_V10*m2+0.1*(jy-jz)+0.025*(m6-m8)-mrt_V6*m9-mrt_V7*m10-0.25*m14+0.125*(m17+m18); - nread = neighborList[n+17*Np]; - dist[nread] = fq; - - // q = 18 - fq = mrt_V1*Den+mrt_V9*m1+mrt_V10*m2+0.1*(jz-jy)+0.025*(m8-m6)-mrt_V6*m9-mrt_V7*m10-0.25*m14-0.125*(m17+m18); - nread = neighborList[n+16*Np]; - dist[nread] = fq; - //........................................................................ - - //Update velocity on device - Velocity[0*Np+n] = ux; - Velocity[1*Np+n] = uy; - Velocity[2*Np+n] = uz; - //Update pressure on device - Pressure[n] = pressure; - } -} - - -extern "C" void ScaLBL_D3Q19_AAodd_Greyscale_MRT(int *neighborList, double *dist, int start, int finish, int Np, double rlx, double rlx_eff, double Gx, double Gy, double Gz,double *Poros,double *Perm, double *Velocity,double rho0,double *Pressure){ - - int nread; - int nr1,nr2,nr3,nr4,nr5,nr6; - int nr7,nr8,nr9,nr10; - int nr11,nr12,nr13,nr14; - double vx,vy,vz,v_mag; - double ux,uy,uz,u_mag; - double pressure;//defined for this incompressible model - // conserved momemnts - double rho,jx,jy,jz; - // non-conserved moments - double m1,m2,m4,m6,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18; - double fq; - //double f0,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18; - double GeoFun;//geometric function from Guo's PRE 66, 036304 (2002) - double porosity; - double perm;//voxel permeability - double c0, c1; //Guo's model parameters - double mu_eff = (1.0/rlx_eff-0.5)/3.0;//kinematic viscosity - double Fx, Fy, Fz;//The total body force including Brinkman force and user-specified (Gx,Gy,Gz) - double rlx_setA = rlx; - double rlx_setB = 8.f*(2.f-rlx_setA)/(8.f-rlx_setA); - - const double mrt_V1=0.05263157894736842; - const double mrt_V2=0.012531328320802; - const double mrt_V3=0.04761904761904762; - const double mrt_V4=0.004594820384294068; - const double mrt_V5=0.01587301587301587; - const double mrt_V6=0.0555555555555555555555555; - const double mrt_V7=0.02777777777777778; - const double mrt_V8=0.08333333333333333; - const double mrt_V9=0.003341687552213868; - const double mrt_V10=0.003968253968253968; - const double mrt_V11=0.01388888888888889; - const double mrt_V12=0.04166666666666666; - - for (int n=start; n even part of dist) - //fq = dist[nread]; // reading the f2 data into register fq - nr2 = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) - fq = dist[nr2]; // reading the f2 data into register fq - rho += fq; - m1 -= 11.0*(fq); - m2 -= 4.0*(fq); - jx -= fq; - m4 += 4.0*(fq); - m9 += 2.0*(fq); - m10 -= 4.0*(fq); + // f2 = dist[10*Np+n]; + fq = dist[1 * Np + n]; + pressure += fq; + m1 -= 11.0 * (fq); + m2 -= 4.0 * (fq); + jx -= fq; + m4 += 4.0 * (fq); + m9 += 2.0 * (fq); + m10 -= 4.0 * (fq); - // q=3 - //nread = neighborList[n+2*Np]; // neighbor 4 - //fq = dist[nread]; - nr3 = neighborList[n+2*Np]; // neighbor 4 - fq = dist[nr3]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jy = fq; - m6 = -4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 = fq; - m12 = -2.0*fq; + // q=3 + fq = dist[4 * Np + n]; + pressure += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy = fq; + m6 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 = fq; + m12 = -2.0 * fq; - // q = 4 - //nread = neighborList[n+3*Np]; // neighbor 3 - //fq = dist[nread]; - nr4 = neighborList[n+3*Np]; // neighbor 3 - fq = dist[nr4]; - rho+= fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jy -= fq; - m6 += 4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 += fq; - m12 -= 2.0*fq; + // q = 4 + fq = dist[3 * Np + n]; + pressure += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy -= fq; + m6 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 += fq; + m12 -= 2.0 * fq; - // q=5 - //nread = neighborList[n+4*Np]; - //fq = dist[nread]; - nr5 = neighborList[n+4*Np]; - fq = dist[nr5]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jz = fq; - m8 = -4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 -= fq; - m12 += 2.0*fq; + // q=5 + fq = dist[6 * Np + n]; + pressure += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz = fq; + m8 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; + // q = 6 + fq = dist[5 * Np + n]; + pressure += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz -= fq; + m8 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; - // q = 6 - //nread = neighborList[n+5*Np]; - //fq = dist[nread]; - nr6 = neighborList[n+5*Np]; - fq = dist[nr6]; - rho+= fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jz -= fq; - m8 += 4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 -= fq; - m12 += 2.0*fq; + // q=7 + fq = dist[8 * Np + n]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 = fq; + m16 = fq; + m17 = -fq; - // q=7 - //nread = neighborList[n+6*Np]; - //fq = dist[nread]; - nr7 = neighborList[n+6*Np]; - - fq = dist[nr7]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jy += fq; - m6 += fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 = fq; - m16 = fq; - m17 = -fq; + // q = 8 + fq = dist[7 * Np + n]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 += fq; + m16 -= fq; + m17 += fq; - // q = 8 - //nread = neighborList[n+7*Np]; - //fq = dist[nread]; - nr8 = neighborList[n+7*Np]; - fq = dist[nr8]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jy -= fq; - m6 -= fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 += fq; - m16 -= fq; - m17 += fq; + // q=9 + fq = dist[10 * Np + n]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 += fq; + m17 += fq; - // q=9 - //nread = neighborList[n+8*Np]; - //fq = dist[nread]; - nr9 = neighborList[n+8*Np]; - fq = dist[nr9]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jy -= fq; - m6 -= fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 -= fq; - m16 += fq; - m17 += fq; + // q = 10 + fq = dist[9 * Np + n]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 -= fq; + m17 -= fq; - // q = 10 - //nread = neighborList[n+9*Np]; - //fq = dist[nread]; - nr10 = neighborList[n+9*Np]; - fq = dist[nr10]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jy += fq; - m6 += fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 -= fq; - m16 -= fq; - m17 -= fq; + // q=11 + fq = dist[12 * Np + n]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 = fq; + m16 -= fq; + m18 = fq; - // q=11 - //nread = neighborList[n+10*Np]; - //fq = dist[nread]; - nr11 = neighborList[n+10*Np]; - fq = dist[nr11]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jz += fq; - m8 += fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 = fq; - m16 -= fq; - m18 = fq; + // q=12 + fq = dist[11 * Np + n]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 += fq; + m16 += fq; + m18 -= fq; - // q=12 - //nread = neighborList[n+11*Np]; - //fq = dist[nread]; - nr12 = neighborList[n+11*Np]; - fq = dist[nr12]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jz -= fq; - m8 -= fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 += fq; - m16 += fq; - m18 -= fq; + // q=13 + fq = dist[14 * Np + n]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 -= fq; + m18 -= fq; - // q=13 - //nread = neighborList[n+12*Np]; - //fq = dist[nread]; - nr13 = neighborList[n+12*Np]; - fq = dist[nr13]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jz -= fq; - m8 -= fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 -= fq; - m16 -= fq; - m18 -= fq; + // q=14 + fq = dist[13 * Np + n]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 += fq; + m18 += fq; - // q=14 - //nread = neighborList[n+13*Np]; - //fq = dist[nread]; - nr14 = neighborList[n+13*Np]; - fq = dist[nr14]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jz += fq; - m8 += fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 -= fq; - m16 += fq; - m18 += fq; + // q=15 + fq = dist[16 * Np + n]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 = fq; + m17 += fq; + m18 -= fq; - // q=15 - nread = neighborList[n+14*Np]; - fq = dist[nread]; - //fq = dist[17*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy += fq; - m6 += fq; - jz += fq; - m8 += fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 = fq; - m17 += fq; - m18 -= fq; + // q=16 + fq = dist[15 * Np + n]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 += fq; + m17 -= fq; + m18 += fq; - // q=16 - nread = neighborList[n+15*Np]; - fq = dist[nread]; - //fq = dist[8*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy -= fq; - m6 -= fq; - jz -= fq; - m8 -= fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 += fq; - m17 -= fq; - m18 += fq; + // q=17 + fq = dist[18 * Np + n]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 += fq; + m18 += fq; - // q=17 - //fq = dist[18*Np+n]; - nread = neighborList[n+16*Np]; - fq = dist[nread]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy += fq; - m6 += fq; - jz -= fq; - m8 -= fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 -= fq; - m17 += fq; - m18 += fq; - - // q=18 - nread = neighborList[n+17*Np]; - fq = dist[nread]; - //fq = dist[9*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy -= fq; - m6 -= fq; - jz += fq; - m8 += fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 -= fq; - m17 -= fq; - m18 -= fq; + // q=18 + fq = dist[17 * Np + n]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 -= fq; + m18 -= fq; //---------------------------------------------------------------------// porosity = Poros[n]; perm = Perm[n]; - c0 = 0.5*(1.0+porosity*0.5*mu_eff/perm); - if (porosity==1.0) c0 = 0.5;//i.e. apparent pore nodes - GeoFun = 1.75/sqrt(150.0*porosity*porosity*porosity); - c1 = porosity*0.5*GeoFun/sqrt(perm); - if (porosity==1.0) c1 = 0.0;//i.e. apparent pore nodes + c0 = 0.5 * (1.0 + porosity * 0.5 * mu_eff / perm); + if (porosity == 1.0) + c0 = 0.5; //i.e. apparent pore nodes + GeoFun = 1.75 / sqrt(150.0 * porosity * porosity * porosity); + c1 = porosity * 0.5 * GeoFun / sqrt(perm); + if (porosity == 1.0) + c1 = 0.0; //i.e. apparent pore nodes - vx = jx/rho0+0.5*porosity*Gx; - vy = jy/rho0+0.5*porosity*Gy; - vz = jz/rho0+0.5*porosity*Gz; - v_mag=sqrt(vx*vx+vy*vy+vz*vz); - ux = vx/(c0+sqrt(c0*c0+c1*v_mag)); - uy = vy/(c0+sqrt(c0*c0+c1*v_mag)); - uz = vz/(c0+sqrt(c0*c0+c1*v_mag)); - u_mag=sqrt(ux*ux+uy*uy+uz*uz); + vx = jx / Den + 0.5 * porosity * Gx; + vy = jy / Den + 0.5 * porosity * Gy; + vz = jz / Den + 0.5 * porosity * Gz; + v_mag = sqrt(vx * vx + vy * vy + vz * vz); + ux = vx / (c0 + sqrt(c0 * c0 + c1 * v_mag)); + uy = vy / (c0 + sqrt(c0 * c0 + c1 * v_mag)); + uz = vz / (c0 + sqrt(c0 * c0 + c1 * v_mag)); + u_mag = sqrt(ux * ux + uy * uy + uz * uz); //Update the total force to include linear (Darcy) and nonlinear (Forchheimer) drags due to the porous medium - Fx = rho0*(-porosity*mu_eff/perm*ux - porosity*GeoFun/sqrt(perm)*u_mag*ux + porosity*Gx); - Fy = rho0*(-porosity*mu_eff/perm*uy - porosity*GeoFun/sqrt(perm)*u_mag*uy + porosity*Gy); - Fz = rho0*(-porosity*mu_eff/perm*uz - porosity*GeoFun/sqrt(perm)*u_mag*uz + porosity*Gz); - if (porosity==1.0){ - Fx=rho0*Gx; - - Fy=rho0*Gy; - Fz=rho0*Gz; + Fx = + Den * (-porosity * mu_eff / perm * ux - + porosity * GeoFun / sqrt(perm) * u_mag * ux + porosity * Gx); + Fy = + Den * (-porosity * mu_eff / perm * uy - + porosity * GeoFun / sqrt(perm) * u_mag * uy + porosity * Gy); + Fz = + Den * (-porosity * mu_eff / perm * uz - + porosity * GeoFun / sqrt(perm) * u_mag * uz + porosity * Gz); + if (porosity == 1.0) { + Fx = Den * Gx; + Fy = Den * Gy; + Fz = Den * Gz; + } + + //Calculate pressure for Incompressible-MRT model + pressure = + 0.5 / porosity * (pressure - 0.5 * Den * u_mag * u_mag / porosity); + + //-------------------- IMRT collison where body force has higher-order terms -------------// + // //..............carry out relaxation process............................................... + // m1 = m1 + rlx_setA*((-30*Den+19*Den*(ux*ux+uy*uy+uz*uz)/porosity + 57*pressure*porosity) - m1) + // + (1-0.5*rlx_setA)*38*(Fx*ux+Fy*uy+Fz*uz)/porosity; + // m2 = m2 + rlx_setA*((12*Den - 5.5*Den*(ux*ux+uy*uy+uz*uz)/porosity-27*pressure*porosity) - m2) + // + (1-0.5*rlx_setA)*11*(-Fx*ux-Fy*uy-Fz*uz)/porosity; + // jx = jx + Fx; + // m4 = m4 + rlx_setB*((-0.6666666666666666*ux*Den) - m4) + // + (1-0.5*rlx_setB)*(-0.6666666666666666*Fx); + // jy = jy + Fy; + // m6 = m6 + rlx_setB*((-0.6666666666666666*uy*Den) - m6) + // + (1-0.5*rlx_setB)*(-0.6666666666666666*Fy); + // jz = jz + Fz; + // m8 = m8 + rlx_setB*((-0.6666666666666666*uz*Den) - m8) + // + (1-0.5*rlx_setB)*(-0.6666666666666666*Fz); + // m9 = m9 + rlx_setA*((Den*(2*ux*ux-uy*uy-uz*uz)/porosity) - m9) + // + (1-0.5*rlx_setA)*(4*Fx*ux-2*Fy*uy-2*Fz*uz)/porosity; + // m10 = m10 + rlx_setA*(-0.5*Den*((2*ux*ux-uy*uy-uz*uz)/porosity)- m10) + // + (1-0.5*rlx_setA)*(-2*Fx*ux+Fy*uy+Fz*uz)/porosity; + // m11 = m11 + rlx_setA*((Den*(uy*uy-uz*uz)/porosity) - m11) + // + (1-0.5*rlx_setA)*(2*Fy*uy-2*Fz*uz)/porosity; + // m12 = m12 + rlx_setA*(-0.5*(Den*(uy*uy-uz*uz)/porosity)- m12) + // + (1-0.5*rlx_setA)*(-Fy*uy+Fz*uz)/porosity; + // m13 = m13 + rlx_setA*((Den*ux*uy/porosity) - m13) + // + (1-0.5*rlx_setA)*(Fy*ux+Fx*uy)/porosity; + // m14 = m14 + rlx_setA*((Den*uy*uz/porosity) - m14) + // + (1-0.5*rlx_setA)*(Fz*uy+Fy*uz)/porosity; + // m15 = m15 + rlx_setA*((Den*ux*uz/porosity) - m15) + // + (1-0.5*rlx_setA)*(Fz*ux+Fx*uz)/porosity; + // m16 = m16 + rlx_setB*( - m16); + // m17 = m17 + rlx_setB*( - m17); + // m18 = m18 + rlx_setB*( - m18); + // //....................................................................................................... + + //-------------------- IMRT collison where body force has NO higher-order terms -------------// + //..............carry out relaxation process............................................... + m1 = m1 + + rlx_setA * ((-30 * Den + + 19 * Den * (ux * ux + uy * uy + uz * uz) / porosity + + 57 * pressure * porosity) - + m1); + m2 = m2 + + rlx_setA * ((12 * Den - + 5.5 * Den * (ux * ux + uy * uy + uz * uz) / porosity - + 27 * pressure * porosity) - + m2); + jx = jx + Fx; + m4 = m4 + rlx_setB * ((-0.6666666666666666 * ux * Den) - m4) + + (1 - 0.5 * rlx_setB) * (-0.6666666666666666 * Fx); + jy = jy + Fy; + m6 = m6 + rlx_setB * ((-0.6666666666666666 * uy * Den) - m6) + + (1 - 0.5 * rlx_setB) * (-0.6666666666666666 * Fy); + jz = jz + Fz; + m8 = m8 + rlx_setB * ((-0.6666666666666666 * uz * Den) - m8) + + (1 - 0.5 * rlx_setB) * (-0.6666666666666666 * Fz); + m9 = m9 + + rlx_setA * + ((Den * (2 * ux * ux - uy * uy - uz * uz) / porosity) - m9); + m10 = m10 + + rlx_setA * + (-0.5 * Den * ((2 * ux * ux - uy * uy - uz * uz) / porosity) - + m10); + m11 = m11 + rlx_setA * ((Den * (uy * uy - uz * uz) / porosity) - m11); + m12 = m12 + + rlx_setA * (-0.5 * (Den * (uy * uy - uz * uz) / porosity) - m12); + m13 = m13 + rlx_setA * ((Den * ux * uy / porosity) - m13); + m14 = m14 + rlx_setA * ((Den * uy * uz / porosity) - m14); + m15 = m15 + rlx_setA * ((Den * ux * uz / porosity) - m15); + m16 = m16 + rlx_setB * (-m16); + m17 = m17 + rlx_setB * (-m17); + m18 = m18 + rlx_setB * (-m18); + //....................................................................................................... + + //.................inverse transformation...................................................... + // q=0 + fq = mrt_V1 * Den - mrt_V2 * m1 + mrt_V3 * m2; + dist[n] = fq; + + // q = 1 + fq = mrt_V1 * Den - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jx - m4) + + mrt_V6 * (m9 - m10); + dist[1 * Np + n] = fq; + + // q=2 + fq = mrt_V1 * Den - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m4 - jx) + + mrt_V6 * (m9 - m10); + dist[2 * Np + n] = fq; + + // q = 3 + fq = mrt_V1 * Den - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jy - m6) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12); + dist[3 * Np + n] = fq; + + // q = 4 + fq = mrt_V1 * Den - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m6 - jy) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12); + dist[4 * Np + n] = fq; + + // q = 5 + fq = mrt_V1 * Den - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jz - m8) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11); + dist[5 * Np + n] = fq; + + // q = 6 + fq = mrt_V1 * Den - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m8 - jz) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11); + dist[6 * Np + n] = fq; + + // q = 7 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jy) + + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m16 - m17); + dist[7 * Np + n] = fq; + + // q = 8 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jy) - + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m17 - m16); + dist[8 * Np + n] = fq; + + // q = 9 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jy) + + 0.025 * (m4 - m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 + 0.125 * (m16 + m17); + dist[9 * Np + n] = fq; + + // q = 10 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jx) + + 0.025 * (m6 - m4) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 - 0.125 * (m16 + m17); + dist[10 * Np + n] = fq; + + // q = 11 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jz) + + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m18 - m16); + dist[11 * Np + n] = fq; + + // q = 12 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jz) - + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m16 - m18); + dist[12 * Np + n] = fq; + + // q = 13 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jz) + + 0.025 * (m4 - m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 - 0.125 * (m16 + m18); + dist[13 * Np + n] = fq; + + // q= 14 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jx) + + 0.025 * (m8 - m4) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 + 0.125 * (m16 + m18); + dist[14 * Np + n] = fq; + + // q = 15 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy + jz) + + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m17 - m18); + dist[15 * Np + n] = fq; + + // q = 16 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jy + jz) - + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m18 - m17); + dist[16 * Np + n] = fq; + + // q = 17 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jz) + + 0.025 * (m6 - m8) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 + + 0.125 * (m17 + m18); + dist[17 * Np + n] = fq; + + // q = 18 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jy) + + 0.025 * (m8 - m6) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 - + 0.125 * (m17 + m18); + dist[18 * Np + n] = fq; + //........................................................................ + + //Update velocity on device + Velocity[0 * Np + n] = ux; + Velocity[1 * Np + n] = uy; + Velocity[2 * Np + n] = uz; + //Update pressure on device + Pressure[n] = pressure; + } +} + +extern "C" void ScaLBL_D3Q19_AAodd_Greyscale_IMRT( + int *neighborList, double *dist, int start, int finish, int Np, double rlx, + double rlx_eff, double Gx, double Gy, double Gz, double *Poros, + double *Perm, double *Velocity, double Den, double *Pressure) { + int nread; + double vx, vy, vz, v_mag; + double ux, uy, uz, u_mag; + double pressure; //defined for this incompressible model + // conserved momemnts + double jx, jy, jz; + // non-conserved moments + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; + double fq; + //double f0,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18; + double GeoFun; //geometric function from Guo's PRE 66, 036304 (2002) + double porosity; + double perm; //voxel permeability + double c0, c1; //Guo's model parameters + double mu_eff = (1.0 / rlx_eff - 0.5) / 3.0; //kinematic viscosity + double Fx, Fy, + Fz; //The total body force including Brinkman force and user-specified (Gx,Gy,Gz) + double rlx_setA = rlx; + double rlx_setB = 8.f * (2.f - rlx_setA) / (8.f - rlx_setA); + + const double mrt_V1 = 0.05263157894736842; + const double mrt_V2 = 0.012531328320802; + const double mrt_V3 = 0.04761904761904762; + const double mrt_V4 = 0.004594820384294068; + const double mrt_V5 = 0.01587301587301587; + const double mrt_V6 = 0.0555555555555555555555555; + const double mrt_V7 = 0.02777777777777778; + const double mrt_V8 = 0.08333333333333333; + const double mrt_V9 = 0.003341687552213868; + const double mrt_V10 = 0.003968253968253968; + const double mrt_V11 = 0.01388888888888889; + const double mrt_V12 = 0.04166666666666666; + + for (int n = start; n < finish; n++) { + + //........................................................................ + // READ THE DISTRIBUTIONS + // (read from opposite array due to previous swap operation) + //........................................................................ + // q=0 + fq = dist[n]; + m1 = -30.0 * fq; + m2 = 12.0 * fq; + + // q=1 + nread = neighborList[n]; // neighbor 2 ( > 10Np => odd part of dist) + fq = dist[nread]; // reading the f1 data into register fq + pressure = fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jx = fq; + m4 = -4.0 * fq; + m9 = 2.0 * fq; + m10 = -4.0 * fq; + + // q=2 + nread = + neighborList[n + Np]; // neighbor 1 ( < 10Np => even part of dist) + fq = dist[nread]; // reading the f2 data into register fq + pressure += fq; + m1 -= 11.0 * (fq); + m2 -= 4.0 * (fq); + jx -= fq; + m4 += 4.0 * (fq); + m9 += 2.0 * (fq); + m10 -= 4.0 * (fq); + + // q=3 + nread = neighborList[n + 2 * Np]; // neighbor 4 + fq = dist[nread]; + pressure += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy = fq; + m6 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 = fq; + m12 = -2.0 * fq; + + // q = 4 + nread = neighborList[n + 3 * Np]; // neighbor 3 + fq = dist[nread]; + pressure += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy -= fq; + m6 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 += fq; + m12 -= 2.0 * fq; + + // q=5 + nread = neighborList[n + 4 * Np]; + fq = dist[nread]; + pressure += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz = fq; + m8 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; + + // q = 6 + nread = neighborList[n + 5 * Np]; + fq = dist[nread]; + pressure += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz -= fq; + m8 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; + + // q=7 + nread = neighborList[n + 6 * Np]; + fq = dist[nread]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 = fq; + m16 = fq; + m17 = -fq; + + // q = 8 + nread = neighborList[n + 7 * Np]; + fq = dist[nread]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 += fq; + m16 -= fq; + m17 += fq; + + // q=9 + nread = neighborList[n + 8 * Np]; + + fq = dist[nread]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 += fq; + m17 += fq; + + // q = 10 + nread = neighborList[n + 9 * Np]; + fq = dist[nread]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 -= fq; + m17 -= fq; + + // q=11 + nread = neighborList[n + 10 * Np]; + fq = dist[nread]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 = fq; + m16 -= fq; + m18 = fq; + + // q=12 + nread = neighborList[n + 11 * Np]; + fq = dist[nread]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 += fq; + m16 += fq; + m18 -= fq; + + // q=13 + nread = neighborList[n + 12 * Np]; + fq = dist[nread]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 -= fq; + m18 -= fq; + + // q=14 + nread = neighborList[n + 13 * Np]; + fq = dist[nread]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 += fq; + m18 += fq; + + // q=15 + nread = neighborList[n + 14 * Np]; + fq = dist[nread]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 = fq; + m17 += fq; + m18 -= fq; + + // q=16 + nread = neighborList[n + 15 * Np]; + fq = dist[nread]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 += fq; + m17 -= fq; + m18 += fq; + + // q=17 + nread = neighborList[n + 16 * Np]; + fq = dist[nread]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 += fq; + m18 += fq; + + // q=18 + nread = neighborList[n + 17 * Np]; + fq = dist[nread]; + pressure += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 -= fq; + m18 -= fq; + //---------------------------------------------------------------------// + + porosity = Poros[n]; + perm = Perm[n]; + + c0 = 0.5 * (1.0 + porosity * 0.5 * mu_eff / perm); + if (porosity == 1.0) + c0 = 0.5; //i.e. apparent pore nodes + GeoFun = 1.75 / sqrt(150.0 * porosity * porosity * porosity); + c1 = porosity * 0.5 * GeoFun / sqrt(perm); + if (porosity == 1.0) + c1 = 0.0; //i.e. apparent pore nodes + + vx = jx / Den + 0.5 * porosity * Gx; + vy = jy / Den + 0.5 * porosity * Gy; + vz = jz / Den + 0.5 * porosity * Gz; + v_mag = sqrt(vx * vx + vy * vy + vz * vz); + ux = vx / (c0 + sqrt(c0 * c0 + c1 * v_mag)); + uy = vy / (c0 + sqrt(c0 * c0 + c1 * v_mag)); + uz = vz / (c0 + sqrt(c0 * c0 + c1 * v_mag)); + u_mag = sqrt(ux * ux + uy * uy + uz * uz); + + //Update the total force to include linear (Darcy) and nonlinear (Forchheimer) drags due to the porous medium + Fx = + Den * (-porosity * mu_eff / perm * ux - + porosity * GeoFun / sqrt(perm) * u_mag * ux + porosity * Gx); + Fy = + Den * (-porosity * mu_eff / perm * uy - + porosity * GeoFun / sqrt(perm) * u_mag * uy + porosity * Gy); + Fz = + Den * (-porosity * mu_eff / perm * uz - + porosity * GeoFun / sqrt(perm) * u_mag * uz + porosity * Gz); + if (porosity == 1.0) { + Fx = Den * Gx; + Fy = Den * Gy; + Fz = Den * Gz; + } + + //Calculate pressure for Incompressible-MRT model + pressure = + 0.5 / porosity * (pressure - 0.5 * Den * u_mag * u_mag / porosity); + + //-------------------- IMRT collison where body force has higher-order terms -------------// + // //..............carry out relaxation process............................................... + // m1 = m1 + rlx_setA*((-30*Den+19*Den*(ux*ux+uy*uy+uz*uz)/porosity + 57*pressure*porosity) - m1) + // + (1-0.5*rlx_setA)*38*(Fx*ux+Fy*uy+Fz*uz)/porosity; + // m2 = m2 + rlx_setA*((12*Den - 5.5*Den*(ux*ux+uy*uy+uz*uz)/porosity-27*pressure*porosity) - m2) + // + (1-0.5*rlx_setA)*11*(-Fx*ux-Fy*uy-Fz*uz)/porosity; + // jx = jx + Fx; + // m4 = m4 + rlx_setB*((-0.6666666666666666*ux*Den) - m4) + // + (1-0.5*rlx_setB)*(-0.6666666666666666*Fx); + // jy = jy + Fy; + // m6 = m6 + rlx_setB*((-0.6666666666666666*uy*Den) - m6) + // + (1-0.5*rlx_setB)*(-0.6666666666666666*Fy); + // jz = jz + Fz; + // m8 = m8 + rlx_setB*((-0.6666666666666666*uz*Den) - m8) + // + (1-0.5*rlx_setB)*(-0.6666666666666666*Fz); + // m9 = m9 + rlx_setA*((Den*(2*ux*ux-uy*uy-uz*uz)/porosity) - m9) + // + (1-0.5*rlx_setA)*(4*Fx*ux-2*Fy*uy-2*Fz*uz)/porosity; + // m10 = m10 + rlx_setA*(-0.5*Den*((2*ux*ux-uy*uy-uz*uz)/porosity)- m10) + // + (1-0.5*rlx_setA)*(-2*Fx*ux+Fy*uy+Fz*uz)/porosity; + // m11 = m11 + rlx_setA*((Den*(uy*uy-uz*uz)/porosity) - m11) + // + (1-0.5*rlx_setA)*(2*Fy*uy-2*Fz*uz)/porosity; + // m12 = m12 + rlx_setA*(-0.5*(Den*(uy*uy-uz*uz)/porosity)- m12) + // + (1-0.5*rlx_setA)*(-Fy*uy+Fz*uz)/porosity; + // m13 = m13 + rlx_setA*((Den*ux*uy/porosity) - m13) + // + (1-0.5*rlx_setA)*(Fy*ux+Fx*uy)/porosity; + // m14 = m14 + rlx_setA*((Den*uy*uz/porosity) - m14) + // + (1-0.5*rlx_setA)*(Fz*uy+Fy*uz)/porosity; + // m15 = m15 + rlx_setA*((Den*ux*uz/porosity) - m15) + // + (1-0.5*rlx_setA)*(Fz*ux+Fx*uz)/porosity; + // m16 = m16 + rlx_setB*( - m16); + // m17 = m17 + rlx_setB*( - m17); + // m18 = m18 + rlx_setB*( - m18); + // //....................................................................................................... + + //-------------------- IMRT collison where body force has NO higher-order terms -------------// + //..............carry out relaxation process............................................... + m1 = m1 + + rlx_setA * ((-30 * Den + + 19 * Den * (ux * ux + uy * uy + uz * uz) / porosity + + 57 * pressure * porosity) - + m1); + m2 = m2 + + rlx_setA * ((12 * Den - + 5.5 * Den * (ux * ux + uy * uy + uz * uz) / porosity - + 27 * pressure * porosity) - + m2); + jx = jx + Fx; + m4 = m4 + rlx_setB * ((-0.6666666666666666 * ux * Den) - m4) + + (1 - 0.5 * rlx_setB) * (-0.6666666666666666 * Fx); + jy = jy + Fy; + m6 = m6 + rlx_setB * ((-0.6666666666666666 * uy * Den) - m6) + + (1 - 0.5 * rlx_setB) * (-0.6666666666666666 * Fy); + jz = jz + Fz; + m8 = m8 + rlx_setB * ((-0.6666666666666666 * uz * Den) - m8) + + (1 - 0.5 * rlx_setB) * (-0.6666666666666666 * Fz); + m9 = m9 + + rlx_setA * + ((Den * (2 * ux * ux - uy * uy - uz * uz) / porosity) - m9); + m10 = m10 + + rlx_setA * + (-0.5 * Den * ((2 * ux * ux - uy * uy - uz * uz) / porosity) - + m10); + m11 = m11 + rlx_setA * ((Den * (uy * uy - uz * uz) / porosity) - m11); + m12 = m12 + + rlx_setA * (-0.5 * (Den * (uy * uy - uz * uz) / porosity) - m12); + m13 = m13 + rlx_setA * ((Den * ux * uy / porosity) - m13); + m14 = m14 + rlx_setA * ((Den * uy * uz / porosity) - m14); + m15 = m15 + rlx_setA * ((Den * ux * uz / porosity) - m15); + m16 = m16 + rlx_setB * (-m16); + m17 = m17 + rlx_setB * (-m17); + m18 = m18 + rlx_setB * (-m18); + //....................................................................................................... + + //.................inverse transformation...................................................... + // q=0 + fq = mrt_V1 * Den - mrt_V2 * m1 + mrt_V3 * m2; + dist[n] = fq; + + // q = 1 + fq = mrt_V1 * Den - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jx - m4) + + mrt_V6 * (m9 - m10); + nread = neighborList[n + Np]; + dist[nread] = fq; + + // q=2 + fq = mrt_V1 * Den - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m4 - jx) + + mrt_V6 * (m9 - m10); + nread = neighborList[n]; + dist[nread] = fq; + + // q = 3 + fq = mrt_V1 * Den - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jy - m6) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12); + nread = neighborList[n + 3 * Np]; + dist[nread] = fq; + + // q = 4 + fq = mrt_V1 * Den - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m6 - jy) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12); + nread = neighborList[n + 2 * Np]; + dist[nread] = fq; + + // q = 5 + fq = mrt_V1 * Den - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jz - m8) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11); + nread = neighborList[n + 5 * Np]; + dist[nread] = fq; + + // q = 6 + fq = mrt_V1 * Den - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m8 - jz) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11); + nread = neighborList[n + 4 * Np]; + dist[nread] = fq; + + // q = 7 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jy) + + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m16 - m17); + nread = neighborList[n + 7 * Np]; + dist[nread] = fq; + + // q = 8 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jy) - + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m17 - m16); + nread = neighborList[n + 6 * Np]; + dist[nread] = fq; + + // q = 9 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jy) + + 0.025 * (m4 - m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 + 0.125 * (m16 + m17); + nread = neighborList[n + 9 * Np]; + dist[nread] = fq; + + // q = 10 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jx) + + 0.025 * (m6 - m4) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 - 0.125 * (m16 + m17); + nread = neighborList[n + 8 * Np]; + dist[nread] = fq; + + // q = 11 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jz) + + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m18 - m16); + nread = neighborList[n + 11 * Np]; + dist[nread] = fq; + + // q = 12 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jz) - + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m16 - m18); + nread = neighborList[n + 10 * Np]; + dist[nread] = fq; + + // q = 13 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jz) + + 0.025 * (m4 - m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 - 0.125 * (m16 + m18); + nread = neighborList[n + 13 * Np]; + dist[nread] = fq; + + // q= 14 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jx) + + 0.025 * (m8 - m4) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 + 0.125 * (m16 + m18); + nread = neighborList[n + 12 * Np]; + dist[nread] = fq; + + // q = 15 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy + jz) + + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m17 - m18); + nread = neighborList[n + 15 * Np]; + dist[nread] = fq; + + // q = 16 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jy + jz) - + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m18 - m17); + nread = neighborList[n + 14 * Np]; + dist[nread] = fq; + + // q = 17 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jz) + + 0.025 * (m6 - m8) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 + + 0.125 * (m17 + m18); + nread = neighborList[n + 17 * Np]; + dist[nread] = fq; + + // q = 18 + fq = mrt_V1 * Den + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jy) + + 0.025 * (m8 - m6) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 - + 0.125 * (m17 + m18); + nread = neighborList[n + 16 * Np]; + dist[nread] = fq; + //........................................................................ + + //Update velocity on device + Velocity[0 * Np + n] = ux; + Velocity[1 * Np + n] = uy; + Velocity[2 * Np + n] = uz; + //Update pressure on device + Pressure[n] = pressure; + } +} + +extern "C" void ScaLBL_D3Q19_AAodd_Greyscale_MRT( + int *neighborList, double *dist, int start, int finish, int Np, double rlx, + double rlx_eff, double Gx, double Gy, double Gz, double *Poros, + double *Perm, double *Velocity, double rho0, double *Pressure) { + + int nread; + int nr1, nr2, nr3, nr4, nr5, nr6; + int nr7, nr8, nr9, nr10; + int nr11, nr12, nr13, nr14; + double vx, vy, vz, v_mag; + double ux, uy, uz, u_mag; + double pressure; //defined for this incompressible model + // conserved momemnts + double rho, jx, jy, jz; + // non-conserved moments + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; + double fq; + //double f0,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18; + double GeoFun; //geometric function from Guo's PRE 66, 036304 (2002) + double porosity; + double perm; //voxel permeability + double c0, c1; //Guo's model parameters + double mu_eff = (1.0 / rlx_eff - 0.5) / 3.0; //kinematic viscosity + double Fx, Fy, + Fz; //The total body force including Brinkman force and user-specified (Gx,Gy,Gz) + double rlx_setA = rlx; + double rlx_setB = 8.f * (2.f - rlx_setA) / (8.f - rlx_setA); + + const double mrt_V1 = 0.05263157894736842; + const double mrt_V2 = 0.012531328320802; + const double mrt_V3 = 0.04761904761904762; + const double mrt_V4 = 0.004594820384294068; + const double mrt_V5 = 0.01587301587301587; + const double mrt_V6 = 0.0555555555555555555555555; + const double mrt_V7 = 0.02777777777777778; + const double mrt_V8 = 0.08333333333333333; + const double mrt_V9 = 0.003341687552213868; + const double mrt_V10 = 0.003968253968253968; + const double mrt_V11 = 0.01388888888888889; + const double mrt_V12 = 0.04166666666666666; + + for (int n = start; n < finish; n++) { + //........................................................................ + // READ THE DISTRIBUTIONS + // (read from opposite array due to previous swap operation) + //........................................................................ + // q=0 + fq = dist[n]; + rho = fq; + m1 = -30.0 * fq; + m2 = 12.0 * fq; + + // q=1 + //nread = neighborList[n]; // neighbor 2 + //fq = dist[nread]; // reading the f1 data into register fq + nr1 = neighborList[n]; + fq = dist[nr1]; // reading the f1 data into register fq + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jx = fq; + m4 = -4.0 * fq; + m9 = 2.0 * fq; + m10 = -4.0 * fq; + + // f2 = dist[10*Np+n]; + //nread = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) + //fq = dist[nread]; // reading the f2 data into register fq + nr2 = neighborList[n + Np]; // neighbor 1 ( < 10Np => even part of dist) + fq = dist[nr2]; // reading the f2 data into register fq + rho += fq; + m1 -= 11.0 * (fq); + m2 -= 4.0 * (fq); + jx -= fq; + m4 += 4.0 * (fq); + m9 += 2.0 * (fq); + m10 -= 4.0 * (fq); + + // q=3 + //nread = neighborList[n+2*Np]; // neighbor 4 + //fq = dist[nread]; + nr3 = neighborList[n + 2 * Np]; // neighbor 4 + fq = dist[nr3]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy = fq; + m6 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 = fq; + m12 = -2.0 * fq; + + // q = 4 + //nread = neighborList[n+3*Np]; // neighbor 3 + //fq = dist[nread]; + nr4 = neighborList[n + 3 * Np]; // neighbor 3 + fq = dist[nr4]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy -= fq; + m6 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 += fq; + m12 -= 2.0 * fq; + + // q=5 + //nread = neighborList[n+4*Np]; + //fq = dist[nread]; + nr5 = neighborList[n + 4 * Np]; + fq = dist[nr5]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz = fq; + m8 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; + + // q = 6 + //nread = neighborList[n+5*Np]; + //fq = dist[nread]; + nr6 = neighborList[n + 5 * Np]; + fq = dist[nr6]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz -= fq; + m8 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; + + // q=7 + //nread = neighborList[n+6*Np]; + //fq = dist[nread]; + nr7 = neighborList[n + 6 * Np]; + + fq = dist[nr7]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 = fq; + m16 = fq; + m17 = -fq; + + // q = 8 + //nread = neighborList[n+7*Np]; + //fq = dist[nread]; + nr8 = neighborList[n + 7 * Np]; + fq = dist[nr8]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 += fq; + m16 -= fq; + m17 += fq; + + // q=9 + //nread = neighborList[n+8*Np]; + //fq = dist[nread]; + nr9 = neighborList[n + 8 * Np]; + fq = dist[nr9]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 += fq; + m17 += fq; + + // q = 10 + //nread = neighborList[n+9*Np]; + //fq = dist[nread]; + nr10 = neighborList[n + 9 * Np]; + fq = dist[nr10]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 -= fq; + m17 -= fq; + + // q=11 + //nread = neighborList[n+10*Np]; + //fq = dist[nread]; + nr11 = neighborList[n + 10 * Np]; + fq = dist[nr11]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 = fq; + m16 -= fq; + m18 = fq; + + // q=12 + //nread = neighborList[n+11*Np]; + //fq = dist[nread]; + nr12 = neighborList[n + 11 * Np]; + fq = dist[nr12]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 += fq; + m16 += fq; + m18 -= fq; + + // q=13 + //nread = neighborList[n+12*Np]; + //fq = dist[nread]; + nr13 = neighborList[n + 12 * Np]; + fq = dist[nr13]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 -= fq; + m18 -= fq; + + // q=14 + //nread = neighborList[n+13*Np]; + //fq = dist[nread]; + nr14 = neighborList[n + 13 * Np]; + fq = dist[nr14]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 += fq; + m18 += fq; + + // q=15 + nread = neighborList[n + 14 * Np]; + fq = dist[nread]; + //fq = dist[17*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 = fq; + m17 += fq; + m18 -= fq; + + // q=16 + nread = neighborList[n + 15 * Np]; + fq = dist[nread]; + //fq = dist[8*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 += fq; + m17 -= fq; + m18 += fq; + + // q=17 + //fq = dist[18*Np+n]; + nread = neighborList[n + 16 * Np]; + fq = dist[nread]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 += fq; + m18 += fq; + + // q=18 + nread = neighborList[n + 17 * Np]; + fq = dist[nread]; + //fq = dist[9*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 -= fq; + m18 -= fq; + //---------------------------------------------------------------------// + + porosity = Poros[n]; + perm = Perm[n]; + + c0 = 0.5 * (1.0 + porosity * 0.5 * mu_eff / perm); + if (porosity == 1.0) + c0 = 0.5; //i.e. apparent pore nodes + GeoFun = 1.75 / sqrt(150.0 * porosity * porosity * porosity); + c1 = porosity * 0.5 * GeoFun / sqrt(perm); + if (porosity == 1.0) + c1 = 0.0; //i.e. apparent pore nodes + + vx = jx / rho0 + 0.5 * porosity * Gx; + vy = jy / rho0 + 0.5 * porosity * Gy; + vz = jz / rho0 + 0.5 * porosity * Gz; + v_mag = sqrt(vx * vx + vy * vy + vz * vz); + ux = vx / (c0 + sqrt(c0 * c0 + c1 * v_mag)); + uy = vy / (c0 + sqrt(c0 * c0 + c1 * v_mag)); + uz = vz / (c0 + sqrt(c0 * c0 + c1 * v_mag)); + u_mag = sqrt(ux * ux + uy * uy + uz * uz); + + //Update the total force to include linear (Darcy) and nonlinear (Forchheimer) drags due to the porous medium + Fx = rho0 * + (-porosity * mu_eff / perm * ux - + porosity * GeoFun / sqrt(perm) * u_mag * ux + porosity * Gx); + Fy = rho0 * + (-porosity * mu_eff / perm * uy - + porosity * GeoFun / sqrt(perm) * u_mag * uy + porosity * Gy); + Fz = rho0 * + (-porosity * mu_eff / perm * uz - + porosity * GeoFun / sqrt(perm) * u_mag * uz + porosity * Gz); + if (porosity == 1.0) { + Fx = rho0 * Gx; + + Fy = rho0 * Gy; + Fz = rho0 * Gz; } //Calculate pressure for MRT model //pressure=rho/3.f/porosity; - pressure=rho/3.f; + pressure = rho / 3.f; //-------------------- MRT collison where body force has NO higher-order terms -------------// - m1 = m1 + rlx_setA*((19*(ux*ux+uy*uy+uz*uz)*rho0/porosity - 11*rho) - m1); - m2 = m2 + rlx_setA*((3*rho - 5.5*(ux*ux+uy*uy+uz*uz)*rho0/porosity) - m2); + m1 = m1 + + rlx_setA * ((19 * (ux * ux + uy * uy + uz * uz) * rho0 / porosity - + 11 * rho) - + m1); + m2 = m2 + rlx_setA * ((3 * rho - 5.5 * (ux * ux + uy * uy + uz * uz) * + rho0 / porosity) - + m2); jx = jx + Fx; - m4 = m4 + rlx_setB*((-0.6666666666666666*ux*rho0)- m4) - + (1-0.5*rlx_setB)*(-0.6666666666666666*Fx); + m4 = m4 + rlx_setB * ((-0.6666666666666666 * ux * rho0) - m4) + + (1 - 0.5 * rlx_setB) * (-0.6666666666666666 * Fx); jy = jy + Fy; - m6 = m6 + rlx_setB*((-0.6666666666666666*uy*rho0)- m6) - + (1-0.5*rlx_setB)*(-0.6666666666666666*Fy); + m6 = m6 + rlx_setB * ((-0.6666666666666666 * uy * rho0) - m6) + + (1 - 0.5 * rlx_setB) * (-0.6666666666666666 * Fy); jz = jz + Fz; - m8 = m8 + rlx_setB*((-0.6666666666666666*uz*rho0)- m8) - + (1-0.5*rlx_setB)*(-0.6666666666666666*Fz); - m9 = m9 + rlx_setA*(((2*ux*ux-uy*uy-uz*uz)*rho0/porosity) - m9); - m10 = m10 + rlx_setA*( - m10); + m8 = m8 + rlx_setB * ((-0.6666666666666666 * uz * rho0) - m8) + + (1 - 0.5 * rlx_setB) * (-0.6666666666666666 * Fz); + m9 = m9 + + rlx_setA * + (((2 * ux * ux - uy * uy - uz * uz) * rho0 / porosity) - m9); + m10 = m10 + rlx_setA * (-m10); //m10 = m10 + rlx_setA*(-0.5*rho0*((2*ux*ux-uy*uy-uz*uz)/porosity)- m10); - m11 = m11 + rlx_setA*(((uy*uy-uz*uz)*rho0/porosity) - m11); - m12 = m12 + rlx_setA*( - m12); + m11 = m11 + rlx_setA * (((uy * uy - uz * uz) * rho0 / porosity) - m11); + m12 = m12 + rlx_setA * (-m12); //m12 = m12 + rlx_setA*(-0.5*(rho0*(uy*uy-uz*uz)/porosity)- m12); - m13 = m13 + rlx_setA*( (ux*uy*rho0/porosity) - m13); - m14 = m14 + rlx_setA*( (uy*uz*rho0/porosity) - m14); - m15 = m15 + rlx_setA*( (ux*uz*rho0/porosity) - m15); - m16 = m16 + rlx_setB*( - m16); - m17 = m17 + rlx_setB*( - m17); - m18 = m18 + rlx_setB*( - m18); + m13 = m13 + rlx_setA * ((ux * uy * rho0 / porosity) - m13); + m14 = m14 + rlx_setA * ((uy * uz * rho0 / porosity) - m14); + m15 = m15 + rlx_setA * ((ux * uz * rho0 / porosity) - m15); + m16 = m16 + rlx_setB * (-m16); + m17 = m17 + rlx_setB * (-m17); + m18 = m18 + rlx_setB * (-m18); //....................................................................................................... - //.................inverse transformation...................................................... // q=0 - fq = mrt_V1*rho-mrt_V2*m1+mrt_V3*m2; - dist[n] = fq; + fq = mrt_V1 * rho - mrt_V2 * m1 + mrt_V3 * m2; + dist[n] = fq; - // q = 1 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jx-m4)+mrt_V6*(m9-m10); - //nread = neighborList[n+Np]; - dist[nr2] = fq; + // q = 1 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jx - m4) + + mrt_V6 * (m9 - m10); + //nread = neighborList[n+Np]; + dist[nr2] = fq; - // q=2 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m4-jx)+mrt_V6*(m9-m10); - //nread = neighborList[n]; - dist[nr1] = fq; + // q=2 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m4 - jx) + + mrt_V6 * (m9 - m10); + //nread = neighborList[n]; + dist[nr1] = fq; - // q = 3 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jy-m6)+mrt_V7*(m10-m9)+mrt_V8*(m11-m12); - //nread = neighborList[n+3*Np]; - dist[nr4] = fq; + // q = 3 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jy - m6) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12); + //nread = neighborList[n+3*Np]; + dist[nr4] = fq; - // q = 4 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m6-jy)+mrt_V7*(m10-m9)+mrt_V8*(m11-m12); - //nread = neighborList[n+2*Np]; - dist[nr3] = fq; + // q = 4 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m6 - jy) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12); + //nread = neighborList[n+2*Np]; + dist[nr3] = fq; - // q = 5 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jz-m8)+mrt_V7*(m10-m9)+mrt_V8*(m12-m11); - //nread = neighborList[n+5*Np]; - dist[nr6] = fq; + // q = 5 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jz - m8) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11); + //nread = neighborList[n+5*Np]; + dist[nr6] = fq; - // q = 6 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m8-jz)+mrt_V7*(m10-m9)+mrt_V8*(m12-m11); - //nread = neighborList[n+4*Np]; - dist[nr5] = fq; + // q = 6 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m8 - jz) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11); + //nread = neighborList[n+4*Np]; + dist[nr5] = fq; - // q = 7 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jx+jy)+0.025*(m4+m6)+ - mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12+0.25*m13+0.125*(m16-m17); - //nread = neighborList[n+7*Np]; - dist[nr8] = fq; + // q = 7 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jy) + + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m16 - m17); + //nread = neighborList[n+7*Np]; + dist[nr8] = fq; - // q = 8 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2-0.1*(jx+jy)-0.025*(m4+m6) +mrt_V7*m9+mrt_V11*m10+mrt_V8*m11 - +mrt_V12*m12+0.25*m13+0.125*(m17-m16); - //nread = neighborList[n+6*Np]; - dist[nr7] = fq; + // q = 8 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jy) - + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m17 - m16); + //nread = neighborList[n+6*Np]; + dist[nr7] = fq; - // q = 9 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jx-jy)+0.025*(m4-m6)+ - mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12-0.25*m13+0.125*(m16+m17); - //nread = neighborList[n+9*Np]; - dist[nr10] = fq; + // q = 9 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jy) + + 0.025 * (m4 - m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 + 0.125 * (m16 + m17); + //nread = neighborList[n+9*Np]; + dist[nr10] = fq; - // q = 10 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jy-jx)+0.025*(m6-m4)+ - mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12-0.25*m13-0.125*(m16+m17); - //nread = neighborList[n+8*Np]; - dist[nr9] = fq; + // q = 10 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jx) + + 0.025 * (m6 - m4) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 - 0.125 * (m16 + m17); + //nread = neighborList[n+8*Np]; + dist[nr9] = fq; - // q = 11 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jx+jz)+0.025*(m4+m8) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12+0.25*m15+0.125*(m18-m16); - //nread = neighborList[n+11*Np]; - dist[nr12] = fq; + // q = 11 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jz) + + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m18 - m16); + //nread = neighborList[n+11*Np]; + dist[nr12] = fq; - // q = 12 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2-0.1*(jx+jz)-0.025*(m4+m8)+ - mrt_V7*m9+mrt_V11*m10-mrt_V8*m11-mrt_V12*m12+0.25*m15+0.125*(m16-m18); - //nread = neighborList[n+10*Np]; - dist[nr11]= fq; + // q = 12 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jz) - + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m16 - m18); + //nread = neighborList[n+10*Np]; + dist[nr11] = fq; - // q = 13 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jx-jz)+0.025*(m4-m8) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12-0.25*m15-0.125*(m16+m18); - //nread = neighborList[n+13*Np]; - dist[nr14] = fq; + // q = 13 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jz) + + 0.025 * (m4 - m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 - 0.125 * (m16 + m18); + //nread = neighborList[n+13*Np]; + dist[nr14] = fq; - // q= 14 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jz-jx)+0.025*(m8-m4) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12-0.25*m15+0.125*(m16+m18); - //nread = neighborList[n+12*Np]; - dist[nr13] = fq; + // q= 14 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jx) + + 0.025 * (m8 - m4) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 + 0.125 * (m16 + m18); + //nread = neighborList[n+12*Np]; + dist[nr13] = fq; + // q = 15 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy + jz) + + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m17 - m18); + nread = neighborList[n + 15 * Np]; + dist[nread] = fq; - // q = 15 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jy+jz)+0.025*(m6+m8) - -mrt_V6*m9-mrt_V7*m10+0.25*m14+0.125*(m17-m18); - nread = neighborList[n+15*Np]; - dist[nread] = fq; + // q = 16 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jy + jz) - + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m18 - m17); + nread = neighborList[n + 14 * Np]; + dist[nread] = fq; - // q = 16 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2-0.1*(jy+jz)-0.025*(m6+m8) - -mrt_V6*m9-mrt_V7*m10+0.25*m14+0.125*(m18-m17); - nread = neighborList[n+14*Np]; - dist[nread] = fq; + // q = 17 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jz) + + 0.025 * (m6 - m8) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 + + 0.125 * (m17 + m18); + nread = neighborList[n + 17 * Np]; + dist[nread] = fq; - - // q = 17 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jy-jz)+0.025*(m6-m8) - -mrt_V6*m9-mrt_V7*m10-0.25*m14+0.125*(m17+m18); - nread = neighborList[n+17*Np]; - dist[nread] = fq; - - // q = 18 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jz-jy)+0.025*(m8-m6) - -mrt_V6*m9-mrt_V7*m10-0.25*m14-0.125*(m17+m18); - nread = neighborList[n+16*Np]; - dist[nread] = fq; + // q = 18 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jy) + + 0.025 * (m8 - m6) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 - + 0.125 * (m17 + m18); + nread = neighborList[n + 16 * Np]; + dist[nread] = fq; //........................................................................ //Update velocity on device - Velocity[0*Np+n] = ux; - Velocity[1*Np+n] = uy; - Velocity[2*Np+n] = uz; + Velocity[0 * Np + n] = ux; + Velocity[1 * Np + n] = uy; + Velocity[2 * Np + n] = uz; //Update pressure on device Pressure[n] = pressure; - } + } } -extern "C" void ScaLBL_D3Q19_AAeven_Greyscale_MRT(double *dist, int start, int finish, int Np, double rlx, double rlx_eff, double Gx, double Gy, double Gz,double *Poros,double *Perm, double *Velocity,double rho0,double *Pressure){ +extern "C" void ScaLBL_D3Q19_AAeven_Greyscale_MRT( + double *dist, int start, int finish, int Np, double rlx, double rlx_eff, + double Gx, double Gy, double Gz, double *Poros, double *Perm, + double *Velocity, double rho0, double *Pressure) { - double vx,vy,vz,v_mag; - double ux,uy,uz,u_mag; - double pressure;//defined for this incompressible model - // conserved momemnts - double rho,jx,jy,jz; - // non-conserved moments - double m1,m2,m4,m6,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18; + double vx, vy, vz, v_mag; + double ux, uy, uz, u_mag; + double pressure; //defined for this incompressible model + // conserved momemnts + double rho, jx, jy, jz; + // non-conserved moments + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; double fq; - //double f0,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18; - double GeoFun;//geometric function from Guo's PRE 66, 036304 (2002) + //double f0,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18; + double GeoFun; //geometric function from Guo's PRE 66, 036304 (2002) double porosity; - double perm;//voxel permeability - double c0, c1; //Guo's model parameters - double mu_eff = (1.0/rlx_eff-0.5)/3.0;//kinematic viscosity - double Fx, Fy, Fz;//The total body force including Brinkman force and user-specified (Gx,Gy,Gz) + double perm; //voxel permeability + double c0, c1; //Guo's model parameters + double mu_eff = (1.0 / rlx_eff - 0.5) / 3.0; //kinematic viscosity + double Fx, Fy, + Fz; //The total body force including Brinkman force and user-specified (Gx,Gy,Gz) double rlx_setA = rlx; - double rlx_setB = 8.f*(2.f-rlx_setA)/(8.f-rlx_setA); + double rlx_setB = 8.f * (2.f - rlx_setA) / (8.f - rlx_setA); - const double mrt_V1=0.05263157894736842; - const double mrt_V2=0.012531328320802; - const double mrt_V3=0.04761904761904762; - const double mrt_V4=0.004594820384294068; - const double mrt_V5=0.01587301587301587; - const double mrt_V6=0.0555555555555555555555555; - const double mrt_V7=0.02777777777777778; - const double mrt_V8=0.08333333333333333; - const double mrt_V9=0.003341687552213868; - const double mrt_V10=0.003968253968253968; - const double mrt_V11=0.01388888888888889; - const double mrt_V12=0.04166666666666666; + const double mrt_V1 = 0.05263157894736842; + const double mrt_V2 = 0.012531328320802; + const double mrt_V3 = 0.04761904761904762; + const double mrt_V4 = 0.004594820384294068; + const double mrt_V5 = 0.01587301587301587; + const double mrt_V6 = 0.0555555555555555555555555; + const double mrt_V7 = 0.02777777777777778; + const double mrt_V8 = 0.08333333333333333; + const double mrt_V9 = 0.003341687552213868; + const double mrt_V10 = 0.003968253968253968; + const double mrt_V11 = 0.01388888888888889; + const double mrt_V12 = 0.04166666666666666; - - for (int n=start; n -extern "C" void ScaLBL_D3Q19_AAodd_GreyscaleColor(int *neighborList, int *Map, double *dist, double *Aq, double *Bq, double *Den, - double *Phi, double *GreySolidGrad, double *Poros,double *Perm,double *Velocity,double *Pressure, - double rhoA, double rhoB, double tauA, double tauB, double tauA_eff,double tauB_eff, double alpha, double beta, - double Gx, double Gy, double Gz, int strideY, int strideZ, int start, int finish, int Np){ +extern "C" void ScaLBL_D3Q19_AAodd_GreyscaleColor( + int *neighborList, int *Map, double *dist, double *Aq, double *Bq, + double *Den, double *Phi, double *GreySolidGrad, double *Poros, + double *Perm, double *Velocity, double *Pressure, double rhoA, double rhoB, + double tauA, double tauB, double tauA_eff, double tauB_eff, double alpha, + double beta, double Gx, double Gy, double Gz, int strideY, int strideZ, + int start, int finish, int Np) { - int n,nn,ijk,nread; - int nr1,nr2,nr3,nr4,nr5,nr6; - int nr7,nr8,nr9,nr10; - int nr11,nr12,nr13,nr14; - //int nr15,nr16,nr17,nr18; - double fq; - // conserved momemnts - double rho,jx,jy,jz; - double vx,vy,vz,v_mag; - double ux,uy,uz,u_mag; - // non-conserved moments - double m1,m2,m4,m6,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18; - double m3,m5,m7; - double nA,nB; // number density - double a1,b1,a2,b2,nAB,delta; - double C,nx,ny,nz; //color gradient magnitude and direction - double phi,tau,rho0,rlx_setA,rlx_setB; + int n, nn, ijk, nread; + int nr1, nr2, nr3, nr4, nr5, nr6; + int nr7, nr8, nr9, nr10; + int nr11, nr12, nr13, nr14; + //int nr15,nr16,nr17,nr18; + double fq; + // conserved momemnts + double rho, jx, jy, jz; + double vx, vy, vz, v_mag; + double ux, uy, uz, u_mag; + // non-conserved moments + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; + double m3, m5, m7; + double nA, nB; // number density + double a1, b1, a2, b2, nAB, delta; + double C, nx, ny, nz; //color gradient magnitude and direction + double phi, tau, rho0, rlx_setA, rlx_setB; - double GeoFun=0.0;//geometric function from Guo's PRE 66, 036304 (2002) + double GeoFun = 0.0; //geometric function from Guo's PRE 66, 036304 (2002) double porosity; - double perm;//voxel permeability + double perm; //voxel permeability double c0, c1; //Guo's model parameters double tau_eff; - double mu_eff;//kinematic viscosity - double nx_gs,ny_gs,nz_gs;//grey-solid color gradient - double Fx,Fy,Fz; + double mu_eff; //kinematic viscosity + double nx_gs, ny_gs, nz_gs; //grey-solid color gradient + double Fx, Fy, Fz; - const double mrt_V1=0.05263157894736842; - const double mrt_V2=0.012531328320802; - const double mrt_V3=0.04761904761904762; - const double mrt_V4=0.004594820384294068; - const double mrt_V5=0.01587301587301587; - const double mrt_V6=0.0555555555555555555555555; - const double mrt_V7=0.02777777777777778; - const double mrt_V8=0.08333333333333333; - const double mrt_V9=0.003341687552213868; - const double mrt_V10=0.003968253968253968; - const double mrt_V11=0.01388888888888889; - const double mrt_V12=0.04166666666666666; + const double mrt_V1 = 0.05263157894736842; + const double mrt_V2 = 0.012531328320802; + const double mrt_V3 = 0.04761904761904762; + const double mrt_V4 = 0.004594820384294068; + const double mrt_V5 = 0.01587301587301587; + const double mrt_V6 = 0.0555555555555555555555555; + const double mrt_V7 = 0.02777777777777778; + const double mrt_V8 = 0.08333333333333333; + const double mrt_V9 = 0.003341687552213868; + const double mrt_V10 = 0.003968253968253968; + const double mrt_V11 = 0.01388888888888889; + const double mrt_V12 = 0.04166666666666666; - for (n=start; n even part of dist) - //fq = dist[nread]; // reading the f2 data into register fq - nr2 = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) - fq = dist[nr2]; // reading the f2 data into register fq - rho += fq; - m1 -= 11.0*(fq); - m2 -= 4.0*(fq); - jx -= fq; - m4 += 4.0*(fq); - m9 += 2.0*(fq); - m10 -= 4.0*(fq); + // f2 = dist[10*Np+n]; + //nread = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) + //fq = dist[nread]; // reading the f2 data into register fq + nr2 = neighborList[n + Np]; // neighbor 1 ( < 10Np => even part of dist) + fq = dist[nr2]; // reading the f2 data into register fq + rho += fq; + m1 -= 11.0 * (fq); + m2 -= 4.0 * (fq); + jx -= fq; + m4 += 4.0 * (fq); + m9 += 2.0 * (fq); + m10 -= 4.0 * (fq); - // q=3 - //nread = neighborList[n+2*Np]; // neighbor 4 - //fq = dist[nread]; - nr3 = neighborList[n+2*Np]; // neighbor 4 - fq = dist[nr3]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jy = fq; - m6 = -4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 = fq; - m12 = -2.0*fq; + // q=3 + //nread = neighborList[n+2*Np]; // neighbor 4 + //fq = dist[nread]; + nr3 = neighborList[n + 2 * Np]; // neighbor 4 + fq = dist[nr3]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy = fq; + m6 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 = fq; + m12 = -2.0 * fq; - // q = 4 - //nread = neighborList[n+3*Np]; // neighbor 3 - //fq = dist[nread]; - nr4 = neighborList[n+3*Np]; // neighbor 3 - fq = dist[nr4]; - rho+= fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jy -= fq; - m6 += 4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 += fq; - m12 -= 2.0*fq; + // q = 4 + //nread = neighborList[n+3*Np]; // neighbor 3 + //fq = dist[nread]; + nr4 = neighborList[n + 3 * Np]; // neighbor 3 + fq = dist[nr4]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy -= fq; + m6 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 += fq; + m12 -= 2.0 * fq; - // q=5 - //nread = neighborList[n+4*Np]; - //fq = dist[nread]; - nr5 = neighborList[n+4*Np]; - fq = dist[nr5]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jz = fq; - m8 = -4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 -= fq; - m12 += 2.0*fq; + // q=5 + //nread = neighborList[n+4*Np]; + //fq = dist[nread]; + nr5 = neighborList[n + 4 * Np]; + fq = dist[nr5]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz = fq; + m8 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; + // q = 6 + //nread = neighborList[n+5*Np]; + //fq = dist[nread]; + nr6 = neighborList[n + 5 * Np]; + fq = dist[nr6]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz -= fq; + m8 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; - // q = 6 - //nread = neighborList[n+5*Np]; - //fq = dist[nread]; - nr6 = neighborList[n+5*Np]; - fq = dist[nr6]; - rho+= fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jz -= fq; - m8 += 4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 -= fq; - m12 += 2.0*fq; + // q=7 + //nread = neighborList[n+6*Np]; + //fq = dist[nread]; + nr7 = neighborList[n + 6 * Np]; + fq = dist[nr7]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 = fq; + m16 = fq; + m17 = -fq; - // q=7 - //nread = neighborList[n+6*Np]; - //fq = dist[nread]; - nr7 = neighborList[n+6*Np]; - fq = dist[nr7]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jy += fq; - m6 += fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 = fq; - m16 = fq; - m17 = -fq; + // q = 8 + //nread = neighborList[n+7*Np]; + //fq = dist[nread]; + nr8 = neighborList[n + 7 * Np]; + fq = dist[nr8]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 += fq; + m16 -= fq; + m17 += fq; - // q = 8 - //nread = neighborList[n+7*Np]; - //fq = dist[nread]; - nr8 = neighborList[n+7*Np]; - fq = dist[nr8]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jy -= fq; - m6 -= fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 += fq; - m16 -= fq; - m17 += fq; + // q=9 + //nread = neighborList[n+8*Np]; + //fq = dist[nread]; + nr9 = neighborList[n + 8 * Np]; + fq = dist[nr9]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 += fq; + m17 += fq; - // q=9 - //nread = neighborList[n+8*Np]; - //fq = dist[nread]; - nr9 = neighborList[n+8*Np]; - fq = dist[nr9]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jy -= fq; - m6 -= fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 -= fq; - m16 += fq; - m17 += fq; + // q = 10 + //nread = neighborList[n+9*Np]; + //fq = dist[nread]; + nr10 = neighborList[n + 9 * Np]; + fq = dist[nr10]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 -= fq; + m17 -= fq; - // q = 10 - //nread = neighborList[n+9*Np]; - //fq = dist[nread]; - nr10 = neighborList[n+9*Np]; - fq = dist[nr10]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jy += fq; - m6 += fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 -= fq; - m16 -= fq; - m17 -= fq; + // q=11 + //nread = neighborList[n+10*Np]; + //fq = dist[nread]; + nr11 = neighborList[n + 10 * Np]; + fq = dist[nr11]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 = fq; + m16 -= fq; + m18 = fq; - // q=11 - //nread = neighborList[n+10*Np]; - //fq = dist[nread]; - nr11 = neighborList[n+10*Np]; - fq = dist[nr11]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jz += fq; - m8 += fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 = fq; - m16 -= fq; - m18 = fq; + // q=12 + //nread = neighborList[n+11*Np]; + //fq = dist[nread]; + nr12 = neighborList[n + 11 * Np]; + fq = dist[nr12]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 += fq; + m16 += fq; + m18 -= fq; - // q=12 - //nread = neighborList[n+11*Np]; - //fq = dist[nread]; - nr12 = neighborList[n+11*Np]; - fq = dist[nr12]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jz -= fq; - m8 -= fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 += fq; - m16 += fq; - m18 -= fq; + // q=13 + //nread = neighborList[n+12*Np]; + //fq = dist[nread]; + nr13 = neighborList[n + 12 * Np]; + fq = dist[nr13]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 -= fq; + m18 -= fq; - // q=13 - //nread = neighborList[n+12*Np]; - //fq = dist[nread]; - nr13 = neighborList[n+12*Np]; - fq = dist[nr13]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jz -= fq; - m8 -= fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 -= fq; - m16 -= fq; - m18 -= fq; + // q=14 + //nread = neighborList[n+13*Np]; + //fq = dist[nread]; + nr14 = neighborList[n + 13 * Np]; + fq = dist[nr14]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 += fq; + m18 += fq; - // q=14 - //nread = neighborList[n+13*Np]; - //fq = dist[nread]; - nr14 = neighborList[n+13*Np]; - fq = dist[nr14]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jz += fq; - m8 += fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 -= fq; - m16 += fq; - m18 += fq; + // q=15 + nread = neighborList[n + 14 * Np]; + fq = dist[nread]; + //fq = dist[17*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 = fq; + m17 += fq; + m18 -= fq; - // q=15 - nread = neighborList[n+14*Np]; - fq = dist[nread]; - //fq = dist[17*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy += fq; - m6 += fq; - jz += fq; - m8 += fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 = fq; - m17 += fq; - m18 -= fq; + // q=16 + nread = neighborList[n + 15 * Np]; + fq = dist[nread]; + //fq = dist[8*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 += fq; + m17 -= fq; + m18 += fq; - // q=16 - nread = neighborList[n+15*Np]; - fq = dist[nread]; - //fq = dist[8*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy -= fq; - m6 -= fq; - jz -= fq; - m8 -= fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 += fq; - m17 -= fq; - m18 += fq; + // q=17 + //fq = dist[18*Np+n]; + nread = neighborList[n + 16 * Np]; + fq = dist[nread]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 += fq; + m18 += fq; - // q=17 - //fq = dist[18*Np+n]; - nread = neighborList[n+16*Np]; - fq = dist[nread]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy += fq; - m6 += fq; - jz -= fq; - m8 -= fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 -= fq; - m17 += fq; - m18 += fq; + // q=18 + nread = neighborList[n + 17 * Np]; + fq = dist[nread]; + //fq = dist[9*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 -= fq; + m18 -= fq; - // q=18 - nread = neighborList[n+17*Np]; - fq = dist[nread]; - //fq = dist[9*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy -= fq; - m6 -= fq; - jz += fq; - m8 += fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 -= fq; - m17 -= fq; - m18 -= fq; - // Compute greyscale related parameters - c0 = 0.5*(1.0+porosity*0.5*mu_eff/perm); - if (porosity==1.0) c0 = 0.5;//i.e. apparent pore nodes + c0 = 0.5 * (1.0 + porosity * 0.5 * mu_eff / perm); + if (porosity == 1.0) + c0 = 0.5; //i.e. apparent pore nodes //GeoFun = 1.75/sqrt(150.0*porosity*porosity*porosity); - c1 = porosity*0.5*GeoFun/sqrt(perm); - if (porosity==1.0) c1 = 0.0;//i.e. apparent pore nodes + c1 = porosity * 0.5 * GeoFun / sqrt(perm); + if (porosity == 1.0) + c1 = 0.0; //i.e. apparent pore nodes - vx = jx/rho0+0.5*(porosity*Gx); - vy = jy/rho0+0.5*(porosity*Gy); - vz = jz/rho0+0.5*(porosity*Gz); - v_mag=sqrt(vx*vx+vy*vy+vz*vz); - ux = vx/(c0+sqrt(c0*c0+c1*v_mag)); - uy = vy/(c0+sqrt(c0*c0+c1*v_mag)); - uz = vz/(c0+sqrt(c0*c0+c1*v_mag)); - u_mag=sqrt(ux*ux+uy*uy+uz*uz); + vx = jx / rho0 + 0.5 * (porosity * Gx); + vy = jy / rho0 + 0.5 * (porosity * Gy); + vz = jz / rho0 + 0.5 * (porosity * Gz); + v_mag = sqrt(vx * vx + vy * vy + vz * vz); + ux = vx / (c0 + sqrt(c0 * c0 + c1 * v_mag)); + uy = vy / (c0 + sqrt(c0 * c0 + c1 * v_mag)); + uz = vz / (c0 + sqrt(c0 * c0 + c1 * v_mag)); + u_mag = sqrt(ux * ux + uy * uy + uz * uz); //Update the total force to include linear (Darcy) and nonlinear (Forchheimer) drags due to the porous medium - Fx = rho0*(-porosity*mu_eff/perm*ux - porosity*GeoFun/sqrt(perm)*u_mag*ux + porosity*Gx); - Fy = rho0*(-porosity*mu_eff/perm*uy - porosity*GeoFun/sqrt(perm)*u_mag*uy + porosity*Gy); - Fz = rho0*(-porosity*mu_eff/perm*uz - porosity*GeoFun/sqrt(perm)*u_mag*uz + porosity*Gz); - if (porosity==1.0){ - Fx=rho0*(Gx); - Fy=rho0*(Gy); - Fz=rho0*(Gz); + Fx = rho0 * + (-porosity * mu_eff / perm * ux - + porosity * GeoFun / sqrt(perm) * u_mag * ux + porosity * Gx); + Fy = rho0 * + (-porosity * mu_eff / perm * uy - + porosity * GeoFun / sqrt(perm) * u_mag * uy + porosity * Gy); + Fz = rho0 * + (-porosity * mu_eff / perm * uz - + porosity * GeoFun / sqrt(perm) * u_mag * uz + porosity * Gz); + if (porosity == 1.0) { + Fx = rho0 * (Gx); + Fy = rho0 * (Gy); + Fz = rho0 * (Gz); } - // write the velocity - Velocity[n] = ux; - Velocity[Np+n] = uy; - Velocity[2*Np+n] = uz; + // write the velocity + Velocity[n] = ux; + Velocity[Np + n] = uy; + Velocity[2 * Np + n] = uz; //Pressure[n] = rho/3.f/porosity; - Pressure[n] = rho/3.f; + Pressure[n] = rho / 3.f; - //........................................................................ - //..............carry out relaxation process.............................. - //..........Toelke, Fruediger et. al. 2006................................ - if (C == 0.0) nx = ny = nz = 0.0; - m1 = m1 + rlx_setA*((19*(ux*ux+uy*uy+uz*uz)*rho0/porosity - 11*rho) -19*alpha*C - m1); - m2 = m2 + rlx_setA*((3*rho - 5.5*(ux*ux+uy*uy+uz*uz)*rho0/porosity)- m2); + //........................................................................ + //..............carry out relaxation process.............................. + //..........Toelke, Fruediger et. al. 2006................................ + if (C == 0.0) + nx = ny = nz = 0.0; + m1 = m1 + + rlx_setA * ((19 * (ux * ux + uy * uy + uz * uz) * rho0 / porosity - + 11 * rho) - + 19 * alpha * C - m1); + m2 = m2 + rlx_setA * ((3 * rho - 5.5 * (ux * ux + uy * uy + uz * uz) * + rho0 / porosity) - + m2); jx = jx + Fx; - m4 = m4 + rlx_setB*((-0.6666666666666666*ux*rho0)- m4) - + (1-0.5*rlx_setB)*(-0.6666666666666666*Fx); + m4 = m4 + rlx_setB * ((-0.6666666666666666 * ux * rho0) - m4) + + (1 - 0.5 * rlx_setB) * (-0.6666666666666666 * Fx); jy = jy + Fy; - m6 = m6 + rlx_setB*((-0.6666666666666666*uy*rho0)- m6) - + (1-0.5*rlx_setB)*(-0.6666666666666666*Fy); + m6 = m6 + rlx_setB * ((-0.6666666666666666 * uy * rho0) - m6) + + (1 - 0.5 * rlx_setB) * (-0.6666666666666666 * Fy); jz = jz + Fz; - m8 = m8 + rlx_setB*((-0.6666666666666666*uz*rho0)- m8) - + (1-0.5*rlx_setB)*(-0.6666666666666666*Fz); - m9 = m9 + rlx_setA*(((2*ux*ux-uy*uy-uz*uz)*rho0/porosity) + 0.5*alpha*C*(2*nx*nx-ny*ny-nz*nz) - m9); - m10 = m10 + rlx_setA*( - m10); + m8 = m8 + rlx_setB * ((-0.6666666666666666 * uz * rho0) - m8) + + (1 - 0.5 * rlx_setB) * (-0.6666666666666666 * Fz); + m9 = + m9 + rlx_setA * + (((2 * ux * ux - uy * uy - uz * uz) * rho0 / porosity) + + 0.5 * alpha * C * (2 * nx * nx - ny * ny - nz * nz) - m9); + m10 = m10 + rlx_setA * (-m10); //m10 = m10 + rlx_setA*(-0.5*rho0*((2*ux*ux-uy*uy-uz*uz)/porosity)- m10); - m11 = m11 + rlx_setA*(((uy*uy-uz*uz)*rho0/porosity) + 0.5*alpha*C*(ny*ny-nz*nz)- m11); - m12 = m12 + rlx_setA*( - m12); + m11 = m11 + rlx_setA * (((uy * uy - uz * uz) * rho0 / porosity) + + 0.5 * alpha * C * (ny * ny - nz * nz) - m11); + m12 = m12 + rlx_setA * (-m12); //m12 = m12 + rlx_setA*(-0.5*(rho0*(uy*uy-uz*uz)/porosity)- m12); - m13 = m13 + rlx_setA*( (ux*uy*rho0/porosity) + 0.5*alpha*C*nx*ny - m13); - m14 = m14 + rlx_setA*( (uy*uz*rho0/porosity) + 0.5*alpha*C*ny*nz - m14); - m15 = m15 + rlx_setA*( (ux*uz*rho0/porosity) + 0.5*alpha*C*nx*nz - m15); - m16 = m16 + rlx_setB*( - m16); - m17 = m17 + rlx_setB*( - m17); - m18 = m18 + rlx_setB*( - m18); + m13 = m13 + rlx_setA * ((ux * uy * rho0 / porosity) + + 0.5 * alpha * C * nx * ny - m13); + m14 = m14 + rlx_setA * ((uy * uz * rho0 / porosity) + + 0.5 * alpha * C * ny * nz - m14); + m15 = m15 + rlx_setA * ((ux * uz * rho0 / porosity) + + 0.5 * alpha * C * nx * nz - m15); + m16 = m16 + rlx_setB * (-m16); + m17 = m17 + rlx_setB * (-m17); + m18 = m18 + rlx_setB * (-m18); - //.................inverse transformation...................................................... - // q=0 - fq = mrt_V1*rho-mrt_V2*m1+mrt_V3*m2; - dist[n] = fq; + //.................inverse transformation...................................................... + // q=0 + fq = mrt_V1 * rho - mrt_V2 * m1 + mrt_V3 * m2; + dist[n] = fq; - // q = 1 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jx-m4)+mrt_V6*(m9-m10); - //nread = neighborList[n+Np]; - dist[nr2] = fq; + // q = 1 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jx - m4) + + mrt_V6 * (m9 - m10); + //nread = neighborList[n+Np]; + dist[nr2] = fq; - // q=2 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m4-jx)+mrt_V6*(m9-m10); - //nread = neighborList[n]; - dist[nr1] = fq; + // q=2 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m4 - jx) + + mrt_V6 * (m9 - m10); + //nread = neighborList[n]; + dist[nr1] = fq; - // q = 3 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jy-m6)+mrt_V7*(m10-m9)+mrt_V8*(m11-m12); - //nread = neighborList[n+3*Np]; - dist[nr4] = fq; + // q = 3 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jy - m6) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12); + //nread = neighborList[n+3*Np]; + dist[nr4] = fq; - // q = 4 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m6-jy)+mrt_V7*(m10-m9)+mrt_V8*(m11-m12); - //nread = neighborList[n+2*Np]; - dist[nr3] = fq; + // q = 4 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m6 - jy) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12); + //nread = neighborList[n+2*Np]; + dist[nr3] = fq; - // q = 5 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jz-m8)+mrt_V7*(m10-m9)+mrt_V8*(m12-m11); - //nread = neighborList[n+5*Np]; - dist[nr6] = fq; + // q = 5 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jz - m8) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11); + //nread = neighborList[n+5*Np]; + dist[nr6] = fq; - // q = 6 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m8-jz)+mrt_V7*(m10-m9)+mrt_V8*(m12-m11); - //nread = neighborList[n+4*Np]; - dist[nr5] = fq; + // q = 6 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m8 - jz) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11); + //nread = neighborList[n+4*Np]; + dist[nr5] = fq; - // q = 7 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jx+jy)+0.025*(m4+m6)+ - mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12+0.25*m13+0.125*(m16-m17); - //nread = neighborList[n+7*Np]; - dist[nr8] = fq; + // q = 7 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jy) + + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m16 - m17); + //nread = neighborList[n+7*Np]; + dist[nr8] = fq; - // q = 8 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2-0.1*(jx+jy)-0.025*(m4+m6) +mrt_V7*m9+mrt_V11*m10+mrt_V8*m11 - +mrt_V12*m12+0.25*m13+0.125*(m17-m16); - //nread = neighborList[n+6*Np]; - dist[nr7] = fq; + // q = 8 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jy) - + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m17 - m16); + //nread = neighborList[n+6*Np]; + dist[nr7] = fq; - // q = 9 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jx-jy)+0.025*(m4-m6)+ - mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12-0.25*m13+0.125*(m16+m17); - //nread = neighborList[n+9*Np]; - dist[nr10] = fq; + // q = 9 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jy) + + 0.025 * (m4 - m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 + 0.125 * (m16 + m17); + //nread = neighborList[n+9*Np]; + dist[nr10] = fq; - // q = 10 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jy-jx)+0.025*(m6-m4)+ - mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12-0.25*m13-0.125*(m16+m17); - //nread = neighborList[n+8*Np]; - dist[nr9] = fq; + // q = 10 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jx) + + 0.025 * (m6 - m4) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 - 0.125 * (m16 + m17); + //nread = neighborList[n+8*Np]; + dist[nr9] = fq; - // q = 11 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jx+jz)+0.025*(m4+m8) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12+0.25*m15+0.125*(m18-m16); - //nread = neighborList[n+11*Np]; - dist[nr12] = fq; + // q = 11 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jz) + + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m18 - m16); + //nread = neighborList[n+11*Np]; + dist[nr12] = fq; - // q = 12 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2-0.1*(jx+jz)-0.025*(m4+m8)+ - mrt_V7*m9+mrt_V11*m10-mrt_V8*m11-mrt_V12*m12+0.25*m15+0.125*(m16-m18); - //nread = neighborList[n+10*Np]; - dist[nr11]= fq; + // q = 12 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jz) - + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m16 - m18); + //nread = neighborList[n+10*Np]; + dist[nr11] = fq; - // q = 13 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jx-jz)+0.025*(m4-m8) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12-0.25*m15-0.125*(m16+m18); - //nread = neighborList[n+13*Np]; - dist[nr14] = fq; + // q = 13 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jz) + + 0.025 * (m4 - m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 - 0.125 * (m16 + m18); + //nread = neighborList[n+13*Np]; + dist[nr14] = fq; - // q= 14 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jz-jx)+0.025*(m8-m4) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12-0.25*m15+0.125*(m16+m18); - //nread = neighborList[n+12*Np]; - dist[nr13] = fq; + // q= 14 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jx) + + 0.025 * (m8 - m4) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 + 0.125 * (m16 + m18); + //nread = neighborList[n+12*Np]; + dist[nr13] = fq; + // q = 15 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy + jz) + + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m17 - m18); + nread = neighborList[n + 15 * Np]; + dist[nread] = fq; - // q = 15 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jy+jz)+0.025*(m6+m8) - -mrt_V6*m9-mrt_V7*m10+0.25*m14+0.125*(m17-m18); - nread = neighborList[n+15*Np]; - dist[nread] = fq; + // q = 16 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jy + jz) - + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m18 - m17); + nread = neighborList[n + 14 * Np]; + dist[nread] = fq; - // q = 16 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2-0.1*(jy+jz)-0.025*(m6+m8) - -mrt_V6*m9-mrt_V7*m10+0.25*m14+0.125*(m18-m17); - nread = neighborList[n+14*Np]; - dist[nread] = fq; + // q = 17 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jz) + + 0.025 * (m6 - m8) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 + + 0.125 * (m17 + m18); + nread = neighborList[n + 17 * Np]; + dist[nread] = fq; + // q = 18 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jy) + + 0.025 * (m8 - m6) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 - + 0.125 * (m17 + m18); + nread = neighborList[n + 16 * Np]; + dist[nread] = fq; + //........................................................................ - // q = 17 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jy-jz)+0.025*(m6-m8) - -mrt_V6*m9-mrt_V7*m10-0.25*m14+0.125*(m17+m18); - nread = neighborList[n+17*Np]; - dist[nread] = fq; + // Instantiate mass transport distributions + // Stationary value - distribution 0 + nAB = 1.0 / (nA + nB); + Aq[n] = 0.3333333333333333 * nA; + Bq[n] = 0.3333333333333333 * nB; - // q = 18 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jz-jy)+0.025*(m8-m6) - -mrt_V6*m9-mrt_V7*m10-0.25*m14-0.125*(m17+m18); - nread = neighborList[n+16*Np]; - dist[nread] = fq; - //........................................................................ + //............................................... + // q = 0,2,4 + // Cq = {1,0,0}, {0,1,0}, {0,0,1} + delta = beta * nA * nB * nAB * 0.1111111111111111 * nx; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * ux)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * ux)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * ux)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * ux)) + delta; - // Instantiate mass transport distributions - // Stationary value - distribution 0 - nAB = 1.0/(nA+nB); - Aq[n] = 0.3333333333333333*nA; - Bq[n] = 0.3333333333333333*nB; + // q = 1 + //nread = neighborList[n+Np]; + Aq[nr2] = a1; + Bq[nr2] = b1; + // q=2 + //nread = neighborList[n]; + Aq[nr1] = a2; + Bq[nr1] = b2; - //............................................... - // q = 0,2,4 - // Cq = {1,0,0}, {0,1,0}, {0,0,1} - delta = beta*nA*nB*nAB*0.1111111111111111*nx; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*ux))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*ux))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*ux))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*ux))+delta; + //............................................... + // Cq = {0,1,0} + delta = beta * nA * nB * nAB * 0.1111111111111111 * ny; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * uy)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * uy)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * uy)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * uy)) + delta; - // q = 1 - //nread = neighborList[n+Np]; - Aq[nr2] = a1; - Bq[nr2] = b1; - // q=2 - //nread = neighborList[n]; - Aq[nr1] = a2; - Bq[nr1] = b2; + // q = 3 + //nread = neighborList[n+3*Np]; + Aq[nr4] = a1; + Bq[nr4] = b1; + // q = 4 + //nread = neighborList[n+2*Np]; + Aq[nr3] = a2; + Bq[nr3] = b2; - //............................................... - // Cq = {0,1,0} - delta = beta*nA*nB*nAB*0.1111111111111111*ny; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*uy))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*uy))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*uy))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*uy))+delta; + //............................................... + // q = 4 + // Cq = {0,0,1} + delta = beta * nA * nB * nAB * 0.1111111111111111 * nz; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * uz)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * uz)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * uz)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * uz)) + delta; - // q = 3 - //nread = neighborList[n+3*Np]; - Aq[nr4] = a1; - Bq[nr4] = b1; - // q = 4 - //nread = neighborList[n+2*Np]; - Aq[nr3] = a2; - Bq[nr3] = b2; - - //............................................... - // q = 4 - // Cq = {0,0,1} - delta = beta*nA*nB*nAB*0.1111111111111111*nz; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*uz))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*uz))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*uz))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*uz))+delta; - - // q = 5 - //nread = neighborList[n+5*Np]; - Aq[nr6] = a1; - Bq[nr6] = b1; - // q = 6 - //nread = neighborList[n+4*Np]; - Aq[nr5] = a2; - Bq[nr5] = b2; - //............................................... - } + // q = 5 + //nread = neighborList[n+5*Np]; + Aq[nr6] = a1; + Bq[nr6] = b1; + // q = 6 + //nread = neighborList[n+4*Np]; + Aq[nr5] = a2; + Bq[nr5] = b2; + //............................................... + } } -extern "C" void ScaLBL_D3Q19_AAeven_GreyscaleColor(int *Map, double *dist, double *Aq, double *Bq, double *Den, - double *Phi,double *GreySolidGrad, double *Poros,double *Perm,double *Velocity,double *Pressure, - double rhoA, double rhoB, double tauA, double tauB,double tauA_eff,double tauB_eff, double alpha, double beta, - double Gx, double Gy, double Gz, int strideY, int strideZ, int start, int finish, int Np){ +extern "C" void ScaLBL_D3Q19_AAeven_GreyscaleColor( + int *Map, double *dist, double *Aq, double *Bq, double *Den, double *Phi, + double *GreySolidGrad, double *Poros, double *Perm, double *Velocity, + double *Pressure, double rhoA, double rhoB, double tauA, double tauB, + double tauA_eff, double tauB_eff, double alpha, double beta, double Gx, + double Gy, double Gz, int strideY, int strideZ, int start, int finish, + int Np) { - int ijk,nn,n; - double fq; - // conserved momemnts - double rho,jx,jy,jz; - double vx,vy,vz,v_mag; - double ux,uy,uz,u_mag; - // non-conserved moments - double m1,m2,m4,m6,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18; - double m3,m5,m7; - double nA,nB; // number density - double a1,b1,a2,b2,nAB,delta; - double C,nx,ny,nz; //color gradient magnitude and direction - double phi,tau,rho0,rlx_setA,rlx_setB; + int ijk, nn, n; + double fq; + // conserved momemnts + double rho, jx, jy, jz; + double vx, vy, vz, v_mag; + double ux, uy, uz, u_mag; + // non-conserved moments + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; + double m3, m5, m7; + double nA, nB; // number density + double a1, b1, a2, b2, nAB, delta; + double C, nx, ny, nz; //color gradient magnitude and direction + double phi, tau, rho0, rlx_setA, rlx_setB; - double GeoFun=0.0;//geometric function from Guo's PRE 66, 036304 (2002) + double GeoFun = 0.0; //geometric function from Guo's PRE 66, 036304 (2002) double porosity; - double perm;//voxel permeability + double perm; //voxel permeability double c0, c1; //Guo's model parameters double tau_eff; - double mu_eff;//kinematic viscosity - double nx_gs,ny_gs,nz_gs;//grey-solid color gradient - double Fx,Fy,Fz; + double mu_eff; //kinematic viscosity + double nx_gs, ny_gs, nz_gs; //grey-solid color gradient + double Fx, Fy, Fz; - const double mrt_V1=0.05263157894736842; - const double mrt_V2=0.012531328320802; - const double mrt_V3=0.04761904761904762; - const double mrt_V4=0.004594820384294068; - const double mrt_V5=0.01587301587301587; - const double mrt_V6=0.0555555555555555555555555; - const double mrt_V7=0.02777777777777778; - const double mrt_V8=0.08333333333333333; - const double mrt_V9=0.003341687552213868; - const double mrt_V10=0.003968253968253968; - const double mrt_V11=0.01388888888888889; - const double mrt_V12=0.04166666666666666; + const double mrt_V1 = 0.05263157894736842; + const double mrt_V2 = 0.012531328320802; + const double mrt_V3 = 0.04761904761904762; + const double mrt_V4 = 0.004594820384294068; + const double mrt_V5 = 0.01587301587301587; + const double mrt_V6 = 0.0555555555555555555555555; + const double mrt_V7 = 0.02777777777777778; + const double mrt_V8 = 0.08333333333333333; + const double mrt_V9 = 0.003341687552213868; + const double mrt_V10 = 0.003968253968253968; + const double mrt_V11 = 0.01388888888888889; + const double mrt_V12 = 0.04166666666666666; - for (n=start; n 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * ux)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * ux)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * ux)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * ux)) + delta; - // Instantiate mass transport distributions - // Stationary value - distribution 0 - nAB = 1.0/(nA+nB); - Aq[n] = 0.3333333333333333*nA; - Bq[n] = 0.3333333333333333*nB; + Aq[1 * Np + n] = a1; + Bq[1 * Np + n] = b1; + Aq[2 * Np + n] = a2; + Bq[2 * Np + n] = b2; - //............................................... - // q = 0,2,4 - // Cq = {1,0,0}, {0,1,0}, {0,0,1} - delta = beta*nA*nB*nAB*0.1111111111111111*nx; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*ux))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*ux))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*ux))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*ux))+delta; + //............................................... + // q = 2 + // Cq = {0,1,0} + delta = beta * nA * nB * nAB * 0.1111111111111111 * ny; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * uy)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * uy)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * uy)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * uy)) + delta; - Aq[1*Np+n] = a1; - Bq[1*Np+n] = b1; - Aq[2*Np+n] = a2; - Bq[2*Np+n] = b2; + Aq[3 * Np + n] = a1; + Bq[3 * Np + n] = b1; + Aq[4 * Np + n] = a2; + Bq[4 * Np + n] = b2; + //............................................... + // q = 4 + // Cq = {0,0,1} + delta = beta * nA * nB * nAB * 0.1111111111111111 * nz; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * uz)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * uz)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * uz)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * uz)) + delta; - //............................................... - // q = 2 - // Cq = {0,1,0} - delta = beta*nA*nB*nAB*0.1111111111111111*ny; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*uy))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*uy))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*uy))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*uy))+delta; - - Aq[3*Np+n] = a1; - Bq[3*Np+n] = b1; - Aq[4*Np+n] = a2; - Bq[4*Np+n] = b2; - //............................................... - // q = 4 - // Cq = {0,0,1} - delta = beta*nA*nB*nAB*0.1111111111111111*nz; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*uz))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*uz))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*uz))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*uz))+delta; - - Aq[5*Np+n] = a1; - Bq[5*Np+n] = b1; - Aq[6*Np+n] = a2; - Bq[6*Np+n] = b2; - //............................................... - } + Aq[5 * Np + n] = a1; + Bq[5 * Np + n] = b1; + Aq[6 * Np + n] = a2; + Bq[6 * Np + n] = b2; + //............................................... + } } //CP: capillary penalty // also turn off recoloring for grey nodes -extern "C" void ScaLBL_D3Q19_AAodd_GreyscaleColor_CP(int *neighborList, int *Map, double *dist, double *Aq, double *Bq, double *Den, - double *Phi, double *GreySolidW, double *GreySn, double *GreySw, double *GreyKn, double *GreyKw, double *Poros,double *Perm, - double *Velocity, double *MobilityRatio, double *Pressure, - double rhoA, double rhoB, double tauA, double tauB,double tauA_eff,double tauB_eff,double alpha, double beta, - double Gx, double Gy, double Gz, bool RecoloringOff, int strideY, int strideZ, int start, int finish, int Np){ +extern "C" void ScaLBL_D3Q19_AAodd_GreyscaleColor_CP( + int *neighborList, int *Map, double *dist, double *Aq, double *Bq, + double *Den, double *Phi, double *GreySolidW, double *GreySn, + double *GreySw, double *GreyKn, double *GreyKw, double *Poros, double *Perm, + double *Velocity, double *MobilityRatio, double *Pressure, double rhoA, + double rhoB, double tauA, double tauB, double tauA_eff, double tauB_eff, + double alpha, double beta, double Gx, double Gy, double Gz, + bool RecoloringOff, int strideY, int strideZ, int start, int finish, + int Np) { - int n,nn,ijk,nread; - int nr1,nr2,nr3,nr4,nr5,nr6; - int nr7,nr8,nr9,nr10; - int nr11,nr12,nr13,nr14; - //int nr15,nr16,nr17,nr18; - double fq; - // conserved momemnts - double rho,jx,jy,jz; - //double vx,vy,vz,v_mag; + int n, nn, ijk, nread; + int nr1, nr2, nr3, nr4, nr5, nr6; + int nr7, nr8, nr9, nr10; + int nr11, nr12, nr13, nr14; + //int nr15,nr16,nr17,nr18; + double fq; + // conserved momemnts + double rho, jx, jy, jz; + //double vx,vy,vz,v_mag; //double ux,uy,uz,u_mag; - double ux,uy,uz; - // non-conserved moments - double m1,m2,m4,m6,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18; - double m3,m5,m7; - double nA,nB; // number density - double a1,b1,a2,b2,nAB,delta; - double C,nx,ny,nz; //color gradient magnitude and direction - double phi,tau,rho0,rlx_setA,rlx_setB; + double ux, uy, uz; + // non-conserved moments + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; + double m3, m5, m7; + double nA, nB; // number density + double a1, b1, a2, b2, nAB, delta; + double C, nx, ny, nz; //color gradient magnitude and direction + double phi, tau, rho0, rlx_setA, rlx_setB; //double GeoFun=0.0;//geometric function from Guo's PRE 66, 036304 (2002) double porosity; - double perm;//voxel permeability + double perm; //voxel permeability //double c0, c1; //Guo's model parameters double tau_eff; - double mu_eff;//kinematic viscosity - double Fx,Fy,Fz; - double Fcpx,Fcpy,Fcpz;//capillary penalty force - double W;//greyscale wetting strength - double Sn_grey,Sw_grey; + double mu_eff; //kinematic viscosity + double Fx, Fy, Fz; + double Fcpx, Fcpy, Fcpz; //capillary penalty force + double W; //greyscale wetting strength + double Sn_grey, Sw_grey; /* Corey model parameters */ - double Kn_grey,Kw_grey; - double Swn,Krn_grey,Krw_grey,mobility_ratio,jA,jB; - double GreyDiff=0.0e-4; // grey diffusion - - const double mrt_V1=0.05263157894736842; - const double mrt_V2=0.012531328320802; - const double mrt_V3=0.04761904761904762; - const double mrt_V4=0.004594820384294068; - const double mrt_V5=0.01587301587301587; - const double mrt_V6=0.0555555555555555555555555; - const double mrt_V7=0.02777777777777778; - const double mrt_V8=0.08333333333333333; - const double mrt_V9=0.003341687552213868; - const double mrt_V10=0.003968253968253968; - const double mrt_V11=0.01388888888888889; - const double mrt_V12=0.04166666666666666; + double Kn_grey, Kw_grey; + double Swn, Krn_grey, Krw_grey, mobility_ratio, jA, jB; + double GreyDiff = 0.0e-4; // grey diffusion - for (n=start; n=Sn_grey && nA/(nA+nB) <= Sw_grey && porosity !=1.0){ - Swn = (nA/(nA+nB) - Sn_grey) /(Sw_grey - Sn_grey); - Krn_grey = Kn_grey*Swn*Swn; // Corey model with exponent = 2, make sure that W cannot shift to zero - Krw_grey = Kw_grey*(1.0-Swn)*(1.0-Swn); // Corey model with exponent = 4, make sure that W cannot shift to zero - // recompute the effective permeability - perm = mu_eff*(Krn_grey*3.0/(tauA-0.5) + Krw_grey*3.0/(tauB-0.5)); - //mobility_ratio =(nA*Krn_grey*3.0/(tauA-0.5) - nB*Krw_grey*3.0/(tauB-0.5))/(nA*Krn_grey*3.0/(tauA-0.5) + nB*Krw_grey*3.0/(tauB-0.5)); - } - else if (nA/(nA+nB)>Sw_grey && porosity !=1.0){ - perm = Kn_grey; - Krn_grey = Kn_grey; - Swn = 1.0; + if (nA / (nA + nB) < Sn_grey && porosity != 1.0) { + perm = Kw_grey; + Krw_grey = Kw_grey; + Swn = 0.0; + } else if (nA / (nA + nB) >= Sn_grey && nA / (nA + nB) <= Sw_grey && + porosity != 1.0) { + Swn = (nA / (nA + nB) - Sn_grey) / (Sw_grey - Sn_grey); + Krn_grey = + Kn_grey * Swn * + Swn; // Corey model with exponent = 2, make sure that W cannot shift to zero + Krw_grey = + Kw_grey * (1.0 - Swn) * + (1.0 - + Swn); // Corey model with exponent = 4, make sure that W cannot shift to zero + // recompute the effective permeability + perm = mu_eff * (Krn_grey * 3.0 / (tauA - 0.5) + + Krw_grey * 3.0 / (tauB - 0.5)); + //mobility_ratio =(nA*Krn_grey*3.0/(tauA-0.5) - nB*Krw_grey*3.0/(tauB-0.5))/(nA*Krn_grey*3.0/(tauA-0.5) + nB*Krw_grey*3.0/(tauB-0.5)); + } else if (nA / (nA + nB) > Sw_grey && porosity != 1.0) { + perm = Kn_grey; + Krn_grey = Kn_grey; + Swn = 1.0; } /* compute the mobility ratio */ - if (porosity != 1.0){ - mobility_ratio =(Krn_grey/(tauA-0.5) - Krw_grey/(tauB-0.5))/(Krn_grey/(tauA-0.5) + Krw_grey/(tauB-0.5)); - } - else if (phi > 0.0){ - mobility_ratio = 1.0; - } - else { - mobility_ratio = -1.0; + if (porosity != 1.0) { + mobility_ratio = + (Krn_grey / (tauA - 0.5) - Krw_grey / (tauB - 0.5)) / + (Krn_grey / (tauA - 0.5) + Krw_grey / (tauB - 0.5)); + } else if (phi > 0.0) { + mobility_ratio = 1.0; + } else { + mobility_ratio = -1.0; } MobilityRatio[n] = mobility_ratio; - // Get the 1D index based on regular data layout - ijk = Map[n]; - // COMPUTE THE COLOR GRADIENT - //........................................................................ - //.................Read Phase Indicator Values............................ - //........................................................................ - nn = ijk-1; // neighbor index (get convention) - m1 = Phi[nn]; // get neighbor for phi - 1 - //........................................................................ - nn = ijk+1; // neighbor index (get convention) - m2 = Phi[nn]; // get neighbor for phi - 2 - //........................................................................ - nn = ijk-strideY; // neighbor index (get convention) - m3 = Phi[nn]; // get neighbor for phi - 3 - //........................................................................ - nn = ijk+strideY; // neighbor index (get convention) - m4 = Phi[nn]; // get neighbor for phi - 4 - //........................................................................ - nn = ijk-strideZ; // neighbor index (get convention) - m5 = Phi[nn]; // get neighbor for phi - 5 - //........................................................................ - nn = ijk+strideZ; // neighbor index (get convention) - m6 = Phi[nn]; // get neighbor for phi - 6 - //........................................................................ - nn = ijk-strideY-1; // neighbor index (get convention) - m7 = Phi[nn]; // get neighbor for phi - 7 - //........................................................................ - nn = ijk+strideY+1; // neighbor index (get convention) - m8 = Phi[nn]; // get neighbor for phi - 8 - //........................................................................ - nn = ijk+strideY-1; // neighbor index (get convention) - m9 = Phi[nn]; // get neighbor for phi - 9 - //........................................................................ - nn = ijk-strideY+1; // neighbor index (get convention) - m10 = Phi[nn]; // get neighbor for phi - 10 - //........................................................................ - nn = ijk-strideZ-1; // neighbor index (get convention) - m11 = Phi[nn]; // get neighbor for phi - 11 - //........................................................................ - nn = ijk+strideZ+1; // neighbor index (get convention) - m12 = Phi[nn]; // get neighbor for phi - 12 - //........................................................................ - nn = ijk+strideZ-1; // neighbor index (get convention) - m13 = Phi[nn]; // get neighbor for phi - 13 - //........................................................................ - nn = ijk-strideZ+1; // neighbor index (get convention) - m14 = Phi[nn]; // get neighbor for phi - 14 - //........................................................................ - nn = ijk-strideZ-strideY; // neighbor index (get convention) - m15 = Phi[nn]; // get neighbor for phi - 15 - //........................................................................ - nn = ijk+strideZ+strideY; // neighbor index (get convention) - m16 = Phi[nn]; // get neighbor for phi - 16 - //........................................................................ - nn = ijk+strideZ-strideY; // neighbor index (get convention) - m17 = Phi[nn]; // get neighbor for phi - 17 - //........................................................................ - nn = ijk-strideZ+strideY; // neighbor index (get convention) - m18 = Phi[nn]; // get neighbor for phi - 18 - //............Compute the Color Gradient................................... - nx = -3.0/18.0*(m1-m2+0.5*(m7-m8+m9-m10+m11-m12+m13-m14)); - ny = -3.0/18.0*(m3-m4+0.5*(m7-m8-m9+m10+m15-m16+m17-m18)); - nz = -3.0/18.0*(m5-m6+0.5*(m11-m12-m13+m14+m15-m16-m17+m18)); + // Get the 1D index based on regular data layout + ijk = Map[n]; + // COMPUTE THE COLOR GRADIENT + //........................................................................ + //.................Read Phase Indicator Values............................ + //........................................................................ + nn = ijk - 1; // neighbor index (get convention) + m1 = Phi[nn]; // get neighbor for phi - 1 + //........................................................................ + nn = ijk + 1; // neighbor index (get convention) + m2 = Phi[nn]; // get neighbor for phi - 2 + //........................................................................ + nn = ijk - strideY; // neighbor index (get convention) + m3 = Phi[nn]; // get neighbor for phi - 3 + //........................................................................ + nn = ijk + strideY; // neighbor index (get convention) + m4 = Phi[nn]; // get neighbor for phi - 4 + //........................................................................ + nn = ijk - strideZ; // neighbor index (get convention) + m5 = Phi[nn]; // get neighbor for phi - 5 + //........................................................................ + nn = ijk + strideZ; // neighbor index (get convention) + m6 = Phi[nn]; // get neighbor for phi - 6 + //........................................................................ + nn = ijk - strideY - 1; // neighbor index (get convention) + m7 = Phi[nn]; // get neighbor for phi - 7 + //........................................................................ + nn = ijk + strideY + 1; // neighbor index (get convention) + m8 = Phi[nn]; // get neighbor for phi - 8 + //........................................................................ + nn = ijk + strideY - 1; // neighbor index (get convention) + m9 = Phi[nn]; // get neighbor for phi - 9 + //........................................................................ + nn = ijk - strideY + 1; // neighbor index (get convention) + m10 = Phi[nn]; // get neighbor for phi - 10 + //........................................................................ + nn = ijk - strideZ - 1; // neighbor index (get convention) + m11 = Phi[nn]; // get neighbor for phi - 11 + //........................................................................ + nn = ijk + strideZ + 1; // neighbor index (get convention) + m12 = Phi[nn]; // get neighbor for phi - 12 + //........................................................................ + nn = ijk + strideZ - 1; // neighbor index (get convention) + m13 = Phi[nn]; // get neighbor for phi - 13 + //........................................................................ + nn = ijk - strideZ + 1; // neighbor index (get convention) + m14 = Phi[nn]; // get neighbor for phi - 14 + //........................................................................ + nn = ijk - strideZ - strideY; // neighbor index (get convention) + m15 = Phi[nn]; // get neighbor for phi - 15 + //........................................................................ + nn = ijk + strideZ + strideY; // neighbor index (get convention) + m16 = Phi[nn]; // get neighbor for phi - 16 + //........................................................................ + nn = ijk + strideZ - strideY; // neighbor index (get convention) + m17 = Phi[nn]; // get neighbor for phi - 17 + //........................................................................ + nn = ijk - strideZ + strideY; // neighbor index (get convention) + m18 = Phi[nn]; // get neighbor for phi - 18 + //............Compute the Color Gradient................................... + nx = -3.0 / 18.0 * + (m1 - m2 + 0.5 * (m7 - m8 + m9 - m10 + m11 - m12 + m13 - m14)); + ny = -3.0 / 18.0 * + (m3 - m4 + 0.5 * (m7 - m8 - m9 + m10 + m15 - m16 + m17 - m18)); + nz = -3.0 / 18.0 * + (m5 - m6 + 0.5 * (m11 - m12 - m13 + m14 + m15 - m16 - m17 + m18)); - //............Compute the Greyscale Potential Gradient..................... -// Fcpx = 0.0; -// Fcpy = 0.0; -// Fcpz = 0.0; -// if (porosity!=1.0){ -// //Fcpx = -3.0/18.0*(gp1-gp2+0.5*(gp7-gp8+gp9-gp10+gp11-gp12+gp13-gp14)); -// //Fcpy = -3.0/18.0*(gp3-gp4+0.5*(gp7-gp8-gp9+gp10+gp15-gp16+gp17-gp18)); -// //Fcpz = -3.0/18.0*(gp5-gp6+0.5*(gp11-gp12-gp13+gp14+gp15-gp16-gp17+gp18)); -// Fcpx = -3.0/18.0*(m1-m2+0.5*(m7-m8+m9-m10+m11-m12+m13-m14)); -// Fcpy = -3.0/18.0*(m3-m4+0.5*(m7-m8-m9+m10+m15-m16+m17-m18)); -// Fcpz = -3.0/18.0*(m5-m6+0.5*(m11-m12-m13+m14+m15-m16-m17+m18)); -// Fcpx *= alpha*W/sqrt(perm); -// Fcpy *= alpha*W/sqrt(perm); -// Fcpz *= alpha*W/sqrt(perm); -// //double Fcp_mag_temp = sqrt(Fcpx*Fcpx+Fcpy*Fcpy+Fcpz*Fcpz); -// //double Fcp_mag = Fcp_mag_temp; -// //if (Fcp_mag_temp==0.0) Fcp_mag=1.0; -// //nx = Fcpx/Fcp_mag; -// //ny = Fcpy/Fcp_mag; -// //nz = Fcpz/Fcp_mag; -// } + //............Compute the Greyscale Potential Gradient..................... + // Fcpx = 0.0; + // Fcpy = 0.0; + // Fcpz = 0.0; + // if (porosity!=1.0){ + // //Fcpx = -3.0/18.0*(gp1-gp2+0.5*(gp7-gp8+gp9-gp10+gp11-gp12+gp13-gp14)); + // //Fcpy = -3.0/18.0*(gp3-gp4+0.5*(gp7-gp8-gp9+gp10+gp15-gp16+gp17-gp18)); + // //Fcpz = -3.0/18.0*(gp5-gp6+0.5*(gp11-gp12-gp13+gp14+gp15-gp16-gp17+gp18)); + // Fcpx = -3.0/18.0*(m1-m2+0.5*(m7-m8+m9-m10+m11-m12+m13-m14)); + // Fcpy = -3.0/18.0*(m3-m4+0.5*(m7-m8-m9+m10+m15-m16+m17-m18)); + // Fcpz = -3.0/18.0*(m5-m6+0.5*(m11-m12-m13+m14+m15-m16-m17+m18)); + // Fcpx *= alpha*W/sqrt(perm); + // Fcpy *= alpha*W/sqrt(perm); + // Fcpz *= alpha*W/sqrt(perm); + // //double Fcp_mag_temp = sqrt(Fcpx*Fcpx+Fcpy*Fcpy+Fcpz*Fcpz); + // //double Fcp_mag = Fcp_mag_temp; + // //if (Fcp_mag_temp==0.0) Fcp_mag=1.0; + // //nx = Fcpx/Fcp_mag; + // //ny = Fcpy/Fcp_mag; + // //nz = Fcpz/Fcp_mag; + // } Fcpx = nx; Fcpy = ny; Fcpz = nz; - double Fcp_mag=sqrt(Fcpx*Fcpx+Fcpy*Fcpy+Fcpz*Fcpz); - if (Fcp_mag==0.0) Fcpx=Fcpy=Fcpz=0.0; + double Fcp_mag = sqrt(Fcpx * Fcpx + Fcpy * Fcpy + Fcpz * Fcpz); + if (Fcp_mag == 0.0) + Fcpx = Fcpy = Fcpz = 0.0; //NOTE for open node (porosity=1.0),Fcp=0.0 - Fcpx *= alpha*W*(1.0-porosity)/sqrt(perm); - Fcpy *= alpha*W*(1.0-porosity)/sqrt(perm); - Fcpz *= alpha*W*(1.0-porosity)/sqrt(perm); + Fcpx *= alpha * W * (1.0 - porosity) / sqrt(perm); + Fcpy *= alpha * W * (1.0 - porosity) / sqrt(perm); + Fcpz *= alpha * W * (1.0 - porosity) / sqrt(perm); - //...........Normalize the Color Gradient................................. - C = sqrt(nx*nx+ny*ny+nz*nz); - double ColorMag = C; - if (C==0.0) ColorMag=1.0; - nx = nx/ColorMag; - ny = ny/ColorMag; - nz = nz/ColorMag; + //...........Normalize the Color Gradient................................. + C = sqrt(nx * nx + ny * ny + nz * nz); + double ColorMag = C; + if (C == 0.0) + ColorMag = 1.0; + nx = nx / ColorMag; + ny = ny / ColorMag; + nz = nz / ColorMag; - // q=0 - fq = dist[n]; - rho = fq; - m1 = -30.0*fq; - m2 = 12.0*fq; + // q=0 + fq = dist[n]; + rho = fq; + m1 = -30.0 * fq; + m2 = 12.0 * fq; - // q=1 - //nread = neighborList[n]; // neighbor 2 - //fq = dist[nread]; // reading the f1 data into register fq - nr1 = neighborList[n]; - fq = dist[nr1]; // reading the f1 data into register fq - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jx = fq; - m4 = -4.0*fq; - m9 = 2.0*fq; - m10 = -4.0*fq; + // q=1 + //nread = neighborList[n]; // neighbor 2 + //fq = dist[nread]; // reading the f1 data into register fq + nr1 = neighborList[n]; + fq = dist[nr1]; // reading the f1 data into register fq + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jx = fq; + m4 = -4.0 * fq; + m9 = 2.0 * fq; + m10 = -4.0 * fq; - // f2 = dist[10*Np+n]; - //nread = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) - //fq = dist[nread]; // reading the f2 data into register fq - nr2 = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) - fq = dist[nr2]; // reading the f2 data into register fq - rho += fq; - m1 -= 11.0*(fq); - m2 -= 4.0*(fq); - jx -= fq; - m4 += 4.0*(fq); - m9 += 2.0*(fq); - m10 -= 4.0*(fq); + // f2 = dist[10*Np+n]; + //nread = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) + //fq = dist[nread]; // reading the f2 data into register fq + nr2 = neighborList[n + Np]; // neighbor 1 ( < 10Np => even part of dist) + fq = dist[nr2]; // reading the f2 data into register fq + rho += fq; + m1 -= 11.0 * (fq); + m2 -= 4.0 * (fq); + jx -= fq; + m4 += 4.0 * (fq); + m9 += 2.0 * (fq); + m10 -= 4.0 * (fq); - // q=3 - //nread = neighborList[n+2*Np]; // neighbor 4 - //fq = dist[nread]; - nr3 = neighborList[n+2*Np]; // neighbor 4 - fq = dist[nr3]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jy = fq; - m6 = -4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 = fq; - m12 = -2.0*fq; + // q=3 + //nread = neighborList[n+2*Np]; // neighbor 4 + //fq = dist[nread]; + nr3 = neighborList[n + 2 * Np]; // neighbor 4 + fq = dist[nr3]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy = fq; + m6 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 = fq; + m12 = -2.0 * fq; - // q = 4 - //nread = neighborList[n+3*Np]; // neighbor 3 - //fq = dist[nread]; - nr4 = neighborList[n+3*Np]; // neighbor 3 - fq = dist[nr4]; - rho+= fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jy -= fq; - m6 += 4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 += fq; - m12 -= 2.0*fq; + // q = 4 + //nread = neighborList[n+3*Np]; // neighbor 3 + //fq = dist[nread]; + nr4 = neighborList[n + 3 * Np]; // neighbor 3 + fq = dist[nr4]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy -= fq; + m6 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 += fq; + m12 -= 2.0 * fq; - // q=5 - //nread = neighborList[n+4*Np]; - //fq = dist[nread]; - nr5 = neighborList[n+4*Np]; - fq = dist[nr5]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jz = fq; - m8 = -4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 -= fq; - m12 += 2.0*fq; + // q=5 + //nread = neighborList[n+4*Np]; + //fq = dist[nread]; + nr5 = neighborList[n + 4 * Np]; + fq = dist[nr5]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz = fq; + m8 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; + // q = 6 + //nread = neighborList[n+5*Np]; + //fq = dist[nread]; + nr6 = neighborList[n + 5 * Np]; + fq = dist[nr6]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz -= fq; + m8 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; - // q = 6 - //nread = neighborList[n+5*Np]; - //fq = dist[nread]; - nr6 = neighborList[n+5*Np]; - fq = dist[nr6]; - rho+= fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jz -= fq; - m8 += 4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 -= fq; - m12 += 2.0*fq; + // q=7 + //nread = neighborList[n+6*Np]; + //fq = dist[nread]; + nr7 = neighborList[n + 6 * Np]; + fq = dist[nr7]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 = fq; + m16 = fq; + m17 = -fq; - // q=7 - //nread = neighborList[n+6*Np]; - //fq = dist[nread]; - nr7 = neighborList[n+6*Np]; - fq = dist[nr7]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jy += fq; - m6 += fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 = fq; - m16 = fq; - m17 = -fq; + // q = 8 + //nread = neighborList[n+7*Np]; + //fq = dist[nread]; + nr8 = neighborList[n + 7 * Np]; + fq = dist[nr8]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 += fq; + m16 -= fq; + m17 += fq; - // q = 8 - //nread = neighborList[n+7*Np]; - //fq = dist[nread]; - nr8 = neighborList[n+7*Np]; - fq = dist[nr8]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jy -= fq; - m6 -= fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 += fq; - m16 -= fq; - m17 += fq; + // q=9 + //nread = neighborList[n+8*Np]; + //fq = dist[nread]; + nr9 = neighborList[n + 8 * Np]; + fq = dist[nr9]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 += fq; + m17 += fq; - // q=9 - //nread = neighborList[n+8*Np]; - //fq = dist[nread]; - nr9 = neighborList[n+8*Np]; - fq = dist[nr9]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jy -= fq; - m6 -= fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 -= fq; - m16 += fq; - m17 += fq; + // q = 10 + //nread = neighborList[n+9*Np]; + //fq = dist[nread]; + nr10 = neighborList[n + 9 * Np]; + fq = dist[nr10]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 -= fq; + m17 -= fq; - // q = 10 - //nread = neighborList[n+9*Np]; - //fq = dist[nread]; - nr10 = neighborList[n+9*Np]; - fq = dist[nr10]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jy += fq; - m6 += fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 -= fq; - m16 -= fq; - m17 -= fq; + // q=11 + //nread = neighborList[n+10*Np]; + //fq = dist[nread]; + nr11 = neighborList[n + 10 * Np]; + fq = dist[nr11]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 = fq; + m16 -= fq; + m18 = fq; - // q=11 - //nread = neighborList[n+10*Np]; - //fq = dist[nread]; - nr11 = neighborList[n+10*Np]; - fq = dist[nr11]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jz += fq; - m8 += fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 = fq; - m16 -= fq; - m18 = fq; + // q=12 + //nread = neighborList[n+11*Np]; + //fq = dist[nread]; + nr12 = neighborList[n + 11 * Np]; + fq = dist[nr12]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 += fq; + m16 += fq; + m18 -= fq; - // q=12 - //nread = neighborList[n+11*Np]; - //fq = dist[nread]; - nr12 = neighborList[n+11*Np]; - fq = dist[nr12]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jz -= fq; - m8 -= fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 += fq; - m16 += fq; - m18 -= fq; + // q=13 + //nread = neighborList[n+12*Np]; + //fq = dist[nread]; + nr13 = neighborList[n + 12 * Np]; + fq = dist[nr13]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 -= fq; + m18 -= fq; - // q=13 - //nread = neighborList[n+12*Np]; - //fq = dist[nread]; - nr13 = neighborList[n+12*Np]; - fq = dist[nr13]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jz -= fq; - m8 -= fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 -= fq; - m16 -= fq; - m18 -= fq; + // q=14 + //nread = neighborList[n+13*Np]; + //fq = dist[nread]; + nr14 = neighborList[n + 13 * Np]; + fq = dist[nr14]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 += fq; + m18 += fq; - // q=14 - //nread = neighborList[n+13*Np]; - //fq = dist[nread]; - nr14 = neighborList[n+13*Np]; - fq = dist[nr14]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jz += fq; - m8 += fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 -= fq; - m16 += fq; - m18 += fq; + // q=15 + nread = neighborList[n + 14 * Np]; + fq = dist[nread]; + //fq = dist[17*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 = fq; + m17 += fq; + m18 -= fq; - // q=15 - nread = neighborList[n+14*Np]; - fq = dist[nread]; - //fq = dist[17*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy += fq; - m6 += fq; - jz += fq; - m8 += fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 = fq; - m17 += fq; - m18 -= fq; + // q=16 + nread = neighborList[n + 15 * Np]; + fq = dist[nread]; + //fq = dist[8*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 += fq; + m17 -= fq; + m18 += fq; - // q=16 - nread = neighborList[n+15*Np]; - fq = dist[nread]; - //fq = dist[8*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy -= fq; - m6 -= fq; - jz -= fq; - m8 -= fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 += fq; - m17 -= fq; - m18 += fq; + // q=17 + //fq = dist[18*Np+n]; + nread = neighborList[n + 16 * Np]; + fq = dist[nread]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 += fq; + m18 += fq; - // q=17 - //fq = dist[18*Np+n]; - nread = neighborList[n+16*Np]; - fq = dist[nread]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy += fq; - m6 += fq; - jz -= fq; - m8 -= fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 -= fq; - m17 += fq; - m18 += fq; + // q=18 + nread = neighborList[n + 17 * Np]; + fq = dist[nread]; + //fq = dist[9*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 -= fq; + m18 -= fq; - // q=18 - nread = neighborList[n+17*Np]; - fq = dist[nread]; - //fq = dist[9*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy -= fq; - m6 -= fq; - jz += fq; - m8 += fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 -= fq; - m17 -= fq; - m18 -= fq; - // Compute greyscale related parameters - ux = (jx/rho0+0.5*porosity*Gx+0.5*Fcpx/rho0)/(1.0+0.5*porosity*mu_eff/perm); - uy = (jy/rho0+0.5*porosity*Gy+0.5*Fcpy/rho0)/(1.0+0.5*porosity*mu_eff/perm); - uz = (jz/rho0+0.5*porosity*Gz+0.5*Fcpz/rho0)/(1.0+0.5*porosity*mu_eff/perm); - if (porosity==1.0){//i.e. open nodes - ux = (jx/rho0+0.5*porosity*Gx); - uy = (jy/rho0+0.5*porosity*Gy); - uz = (jz/rho0+0.5*porosity*Gz); + ux = (jx / rho0 + 0.5 * porosity * Gx + 0.5 * Fcpx / rho0) / + (1.0 + 0.5 * porosity * mu_eff / perm); + uy = (jy / rho0 + 0.5 * porosity * Gy + 0.5 * Fcpy / rho0) / + (1.0 + 0.5 * porosity * mu_eff / perm); + uz = (jz / rho0 + 0.5 * porosity * Gz + 0.5 * Fcpz / rho0) / + (1.0 + 0.5 * porosity * mu_eff / perm); + if (porosity == 1.0) { //i.e. open nodes + ux = (jx / rho0 + 0.5 * porosity * Gx); + uy = (jy / rho0 + 0.5 * porosity * Gy); + uz = (jz / rho0 + 0.5 * porosity * Gz); } //Update the total force to include linear (Darcy) and nonlinear (Forchheimer) drags due to the porous medium - Fx = rho0*(-porosity*mu_eff/perm*ux + porosity*Gx)+Fcpx; - Fy = rho0*(-porosity*mu_eff/perm*uy + porosity*Gy)+Fcpy; - Fz = rho0*(-porosity*mu_eff/perm*uz + porosity*Gz)+Fcpz; - if (porosity==1.0){ - Fx=rho0*(porosity*Gx); - Fy=rho0*(porosity*Gy); - Fz=rho0*(porosity*Gz); + Fx = rho0 * (-porosity * mu_eff / perm * ux + porosity * Gx) + Fcpx; + Fy = rho0 * (-porosity * mu_eff / perm * uy + porosity * Gy) + Fcpy; + Fz = rho0 * (-porosity * mu_eff / perm * uz + porosity * Gz) + Fcpz; + if (porosity == 1.0) { + Fx = rho0 * (porosity * Gx); + Fy = rho0 * (porosity * Gy); + Fz = rho0 * (porosity * Gz); } - // write the velocity - Velocity[n] = ux; - Velocity[Np+n] = uy; - Velocity[2*Np+n] = uz; + // write the velocity + Velocity[n] = ux; + Velocity[Np + n] = uy; + Velocity[2 * Np + n] = uz; //Pressure[n] = rho/3.f/porosity; - Pressure[n] = rho/3.f; + Pressure[n] = rho / 3.f; - //........................................................................ - //..............carry out relaxation process.............................. - //..........Toelke, Fruediger et. al. 2006................................ + //........................................................................ + //..............carry out relaxation process.............................. + //..........Toelke, Fruediger et. al. 2006................................ //---------------- NO higher-order force -------------------------------// - if (C == 0.0) nx = ny = nz = 0.0; - m1 = m1 + rlx_setA*((19*(ux*ux+uy*uy+uz*uz)*rho0/porosity - 11*rho) -19*alpha*C - m1); - m2 = m2 + rlx_setA*((3*rho - 5.5*(ux*ux+uy*uy+uz*uz)*rho0/porosity)- m2); + if (C == 0.0) + nx = ny = nz = 0.0; + m1 = m1 + + rlx_setA * ((19 * (ux * ux + uy * uy + uz * uz) * rho0 / porosity - + 11 * rho) - + 19 * alpha * C - m1); + m2 = m2 + rlx_setA * ((3 * rho - 5.5 * (ux * ux + uy * uy + uz * uz) * + rho0 / porosity) - + m2); jx = jx + Fx; - m4 = m4 + rlx_setB*((-0.6666666666666666*ux*rho0)- m4) - + (1-0.5*rlx_setB)*(-0.6666666666666666*Fx); + m4 = m4 + rlx_setB * ((-0.6666666666666666 * ux * rho0) - m4) + + (1 - 0.5 * rlx_setB) * (-0.6666666666666666 * Fx); jy = jy + Fy; - m6 = m6 + rlx_setB*((-0.6666666666666666*uy*rho0)- m6) - + (1-0.5*rlx_setB)*(-0.6666666666666666*Fy); + m6 = m6 + rlx_setB * ((-0.6666666666666666 * uy * rho0) - m6) + + (1 - 0.5 * rlx_setB) * (-0.6666666666666666 * Fy); jz = jz + Fz; - m8 = m8 + rlx_setB*((-0.6666666666666666*uz*rho0)- m8) - + (1-0.5*rlx_setB)*(-0.6666666666666666*Fz); - m9 = m9 + rlx_setA*(((2*ux*ux-uy*uy-uz*uz)*rho0/porosity) + 0.5*alpha*C*(2*nx*nx-ny*ny-nz*nz) - m9); - m10 = m10 + rlx_setA*( - m10); + m8 = m8 + rlx_setB * ((-0.6666666666666666 * uz * rho0) - m8) + + (1 - 0.5 * rlx_setB) * (-0.6666666666666666 * Fz); + m9 = + m9 + rlx_setA * + (((2 * ux * ux - uy * uy - uz * uz) * rho0 / porosity) + + 0.5 * alpha * C * (2 * nx * nx - ny * ny - nz * nz) - m9); + m10 = m10 + rlx_setA * (-m10); //m10 = m10 + rlx_setA*(-0.5*rho0*((2*ux*ux-uy*uy-uz*uz)/porosity)- m10); - m11 = m11 + rlx_setA*(((uy*uy-uz*uz)*rho0/porosity) + 0.5*alpha*C*(ny*ny-nz*nz)- m11); - m12 = m12 + rlx_setA*( - m12); + m11 = m11 + rlx_setA * (((uy * uy - uz * uz) * rho0 / porosity) + + 0.5 * alpha * C * (ny * ny - nz * nz) - m11); + m12 = m12 + rlx_setA * (-m12); //m12 = m12 + rlx_setA*(-0.5*(rho0*(uy*uy-uz*uz)/porosity)- m12); - m13 = m13 + rlx_setA*( (ux*uy*rho0/porosity) + 0.5*alpha*C*nx*ny - m13); - m14 = m14 + rlx_setA*( (uy*uz*rho0/porosity) + 0.5*alpha*C*ny*nz - m14); - m15 = m15 + rlx_setA*( (ux*uz*rho0/porosity) + 0.5*alpha*C*nx*nz - m15); - m16 = m16 + rlx_setB*( - m16); - m17 = m17 + rlx_setB*( - m17); - m18 = m18 + rlx_setB*( - m18); + m13 = m13 + rlx_setA * ((ux * uy * rho0 / porosity) + + 0.5 * alpha * C * nx * ny - m13); + m14 = m14 + rlx_setA * ((uy * uz * rho0 / porosity) + + 0.5 * alpha * C * ny * nz - m14); + m15 = m15 + rlx_setA * ((ux * uz * rho0 / porosity) + + 0.5 * alpha * C * nx * nz - m15); + m16 = m16 + rlx_setB * (-m16); + m17 = m17 + rlx_setB * (-m17); + m18 = m18 + rlx_setB * (-m18); //----------------------------------------------------------------------// //----------------With higher-order force ------------------------------// - //if (C == 0.0) nx = ny = nz = 0.0; - //m1 = m1 + rlx_setA*((19*(ux*ux+uy*uy+uz*uz)*rho0/porosity - 11*rho) -19*alpha*C - m1) + //if (C == 0.0) nx = ny = nz = 0.0; + //m1 = m1 + rlx_setA*((19*(ux*ux+uy*uy+uz*uz)*rho0/porosity - 11*rho) -19*alpha*C - m1) // + (1-0.5*rlx_setA)*38*(Fx*ux+Fy*uy+Fz*uz)/porosity; - //m2 = m2 + rlx_setA*((3*rho - 5.5*(ux*ux+uy*uy+uz*uz)*rho0/porosity)- m2) + //m2 = m2 + rlx_setA*((3*rho - 5.5*(ux*ux+uy*uy+uz*uz)*rho0/porosity)- m2) // + (1-0.5*rlx_setA)*11*(-Fx*ux-Fy*uy-Fz*uz)/porosity; //jx = jx + Fx; - //m4 = m4 + rlx_setB*((-0.6666666666666666*ux*rho0)- m4) + //m4 = m4 + rlx_setB*((-0.6666666666666666*ux*rho0)- m4) // + (1-0.5*rlx_setB)*(-0.6666666666666666*Fx); //jy = jy + Fy; - //m6 = m6 + rlx_setB*((-0.6666666666666666*uy*rho0)- m6) + //m6 = m6 + rlx_setB*((-0.6666666666666666*uy*rho0)- m6) // + (1-0.5*rlx_setB)*(-0.6666666666666666*Fy); //jz = jz + Fz; - //m8 = m8 + rlx_setB*((-0.6666666666666666*uz*rho0)- m8) + //m8 = m8 + rlx_setB*((-0.6666666666666666*uz*rho0)- m8) // + (1-0.5*rlx_setB)*(-0.6666666666666666*Fz); - //m9 = m9 + rlx_setA*(((2*ux*ux-uy*uy-uz*uz)*rho0/porosity) + 0.5*alpha*C*(2*nx*nx-ny*ny-nz*nz) - m9) + //m9 = m9 + rlx_setA*(((2*ux*ux-uy*uy-uz*uz)*rho0/porosity) + 0.5*alpha*C*(2*nx*nx-ny*ny-nz*nz) - m9) // + (1-0.5*rlx_setA)*(4*Fx*ux-2*Fy*uy-2*Fz*uz)/porosity; - ////m10 = m10 + rlx_setA*( - m10); + ////m10 = m10 + rlx_setA*( - m10); //m10 = m10 + rlx_setA*(-0.5*rho0*((2*ux*ux-uy*uy-uz*uz)/porosity)- m10) // + (1-0.5*rlx_setA)*(-2*Fx*ux+Fy*uy+Fz*uz)/porosity; - //m11 = m11 + rlx_setA*(((uy*uy-uz*uz)*rho0/porosity) + 0.5*alpha*C*(ny*ny-nz*nz)- m11) + //m11 = m11 + rlx_setA*(((uy*uy-uz*uz)*rho0/porosity) + 0.5*alpha*C*(ny*ny-nz*nz)- m11) // + (1-0.5*rlx_setA)*(2*Fy*uy-2*Fz*uz)/porosity; - ////m12 = m12 + rlx_setA*( - m12); + ////m12 = m12 + rlx_setA*( - m12); //m12 = m12 + rlx_setA*(-0.5*(rho0*(uy*uy-uz*uz)/porosity)- m12) // + (1-0.5*rlx_setA)*(-Fy*uy+Fz*uz)/porosity; - //m13 = m13 + rlx_setA*( (ux*uy*rho0/porosity) + 0.5*alpha*C*nx*ny - m13); + //m13 = m13 + rlx_setA*( (ux*uy*rho0/porosity) + 0.5*alpha*C*nx*ny - m13); // + (1-0.5*rlx_setA)*(Fy*ux+Fx*uy)/porosity; - //m14 = m14 + rlx_setA*( (uy*uz*rho0/porosity) + 0.5*alpha*C*ny*nz - m14); + //m14 = m14 + rlx_setA*( (uy*uz*rho0/porosity) + 0.5*alpha*C*ny*nz - m14); // + (1-0.5*rlx_setA)*(Fz*uy+Fy*uz)/porosity; - //m15 = m15 + rlx_setA*( (ux*uz*rho0/porosity) + 0.5*alpha*C*nx*nz - m15); + //m15 = m15 + rlx_setA*( (ux*uz*rho0/porosity) + 0.5*alpha*C*nx*nz - m15); // + (1-0.5*rlx_setA)*(Fz*ux+Fx*uz)/porosity; - //m16 = m16 + rlx_setB*( - m16); - //m17 = m17 + rlx_setB*( - m17); - //m18 = m18 + rlx_setB*( - m18); + //m16 = m16 + rlx_setB*( - m16); + //m17 = m17 + rlx_setB*( - m17); + //m18 = m18 + rlx_setB*( - m18); //----------------------------------------------------------------------// - //.................inverse transformation...................................................... - // q=0 - fq = mrt_V1*rho-mrt_V2*m1+mrt_V3*m2; - dist[n] = fq; + //.................inverse transformation...................................................... + // q=0 + fq = mrt_V1 * rho - mrt_V2 * m1 + mrt_V3 * m2; + dist[n] = fq; - // q = 1 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jx-m4)+mrt_V6*(m9-m10); - //nread = neighborList[n+Np]; - dist[nr2] = fq; + // q = 1 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jx - m4) + + mrt_V6 * (m9 - m10); + //nread = neighborList[n+Np]; + dist[nr2] = fq; - // q=2 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m4-jx)+mrt_V6*(m9-m10); - //nread = neighborList[n]; - dist[nr1] = fq; + // q=2 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m4 - jx) + + mrt_V6 * (m9 - m10); + //nread = neighborList[n]; + dist[nr1] = fq; - // q = 3 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jy-m6)+mrt_V7*(m10-m9)+mrt_V8*(m11-m12); - //nread = neighborList[n+3*Np]; - dist[nr4] = fq; + // q = 3 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jy - m6) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12); + //nread = neighborList[n+3*Np]; + dist[nr4] = fq; - // q = 4 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m6-jy)+mrt_V7*(m10-m9)+mrt_V8*(m11-m12); - //nread = neighborList[n+2*Np]; - dist[nr3] = fq; + // q = 4 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m6 - jy) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12); + //nread = neighborList[n+2*Np]; + dist[nr3] = fq; - // q = 5 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jz-m8)+mrt_V7*(m10-m9)+mrt_V8*(m12-m11); - //nread = neighborList[n+5*Np]; - dist[nr6] = fq; + // q = 5 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jz - m8) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11); + //nread = neighborList[n+5*Np]; + dist[nr6] = fq; - // q = 6 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m8-jz)+mrt_V7*(m10-m9)+mrt_V8*(m12-m11); - //nread = neighborList[n+4*Np]; - dist[nr5] = fq; + // q = 6 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m8 - jz) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11); + //nread = neighborList[n+4*Np]; + dist[nr5] = fq; - // q = 7 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jx+jy)+0.025*(m4+m6)+ - mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12+0.25*m13+0.125*(m16-m17); - //nread = neighborList[n+7*Np]; - dist[nr8] = fq; + // q = 7 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jy) + + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m16 - m17); + //nread = neighborList[n+7*Np]; + dist[nr8] = fq; - // q = 8 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2-0.1*(jx+jy)-0.025*(m4+m6) +mrt_V7*m9+mrt_V11*m10+mrt_V8*m11 - +mrt_V12*m12+0.25*m13+0.125*(m17-m16); - //nread = neighborList[n+6*Np]; - dist[nr7] = fq; + // q = 8 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jy) - + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m17 - m16); + //nread = neighborList[n+6*Np]; + dist[nr7] = fq; - // q = 9 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jx-jy)+0.025*(m4-m6)+ - mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12-0.25*m13+0.125*(m16+m17); - //nread = neighborList[n+9*Np]; - dist[nr10] = fq; + // q = 9 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jy) + + 0.025 * (m4 - m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 + 0.125 * (m16 + m17); + //nread = neighborList[n+9*Np]; + dist[nr10] = fq; - // q = 10 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jy-jx)+0.025*(m6-m4)+ - mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12-0.25*m13-0.125*(m16+m17); - //nread = neighborList[n+8*Np]; - dist[nr9] = fq; + // q = 10 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jx) + + 0.025 * (m6 - m4) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 - 0.125 * (m16 + m17); + //nread = neighborList[n+8*Np]; + dist[nr9] = fq; - // q = 11 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jx+jz)+0.025*(m4+m8) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12+0.25*m15+0.125*(m18-m16); - //nread = neighborList[n+11*Np]; - dist[nr12] = fq; + // q = 11 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jz) + + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m18 - m16); + //nread = neighborList[n+11*Np]; + dist[nr12] = fq; - // q = 12 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2-0.1*(jx+jz)-0.025*(m4+m8)+ - mrt_V7*m9+mrt_V11*m10-mrt_V8*m11-mrt_V12*m12+0.25*m15+0.125*(m16-m18); - //nread = neighborList[n+10*Np]; - dist[nr11]= fq; + // q = 12 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jz) - + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m16 - m18); + //nread = neighborList[n+10*Np]; + dist[nr11] = fq; - // q = 13 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jx-jz)+0.025*(m4-m8) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12-0.25*m15-0.125*(m16+m18); - //nread = neighborList[n+13*Np]; - dist[nr14] = fq; + // q = 13 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jz) + + 0.025 * (m4 - m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 - 0.125 * (m16 + m18); + //nread = neighborList[n+13*Np]; + dist[nr14] = fq; - // q= 14 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jz-jx)+0.025*(m8-m4) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12-0.25*m15+0.125*(m16+m18); - //nread = neighborList[n+12*Np]; - dist[nr13] = fq; + // q= 14 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jx) + + 0.025 * (m8 - m4) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 + 0.125 * (m16 + m18); + //nread = neighborList[n+12*Np]; + dist[nr13] = fq; + // q = 15 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy + jz) + + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m17 - m18); + nread = neighborList[n + 15 * Np]; + dist[nread] = fq; - // q = 15 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jy+jz)+0.025*(m6+m8) - -mrt_V6*m9-mrt_V7*m10+0.25*m14+0.125*(m17-m18); - nread = neighborList[n+15*Np]; - dist[nread] = fq; + // q = 16 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jy + jz) - + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m18 - m17); + nread = neighborList[n + 14 * Np]; + dist[nread] = fq; - // q = 16 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2-0.1*(jy+jz)-0.025*(m6+m8) - -mrt_V6*m9-mrt_V7*m10+0.25*m14+0.125*(m18-m17); - nread = neighborList[n+14*Np]; - dist[nread] = fq; + // q = 17 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jz) + + 0.025 * (m6 - m8) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 + + 0.125 * (m17 + m18); + nread = neighborList[n + 17 * Np]; + dist[nread] = fq; + // q = 18 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jy) + + 0.025 * (m8 - m6) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 - + 0.125 * (m17 + m18); + nread = neighborList[n + 16 * Np]; + dist[nread] = fq; + //........................................................................ - // q = 17 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jy-jz)+0.025*(m6-m8) - -mrt_V6*m9-mrt_V7*m10-0.25*m14+0.125*(m17+m18); - nread = neighborList[n+17*Np]; - dist[nread] = fq; + // Instantiate mass transport distributions + // Stationary value - distribution 0 + nAB = 1.0 / (nA + nB); + Aq[n] = 0.3333333333333333 * nA; + Bq[n] = 0.3333333333333333 * nB; - // q = 18 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jz-jy)+0.025*(m8-m6) - -mrt_V6*m9-mrt_V7*m10-0.25*m14-0.125*(m17+m18); - nread = neighborList[n+16*Np]; - dist[nread] = fq; - //........................................................................ - - // Instantiate mass transport distributions - // Stationary value - distribution 0 - nAB = 1.0/(nA+nB); - Aq[n] = 0.3333333333333333*nA; - Bq[n] = 0.3333333333333333*nB; - - //............................................... - // q = 0,2,4 - // Cq = {1,0,0}, {0,1,0}, {0,0,1} - jA = nA*ux; - jB = nB*ux; - delta = beta*nA*nB*nAB*0.1111111111111111*nx; - if (!(nA*nB*nAB>0)) delta=0; + //............................................... + // q = 0,2,4 + // Cq = {1,0,0}, {0,1,0}, {0,0,1} + jA = nA * ux; + jB = nB * ux; + delta = beta * nA * nB * nAB * 0.1111111111111111 * nx; + if (!(nA * nB * nAB > 0)) + delta = 0; //----------------newly added for better control of recoloring---------------// - if (nA/(nA+nB)>=Sn_grey && nA/(nA+nB) <= Sw_grey && porosity !=1.0){ - //delta = 0.0; - delta = -0.111111111111111*C*W*GreyDiff*nA*nB*nAB*nx; - jA = 0.5*ux*(nA+nB)*(1.0+mobility_ratio); - jB = 0.5*ux*(nA+nB)*(1.0-mobility_ratio); + if (nA / (nA + nB) >= Sn_grey && nA / (nA + nB) <= Sw_grey && + porosity != 1.0) { + //delta = 0.0; + delta = -0.111111111111111 * C * W * GreyDiff * nA * nB * nAB * nx; + jA = 0.5 * ux * (nA + nB) * (1.0 + mobility_ratio); + jB = 0.5 * ux * (nA + nB) * (1.0 - mobility_ratio); } - if (nA/(nA+nB)>Sw_grey && porosity !=1.0) delta = -1.0*delta; + if (nA / (nA + nB) > Sw_grey && porosity != 1.0) + delta = -1.0 * delta; //---------------------------------------------------------------------------// - if (RecoloringOff==true && porosity !=1.0) delta=0; - a1 = (0.1111111111111111*(nA+4.5*jA))+delta; - b1 = (0.1111111111111111*(nB+4.5*jB))-delta; - a2 = (0.1111111111111111*(nA-4.5*jA))-delta; - b2 = (0.1111111111111111*(nB-4.5*jB))+delta; + if (RecoloringOff == true && porosity != 1.0) + delta = 0; + a1 = (0.1111111111111111 * (nA + 4.5 * jA)) + delta; + b1 = (0.1111111111111111 * (nB + 4.5 * jB)) - delta; + a2 = (0.1111111111111111 * (nA - 4.5 * jA)) - delta; + b2 = (0.1111111111111111 * (nB - 4.5 * jB)) + delta; - // q = 1 - //nread = neighborList[n+Np]; - Aq[nr2] = a1; - Bq[nr2] = b1; - // q=2 - //nread = neighborList[n]; - Aq[nr1] = a2; - Bq[nr1] = b2; + // q = 1 + //nread = neighborList[n+Np]; + Aq[nr2] = a1; + Bq[nr2] = b1; + // q=2 + //nread = neighborList[n]; + Aq[nr1] = a2; + Bq[nr1] = b2; - //............................................... - // Cq = {0,1,0} - jA = nA*uy; - jB = nB*uy; - delta = beta*nA*nB*nAB*0.1111111111111111*ny; - if (!(nA*nB*nAB>0)) delta=0; + //............................................... + // Cq = {0,1,0} + jA = nA * uy; + jB = nB * uy; + delta = beta * nA * nB * nAB * 0.1111111111111111 * ny; + if (!(nA * nB * nAB > 0)) + delta = 0; //----------------newly added for better control of recoloring---------------// - if (nA/(nA+nB)>=Sn_grey && nA/(nA+nB) <= Sw_grey && porosity !=1.0){ - //delta = 0.0; - delta = -0.111111111111111*C*W*GreyDiff*nA*nB*nAB*ny; - jA = 0.5*uy*(nA+nB)*(1.0+mobility_ratio); - jB = 0.5*uy*(nA+nB)*(1.0-mobility_ratio); + if (nA / (nA + nB) >= Sn_grey && nA / (nA + nB) <= Sw_grey && + porosity != 1.0) { + //delta = 0.0; + delta = -0.111111111111111 * C * W * GreyDiff * nA * nB * nAB * ny; + jA = 0.5 * uy * (nA + nB) * (1.0 + mobility_ratio); + jB = 0.5 * uy * (nA + nB) * (1.0 - mobility_ratio); } - if (nA/(nA+nB)>Sw_grey && porosity !=1.0) delta = -1.0*delta; + if (nA / (nA + nB) > Sw_grey && porosity != 1.0) + delta = -1.0 * delta; //---------------------------------------------------------------------------// - if (RecoloringOff==true && porosity !=1.0) delta=0; - a1 = (0.1111111111111111*(nA+4.5*jA))+delta; - b1 = (0.1111111111111111*(nB+4.5*jB))-delta; - a2 = (0.1111111111111111*(nA-4.5*jA))-delta; - b2 = (0.1111111111111111*(nB-4.5*jB))+delta; + if (RecoloringOff == true && porosity != 1.0) + delta = 0; + a1 = (0.1111111111111111 * (nA + 4.5 * jA)) + delta; + b1 = (0.1111111111111111 * (nB + 4.5 * jB)) - delta; + a2 = (0.1111111111111111 * (nA - 4.5 * jA)) - delta; + b2 = (0.1111111111111111 * (nB - 4.5 * jB)) + delta; - // q = 3 - //nread = neighborList[n+3*Np]; - Aq[nr4] = a1; - Bq[nr4] = b1; - // q = 4 - //nread = neighborList[n+2*Np]; - Aq[nr3] = a2; - Bq[nr3] = b2; + // q = 3 + //nread = neighborList[n+3*Np]; + Aq[nr4] = a1; + Bq[nr4] = b1; + // q = 4 + //nread = neighborList[n+2*Np]; + Aq[nr3] = a2; + Bq[nr3] = b2; - //............................................... - // q = 4 - // Cq = {0,0,1} - jA = nA*uz; - jB = nB*uz; - delta = beta*nA*nB*nAB*0.1111111111111111*nz; - if (!(nA*nB*nAB>0)) delta=0; + //............................................... + // q = 4 + // Cq = {0,0,1} + jA = nA * uz; + jB = nB * uz; + delta = beta * nA * nB * nAB * 0.1111111111111111 * nz; + if (!(nA * nB * nAB > 0)) + delta = 0; //----------------newly added for better control of recoloring---------------// - if (nA/(nA+nB)>=Sn_grey && nA/(nA+nB) <= Sw_grey && porosity !=1.0){ - //delta = 0.0; - delta = -0.111111111111111*C*W*GreyDiff*nA*nB*nAB*nz; - jA = 0.5*uz*(nA+nB)*(1.0+mobility_ratio); - jB = 0.5*uz*(nA+nB)*(1.0-mobility_ratio); + if (nA / (nA + nB) >= Sn_grey && nA / (nA + nB) <= Sw_grey && + porosity != 1.0) { + //delta = 0.0; + delta = -0.111111111111111 * C * W * GreyDiff * nA * nB * nAB * nz; + jA = 0.5 * uz * (nA + nB) * (1.0 + mobility_ratio); + jB = 0.5 * uz * (nA + nB) * (1.0 - mobility_ratio); } - if (nA/(nA+nB)>Sw_grey && porosity !=1.0) delta = -1.0*delta; + if (nA / (nA + nB) > Sw_grey && porosity != 1.0) + delta = -1.0 * delta; //---------------------------------------------------------------------------// - if (RecoloringOff==true && porosity !=1.0) delta=0; + if (RecoloringOff == true && porosity != 1.0) + delta = 0; - a1 = (0.1111111111111111*(nA+4.5*jA))+delta; - b1 = (0.1111111111111111*(nB+4.5*jB))-delta; - a2 = (0.1111111111111111*(nA-4.5*jA))-delta; - b2 = (0.1111111111111111*(nB-4.5*jB))+delta; + a1 = (0.1111111111111111 * (nA + 4.5 * jA)) + delta; + b1 = (0.1111111111111111 * (nB + 4.5 * jB)) - delta; + a2 = (0.1111111111111111 * (nA - 4.5 * jA)) - delta; + b2 = (0.1111111111111111 * (nB - 4.5 * jB)) + delta; - // q = 5 - //nread = neighborList[n+5*Np]; - Aq[nr6] = a1; - Bq[nr6] = b1; - // q = 6 - //nread = neighborList[n+4*Np]; - Aq[nr5] = a2; - Bq[nr5] = b2; - //............................................... - } + // q = 5 + //nread = neighborList[n+5*Np]; + Aq[nr6] = a1; + Bq[nr6] = b1; + // q = 6 + //nread = neighborList[n+4*Np]; + Aq[nr5] = a2; + Bq[nr5] = b2; + //............................................... + } } //CP: capillary penalty // also turn off recoloring for grey nodes -extern "C" void ScaLBL_D3Q19_AAeven_GreyscaleColor_CP(int *Map, double *dist, double *Aq, double *Bq, double *Den, - double *Phi, double *GreySolidW, double *GreySn, double *GreySw, double *GreyKn, double *GreyKw, double *Poros,double *Perm, - double *Velocity, double *MobilityRatio, double *Pressure, - double rhoA, double rhoB, double tauA, double tauB,double tauA_eff,double tauB_eff, double alpha, double beta, - double Gx, double Gy, double Gz, bool RecoloringOff, int strideY, int strideZ, int start, int finish, int Np){ +extern "C" void ScaLBL_D3Q19_AAeven_GreyscaleColor_CP( + int *Map, double *dist, double *Aq, double *Bq, double *Den, double *Phi, + double *GreySolidW, double *GreySn, double *GreySw, double *GreyKn, + double *GreyKw, double *Poros, double *Perm, double *Velocity, + double *MobilityRatio, double *Pressure, double rhoA, double rhoB, + double tauA, double tauB, double tauA_eff, double tauB_eff, double alpha, + double beta, double Gx, double Gy, double Gz, bool RecoloringOff, + int strideY, int strideZ, int start, int finish, int Np) { - int ijk,nn,n; - double fq; - // conserved momemnts - double rho,jx,jy,jz; - //double vx,vy,vz,v_mag; + int ijk, nn, n; + double fq; + // conserved momemnts + double rho, jx, jy, jz; + //double vx,vy,vz,v_mag; //double ux,uy,uz,u_mag; - double ux,uy,uz; - // non-conserved moments - double m1,m2,m4,m6,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18; - double m3,m5,m7; - double nA,nB; // number density - double a1,b1,a2,b2,nAB,delta; - double C,nx,ny,nz; //color gradient magnitude and direction - double phi,tau,rho0,rlx_setA,rlx_setB; - double W;//greyscale wetting strength - double Sn_grey,Sw_grey; - + double ux, uy, uz; + // non-conserved moments + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; + double m3, m5, m7; + double nA, nB; // number density + double a1, b1, a2, b2, nAB, delta; + double C, nx, ny, nz; //color gradient magnitude and direction + double phi, tau, rho0, rlx_setA, rlx_setB; + double W; //greyscale wetting strength + double Sn_grey, Sw_grey; + /* Corey model parameters */ - double Kn_grey,Kw_grey; - double Swn,Krn_grey,Krw_grey,mobility_ratio,jA,jB; - double GreyDiff=0.0e-4; // grey diffusion + double Kn_grey, Kw_grey; + double Swn, Krn_grey, Krw_grey, mobility_ratio, jA, jB; + double GreyDiff = 0.0e-4; // grey diffusion //double GeoFun=0.0;//geometric function from Guo's PRE 66, 036304 (2002) double porosity; - double perm;//voxel permeability + double perm; //voxel permeability //double c0, c1; //Guo's model parameters double tau_eff; - double mu_eff;//kinematic viscosity - double Fx,Fy,Fz; - double Fcpx,Fcpy,Fcpz;//capillary penalty force + double mu_eff; //kinematic viscosity + double Fx, Fy, Fz; + double Fcpx, Fcpy, Fcpz; //capillary penalty force - const double mrt_V1=0.05263157894736842; - const double mrt_V2=0.012531328320802; - const double mrt_V3=0.04761904761904762; - const double mrt_V4=0.004594820384294068; - const double mrt_V5=0.01587301587301587; - const double mrt_V6=0.0555555555555555555555555; - const double mrt_V7=0.02777777777777778; - const double mrt_V8=0.08333333333333333; - const double mrt_V9=0.003341687552213868; - const double mrt_V10=0.003968253968253968; - const double mrt_V11=0.01388888888888889; - const double mrt_V12=0.04166666666666666; + const double mrt_V1 = 0.05263157894736842; + const double mrt_V2 = 0.012531328320802; + const double mrt_V3 = 0.04761904761904762; + const double mrt_V4 = 0.004594820384294068; + const double mrt_V5 = 0.01587301587301587; + const double mrt_V6 = 0.0555555555555555555555555; + const double mrt_V7 = 0.02777777777777778; + const double mrt_V8 = 0.08333333333333333; + const double mrt_V9 = 0.003341687552213868; + const double mrt_V10 = 0.003968253968253968; + const double mrt_V11 = 0.01388888888888889; + const double mrt_V12 = 0.04166666666666666; - for (n=start; n=Sn_grey && nA/(nA+nB) <= Sw_grey && porosity !=1.0){ - Swn = (nA/(nA+nB) - Sn_grey) /(Sw_grey - Sn_grey); - Krn_grey = Kn_grey*Swn*Swn; // Corey model with exponent = 2, make sure that W cannot shift to zero - Krw_grey = Kw_grey*(1.0-Swn)*(1.0-Swn); // Corey model with exponent = 4, make sure that W cannot shift to zero - // recompute the effective permeability - perm = mu_eff*(Krn_grey*3.0/(tauA-0.5) + Krw_grey*3.0/(tauB-0.5)); - //mobility_ratio =(nA*Krn_grey*3.0/(tauA-0.5) - nB*Krw_grey*3.0/(tauB-0.5))/(nA*Krn_grey*3.0/(tauA-0.5) + nB*Krw_grey*3.0/(tauB-0.5)); - } - else if (nA/(nA+nB)>Sw_grey && porosity !=1.0){ - perm = Kn_grey; - Krn_grey = Kn_grey; - Swn = 1.0; + if (nA / (nA + nB) < Sn_grey && porosity != 1.0) { + perm = Kw_grey; + Krw_grey = Kw_grey; + Swn = 0.0; + } else if (nA / (nA + nB) >= Sn_grey && nA / (nA + nB) <= Sw_grey && + porosity != 1.0) { + Swn = (nA / (nA + nB) - Sn_grey) / (Sw_grey - Sn_grey); + Krn_grey = + Kn_grey * Swn * + Swn; // Corey model with exponent = 2, make sure that W cannot shift to zero + Krw_grey = + Kw_grey * (1.0 - Swn) * + (1.0 - + Swn); // Corey model with exponent = 4, make sure that W cannot shift to zero + // recompute the effective permeability + perm = mu_eff * (Krn_grey * 3.0 / (tauA - 0.5) + + Krw_grey * 3.0 / (tauB - 0.5)); + //mobility_ratio =(nA*Krn_grey*3.0/(tauA-0.5) - nB*Krw_grey*3.0/(tauB-0.5))/(nA*Krn_grey*3.0/(tauA-0.5) + nB*Krw_grey*3.0/(tauB-0.5)); + } else if (nA / (nA + nB) > Sw_grey && porosity != 1.0) { + perm = Kn_grey; + Krn_grey = Kn_grey; + Swn = 1.0; } /* compute the mobility ratio */ - if (porosity != 1.0){ - mobility_ratio =(Krn_grey/(tauA-0.5) - Krw_grey/(tauB-0.5))/(Krn_grey/(tauA-0.5) + Krw_grey/(tauB-0.5)); - } - else if (phi > 0.0){ - mobility_ratio = 1.0; - } - else { - mobility_ratio = -1.0; + if (porosity != 1.0) { + mobility_ratio = + (Krn_grey / (tauA - 0.5) - Krw_grey / (tauB - 0.5)) / + (Krn_grey / (tauA - 0.5) + Krw_grey / (tauB - 0.5)); + } else if (phi > 0.0) { + mobility_ratio = 1.0; + } else { + mobility_ratio = -1.0; } MobilityRatio[n] = mobility_ratio; - - // Get the 1D index based on regular data layout - ijk = Map[n]; - // COMPUTE THE COLOR GRADIENT - //........................................................................ - //.................Read Phase Indicator Values............................ - //........................................................................ - nn = ijk-1; // neighbor index (get convention) - m1 = Phi[nn]; // get neighbor for phi - 1 - //........................................................................ - nn = ijk+1; // neighbor index (get convention) - m2 = Phi[nn]; // get neighbor for phi - 2 - //........................................................................ - nn = ijk-strideY; // neighbor index (get convention) - m3 = Phi[nn]; // get neighbor for phi - 3 - //........................................................................ - nn = ijk+strideY; // neighbor index (get convention) - m4 = Phi[nn]; // get neighbor for phi - 4 - //........................................................................ - nn = ijk-strideZ; // neighbor index (get convention) - m5 = Phi[nn]; // get neighbor for phi - 5 - //........................................................................ - nn = ijk+strideZ; // neighbor index (get convention) - m6 = Phi[nn]; // get neighbor for phi - 6 - //........................................................................ - nn = ijk-strideY-1; // neighbor index (get convention) - m7 = Phi[nn]; // get neighbor for phi - 7 - //........................................................................ - nn = ijk+strideY+1; // neighbor index (get convention) - m8 = Phi[nn]; // get neighbor for phi - 8 - //........................................................................ - nn = ijk+strideY-1; // neighbor index (get convention) - m9 = Phi[nn]; // get neighbor for phi - 9 - //........................................................................ - nn = ijk-strideY+1; // neighbor index (get convention) - m10 = Phi[nn]; // get neighbor for phi - 10 - //........................................................................ - nn = ijk-strideZ-1; // neighbor index (get convention) - m11 = Phi[nn]; // get neighbor for phi - 11 - //........................................................................ - nn = ijk+strideZ+1; // neighbor index (get convention) - m12 = Phi[nn]; // get neighbor for phi - 12 - //........................................................................ - nn = ijk+strideZ-1; // neighbor index (get convention) - m13 = Phi[nn]; // get neighbor for phi - 13 - //........................................................................ - nn = ijk-strideZ+1; // neighbor index (get convention) - m14 = Phi[nn]; // get neighbor for phi - 14 - //........................................................................ - nn = ijk-strideZ-strideY; // neighbor index (get convention) - m15 = Phi[nn]; // get neighbor for phi - 15 - //........................................................................ - nn = ijk+strideZ+strideY; // neighbor index (get convention) - m16 = Phi[nn]; // get neighbor for phi - 16 - //........................................................................ - nn = ijk+strideZ-strideY; // neighbor index (get convention) - m17 = Phi[nn]; // get neighbor for phi - 17 - //........................................................................ - nn = ijk-strideZ+strideY; // neighbor index (get convention) - m18 = Phi[nn]; // get neighbor for phi - 18 - //............Compute the Color Gradient................................... - nx = -3.0/18.0*(m1-m2+0.5*(m7-m8+m9-m10+m11-m12+m13-m14)); - ny = -3.0/18.0*(m3-m4+0.5*(m7-m8-m9+m10+m15-m16+m17-m18)); - nz = -3.0/18.0*(m5-m6+0.5*(m11-m12-m13+m14+m15-m16-m17+m18)); - //............Compute the Greyscale Potential Gradient..................... -// Fcpx = 0.0; -// Fcpy = 0.0; -// Fcpz = 0.0; -// if (porosity!=1.0){ -// //Fcpx = -3.0/18.0*(gp1-gp2+0.5*(gp7-gp8+gp9-gp10+gp11-gp12+gp13-gp14)); -// //Fcpy = -3.0/18.0*(gp3-gp4+0.5*(gp7-gp8-gp9+gp10+gp15-gp16+gp17-gp18)); -// //Fcpz = -3.0/18.0*(gp5-gp6+0.5*(gp11-gp12-gp13+gp14+gp15-gp16-gp17+gp18)); -// Fcpx = -3.0/18.0*(m1-m2+0.5*(m7-m8+m9-m10+m11-m12+m13-m14)); -// Fcpy = -3.0/18.0*(m3-m4+0.5*(m7-m8-m9+m10+m15-m16+m17-m18)); -// Fcpz = -3.0/18.0*(m5-m6+0.5*(m11-m12-m13+m14+m15-m16-m17+m18)); -// Fcpx *= alpha*W/sqrt(perm); -// Fcpy *= alpha*W/sqrt(perm); -// Fcpz *= alpha*W/sqrt(perm); -// double Fcp_mag_temp = sqrt(Fcpx*Fcpx+Fcpy*Fcpy+Fcpz*Fcpz); -// double Fcp_mag = Fcp_mag_temp; -// if (Fcp_mag_temp==0.0) Fcp_mag=1.0; -// nx = Fcpx/Fcp_mag; -// ny = Fcpy/Fcp_mag; -// nz = Fcpz/Fcp_mag; -// } - Fcpx = nx; - Fcpy = ny; - Fcpz = nz; - double Fcp_mag=sqrt(Fcpx*Fcpx+Fcpy*Fcpy+Fcpz*Fcpz); - if (Fcp_mag==0.0) Fcpx=Fcpy=Fcpz=0.0; + // Get the 1D index based on regular data layout + ijk = Map[n]; + // COMPUTE THE COLOR GRADIENT + //........................................................................ + //.................Read Phase Indicator Values............................ + //........................................................................ + nn = ijk - 1; // neighbor index (get convention) + m1 = Phi[nn]; // get neighbor for phi - 1 + //........................................................................ + nn = ijk + 1; // neighbor index (get convention) + m2 = Phi[nn]; // get neighbor for phi - 2 + //........................................................................ + nn = ijk - strideY; // neighbor index (get convention) + m3 = Phi[nn]; // get neighbor for phi - 3 + //........................................................................ + nn = ijk + strideY; // neighbor index (get convention) + m4 = Phi[nn]; // get neighbor for phi - 4 + //........................................................................ + nn = ijk - strideZ; // neighbor index (get convention) + m5 = Phi[nn]; // get neighbor for phi - 5 + //........................................................................ + nn = ijk + strideZ; // neighbor index (get convention) + m6 = Phi[nn]; // get neighbor for phi - 6 + //........................................................................ + nn = ijk - strideY - 1; // neighbor index (get convention) + m7 = Phi[nn]; // get neighbor for phi - 7 + //........................................................................ + nn = ijk + strideY + 1; // neighbor index (get convention) + m8 = Phi[nn]; // get neighbor for phi - 8 + //........................................................................ + nn = ijk + strideY - 1; // neighbor index (get convention) + m9 = Phi[nn]; // get neighbor for phi - 9 + //........................................................................ + nn = ijk - strideY + 1; // neighbor index (get convention) + m10 = Phi[nn]; // get neighbor for phi - 10 + //........................................................................ + nn = ijk - strideZ - 1; // neighbor index (get convention) + m11 = Phi[nn]; // get neighbor for phi - 11 + //........................................................................ + nn = ijk + strideZ + 1; // neighbor index (get convention) + m12 = Phi[nn]; // get neighbor for phi - 12 + //........................................................................ + nn = ijk + strideZ - 1; // neighbor index (get convention) + m13 = Phi[nn]; // get neighbor for phi - 13 + //........................................................................ + nn = ijk - strideZ + 1; // neighbor index (get convention) + m14 = Phi[nn]; // get neighbor for phi - 14 + //........................................................................ + nn = ijk - strideZ - strideY; // neighbor index (get convention) + m15 = Phi[nn]; // get neighbor for phi - 15 + //........................................................................ + nn = ijk + strideZ + strideY; // neighbor index (get convention) + m16 = Phi[nn]; // get neighbor for phi - 16 + //........................................................................ + nn = ijk + strideZ - strideY; // neighbor index (get convention) + m17 = Phi[nn]; // get neighbor for phi - 17 + //........................................................................ + nn = ijk - strideZ + strideY; // neighbor index (get convention) + m18 = Phi[nn]; // get neighbor for phi - 18 + //............Compute the Color Gradient................................... + nx = -3.0 / 18.0 * + (m1 - m2 + 0.5 * (m7 - m8 + m9 - m10 + m11 - m12 + m13 - m14)); + ny = -3.0 / 18.0 * + (m3 - m4 + 0.5 * (m7 - m8 - m9 + m10 + m15 - m16 + m17 - m18)); + nz = -3.0 / 18.0 * + (m5 - m6 + 0.5 * (m11 - m12 - m13 + m14 + m15 - m16 - m17 + m18)); + + //............Compute the Greyscale Potential Gradient..................... + // Fcpx = 0.0; + // Fcpy = 0.0; + // Fcpz = 0.0; + // if (porosity!=1.0){ + // //Fcpx = -3.0/18.0*(gp1-gp2+0.5*(gp7-gp8+gp9-gp10+gp11-gp12+gp13-gp14)); + // //Fcpy = -3.0/18.0*(gp3-gp4+0.5*(gp7-gp8-gp9+gp10+gp15-gp16+gp17-gp18)); + // //Fcpz = -3.0/18.0*(gp5-gp6+0.5*(gp11-gp12-gp13+gp14+gp15-gp16-gp17+gp18)); + // Fcpx = -3.0/18.0*(m1-m2+0.5*(m7-m8+m9-m10+m11-m12+m13-m14)); + // Fcpy = -3.0/18.0*(m3-m4+0.5*(m7-m8-m9+m10+m15-m16+m17-m18)); + // Fcpz = -3.0/18.0*(m5-m6+0.5*(m11-m12-m13+m14+m15-m16-m17+m18)); + // Fcpx *= alpha*W/sqrt(perm); + // Fcpy *= alpha*W/sqrt(perm); + // Fcpz *= alpha*W/sqrt(perm); + // double Fcp_mag_temp = sqrt(Fcpx*Fcpx+Fcpy*Fcpy+Fcpz*Fcpz); + // double Fcp_mag = Fcp_mag_temp; + // if (Fcp_mag_temp==0.0) Fcp_mag=1.0; + // nx = Fcpx/Fcp_mag; + // ny = Fcpy/Fcp_mag; + // nz = Fcpz/Fcp_mag; + // } + Fcpx = nx; + Fcpy = ny; + Fcpz = nz; + double Fcp_mag = sqrt(Fcpx * Fcpx + Fcpy * Fcpy + Fcpz * Fcpz); + if (Fcp_mag == 0.0) + Fcpx = Fcpy = Fcpz = 0.0; //NOTE for open node (porosity=1.0),Fcp=0.0 - Fcpx *= alpha*W*(1.0-porosity)/sqrt(perm); - Fcpy *= alpha*W*(1.0-porosity)/sqrt(perm); - Fcpz *= alpha*W*(1.0-porosity)/sqrt(perm); + Fcpx *= alpha * W * (1.0 - porosity) / sqrt(perm); + Fcpy *= alpha * W * (1.0 - porosity) / sqrt(perm); + Fcpz *= alpha * W * (1.0 - porosity) / sqrt(perm); - //...........Normalize the Color Gradient................................. - C = sqrt(nx*nx+ny*ny+nz*nz); - double ColorMag = C; - if (C==0.0) ColorMag=1.0; - nx = nx/ColorMag; - ny = ny/ColorMag; - nz = nz/ColorMag; + //...........Normalize the Color Gradient................................. + C = sqrt(nx * nx + ny * ny + nz * nz); + double ColorMag = C; + if (C == 0.0) + ColorMag = 1.0; + nx = nx / ColorMag; + ny = ny / ColorMag; + nz = nz / ColorMag; - // q=0 - fq = dist[n]; - rho = fq; - m1 = -30.0*fq; - m2 = 12.0*fq; + // q=0 + fq = dist[n]; + rho = fq; + m1 = -30.0 * fq; + m2 = 12.0 * fq; - // q=1 - fq = dist[2*Np+n]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jx = fq; - m4 = -4.0*fq; - m9 = 2.0*fq; - m10 = -4.0*fq; + // q=1 + fq = dist[2 * Np + n]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jx = fq; + m4 = -4.0 * fq; + m9 = 2.0 * fq; + m10 = -4.0 * fq; - // f2 = dist[10*Np+n]; - fq = dist[1*Np+n]; - rho += fq; - m1 -= 11.0*(fq); - m2 -= 4.0*(fq); - jx -= fq; - m4 += 4.0*(fq); - m9 += 2.0*(fq); - m10 -= 4.0*(fq); + // f2 = dist[10*Np+n]; + fq = dist[1 * Np + n]; + rho += fq; + m1 -= 11.0 * (fq); + m2 -= 4.0 * (fq); + jx -= fq; + m4 += 4.0 * (fq); + m9 += 2.0 * (fq); + m10 -= 4.0 * (fq); - // q=3 - fq = dist[4*Np+n]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jy = fq; - m6 = -4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 = fq; - m12 = -2.0*fq; + // q=3 + fq = dist[4 * Np + n]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy = fq; + m6 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 = fq; + m12 = -2.0 * fq; - // q = 4 - fq = dist[3*Np+n]; - rho+= fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jy -= fq; - m6 += 4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 += fq; - m12 -= 2.0*fq; + // q = 4 + fq = dist[3 * Np + n]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy -= fq; + m6 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 += fq; + m12 -= 2.0 * fq; - // q=5 - fq = dist[6*Np+n]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jz = fq; - m8 = -4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 -= fq; - m12 += 2.0*fq; + // q=5 + fq = dist[6 * Np + n]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz = fq; + m8 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; - // q = 6 - fq = dist[5*Np+n]; - rho+= fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jz -= fq; - m8 += 4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 -= fq; - m12 += 2.0*fq; + // q = 6 + fq = dist[5 * Np + n]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz -= fq; + m8 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; - // q=7 - fq = dist[8*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jy += fq; - m6 += fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 = fq; - m16 = fq; - m17 = -fq; + // q=7 + fq = dist[8 * Np + n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 = fq; + m16 = fq; + m17 = -fq; - // q = 8 - fq = dist[7*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jy -= fq; - m6 -= fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 += fq; - m16 -= fq; - m17 += fq; + // q = 8 + fq = dist[7 * Np + n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 += fq; + m16 -= fq; + m17 += fq; - // q=9 - fq = dist[10*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jy -= fq; - m6 -= fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 -= fq; - m16 += fq; - m17 += fq; + // q=9 + fq = dist[10 * Np + n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 += fq; + m17 += fq; - // q = 10 - fq = dist[9*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jy += fq; - m6 += fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 -= fq; - m16 -= fq; - m17 -= fq; + // q = 10 + fq = dist[9 * Np + n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 -= fq; + m17 -= fq; - // q=11 - fq = dist[12*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jz += fq; - m8 += fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 = fq; - m16 -= fq; - m18 = fq; + // q=11 + fq = dist[12 * Np + n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 = fq; + m16 -= fq; + m18 = fq; - // q=12 - fq = dist[11*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jz -= fq; - m8 -= fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 += fq; - m16 += fq; - m18 -= fq; + // q=12 + fq = dist[11 * Np + n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 += fq; + m16 += fq; + m18 -= fq; - // q=13 - fq = dist[14*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jz -= fq; - m8 -= fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 -= fq; - m16 -= fq; - m18 -= fq; + // q=13 + fq = dist[14 * Np + n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 -= fq; + m18 -= fq; - // q=14 - fq = dist[13*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jz += fq; - m8 += fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 -= fq; - m16 += fq; - m18 += fq; + // q=14 + fq = dist[13 * Np + n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 += fq; + m18 += fq; - // q=15 - fq = dist[16*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy += fq; - m6 += fq; - jz += fq; - m8 += fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 = fq; - m17 += fq; - m18 -= fq; + // q=15 + fq = dist[16 * Np + n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 = fq; + m17 += fq; + m18 -= fq; - // q=16 - fq = dist[15*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy -= fq; - m6 -= fq; - jz -= fq; - m8 -= fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 += fq; - m17 -= fq; - m18 += fq; + // q=16 + fq = dist[15 * Np + n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 += fq; + m17 -= fq; + m18 += fq; - // q=17 - fq = dist[18*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy += fq; - m6 += fq; - jz -= fq; - m8 -= fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 -= fq; - m17 += fq; - m18 += fq; + // q=17 + fq = dist[18 * Np + n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 += fq; + m18 += fq; - // q=18 - fq = dist[17*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy -= fq; - m6 -= fq; - jz += fq; - m8 += fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 -= fq; - m17 -= fq; - m18 -= fq; + // q=18 + fq = dist[17 * Np + n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 -= fq; + m18 -= fq; // Compute greyscale related parameters - ux = (jx/rho0+0.5*porosity*Gx+0.5*Fcpx/rho0)/(1.0+0.5*porosity*mu_eff/perm); - uy = (jy/rho0+0.5*porosity*Gy+0.5*Fcpy/rho0)/(1.0+0.5*porosity*mu_eff/perm); - uz = (jz/rho0+0.5*porosity*Gz+0.5*Fcpz/rho0)/(1.0+0.5*porosity*mu_eff/perm); - if (porosity==1.0){//i.e. open nodes - ux = (jx/rho0+0.5*porosity*Gx); - uy = (jy/rho0+0.5*porosity*Gy); - uz = (jz/rho0+0.5*porosity*Gz); + ux = (jx / rho0 + 0.5 * porosity * Gx + 0.5 * Fcpx / rho0) / + (1.0 + 0.5 * porosity * mu_eff / perm); + uy = (jy / rho0 + 0.5 * porosity * Gy + 0.5 * Fcpy / rho0) / + (1.0 + 0.5 * porosity * mu_eff / perm); + uz = (jz / rho0 + 0.5 * porosity * Gz + 0.5 * Fcpz / rho0) / + (1.0 + 0.5 * porosity * mu_eff / perm); + if (porosity == 1.0) { //i.e. open nodes + ux = (jx / rho0 + 0.5 * porosity * Gx); + uy = (jy / rho0 + 0.5 * porosity * Gy); + uz = (jz / rho0 + 0.5 * porosity * Gz); } //Update the total force to include linear (Darcy) and nonlinear (Forchheimer) drags due to the porous medium - Fx = rho0*(-porosity*mu_eff/perm*ux + porosity*Gx)+Fcpx; - Fy = rho0*(-porosity*mu_eff/perm*uy + porosity*Gy)+Fcpy; - Fz = rho0*(-porosity*mu_eff/perm*uz + porosity*Gz)+Fcpz; - if (porosity==1.0){ - Fx=rho0*(porosity*Gx); - Fy=rho0*(porosity*Gy); - Fz=rho0*(porosity*Gz); + Fx = rho0 * (-porosity * mu_eff / perm * ux + porosity * Gx) + Fcpx; + Fy = rho0 * (-porosity * mu_eff / perm * uy + porosity * Gy) + Fcpy; + Fz = rho0 * (-porosity * mu_eff / perm * uz + porosity * Gz) + Fcpz; + if (porosity == 1.0) { + Fx = rho0 * (porosity * Gx); + Fy = rho0 * (porosity * Gy); + Fz = rho0 * (porosity * Gz); } - // write the velocity - Velocity[n] = ux; - Velocity[Np+n] = uy; - Velocity[2*Np+n] = uz; + // write the velocity + Velocity[n] = ux; + Velocity[Np + n] = uy; + Velocity[2 * Np + n] = uz; //Pressure[n] = rho/3.f/porosity; - Pressure[n] = rho/3.f; + Pressure[n] = rho / 3.f; - //........................................................................ - //..............carry out relaxation process.............................. - //..........Toelke, Fruediger et. al. 2006................................ + //........................................................................ + //..............carry out relaxation process.............................. + //..........Toelke, Fruediger et. al. 2006................................ //---------------- NO higher-order force -------------------------------// - if (C == 0.0) nx = ny = nz = 0.0; - m1 = m1 + rlx_setA*((19*(ux*ux+uy*uy+uz*uz)*rho0/porosity - 11*rho) -19*alpha*C - m1); - m2 = m2 + rlx_setA*((3*rho - 5.5*(ux*ux+uy*uy+uz*uz)*rho0/porosity)- m2); + if (C == 0.0) + nx = ny = nz = 0.0; + m1 = m1 + + rlx_setA * ((19 * (ux * ux + uy * uy + uz * uz) * rho0 / porosity - + 11 * rho) - + 19 * alpha * C - m1); + m2 = m2 + rlx_setA * ((3 * rho - 5.5 * (ux * ux + uy * uy + uz * uz) * + rho0 / porosity) - + m2); jx = jx + Fx; - m4 = m4 + rlx_setB*((-0.6666666666666666*ux*rho0)- m4) - + (1-0.5*rlx_setB)*(-0.6666666666666666*Fx); + m4 = m4 + rlx_setB * ((-0.6666666666666666 * ux * rho0) - m4) + + (1 - 0.5 * rlx_setB) * (-0.6666666666666666 * Fx); jy = jy + Fy; - m6 = m6 + rlx_setB*((-0.6666666666666666*uy*rho0)- m6) - + (1-0.5*rlx_setB)*(-0.6666666666666666*Fy); + m6 = m6 + rlx_setB * ((-0.6666666666666666 * uy * rho0) - m6) + + (1 - 0.5 * rlx_setB) * (-0.6666666666666666 * Fy); jz = jz + Fz; - m8 = m8 + rlx_setB*((-0.6666666666666666*uz*rho0)- m8) - + (1-0.5*rlx_setB)*(-0.6666666666666666*Fz); - m9 = m9 + rlx_setA*(((2*ux*ux-uy*uy-uz*uz)*rho0/porosity) + 0.5*alpha*C*(2*nx*nx-ny*ny-nz*nz) - m9); - m10 = m10 + rlx_setA*( - m10); + m8 = m8 + rlx_setB * ((-0.6666666666666666 * uz * rho0) - m8) + + (1 - 0.5 * rlx_setB) * (-0.6666666666666666 * Fz); + m9 = + m9 + rlx_setA * + (((2 * ux * ux - uy * uy - uz * uz) * rho0 / porosity) + + 0.5 * alpha * C * (2 * nx * nx - ny * ny - nz * nz) - m9); + m10 = m10 + rlx_setA * (-m10); //m10 = m10 + rlx_setA*(-0.5*rho0*((2*ux*ux-uy*uy-uz*uz)/porosity)- m10); - m11 = m11 + rlx_setA*(((uy*uy-uz*uz)*rho0/porosity) + 0.5*alpha*C*(ny*ny-nz*nz)- m11); - m12 = m12 + rlx_setA*( - m12); + m11 = m11 + rlx_setA * (((uy * uy - uz * uz) * rho0 / porosity) + + 0.5 * alpha * C * (ny * ny - nz * nz) - m11); + m12 = m12 + rlx_setA * (-m12); //m12 = m12 + rlx_setA*(-0.5*(rho0*(uy*uy-uz*uz)/porosity)- m12); - m13 = m13 + rlx_setA*( (ux*uy*rho0/porosity) + 0.5*alpha*C*nx*ny - m13); - m14 = m14 + rlx_setA*( (uy*uz*rho0/porosity) + 0.5*alpha*C*ny*nz - m14); - m15 = m15 + rlx_setA*( (ux*uz*rho0/porosity) + 0.5*alpha*C*nx*nz - m15); - m16 = m16 + rlx_setB*( - m16); - m17 = m17 + rlx_setB*( - m17); - m18 = m18 + rlx_setB*( - m18); + m13 = m13 + rlx_setA * ((ux * uy * rho0 / porosity) + + 0.5 * alpha * C * nx * ny - m13); + m14 = m14 + rlx_setA * ((uy * uz * rho0 / porosity) + + 0.5 * alpha * C * ny * nz - m14); + m15 = m15 + rlx_setA * ((ux * uz * rho0 / porosity) + + 0.5 * alpha * C * nx * nz - m15); + m16 = m16 + rlx_setB * (-m16); + m17 = m17 + rlx_setB * (-m17); + m18 = m18 + rlx_setB * (-m18); //----------------------------------------------------------------------// //----------------With higher-order force ------------------------------// - //if (C == 0.0) nx = ny = nz = 0.0; - //m1 = m1 + rlx_setA*((19*(ux*ux+uy*uy+uz*uz)*rho0/porosity - 11*rho) -19*alpha*C - m1) + //if (C == 0.0) nx = ny = nz = 0.0; + //m1 = m1 + rlx_setA*((19*(ux*ux+uy*uy+uz*uz)*rho0/porosity - 11*rho) -19*alpha*C - m1) // + (1-0.5*rlx_setA)*38*(Fx*ux+Fy*uy+Fz*uz)/porosity; - //m2 = m2 + rlx_setA*((3*rho - 5.5*(ux*ux+uy*uy+uz*uz)*rho0/porosity)- m2) + //m2 = m2 + rlx_setA*((3*rho - 5.5*(ux*ux+uy*uy+uz*uz)*rho0/porosity)- m2) // + (1-0.5*rlx_setA)*11*(-Fx*ux-Fy*uy-Fz*uz)/porosity; //jx = jx + Fx; - //m4 = m4 + rlx_setB*((-0.6666666666666666*ux*rho0)- m4) + //m4 = m4 + rlx_setB*((-0.6666666666666666*ux*rho0)- m4) // + (1-0.5*rlx_setB)*(-0.6666666666666666*Fx); //jy = jy + Fy; - //m6 = m6 + rlx_setB*((-0.6666666666666666*uy*rho0)- m6) + //m6 = m6 + rlx_setB*((-0.6666666666666666*uy*rho0)- m6) // + (1-0.5*rlx_setB)*(-0.6666666666666666*Fy); //jz = jz + Fz; - //m8 = m8 + rlx_setB*((-0.6666666666666666*uz*rho0)- m8) + //m8 = m8 + rlx_setB*((-0.6666666666666666*uz*rho0)- m8) // + (1-0.5*rlx_setB)*(-0.6666666666666666*Fz); - //m9 = m9 + rlx_setA*(((2*ux*ux-uy*uy-uz*uz)*rho0/porosity) + 0.5*alpha*C*(2*nx*nx-ny*ny-nz*nz) - m9) + //m9 = m9 + rlx_setA*(((2*ux*ux-uy*uy-uz*uz)*rho0/porosity) + 0.5*alpha*C*(2*nx*nx-ny*ny-nz*nz) - m9) // + (1-0.5*rlx_setA)*(4*Fx*ux-2*Fy*uy-2*Fz*uz)/porosity; - ////m10 = m10 + rlx_setA*( - m10); + ////m10 = m10 + rlx_setA*( - m10); //m10 = m10 + rlx_setA*(-0.5*rho0*((2*ux*ux-uy*uy-uz*uz)/porosity)- m10) // + (1-0.5*rlx_setA)*(-2*Fx*ux+Fy*uy+Fz*uz)/porosity; - //m11 = m11 + rlx_setA*(((uy*uy-uz*uz)*rho0/porosity) + 0.5*alpha*C*(ny*ny-nz*nz)- m11) + //m11 = m11 + rlx_setA*(((uy*uy-uz*uz)*rho0/porosity) + 0.5*alpha*C*(ny*ny-nz*nz)- m11) // + (1-0.5*rlx_setA)*(2*Fy*uy-2*Fz*uz)/porosity; - ////m12 = m12 + rlx_setA*( - m12); + ////m12 = m12 + rlx_setA*( - m12); //m12 = m12 + rlx_setA*(-0.5*(rho0*(uy*uy-uz*uz)/porosity)- m12) // + (1-0.5*rlx_setA)*(-Fy*uy+Fz*uz)/porosity; - //m13 = m13 + rlx_setA*( (ux*uy*rho0/porosity) + 0.5*alpha*C*nx*ny - m13); + //m13 = m13 + rlx_setA*( (ux*uy*rho0/porosity) + 0.5*alpha*C*nx*ny - m13); // + (1-0.5*rlx_setA)*(Fy*ux+Fx*uy)/porosity; - //m14 = m14 + rlx_setA*( (uy*uz*rho0/porosity) + 0.5*alpha*C*ny*nz - m14); + //m14 = m14 + rlx_setA*( (uy*uz*rho0/porosity) + 0.5*alpha*C*ny*nz - m14); // + (1-0.5*rlx_setA)*(Fz*uy+Fy*uz)/porosity; - //m15 = m15 + rlx_setA*( (ux*uz*rho0/porosity) + 0.5*alpha*C*nx*nz - m15); + //m15 = m15 + rlx_setA*( (ux*uz*rho0/porosity) + 0.5*alpha*C*nx*nz - m15); // + (1-0.5*rlx_setA)*(Fz*ux+Fx*uz)/porosity; - //m16 = m16 + rlx_setB*( - m16); - //m17 = m17 + rlx_setB*( - m17); - //m18 = m18 + rlx_setB*( - m18); + //m16 = m16 + rlx_setB*( - m16); + //m17 = m17 + rlx_setB*( - m17); + //m18 = m18 + rlx_setB*( - m18); //----------------------------------------------------------------------// - //.................inverse transformation...................................................... - // q=0 - fq = mrt_V1*rho-mrt_V2*m1+mrt_V3*m2; - dist[n] = fq; + //.................inverse transformation...................................................... + // q=0 + fq = mrt_V1 * rho - mrt_V2 * m1 + mrt_V3 * m2; + dist[n] = fq; - // q = 1 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jx-m4)+mrt_V6*(m9-m10); - dist[1*Np+n] = fq; + // q = 1 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jx - m4) + + mrt_V6 * (m9 - m10); + dist[1 * Np + n] = fq; - // q=2 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m4-jx)+mrt_V6*(m9-m10); - dist[2*Np+n] = fq; + // q=2 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m4 - jx) + + mrt_V6 * (m9 - m10); + dist[2 * Np + n] = fq; - // q = 3 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jy-m6)+mrt_V7*(m10-m9)+mrt_V8*(m11-m12); - dist[3*Np+n] = fq; + // q = 3 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jy - m6) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12); + dist[3 * Np + n] = fq; - // q = 4 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m6-jy)+mrt_V7*(m10-m9)+mrt_V8*(m11-m12); - dist[4*Np+n] = fq; + // q = 4 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m6 - jy) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12); + dist[4 * Np + n] = fq; - // q = 5 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jz-m8)+mrt_V7*(m10-m9)+mrt_V8*(m12-m11); - dist[5*Np+n] = fq; + // q = 5 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jz - m8) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11); + dist[5 * Np + n] = fq; - // q = 6 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m8-jz)+mrt_V7*(m10-m9)+mrt_V8*(m12-m11); - dist[6*Np+n] = fq; + // q = 6 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m8 - jz) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11); + dist[6 * Np + n] = fq; - // q = 7 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jx+jy)+0.025*(m4+m6)+ - mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12+0.25*m13+0.125*(m16-m17); - dist[7*Np+n] = fq; + // q = 7 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jy) + + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m16 - m17); + dist[7 * Np + n] = fq; + // q = 8 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jy) - + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m17 - m16); + dist[8 * Np + n] = fq; - // q = 8 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2-0.1*(jx+jy)-0.025*(m4+m6) +mrt_V7*m9+mrt_V11*m10+mrt_V8*m11 - +mrt_V12*m12+0.25*m13+0.125*(m17-m16); - dist[8*Np+n] = fq; + // q = 9 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jy) + + 0.025 * (m4 - m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 + 0.125 * (m16 + m17); + dist[9 * Np + n] = fq; - // q = 9 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jx-jy)+0.025*(m4-m6)+ - mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12-0.25*m13+0.125*(m16+m17); - dist[9*Np+n] = fq; + // q = 10 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jx) + + 0.025 * (m6 - m4) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 - 0.125 * (m16 + m17); + dist[10 * Np + n] = fq; - // q = 10 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jy-jx)+0.025*(m6-m4)+ - mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12-0.25*m13-0.125*(m16+m17); - dist[10*Np+n] = fq; + // q = 11 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jz) + + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m18 - m16); + dist[11 * Np + n] = fq; + // q = 12 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jz) - + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m16 - m18); + dist[12 * Np + n] = fq; - // q = 11 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jx+jz)+0.025*(m4+m8) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12+0.25*m15+0.125*(m18-m16); - dist[11*Np+n] = fq; + // q = 13 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jz) + + 0.025 * (m4 - m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 - 0.125 * (m16 + m18); + dist[13 * Np + n] = fq; - // q = 12 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2-0.1*(jx+jz)-0.025*(m4+m8)+ - mrt_V7*m9+mrt_V11*m10-mrt_V8*m11-mrt_V12*m12+0.25*m15+0.125*(m16-m18); - dist[12*Np+n] = fq; + // q= 14 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jx) + + 0.025 * (m8 - m4) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 + 0.125 * (m16 + m18); - // q = 13 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jx-jz)+0.025*(m4-m8) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12-0.25*m15-0.125*(m16+m18); - dist[13*Np+n] = fq; + dist[14 * Np + n] = fq; - // q= 14 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jz-jx)+0.025*(m8-m4) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12-0.25*m15+0.125*(m16+m18); + // q = 15 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy + jz) + + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m17 - m18); + dist[15 * Np + n] = fq; - dist[14*Np+n] = fq; + // q = 16 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jy + jz) - + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m18 - m17); + dist[16 * Np + n] = fq; - // q = 15 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jy+jz)+0.025*(m6+m8) - -mrt_V6*m9-mrt_V7*m10+0.25*m14+0.125*(m17-m18); - dist[15*Np+n] = fq; + // q = 17 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jz) + + 0.025 * (m6 - m8) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 + + 0.125 * (m17 + m18); + dist[17 * Np + n] = fq; - // q = 16 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2-0.1*(jy+jz)-0.025*(m6+m8) - -mrt_V6*m9-mrt_V7*m10+0.25*m14+0.125*(m18-m17); - dist[16*Np+n] = fq; + // q = 18 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jy) + + 0.025 * (m8 - m6) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 - + 0.125 * (m17 + m18); + dist[18 * Np + n] = fq; + //........................................................................ + // Instantiate mass transport distributions + // Stationary value - distribution 0 + nAB = 1.0 / (nA + nB); + Aq[n] = 0.3333333333333333 * nA; + Bq[n] = 0.3333333333333333 * nB; - // q = 17 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jy-jz)+0.025*(m6-m8) - -mrt_V6*m9-mrt_V7*m10-0.25*m14+0.125*(m17+m18); - dist[17*Np+n] = fq; - - // q = 18 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jz-jy)+0.025*(m8-m6) - -mrt_V6*m9-mrt_V7*m10-0.25*m14-0.125*(m17+m18); - dist[18*Np+n] = fq; - //........................................................................ - - // Instantiate mass transport distributions - // Stationary value - distribution 0 - nAB = 1.0/(nA+nB); - Aq[n] = 0.3333333333333333*nA; - Bq[n] = 0.3333333333333333*nB; - - //............................................... - // q = 0,2,4 - // Cq = {1,0,0}, {0,1,0}, {0,0,1} - jA = nA*ux; - jB = nB*ux; - delta = beta*nA*nB*nAB*0.1111111111111111*nx; - if (!(nA*nB*nAB>0)) delta=0; + //............................................... + // q = 0,2,4 + // Cq = {1,0,0}, {0,1,0}, {0,0,1} + jA = nA * ux; + jB = nB * ux; + delta = beta * nA * nB * nAB * 0.1111111111111111 * nx; + if (!(nA * nB * nAB > 0)) + delta = 0; //----------------newly added for better control of recoloring---------------// - if (nA/(nA+nB)>=Sn_grey && nA/(nA+nB) <= Sw_grey && porosity !=1.0){ - //delta = 0.0; - delta = -0.111111111111111*C*W*GreyDiff*nA*nB*nAB*nx; - jA = 0.5*ux*(nA+nB)*(1.0+mobility_ratio); - jB = 0.5*ux*(nA+nB)*(1.0-mobility_ratio); + if (nA / (nA + nB) >= Sn_grey && nA / (nA + nB) <= Sw_grey && + porosity != 1.0) { + //delta = 0.0; + delta = -0.111111111111111 * C * W * GreyDiff * nA * nB * nAB * nx; + jA = 0.5 * ux * (nA + nB) * (1.0 + mobility_ratio); + jB = 0.5 * ux * (nA + nB) * (1.0 - mobility_ratio); } - if (nA/(nA+nB)>Sw_grey && porosity !=1.0) delta = -1.0*delta; + if (nA / (nA + nB) > Sw_grey && porosity != 1.0) + delta = -1.0 * delta; //---------------------------------------------------------------------------// - if (RecoloringOff==true && porosity !=1.0) delta=0; - a1 = (0.1111111111111111*(nA+4.5*jA))+delta; - b1 = (0.1111111111111111*(nB+4.5*jB))-delta; - a2 = (0.1111111111111111*(nA-4.5*jA))-delta; - b2 = (0.1111111111111111*(nB-4.5*jB))+delta; + if (RecoloringOff == true && porosity != 1.0) + delta = 0; + a1 = (0.1111111111111111 * (nA + 4.5 * jA)) + delta; + b1 = (0.1111111111111111 * (nB + 4.5 * jB)) - delta; + a2 = (0.1111111111111111 * (nA - 4.5 * jA)) - delta; + b2 = (0.1111111111111111 * (nB - 4.5 * jB)) + delta; - Aq[1*Np+n] = a1; - Bq[1*Np+n] = b1; - Aq[2*Np+n] = a2; - Bq[2*Np+n] = b2; + Aq[1 * Np + n] = a1; + Bq[1 * Np + n] = b1; + Aq[2 * Np + n] = a2; + Bq[2 * Np + n] = b2; - //............................................... - // Cq = {0,1,0} - jA = nA*uy; - jB = nB*uy; - delta = beta*nA*nB*nAB*0.1111111111111111*ny; - if (!(nA*nB*nAB>0)) delta=0; + //............................................... + // Cq = {0,1,0} + jA = nA * uy; + jB = nB * uy; + delta = beta * nA * nB * nAB * 0.1111111111111111 * ny; + if (!(nA * nB * nAB > 0)) + delta = 0; //----------------newly added for better control of recoloring---------------// - if (nA/(nA+nB)>=Sn_grey && nA/(nA+nB) <= Sw_grey && porosity !=1.0){ - //delta = 0.0; - delta = -0.111111111111111*C*W*GreyDiff*nA*nB*nAB*ny; - jA = 0.5*uy*(nA+nB)*(1.0+mobility_ratio); - jB = 0.5*uy*(nA+nB)*(1.0-mobility_ratio); + if (nA / (nA + nB) >= Sn_grey && nA / (nA + nB) <= Sw_grey && + porosity != 1.0) { + //delta = 0.0; + delta = -0.111111111111111 * C * W * GreyDiff * nA * nB * nAB * ny; + jA = 0.5 * uy * (nA + nB) * (1.0 + mobility_ratio); + jB = 0.5 * uy * (nA + nB) * (1.0 - mobility_ratio); } - if (nA/(nA+nB)>Sw_grey && porosity !=1.0) delta = -1.0*delta; + if (nA / (nA + nB) > Sw_grey && porosity != 1.0) + delta = -1.0 * delta; //---------------------------------------------------------------------------// - if (RecoloringOff==true && porosity !=1.0) delta=0; - a1 = (0.1111111111111111*(nA+4.5*jA))+delta; - b1 = (0.1111111111111111*(nB+4.5*jB))-delta; - a2 = (0.1111111111111111*(nA-4.5*jA))-delta; - b2 = (0.1111111111111111*(nB-4.5*jB))+delta; + if (RecoloringOff == true && porosity != 1.0) + delta = 0; + a1 = (0.1111111111111111 * (nA + 4.5 * jA)) + delta; + b1 = (0.1111111111111111 * (nB + 4.5 * jB)) - delta; + a2 = (0.1111111111111111 * (nA - 4.5 * jA)) - delta; + b2 = (0.1111111111111111 * (nB - 4.5 * jB)) + delta; - Aq[3*Np+n] = a1; - Bq[3*Np+n] = b1; - Aq[4*Np+n] = a2; - Bq[4*Np+n] = b2; + Aq[3 * Np + n] = a1; + Bq[3 * Np + n] = b1; + Aq[4 * Np + n] = a2; + Bq[4 * Np + n] = b2; - //............................................... - // q = 4 - // Cq = {0,0,1} - jA = nA*uz; - jB = nB*uz; - delta = beta*nA*nB*nAB*0.1111111111111111*nz; - if (!(nA*nB*nAB>0)) delta=0; + //............................................... + // q = 4 + // Cq = {0,0,1} + jA = nA * uz; + jB = nB * uz; + delta = beta * nA * nB * nAB * 0.1111111111111111 * nz; + if (!(nA * nB * nAB > 0)) + delta = 0; //----------------newly added for better control of recoloring---------------// - if (nA/(nA+nB)>=Sn_grey && nA/(nA+nB) <= Sw_grey && porosity !=1.0){ - //delta = 0.0; - delta = -0.111111111111111*C*W*GreyDiff*nA*nB*nAB*nz; - jA = 0.5*uz*(nA+nB)*(1.0+mobility_ratio); - jB = 0.5*uz*(nA+nB)*(1.0-mobility_ratio); + if (nA / (nA + nB) >= Sn_grey && nA / (nA + nB) <= Sw_grey && + porosity != 1.0) { + //delta = 0.0; + delta = -0.111111111111111 * C * W * GreyDiff * nA * nB * nAB * nz; + jA = 0.5 * uz * (nA + nB) * (1.0 + mobility_ratio); + jB = 0.5 * uz * (nA + nB) * (1.0 - mobility_ratio); } - if (nA/(nA+nB)>Sw_grey && porosity !=1.0) delta = -1.0*delta; + if (nA / (nA + nB) > Sw_grey && porosity != 1.0) + delta = -1.0 * delta; //---------------------------------------------------------------------------// - if (RecoloringOff==true && porosity !=1.0) delta=0; + if (RecoloringOff == true && porosity != 1.0) + delta = 0; - a1 = (0.1111111111111111*(nA+4.5*jA))+delta; - b1 = (0.1111111111111111*(nB+4.5*jB))-delta; - a2 = (0.1111111111111111*(nA-4.5*jA))-delta; - b2 = (0.1111111111111111*(nB-4.5*jB))+delta; + a1 = (0.1111111111111111 * (nA + 4.5 * jA)) + delta; + b1 = (0.1111111111111111 * (nB + 4.5 * jB)) - delta; + a2 = (0.1111111111111111 * (nA - 4.5 * jA)) - delta; + b2 = (0.1111111111111111 * (nB - 4.5 * jB)) + delta; - Aq[5*Np+n] = a1; - Bq[5*Np+n] = b1; - Aq[6*Np+n] = a2; - Bq[6*Np+n] = b2; - //............................................... - - } + Aq[5 * Np + n] = a1; + Bq[5 * Np + n] = b1; + Aq[6 * Np + n] = a2; + Bq[6 * Np + n] = b2; + //............................................... + } } +extern "C" void ScaLBL_PhaseField_InitFromRestart(double *Den, double *Aq, + double *Bq, int start, + int finish, int Np) { + int idx; + double nA, nB; -extern "C" void ScaLBL_PhaseField_InitFromRestart(double *Den, double *Aq, double *Bq, int start, int finish, int Np){ - int idx; - double nA,nB; + for (idx = start; idx < finish; idx++) { - for (idx=start; idx -extern "C" void ScaLBL_D3Q7_AAodd_IonConcentration(int *neighborList, double *dist, double *Den, int start, int finish, int Np){ - int n,nread; - double fq,Ci; - for (n=start; n 10Np => odd part of dist) - f1 = dist[nr1]; // reading the f1 data into register fq - // q=2 - nr2 = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) - f2 = dist[nr2]; // reading the f2 data into register fq - // q=3 - nr3 = neighborList[n+2*Np]; // neighbor 4 - f3 = dist[nr3]; - // q=4 - nr4 = neighborList[n+3*Np]; // neighbor 3 - f4 = dist[nr4]; - // q=5 - nr5 = neighborList[n+4*Np]; - f5 = dist[nr5]; - // q=6 - nr6 = neighborList[n+5*Np]; - f6 = dist[nr6]; + // q=0 + f0 = dist[n]; + // q=1 + nr1 = neighborList[n]; // neighbor 2 ( > 10Np => odd part of dist) + f1 = dist[nr1]; // reading the f1 data into register fq + // q=2 + nr2 = neighborList[n + Np]; // neighbor 1 ( < 10Np => even part of dist) + f2 = dist[nr2]; // reading the f2 data into register fq + // q=3 + nr3 = neighborList[n + 2 * Np]; // neighbor 4 + f3 = dist[nr3]; + // q=4 + nr4 = neighborList[n + 3 * Np]; // neighbor 3 + f4 = dist[nr4]; + // q=5 + nr5 = neighborList[n + 4 * Np]; + f5 = dist[nr5]; + // q=6 + nr6 = neighborList[n + 5 * Np]; + f6 = dist[nr6]; // compute diffusive flux - flux_diffusive_x = (1.0-0.5*rlx)*((f1-f2)-ux*Ci); - flux_diffusive_y = (1.0-0.5*rlx)*((f3-f4)-uy*Ci); - flux_diffusive_z = (1.0-0.5*rlx)*((f5-f6)-uz*Ci); - FluxDiffusive[n+0*Np] = flux_diffusive_x; - FluxDiffusive[n+1*Np] = flux_diffusive_y; - FluxDiffusive[n+2*Np] = flux_diffusive_z; - FluxAdvective[n+0*Np] = ux*Ci; - FluxAdvective[n+1*Np] = uy*Ci; - FluxAdvective[n+2*Np] = uz*Ci; - FluxElectrical[n+0*Np] = uEPx*Ci; - FluxElectrical[n+1*Np] = uEPy*Ci; - FluxElectrical[n+2*Np] = uEPz*Ci; - - // q=0 - dist[n] = f0*(1.0-rlx)+rlx*0.25*Ci; + flux_diffusive_x = (1.0 - 0.5 * rlx) * ((f1 - f2) - ux * Ci); + flux_diffusive_y = (1.0 - 0.5 * rlx) * ((f3 - f4) - uy * Ci); + flux_diffusive_z = (1.0 - 0.5 * rlx) * ((f5 - f6) - uz * Ci); + FluxDiffusive[n + 0 * Np] = flux_diffusive_x; + FluxDiffusive[n + 1 * Np] = flux_diffusive_y; + FluxDiffusive[n + 2 * Np] = flux_diffusive_z; + FluxAdvective[n + 0 * Np] = ux * Ci; + FluxAdvective[n + 1 * Np] = uy * Ci; + FluxAdvective[n + 2 * Np] = uz * Ci; + FluxElectrical[n + 0 * Np] = uEPx * Ci; + FluxElectrical[n + 1 * Np] = uEPy * Ci; + FluxElectrical[n + 2 * Np] = uEPz * Ci; - // q = 1 - dist[nr2] = f1*(1.0-rlx) + rlx*0.125*Ci*(1.0+4.0*(ux+uEPx)); + // q=0 + dist[n] = f0 * (1.0 - rlx) + rlx * 0.25 * Ci; - // q=2 - dist[nr1] = f2*(1.0-rlx) + rlx*0.125*Ci*(1.0-4.0*(ux+uEPx)); + // q = 1 + dist[nr2] = + f1 * (1.0 - rlx) + rlx * 0.125 * Ci * (1.0 + 4.0 * (ux + uEPx)); - // q = 3 - dist[nr4] = f3*(1.0-rlx) + rlx*0.125*Ci*(1.0+4.0*(uy+uEPy)); + // q=2 + dist[nr1] = + f2 * (1.0 - rlx) + rlx * 0.125 * Ci * (1.0 - 4.0 * (ux + uEPx)); - // q = 4 - dist[nr3] = f4*(1.0-rlx) + rlx*0.125*Ci*(1.0-4.0*(uy+uEPy)); + // q = 3 + dist[nr4] = + f3 * (1.0 - rlx) + rlx * 0.125 * Ci * (1.0 + 4.0 * (uy + uEPy)); - // q = 5 - dist[nr6] = f5*(1.0-rlx) + rlx*0.125*Ci*(1.0+4.0*(uz+uEPz)); + // q = 4 + dist[nr3] = + f4 * (1.0 - rlx) + rlx * 0.125 * Ci * (1.0 - 4.0 * (uy + uEPy)); - // q = 6 - dist[nr5] = f6*(1.0-rlx) + rlx*0.125*Ci*(1.0-4.0*(uz+uEPz)); - - } + // q = 5 + dist[nr6] = + f5 * (1.0 - rlx) + rlx * 0.125 * Ci * (1.0 + 4.0 * (uz + uEPz)); + + // q = 6 + dist[nr5] = + f6 * (1.0 - rlx) + rlx * 0.125 * Ci * (1.0 - 4.0 * (uz + uEPz)); + } } -extern "C" void ScaLBL_D3Q7_AAeven_Ion(double *dist, double *Den, double *FluxDiffusive, double *FluxAdvective, double *FluxElectrical, double *Velocity, double *ElectricField, - double Di, int zi, double rlx, double Vt, int start, int finish, int Np){ - int n; - double Ci; - double ux,uy,uz; - double uEPx,uEPy,uEPz;//electrochemical induced velocity - double Ex,Ey,Ez;//electrical field - double flux_diffusive_x,flux_diffusive_y,flux_diffusive_z; - double f0,f1,f2,f3,f4,f5,f6; +extern "C" void ScaLBL_D3Q7_AAeven_Ion( + double *dist, double *Den, double *FluxDiffusive, double *FluxAdvective, + double *FluxElectrical, double *Velocity, double *ElectricField, double Di, + int zi, double rlx, double Vt, int start, int finish, int Np) { + int n; + double Ci; + double ux, uy, uz; + double uEPx, uEPy, uEPz; //electrochemical induced velocity + double Ex, Ey, Ez; //electrical field + double flux_diffusive_x, flux_diffusive_y, flux_diffusive_z; + double f0, f1, f2, f3, f4, f5, f6; + + for (n = start; n < finish; n++) { - for (n=start; n0) + CD_tmp; } } +extern "C" void ScaLBL_D3Q7_Ion_Init_FromFile(double *dist, double *Den, + int Np) { + int n; + double DenInit; + for (n = 0; n < Np; n++) { + DenInit = Den[n]; + dist[0 * Np + n] = 0.25 * DenInit; + dist[1 * Np + n] = 0.125 * DenInit; + dist[2 * Np + n] = 0.125 * DenInit; + dist[3 * Np + n] = 0.125 * DenInit; + dist[4 * Np + n] = 0.125 * DenInit; + dist[5 * Np + n] = 0.125 * DenInit; + dist[6 * Np + n] = 0.125 * DenInit; + } +} + +extern "C" void ScaLBL_D3Q7_Ion_ChargeDensity(double *Den, + double *ChargeDensity, + int IonValence, int ion_component, + int start, int finish, int Np) { + + int n; + double Ci; //ion concentration of species i + double CD; //charge density + double CD_tmp; + double F = + 96485.0; //Faraday's constant; unit[C/mol]; F=e*Na, where Na is the Avogadro constant + + for (n = start; n < finish; n++) { + Ci = Den[n + ion_component * Np]; + CD = ChargeDensity[n]; + CD_tmp = F * IonValence * Ci; + ChargeDensity[n] = CD * (ion_component > 0) + CD_tmp; + } +} diff --git a/cpu/MRT.cpp b/cpu/MRT.cpp index f513c6c2..70a3ee37 100644 --- a/cpu/MRT.cpp +++ b/cpu/MRT.cpp @@ -14,279 +14,316 @@ along with OPM. If not, see . */ -extern "C" void INITIALIZE(char *ID, double *f_even, double *f_odd, int Nx, int Ny, int Nz) -{ - int n,N; - N = Nx*Ny*Nz; +extern "C" void INITIALIZE(char *ID, double *f_even, double *f_odd, int Nx, + int Ny, int Nz) { + int n, N; + N = Nx * Ny * Nz; - for (n=0; n 0){ - f_even[n] = 0.3333333333333333; - f_odd[n] = 0.055555555555555555; //double(100*n)+1.f; - f_even[N+n] = 0.055555555555555555; //double(100*n)+2.f; - f_odd[N+n] = 0.055555555555555555; //double(100*n)+3.f; - f_even[2*N+n] = 0.055555555555555555; //double(100*n)+4.f; - f_odd[2*N+n] = 0.055555555555555555; //double(100*n)+5.f; - f_even[3*N+n] = 0.055555555555555555; //double(100*n)+6.f; - f_odd[3*N+n] = 0.0277777777777778; //double(100*n)+7.f; - f_even[4*N+n] = 0.0277777777777778; //double(100*n)+8.f; - f_odd[4*N+n] = 0.0277777777777778; //double(100*n)+9.f; - f_even[5*N+n] = 0.0277777777777778; //double(100*n)+10.f; - f_odd[5*N+n] = 0.0277777777777778; //double(100*n)+11.f; - f_even[6*N+n] = 0.0277777777777778; //double(100*n)+12.f; - f_odd[6*N+n] = 0.0277777777777778; //double(100*n)+13.f; - f_even[7*N+n] = 0.0277777777777778; //double(100*n)+14.f; - f_odd[7*N+n] = 0.0277777777777778; //double(100*n)+15.f; - f_even[8*N+n] = 0.0277777777777778; //double(100*n)+16.f; - f_odd[8*N+n] = 0.0277777777777778; //double(100*n)+17.f; - f_even[9*N+n] = 0.0277777777777778; //double(100*n)+18.f; - } - else{ - for(int q=0; q<9; q++){ - f_even[q*N+n] = -1.0; - f_odd[q*N+n] = -1.0; - } - f_even[9*N+n] = -1.0; - } - } - + if (ID[n] > 0) { + f_even[n] = 0.3333333333333333; + f_odd[n] = 0.055555555555555555; //double(100*n)+1.f; + f_even[N + n] = 0.055555555555555555; //double(100*n)+2.f; + f_odd[N + n] = 0.055555555555555555; //double(100*n)+3.f; + f_even[2 * N + n] = 0.055555555555555555; //double(100*n)+4.f; + f_odd[2 * N + n] = 0.055555555555555555; //double(100*n)+5.f; + f_even[3 * N + n] = 0.055555555555555555; //double(100*n)+6.f; + f_odd[3 * N + n] = 0.0277777777777778; //double(100*n)+7.f; + f_even[4 * N + n] = 0.0277777777777778; //double(100*n)+8.f; + f_odd[4 * N + n] = 0.0277777777777778; //double(100*n)+9.f; + f_even[5 * N + n] = 0.0277777777777778; //double(100*n)+10.f; + f_odd[5 * N + n] = 0.0277777777777778; //double(100*n)+11.f; + f_even[6 * N + n] = 0.0277777777777778; //double(100*n)+12.f; + f_odd[6 * N + n] = 0.0277777777777778; //double(100*n)+13.f; + f_even[7 * N + n] = 0.0277777777777778; //double(100*n)+14.f; + f_odd[7 * N + n] = 0.0277777777777778; //double(100*n)+15.f; + f_even[8 * N + n] = 0.0277777777777778; //double(100*n)+16.f; + f_odd[8 * N + n] = 0.0277777777777778; //double(100*n)+17.f; + f_even[9 * N + n] = 0.0277777777777778; //double(100*n)+18.f; + } else { + for (int q = 0; q < 9; q++) { + f_even[q * N + n] = -1.0; + f_odd[q * N + n] = -1.0; + } + f_even[9 * N + n] = -1.0; + } + } } -extern "C" void Compute_VELOCITY(char *ID, double *disteven, double *distodd, double *vel, int Nx, int Ny, int Nz) -{ - int n,N; - // distributions - double f1,f2,f3,f4,f5,f6,f7,f8,f9; - double f10,f11,f12,f13,f14,f15,f16,f17,f18; - double vx,vy,vz; +extern "C" void Compute_VELOCITY(char *ID, double *disteven, double *distodd, + double *vel, int Nx, int Ny, int Nz) { + int n, N; + // distributions + double f1, f2, f3, f4, f5, f6, f7, f8, f9; + double f10, f11, f12, f13, f14, f15, f16, f17, f18; + double vx, vy, vz; - N = Nx*Ny*Nz; - for (n=0; n 0){ - //........................................................................ - // Registers to store the distributions - //........................................................................ - f2 = disteven[N+n]; - f4 = disteven[2*N+n]; - f6 = disteven[3*N+n]; - f8 = disteven[4*N+n]; - f10 = disteven[5*N+n]; - f12 = disteven[6*N+n]; - f14 = disteven[7*N+n]; - f16 = disteven[8*N+n]; - f18 = disteven[9*N+n]; - //........................................................................ - f1 = distodd[n]; - f3 = distodd[1*N+n]; - f5 = distodd[2*N+n]; - f7 = distodd[3*N+n]; - f9 = distodd[4*N+n]; - f11 = distodd[5*N+n]; - f13 = distodd[6*N+n]; - f15 = distodd[7*N+n]; - f17 = distodd[8*N+n]; - //.................Compute the velocity................................... - vx = f1-f2+f7-f8+f9-f10+f11-f12+f13-f14; - vy = f3-f4+f7-f8-f9+f10+f15-f16+f17-f18; - vz = f5-f6+f11-f12-f13+f14+f15-f16-f17+f18; - //..................Write the velocity..................................... - vel[n] = vx; - vel[N+n] = vy; - vel[2*N+n] = vz; - //........................................................................ - - } - } - } + N = Nx * Ny * Nz; + for (n = 0; n < N; n++) { + if (ID[n] > 0) { + //........................................................................ + // Registers to store the distributions + //........................................................................ + f2 = disteven[N + n]; + f4 = disteven[2 * N + n]; + f6 = disteven[3 * N + n]; + f8 = disteven[4 * N + n]; + f10 = disteven[5 * N + n]; + f12 = disteven[6 * N + n]; + f14 = disteven[7 * N + n]; + f16 = disteven[8 * N + n]; + f18 = disteven[9 * N + n]; + //........................................................................ + f1 = distodd[n]; + f3 = distodd[1 * N + n]; + f5 = distodd[2 * N + n]; + f7 = distodd[3 * N + n]; + f9 = distodd[4 * N + n]; + f11 = distodd[5 * N + n]; + f13 = distodd[6 * N + n]; + f15 = distodd[7 * N + n]; + f17 = distodd[8 * N + n]; + //.................Compute the velocity................................... + vx = f1 - f2 + f7 - f8 + f9 - f10 + f11 - f12 + f13 - f14; + vy = f3 - f4 + f7 - f8 - f9 + f10 + f15 - f16 + f17 - f18; + vz = f5 - f6 + f11 - f12 - f13 + f14 + f15 - f16 - f17 + f18; + //..................Write the velocity..................................... + vel[n] = vx; + vel[N + n] = vy; + vel[2 * N + n] = vz; + //........................................................................ + } + } +} //************************************************************************* -extern "C" void ScaLBL_D3Q19_MRT(char *ID, double *disteven, double *distodd, int Nx, int Ny, int Nz, - double rlx_setA, double rlx_setB, double Fx, double Fy, double Fz) -{ +extern "C" void ScaLBL_D3Q19_MRT(char *ID, double *disteven, double *distodd, + int Nx, int Ny, int Nz, double rlx_setA, + double rlx_setB, double Fx, double Fy, + double Fz) { - int n,N; - // distributions - double f0,f1,f2,f3,f4,f5,f6,f7,f8,f9; - double f10,f11,f12,f13,f14,f15,f16,f17,f18; + int n, N; + // distributions + double f0, f1, f2, f3, f4, f5, f6, f7, f8, f9; + double f10, f11, f12, f13, f14, f15, f16, f17, f18; - // conserved momemnts - double rho,jx,jy,jz; - // non-conserved moments - double m1,m2,m4,m6,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18; + // conserved momemnts + double rho, jx, jy, jz; + // non-conserved moments + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; - N = Nx*Ny*Nz; + N = Nx * Ny * Nz; - char id; - for (n=0; n 0){ - //........................................................................ - // Registers to store the distributions - read based on swap convention - //........................................................................ - f2 = distodd[n]; - f4 = distodd[N+n]; - f6 = distodd[2*N+n]; - f8 = distodd[3*N+n]; - f10 = distodd[4*N+n]; - f12 = distodd[5*N+n]; - f14 = distodd[6*N+n]; - f16 = distodd[7*N+n]; - f18 = distodd[8*N+n]; - //........................................................................ - f0 = disteven[n]; - f1 = disteven[N+n]; - f3 = disteven[2*N+n]; - f5 = disteven[3*N+n]; - f7 = disteven[4*N+n]; - f9 = disteven[5*N+n]; - f11 = disteven[6*N+n]; - f13 = disteven[7*N+n]; - f15 = disteven[8*N+n]; - f17 = disteven[9*N+n]; - //........................................................................ - //....................compute the moments............................................... - rho = f0+f2+f1+f4+f3+f6+f5+f8+f7+f10+f9+f12+f11+f14+f13+f16+f15+f18+f17; - m1 = -30*f0-11*(f2+f1+f4+f3+f6+f5)+8*(f8+f7+f10+f9+f12+f11+f14+f13+f16+f15+f18 +f17); - m2 = 12*f0-4*(f2+f1 +f4+f3+f6 +f5)+f8+f7+f10+f9+f12+f11+f14+f13+f16+f15+f18+f17; - jx = f1-f2+f7-f8+f9-f10+f11-f12+f13-f14; - m4 = 4*(-f1+f2)+f7-f8+f9-f10+f11-f12+f13-f14; - jy = f3-f4+f7-f8-f9+f10+f15-f16+f17-f18; - m6 = -4*(f3-f4)+f7-f8-f9+f10+f15-f16+f17-f18; - jz = f5-f6+f11-f12-f13+f14+f15-f16-f17+f18; - m8 = -4*(f5-f6)+f11-f12-f13+f14+f15-f16-f17+f18; - m9 = 2*(f1+f2)-f3-f4-f5-f6+f7+f8+f9+f10+f11+f12+f13+f14-2*(f15+f16+f17+f18); - m10 = -4*(f1+f2)+2*(f4+f3+f6+f5)+f8+f7+f10+f9+f12+f11+f14+f13-2*(f16+f15+f18+f17); - m11 = f4+f3-f6-f5+f8+f7+f10+f9-f12-f11-f14-f13; - m12 = -2*(f4+f3-f6-f5)+f8+f7+f10+f9-f12-f11-f14-f13; - m13 = f8+f7-f10-f9; - m14 = f16+f15-f18-f17; - m15 = f12+f11-f14-f13; - m16 = f7-f8+f9-f10-f11+f12-f13+f14; - m17 = -f7+f8+f9-f10+f15-f16+f17-f18; - m18 = f11-f12-f13+f14-f15+f16+f17-f18; - //..............incorporate external force................................................ - //jx += 0.5*Fx; - //jy += 0.5*Fy; - //jz += 0.5*Fz; - //..............carry out relaxation process............................................... - m1 = m1 + rlx_setA*((19*(jx*jx+jy*jy+jz*jz)/rho - 11*rho) - m1); - m2 = m2 + rlx_setA*((3*rho - 5.5*(jx*jx+jy*jy+jz*jz)/rho) - m2); - m4 = m4 + rlx_setB*((-0.6666666666666666*jx) - m4); - m6 = m6 + rlx_setB*((-0.6666666666666666*jy) - m6); - m8 = m8 + rlx_setB*((-0.6666666666666666*jz) - m8); - m9 = m9 + rlx_setA*(((2*jx*jx-jy*jy-jz*jz)/rho) - m9); - m10 = m10 + rlx_setA*(-0.5*((2*jx*jx-jy*jy-jz*jz)/rho) - m10); - m11 = m11 + rlx_setA*(((jy*jy-jz*jz)/rho) - m11); - m12 = m12 + rlx_setA*(-0.5*((jy*jy-jz*jz)/rho) - m12); - m13 = m13 + rlx_setA*((jx*jy/rho) - m13); - m14 = m14 + rlx_setA*((jy*jz/rho) - m14); - m15 = m15 + rlx_setA*((jx*jz/rho) - m15); - m16 = m16 + rlx_setB*( - m16); - m17 = m17 + rlx_setB*( - m17); - m18 = m18 + rlx_setB*( - m18); - //.................inverse transformation...................................................... - f0 = 0.05263157894736842*rho-0.012531328320802*m1+0.04761904761904762*m2; - f1 = 0.05263157894736842*rho-0.004594820384294068*m1-0.01587301587301587*m2 - +0.1*(jx-m4)+0.05555555555555555*(m9-m10); - f2 = 0.05263157894736842*rho-0.004594820384294068*m1-0.01587301587301587*m2 - +0.1*(m4-jx)+0.05555555555555555*(m9-m10); - f3 = 0.05263157894736842*rho-0.004594820384294068*m1-0.01587301587301587*m2 - +0.1*(jy-m6)+0.02777777777777778*(m10-m9)+0.08333333333333333*(m11-m12); - f4 = 0.05263157894736842*rho-0.004594820384294068*m1-0.01587301587301587*m2 - +0.1*(m6-jy)+0.02777777777777778*(m10-m9)+0.08333333333333333*(m11-m12); - f5 = 0.05263157894736842*rho-0.004594820384294068*m1-0.01587301587301587*m2 - +0.1*(jz-m8)+0.02777777777777778*(m10-m9)+0.08333333333333333*(m12-m11); - f6 = 0.05263157894736842*rho-0.004594820384294068*m1-0.01587301587301587*m2 - +0.1*(m8-jz)+0.02777777777777778*(m10-m9)+0.08333333333333333*(m12-m11); - f7 = 0.05263157894736842*rho+0.003341687552213868*m1+0.003968253968253968*m2+0.1*(jx+jy)+0.025*(m4+m6) - +0.02777777777777778*m9+0.01388888888888889*m10+0.08333333333333333*m11 - +0.04166666666666666*m12+0.25*m13+0.125*(m16-m17); - f8 = 0.05263157894736842*rho+0.003341687552213868*m1+0.003968253968253968*m2-0.1*(jx+jy)-0.025*(m4+m6) - +0.02777777777777778*m9+0.01388888888888889*m10+0.08333333333333333*m11 - +0.04166666666666666*m12+0.25*m13+0.125*(m17-m16); - f9 = 0.05263157894736842*rho+0.003341687552213868*m1+0.003968253968253968*m2+0.1*(jx-jy)+0.025*(m4-m6) - +0.02777777777777778*m9+0.01388888888888889*m10+0.08333333333333333*m11 - +0.04166666666666666*m12-0.25*m13+0.125*(m16+m17); - f10 = 0.05263157894736842*rho+0.003341687552213868*m1+0.003968253968253968*m2+0.1*(jy-jx)+0.025*(m6-m4) - +0.02777777777777778*m9+0.01388888888888889*m10+0.08333333333333333*m11 - +0.04166666666666666*m12-0.25*m13-0.125*(m16+m17); - f11 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2+0.1*(jx+jz)+0.025*(m4+m8) - +0.02777777777777778*m9+0.01388888888888889*m10-0.08333333333333333*m11 - -0.04166666666666666*m12+0.25*m15+0.125*(m18-m16); - f12 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2-0.1*(jx+jz)-0.025*(m4+m8) - +0.02777777777777778*m9+0.01388888888888889*m10-0.08333333333333333*m11 - -0.04166666666666666*m12+0.25*m15+0.125*(m16-m18); - f13 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2+0.1*(jx-jz)+0.025*(m4-m8) - +0.02777777777777778*m9+0.01388888888888889*m10-0.08333333333333333*m11 - -0.04166666666666666*m12-0.25*m15-0.125*(m16+m18); - f14 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2+0.1*(jz-jx)+0.025*(m8-m4) - +0.02777777777777778*m9+0.01388888888888889*m10-0.08333333333333333*m11 - -0.04166666666666666*m12-0.25*m15+0.125*(m16+m18); - f15 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2+0.1*(jy+jz)+0.025*(m6+m8) - -0.05555555555555555*m9-0.02777777777777778*m10+0.25*m14+0.125*(m17-m18); - f16 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2-0.1*(jy+jz)-0.025*(m6+m8) - -0.05555555555555555*m9-0.02777777777777778*m10+0.25*m14+0.125*(m18-m17); - f17 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2+0.1*(jy-jz)+0.025*(m6-m8) - -0.05555555555555555*m9-0.02777777777777778*m10-0.25*m14+0.125*(m17+m18); - f18 = 0.05263157894736842*rho+0.003341687552213868*m1 - +0.003968253968253968*m2+0.1*(jz-jy)+0.025*(m8-m6) - -0.05555555555555555*m9-0.02777777777777778*m10-0.25*m14-0.125*(m17+m18); - //....................................................................................................... - // incorporate external force - f1 += 0.16666666*Fx; - f2 -= 0.16666666*Fx; - f3 += 0.16666666*Fy; - f4 -= 0.16666666*Fy; - f5 += 0.16666666*Fz; - f6 -= 0.16666666*Fz; - f7 += 0.08333333333*(Fx+Fy); - f8 -= 0.08333333333*(Fx+Fy); - f9 += 0.08333333333*(Fx-Fy); - f10 -= 0.08333333333*(Fx-Fy); - f11 += 0.08333333333*(Fx+Fz); - f12 -= 0.08333333333*(Fx+Fz); - f13 += 0.08333333333*(Fx-Fz); - f14 -= 0.08333333333*(Fx-Fz); - f15 += 0.08333333333*(Fy+Fz); - f16 -= 0.08333333333*(Fy+Fz); - f17 += 0.08333333333*(Fy-Fz); - f18 -= 0.08333333333*(Fy-Fz); - //....................................................................................................... - // Write data based on un-swapped convention - disteven[n] = f0; - disteven[N+n] = f2; - disteven[2*N+n] = f4; - disteven[3*N+n] = f6; - disteven[4*N+n] = f8; - disteven[5*N+n] = f10; - disteven[6*N+n] = f12; - disteven[7*N+n] = f14; - disteven[8*N+n] = f16; - disteven[9*N+n] = f18; - - distodd[n] = f1; - distodd[N+n] = f3; - distodd[2*N+n] = f5; - distodd[3*N+n] = f7; - distodd[4*N+n] = f9; - distodd[5*N+n] = f11; - distodd[6*N+n] = f13; - distodd[7*N+n] = f15; - distodd[8*N+n] = f17; - //....................................................................................................... - } - } - } + char id; + for (n = 0; n < N; n++) { + id = ID[n]; + if (id > 0) { + //........................................................................ + // Registers to store the distributions - read based on swap convention + //........................................................................ + f2 = distodd[n]; + f4 = distodd[N + n]; + f6 = distodd[2 * N + n]; + f8 = distodd[3 * N + n]; + f10 = distodd[4 * N + n]; + f12 = distodd[5 * N + n]; + f14 = distodd[6 * N + n]; + f16 = distodd[7 * N + n]; + f18 = distodd[8 * N + n]; + //........................................................................ + f0 = disteven[n]; + f1 = disteven[N + n]; + f3 = disteven[2 * N + n]; + f5 = disteven[3 * N + n]; + f7 = disteven[4 * N + n]; + f9 = disteven[5 * N + n]; + f11 = disteven[6 * N + n]; + f13 = disteven[7 * N + n]; + f15 = disteven[8 * N + n]; + f17 = disteven[9 * N + n]; + //........................................................................ + //....................compute the moments............................................... + rho = f0 + f2 + f1 + f4 + f3 + f6 + f5 + f8 + f7 + f10 + f9 + f12 + + f11 + f14 + f13 + f16 + f15 + f18 + f17; + m1 = -30 * f0 - 11 * (f2 + f1 + f4 + f3 + f6 + f5) + + 8 * (f8 + f7 + f10 + f9 + f12 + f11 + f14 + f13 + f16 + f15 + + f18 + f17); + m2 = 12 * f0 - 4 * (f2 + f1 + f4 + f3 + f6 + f5) + f8 + f7 + f10 + + f9 + f12 + f11 + f14 + f13 + f16 + f15 + f18 + f17; + jx = f1 - f2 + f7 - f8 + f9 - f10 + f11 - f12 + f13 - f14; + m4 = 4 * (-f1 + f2) + f7 - f8 + f9 - f10 + f11 - f12 + f13 - f14; + jy = f3 - f4 + f7 - f8 - f9 + f10 + f15 - f16 + f17 - f18; + m6 = -4 * (f3 - f4) + f7 - f8 - f9 + f10 + f15 - f16 + f17 - f18; + jz = f5 - f6 + f11 - f12 - f13 + f14 + f15 - f16 - f17 + f18; + m8 = -4 * (f5 - f6) + f11 - f12 - f13 + f14 + f15 - f16 - f17 + f18; + m9 = 2 * (f1 + f2) - f3 - f4 - f5 - f6 + f7 + f8 + f9 + f10 + f11 + + f12 + f13 + f14 - 2 * (f15 + f16 + f17 + f18); + m10 = -4 * (f1 + f2) + 2 * (f4 + f3 + f6 + f5) + f8 + f7 + f10 + + f9 + f12 + f11 + f14 + f13 - 2 * (f16 + f15 + f18 + f17); + m11 = + f4 + f3 - f6 - f5 + f8 + f7 + f10 + f9 - f12 - f11 - f14 - f13; + m12 = -2 * (f4 + f3 - f6 - f5) + f8 + f7 + f10 + f9 - f12 - f11 - + f14 - f13; + m13 = f8 + f7 - f10 - f9; + m14 = f16 + f15 - f18 - f17; + m15 = f12 + f11 - f14 - f13; + m16 = f7 - f8 + f9 - f10 - f11 + f12 - f13 + f14; + m17 = -f7 + f8 + f9 - f10 + f15 - f16 + f17 - f18; + m18 = f11 - f12 - f13 + f14 - f15 + f16 + f17 - f18; + //..............incorporate external force................................................ + //jx += 0.5*Fx; + //jy += 0.5*Fy; + //jz += 0.5*Fz; + //..............carry out relaxation process............................................... + m1 = m1 + rlx_setA * ((19 * (jx * jx + jy * jy + jz * jz) / rho - + 11 * rho) - + m1); + m2 = m2 + rlx_setA * ((3 * rho - + 5.5 * (jx * jx + jy * jy + jz * jz) / rho) - + m2); + m4 = m4 + rlx_setB * ((-0.6666666666666666 * jx) - m4); + m6 = m6 + rlx_setB * ((-0.6666666666666666 * jy) - m6); + m8 = m8 + rlx_setB * ((-0.6666666666666666 * jz) - m8); + m9 = m9 + + rlx_setA * (((2 * jx * jx - jy * jy - jz * jz) / rho) - m9); + m10 = m10 + + rlx_setA * + (-0.5 * ((2 * jx * jx - jy * jy - jz * jz) / rho) - m10); + m11 = m11 + rlx_setA * (((jy * jy - jz * jz) / rho) - m11); + m12 = m12 + rlx_setA * (-0.5 * ((jy * jy - jz * jz) / rho) - m12); + m13 = m13 + rlx_setA * ((jx * jy / rho) - m13); + m14 = m14 + rlx_setA * ((jy * jz / rho) - m14); + m15 = m15 + rlx_setA * ((jx * jz / rho) - m15); + m16 = m16 + rlx_setB * (-m16); + m17 = m17 + rlx_setB * (-m17); + m18 = m18 + rlx_setB * (-m18); + //.................inverse transformation...................................................... + f0 = 0.05263157894736842 * rho - 0.012531328320802 * m1 + + 0.04761904761904762 * m2; + f1 = 0.05263157894736842 * rho - 0.004594820384294068 * m1 - + 0.01587301587301587 * m2 + 0.1 * (jx - m4) + + 0.05555555555555555 * (m9 - m10); + f2 = 0.05263157894736842 * rho - 0.004594820384294068 * m1 - + 0.01587301587301587 * m2 + 0.1 * (m4 - jx) + + 0.05555555555555555 * (m9 - m10); + f3 = 0.05263157894736842 * rho - 0.004594820384294068 * m1 - + 0.01587301587301587 * m2 + 0.1 * (jy - m6) + + 0.02777777777777778 * (m10 - m9) + + 0.08333333333333333 * (m11 - m12); + f4 = 0.05263157894736842 * rho - 0.004594820384294068 * m1 - + 0.01587301587301587 * m2 + 0.1 * (m6 - jy) + + 0.02777777777777778 * (m10 - m9) + + 0.08333333333333333 * (m11 - m12); + f5 = 0.05263157894736842 * rho - 0.004594820384294068 * m1 - + 0.01587301587301587 * m2 + 0.1 * (jz - m8) + + 0.02777777777777778 * (m10 - m9) + + 0.08333333333333333 * (m12 - m11); + f6 = 0.05263157894736842 * rho - 0.004594820384294068 * m1 - + 0.01587301587301587 * m2 + 0.1 * (m8 - jz) + + 0.02777777777777778 * (m10 - m9) + + 0.08333333333333333 * (m12 - m11); + f7 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jx + jy) + + 0.025 * (m4 + m6) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 + 0.08333333333333333 * m11 + + 0.04166666666666666 * m12 + 0.25 * m13 + 0.125 * (m16 - m17); + f8 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 - 0.1 * (jx + jy) - + 0.025 * (m4 + m6) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 + 0.08333333333333333 * m11 + + 0.04166666666666666 * m12 + 0.25 * m13 + 0.125 * (m17 - m16); + f9 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jx - jy) + + 0.025 * (m4 - m6) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 + 0.08333333333333333 * m11 + + 0.04166666666666666 * m12 - 0.25 * m13 + 0.125 * (m16 + m17); + f10 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jy - jx) + + 0.025 * (m6 - m4) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 + 0.08333333333333333 * m11 + + 0.04166666666666666 * m12 - 0.25 * m13 - 0.125 * (m16 + m17); + f11 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jx + jz) + + 0.025 * (m4 + m8) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 - 0.08333333333333333 * m11 - + 0.04166666666666666 * m12 + 0.25 * m15 + 0.125 * (m18 - m16); + f12 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 - 0.1 * (jx + jz) - + 0.025 * (m4 + m8) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 - 0.08333333333333333 * m11 - + 0.04166666666666666 * m12 + 0.25 * m15 + 0.125 * (m16 - m18); + f13 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jx - jz) + + 0.025 * (m4 - m8) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 - 0.08333333333333333 * m11 - + 0.04166666666666666 * m12 - 0.25 * m15 - 0.125 * (m16 + m18); + f14 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jz - jx) + + 0.025 * (m8 - m4) + 0.02777777777777778 * m9 + + 0.01388888888888889 * m10 - 0.08333333333333333 * m11 - + 0.04166666666666666 * m12 - 0.25 * m15 + 0.125 * (m16 + m18); + f15 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jy + jz) + + 0.025 * (m6 + m8) - 0.05555555555555555 * m9 - + 0.02777777777777778 * m10 + 0.25 * m14 + 0.125 * (m17 - m18); + f16 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 - 0.1 * (jy + jz) - + 0.025 * (m6 + m8) - 0.05555555555555555 * m9 - + 0.02777777777777778 * m10 + 0.25 * m14 + 0.125 * (m18 - m17); + f17 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jy - jz) + + 0.025 * (m6 - m8) - 0.05555555555555555 * m9 - + 0.02777777777777778 * m10 - 0.25 * m14 + 0.125 * (m17 + m18); + f18 = 0.05263157894736842 * rho + 0.003341687552213868 * m1 + + 0.003968253968253968 * m2 + 0.1 * (jz - jy) + + 0.025 * (m8 - m6) - 0.05555555555555555 * m9 - + 0.02777777777777778 * m10 - 0.25 * m14 - 0.125 * (m17 + m18); + //....................................................................................................... + // incorporate external force + f1 += 0.16666666 * Fx; + f2 -= 0.16666666 * Fx; + f3 += 0.16666666 * Fy; + f4 -= 0.16666666 * Fy; + f5 += 0.16666666 * Fz; + f6 -= 0.16666666 * Fz; + f7 += 0.08333333333 * (Fx + Fy); + f8 -= 0.08333333333 * (Fx + Fy); + f9 += 0.08333333333 * (Fx - Fy); + f10 -= 0.08333333333 * (Fx - Fy); + f11 += 0.08333333333 * (Fx + Fz); + f12 -= 0.08333333333 * (Fx + Fz); + f13 += 0.08333333333 * (Fx - Fz); + f14 -= 0.08333333333 * (Fx - Fz); + f15 += 0.08333333333 * (Fy + Fz); + f16 -= 0.08333333333 * (Fy + Fz); + f17 += 0.08333333333 * (Fy - Fz); + f18 -= 0.08333333333 * (Fy - Fz); + //....................................................................................................... + // Write data based on un-swapped convention + disteven[n] = f0; + disteven[N + n] = f2; + disteven[2 * N + n] = f4; + disteven[3 * N + n] = f6; + disteven[4 * N + n] = f8; + disteven[5 * N + n] = f10; + disteven[6 * N + n] = f12; + disteven[7 * N + n] = f14; + disteven[8 * N + n] = f16; + disteven[9 * N + n] = f18; + distodd[n] = f1; + distodd[N + n] = f3; + distodd[2 * N + n] = f5; + distodd[3 * N + n] = f7; + distodd[4 * N + n] = f9; + distodd[5 * N + n] = f11; + distodd[6 * N + n] = f13; + distodd[7 * N + n] = f15; + distodd[8 * N + n] = f17; + //....................................................................................................... + } + } +} diff --git a/cpu/MixedGradient.cpp b/cpu/MixedGradient.cpp index b5b18694..63e71b61 100644 --- a/cpu/MixedGradient.cpp +++ b/cpu/MixedGradient.cpp @@ -1,48 +1,51 @@ /* Implement Mixed Gradient (Lee et al. JCP 2016)*/ -extern "C" void ScaLBL_D3Q19_MixedGradient(int *Map, double *Phi, double *Gradient, int start, int finish, int Np, int Nx, int Ny, int Nz) -{ - static int D3Q19[18][3]={{1,0,0},{-1,0,0},{0,1,0},{0,-1,0},{0,0,1},{0,0,-1}, - {1,1,0},{-1,-1,0},{1,-1,0},{-1,1,0}, - {1,0,1},{-1,0,-1},{1,0,-1},{-1,0,1}, - {0,1,1},{0,-1,-1},{0,1,-1},{0,-1,1}}; - - int i,j,k,n; - int np,np2,nm; // neighbors - double v,vp,vp2,vm; // values at neighbors - double grad; - for (int idx=start; idx 10Np => odd part of dist) - f1 = dist[nr1]; // reading the f1 data into register fq - nr2 = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) - f2 = dist[nr2]; // reading the f2 data into register fq + // q=0 + f0 = dist[n]; + // q=1 + nr1 = neighborList[n]; // neighbor 2 ( > 10Np => odd part of dist) + f1 = dist[nr1]; // reading the f1 data into register fq - // q=3 - nr3 = neighborList[n+2*Np]; // neighbor 4 - f3 = dist[nr3]; + nr2 = neighborList[n + Np]; // neighbor 1 ( < 10Np => even part of dist) + f2 = dist[nr2]; // reading the f2 data into register fq - // q = 4 - nr4 = neighborList[n+3*Np]; // neighbor 3 - f4 = dist[nr4]; + // q=3 + nr3 = neighborList[n + 2 * Np]; // neighbor 4 + f3 = dist[nr3]; - // q=5 - nr5 = neighborList[n+4*Np]; - f5 = dist[nr5]; + // q = 4 + nr4 = neighborList[n + 3 * Np]; // neighbor 3 + f4 = dist[nr4]; - // q = 6 - nr6 = neighborList[n+5*Np]; - f6 = dist[nr6]; - - Ex = (f1-f2)*rlx*4.0;//NOTE the unit of electric field here is V/lu - Ey = (f3-f4)*rlx*4.0;//factor 4.0 is D3Q7 lattice squared speed of sound - Ez = (f5-f6)*rlx*4.0; - ElectricField[n+0*Np] = Ex; - ElectricField[n+1*Np] = Ey; - ElectricField[n+2*Np] = Ez; + // q=5 + nr5 = neighborList[n + 4 * Np]; + f5 = dist[nr5]; - // q = 0 - dist[n] = f0*(1.0-rlx) + 0.25*(rlx*psi+rho_e); + // q = 6 + nr6 = neighborList[n + 5 * Np]; + f6 = dist[nr6]; - // q = 1 - dist[nr2] = f1*(1.0-rlx) + 0.125*(rlx*psi+rho_e); + Ex = (f1 - f2) * rlx * + 4.0; //NOTE the unit of electric field here is V/lu + Ey = (f3 - f4) * rlx * + 4.0; //factor 4.0 is D3Q7 lattice squared speed of sound + Ez = (f5 - f6) * rlx * 4.0; + ElectricField[n + 0 * Np] = Ex; + ElectricField[n + 1 * Np] = Ey; + ElectricField[n + 2 * Np] = Ez; - // q = 2 - dist[nr1] = f2*(1.0-rlx) + 0.125*(rlx*psi+rho_e); + // q = 0 + dist[n] = f0 * (1.0 - rlx) + 0.25 * (rlx * psi + rho_e); - // q = 3 - dist[nr4] = f3*(1.0-rlx) + 0.125*(rlx*psi+rho_e); + // q = 1 + dist[nr2] = f1 * (1.0 - rlx) + 0.125 * (rlx * psi + rho_e); - // q = 4 - dist[nr3] = f4*(1.0-rlx) + 0.125*(rlx*psi+rho_e); + // q = 2 + dist[nr1] = f2 * (1.0 - rlx) + 0.125 * (rlx * psi + rho_e); - // q = 5 - dist[nr6] = f5*(1.0-rlx) + 0.125*(rlx*psi+rho_e); + // q = 3 + dist[nr4] = f3 * (1.0 - rlx) + 0.125 * (rlx * psi + rho_e); - // q = 6 - dist[nr5] = f6*(1.0-rlx) + 0.125*(rlx*psi+rho_e); - //........................................................................ - } + // q = 4 + dist[nr3] = f4 * (1.0 - rlx) + 0.125 * (rlx * psi + rho_e); + + // q = 5 + dist[nr6] = f5 * (1.0 - rlx) + 0.125 * (rlx * psi + rho_e); + + // q = 6 + dist[nr5] = f6 * (1.0 - rlx) + 0.125 * (rlx * psi + rho_e); + //........................................................................ + } } -extern "C" void ScaLBL_D3Q7_AAeven_Poisson(int *Map, double *dist, double *Den_charge, double *Psi, double *ElectricField, double tau, double epsilon_LB,int start, int finish, int Np){ - int n; - double psi;//electric potential - double Ex,Ey,Ez;//electric field - double rho_e;//local charge density - double f0,f1,f2,f3,f4,f5,f6; - double rlx=1.0/tau; +extern "C" void ScaLBL_D3Q7_AAeven_Poisson(int *Map, double *dist, + double *Den_charge, double *Psi, + double *ElectricField, double tau, + double epsilon_LB, int start, + int finish, int Np) { + int n; + double psi; //electric potential + double Ex, Ey, Ez; //electric field + double rho_e; //local charge density + double f0, f1, f2, f3, f4, f5, f6; + double rlx = 1.0 / tau; int idx; - for (n=start; n -extern "C" void ScaLBL_D3Q19_AAeven_StokesMRT(double *dist, double *Velocity, double *ChargeDensity, double *ElectricField, double rlx_setA, double rlx_setB, double Gx, double Gy, double Gz,double rho0, double den_scale, double h, double time_conv, int start, int finish, int Np) -{ +extern "C" void ScaLBL_D3Q19_AAeven_StokesMRT( + double *dist, double *Velocity, double *ChargeDensity, + double *ElectricField, double rlx_setA, double rlx_setB, double Gx, + double Gy, double Gz, double rho0, double den_scale, double h, + double time_conv, int start, int finish, int Np) { double fq; - // conserved momemnts - double rho,jx,jy,jz; - double ux,uy,uz; - // non-conserved moments - double m1,m2,m4,m6,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18; + // conserved momemnts + double rho, jx, jy, jz; + double ux, uy, uz; + // non-conserved moments + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; // body force due to electric field - double rhoE;//charge density - double Ex,Ey,Ez; + double rhoE; //charge density + double Ex, Ey, Ez; // total body force - double Fx,Fy,Fz; + double Fx, Fy, Fz; - constexpr double mrt_V1=0.05263157894736842; - constexpr double mrt_V2=0.012531328320802; - constexpr double mrt_V3=0.04761904761904762; - constexpr double mrt_V4=0.004594820384294068; - constexpr double mrt_V5=0.01587301587301587; - constexpr double mrt_V6=0.0555555555555555555555555; - constexpr double mrt_V7=0.02777777777777778; - constexpr double mrt_V8=0.08333333333333333; - constexpr double mrt_V9=0.003341687552213868; - constexpr double mrt_V10=0.003968253968253968; - constexpr double mrt_V11=0.01388888888888889; - constexpr double mrt_V12=0.04166666666666666; + constexpr double mrt_V1 = 0.05263157894736842; + constexpr double mrt_V2 = 0.012531328320802; + constexpr double mrt_V3 = 0.04761904761904762; + constexpr double mrt_V4 = 0.004594820384294068; + constexpr double mrt_V5 = 0.01587301587301587; + constexpr double mrt_V6 = 0.0555555555555555555555555; + constexpr double mrt_V7 = 0.02777777777777778; + constexpr double mrt_V8 = 0.08333333333333333; + constexpr double mrt_V9 = 0.003341687552213868; + constexpr double mrt_V10 = 0.003968253968253968; + constexpr double mrt_V11 = 0.01388888888888889; + constexpr double mrt_V12 = 0.04166666666666666; + + for (int n = start; n < finish; n++) { - for (int n=start; n 10Np => odd part of dist) - fq = dist[nread]; // reading the f1 data into register fq - //fp = dist[10*Np+n]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jx = fq; - m4 = -4.0*fq; - m9 = 2.0*fq; - m10 = -4.0*fq; + // q=1 + nread = neighborList[n]; // neighbor 2 ( > 10Np => odd part of dist) + fq = dist[nread]; // reading the f1 data into register fq + //fp = dist[10*Np+n]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jx = fq; + m4 = -4.0 * fq; + m9 = 2.0 * fq; + m10 = -4.0 * fq; - // f2 = dist[10*Np+n]; - nread = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) - fq = dist[nread]; // reading the f2 data into register fq - //fq = dist[Np+n]; - rho += fq; - m1 -= 11.0*(fq); - m2 -= 4.0*(fq); - jx -= fq; - m4 += 4.0*(fq); - m9 += 2.0*(fq); - m10 -= 4.0*(fq); + // f2 = dist[10*Np+n]; + nread = + neighborList[n + Np]; // neighbor 1 ( < 10Np => even part of dist) + fq = dist[nread]; // reading the f2 data into register fq + //fq = dist[Np+n]; + rho += fq; + m1 -= 11.0 * (fq); + m2 -= 4.0 * (fq); + jx -= fq; + m4 += 4.0 * (fq); + m9 += 2.0 * (fq); + m10 -= 4.0 * (fq); - // q=3 - nread = neighborList[n+2*Np]; // neighbor 4 - fq = dist[nread]; - //fq = dist[11*Np+n]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jy = fq; - m6 = -4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 = fq; - m12 = -2.0*fq; + // q=3 + nread = neighborList[n + 2 * Np]; // neighbor 4 + fq = dist[nread]; + //fq = dist[11*Np+n]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy = fq; + m6 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 = fq; + m12 = -2.0 * fq; - // q = 4 - nread = neighborList[n+3*Np]; // neighbor 3 - fq = dist[nread]; - //fq = dist[2*Np+n]; - rho+= fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jy -= fq; - m6 += 4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 += fq; - m12 -= 2.0*fq; + // q = 4 + nread = neighborList[n + 3 * Np]; // neighbor 3 + fq = dist[nread]; + //fq = dist[2*Np+n]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy -= fq; + m6 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 += fq; + m12 -= 2.0 * fq; - // q=5 - nread = neighborList[n+4*Np]; - fq = dist[nread]; - //fq = dist[12*Np+n]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jz = fq; - m8 = -4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 -= fq; - m12 += 2.0*fq; + // q=5 + nread = neighborList[n + 4 * Np]; + fq = dist[nread]; + //fq = dist[12*Np+n]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz = fq; + m8 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; + // q = 6 + nread = neighborList[n + 5 * Np]; + fq = dist[nread]; + //fq = dist[3*Np+n]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz -= fq; + m8 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; - // q = 6 - nread = neighborList[n+5*Np]; - fq = dist[nread]; - //fq = dist[3*Np+n]; - rho+= fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jz -= fq; - m8 += 4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 -= fq; - m12 += 2.0*fq; + // q=7 + nread = neighborList[n + 6 * Np]; + fq = dist[nread]; + //fq = dist[13*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 = fq; + m16 = fq; + m17 = -fq; - // q=7 - nread = neighborList[n+6*Np]; - fq = dist[nread]; - //fq = dist[13*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jy += fq; - m6 += fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 = fq; - m16 = fq; - m17 = -fq; + // q = 8 + nread = neighborList[n + 7 * Np]; + fq = dist[nread]; + //fq = dist[4*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 += fq; + m16 -= fq; + m17 += fq; - // q = 8 - nread = neighborList[n+7*Np]; - fq = dist[nread]; - //fq = dist[4*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jy -= fq; - m6 -= fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 += fq; - m16 -= fq; - m17 += fq; + // q=9 + nread = neighborList[n + 8 * Np]; + fq = dist[nread]; + //fq = dist[14*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 += fq; + m17 += fq; - // q=9 - nread = neighborList[n+8*Np]; - fq = dist[nread]; - //fq = dist[14*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jy -= fq; - m6 -= fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 -= fq; - m16 += fq; - m17 += fq; + // q = 10 + nread = neighborList[n + 9 * Np]; + fq = dist[nread]; + //fq = dist[5*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 -= fq; + m17 -= fq; - // q = 10 - nread = neighborList[n+9*Np]; - fq = dist[nread]; - //fq = dist[5*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jy += fq; - m6 += fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 -= fq; - m16 -= fq; - m17 -= fq; + // q=11 + nread = neighborList[n + 10 * Np]; + fq = dist[nread]; + //fq = dist[15*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 = fq; + m16 -= fq; + m18 = fq; - // q=11 - nread = neighborList[n+10*Np]; - fq = dist[nread]; - //fq = dist[15*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jz += fq; - m8 += fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 = fq; - m16 -= fq; - m18 = fq; + // q=12 + nread = neighborList[n + 11 * Np]; + fq = dist[nread]; + //fq = dist[6*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 += fq; + m16 += fq; + m18 -= fq; - // q=12 - nread = neighborList[n+11*Np]; - fq = dist[nread]; - //fq = dist[6*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jz -= fq; - m8 -= fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 += fq; - m16 += fq; - m18 -= fq; + // q=13 + nread = neighborList[n + 12 * Np]; + fq = dist[nread]; + //fq = dist[16*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 -= fq; + m18 -= fq; - // q=13 - nread = neighborList[n+12*Np]; - fq = dist[nread]; - //fq = dist[16*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jz -= fq; - m8 -= fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 -= fq; - m16 -= fq; - m18 -= fq; + // q=14 + nread = neighborList[n + 13 * Np]; + fq = dist[nread]; + //fq = dist[7*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 += fq; + m18 += fq; - // q=14 - nread = neighborList[n+13*Np]; - fq = dist[nread]; - //fq = dist[7*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jz += fq; - m8 += fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 -= fq; - m16 += fq; - m18 += fq; + // q=15 + nread = neighborList[n + 14 * Np]; + fq = dist[nread]; + //fq = dist[17*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 = fq; + m17 += fq; + m18 -= fq; - // q=15 - nread = neighborList[n+14*Np]; - fq = dist[nread]; - //fq = dist[17*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy += fq; - m6 += fq; - jz += fq; - m8 += fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 = fq; - m17 += fq; - m18 -= fq; + // q=16 + nread = neighborList[n + 15 * Np]; + fq = dist[nread]; + //fq = dist[8*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 += fq; + m17 -= fq; + m18 += fq; - // q=16 - nread = neighborList[n+15*Np]; - fq = dist[nread]; - //fq = dist[8*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy -= fq; - m6 -= fq; - jz -= fq; - m8 -= fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 += fq; - m17 -= fq; - m18 += fq; + // q=17 + //fq = dist[18*Np+n]; + nread = neighborList[n + 16 * Np]; + fq = dist[nread]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 += fq; + m18 += fq; - // q=17 - //fq = dist[18*Np+n]; - nread = neighborList[n+16*Np]; - fq = dist[nread]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy += fq; - m6 += fq; - jz -= fq; - m8 -= fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 -= fq; - m17 += fq; - m18 += fq; + // q=18 + nread = neighborList[n + 17 * Np]; + fq = dist[nread]; + //fq = dist[9*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 -= fq; + m18 -= fq; - // q=18 - nread = neighborList[n+17*Np]; - fq = dist[nread]; - //fq = dist[9*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy -= fq; - m6 -= fq; - jz += fq; - m8 += fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 -= fq; - m17 -= fq; - m18 -= fq; + // write the velocity + ux = jx / rho0; + uy = jy / rho0; + uz = jz / rho0; + Velocity[n] = ux; + Velocity[Np + n] = uy; + Velocity[2 * Np + n] = uz; - // write the velocity - ux = jx / rho0; - uy = jy / rho0; - uz = jz / rho0; - Velocity[n] = ux; - Velocity[Np+n] = uy; - Velocity[2*Np+n] = uz; + //..............incorporate external force................................................ + //..............carry out relaxation process............................................... + m1 = m1 + + rlx_setA * + ((19 * (jx * jx + jy * jy + jz * jz) / rho0 - 11 * rho) - m1); + m2 = m2 + + rlx_setA * + ((3 * rho - 5.5 * (jx * jx + jy * jy + jz * jz) / rho0) - m2); + m4 = m4 + rlx_setB * ((-0.6666666666666666 * jx) - m4); + m6 = m6 + rlx_setB * ((-0.6666666666666666 * jy) - m6); + m8 = m8 + rlx_setB * ((-0.6666666666666666 * jz) - m8); + m9 = m9 + rlx_setA * (((2 * jx * jx - jy * jy - jz * jz) / rho0) - m9); + m10 = + m10 + + rlx_setA * (-0.5 * ((2 * jx * jx - jy * jy - jz * jz) / rho) - m10); + m11 = m11 + rlx_setA * (((jy * jy - jz * jz) / rho0) - m11); + m12 = m12 + rlx_setA * (-0.5 * ((jy * jy - jz * jz) / rho0) - m12); + m13 = m13 + rlx_setA * ((jx * jy / rho0) - m13); + m14 = m14 + rlx_setA * ((jy * jz / rho0) - m14); + m15 = m15 + rlx_setA * ((jx * jz / rho0) - m15); + m16 = m16 + rlx_setB * (-m16); + m17 = m17 + rlx_setB * (-m17); + m18 = m18 + rlx_setB * (-m18); + //....................................................................................................... + //.................inverse transformation...................................................... - //..............incorporate external force................................................ - //..............carry out relaxation process............................................... - m1 = m1 + rlx_setA*((19*(jx*jx+jy*jy+jz*jz)/rho0 - 11*rho) - m1); - m2 = m2 + rlx_setA*((3*rho - 5.5*(jx*jx+jy*jy+jz*jz)/rho0) - m2); - m4 = m4 + rlx_setB*((-0.6666666666666666*jx) - m4); - m6 = m6 + rlx_setB*((-0.6666666666666666*jy) - m6); - m8 = m8 + rlx_setB*((-0.6666666666666666*jz) - m8); - m9 = m9 + rlx_setA*(((2*jx*jx-jy*jy-jz*jz)/rho0) - m9); - m10 = m10 + rlx_setA*(-0.5*((2*jx*jx-jy*jy-jz*jz)/rho) - m10); - m11 = m11 + rlx_setA*(((jy*jy-jz*jz)/rho0) - m11); - m12 = m12 + rlx_setA*(-0.5*((jy*jy-jz*jz)/rho0) - m12); - m13 = m13 + rlx_setA*((jx*jy/rho0) - m13); - m14 = m14 + rlx_setA*((jy*jz/rho0) - m14); - m15 = m15 + rlx_setA*((jx*jz/rho0) - m15); - m16 = m16 + rlx_setB*( - m16); - m17 = m17 + rlx_setB*( - m17); - m18 = m18 + rlx_setB*( - m18); - //....................................................................................................... - //.................inverse transformation...................................................... + // q=0 + fq = mrt_V1 * rho - mrt_V2 * m1 + mrt_V3 * m2; + dist[n] = fq; - // q=0 - fq = mrt_V1*rho-mrt_V2*m1+mrt_V3*m2; - dist[n] = fq; + // q = 1 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jx - m4) + + mrt_V6 * (m9 - m10) + 0.16666666 * Fx; + nread = neighborList[n + Np]; + dist[nread] = fq; - // q = 1 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jx-m4)+mrt_V6*(m9-m10)+0.16666666*Fx; - nread = neighborList[n+Np]; - dist[nread] = fq; + // q=2 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m4 - jx) + + mrt_V6 * (m9 - m10) - 0.16666666 * Fx; + nread = neighborList[n]; + dist[nread] = fq; - // q=2 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m4-jx)+mrt_V6*(m9-m10) - 0.16666666*Fx; - nread = neighborList[n]; - dist[nread] = fq; + // q = 3 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jy - m6) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12) + 0.16666666 * Fy; + nread = neighborList[n + 3 * Np]; + dist[nread] = fq; - // q = 3 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jy-m6)+mrt_V7*(m10-m9)+mrt_V8*(m11-m12) + 0.16666666*Fy; - nread = neighborList[n+3*Np]; - dist[nread] = fq; + // q = 4 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m6 - jy) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12) - 0.16666666 * Fy; + nread = neighborList[n + 2 * Np]; + dist[nread] = fq; - // q = 4 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m6-jy)+mrt_V7*(m10-m9)+mrt_V8*(m11-m12) - 0.16666666*Fy; - nread = neighborList[n+2*Np]; - dist[nread] = fq; + // q = 5 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jz - m8) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11) + 0.16666666 * Fz; + nread = neighborList[n + 5 * Np]; + dist[nread] = fq; - // q = 5 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jz-m8)+mrt_V7*(m10-m9)+mrt_V8*(m12-m11) + 0.16666666*Fz; - nread = neighborList[n+5*Np]; - dist[nread] = fq; + // q = 6 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m8 - jz) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11) - 0.16666666 * Fz; + nread = neighborList[n + 4 * Np]; + dist[nread] = fq; - // q = 6 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m8-jz)+mrt_V7*(m10-m9)+mrt_V8*(m12-m11) - 0.16666666*Fz; - nread = neighborList[n+4*Np]; - dist[nread] = fq; + // q = 7 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jy) + + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m16 - m17) + + 0.08333333333 * (Fx + Fy); + nread = neighborList[n + 7 * Np]; + dist[nread] = fq; - // q = 7 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jx+jy)+0.025*(m4+m6) - +mrt_V7*m9+mrt_V11*m10+mrt_V8*m11 - +mrt_V12*m12+0.25*m13+0.125*(m16-m17) + 0.08333333333*(Fx+Fy); - nread = neighborList[n+7*Np]; - dist[nread] = fq; + // q = 8 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jy) - + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m17 - m16) - + 0.08333333333 * (Fx + Fy); + nread = neighborList[n + 6 * Np]; + dist[nread] = fq; - // q = 8 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2-0.1*(jx+jy)-0.025*(m4+m6) +mrt_V7*m9+mrt_V11*m10+mrt_V8*m11 - +mrt_V12*m12+0.25*m13+0.125*(m17-m16) - 0.08333333333*(Fx+Fy); - nread = neighborList[n+6*Np]; - dist[nread] = fq; + // q = 9 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jy) + + 0.025 * (m4 - m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 + 0.125 * (m16 + m17) + + 0.08333333333 * (Fx - Fy); + nread = neighborList[n + 9 * Np]; + dist[nread] = fq; - // q = 9 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jx-jy)+0.025*(m4-m6) - +mrt_V7*m9+mrt_V11*m10+mrt_V8*m11 - +mrt_V12*m12-0.25*m13+0.125*(m16+m17) + 0.08333333333*(Fx-Fy); - nread = neighborList[n+9*Np]; - dist[nread] = fq; + // q = 10 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jx) + + 0.025 * (m6 - m4) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 - 0.125 * (m16 + m17) - + 0.08333333333 * (Fx - Fy); + nread = neighborList[n + 8 * Np]; + dist[nread] = fq; - // q = 10 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jy-jx)+0.025*(m6-m4) - +mrt_V7*m9+mrt_V11*m10+mrt_V8*m11 - +mrt_V12*m12-0.25*m13-0.125*(m16+m17)- 0.08333333333*(Fx-Fy); - nread = neighborList[n+8*Np]; - dist[nread] = fq; + // q = 11 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jz) + + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m18 - m16) + + 0.08333333333 * (Fx + Fz); + nread = neighborList[n + 11 * Np]; + dist[nread] = fq; - // q = 11 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jx+jz)+0.025*(m4+m8) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12+0.25*m15+0.125*(m18-m16) + 0.08333333333*(Fx+Fz); - nread = neighborList[n+11*Np]; - dist[nread] = fq; + // q = 12 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jz) - + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m16 - m18) - + 0.08333333333 * (Fx + Fz); + nread = neighborList[n + 10 * Np]; + dist[nread] = fq; - // q = 12 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2-0.1*(jx+jz)-0.025*(m4+m8) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12+0.25*m15+0.125*(m16-m18) - 0.08333333333*(Fx+Fz); - nread = neighborList[n+10*Np]; - dist[nread]= fq; + // q = 13 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jz) + + 0.025 * (m4 - m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 - 0.125 * (m16 + m18) + + 0.08333333333 * (Fx - Fz); + nread = neighborList[n + 13 * Np]; + dist[nread] = fq; - // q = 13 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jx-jz)+0.025*(m4-m8) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12-0.25*m15-0.125*(m16+m18) + 0.08333333333*(Fx-Fz); - nread = neighborList[n+13*Np]; - dist[nread] = fq; + // q= 14 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jx) + + 0.025 * (m8 - m4) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 + 0.125 * (m16 + m18) - + 0.08333333333 * (Fx - Fz); + nread = neighborList[n + 12 * Np]; + dist[nread] = fq; - // q= 14 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jz-jx)+0.025*(m8-m4) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12-0.25*m15+0.125*(m16+m18) - 0.08333333333*(Fx-Fz); - nread = neighborList[n+12*Np]; - dist[nread] = fq; + // q = 15 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy + jz) + + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m17 - m18) + 0.08333333333 * (Fy + Fz); + nread = neighborList[n + 15 * Np]; + dist[nread] = fq; + // q = 16 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jy + jz) - + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m18 - m17) - 0.08333333333 * (Fy + Fz); + nread = neighborList[n + 14 * Np]; + dist[nread] = fq; - // q = 15 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jy+jz)+0.025*(m6+m8) - -mrt_V6*m9-mrt_V7*m10+0.25*m14+0.125*(m17-m18) + 0.08333333333*(Fy+Fz); - nread = neighborList[n+15*Np]; - dist[nread] = fq; + // q = 17 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jz) + + 0.025 * (m6 - m8) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 + + 0.125 * (m17 + m18) + 0.08333333333 * (Fy - Fz); + nread = neighborList[n + 17 * Np]; + dist[nread] = fq; - // q = 16 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2-0.1*(jy+jz)-0.025*(m6+m8) - -mrt_V6*m9-mrt_V7*m10+0.25*m14+0.125*(m18-m17)- 0.08333333333*(Fy+Fz); - nread = neighborList[n+14*Np]; - dist[nread] = fq; - - - // q = 17 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jy-jz)+0.025*(m6-m8) - -mrt_V6*m9-mrt_V7*m10-0.25*m14+0.125*(m17+m18) + 0.08333333333*(Fy-Fz); - nread = neighborList[n+17*Np]; - dist[nread] = fq; - - // q = 18 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jz-jy)+0.025*(m8-m6) - -mrt_V6*m9-mrt_V7*m10-0.25*m14-0.125*(m17+m18) - 0.08333333333*(Fy-Fz); - nread = neighborList[n+16*Np]; - dist[nread] = fq; - - } + // q = 18 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jy) + + 0.025 * (m8 - m6) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 - + 0.125 * (m17 + m18) - 0.08333333333 * (Fy - Fz); + nread = neighborList[n + 16 * Np]; + dist[nread] = fq; + } } //extern "C" void ScaLBL_D3Q19_Momentum_Phys(double *dist, double *vel, double h, double time_conv, int Np) diff --git a/cpu/dfh.cpp b/cpu/dfh.cpp index 419af282..24c1b7b2 100644 --- a/cpu/dfh.cpp +++ b/cpu/dfh.cpp @@ -33,1415 +33,1476 @@ #include #include -extern "C" void ScaLBL_Gradient_Unpack(double weight, double Cqx, double Cqy, double Cqz, - int *list, int start, int count, double *recvbuf, double *phi, double *grad, int N){ - //.................................................................................... - // unpack halo and incorporate into D3Q19 based gradient - // Distribution q matche Cqx, Cqy, Cqz - //.................................................................................... - int n,idx; - double value; - for (idx=0; idx 0.f){ - nA = 1.0; nB = 0.f; - } - else{ - nB = 1.0; nA = 0.f; - } - Den[idx] = nA; - Den[Np+idx] = nB; +extern "C" void ScaLBL_DFH_Init(double *Phi, double *Den, double *Aq, + double *Bq, int start, int finish, int Np) { + for (int idx = start; idx < finish; idx++) { + double phi, nA, nB; + phi = Phi[idx]; + if (phi > 0.f) { + nA = 1.0; + nB = 0.f; + } else { + nB = 1.0; + nA = 0.f; + } + Den[idx] = nA; + Den[Np + idx] = nB; - Aq[idx]=0.3333333333333333*nA; - Aq[Np+idx]=0.1111111111111111*nA; - Aq[2*Np+idx]=0.1111111111111111*nA; - Aq[3*Np+idx]=0.1111111111111111*nA; - Aq[4*Np+idx]=0.1111111111111111*nA; - Aq[5*Np+idx]=0.1111111111111111*nA; - Aq[6*Np+idx]=0.1111111111111111*nA; + Aq[idx] = 0.3333333333333333 * nA; + Aq[Np + idx] = 0.1111111111111111 * nA; + Aq[2 * Np + idx] = 0.1111111111111111 * nA; + Aq[3 * Np + idx] = 0.1111111111111111 * nA; + Aq[4 * Np + idx] = 0.1111111111111111 * nA; + Aq[5 * Np + idx] = 0.1111111111111111 * nA; + Aq[6 * Np + idx] = 0.1111111111111111 * nA; - Bq[idx]=0.3333333333333333*nB; - Bq[Np+idx]=0.1111111111111111*nB; - Bq[2*Np+idx]=0.1111111111111111*nB; - Bq[3*Np+idx]=0.1111111111111111*nB; - Bq[4*Np+idx]=0.1111111111111111*nB; - Bq[5*Np+idx]=0.1111111111111111*nB; - Bq[6*Np+idx]=0.1111111111111111*nB; - } + Bq[idx] = 0.3333333333333333 * nB; + Bq[Np + idx] = 0.1111111111111111 * nB; + Bq[2 * Np + idx] = 0.1111111111111111 * nB; + Bq[3 * Np + idx] = 0.1111111111111111 * nB; + Bq[4 * Np + idx] = 0.1111111111111111 * nB; + Bq[5 * Np + idx] = 0.1111111111111111 * nB; + Bq[6 * Np + idx] = 0.1111111111111111 * nB; + } } - // LBM based on density functional hydrodynamics -extern "C" void ScaLBL_D3Q19_AAeven_DFH(int *neighborList, double *dist, double *Aq, double *Bq, double *Den, double *Phi, - double *Gradient, double *SolidForce, double rhoA, double rhoB, double tauA, double tauB, double alpha, double beta, - double Fx, double Fy, double Fz, int start, int finish, int Np) -{ - double fq; - // conserved momemnts - double rho,jx,jy,jz; - // non-conserved moments - double m1,m2,m4,m6,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18; - double nA,nB; // number density - double a1,b1,a2,b2,nAB,delta; - double C,nx,ny,nz; //color gradient magnitude and direction - double ux,uy,uz; - double phi,tau,rho0,rlx_setA,rlx_setB; - double force_x,force_y,force_z; - - const double mrt_V1=0.05263157894736842; - const double mrt_V2=0.012531328320802; - const double mrt_V3=0.04761904761904762; - const double mrt_V4=0.004594820384294068; - const double mrt_V5=0.01587301587301587; - const double mrt_V6=0.0555555555555555555555555; - const double mrt_V7=0.02777777777777778; - const double mrt_V8=0.08333333333333333; - const double mrt_V9=0.003341687552213868; - const double mrt_V10=0.003968253968253968; - const double mrt_V11=0.01388888888888889; - const double mrt_V12=0.04166666666666666; +extern "C" void ScaLBL_D3Q19_AAeven_DFH( + int *neighborList, double *dist, double *Aq, double *Bq, double *Den, + double *Phi, double *Gradient, double *SolidForce, double rhoA, double rhoB, + double tauA, double tauB, double alpha, double beta, double Fx, double Fy, + double Fz, int start, int finish, int Np) { + double fq; + // conserved momemnts + double rho, jx, jy, jz; + // non-conserved moments + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; + double nA, nB; // number density + double a1, b1, a2, b2, nAB, delta; + double C, nx, ny, nz; //color gradient magnitude and direction + double ux, uy, uz; + double phi, tau, rho0, rlx_setA, rlx_setB; + double force_x, force_y, force_z; + const double mrt_V1 = 0.05263157894736842; + const double mrt_V2 = 0.012531328320802; + const double mrt_V3 = 0.04761904761904762; + const double mrt_V4 = 0.004594820384294068; + const double mrt_V5 = 0.01587301587301587; + const double mrt_V6 = 0.0555555555555555555555555; + const double mrt_V7 = 0.02777777777777778; + const double mrt_V8 = 0.08333333333333333; + const double mrt_V9 = 0.003341687552213868; + const double mrt_V10 = 0.003968253968253968; + const double mrt_V11 = 0.01388888888888889; + const double mrt_V12 = 0.04166666666666666; - for (int n=start; n 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * ux)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * ux)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * ux)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * ux)) + delta; - //............................................... - // q = 0,2,4 - // Cq = {1,0,0}, {0,1,0}, {0,0,1} - delta = beta*nA*nB*nAB*0.1111111111111111*nx; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*ux))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*ux))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*ux))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*ux))+delta; + Aq[1 * Np + n] = a1; + Bq[1 * Np + n] = b1; + Aq[2 * Np + n] = a2; + Bq[2 * Np + n] = b2; - Aq[1*Np+n] = a1; - Bq[1*Np+n] = b1; - Aq[2*Np+n] = a2; - Bq[2*Np+n] = b2; + //............................................... + // q = 2 + // Cq = {0,1,0} + delta = beta * nA * nB * nAB * 0.1111111111111111 * ny; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * uy)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * uy)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * uy)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * uy)) + delta; - //............................................... - // q = 2 - // Cq = {0,1,0} - delta = beta*nA*nB*nAB*0.1111111111111111*ny; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*uy))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*uy))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*uy))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*uy))+delta; + Aq[3 * Np + n] = a1; + Bq[3 * Np + n] = b1; + Aq[4 * Np + n] = a2; + Bq[4 * Np + n] = b2; + //............................................... + // q = 4 + // Cq = {0,0,1} + delta = beta * nA * nB * nAB * 0.1111111111111111 * nz; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * uz)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * uz)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * uz)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * uz)) + delta; - Aq[3*Np+n] = a1; - Bq[3*Np+n] = b1; - Aq[4*Np+n] = a2; - Bq[4*Np+n] = b2; - //............................................... - // q = 4 - // Cq = {0,0,1} - delta = beta*nA*nB*nAB*0.1111111111111111*nz; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*uz))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*uz))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*uz))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*uz))+delta; - - Aq[5*Np+n] = a1; - Bq[5*Np+n] = b1; - Aq[6*Np+n] = a2; - Bq[6*Np+n] = b2; - //............................................... - - } - + Aq[5 * Np + n] = a1; + Bq[5 * Np + n] = b1; + Aq[6 * Np + n] = a2; + Bq[6 * Np + n] = b2; + //............................................... + } } -extern "C" void ScaLBL_D3Q19_AAodd_DFH(int *neighborList, double *dist, double *Aq, double *Bq, double *Den, - double *Phi, double *Gradient, double *SolidForce, double rhoA, double rhoB, double tauA, double tauB, double alpha, double beta, - double Fx, double Fy, double Fz, int start, int finish, int Np){ - - int nread; - int nr1,nr2,nr3,nr4,nr5,nr6; - int nr7,nr8,nr9,nr10; - int nr11,nr12,nr13,nr14; - //int nr15,nr16,nr17,nr18; - double fq; - // conserved momemnts - double rho,jx,jy,jz; - // non-conserved moments - double m1,m2,m4,m6,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18; - double nA,nB; // number density - double a1,b1,a2,b2,nAB,delta; - double C,nx,ny,nz; //color gradient magnitude and direction - double ux,uy,uz; - double phi,tau,rho0,rlx_setA,rlx_setB; - double force_x,force_y,force_z; +extern "C" void ScaLBL_D3Q19_AAodd_DFH( + int *neighborList, double *dist, double *Aq, double *Bq, double *Den, + double *Phi, double *Gradient, double *SolidForce, double rhoA, double rhoB, + double tauA, double tauB, double alpha, double beta, double Fx, double Fy, + double Fz, int start, int finish, int Np) { - const double mrt_V1=0.05263157894736842; - const double mrt_V2=0.012531328320802; - const double mrt_V3=0.04761904761904762; - const double mrt_V4=0.004594820384294068; - const double mrt_V5=0.01587301587301587; - const double mrt_V6=0.0555555555555555555555555; - const double mrt_V7=0.02777777777777778; - const double mrt_V8=0.08333333333333333; - const double mrt_V9=0.003341687552213868; - const double mrt_V10=0.003968253968253968; - const double mrt_V11=0.01388888888888889; - const double mrt_V12=0.04166666666666666; + int nread; + int nr1, nr2, nr3, nr4, nr5, nr6; + int nr7, nr8, nr9, nr10; + int nr11, nr12, nr13, nr14; + //int nr15,nr16,nr17,nr18; + double fq; + // conserved momemnts + double rho, jx, jy, jz; + // non-conserved moments + double m1, m2, m4, m6, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18; + double nA, nB; // number density + double a1, b1, a2, b2, nAB, delta; + double C, nx, ny, nz; //color gradient magnitude and direction + double ux, uy, uz; + double phi, tau, rho0, rlx_setA, rlx_setB; + double force_x, force_y, force_z; - for (int n=start; n even part of dist) - //fq = dist[nread]; // reading the f2 data into register fq - nr2 = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) - fq = dist[nr2]; // reading the f2 data into register fq - rho += fq; - m1 -= 11.0*(fq); - m2 -= 4.0*(fq); - jx -= fq; - m4 += 4.0*(fq); - m9 += 2.0*(fq); - m10 -= 4.0*(fq); + //...........Read the Color Gradient................................. + nx = Gradient[n]; + ny = Gradient[n + Np]; + nz = Gradient[n + 2 * Np]; + C = sqrt(nx * nx + ny * ny + nz * nz); + if (C == 0.0) + C = 1.0; + nx = nx / C; + ny = ny / C; + nz = nz / C; - // q=3 - //nread = neighborList[n+2*Np]; // neighbor 4 - //fq = dist[nread]; - nr3 = neighborList[n+2*Np]; // neighbor 4 - fq = dist[nr3]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jy = fq; - m6 = -4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 = fq; - m12 = -2.0*fq; + // q=0 + fq = dist[n]; + rho = fq; + m1 = -30.0 * fq; + m2 = 12.0 * fq; - // q = 4 - //nread = neighborList[n+3*Np]; // neighbor 3 - //fq = dist[nread]; - nr4 = neighborList[n+3*Np]; // neighbor 3 - fq = dist[nr4]; - rho+= fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jy -= fq; - m6 += 4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 += fq; - m12 -= 2.0*fq; + // q=1 + //nread = neighborList[n]; // neighbor 2 + //fq = dist[nread]; // reading the f1 data into register fq + nr1 = neighborList[n]; + fq = dist[nr1]; // reading the f1 data into register fq + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jx = fq; + m4 = -4.0 * fq; + m9 = 2.0 * fq; + m10 = -4.0 * fq; - // q=5 - //nread = neighborList[n+4*Np]; - //fq = dist[nread]; - nr5 = neighborList[n+4*Np]; - fq = dist[nr5]; - rho += fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jz = fq; - m8 = -4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 -= fq; - m12 += 2.0*fq; + // f2 = dist[10*Np+n]; + //nread = neighborList[n+Np]; // neighbor 1 ( < 10Np => even part of dist) + //fq = dist[nread]; // reading the f2 data into register fq + nr2 = neighborList[n + Np]; // neighbor 1 ( < 10Np => even part of dist) + fq = dist[nr2]; // reading the f2 data into register fq + rho += fq; + m1 -= 11.0 * (fq); + m2 -= 4.0 * (fq); + jx -= fq; + m4 += 4.0 * (fq); + m9 += 2.0 * (fq); + m10 -= 4.0 * (fq); + // q=3 + //nread = neighborList[n+2*Np]; // neighbor 4 + //fq = dist[nread]; + nr3 = neighborList[n + 2 * Np]; // neighbor 4 + fq = dist[nr3]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy = fq; + m6 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 = fq; + m12 = -2.0 * fq; - // q = 6 - //nread = neighborList[n+5*Np]; - //fq = dist[nread]; - nr6 = neighborList[n+5*Np]; - fq = dist[nr6]; - rho+= fq; - m1 -= 11.0*fq; - m2 -= 4.0*fq; - jz -= fq; - m8 += 4.0*fq; - m9 -= fq; - m10 += 2.0*fq; - m11 -= fq; - m12 += 2.0*fq; + // q = 4 + //nread = neighborList[n+3*Np]; // neighbor 3 + //fq = dist[nread]; + nr4 = neighborList[n + 3 * Np]; // neighbor 3 + fq = dist[nr4]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jy -= fq; + m6 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 += fq; + m12 -= 2.0 * fq; - // q=7 - //nread = neighborList[n+6*Np]; - //fq = dist[nread]; - nr7 = neighborList[n+6*Np]; - fq = dist[nr7]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jy += fq; - m6 += fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 = fq; - m16 = fq; - m17 = -fq; + // q=5 + //nread = neighborList[n+4*Np]; + //fq = dist[nread]; + nr5 = neighborList[n + 4 * Np]; + fq = dist[nr5]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz = fq; + m8 = -4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; - // q = 8 - //nread = neighborList[n+7*Np]; - //fq = dist[nread]; - nr8 = neighborList[n+7*Np]; - fq = dist[nr8]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jy -= fq; - m6 -= fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 += fq; - m16 -= fq; - m17 += fq; + // q = 6 + //nread = neighborList[n+5*Np]; + //fq = dist[nread]; + nr6 = neighborList[n + 5 * Np]; + fq = dist[nr6]; + rho += fq; + m1 -= 11.0 * fq; + m2 -= 4.0 * fq; + jz -= fq; + m8 += 4.0 * fq; + m9 -= fq; + m10 += 2.0 * fq; + m11 -= fq; + m12 += 2.0 * fq; - // q=9 - //nread = neighborList[n+8*Np]; - //fq = dist[nread]; - nr9 = neighborList[n+8*Np]; - fq = dist[nr9]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jy -= fq; - m6 -= fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 -= fq; - m16 += fq; - m17 += fq; + // q=7 + //nread = neighborList[n+6*Np]; + //fq = dist[nread]; + nr7 = neighborList[n + 6 * Np]; + fq = dist[nr7]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 = fq; + m16 = fq; + m17 = -fq; - // q = 10 - //nread = neighborList[n+9*Np]; - //fq = dist[nread]; - nr10 = neighborList[n+9*Np]; - fq = dist[nr10]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jy += fq; - m6 += fq; - m9 += fq; - m10 += fq; - m11 += fq; - m12 += fq; - m13 -= fq; - m16 -= fq; - m17 -= fq; + // q = 8 + //nread = neighborList[n+7*Np]; + //fq = dist[nread]; + nr8 = neighborList[n + 7 * Np]; + fq = dist[nr8]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 += fq; + m16 -= fq; + m17 += fq; - // q=11 - //nread = neighborList[n+10*Np]; - //fq = dist[nread]; - nr11 = neighborList[n+10*Np]; - fq = dist[nr11]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jz += fq; - m8 += fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 = fq; - m16 -= fq; - m18 = fq; + // q=9 + //nread = neighborList[n+8*Np]; + //fq = dist[nread]; + nr9 = neighborList[n + 8 * Np]; + fq = dist[nr9]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jy -= fq; + m6 -= fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 += fq; + m17 += fq; - // q=12 - //nread = neighborList[n+11*Np]; - //fq = dist[nread]; - nr12 = neighborList[n+11*Np]; - fq = dist[nr12]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jz -= fq; - m8 -= fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 += fq; - m16 += fq; - m18 -= fq; + // q = 10 + //nread = neighborList[n+9*Np]; + //fq = dist[nread]; + nr10 = neighborList[n + 9 * Np]; + fq = dist[nr10]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jy += fq; + m6 += fq; + m9 += fq; + m10 += fq; + m11 += fq; + m12 += fq; + m13 -= fq; + m16 -= fq; + m17 -= fq; - // q=13 - //nread = neighborList[n+12*Np]; - //fq = dist[nread]; - nr13 = neighborList[n+12*Np]; - fq = dist[nr13]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx += fq; - m4 += fq; - jz -= fq; - m8 -= fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 -= fq; - m16 -= fq; - m18 -= fq; + // q=11 + //nread = neighborList[n+10*Np]; + //fq = dist[nread]; + nr11 = neighborList[n + 10 * Np]; + fq = dist[nr11]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 = fq; + m16 -= fq; + m18 = fq; - // q=14 - //nread = neighborList[n+13*Np]; - //fq = dist[nread]; - nr14 = neighborList[n+13*Np]; - fq = dist[nr14]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jx -= fq; - m4 -= fq; - jz += fq; - m8 += fq; - m9 += fq; - m10 += fq; - m11 -= fq; - m12 -= fq; - m15 -= fq; - m16 += fq; - m18 += fq; + // q=12 + //nread = neighborList[n+11*Np]; + //fq = dist[nread]; + nr12 = neighborList[n + 11 * Np]; + fq = dist[nr12]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 += fq; + m16 += fq; + m18 -= fq; - // q=15 - nread = neighborList[n+14*Np]; - fq = dist[nread]; - //fq = dist[17*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy += fq; - m6 += fq; - jz += fq; - m8 += fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 = fq; - m17 += fq; - m18 -= fq; + // q=13 + //nread = neighborList[n+12*Np]; + //fq = dist[nread]; + nr13 = neighborList[n + 12 * Np]; + fq = dist[nr13]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx += fq; + m4 += fq; + jz -= fq; + m8 -= fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 -= fq; + m18 -= fq; - // q=16 - nread = neighborList[n+15*Np]; - fq = dist[nread]; - //fq = dist[8*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy -= fq; - m6 -= fq; - jz -= fq; - m8 -= fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 += fq; - m17 -= fq; - m18 += fq; + // q=14 + //nread = neighborList[n+13*Np]; + //fq = dist[nread]; + nr14 = neighborList[n + 13 * Np]; + fq = dist[nr14]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jx -= fq; + m4 -= fq; + jz += fq; + m8 += fq; + m9 += fq; + m10 += fq; + m11 -= fq; + m12 -= fq; + m15 -= fq; + m16 += fq; + m18 += fq; - // q=17 - //fq = dist[18*Np+n]; - nread = neighborList[n+16*Np]; - fq = dist[nread]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy += fq; - m6 += fq; - jz -= fq; - m8 -= fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 -= fq; - m17 += fq; - m18 += fq; + // q=15 + nread = neighborList[n + 14 * Np]; + fq = dist[nread]; + //fq = dist[17*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 = fq; + m17 += fq; + m18 -= fq; - // q=18 - nread = neighborList[n+17*Np]; - fq = dist[nread]; - //fq = dist[9*Np+n]; - rho += fq; - m1 += 8.0*fq; - m2 += fq; - jy -= fq; - m6 -= fq; - jz += fq; - m8 += fq; - m9 -= 2.0*fq; - m10 -= 2.0*fq; - m14 -= fq; - m17 -= fq; - m18 -= fq; - - //........................................................................ - //..............carry out relaxation process.............................. - //..........Toelke, Fruediger et. al. 2006................................ - if (C == 0.0) nx = ny = nz = 0.0; - m1 = m1 + rlx_setA*((19*(jx*jx+jy*jy+jz*jz)/rho0 - 11*rho) -alpha*C - m1); - m2 = m2 + rlx_setA*((3*rho - 5.5*(jx*jx+jy*jy+jz*jz)/rho0)- m2); - m4 = m4 + rlx_setB*((-0.6666666666666666*jx)- m4); - m6 = m6 + rlx_setB*((-0.6666666666666666*jy)- m6); - m8 = m8 + rlx_setB*((-0.6666666666666666*jz)- m8); - m9 = m9 + rlx_setA*(((2*jx*jx-jy*jy-jz*jz)/rho0) + 0.5*alpha*C*(2*nx*nx-ny*ny-nz*nz) - m9); - m10 = m10 + rlx_setA*( - m10); - m11 = m11 + rlx_setA*(((jy*jy-jz*jz)/rho0) + 0.5*alpha*C*(ny*ny-nz*nz)- m11); - m12 = m12 + rlx_setA*( - m12); - m13 = m13 + rlx_setA*( (jx*jy/rho0) + 0.5*alpha*C*nx*ny - m13); - m14 = m14 + rlx_setA*( (jy*jz/rho0) + 0.5*alpha*C*ny*nz - m14); - m15 = m15 + rlx_setA*( (jx*jz/rho0) + 0.5*alpha*C*nx*nz - m15); - m16 = m16 + rlx_setB*( - m16); - m17 = m17 + rlx_setB*( - m17); - m18 = m18 + rlx_setB*( - m18); + // q=16 + nread = neighborList[n + 15 * Np]; + fq = dist[nread]; + //fq = dist[8*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 += fq; + m17 -= fq; + m18 += fq; - // assign force with wetting BC - force_x = alpha*(nA-nB)*SolidForce[n] + Fx; - force_y = alpha*(nA-nB)*SolidForce[n+Np] + Fy; - force_z = alpha*(nA-nB)*SolidForce[n+2*Np] + Fz; - - //.................inverse transformation...................................................... - // q=0 - fq = mrt_V1*rho-mrt_V2*m1+mrt_V3*m2; - dist[n] = fq; + // q=17 + //fq = dist[18*Np+n]; + nread = neighborList[n + 16 * Np]; + fq = dist[nread]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy += fq; + m6 += fq; + jz -= fq; + m8 -= fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 += fq; + m18 += fq; - // q = 1 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jx-m4)+mrt_V6*(m9-m10)+0.16666666*force_x; - //nread = neighborList[n+Np]; - dist[nr2] = fq; + // q=18 + nread = neighborList[n + 17 * Np]; + fq = dist[nread]; + //fq = dist[9*Np+n]; + rho += fq; + m1 += 8.0 * fq; + m2 += fq; + jy -= fq; + m6 -= fq; + jz += fq; + m8 += fq; + m9 -= 2.0 * fq; + m10 -= 2.0 * fq; + m14 -= fq; + m17 -= fq; + m18 -= fq; - // q=2 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m4-jx)+mrt_V6*(m9-m10) - 0.16666666*force_x; - //nread = neighborList[n]; - dist[nr1] = fq; + //........................................................................ + //..............carry out relaxation process.............................. + //..........Toelke, Fruediger et. al. 2006................................ + if (C == 0.0) + nx = ny = nz = 0.0; + m1 = m1 + rlx_setA * + ((19 * (jx * jx + jy * jy + jz * jz) / rho0 - 11 * rho) - + alpha * C - m1); + m2 = m2 + + rlx_setA * + ((3 * rho - 5.5 * (jx * jx + jy * jy + jz * jz) / rho0) - m2); + m4 = m4 + rlx_setB * ((-0.6666666666666666 * jx) - m4); + m6 = m6 + rlx_setB * ((-0.6666666666666666 * jy) - m6); + m8 = m8 + rlx_setB * ((-0.6666666666666666 * jz) - m8); + m9 = + m9 + rlx_setA * + (((2 * jx * jx - jy * jy - jz * jz) / rho0) + + 0.5 * alpha * C * (2 * nx * nx - ny * ny - nz * nz) - m9); + m10 = m10 + rlx_setA * (-m10); + m11 = m11 + rlx_setA * (((jy * jy - jz * jz) / rho0) + + 0.5 * alpha * C * (ny * ny - nz * nz) - m11); + m12 = m12 + rlx_setA * (-m12); + m13 = m13 + + rlx_setA * ((jx * jy / rho0) + 0.5 * alpha * C * nx * ny - m13); + m14 = m14 + + rlx_setA * ((jy * jz / rho0) + 0.5 * alpha * C * ny * nz - m14); + m15 = m15 + + rlx_setA * ((jx * jz / rho0) + 0.5 * alpha * C * nx * nz - m15); + m16 = m16 + rlx_setB * (-m16); + m17 = m17 + rlx_setB * (-m17); + m18 = m18 + rlx_setB * (-m18); - // q = 3 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jy-m6)+mrt_V7*(m10-m9)+mrt_V8*(m11-m12) + 0.16666666*force_y; - //nread = neighborList[n+3*Np]; - dist[nr4] = fq; + // assign force with wetting BC + force_x = alpha * (nA - nB) * SolidForce[n] + Fx; + force_y = alpha * (nA - nB) * SolidForce[n + Np] + Fy; + force_z = alpha * (nA - nB) * SolidForce[n + 2 * Np] + Fz; - // q = 4 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m6-jy)+mrt_V7*(m10-m9)+mrt_V8*(m11-m12) - 0.16666666*force_y; - //nread = neighborList[n+2*Np]; - dist[nr3] = fq; + //.................inverse transformation...................................................... + // q=0 + fq = mrt_V1 * rho - mrt_V2 * m1 + mrt_V3 * m2; + dist[n] = fq; - // q = 5 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(jz-m8)+mrt_V7*(m10-m9)+mrt_V8*(m12-m11) + 0.16666666*force_z; - //nread = neighborList[n+5*Np]; - dist[nr6] = fq; + // q = 1 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jx - m4) + + mrt_V6 * (m9 - m10) + 0.16666666 * force_x; + //nread = neighborList[n+Np]; + dist[nr2] = fq; - // q = 6 - fq = mrt_V1*rho-mrt_V4*m1-mrt_V5*m2+0.1*(m8-jz)+mrt_V7*(m10-m9)+mrt_V8*(m12-m11) - 0.16666666*force_z; - //nread = neighborList[n+4*Np]; - dist[nr5] = fq; + // q=2 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m4 - jx) + + mrt_V6 * (m9 - m10) - 0.16666666 * force_x; + //nread = neighborList[n]; + dist[nr1] = fq; - // q = 7 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jx+jy)+0.025*(m4+m6)+ - mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12+0.25*m13+0.125*(m16-m17) + 0.08333333333*(force_x+force_y); - //nread = neighborList[n+7*Np]; - dist[nr8] = fq; + // q = 3 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jy - m6) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12) + 0.16666666 * force_y; + //nread = neighborList[n+3*Np]; + dist[nr4] = fq; - // q = 8 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2-0.1*(jx+jy)-0.025*(m4+m6) +mrt_V7*m9+mrt_V11*m10+mrt_V8*m11 - +mrt_V12*m12+0.25*m13+0.125*(m17-m16) - 0.08333333333*(force_x+force_y); - //nread = neighborList[n+6*Np]; - dist[nr7] = fq; + // q = 4 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m6 - jy) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m11 - m12) - 0.16666666 * force_y; + //nread = neighborList[n+2*Np]; + dist[nr3] = fq; - // q = 9 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jx-jy)+0.025*(m4-m6)+ - mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12-0.25*m13+0.125*(m16+m17) + 0.08333333333*(force_x-force_y); - //nread = neighborList[n+9*Np]; - dist[nr10] = fq; + // q = 5 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (jz - m8) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11) + 0.16666666 * force_z; + //nread = neighborList[n+5*Np]; + dist[nr6] = fq; - // q = 10 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2+0.1*(jy-jx)+0.025*(m6-m4)+ - mrt_V7*m9+mrt_V11*m10+mrt_V8*m11+mrt_V12*m12-0.25*m13-0.125*(m16+m17)- 0.08333333333*(force_x-force_y); - //nread = neighborList[n+8*Np]; - dist[nr9] = fq; + // q = 6 + fq = mrt_V1 * rho - mrt_V4 * m1 - mrt_V5 * m2 + 0.1 * (m8 - jz) + + mrt_V7 * (m10 - m9) + mrt_V8 * (m12 - m11) - 0.16666666 * force_z; + //nread = neighborList[n+4*Np]; + dist[nr5] = fq; - // q = 11 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jx+jz)+0.025*(m4+m8) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12+0.25*m15+0.125*(m18-m16) + 0.08333333333*(force_x+force_z); - //nread = neighborList[n+11*Np]; - dist[nr12] = fq; + // q = 7 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jy) + + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m16 - m17) + + 0.08333333333 * (force_x + force_y); + //nread = neighborList[n+7*Np]; + dist[nr8] = fq; - // q = 12 - fq = mrt_V1*rho+mrt_V9*m1+mrt_V10*m2-0.1*(jx+jz)-0.025*(m4+m8)+ - mrt_V7*m9+mrt_V11*m10-mrt_V8*m11-mrt_V12*m12+0.25*m15+0.125*(m16-m18) - 0.08333333333*(force_x+force_z); - //nread = neighborList[n+10*Np]; - dist[nr11]= fq; + // q = 8 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jy) - + 0.025 * (m4 + m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 + 0.25 * m13 + 0.125 * (m17 - m16) - + 0.08333333333 * (force_x + force_y); + //nread = neighborList[n+6*Np]; + dist[nr7] = fq; - // q = 13 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jx-jz)+0.025*(m4-m8) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12-0.25*m15-0.125*(m16+m18) + 0.08333333333*(force_x-force_z); - //nread = neighborList[n+13*Np]; - dist[nr14] = fq; + // q = 9 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jy) + + 0.025 * (m4 - m6) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 + 0.125 * (m16 + m17) + + 0.08333333333 * (force_x - force_y); + //nread = neighborList[n+9*Np]; + dist[nr10] = fq; - // q= 14 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jz-jx)+0.025*(m8-m4) - +mrt_V7*m9+mrt_V11*m10-mrt_V8*m11 - -mrt_V12*m12-0.25*m15+0.125*(m16+m18) - 0.08333333333*(force_x-force_z); - //nread = neighborList[n+12*Np]; - dist[nr13] = fq; + // q = 10 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jx) + + 0.025 * (m6 - m4) + mrt_V7 * m9 + mrt_V11 * m10 + mrt_V8 * m11 + + mrt_V12 * m12 - 0.25 * m13 - 0.125 * (m16 + m17) - + 0.08333333333 * (force_x - force_y); + //nread = neighborList[n+8*Np]; + dist[nr9] = fq; + // q = 11 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx + jz) + + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m18 - m16) + + 0.08333333333 * (force_x + force_z); + //nread = neighborList[n+11*Np]; + dist[nr12] = fq; - // q = 15 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jy+jz)+0.025*(m6+m8) - -mrt_V6*m9-mrt_V7*m10+0.25*m14+0.125*(m17-m18) + 0.08333333333*(force_y+force_z); - nread = neighborList[n+15*Np]; - dist[nread] = fq; + // q = 12 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jx + jz) - + 0.025 * (m4 + m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 + 0.25 * m15 + 0.125 * (m16 - m18) - + 0.08333333333 * (force_x + force_z); + //nread = neighborList[n+10*Np]; + dist[nr11] = fq; - // q = 16 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2-0.1*(jy+jz)-0.025*(m6+m8) - -mrt_V6*m9-mrt_V7*m10+0.25*m14+0.125*(m18-m17)- 0.08333333333*(force_y+force_z); - nread = neighborList[n+14*Np]; - dist[nread] = fq; + // q = 13 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jx - jz) + + 0.025 * (m4 - m8) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 - 0.125 * (m16 + m18) + + 0.08333333333 * (force_x - force_z); + //nread = neighborList[n+13*Np]; + dist[nr14] = fq; + // q= 14 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jx) + + 0.025 * (m8 - m4) + mrt_V7 * m9 + mrt_V11 * m10 - mrt_V8 * m11 - + mrt_V12 * m12 - 0.25 * m15 + 0.125 * (m16 + m18) - + 0.08333333333 * (force_x - force_z); + //nread = neighborList[n+12*Np]; + dist[nr13] = fq; - // q = 17 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jy-jz)+0.025*(m6-m8) - -mrt_V6*m9-mrt_V7*m10-0.25*m14+0.125*(m17+m18) + 0.08333333333*(force_y-force_z); - nread = neighborList[n+17*Np]; - dist[nread] = fq; + // q = 15 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy + jz) + + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m17 - m18) + 0.08333333333 * (force_y + force_z); + nread = neighborList[n + 15 * Np]; + dist[nread] = fq; - // q = 18 - fq = mrt_V1*rho+mrt_V9*m1 - +mrt_V10*m2+0.1*(jz-jy)+0.025*(m8-m6) - -mrt_V6*m9-mrt_V7*m10-0.25*m14-0.125*(m17+m18) - 0.08333333333*(force_y-force_z); - nread = neighborList[n+16*Np]; - dist[nread] = fq; + // q = 16 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 - 0.1 * (jy + jz) - + 0.025 * (m6 + m8) - mrt_V6 * m9 - mrt_V7 * m10 + 0.25 * m14 + + 0.125 * (m18 - m17) - 0.08333333333 * (force_y + force_z); + nread = neighborList[n + 14 * Np]; + dist[nread] = fq; - // write the velocity - ux = (jx + force_x) / rho0; - uy = (jy + force_y) / rho0; - uz = (jz + force_z) / rho0; - //Velocity[n] = ux; - //Velocity[Np+n] = uy; - //Velocity[2*Np+n] = uz; + // q = 17 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jy - jz) + + 0.025 * (m6 - m8) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 + + 0.125 * (m17 + m18) + 0.08333333333 * (force_y - force_z); + nread = neighborList[n + 17 * Np]; + dist[nread] = fq; - // Instantiate mass transport distributions - // Stationary value - distribution 0 - nAB = 1.0/(nA+nB); - Aq[n] = 0.3333333333333333*nA; - Bq[n] = 0.3333333333333333*nB; + // q = 18 + fq = mrt_V1 * rho + mrt_V9 * m1 + mrt_V10 * m2 + 0.1 * (jz - jy) + + 0.025 * (m8 - m6) - mrt_V6 * m9 - mrt_V7 * m10 - 0.25 * m14 - + 0.125 * (m17 + m18) - 0.08333333333 * (force_y - force_z); + nread = neighborList[n + 16 * Np]; + dist[nread] = fq; - //............................................... - // q = 0,2,4 - // Cq = {1,0,0}, {0,1,0}, {0,0,1} - delta = beta*nA*nB*nAB*0.1111111111111111*nx; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*ux))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*ux))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*ux))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*ux))+delta; + // write the velocity + ux = (jx + force_x) / rho0; + uy = (jy + force_y) / rho0; + uz = (jz + force_z) / rho0; + //Velocity[n] = ux; + //Velocity[Np+n] = uy; + //Velocity[2*Np+n] = uz; - // q = 1 - //nread = neighborList[n+Np]; - Aq[nr2] = a1; - Bq[nr2] = b1; - // q=2 - //nread = neighborList[n]; - Aq[nr1] = a2; - Bq[nr1] = b2; + // Instantiate mass transport distributions + // Stationary value - distribution 0 + nAB = 1.0 / (nA + nB); + Aq[n] = 0.3333333333333333 * nA; + Bq[n] = 0.3333333333333333 * nB; - //............................................... - // Cq = {0,1,0} - delta = beta*nA*nB*nAB*0.1111111111111111*ny; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*uy))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*uy))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*uy))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*uy))+delta; + //............................................... + // q = 0,2,4 + // Cq = {1,0,0}, {0,1,0}, {0,0,1} + delta = beta * nA * nB * nAB * 0.1111111111111111 * nx; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * ux)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * ux)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * ux)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * ux)) + delta; - // q = 3 - //nread = neighborList[n+3*Np]; - Aq[nr4] = a1; - Bq[nr4] = b1; - // q = 4 - //nread = neighborList[n+2*Np]; - Aq[nr3] = a2; - Bq[nr3] = b2; + // q = 1 + //nread = neighborList[n+Np]; + Aq[nr2] = a1; + Bq[nr2] = b1; + // q=2 + //nread = neighborList[n]; + Aq[nr1] = a2; + Bq[nr1] = b2; - //............................................... - // q = 4 - // Cq = {0,0,1} - delta = beta*nA*nB*nAB*0.1111111111111111*nz; - if (!(nA*nB*nAB>0)) delta=0; - a1 = nA*(0.1111111111111111*(1+4.5*uz))+delta; - b1 = nB*(0.1111111111111111*(1+4.5*uz))-delta; - a2 = nA*(0.1111111111111111*(1-4.5*uz))-delta; - b2 = nB*(0.1111111111111111*(1-4.5*uz))+delta; + //............................................... + // Cq = {0,1,0} + delta = beta * nA * nB * nAB * 0.1111111111111111 * ny; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * uy)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * uy)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * uy)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * uy)) + delta; - // q = 5 - //nread = neighborList[n+5*Np]; - Aq[nr6] = a1; - Bq[nr6] = b1; - // q = 6 - //nread = neighborList[n+4*Np]; - Aq[nr5] = a2; - Bq[nr5] = b2; - //............................................... - } + // q = 3 + //nread = neighborList[n+3*Np]; + Aq[nr4] = a1; + Bq[nr4] = b1; + // q = 4 + //nread = neighborList[n+2*Np]; + Aq[nr3] = a2; + Bq[nr3] = b2; + + //............................................... + // q = 4 + // Cq = {0,0,1} + delta = beta * nA * nB * nAB * 0.1111111111111111 * nz; + if (!(nA * nB * nAB > 0)) + delta = 0; + a1 = nA * (0.1111111111111111 * (1 + 4.5 * uz)) + delta; + b1 = nB * (0.1111111111111111 * (1 + 4.5 * uz)) - delta; + a2 = nA * (0.1111111111111111 * (1 - 4.5 * uz)) - delta; + b2 = nB * (0.1111111111111111 * (1 - 4.5 * uz)) + delta; + + // q = 5 + //nread = neighborList[n+5*Np]; + Aq[nr6] = a1; + Bq[nr6] = b1; + // q = 6 + //nread = neighborList[n+4*Np]; + Aq[nr5] = a2; + Bq[nr5] = b2; + //............................................... + } } -extern "C" void ScaLBL_D3Q7_AAodd_DFH(int *neighborList, double *Aq, double *Bq, - double *Den, double *Phi, int start, int finish, int Np) -{ +extern "C" void ScaLBL_D3Q7_AAodd_DFH(int *neighborList, double *Aq, double *Bq, + double *Den, double *Phi, int start, + int finish, int Np) { - for (int n=start; n> FILENAME; - // Line 2: domain size (Nx, Ny, Nz) - input >> Nz; // number of nodes (x,y,z) - input >> nBlocks; - input >> nthreads; - // Line 3: model parameters (tau, alpha, beta, das, dbs) - input >> tau; - input >> alpha; - input >> beta; - input >> das; - input >> dbs; - // Line 4: External force components (Fx,Fy, Fz) - input >> Fx; - input >> Fy; - input >> Fz; - // Line 5: Pressure Boundary conditions - input >> pBC; - input >> din; - input >> dout; - // Line 6: time-stepping criteria - input >> timestepMax; // max no. of timesteps - input >> interval; // error interval - input >> tol; // error tolerance - //............................................................. + if (rank == 0) { + //............................................................. + // READ SIMULATION PARMAETERS FROM INPUT FILE + //............................................................. + ifstream input("Color.in"); + // Line 1: Name of the phase indicator file (s=0,w=1,n=2) + input >> FILENAME; + // Line 2: domain size (Nx, Ny, Nz) + input >> Nz; // number of nodes (x,y,z) + input >> nBlocks; + input >> nthreads; + // Line 3: model parameters (tau, alpha, beta, das, dbs) + input >> tau; + input >> alpha; + input >> beta; + input >> das; + input >> dbs; + // Line 4: External force components (Fx,Fy, Fz) + input >> Fx; + input >> Fy; + input >> Fz; + // Line 5: Pressure Boundary conditions + input >> pBC; + input >> din; + input >> dout; + // Line 6: time-stepping criteria + input >> timestepMax; // max no. of timesteps + input >> interval; // error interval + input >> tol; // error tolerance + //............................................................. - ifstream domain("Domain.in"); - domain >> nprocx; - domain >> nprocy; - domain >> nprocz; - } - // ************************************************************** - // Broadcast simulation parameters from rank 0 to all other procs - MPI_Barrier(comm); - //................................................. - MPI_Bcast(&Nz,1,MPI_INT,0,comm); - MPI_Bcast(&nBlocks,1,MPI_INT,0,comm); - MPI_Bcast(&nthreads,1,MPI_INT,0,comm); - MPI_Bcast(&Fx,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&Fy,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&Fz,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&tau,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&alpha,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&beta,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&das,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&dbs,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&pBC,1,MPI_LOGICAL,0,comm); - MPI_Bcast(&din,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&dout,1,MPI_DOUBLE,0,comm); - MPI_Bcast(×tepMax,1,MPI_INT,0,comm); - MPI_Bcast(&interval,1,MPI_INT,0,comm); - MPI_Bcast(&tol,1,MPI_DOUBLE,0,comm); + ifstream domain("Domain.in"); + domain >> nprocx; + domain >> nprocy; + domain >> nprocz; + } + // ************************************************************** + // Broadcast simulation parameters from rank 0 to all other procs + MPI_Barrier(comm); + //................................................. + MPI_Bcast(&Nz, 1, MPI_INT, 0, comm); + MPI_Bcast(&nBlocks, 1, MPI_INT, 0, comm); + MPI_Bcast(&nthreads, 1, MPI_INT, 0, comm); + MPI_Bcast(&Fx, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&Fy, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&Fz, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&tau, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&alpha, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&beta, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&das, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&dbs, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&pBC, 1, MPI_LOGICAL, 0, comm); + MPI_Bcast(&din, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&dout, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(×tepMax, 1, MPI_INT, 0, comm); + MPI_Bcast(&interval, 1, MPI_INT, 0, comm); + MPI_Bcast(&tol, 1, MPI_DOUBLE, 0, comm); - MPI_Bcast(&nprocx,1,MPI_INT,0,comm); - MPI_Bcast(&nprocy,1,MPI_INT,0,comm); - MPI_Bcast(&nprocz,1,MPI_INT,0,comm); - //................................................. - MPI_Barrier(comm); - // ************************************************************** - // ************************************************************** + MPI_Bcast(&nprocx, 1, MPI_INT, 0, comm); + MPI_Bcast(&nprocy, 1, MPI_INT, 0, comm); + MPI_Bcast(&nprocz, 1, MPI_INT, 0, comm); + //................................................. + MPI_Barrier(comm); + // ************************************************************** + // ************************************************************** - double rlxA = 1.f/tau; - double rlxB = 8.f*(2.f-rlxA)/(8.f-rlxA); + double rlxA = 1.f / tau; + double rlxB = 8.f * (2.f - rlxA) / (8.f - rlxA); - if (nprocs != nprocx*nprocy*nprocz){ - printf("Fatal error in processor number! \n"); - printf("nprocx = %i \n",nprocx); - printf("nprocy = %i \n",nprocy); - printf("nprocz = %i \n",nprocz); - } + if (nprocs != nprocx * nprocy * nprocz) { + printf("Fatal error in processor number! \n"); + printf("nprocx = %i \n", nprocx); + printf("nprocy = %i \n", nprocy); + printf("nprocz = %i \n", nprocz); + } - if (rank==0){ - printf("********************************************************\n"); - printf("tau = %f \n", tau); - printf("alpha = %f \n", alpha); - printf("beta = %f \n", beta); - printf("das = %f \n", das); - printf("dbs = %f \n", dbs); - printf("Force(x) = %f \n", Fx); - printf("Force(y) = %f \n", Fy); - printf("Force(z) = %f \n", Fz); - printf("Sub-domain size = %i x %i x %i\n",Nz,Nz,Nz); - printf("Parallel domain size = %i x %i x %i\n",nprocx,nprocy,nprocz); - printf("********************************************************\n"); + if (rank == 0) { + printf("********************************************************\n"); + printf("tau = %f \n", tau); + printf("alpha = %f \n", alpha); + printf("beta = %f \n", beta); + printf("das = %f \n", das); + printf("dbs = %f \n", dbs); + printf("Force(x) = %f \n", Fx); + printf("Force(y) = %f \n", Fy); + printf("Force(z) = %f \n", Fz); + printf("Sub-domain size = %i x %i x %i\n", Nz, Nz, Nz); + printf("Parallel domain size = %i x %i x %i\n", nprocx, nprocy, nprocz); + printf("********************************************************\n"); + } - } + MPI_Barrier(comm); + kproc = rank / (nprocx * nprocy); + jproc = (rank - nprocx * nprocy * kproc) / nprocx; + iproc = rank - nprocx * nprocy * kproc - nprocz * jproc; - MPI_Barrier(comm); - kproc = rank/(nprocx*nprocy); - jproc = (rank-nprocx*nprocy*kproc)/nprocx; - iproc = rank-nprocx*nprocy*kproc-nprocz*jproc; + //.......................................... + // set up the neighbor ranks + //.......................................... + i = iproc; + j = jproc; + k = kproc; + i += 1; + j += 0; + k += 0; + if (i < 0) + i += nprocx; + if (j < 0) + j += nprocy; + if (k < 0) + k += nprocz; + if (!(i < nprocx)) + i -= nprocx; + if (!(j < nprocy)) + j -= nprocy; + if (!(k < nprocz)) + k -= nprocz; + rank_X = k * nprocx * nprocy + j * nprocx + i; + //.......................................... + i = iproc; + j = jproc; + k = kproc; + i -= 1; + j += 0; + k += 0; + if (i < 0) + i += nprocx; + if (j < 0) + j += nprocy; + if (k < 0) + k += nprocz; + if (!(i < nprocx)) + i -= nprocx; + if (!(j < nprocy)) + j -= nprocy; + if (!(k < nprocz)) + k -= nprocz; + rank_x = k * nprocx * nprocy + j * nprocx + i; + //.......................................... + i = iproc; + j = jproc; + k = kproc; + i += 0; + j += 1; + k += 0; + if (i < 0) + i += nprocx; + if (j < 0) + j += nprocy; + if (k < 0) + k += nprocz; + if (!(i < nprocx)) + i -= nprocx; + if (!(j < nprocy)) + j -= nprocy; + if (!(k < nprocz)) + k -= nprocz; + rank_Y = k * nprocx * nprocy + j * nprocx + i; + //.......................................... + i = iproc; + j = jproc; + k = kproc; + i += 0; + j -= 1; + k += 0; + if (i < 0) + i += nprocx; + if (j < 0) + j += nprocy; + if (k < 0) + k += nprocz; + if (!(i < nprocx)) + i -= nprocx; + if (!(j < nprocy)) + j -= nprocy; + if (!(k < nprocz)) + k -= nprocz; + rank_y = k * nprocx * nprocy + j * nprocx + i; + //.......................................... + i = iproc; + j = jproc; + k = kproc; + i += 0; + j += 0; + k += 1; + if (i < 0) + i += nprocx; + if (j < 0) + j += nprocy; + if (k < 0) + k += nprocz; + if (!(i < nprocx)) + i -= nprocx; + if (!(j < nprocy)) + j -= nprocy; + if (!(k < nprocz)) + k -= nprocz; + rank_Z = k * nprocx * nprocy + j * nprocx + i; + //.......................................... + i = iproc; + j = jproc; + k = kproc; + i += 0; + j += 0; + k -= 1; + if (i < 0) + i += nprocx; + if (j < 0) + j += nprocy; + if (k < 0) + k += nprocz; + if (!(i < nprocx)) + i -= nprocx; + if (!(j < nprocy)) + j -= nprocy; + if (!(k < nprocz)) + k -= nprocz; + rank_z = k * nprocx * nprocy + j * nprocx + i; + //.......................................... + i = iproc; + j = jproc; + k = kproc; + i += 1; + j += 1; + k += 0; + if (i < 0) + i += nprocx; + if (j < 0) + j += nprocy; + if (k < 0) + k += nprocz; + if (!(i < nprocx)) + i -= nprocx; + if (!(j < nprocy)) + j -= nprocy; + if (!(k < nprocz)) + k -= nprocz; + rank_XY = k * nprocx * nprocy + j * nprocx + i; + //.......................................... + i = iproc; + j = jproc; + k = kproc; + i -= 1; + j -= 1; + k += 0; + if (i < 0) + i += nprocx; + if (j < 0) + j += nprocy; + if (k < 0) + k += nprocz; + if (!(i < nprocx)) + i -= nprocx; + if (!(j < nprocy)) + j -= nprocy; + if (!(k < nprocz)) + k -= nprocz; + rank_xy = k * nprocx * nprocy + j * nprocx + i; + //.......................................... + i = iproc; + j = jproc; + k = kproc; + i += 1; + j -= 1; + k += 0; + if (i < 0) + i += nprocx; + if (j < 0) + j += nprocy; + if (k < 0) + k += nprocz; + if (!(i < nprocx)) + i -= nprocx; + if (!(j < nprocy)) + j -= nprocy; + if (!(k < nprocz)) + k -= nprocz; + rank_Xy = k * nprocx * nprocy + j * nprocx + i; + //.......................................... + i = iproc; + j = jproc; + k = kproc; + i -= 1; + j += 1; + k += 0; + if (i < 0) + i += nprocx; + if (j < 0) + j += nprocy; + if (k < 0) + k += nprocz; + if (!(i < nprocx)) + i -= nprocx; + if (!(j < nprocy)) + j -= nprocy; + if (!(k < nprocz)) + k -= nprocz; + rank_xY = k * nprocx * nprocy + j * nprocx + i; + //.......................................... + i = iproc; + j = jproc; + k = kproc; + i += 1; + j += 0; + k += 1; + if (i < 0) + i += nprocx; + if (j < 0) + j += nprocy; + if (k < 0) + k += nprocz; + if (!(i < nprocx)) + i -= nprocx; + if (!(j < nprocy)) + j -= nprocy; + if (!(k < nprocz)) + k -= nprocz; + rank_XZ = k * nprocx * nprocy + j * nprocx + i; + //.......................................... + i = iproc; + j = jproc; + k = kproc; + i -= 1; + j += 0; + k -= 1; + if (i < 0) + i += nprocx; + if (j < 0) + j += nprocy; + if (k < 0) + k += nprocz; + if (!(i < nprocx)) + i -= nprocx; + if (!(j < nprocy)) + j -= nprocy; + if (!(k < nprocz)) + k -= nprocz; + rank_xz = k * nprocx * nprocy + j * nprocx + i; + //.......................................... + i = iproc; + j = jproc; + k = kproc; + i -= 1; + j += 0; + k += 1; + if (i < 0) + i += nprocx; + if (j < 0) + j += nprocy; + if (k < 0) + k += nprocz; + if (!(i < nprocx)) + i -= nprocx; + if (!(j < nprocy)) + j -= nprocy; + if (!(k < nprocz)) + k -= nprocz; + rank_xZ = k * nprocx * nprocy + j * nprocx + i; + //.......................................... + i = iproc; + j = jproc; + k = kproc; + i += 1; + j += 0; + k -= 1; + if (i < 0) + i += nprocx; + if (j < 0) + j += nprocy; + if (k < 0) + k += nprocz; + if (!(i < nprocx)) + i -= nprocx; + if (!(j < nprocy)) + j -= nprocy; + if (!(k < nprocz)) + k -= nprocz; + rank_Xz = k * nprocx * nprocy + j * nprocx + i; + //.......................................... + i = iproc; + j = jproc; + k = kproc; + i += 0; + j += 1; + k += 1; + if (i < 0) + i += nprocx; + if (j < 0) + j += nprocy; + if (k < 0) + k += nprocz; + if (!(i < nprocx)) + i -= nprocx; + if (!(j < nprocy)) + j -= nprocy; + if (!(k < nprocz)) + k -= nprocz; + rank_YZ = k * nprocx * nprocy + j * nprocx + i; + //.......................................... + i = iproc; + j = jproc; + k = kproc; + i += 0; + j -= 1; + k -= 1; + if (i < 0) + i += nprocx; + if (j < 0) + j += nprocy; + if (k < 0) + k += nprocz; + if (!(i < nprocx)) + i -= nprocx; + if (!(j < nprocy)) + j -= nprocy; + if (!(k < nprocz)) + k -= nprocz; + rank_yz = k * nprocx * nprocy + j * nprocx + i; + //.......................................... + i = iproc; + j = jproc; + k = kproc; + i += 0; + j -= 1; + k += 1; + if (i < 0) + i += nprocx; + if (j < 0) + j += nprocy; + if (k < 0) + k += nprocz; + if (!(i < nprocx)) + i -= nprocx; + if (!(j < nprocy)) + j -= nprocy; + if (!(k < nprocz)) + k -= nprocz; + rank_yZ = k * nprocx * nprocy + j * nprocx + i; + //.......................................... + i = iproc; + j = jproc; + k = kproc; + i += 0; + j += 1; + k -= 1; + if (i < 0) + i += nprocx; + if (j < 0) + j += nprocy; + if (k < 0) + k += nprocz; + if (!(i < nprocx)) + i -= nprocx; + if (!(j < nprocy)) + j -= nprocy; + if (!(k < nprocz)) + k -= nprocz; + rank_Yz = k * nprocx * nprocy + j * nprocx + i; + //.......................................... - //.......................................... - // set up the neighbor ranks - //.......................................... - i=iproc; j=jproc; k =kproc; - i+=1; - j+=0; - k+=0; - if (i<0) i+=nprocx; - if (j<0) j+=nprocy; - if (k<0) k+=nprocz; - if (!(i 0) sum++; - } - } - } - PM.close(); -// printf("File porosity = %f\n", double(sum)/N); - //........................................................................... - MPI_Barrier(comm); - if (rank == 0) cout << "Domain set." << endl; - //........................................................................... - // Write the communcation structure into a file for debugging -/* char LocalCommFile[40]; + //....................................................................... + if (rank == 0) + printf("Read input media... \n"); + //....................................................................... + char LocalRankString[8]; + char LocalRankFilename[40]; + sprintf(LocalRankString, "%05d", rank); + sprintf(LocalRankFilename, "%s%s", "ID.", LocalRankString); + // printf("Local File Name = %s \n",LocalRankFilename); + // .......... READ THE INPUT FILE ....................................... + char value; + char *id; + id = new char[N]; + int sum = 0; + // double porosity; + //....................................................................... + ifstream PM(LocalRankFilename, ios::binary); + for (k = 0; k < Nz; k++) { + for (j = 0; j < Ny; j++) { + for (i = 0; i < Nx; i++) { + n = k * Nx * Ny + j * Nx + i; + id[n] = 0; + } + } + } + for (k = 1; k < Nz - 1; k++) { + for (j = 1; j < Ny - 1; j++) { + for (i = 1; i < Nx - 1; i++) { + PM.read((char *)(&value), sizeof(value)); + n = k * Nx * Ny + j * Nx + i; + id[n] = value; + if (value > 0) + sum++; + } + } + } + PM.close(); + // printf("File porosity = %f\n", double(sum)/N); + //........................................................................... + MPI_Barrier(comm); + if (rank == 0) + cout << "Domain set." << endl; + //........................................................................... + // Write the communcation structure into a file for debugging + /* char LocalCommFile[40]; sprintf(LocalCommFile,"%s%s","Comm.",LocalRankString); FILE *CommFile; CommFile = fopen(LocalCommFile,"w"); @@ -483,427 +634,580 @@ int main(int argc, char **argv) fclose(CommFile); */ //........................................................................... - // Set up MPI communication structures - if (rank==0) printf ("Setting up communication control structures \n"); - //...................................................................................... - // Get the actual D3Q19 communication counts (based on location of solid phase) - // Discrete velocity set symmetry implies the sendcount = recvcount - int sendCount_x, sendCount_y, sendCount_z, sendCount_X, sendCount_Y, sendCount_Z; - int sendCount_xy, sendCount_yz, sendCount_xz, sendCount_Xy, sendCount_Yz, sendCount_xZ; - int sendCount_xY, sendCount_yZ, sendCount_Xz, sendCount_XY, sendCount_YZ, sendCount_XZ; - sendCount_x = sendCount_y = sendCount_z = sendCount_X = sendCount_Y = sendCount_Z = 0; - sendCount_xy = sendCount_yz = sendCount_xz = sendCount_Xy = sendCount_Yz = sendCount_xZ = 0; - sendCount_xY = sendCount_yZ = sendCount_Xz = sendCount_XY = sendCount_YZ = sendCount_XZ = 0; - //...................................................................................... - for (k=0; k #include - #include "pmmc.h" #include "Domain.h" #include "Extras.h" @@ -14,606 +13,782 @@ using namespace std; - //************************************************************************* // Implementation of Two-Phase Immiscible LBM using CUDA //************************************************************************* -inline void PackID(int *list, int count, char *sendbuf, char *ID){ - // Fill in the phase ID values from neighboring processors - // This packs up the values that need to be sent from one processor to another - int idx,n; +inline void PackID(int *list, int count, char *sendbuf, char *ID) { + // Fill in the phase ID values from neighboring processors + // This packs up the values that need to be sent from one processor to another + int idx, n; - for (idx=0; idx> FILENAME; - // Line 2: domain size (Nx, Ny, Nz) -// input >> Nz; // number of nodes (x,y,z) -// input >> nBlocks; -// input >> nthreads; - // Line 3: model parameters (tau, alpha, beta, das, dbs) - input >> tau; // Viscosity parameter - input >> alpha; // Surface Tension parameter - input >> beta; // Width of the interface - input >> xIntPos; // Contact angle parameter -// input >> das; -// input >> dbs; - // Line 4: wetting phase saturation to initialize - input >> wp_saturation; - // Line 5: External force components (Fx,Fy, Fz) - input >> Fx; - input >> Fy; - input >> Fz; - // Line 6: Pressure Boundary conditions - input >> Restart; - input >> pBC; - input >> din; - input >> dout; - // Line 7: time-stepping criteria - input >> timestepMax; // max no. of timesteps - input >> interval; // error interval - input >> tol; // error tolerance - //............................................................. - das = 0.1; dbs = 0.9; // hard coded for density initialization - // should be OK to remove these parameters - // they should have no impact with the - // current boundary condition - //....................................................................... - // Reading the domain information file - //....................................................................... - ifstream domain("Domain.in"); - domain >> nprocx; - domain >> nprocy; - domain >> nprocz; - domain >> Nx; - domain >> Ny; - domain >> Nz; - domain >> nspheres; - domain >> Lx; - domain >> Ly; - domain >> Lz; - //....................................................................... - } - // ************************************************************** - // Broadcast simulation parameters from rank 0 to all other procs - MPI_Barrier(comm); - //................................................. - MPI_Bcast(&tau,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&alpha,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&beta,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&das,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&dbs,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&xIntPos,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&wp_saturation,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&pBC,1,MPI_LOGICAL,0,comm); - MPI_Bcast(&Restart,1,MPI_LOGICAL,0,comm); - MPI_Bcast(&din,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&dout,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&Fx,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&Fy,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&Fz,1,MPI_DOUBLE,0,comm); - MPI_Bcast(×tepMax,1,MPI_INT,0,comm); - MPI_Bcast(&interval,1,MPI_INT,0,comm); - MPI_Bcast(&tol,1,MPI_DOUBLE,0,comm); - // Computational domain - MPI_Bcast(&Nz,1,MPI_INT,0,comm); -// MPI_Bcast(&nBlocks,1,MPI_INT,0,comm); -// MPI_Bcast(&nthreads,1,MPI_INT,0,comm); - MPI_Bcast(&nprocx,1,MPI_INT,0,comm); - MPI_Bcast(&nprocy,1,MPI_INT,0,comm); - MPI_Bcast(&nprocz,1,MPI_INT,0,comm); - MPI_Bcast(&nspheres,1,MPI_INT,0,comm); - MPI_Bcast(&Lx,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&Ly,1,MPI_DOUBLE,0,comm); - MPI_Bcast(&Lz,1,MPI_DOUBLE,0,comm); - //................................................. - MPI_Barrier(comm); - // ************************************************************** - // ************************************************************** - double Ps = -(das-dbs)/(das+dbs); - double rlxA = 1.f/tau; - double rlxB = 8.f*(2.f-rlxA)/(8.f-rlxA); + // pmmc threshold values + double fluid_isovalue, solid_isovalue; + fluid_isovalue = 0.0; + solid_isovalue = 0.0; + nBlocks = 32; + nthreads = 128; - if (nprocs != nprocx*nprocy*nprocz){ - printf("Fatal error in processor number! \n"); - printf("nprocx = %i \n",nprocx); - printf("nprocy = %i \n",nprocy); - printf("nprocz = %i \n",nprocz); - } + int RESTART_INTERVAL = 1000; - if (rank==0){ - printf("********************************************************\n"); - printf("tau = %f \n", tau); - printf("alpha = %f \n", alpha); - printf("beta = %f \n", beta); - printf("das = %f \n", das); - printf("dbs = %f \n", dbs); - printf("phi_s = %f \n", Ps); - printf("gamma_{wn} = %f \n", 6.01603*alpha); - printf("cos theta_c = %f \n", 1.05332*Ps); - printf("Force(x) = %f \n", Fx); - printf("Force(y) = %f \n", Fy); - printf("Force(z) = %f \n", Fz); - printf("Sub-domain size = %i x %i x %i\n",Nz,Nz,Nz); - printf("Parallel domain size = %i x %i x %i\n",nprocx,nprocy,nprocz); - printf("********************************************************\n"); - } + if (rank == 0) { + //............................................................. + // READ SIMULATION PARMAETERS FROM INPUT FILE + //............................................................. + ifstream input("Color.in"); + // Line 1: Name of the phase indicator file (s=0,w=1,n=2) + // input >> FILENAME; + // Line 2: domain size (Nx, Ny, Nz) + // input >> Nz; // number of nodes (x,y,z) + // input >> nBlocks; + // input >> nthreads; + // Line 3: model parameters (tau, alpha, beta, das, dbs) + input >> tau; // Viscosity parameter + input >> alpha; // Surface Tension parameter + input >> beta; // Width of the interface + input >> xIntPos; // Contact angle parameter + // input >> das; + // input >> dbs; + // Line 4: wetting phase saturation to initialize + input >> wp_saturation; + // Line 5: External force components (Fx,Fy, Fz) + input >> Fx; + input >> Fy; + input >> Fz; + // Line 6: Pressure Boundary conditions + input >> Restart; + input >> pBC; + input >> din; + input >> dout; + // Line 7: time-stepping criteria + input >> timestepMax; // max no. of timesteps + input >> interval; // error interval + input >> tol; // error tolerance + //............................................................. + das = 0.1; + dbs = 0.9; // hard coded for density initialization + // should be OK to remove these parameters + // they should have no impact with the + // current boundary condition + //....................................................................... + // Reading the domain information file + //....................................................................... + ifstream domain("Domain.in"); + domain >> nprocx; + domain >> nprocy; + domain >> nprocz; + domain >> Nx; + domain >> Ny; + domain >> Nz; + domain >> nspheres; + domain >> Lx; + domain >> Ly; + domain >> Lz; + //....................................................................... + } + // ************************************************************** + // Broadcast simulation parameters from rank 0 to all other procs + MPI_Barrier(comm); + //................................................. + MPI_Bcast(&tau, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&alpha, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&beta, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&das, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&dbs, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&xIntPos, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&wp_saturation, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&pBC, 1, MPI_LOGICAL, 0, comm); + MPI_Bcast(&Restart, 1, MPI_LOGICAL, 0, comm); + MPI_Bcast(&din, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&dout, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&Fx, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&Fy, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&Fz, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(×tepMax, 1, MPI_INT, 0, comm); + MPI_Bcast(&interval, 1, MPI_INT, 0, comm); + MPI_Bcast(&tol, 1, MPI_DOUBLE, 0, comm); + // Computational domain + MPI_Bcast(&Nz, 1, MPI_INT, 0, comm); + // MPI_Bcast(&nBlocks,1,MPI_INT,0,comm); + // MPI_Bcast(&nthreads,1,MPI_INT,0,comm); + MPI_Bcast(&nprocx, 1, MPI_INT, 0, comm); + MPI_Bcast(&nprocy, 1, MPI_INT, 0, comm); + MPI_Bcast(&nprocz, 1, MPI_INT, 0, comm); + MPI_Bcast(&nspheres, 1, MPI_INT, 0, comm); + MPI_Bcast(&Lx, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&Ly, 1, MPI_DOUBLE, 0, comm); + MPI_Bcast(&Lz, 1, MPI_DOUBLE, 0, comm); + //................................................. + MPI_Barrier(comm); + // ************************************************************** + // ************************************************************** + double Ps = -(das - dbs) / (das + dbs); + double rlxA = 1.f / tau; + double rlxB = 8.f * (2.f - rlxA) / (8.f - rlxA); - MPI_Barrier(comm); - kproc = rank/(nprocx*nprocy); - jproc = (rank-nprocx*nprocy*kproc)/nprocx; - iproc = rank-nprocx*nprocy*kproc-nprocz*jproc; + if (nprocs != nprocx * nprocy * nprocz) { + printf("Fatal error in processor number! \n"); + printf("nprocx = %i \n", nprocx); + printf("nprocy = %i \n", nprocy); + printf("nprocz = %i \n", nprocz); + } - //.......................................... - // set up the neighbor ranks - //.......................................... - i=iproc; j=jproc; k =kproc; - i+=1; - j+=0; - k+=0; - if (i<0) i+=nprocx; - if (j<0) j+=nprocy; - if (k<0) k+=nprocz; - if (!(i 0.0){ - id[n] = 1; - sum++; - } - } - } - } - sum_local = 1.0*sum; - MPI_Allreduce(&sum_local,&porosity,1,MPI_DOUBLE,MPI_SUM,comm); - porosity = porosity*iVol_global; - if (rank==0) printf("Media porosity = %f \n",porosity); + //....................................................................... + // Assign the phase ID field based on the signed distance + //....................................................................... + for (k = 0; k < Nz; k++) { + for (j = 0; j < Ny; j++) { + for (i = 0; i < Nx; i++) { + n = k * Nx * Ny + j * Nx + i; + id[n] = 0; + } + } + } + sum = 0; + for (k = 1; k < Nz - 1; k++) { + for (j = 1; j < Ny - 1; j++) { + for (i = 1; i < Nx - 1; i++) { + n = k * Nx * Ny + j * Nx + i; + if (SignDist.data[n] > 0.0) { + id[n] = 1; + sum++; + } + } + } + } + sum_local = 1.0 * sum; + MPI_Allreduce(&sum_local, &porosity, 1, MPI_DOUBLE, MPI_SUM, comm); + porosity = porosity * iVol_global; + if (rank == 0) + printf("Media porosity = %f \n", porosity); - // Generate the residual NWP - if (rank==0) printf("Initializing with NWP saturation = %f \n",wp_saturation); - GenerateResidual(id,Nx,Ny,Nz,wp_saturation); + // Generate the residual NWP + if (rank == 0) + printf("Initializing with NWP saturation = %f \n", wp_saturation); + GenerateResidual(id, Nx, Ny, Nz, wp_saturation); #endif - - double BubbleRadius = 15.5; // Radius of the capillary tube - sum=0; - for (k=0;k fluid_isovalue) - int cube[8][3] = {{0,0,0},{1,0,0},{0,1,0},{1,1,0},{0,0,1},{1,0,1},{0,1,1},{1,1,1}}; // cube corners - DoubleArray CubeValues(2,2,2); -// int count_in=0,count_out=0; -// int nodx,nody,nodz; - // initialize lists for vertices for surfaces, common line - DTMutableList nw_pts(20); - DTMutableList ns_pts(20); - DTMutableList ws_pts(20); - DTMutableList nws_pts(20); - // initialize triangle lists for surfaces - IntArray nw_tris(3,20); - IntArray ns_tris(3,20); - IntArray ws_tris(3,20); - // initialize list for line segments - IntArray nws_seg(2,20); - DTMutableList tmp(20); - DoubleArray Values(20); - DoubleArray ContactAngle(20); - DoubleArray Curvature(20); - DoubleArray InterfaceSpeed(20); - DoubleArray NormalVector(60); - - // IntArray store; - - int n_nw_pts=0,n_ns_pts=0,n_ws_pts=0,n_nws_pts=0; - int n_nw_tris=0, n_ns_tris=0, n_ws_tris=0, n_nws_seg=0; - -// double s,s1,s2,s3; // Triangle sides (lengths) - Point A,B,C,P; -// double area; - - // Initialize arrays for local solid surface - DTMutableList local_sol_pts(20); - int n_local_sol_pts = 0; - IntArray local_sol_tris(3,18); - int n_local_sol_tris; - DoubleArray values(20); - DTMutableList local_nws_pts(20); - int n_local_nws_pts; - - //int n_nw_tris_beg, n_ns_tris_beg, n_ws_tris_beg; - int c; - //int newton_steps = 0; - //........................................................................... - int ncubes = (Nx-2)*(Ny-2)*(Nz-2); // Exclude the "upper" halo - IntArray cubeList(3,ncubes); - int nc=0; - //........................................................................... - // Set up the cube list (very regular in this case due to lack of blob-ID) - for (k=0; k fluid_isovalue) + int cube[8][3] = {{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {1, 1, 0}, {0, 0, 1}, + {1, 0, 1}, {0, 1, 1}, {1, 1, 1}}; // cube corners + DoubleArray CubeValues(2, 2, 2); + // int count_in=0,count_out=0; + // int nodx,nody,nodz; + // initialize lists for vertices for surfaces, common line + DTMutableList nw_pts(20); + DTMutableList ns_pts(20); + DTMutableList ws_pts(20); + DTMutableList nws_pts(20); + // initialize triangle lists for surfaces + IntArray nw_tris(3, 20); + IntArray ns_tris(3, 20); + IntArray ws_tris(3, 20); + // initialize list for line segments + IntArray nws_seg(2, 20); + DTMutableList tmp(20); + DoubleArray Values(20); + DoubleArray ContactAngle(20); + DoubleArray Curvature(20); + DoubleArray InterfaceSpeed(20); + DoubleArray NormalVector(60); - //.......create a stream for the LB calculation....... - // cudaStream_t stream; - // cudaStreamCreate(&stream); + // IntArray store; - //.......create and start timer............ - double starttime,stoptime,cputime; - MPI_Barrier(comm); - starttime = MPI_Wtime(); - //......................................... - //........................................................................... - // MAIN VARIABLES INITIALIZED HERE - //........................................................................... - double BubRad[5]; - BubRad[0] = 8.0; - BubRad[1] = 10.0; - BubRad[2] = 12.0; - BubRad[3] = 15.0; - BubRad[4] = 20.0; - //........................................................................... - if (rank==0){ - printf("--------------------------------------------------------------------------------------\n"); - printf("radius "); // Timestep, Change in Surface Energy - printf("sw pw pn awn Jwn "); // Scalar averages - printf("Gwn [xx, yy, zz, xy, xz, yz] "); // Orientation tensors - printf("--------------------------------------------------------------------------------------\n"); - } + int n_nw_pts = 0, n_ns_pts = 0, n_ws_pts = 0, n_nws_pts = 0; + int n_nw_tris = 0, n_ns_tris = 0, n_ws_tris = 0, n_nws_seg = 0; - for (int bubbleCount=0; bubbleCount<5; bubbleCount++){ + // double s,s1,s2,s3; // Triangle sides (lengths) + Point A, B, C, P; + // double area; - BubbleRadius = BubRad[bubbleCount]; // Radius of the current bubble + // Initialize arrays for local solid surface + DTMutableList local_sol_pts(20); + int n_local_sol_pts = 0; + IntArray local_sol_tris(3, 18); + int n_local_sol_tris; + DoubleArray values(20); + DTMutableList local_nws_pts(20); + int n_local_nws_pts; - // Initialize the bubble - for (k=0;k CPU - //........................................................................... - dvc_Barrier(); - dvc_ComputePressureD3Q19(ID,f_even,f_odd,Pressure,Nx,Ny,Nz,S); - dvc_CopyToHost(Phase.data,Phi,N*sizeof(double)); - dvc_CopyToHost(Press.data,Pressure,N*sizeof(double)); - dvc_CopyToHost(Vel,Velocity,3*N*sizeof(double)); - MPI_Barrier(comm); - //........................................................................... + //........................................................................... + // Copy the phase indicator field for the earlier timestep + dvc_Barrier(); + dvc_CopyToHost(Phase_tplus.data, Phi, N * sizeof(double)); + //........................................................................... + //........................................................................... + // Copy the data for for the analysis timestep + //........................................................................... + // Copy the phase from the GPU -> CPU + //........................................................................... + dvc_Barrier(); + dvc_ComputePressureD3Q19(ID, f_even, f_odd, Pressure, Nx, Ny, Nz, S); + dvc_CopyToHost(Phase.data, Phi, N * sizeof(double)); + dvc_CopyToHost(Press.data, Pressure, N * sizeof(double)); + dvc_CopyToHost(Vel, Velocity, 3 * N * sizeof(double)); + MPI_Barrier(comm); + //........................................................................... - timestep=0; + timestep = 0; - sendtag = recvtag = 5; + sendtag = recvtag = 5; - //************ MAIN ITERATION LOOP ***************************************/ - while (timestep < timestepMax){ + //************ MAIN ITERATION LOOP ***************************************/ + while (timestep < timestepMax) { - //************************************************************************* - // Compute the color gradient - //************************************************************************* - //dvc_ComputeColorGradient(nBlocks, nthreads, S, - // ID, Phi, ColorGrad, Nx, Ny, Nz); - //************************************************************************* + //************************************************************************* + // Compute the color gradient + //************************************************************************* + //dvc_ComputeColorGradient(nBlocks, nthreads, S, + // ID, Phi, ColorGrad, Nx, Ny, Nz); + //************************************************************************* - //************************************************************************* - // Perform collision step for the momentum transport - //************************************************************************* - // dvc_ColorCollide(nBlocks, nthreads, S, ID, f_even, f_odd, ColorGrad, Velocity, - // rlxA, rlxB,alpha, beta, Fx, Fy, Fz, Nx, Ny, Nz, pBC); - //************************************************************************* + //************************************************************************* + // Perform collision step for the momentum transport + //************************************************************************* + // dvc_ColorCollide(nBlocks, nthreads, S, ID, f_even, f_odd, ColorGrad, Velocity, + // rlxA, rlxB,alpha, beta, Fx, Fy, Fz, Nx, Ny, Nz, pBC); + //************************************************************************* - //************************************************************************* - // Fused Color Gradient and Collision - //************************************************************************* - dvc_ColorCollideOpt( ID,f_even,f_odd,Phi,ColorGrad, - Velocity,Nx,Ny,Nz,S,rlxA,rlxB,alpha,beta,Fx,Fy,Fz); - //************************************************************************* + //************************************************************************* + // Fused Color Gradient and Collision + //************************************************************************* + dvc_ColorCollideOpt(ID, f_even, f_odd, Phi, ColorGrad, Velocity, Nx, + Ny, Nz, S, rlxA, rlxB, alpha, beta, Fx, Fy, Fz); + //************************************************************************* - //................................................................................... - dvc_PackDist(1,dvcSendList_x,0,sendCount_x,sendbuf_x,f_even,N); - dvc_PackDist(4,dvcSendList_x,sendCount_x,sendCount_x,sendbuf_x,f_even,N); - dvc_PackDist(5,dvcSendList_x,2*sendCount_x,sendCount_x,sendbuf_x,f_even,N); - dvc_PackDist(6,dvcSendList_x,3*sendCount_x,sendCount_x,sendbuf_x,f_even,N); - dvc_PackDist(7,dvcSendList_x,4*sendCount_x,sendCount_x,sendbuf_x,f_even,N); - //...Packing for X face(1,7,9,11,13)................................ - dvc_PackDist(0,dvcSendList_X,0,sendCount_X,sendbuf_X,f_odd,N); - dvc_PackDist(3,dvcSendList_X,sendCount_X,sendCount_X,sendbuf_X,f_odd,N); - dvc_PackDist(4,dvcSendList_X,2*sendCount_X,sendCount_X,sendbuf_X,f_odd,N); - dvc_PackDist(5,dvcSendList_X,3*sendCount_X,sendCount_X,sendbuf_X,f_odd,N); - dvc_PackDist(6,dvcSendList_X,4*sendCount_X,sendCount_X,sendbuf_X,f_odd,N); - //...Packing for y face(4,8,9,16,18)................................. - dvc_PackDist(2,dvcSendList_y,0,sendCount_y,sendbuf_y,f_even,N); - dvc_PackDist(4,dvcSendList_y,sendCount_y,sendCount_y,sendbuf_y,f_even,N); - dvc_PackDist(4,dvcSendList_y,2*sendCount_y,sendCount_y,sendbuf_y,f_odd,N); - dvc_PackDist(8,dvcSendList_y,3*sendCount_y,sendCount_y,sendbuf_y,f_even,N); - dvc_PackDist(9,dvcSendList_y,4*sendCount_y,sendCount_y,sendbuf_y,f_even,N); - //...Packing for Y face(3,7,10,15,17)................................. - dvc_PackDist(1,dvcSendList_Y,0,sendCount_Y,sendbuf_Y,f_odd,N); - dvc_PackDist(3,dvcSendList_Y,sendCount_Y,sendCount_Y,sendbuf_Y,f_odd,N); - dvc_PackDist(5,dvcSendList_Y,2*sendCount_Y,sendCount_Y,sendbuf_Y,f_even,N); - dvc_PackDist(7,dvcSendList_Y,3*sendCount_Y,sendCount_Y,sendbuf_Y,f_odd,N); - dvc_PackDist(8,dvcSendList_Y,4*sendCount_Y,sendCount_Y,sendbuf_Y,f_odd,N); - //...Packing for z face(6,12,13,16,17)................................ - dvc_PackDist(3,dvcSendList_z,0,sendCount_z,sendbuf_z,f_even,N); - dvc_PackDist(6,dvcSendList_z,sendCount_z,sendCount_z,sendbuf_z,f_even,N); - dvc_PackDist(6,dvcSendList_z,2*sendCount_z,sendCount_z,sendbuf_z,f_odd,N); - dvc_PackDist(8,dvcSendList_z,3*sendCount_z,sendCount_z,sendbuf_z,f_even,N); - dvc_PackDist(8,dvcSendList_z,4*sendCount_z,sendCount_z,sendbuf_z,f_odd,N); - //...Packing for Z face(5,11,14,15,18)................................ - dvc_PackDist(2,dvcSendList_Z,0,sendCount_Z,sendbuf_Z,f_odd,N); - dvc_PackDist(5,dvcSendList_Z,sendCount_Z,sendCount_Z,sendbuf_Z,f_odd,N); - dvc_PackDist(7,dvcSendList_Z,2*sendCount_Z,sendCount_Z,sendbuf_Z,f_even,N); - dvc_PackDist(7,dvcSendList_Z,3*sendCount_Z,sendCount_Z,sendbuf_Z,f_odd,N); - dvc_PackDist(9,dvcSendList_Z,4*sendCount_Z,sendCount_Z,sendbuf_Z,f_even,N); - //...Pack the xy edge (8)................................ - dvc_PackDist(4,dvcSendList_xy,0,sendCount_xy,sendbuf_xy,f_even,N); - //...Pack the Xy edge (9)................................ - dvc_PackDist(4,dvcSendList_Xy,0,sendCount_Xy,sendbuf_Xy,f_odd,N); - //...Pack the xY edge (10)................................ - dvc_PackDist(5,dvcSendList_xY,0,sendCount_xY,sendbuf_xY,f_even,N); - //...Pack the XY edge (7)................................ - dvc_PackDist(3,dvcSendList_XY,0,sendCount_XY,sendbuf_XY,f_odd,N); - //...Pack the xz edge (12)................................ - dvc_PackDist(6,dvcSendList_xz,0,sendCount_xz,sendbuf_xz,f_even,N); - //...Pack the xZ edge (14)................................ - dvc_PackDist(7,dvcSendList_xZ,0,sendCount_xZ,sendbuf_xZ,f_even,N); - //...Pack the Xz edge (13)................................ - dvc_PackDist(6,dvcSendList_Xz,0,sendCount_Xz,sendbuf_Xz,f_odd,N); - //...Pack the XZ edge (11)................................ - dvc_PackDist(5,dvcSendList_XZ,0,sendCount_XZ,sendbuf_XZ,f_odd,N); - //...Pack the xz edge (12)................................ - //...Pack the yz edge (16)................................ - dvc_PackDist(8,dvcSendList_yz,0,sendCount_yz,sendbuf_yz,f_even,N); - //...Pack the yZ edge (18)................................ - dvc_PackDist(9,dvcSendList_yZ,0,sendCount_yZ,sendbuf_yZ,f_even,N); - //...Pack the Yz edge (17)................................ - dvc_PackDist(8,dvcSendList_Yz,0,sendCount_Yz,sendbuf_Yz,f_odd,N); - //...Pack the YZ edge (15)................................ - dvc_PackDist(7,dvcSendList_YZ,0,sendCount_YZ,sendbuf_YZ,f_odd,N); - //................................................................................... + //................................................................................... + dvc_PackDist(1, dvcSendList_x, 0, sendCount_x, sendbuf_x, f_even, + N); + dvc_PackDist(4, dvcSendList_x, sendCount_x, sendCount_x, sendbuf_x, + f_even, N); + dvc_PackDist(5, dvcSendList_x, 2 * sendCount_x, sendCount_x, + sendbuf_x, f_even, N); + dvc_PackDist(6, dvcSendList_x, 3 * sendCount_x, sendCount_x, + sendbuf_x, f_even, N); + dvc_PackDist(7, dvcSendList_x, 4 * sendCount_x, sendCount_x, + sendbuf_x, f_even, N); + //...Packing for X face(1,7,9,11,13)................................ + dvc_PackDist(0, dvcSendList_X, 0, sendCount_X, sendbuf_X, f_odd, N); + dvc_PackDist(3, dvcSendList_X, sendCount_X, sendCount_X, sendbuf_X, + f_odd, N); + dvc_PackDist(4, dvcSendList_X, 2 * sendCount_X, sendCount_X, + sendbuf_X, f_odd, N); + dvc_PackDist(5, dvcSendList_X, 3 * sendCount_X, sendCount_X, + sendbuf_X, f_odd, N); + dvc_PackDist(6, dvcSendList_X, 4 * sendCount_X, sendCount_X, + sendbuf_X, f_odd, N); + //...Packing for y face(4,8,9,16,18)................................. + dvc_PackDist(2, dvcSendList_y, 0, sendCount_y, sendbuf_y, f_even, + N); + dvc_PackDist(4, dvcSendList_y, sendCount_y, sendCount_y, sendbuf_y, + f_even, N); + dvc_PackDist(4, dvcSendList_y, 2 * sendCount_y, sendCount_y, + sendbuf_y, f_odd, N); + dvc_PackDist(8, dvcSendList_y, 3 * sendCount_y, sendCount_y, + sendbuf_y, f_even, N); + dvc_PackDist(9, dvcSendList_y, 4 * sendCount_y, sendCount_y, + sendbuf_y, f_even, N); + //...Packing for Y face(3,7,10,15,17)................................. + dvc_PackDist(1, dvcSendList_Y, 0, sendCount_Y, sendbuf_Y, f_odd, N); + dvc_PackDist(3, dvcSendList_Y, sendCount_Y, sendCount_Y, sendbuf_Y, + f_odd, N); + dvc_PackDist(5, dvcSendList_Y, 2 * sendCount_Y, sendCount_Y, + sendbuf_Y, f_even, N); + dvc_PackDist(7, dvcSendList_Y, 3 * sendCount_Y, sendCount_Y, + sendbuf_Y, f_odd, N); + dvc_PackDist(8, dvcSendList_Y, 4 * sendCount_Y, sendCount_Y, + sendbuf_Y, f_odd, N); + //...Packing for z face(6,12,13,16,17)................................ + dvc_PackDist(3, dvcSendList_z, 0, sendCount_z, sendbuf_z, f_even, + N); + dvc_PackDist(6, dvcSendList_z, sendCount_z, sendCount_z, sendbuf_z, + f_even, N); + dvc_PackDist(6, dvcSendList_z, 2 * sendCount_z, sendCount_z, + sendbuf_z, f_odd, N); + dvc_PackDist(8, dvcSendList_z, 3 * sendCount_z, sendCount_z, + sendbuf_z, f_even, N); + dvc_PackDist(8, dvcSendList_z, 4 * sendCount_z, sendCount_z, + sendbuf_z, f_odd, N); + //...Packing for Z face(5,11,14,15,18)................................ + dvc_PackDist(2, dvcSendList_Z, 0, sendCount_Z, sendbuf_Z, f_odd, N); + dvc_PackDist(5, dvcSendList_Z, sendCount_Z, sendCount_Z, sendbuf_Z, + f_odd, N); + dvc_PackDist(7, dvcSendList_Z, 2 * sendCount_Z, sendCount_Z, + sendbuf_Z, f_even, N); + dvc_PackDist(7, dvcSendList_Z, 3 * sendCount_Z, sendCount_Z, + sendbuf_Z, f_odd, N); + dvc_PackDist(9, dvcSendList_Z, 4 * sendCount_Z, sendCount_Z, + sendbuf_Z, f_even, N); + //...Pack the xy edge (8)................................ + dvc_PackDist(4, dvcSendList_xy, 0, sendCount_xy, sendbuf_xy, f_even, + N); + //...Pack the Xy edge (9)................................ + dvc_PackDist(4, dvcSendList_Xy, 0, sendCount_Xy, sendbuf_Xy, f_odd, + N); + //...Pack the xY edge (10)................................ + dvc_PackDist(5, dvcSendList_xY, 0, sendCount_xY, sendbuf_xY, f_even, + N); + //...Pack the XY edge (7)................................ + dvc_PackDist(3, dvcSendList_XY, 0, sendCount_XY, sendbuf_XY, f_odd, + N); + //...Pack the xz edge (12)................................ + dvc_PackDist(6, dvcSendList_xz, 0, sendCount_xz, sendbuf_xz, f_even, + N); + //...Pack the xZ edge (14)................................ + dvc_PackDist(7, dvcSendList_xZ, 0, sendCount_xZ, sendbuf_xZ, f_even, + N); + //...Pack the Xz edge (13)................................ + dvc_PackDist(6, dvcSendList_Xz, 0, sendCount_Xz, sendbuf_Xz, f_odd, + N); + //...Pack the XZ edge (11)................................ + dvc_PackDist(5, dvcSendList_XZ, 0, sendCount_XZ, sendbuf_XZ, f_odd, + N); + //...Pack the xz edge (12)................................ + //...Pack the yz edge (16)................................ + dvc_PackDist(8, dvcSendList_yz, 0, sendCount_yz, sendbuf_yz, f_even, + N); + //...Pack the yZ edge (18)................................ + dvc_PackDist(9, dvcSendList_yZ, 0, sendCount_yZ, sendbuf_yZ, f_even, + N); + //...Pack the Yz edge (17)................................ + dvc_PackDist(8, dvcSendList_Yz, 0, sendCount_Yz, sendbuf_Yz, f_odd, + N); + //...Pack the YZ edge (15)................................ + dvc_PackDist(7, dvcSendList_YZ, 0, sendCount_YZ, sendbuf_YZ, f_odd, + N); + //................................................................................... - //................................................................................... - // Send all the distributions - MPI_Isend(sendbuf_x, 5*sendCount_x,MPI_DOUBLE,rank_x,sendtag,comm,&req1[0]); - MPI_Irecv(recvbuf_X, 5*recvCount_X,MPI_DOUBLE,rank_X,recvtag,comm,&req2[0]); - MPI_Isend(sendbuf_X, 5*sendCount_X,MPI_DOUBLE,rank_X,sendtag,comm,&req1[1]); - MPI_Irecv(recvbuf_x, 5*recvCount_x,MPI_DOUBLE,rank_x,recvtag,comm,&req2[1]); - MPI_Isend(sendbuf_y, 5*sendCount_y,MPI_DOUBLE,rank_y,sendtag,comm,&req1[2]); - MPI_Irecv(recvbuf_Y, 5*recvCount_Y,MPI_DOUBLE,rank_Y,recvtag,comm,&req2[2]); - MPI_Isend(sendbuf_Y, 5*sendCount_Y,MPI_DOUBLE,rank_Y,sendtag,comm,&req1[3]); - MPI_Irecv(recvbuf_y, 5*recvCount_y,MPI_DOUBLE,rank_y,recvtag,comm,&req2[3]); - MPI_Isend(sendbuf_z, 5*sendCount_z,MPI_DOUBLE,rank_z,sendtag,comm,&req1[4]); - MPI_Irecv(recvbuf_Z, 5*recvCount_Z,MPI_DOUBLE,rank_Z,recvtag,comm,&req2[4]); - MPI_Isend(sendbuf_Z, 5*sendCount_Z,MPI_DOUBLE,rank_Z,sendtag,comm,&req1[5]); - MPI_Irecv(recvbuf_z, 5*recvCount_z,MPI_DOUBLE,rank_z,recvtag,comm,&req2[5]); - MPI_Isend(sendbuf_xy, sendCount_xy,MPI_DOUBLE,rank_xy,sendtag,comm,&req1[6]); - MPI_Irecv(recvbuf_XY, recvCount_XY,MPI_DOUBLE,rank_XY,recvtag,comm,&req2[6]); - MPI_Isend(sendbuf_XY, sendCount_XY,MPI_DOUBLE,rank_XY,sendtag,comm,&req1[7]); - MPI_Irecv(recvbuf_xy, recvCount_xy,MPI_DOUBLE,rank_xy,recvtag,comm,&req2[7]); - MPI_Isend(sendbuf_Xy, sendCount_Xy,MPI_DOUBLE,rank_Xy,sendtag,comm,&req1[8]); - MPI_Irecv(recvbuf_xY, recvCount_xY,MPI_DOUBLE,rank_xY,recvtag,comm,&req2[8]); - MPI_Isend(sendbuf_xY, sendCount_xY,MPI_DOUBLE,rank_xY,sendtag,comm,&req1[9]); - MPI_Irecv(recvbuf_Xy, recvCount_Xy,MPI_DOUBLE,rank_Xy,recvtag,comm,&req2[9]); - MPI_Isend(sendbuf_xz, sendCount_xz,MPI_DOUBLE,rank_xz,sendtag,comm,&req1[10]); - MPI_Irecv(recvbuf_XZ, recvCount_XZ,MPI_DOUBLE,rank_XZ,recvtag,comm,&req2[10]); - MPI_Isend(sendbuf_XZ, sendCount_XZ,MPI_DOUBLE,rank_XZ,sendtag,comm,&req1[11]); - MPI_Irecv(recvbuf_xz, recvCount_xz,MPI_DOUBLE,rank_xz,recvtag,comm,&req2[11]); - MPI_Isend(sendbuf_Xz, sendCount_Xz,MPI_DOUBLE,rank_Xz,sendtag,comm,&req1[12]); - MPI_Irecv(recvbuf_xZ, recvCount_xZ,MPI_DOUBLE,rank_xZ,recvtag,comm,&req2[12]); - MPI_Isend(sendbuf_xZ, sendCount_xZ,MPI_DOUBLE,rank_xZ,sendtag,comm,&req1[13]); - MPI_Irecv(recvbuf_Xz, recvCount_Xz,MPI_DOUBLE,rank_Xz,recvtag,comm,&req2[13]); - MPI_Isend(sendbuf_yz, sendCount_yz,MPI_DOUBLE,rank_yz,sendtag,comm,&req1[14]); - MPI_Irecv(recvbuf_YZ, recvCount_YZ,MPI_DOUBLE,rank_YZ,recvtag,comm,&req2[14]); - MPI_Isend(sendbuf_YZ, sendCount_YZ,MPI_DOUBLE,rank_YZ,sendtag,comm,&req1[15]); - MPI_Irecv(recvbuf_yz, recvCount_yz,MPI_DOUBLE,rank_yz,recvtag,comm,&req2[15]); - MPI_Isend(sendbuf_Yz, sendCount_Yz,MPI_DOUBLE,rank_Yz,sendtag,comm,&req1[16]); - MPI_Irecv(recvbuf_yZ, recvCount_yZ,MPI_DOUBLE,rank_yZ,recvtag,comm,&req2[16]); - MPI_Isend(sendbuf_yZ, sendCount_yZ,MPI_DOUBLE,rank_yZ,sendtag,comm,&req1[17]); - MPI_Irecv(recvbuf_Yz, recvCount_Yz,MPI_DOUBLE,rank_Yz,recvtag,comm,&req2[17]); - //................................................................................... + //................................................................................... + // Send all the distributions + MPI_Isend(sendbuf_x, 5 * sendCount_x, MPI_DOUBLE, rank_x, sendtag, + comm, &req1[0]); + MPI_Irecv(recvbuf_X, 5 * recvCount_X, MPI_DOUBLE, rank_X, recvtag, + comm, &req2[0]); + MPI_Isend(sendbuf_X, 5 * sendCount_X, MPI_DOUBLE, rank_X, sendtag, + comm, &req1[1]); + MPI_Irecv(recvbuf_x, 5 * recvCount_x, MPI_DOUBLE, rank_x, recvtag, + comm, &req2[1]); + MPI_Isend(sendbuf_y, 5 * sendCount_y, MPI_DOUBLE, rank_y, sendtag, + comm, &req1[2]); + MPI_Irecv(recvbuf_Y, 5 * recvCount_Y, MPI_DOUBLE, rank_Y, recvtag, + comm, &req2[2]); + MPI_Isend(sendbuf_Y, 5 * sendCount_Y, MPI_DOUBLE, rank_Y, sendtag, + comm, &req1[3]); + MPI_Irecv(recvbuf_y, 5 * recvCount_y, MPI_DOUBLE, rank_y, recvtag, + comm, &req2[3]); + MPI_Isend(sendbuf_z, 5 * sendCount_z, MPI_DOUBLE, rank_z, sendtag, + comm, &req1[4]); + MPI_Irecv(recvbuf_Z, 5 * recvCount_Z, MPI_DOUBLE, rank_Z, recvtag, + comm, &req2[4]); + MPI_Isend(sendbuf_Z, 5 * sendCount_Z, MPI_DOUBLE, rank_Z, sendtag, + comm, &req1[5]); + MPI_Irecv(recvbuf_z, 5 * recvCount_z, MPI_DOUBLE, rank_z, recvtag, + comm, &req2[5]); + MPI_Isend(sendbuf_xy, sendCount_xy, MPI_DOUBLE, rank_xy, sendtag, + comm, &req1[6]); + MPI_Irecv(recvbuf_XY, recvCount_XY, MPI_DOUBLE, rank_XY, recvtag, + comm, &req2[6]); + MPI_Isend(sendbuf_XY, sendCount_XY, MPI_DOUBLE, rank_XY, sendtag, + comm, &req1[7]); + MPI_Irecv(recvbuf_xy, recvCount_xy, MPI_DOUBLE, rank_xy, recvtag, + comm, &req2[7]); + MPI_Isend(sendbuf_Xy, sendCount_Xy, MPI_DOUBLE, rank_Xy, sendtag, + comm, &req1[8]); + MPI_Irecv(recvbuf_xY, recvCount_xY, MPI_DOUBLE, rank_xY, recvtag, + comm, &req2[8]); + MPI_Isend(sendbuf_xY, sendCount_xY, MPI_DOUBLE, rank_xY, sendtag, + comm, &req1[9]); + MPI_Irecv(recvbuf_Xy, recvCount_Xy, MPI_DOUBLE, rank_Xy, recvtag, + comm, &req2[9]); + MPI_Isend(sendbuf_xz, sendCount_xz, MPI_DOUBLE, rank_xz, sendtag, + comm, &req1[10]); + MPI_Irecv(recvbuf_XZ, recvCount_XZ, MPI_DOUBLE, rank_XZ, recvtag, + comm, &req2[10]); + MPI_Isend(sendbuf_XZ, sendCount_XZ, MPI_DOUBLE, rank_XZ, sendtag, + comm, &req1[11]); + MPI_Irecv(recvbuf_xz, recvCount_xz, MPI_DOUBLE, rank_xz, recvtag, + comm, &req2[11]); + MPI_Isend(sendbuf_Xz, sendCount_Xz, MPI_DOUBLE, rank_Xz, sendtag, + comm, &req1[12]); + MPI_Irecv(recvbuf_xZ, recvCount_xZ, MPI_DOUBLE, rank_xZ, recvtag, + comm, &req2[12]); + MPI_Isend(sendbuf_xZ, sendCount_xZ, MPI_DOUBLE, rank_xZ, sendtag, + comm, &req1[13]); + MPI_Irecv(recvbuf_Xz, recvCount_Xz, MPI_DOUBLE, rank_Xz, recvtag, + comm, &req2[13]); + MPI_Isend(sendbuf_yz, sendCount_yz, MPI_DOUBLE, rank_yz, sendtag, + comm, &req1[14]); + MPI_Irecv(recvbuf_YZ, recvCount_YZ, MPI_DOUBLE, rank_YZ, recvtag, + comm, &req2[14]); + MPI_Isend(sendbuf_YZ, sendCount_YZ, MPI_DOUBLE, rank_YZ, sendtag, + comm, &req1[15]); + MPI_Irecv(recvbuf_yz, recvCount_yz, MPI_DOUBLE, rank_yz, recvtag, + comm, &req2[15]); + MPI_Isend(sendbuf_Yz, sendCount_Yz, MPI_DOUBLE, rank_Yz, sendtag, + comm, &req1[16]); + MPI_Irecv(recvbuf_yZ, recvCount_yZ, MPI_DOUBLE, rank_yZ, recvtag, + comm, &req2[16]); + MPI_Isend(sendbuf_yZ, sendCount_yZ, MPI_DOUBLE, rank_yZ, sendtag, + comm, &req1[17]); + MPI_Irecv(recvbuf_Yz, recvCount_Yz, MPI_DOUBLE, rank_Yz, recvtag, + comm, &req2[17]); + //................................................................................... - //************************************************************************* - // Carry out the density streaming step for mass transport - //************************************************************************* - dvc_DensityStreamD3Q7(ID, Den, Copy, Phi, ColorGrad, Velocity, beta, Nx, Ny, Nz, pBC, S); - //************************************************************************* + //************************************************************************* + // Carry out the density streaming step for mass transport + //************************************************************************* + dvc_DensityStreamD3Q7(ID, Den, Copy, Phi, ColorGrad, Velocity, beta, + Nx, Ny, Nz, pBC, S); + //************************************************************************* - //************************************************************************* - // Swap the distributions for momentum transport - //************************************************************************* - dvc_SwapD3Q19(ID, f_even, f_odd, Nx, Ny, Nz, S); - //************************************************************************* + //************************************************************************* + // Swap the distributions for momentum transport + //************************************************************************* + dvc_SwapD3Q19(ID, f_even, f_odd, Nx, Ny, Nz, S); + //************************************************************************* - //................................................................................... - // Wait for completion of D3Q19 communication - MPI_Waitall(18,req1,stat1); - MPI_Waitall(18,req2,stat2); + //................................................................................... + // Wait for completion of D3Q19 communication + MPI_Waitall(18, req1, stat1); + MPI_Waitall(18, req2, stat2); - //................................................................................... - // Unpack the distributions on the device - //................................................................................... - //...Map recieve list for the X face: q=2,8,10,12,13 ................................. - dvc_UnpackDist(0,-1,0,0,dvcRecvList_X,0,recvCount_X,recvbuf_X,f_odd,Nx,Ny,Nz); - dvc_UnpackDist(3,-1,-1,0,dvcRecvList_X,recvCount_X,recvCount_X,recvbuf_X,f_odd,Nx,Ny,Nz); - dvc_UnpackDist(4,-1,1,0,dvcRecvList_X,2*recvCount_X,recvCount_X,recvbuf_X,f_odd,Nx,Ny,Nz); - dvc_UnpackDist(5,-1,0,-1,dvcRecvList_X,3*recvCount_X,recvCount_X,recvbuf_X,f_odd,Nx,Ny,Nz); - dvc_UnpackDist(6,-1,0,1,dvcRecvList_X,4*recvCount_X,recvCount_X,recvbuf_X,f_odd,Nx,Ny,Nz); - //................................................................................... - //...Map recieve list for the x face: q=1,7,9,11,13.................................. - dvc_UnpackDist(1,1,0,0,dvcRecvList_x,0,recvCount_x,recvbuf_x,f_even,Nx,Ny,Nz); - dvc_UnpackDist(4,1,1,0,dvcRecvList_x,recvCount_x,recvCount_x,recvbuf_x,f_even,Nx,Ny,Nz); - dvc_UnpackDist(5,1,-1,0,dvcRecvList_x,2*recvCount_x,recvCount_x,recvbuf_x,f_even,Nx,Ny,Nz); - dvc_UnpackDist(6,1,0,1,dvcRecvList_x,3*recvCount_x,recvCount_x,recvbuf_x,f_even,Nx,Ny,Nz); - dvc_UnpackDist(7,1,0,-1,dvcRecvList_x,4*recvCount_x,recvCount_x,recvbuf_x,f_even,Nx,Ny,Nz); - //................................................................................... - //...Map recieve list for the y face: q=4,8,9,16,18 ................................... - dvc_UnpackDist(1,0,-1,0,dvcRecvList_Y,0,recvCount_Y,recvbuf_Y,f_odd,Nx,Ny,Nz); - dvc_UnpackDist(3,-1,-1,0,dvcRecvList_Y,recvCount_Y,recvCount_Y,recvbuf_Y,f_odd,Nx,Ny,Nz); - dvc_UnpackDist(5,1,-1,0,dvcRecvList_Y,2*recvCount_Y,recvCount_Y,recvbuf_Y,f_even,Nx,Ny,Nz); - dvc_UnpackDist(7,0,-1,-1,dvcRecvList_Y,3*recvCount_Y,recvCount_Y,recvbuf_Y,f_odd,Nx,Ny,Nz); - dvc_UnpackDist(8,0,-1,1,dvcRecvList_Y,4*recvCount_Y,recvCount_Y,recvbuf_Y,f_odd,Nx,Ny,Nz); - //................................................................................... - //...Map recieve list for the Y face: q=3,7,10,15,17 .................................. - dvc_UnpackDist(2,0,1,0,dvcRecvList_y,0,recvCount_y,recvbuf_y,f_even,Nx,Ny,Nz); - dvc_UnpackDist(4,1,1,0,dvcRecvList_y,recvCount_y,recvCount_y,recvbuf_y,f_even,Nx,Ny,Nz); - dvc_UnpackDist(4,-1,1,0,dvcRecvList_y,2*recvCount_y,recvCount_y,recvbuf_y,f_odd,Nx,Ny,Nz); - dvc_UnpackDist(8,0,1,1,dvcRecvList_y,3*recvCount_y,recvCount_y,recvbuf_y,f_even,Nx,Ny,Nz); - dvc_UnpackDist(9,0,1,-1,dvcRecvList_y,4*recvCount_y,recvCount_y,recvbuf_y,f_even,Nx,Ny,Nz); - //................................................................................... - //...Map recieve list for the z face<<<6,12,13,16,17).............................................. - dvc_UnpackDist(2,0,0,-1,dvcRecvList_Z,0,recvCount_Z,recvbuf_Z,f_odd,Nx,Ny,Nz); - dvc_UnpackDist(5,-1,0,-1,dvcRecvList_Z,recvCount_Z,recvCount_Z,recvbuf_Z,f_odd,Nx,Ny,Nz); - dvc_UnpackDist(7,1,0,-1,dvcRecvList_Z,2*recvCount_Z,recvCount_Z,recvbuf_Z,f_even,Nx,Ny,Nz); - dvc_UnpackDist(7,0,-1,-1,dvcRecvList_Z,3*recvCount_Z,recvCount_Z,recvbuf_Z,f_odd,Nx,Ny,Nz); - dvc_UnpackDist(9,0,1,-1,dvcRecvList_Z,4*recvCount_Z,recvCount_Z,recvbuf_Z,f_even,Nx,Ny,Nz); - //...Map recieve list for the Z face<<<5,11,14,15,18).............................................. - dvc_UnpackDist(3,0,0,1,dvcRecvList_z,0,recvCount_z,recvbuf_z,f_even,Nx,Ny,Nz); - dvc_UnpackDist(6,1,0,1,dvcRecvList_z,recvCount_z,recvCount_z,recvbuf_z,f_even,Nx,Ny,Nz); - dvc_UnpackDist(6,-1,0,1,dvcRecvList_z,2*recvCount_z,recvCount_z,recvbuf_z,f_odd,Nx,Ny,Nz); - dvc_UnpackDist(8,0,1,1,dvcRecvList_z,3*recvCount_z,recvCount_z,recvbuf_z,f_even,Nx,Ny,Nz); - dvc_UnpackDist(8,0,-1,1,dvcRecvList_z,4*recvCount_z,recvCount_z,recvbuf_z,f_odd,Nx,Ny,Nz); - //.................................................................................. - //...Map recieve list for the xy edge <<<8)................................ - dvc_UnpackDist(3,-1,-1,0,dvcRecvList_XY,0,recvCount_XY,recvbuf_XY,f_odd,Nx,Ny,Nz); - //...Map recieve list for the Xy edge <<<9)................................ - dvc_UnpackDist(5,1,-1,0,dvcRecvList_xY,0,recvCount_xY,recvbuf_xY,f_even,Nx,Ny,Nz); - //...Map recieve list for the xY edge <<<10)................................ - dvc_UnpackDist(4,-1,1,0,dvcRecvList_Xy,0,recvCount_Xy,recvbuf_Xy,f_odd,Nx,Ny,Nz); - //...Map recieve list for the XY edge <<<7)................................ - dvc_UnpackDist(4,1,1,0,dvcRecvList_xy,0,recvCount_xy,recvbuf_xy,f_even,Nx,Ny,Nz); - //...Map recieve list for the xz edge <<<12)................................ - dvc_UnpackDist(5,-1,0,-1,dvcRecvList_XZ,0,recvCount_XZ,recvbuf_XZ,f_odd,Nx,Ny,Nz); - //...Map recieve list for the xZ edge <<<14)................................ - dvc_UnpackDist(6,-1,0,1,dvcRecvList_Xz,0,recvCount_Xz,recvbuf_Xz,f_odd,Nx,Ny,Nz); - //...Map recieve list for the Xz edge <<<13)................................ - dvc_UnpackDist(7,1,0,-1,dvcRecvList_xZ,0,recvCount_xZ,recvbuf_xZ,f_even,Nx,Ny,Nz); - //...Map recieve list for the XZ edge <<<11)................................ - dvc_UnpackDist(6,1,0,1,dvcRecvList_xz,0,recvCount_xz,recvbuf_xz,f_even,Nx,Ny,Nz); - //...Map recieve list for the yz edge <<<16)................................ - dvc_UnpackDist(7,0,-1,-1,dvcRecvList_YZ,0,recvCount_YZ,recvbuf_YZ,f_odd,Nx,Ny,Nz); - //...Map recieve list for the yZ edge <<<18)................................ - dvc_UnpackDist(8,0,-1,1,dvcRecvList_Yz,0,recvCount_Yz,recvbuf_Yz,f_odd,Nx,Ny,Nz); - //...Map recieve list for the Yz edge <<<17)................................ - dvc_UnpackDist(9,0,1,-1,dvcRecvList_yZ,0,recvCount_yZ,recvbuf_yZ,f_even,Nx,Ny,Nz); - //...Map recieve list for the YZ edge <<<15)................................ - dvc_UnpackDist(8,0,1,1,dvcRecvList_yz,0,recvCount_yz,recvbuf_yz,f_even,Nx,Ny,Nz); - //................................................................................... + //................................................................................... + // Unpack the distributions on the device + //................................................................................... + //...Map recieve list for the X face: q=2,8,10,12,13 ................................. + dvc_UnpackDist(0, -1, 0, 0, dvcRecvList_X, 0, recvCount_X, + recvbuf_X, f_odd, Nx, Ny, Nz); + dvc_UnpackDist(3, -1, -1, 0, dvcRecvList_X, recvCount_X, + recvCount_X, recvbuf_X, f_odd, Nx, Ny, Nz); + dvc_UnpackDist(4, -1, 1, 0, dvcRecvList_X, 2 * recvCount_X, + recvCount_X, recvbuf_X, f_odd, Nx, Ny, Nz); + dvc_UnpackDist(5, -1, 0, -1, dvcRecvList_X, 3 * recvCount_X, + recvCount_X, recvbuf_X, f_odd, Nx, Ny, Nz); + dvc_UnpackDist(6, -1, 0, 1, dvcRecvList_X, 4 * recvCount_X, + recvCount_X, recvbuf_X, f_odd, Nx, Ny, Nz); + //................................................................................... + //...Map recieve list for the x face: q=1,7,9,11,13.................................. + dvc_UnpackDist(1, 1, 0, 0, dvcRecvList_x, 0, recvCount_x, recvbuf_x, + f_even, Nx, Ny, Nz); + dvc_UnpackDist(4, 1, 1, 0, dvcRecvList_x, recvCount_x, recvCount_x, + recvbuf_x, f_even, Nx, Ny, Nz); + dvc_UnpackDist(5, 1, -1, 0, dvcRecvList_x, 2 * recvCount_x, + recvCount_x, recvbuf_x, f_even, Nx, Ny, Nz); + dvc_UnpackDist(6, 1, 0, 1, dvcRecvList_x, 3 * recvCount_x, + recvCount_x, recvbuf_x, f_even, Nx, Ny, Nz); + dvc_UnpackDist(7, 1, 0, -1, dvcRecvList_x, 4 * recvCount_x, + recvCount_x, recvbuf_x, f_even, Nx, Ny, Nz); + //................................................................................... + //...Map recieve list for the y face: q=4,8,9,16,18 ................................... + dvc_UnpackDist(1, 0, -1, 0, dvcRecvList_Y, 0, recvCount_Y, + recvbuf_Y, f_odd, Nx, Ny, Nz); + dvc_UnpackDist(3, -1, -1, 0, dvcRecvList_Y, recvCount_Y, + recvCount_Y, recvbuf_Y, f_odd, Nx, Ny, Nz); + dvc_UnpackDist(5, 1, -1, 0, dvcRecvList_Y, 2 * recvCount_Y, + recvCount_Y, recvbuf_Y, f_even, Nx, Ny, Nz); + dvc_UnpackDist(7, 0, -1, -1, dvcRecvList_Y, 3 * recvCount_Y, + recvCount_Y, recvbuf_Y, f_odd, Nx, Ny, Nz); + dvc_UnpackDist(8, 0, -1, 1, dvcRecvList_Y, 4 * recvCount_Y, + recvCount_Y, recvbuf_Y, f_odd, Nx, Ny, Nz); + //................................................................................... + //...Map recieve list for the Y face: q=3,7,10,15,17 .................................. + dvc_UnpackDist(2, 0, 1, 0, dvcRecvList_y, 0, recvCount_y, recvbuf_y, + f_even, Nx, Ny, Nz); + dvc_UnpackDist(4, 1, 1, 0, dvcRecvList_y, recvCount_y, recvCount_y, + recvbuf_y, f_even, Nx, Ny, Nz); + dvc_UnpackDist(4, -1, 1, 0, dvcRecvList_y, 2 * recvCount_y, + recvCount_y, recvbuf_y, f_odd, Nx, Ny, Nz); + dvc_UnpackDist(8, 0, 1, 1, dvcRecvList_y, 3 * recvCount_y, + recvCount_y, recvbuf_y, f_even, Nx, Ny, Nz); + dvc_UnpackDist(9, 0, 1, -1, dvcRecvList_y, 4 * recvCount_y, + recvCount_y, recvbuf_y, f_even, Nx, Ny, Nz); + //................................................................................... + //...Map recieve list for the z face<<<6,12,13,16,17).............................................. + dvc_UnpackDist(2, 0, 0, -1, dvcRecvList_Z, 0, recvCount_Z, + recvbuf_Z, f_odd, Nx, Ny, Nz); + dvc_UnpackDist(5, -1, 0, -1, dvcRecvList_Z, recvCount_Z, + recvCount_Z, recvbuf_Z, f_odd, Nx, Ny, Nz); + dvc_UnpackDist(7, 1, 0, -1, dvcRecvList_Z, 2 * recvCount_Z, + recvCount_Z, recvbuf_Z, f_even, Nx, Ny, Nz); + dvc_UnpackDist(7, 0, -1, -1, dvcRecvList_Z, 3 * recvCount_Z, + recvCount_Z, recvbuf_Z, f_odd, Nx, Ny, Nz); + dvc_UnpackDist(9, 0, 1, -1, dvcRecvList_Z, 4 * recvCount_Z, + recvCount_Z, recvbuf_Z, f_even, Nx, Ny, Nz); + //...Map recieve list for the Z face<<<5,11,14,15,18).............................................. + dvc_UnpackDist(3, 0, 0, 1, dvcRecvList_z, 0, recvCount_z, recvbuf_z, + f_even, Nx, Ny, Nz); + dvc_UnpackDist(6, 1, 0, 1, dvcRecvList_z, recvCount_z, recvCount_z, + recvbuf_z, f_even, Nx, Ny, Nz); + dvc_UnpackDist(6, -1, 0, 1, dvcRecvList_z, 2 * recvCount_z, + recvCount_z, recvbuf_z, f_odd, Nx, Ny, Nz); + dvc_UnpackDist(8, 0, 1, 1, dvcRecvList_z, 3 * recvCount_z, + recvCount_z, recvbuf_z, f_even, Nx, Ny, Nz); + dvc_UnpackDist(8, 0, -1, 1, dvcRecvList_z, 4 * recvCount_z, + recvCount_z, recvbuf_z, f_odd, Nx, Ny, Nz); + //.................................................................................. + //...Map recieve list for the xy edge <<<8)................................ + dvc_UnpackDist(3, -1, -1, 0, dvcRecvList_XY, 0, recvCount_XY, + recvbuf_XY, f_odd, Nx, Ny, Nz); + //...Map recieve list for the Xy edge <<<9)................................ + dvc_UnpackDist(5, 1, -1, 0, dvcRecvList_xY, 0, recvCount_xY, + recvbuf_xY, f_even, Nx, Ny, Nz); + //...Map recieve list for the xY edge <<<10)................................ + dvc_UnpackDist(4, -1, 1, 0, dvcRecvList_Xy, 0, recvCount_Xy, + recvbuf_Xy, f_odd, Nx, Ny, Nz); + //...Map recieve list for the XY edge <<<7)................................ + dvc_UnpackDist(4, 1, 1, 0, dvcRecvList_xy, 0, recvCount_xy, + recvbuf_xy, f_even, Nx, Ny, Nz); + //...Map recieve list for the xz edge <<<12)................................ + dvc_UnpackDist(5, -1, 0, -1, dvcRecvList_XZ, 0, recvCount_XZ, + recvbuf_XZ, f_odd, Nx, Ny, Nz); + //...Map recieve list for the xZ edge <<<14)................................ + dvc_UnpackDist(6, -1, 0, 1, dvcRecvList_Xz, 0, recvCount_Xz, + recvbuf_Xz, f_odd, Nx, Ny, Nz); + //...Map recieve list for the Xz edge <<<13)................................ + dvc_UnpackDist(7, 1, 0, -1, dvcRecvList_xZ, 0, recvCount_xZ, + recvbuf_xZ, f_even, Nx, Ny, Nz); + //...Map recieve list for the XZ edge <<<11)................................ + dvc_UnpackDist(6, 1, 0, 1, dvcRecvList_xz, 0, recvCount_xz, + recvbuf_xz, f_even, Nx, Ny, Nz); + //...Map recieve list for the yz edge <<<16)................................ + dvc_UnpackDist(7, 0, -1, -1, dvcRecvList_YZ, 0, recvCount_YZ, + recvbuf_YZ, f_odd, Nx, Ny, Nz); + //...Map recieve list for the yZ edge <<<18)................................ + dvc_UnpackDist(8, 0, -1, 1, dvcRecvList_Yz, 0, recvCount_Yz, + recvbuf_Yz, f_odd, Nx, Ny, Nz); + //...Map recieve list for the Yz edge <<<17)................................ + dvc_UnpackDist(9, 0, 1, -1, dvcRecvList_yZ, 0, recvCount_yZ, + recvbuf_yZ, f_even, Nx, Ny, Nz); + //...Map recieve list for the YZ edge <<<15)................................ + dvc_UnpackDist(8, 0, 1, 1, dvcRecvList_yz, 0, recvCount_yz, + recvbuf_yz, f_even, Nx, Ny, Nz); + //................................................................................... - //................................................................................... - dvc_PackDenD3Q7(dvcRecvList_x,recvCount_x,recvbuf_x,2,Den,N); - dvc_PackDenD3Q7(dvcRecvList_y,recvCount_y,recvbuf_y,2,Den,N); - dvc_PackDenD3Q7(dvcRecvList_z,recvCount_z,recvbuf_z,2,Den,N); - dvc_PackDenD3Q7(dvcRecvList_X,recvCount_X,recvbuf_X,2,Den,N); - dvc_PackDenD3Q7(dvcRecvList_Y,recvCount_Y,recvbuf_Y,2,Den,N); - dvc_PackDenD3Q7(dvcRecvList_Z,recvCount_Z,recvbuf_Z,2,Den,N); - //................................................................................... + //................................................................................... + dvc_PackDenD3Q7(dvcRecvList_x, recvCount_x, recvbuf_x, 2, Den, N); + dvc_PackDenD3Q7(dvcRecvList_y, recvCount_y, recvbuf_y, 2, Den, N); + dvc_PackDenD3Q7(dvcRecvList_z, recvCount_z, recvbuf_z, 2, Den, N); + dvc_PackDenD3Q7(dvcRecvList_X, recvCount_X, recvbuf_X, 2, Den, N); + dvc_PackDenD3Q7(dvcRecvList_Y, recvCount_Y, recvbuf_Y, 2, Den, N); + dvc_PackDenD3Q7(dvcRecvList_Z, recvCount_Z, recvbuf_Z, 2, Den, N); + //................................................................................... - //................................................................................... - // Send all the D3Q7 distributions - MPI_Isend(recvbuf_x, 2*recvCount_x,MPI_DOUBLE,rank_x,sendtag,comm,&req1[0]); - MPI_Irecv(sendbuf_X, 2*sendCount_X,MPI_DOUBLE,rank_X,recvtag,comm,&req2[0]); - MPI_Isend(recvbuf_X, 2*recvCount_X,MPI_DOUBLE,rank_X,sendtag,comm,&req1[1]); - MPI_Irecv(sendbuf_x, 2*sendCount_x,MPI_DOUBLE,rank_x,recvtag,comm,&req2[1]); - MPI_Isend(recvbuf_y, 2*recvCount_y,MPI_DOUBLE,rank_y,sendtag,comm,&req1[2]); - MPI_Irecv(sendbuf_Y, 2*sendCount_Y,MPI_DOUBLE,rank_Y,recvtag,comm,&req2[2]); - MPI_Isend(recvbuf_Y, 2*recvCount_Y,MPI_DOUBLE,rank_Y,sendtag,comm,&req1[3]); - MPI_Irecv(sendbuf_y, 2*sendCount_y,MPI_DOUBLE,rank_y,recvtag,comm,&req2[3]); - MPI_Isend(recvbuf_z, 2*recvCount_z,MPI_DOUBLE,rank_z,sendtag,comm,&req1[4]); - MPI_Irecv(sendbuf_Z, 2*sendCount_Z,MPI_DOUBLE,rank_Z,recvtag,comm,&req2[4]); - MPI_Isend(recvbuf_Z, 2*recvCount_Z,MPI_DOUBLE,rank_Z,sendtag,comm,&req1[5]); - MPI_Irecv(sendbuf_z, 2*sendCount_z,MPI_DOUBLE,rank_z,recvtag,comm,&req2[5]); - //................................................................................... - //................................................................................... - // Wait for completion of D3Q7 communication - MPI_Waitall(6,req1,stat1); - MPI_Waitall(6,req2,stat2); - //................................................................................... - //................................................................................... - dvc_UnpackDenD3Q7(dvcSendList_x,sendCount_x,sendbuf_x,2,Den,N); - dvc_UnpackDenD3Q7(dvcSendList_y,sendCount_y,sendbuf_y,2,Den,N); - dvc_UnpackDenD3Q7(dvcSendList_z,sendCount_z,sendbuf_z,2,Den,N); - dvc_UnpackDenD3Q7(dvcSendList_X,sendCount_X,sendbuf_X,2,Den,N); - dvc_UnpackDenD3Q7(dvcSendList_Y,sendCount_Y,sendbuf_Y,2,Den,N); - dvc_UnpackDenD3Q7(dvcSendList_Z,sendCount_Z,sendbuf_Z,2,Den,N); - //................................................................................... + //................................................................................... + // Send all the D3Q7 distributions + MPI_Isend(recvbuf_x, 2 * recvCount_x, MPI_DOUBLE, rank_x, sendtag, + comm, &req1[0]); + MPI_Irecv(sendbuf_X, 2 * sendCount_X, MPI_DOUBLE, rank_X, recvtag, + comm, &req2[0]); + MPI_Isend(recvbuf_X, 2 * recvCount_X, MPI_DOUBLE, rank_X, sendtag, + comm, &req1[1]); + MPI_Irecv(sendbuf_x, 2 * sendCount_x, MPI_DOUBLE, rank_x, recvtag, + comm, &req2[1]); + MPI_Isend(recvbuf_y, 2 * recvCount_y, MPI_DOUBLE, rank_y, sendtag, + comm, &req1[2]); + MPI_Irecv(sendbuf_Y, 2 * sendCount_Y, MPI_DOUBLE, rank_Y, recvtag, + comm, &req2[2]); + MPI_Isend(recvbuf_Y, 2 * recvCount_Y, MPI_DOUBLE, rank_Y, sendtag, + comm, &req1[3]); + MPI_Irecv(sendbuf_y, 2 * sendCount_y, MPI_DOUBLE, rank_y, recvtag, + comm, &req2[3]); + MPI_Isend(recvbuf_z, 2 * recvCount_z, MPI_DOUBLE, rank_z, sendtag, + comm, &req1[4]); + MPI_Irecv(sendbuf_Z, 2 * sendCount_Z, MPI_DOUBLE, rank_Z, recvtag, + comm, &req2[4]); + MPI_Isend(recvbuf_Z, 2 * recvCount_Z, MPI_DOUBLE, rank_Z, sendtag, + comm, &req1[5]); + MPI_Irecv(sendbuf_z, 2 * sendCount_z, MPI_DOUBLE, rank_z, recvtag, + comm, &req2[5]); + //................................................................................... + //................................................................................... + // Wait for completion of D3Q7 communication + MPI_Waitall(6, req1, stat1); + MPI_Waitall(6, req2, stat2); + //................................................................................... + //................................................................................... + dvc_UnpackDenD3Q7(dvcSendList_x, sendCount_x, sendbuf_x, 2, Den, N); + dvc_UnpackDenD3Q7(dvcSendList_y, sendCount_y, sendbuf_y, 2, Den, N); + dvc_UnpackDenD3Q7(dvcSendList_z, sendCount_z, sendbuf_z, 2, Den, N); + dvc_UnpackDenD3Q7(dvcSendList_X, sendCount_X, sendbuf_X, 2, Den, N); + dvc_UnpackDenD3Q7(dvcSendList_Y, sendCount_Y, sendbuf_Y, 2, Den, N); + dvc_UnpackDenD3Q7(dvcSendList_Z, sendCount_Z, sendbuf_Z, 2, Den, N); + //................................................................................... - //************************************************************************* - // Compute the phase indicator field and reset Copy, Den - //************************************************************************* - dvc_ComputePhi(ID, Phi, Copy, Den, N, S); - //************************************************************************* + //************************************************************************* + // Compute the phase indicator field and reset Copy, Den + //************************************************************************* + dvc_ComputePhi(ID, Phi, Copy, Den, N, S); + //************************************************************************* - //................................................................................... - dvc_PackValues(dvcSendList_x, sendCount_x,sendbuf_x, Phi, N); - dvc_PackValues(dvcSendList_y, sendCount_y,sendbuf_y, Phi, N); - dvc_PackValues(dvcSendList_z, sendCount_z,sendbuf_z, Phi, N); - dvc_PackValues(dvcSendList_X, sendCount_X,sendbuf_X, Phi, N); - dvc_PackValues(dvcSendList_Y, sendCount_Y,sendbuf_Y, Phi, N); - dvc_PackValues(dvcSendList_Z, sendCount_Z,sendbuf_Z, Phi, N); - dvc_PackValues(dvcSendList_xy, sendCount_xy,sendbuf_xy, Phi, N); - dvc_PackValues(dvcSendList_xY, sendCount_xY,sendbuf_xY, Phi, N); - dvc_PackValues(dvcSendList_Xy, sendCount_Xy,sendbuf_Xy, Phi, N); - dvc_PackValues(dvcSendList_XY, sendCount_XY,sendbuf_XY, Phi, N); - dvc_PackValues(dvcSendList_xz, sendCount_xz,sendbuf_xz, Phi, N); - dvc_PackValues(dvcSendList_xZ, sendCount_xZ,sendbuf_xZ, Phi, N); - dvc_PackValues(dvcSendList_Xz, sendCount_Xz,sendbuf_Xz, Phi, N); - dvc_PackValues(dvcSendList_XZ, sendCount_XZ,sendbuf_XZ, Phi, N); - dvc_PackValues(dvcSendList_yz, sendCount_yz,sendbuf_yz, Phi, N); - dvc_PackValues(dvcSendList_yZ, sendCount_yZ,sendbuf_yZ, Phi, N); - dvc_PackValues(dvcSendList_Yz, sendCount_Yz,sendbuf_Yz, Phi, N); - dvc_PackValues(dvcSendList_YZ, sendCount_YZ,sendbuf_YZ, Phi, N); - //................................................................................... - // Send / Recv all the phase indcator field values - //................................................................................... - MPI_Isend(sendbuf_x, sendCount_x,MPI_DOUBLE,rank_x,sendtag,comm,&req1[0]); - MPI_Irecv(recvbuf_X, recvCount_X,MPI_DOUBLE,rank_X,recvtag,comm,&req2[0]); - MPI_Isend(sendbuf_X, sendCount_X,MPI_DOUBLE,rank_X,sendtag,comm,&req1[1]); - MPI_Irecv(recvbuf_x, recvCount_x,MPI_DOUBLE,rank_x,recvtag,comm,&req2[1]); - MPI_Isend(sendbuf_y, sendCount_y,MPI_DOUBLE,rank_y,sendtag,comm,&req1[2]); - MPI_Irecv(recvbuf_Y, recvCount_Y,MPI_DOUBLE,rank_Y,recvtag,comm,&req2[2]); - MPI_Isend(sendbuf_Y, sendCount_Y,MPI_DOUBLE,rank_Y,sendtag,comm,&req1[3]); - MPI_Irecv(recvbuf_y, recvCount_y,MPI_DOUBLE,rank_y,recvtag,comm,&req2[3]); - MPI_Isend(sendbuf_z, sendCount_z,MPI_DOUBLE,rank_z,sendtag,comm,&req1[4]); - MPI_Irecv(recvbuf_Z, recvCount_Z,MPI_DOUBLE,rank_Z,recvtag,comm,&req2[4]); - MPI_Isend(sendbuf_Z, sendCount_Z,MPI_DOUBLE,rank_Z,sendtag,comm,&req1[5]); - MPI_Irecv(recvbuf_z, recvCount_z,MPI_DOUBLE,rank_z,recvtag,comm,&req2[5]); - MPI_Isend(sendbuf_xy, sendCount_xy,MPI_DOUBLE,rank_xy,sendtag,comm,&req1[6]); - MPI_Irecv(recvbuf_XY, recvCount_XY,MPI_DOUBLE,rank_XY,recvtag,comm,&req2[6]); - MPI_Isend(sendbuf_XY, sendCount_XY,MPI_DOUBLE,rank_XY,sendtag,comm,&req1[7]); - MPI_Irecv(recvbuf_xy, recvCount_xy,MPI_DOUBLE,rank_xy,recvtag,comm,&req2[7]); - MPI_Isend(sendbuf_Xy, sendCount_Xy,MPI_DOUBLE,rank_Xy,sendtag,comm,&req1[8]); - MPI_Irecv(recvbuf_xY, recvCount_xY,MPI_DOUBLE,rank_xY,recvtag,comm,&req2[8]); - MPI_Isend(sendbuf_xY, sendCount_xY,MPI_DOUBLE,rank_xY,sendtag,comm,&req1[9]); - MPI_Irecv(recvbuf_Xy, recvCount_Xy,MPI_DOUBLE,rank_Xy,recvtag,comm,&req2[9]); - MPI_Isend(sendbuf_xz, sendCount_xz,MPI_DOUBLE,rank_xz,sendtag,comm,&req1[10]); - MPI_Irecv(recvbuf_XZ, recvCount_XZ,MPI_DOUBLE,rank_XZ,recvtag,comm,&req2[10]); - MPI_Isend(sendbuf_XZ, sendCount_XZ,MPI_DOUBLE,rank_XZ,sendtag,comm,&req1[11]); - MPI_Irecv(recvbuf_xz, recvCount_xz,MPI_DOUBLE,rank_xz,recvtag,comm,&req2[11]); - MPI_Isend(sendbuf_Xz, sendCount_Xz,MPI_DOUBLE,rank_Xz,sendtag,comm,&req1[12]); - MPI_Irecv(recvbuf_xZ, recvCount_xZ,MPI_DOUBLE,rank_xZ,recvtag,comm,&req2[12]); - MPI_Isend(sendbuf_xZ, sendCount_xZ,MPI_DOUBLE,rank_xZ,sendtag,comm,&req1[13]); - MPI_Irecv(recvbuf_Xz, recvCount_Xz,MPI_DOUBLE,rank_Xz,recvtag,comm,&req2[13]); - MPI_Isend(sendbuf_yz, sendCount_yz,MPI_DOUBLE,rank_yz,sendtag,comm,&req1[14]); - MPI_Irecv(recvbuf_YZ, recvCount_YZ,MPI_DOUBLE,rank_YZ,recvtag,comm,&req2[14]); - MPI_Isend(sendbuf_YZ, sendCount_YZ,MPI_DOUBLE,rank_YZ,sendtag,comm,&req1[15]); - MPI_Irecv(recvbuf_yz, recvCount_yz,MPI_DOUBLE,rank_yz,recvtag,comm,&req2[15]); - MPI_Isend(sendbuf_Yz, sendCount_Yz,MPI_DOUBLE,rank_Yz,sendtag,comm,&req1[16]); - MPI_Irecv(recvbuf_yZ, recvCount_yZ,MPI_DOUBLE,rank_yZ,recvtag,comm,&req2[16]); - MPI_Isend(sendbuf_yZ, sendCount_yZ,MPI_DOUBLE,rank_yZ,sendtag,comm,&req1[17]); - MPI_Irecv(recvbuf_Yz, recvCount_Yz,MPI_DOUBLE,rank_Yz,recvtag,comm,&req2[17]); - //................................................................................... - //................................................................................... - // Wait for completion of Indicator Field communication - //................................................................................... - MPI_Waitall(18,req1,stat1); - MPI_Waitall(18,req2,stat2); - dvc_Barrier(); - //................................................................................... - //................................................................................... - /* dvc_UnpackValues(faceGrid, packThreads, dvcSendList_x, sendCount_x,sendbuf_x, Phi, N); + //................................................................................... + dvc_PackValues(dvcSendList_x, sendCount_x, sendbuf_x, Phi, N); + dvc_PackValues(dvcSendList_y, sendCount_y, sendbuf_y, Phi, N); + dvc_PackValues(dvcSendList_z, sendCount_z, sendbuf_z, Phi, N); + dvc_PackValues(dvcSendList_X, sendCount_X, sendbuf_X, Phi, N); + dvc_PackValues(dvcSendList_Y, sendCount_Y, sendbuf_Y, Phi, N); + dvc_PackValues(dvcSendList_Z, sendCount_Z, sendbuf_Z, Phi, N); + dvc_PackValues(dvcSendList_xy, sendCount_xy, sendbuf_xy, Phi, N); + dvc_PackValues(dvcSendList_xY, sendCount_xY, sendbuf_xY, Phi, N); + dvc_PackValues(dvcSendList_Xy, sendCount_Xy, sendbuf_Xy, Phi, N); + dvc_PackValues(dvcSendList_XY, sendCount_XY, sendbuf_XY, Phi, N); + dvc_PackValues(dvcSendList_xz, sendCount_xz, sendbuf_xz, Phi, N); + dvc_PackValues(dvcSendList_xZ, sendCount_xZ, sendbuf_xZ, Phi, N); + dvc_PackValues(dvcSendList_Xz, sendCount_Xz, sendbuf_Xz, Phi, N); + dvc_PackValues(dvcSendList_XZ, sendCount_XZ, sendbuf_XZ, Phi, N); + dvc_PackValues(dvcSendList_yz, sendCount_yz, sendbuf_yz, Phi, N); + dvc_PackValues(dvcSendList_yZ, sendCount_yZ, sendbuf_yZ, Phi, N); + dvc_PackValues(dvcSendList_Yz, sendCount_Yz, sendbuf_Yz, Phi, N); + dvc_PackValues(dvcSendList_YZ, sendCount_YZ, sendbuf_YZ, Phi, N); + //................................................................................... + // Send / Recv all the phase indcator field values + //................................................................................... + MPI_Isend(sendbuf_x, sendCount_x, MPI_DOUBLE, rank_x, sendtag, comm, + &req1[0]); + MPI_Irecv(recvbuf_X, recvCount_X, MPI_DOUBLE, rank_X, recvtag, comm, + &req2[0]); + MPI_Isend(sendbuf_X, sendCount_X, MPI_DOUBLE, rank_X, sendtag, comm, + &req1[1]); + MPI_Irecv(recvbuf_x, recvCount_x, MPI_DOUBLE, rank_x, recvtag, comm, + &req2[1]); + MPI_Isend(sendbuf_y, sendCount_y, MPI_DOUBLE, rank_y, sendtag, comm, + &req1[2]); + MPI_Irecv(recvbuf_Y, recvCount_Y, MPI_DOUBLE, rank_Y, recvtag, comm, + &req2[2]); + MPI_Isend(sendbuf_Y, sendCount_Y, MPI_DOUBLE, rank_Y, sendtag, comm, + &req1[3]); + MPI_Irecv(recvbuf_y, recvCount_y, MPI_DOUBLE, rank_y, recvtag, comm, + &req2[3]); + MPI_Isend(sendbuf_z, sendCount_z, MPI_DOUBLE, rank_z, sendtag, comm, + &req1[4]); + MPI_Irecv(recvbuf_Z, recvCount_Z, MPI_DOUBLE, rank_Z, recvtag, comm, + &req2[4]); + MPI_Isend(sendbuf_Z, sendCount_Z, MPI_DOUBLE, rank_Z, sendtag, comm, + &req1[5]); + MPI_Irecv(recvbuf_z, recvCount_z, MPI_DOUBLE, rank_z, recvtag, comm, + &req2[5]); + MPI_Isend(sendbuf_xy, sendCount_xy, MPI_DOUBLE, rank_xy, sendtag, + comm, &req1[6]); + MPI_Irecv(recvbuf_XY, recvCount_XY, MPI_DOUBLE, rank_XY, recvtag, + comm, &req2[6]); + MPI_Isend(sendbuf_XY, sendCount_XY, MPI_DOUBLE, rank_XY, sendtag, + comm, &req1[7]); + MPI_Irecv(recvbuf_xy, recvCount_xy, MPI_DOUBLE, rank_xy, recvtag, + comm, &req2[7]); + MPI_Isend(sendbuf_Xy, sendCount_Xy, MPI_DOUBLE, rank_Xy, sendtag, + comm, &req1[8]); + MPI_Irecv(recvbuf_xY, recvCount_xY, MPI_DOUBLE, rank_xY, recvtag, + comm, &req2[8]); + MPI_Isend(sendbuf_xY, sendCount_xY, MPI_DOUBLE, rank_xY, sendtag, + comm, &req1[9]); + MPI_Irecv(recvbuf_Xy, recvCount_Xy, MPI_DOUBLE, rank_Xy, recvtag, + comm, &req2[9]); + MPI_Isend(sendbuf_xz, sendCount_xz, MPI_DOUBLE, rank_xz, sendtag, + comm, &req1[10]); + MPI_Irecv(recvbuf_XZ, recvCount_XZ, MPI_DOUBLE, rank_XZ, recvtag, + comm, &req2[10]); + MPI_Isend(sendbuf_XZ, sendCount_XZ, MPI_DOUBLE, rank_XZ, sendtag, + comm, &req1[11]); + MPI_Irecv(recvbuf_xz, recvCount_xz, MPI_DOUBLE, rank_xz, recvtag, + comm, &req2[11]); + MPI_Isend(sendbuf_Xz, sendCount_Xz, MPI_DOUBLE, rank_Xz, sendtag, + comm, &req1[12]); + MPI_Irecv(recvbuf_xZ, recvCount_xZ, MPI_DOUBLE, rank_xZ, recvtag, + comm, &req2[12]); + MPI_Isend(sendbuf_xZ, sendCount_xZ, MPI_DOUBLE, rank_xZ, sendtag, + comm, &req1[13]); + MPI_Irecv(recvbuf_Xz, recvCount_Xz, MPI_DOUBLE, rank_Xz, recvtag, + comm, &req2[13]); + MPI_Isend(sendbuf_yz, sendCount_yz, MPI_DOUBLE, rank_yz, sendtag, + comm, &req1[14]); + MPI_Irecv(recvbuf_YZ, recvCount_YZ, MPI_DOUBLE, rank_YZ, recvtag, + comm, &req2[14]); + MPI_Isend(sendbuf_YZ, sendCount_YZ, MPI_DOUBLE, rank_YZ, sendtag, + comm, &req1[15]); + MPI_Irecv(recvbuf_yz, recvCount_yz, MPI_DOUBLE, rank_yz, recvtag, + comm, &req2[15]); + MPI_Isend(sendbuf_Yz, sendCount_Yz, MPI_DOUBLE, rank_Yz, sendtag, + comm, &req1[16]); + MPI_Irecv(recvbuf_yZ, recvCount_yZ, MPI_DOUBLE, rank_yZ, recvtag, + comm, &req2[16]); + MPI_Isend(sendbuf_yZ, sendCount_yZ, MPI_DOUBLE, rank_yZ, sendtag, + comm, &req1[17]); + MPI_Irecv(recvbuf_Yz, recvCount_Yz, MPI_DOUBLE, rank_Yz, recvtag, + comm, &req2[17]); + //................................................................................... + //................................................................................... + // Wait for completion of Indicator Field communication + //................................................................................... + MPI_Waitall(18, req1, stat1); + MPI_Waitall(18, req2, stat2); + dvc_Barrier(); + //................................................................................... + //................................................................................... + /* dvc_UnpackValues(faceGrid, packThreads, dvcSendList_x, sendCount_x,sendbuf_x, Phi, N); dvc_UnpackValues(faceGrid, packThreads, dvcSendList_y, sendCount_y,sendbuf_y, Phi, N); dvc_UnpackValues(faceGrid, packThreads, dvcSendList_z, sendCount_z,sendbuf_z, Phi, N); dvc_UnpackValues(faceGrid, packThreads, dvcSendList_X, sendCount_X,sendbuf_X, Phi, N); dvc_UnpackValues(faceGrid, packThreads, dvcSendList_Y, sendCount_Y,sendbuf_Y, Phi, N); dvc_UnpackValues(faceGrid, packThreads, dvcSendList_Z, sendCount_Z,sendbuf_Z, Phi, N); - */ - dvc_UnpackValues(dvcRecvList_x, recvCount_x,recvbuf_x, Phi, N); - dvc_UnpackValues(dvcRecvList_y, recvCount_y,recvbuf_y, Phi, N); - dvc_UnpackValues(dvcRecvList_z, recvCount_z,recvbuf_z, Phi, N); - dvc_UnpackValues(dvcRecvList_X, recvCount_X,recvbuf_X, Phi, N); - dvc_UnpackValues(dvcRecvList_Y, recvCount_Y,recvbuf_Y, Phi, N); - dvc_UnpackValues(dvcRecvList_Z, recvCount_Z,recvbuf_Z, Phi, N); - dvc_UnpackValues(dvcRecvList_xy, recvCount_xy,recvbuf_xy, Phi, N); - dvc_UnpackValues(dvcRecvList_xY, recvCount_xY,recvbuf_xY, Phi, N); - dvc_UnpackValues(dvcRecvList_Xy, recvCount_Xy,recvbuf_Xy, Phi, N); - dvc_UnpackValues(dvcRecvList_XY, recvCount_XY,recvbuf_XY, Phi, N); - dvc_UnpackValues(dvcRecvList_xz, recvCount_xz,recvbuf_xz, Phi, N); - dvc_UnpackValues(dvcRecvList_xZ, recvCount_xZ,recvbuf_xZ, Phi, N); - dvc_UnpackValues(dvcRecvList_Xz, recvCount_Xz,recvbuf_Xz, Phi, N); - dvc_UnpackValues(dvcRecvList_XZ, recvCount_XZ,recvbuf_XZ, Phi, N); - dvc_UnpackValues(dvcRecvList_yz, recvCount_yz,recvbuf_yz, Phi, N); - dvc_UnpackValues(dvcRecvList_yZ, recvCount_yZ,recvbuf_yZ, Phi, N); - dvc_UnpackValues(dvcRecvList_Yz, recvCount_Yz,recvbuf_Yz, Phi, N); - dvc_UnpackValues(dvcRecvList_YZ, recvCount_YZ,recvbuf_YZ, Phi, N); - //................................................................................... - MPI_Barrier(comm); + */ + dvc_UnpackValues(dvcRecvList_x, recvCount_x, recvbuf_x, Phi, N); + dvc_UnpackValues(dvcRecvList_y, recvCount_y, recvbuf_y, Phi, N); + dvc_UnpackValues(dvcRecvList_z, recvCount_z, recvbuf_z, Phi, N); + dvc_UnpackValues(dvcRecvList_X, recvCount_X, recvbuf_X, Phi, N); + dvc_UnpackValues(dvcRecvList_Y, recvCount_Y, recvbuf_Y, Phi, N); + dvc_UnpackValues(dvcRecvList_Z, recvCount_Z, recvbuf_Z, Phi, N); + dvc_UnpackValues(dvcRecvList_xy, recvCount_xy, recvbuf_xy, Phi, N); + dvc_UnpackValues(dvcRecvList_xY, recvCount_xY, recvbuf_xY, Phi, N); + dvc_UnpackValues(dvcRecvList_Xy, recvCount_Xy, recvbuf_Xy, Phi, N); + dvc_UnpackValues(dvcRecvList_XY, recvCount_XY, recvbuf_XY, Phi, N); + dvc_UnpackValues(dvcRecvList_xz, recvCount_xz, recvbuf_xz, Phi, N); + dvc_UnpackValues(dvcRecvList_xZ, recvCount_xZ, recvbuf_xZ, Phi, N); + dvc_UnpackValues(dvcRecvList_Xz, recvCount_Xz, recvbuf_Xz, Phi, N); + dvc_UnpackValues(dvcRecvList_XZ, recvCount_XZ, recvbuf_XZ, Phi, N); + dvc_UnpackValues(dvcRecvList_yz, recvCount_yz, recvbuf_yz, Phi, N); + dvc_UnpackValues(dvcRecvList_yZ, recvCount_yZ, recvbuf_yZ, Phi, N); + dvc_UnpackValues(dvcRecvList_Yz, recvCount_Yz, recvbuf_Yz, Phi, N); + dvc_UnpackValues(dvcRecvList_YZ, recvCount_YZ, recvbuf_YZ, Phi, N); + //................................................................................... + MPI_Barrier(comm); - // Iteration completed! - timestep++; + // Iteration completed! + timestep++; - if (timestep%RESTART_INTERVAL == 0){ - // Copy the data to the CPU - dvc_CopyToHost(cDistEven,f_even,10*N*sizeof(double)); - dvc_CopyToHost(cDistOdd,f_odd,9*N*sizeof(double)); - dvc_CopyToHost(cDen,Copy,2*N*sizeof(double)); - // Read in the restart file to CPU buffers - WriteCheckpoint(LocalRestartFile, cDen, cDistEven, cDistOdd, N); - } - } - // End the bubble loop - //........................................................................... - // Copy the phase indicator field for the later timestep - dvc_Barrier(); - dvc_ComputePressureD3Q19(ID,f_even,f_odd,Pressure,Nx,Ny,Nz,S); - dvc_CopyToHost(Phase_tminus.data,Phi,N*sizeof(double)); - dvc_CopyToHost(Phase_tplus.data,Phi,N*sizeof(double)); - dvc_CopyToHost(Phase.data,Phi,N*sizeof(double)); - dvc_CopyToHost(Press.data,Pressure,N*sizeof(double)); + if (timestep % RESTART_INTERVAL == 0) { + // Copy the data to the CPU + dvc_CopyToHost(cDistEven, f_even, 10 * N * sizeof(double)); + dvc_CopyToHost(cDistOdd, f_odd, 9 * N * sizeof(double)); + dvc_CopyToHost(cDen, Copy, 2 * N * sizeof(double)); + // Read in the restart file to CPU buffers + WriteCheckpoint(LocalRestartFile, cDen, cDistEven, cDistOdd, N); + } + } + // End the bubble loop + //........................................................................... + // Copy the phase indicator field for the later timestep + dvc_Barrier(); + dvc_ComputePressureD3Q19(ID, f_even, f_odd, Pressure, Nx, Ny, Nz, S); + dvc_CopyToHost(Phase_tminus.data, Phi, N * sizeof(double)); + dvc_CopyToHost(Phase_tplus.data, Phi, N * sizeof(double)); + dvc_CopyToHost(Phase.data, Phi, N * sizeof(double)); + dvc_CopyToHost(Press.data, Pressure, N * sizeof(double)); - //........................................................................... - // Calculate the time derivative of the phase indicator field - for (n=0; n 0 ){ + //........................................................................... + // Compute volume averages + for (p = 0; p < 8; p++) { + if (SignDist(i + cube[p][0], j + cube[p][1], k + cube[p][2]) > + 0) { - // 1-D index for this cube corner - n = i+cube[p][0] + (j+cube[p][1])*Nx + (k+cube[p][2])*Nx*Ny; + // 1-D index for this cube corner + n = i + cube[p][0] + (j + cube[p][1]) * Nx + + (k + cube[p][2]) * Nx * Ny; - // Compute the non-wetting phase volume contribution - if ( Phase(i+cube[p][0],j+cube[p][1],k+cube[p][2]) > 0 ) - nwp_volume += 0.125; + // Compute the non-wetting phase volume contribution + if (Phase(i + cube[p][0], j + cube[p][1], k + cube[p][2]) > + 0) + nwp_volume += 0.125; - // volume averages over the non-wetting phase - if ( Phase(i+cube[p][0],j+cube[p][1],k+cube[p][2]) > 0.99999 ){ - // volume the excludes the interfacial region - vol_n += 0.125; - // pressure - pan += 0.125*Press.data[n]; - // velocity - van(0) += 0.125*Vel[3*n]; - van(1) += 0.125*Vel[3*n+1]; - van(2) += 0.125*Vel[3*n+2]; - } + // volume averages over the non-wetting phase + if (Phase(i + cube[p][0], j + cube[p][1], k + cube[p][2]) > + 0.99999) { + // volume the excludes the interfacial region + vol_n += 0.125; + // pressure + pan += 0.125 * Press.data[n]; + // velocity + van(0) += 0.125 * Vel[3 * n]; + van(1) += 0.125 * Vel[3 * n + 1]; + van(2) += 0.125 * Vel[3 * n + 2]; + } - // volume averages over the wetting phase - if ( Phase(i+cube[p][0],j+cube[p][1],k+cube[p][2]) < -0.99999 ){ - // volume the excludes the interfacial region - vol_w += 0.125; - // pressure - paw += 0.125*Press.data[n]; - // velocity - vaw(0) += 0.125*Vel[3*n]; - vaw(1) += 0.125*Vel[3*n+1]; - vaw(2) += 0.125*Vel[3*n+2]; - } - } - } + // volume averages over the wetting phase + if (Phase(i + cube[p][0], j + cube[p][1], k + cube[p][2]) < + -0.99999) { + // volume the excludes the interfacial region + vol_w += 0.125; + // pressure + paw += 0.125 * Press.data[n]; + // velocity + vaw(0) += 0.125 * Vel[3 * n]; + vaw(1) += 0.125 * Vel[3 * n + 1]; + vaw(2) += 0.125 * Vel[3 * n + 2]; + } + } + } - //........................................................................... - // Construct the interfaces and common curve - pmmc_ConstructLocalCube(SignDist, Phase, solid_isovalue, fluid_isovalue, - nw_pts, nw_tris, values, ns_pts, ns_tris, ws_pts, ws_tris, - local_nws_pts, nws_pts, nws_seg, local_sol_pts, local_sol_tris, - n_local_sol_tris, n_local_sol_pts, n_nw_pts, n_nw_tris, - n_ws_pts, n_ws_tris, n_ns_tris, n_ns_pts, n_local_nws_pts, n_nws_pts, n_nws_seg, - i, j, k, Nx, Ny, Nz); + //........................................................................... + // Construct the interfaces and common curve + pmmc_ConstructLocalCube( + SignDist, Phase, solid_isovalue, fluid_isovalue, nw_pts, + nw_tris, values, ns_pts, ns_tris, ws_pts, ws_tris, + local_nws_pts, nws_pts, nws_seg, local_sol_pts, local_sol_tris, + n_local_sol_tris, n_local_sol_pts, n_nw_pts, n_nw_tris, + n_ws_pts, n_ws_tris, n_ns_tris, n_ns_pts, n_local_nws_pts, + n_nws_pts, n_nws_seg, i, j, k, Nx, Ny, Nz); - // Integrate the contact angle - efawns += pmmc_CubeContactAngle(CubeValues,Values,Phase_x,Phase_y,Phase_z,SignDist_x,SignDist_y,SignDist_z, - local_nws_pts,i,j,k,n_local_nws_pts); + // Integrate the contact angle + efawns += pmmc_CubeContactAngle( + CubeValues, Values, Phase_x, Phase_y, Phase_z, SignDist_x, + SignDist_y, SignDist_z, local_nws_pts, i, j, k, + n_local_nws_pts); - // Integrate the mean curvature - Jwn += pmmc_CubeSurfaceInterpValue(CubeValues,MeanCurvature,nw_pts,nw_tris,Values,i,j,k,n_nw_pts,n_nw_tris); + // Integrate the mean curvature + Jwn += pmmc_CubeSurfaceInterpValue(CubeValues, MeanCurvature, + nw_pts, nw_tris, Values, i, j, k, + n_nw_pts, n_nw_tris); - pmmc_InterfaceSpeed(dPdt, Phase_x, Phase_y, Phase_z, CubeValues, nw_pts, nw_tris, - NormalVector, InterfaceSpeed, vawn, i, j, k, n_nw_pts, n_nw_tris); + pmmc_InterfaceSpeed(dPdt, Phase_x, Phase_y, Phase_z, CubeValues, + nw_pts, nw_tris, NormalVector, InterfaceSpeed, + vawn, i, j, k, n_nw_pts, n_nw_tris); - //........................................................................... - // Compute the Interfacial Areas, Common Line length - /* awn += pmmc_CubeSurfaceArea(nw_pts,nw_tris,n_nw_tris); + //........................................................................... + // Compute the Interfacial Areas, Common Line length + /* awn += pmmc_CubeSurfaceArea(nw_pts,nw_tris,n_nw_tris); ans += pmmc_CubeSurfaceArea(ns_pts,ns_tris,n_ns_tris); aws += pmmc_CubeSurfaceArea(ws_pts,ws_tris,n_ws_tris); */ - As += pmmc_CubeSurfaceArea(local_sol_pts,local_sol_tris,n_local_sol_tris); + As += pmmc_CubeSurfaceArea(local_sol_pts, local_sol_tris, + n_local_sol_tris); - // Compute the surface orientation and the interfacial area - awn += pmmc_CubeSurfaceOrientation(Gwn,nw_pts,nw_tris,n_nw_tris); - ans += pmmc_CubeSurfaceOrientation(Gns,ns_pts,ns_tris,n_ns_tris); - aws += pmmc_CubeSurfaceOrientation(Gws,ws_pts,ws_tris,n_ws_tris); - lwns += pmmc_CubeCurveLength(local_nws_pts,n_local_nws_pts); - //........................................................................... - } - //........................................................................... - MPI_Barrier(comm); - MPI_Allreduce(&nwp_volume,&nwp_volume_global,1,MPI_DOUBLE,MPI_SUM,comm); - MPI_Allreduce(&awn,&awn_global,1,MPI_DOUBLE,MPI_SUM,comm); - MPI_Allreduce(&ans,&ans_global,1,MPI_DOUBLE,MPI_SUM,comm); - MPI_Allreduce(&aws,&aws_global,1,MPI_DOUBLE,MPI_SUM,comm); - MPI_Allreduce(&lwns,&lwns_global,1,MPI_DOUBLE,MPI_SUM,comm); - MPI_Allreduce(&As,&As_global,1,MPI_DOUBLE,MPI_SUM,comm); - MPI_Allreduce(&Jwn,&Jwn_global,1,MPI_DOUBLE,MPI_SUM,comm); - MPI_Allreduce(&efawns,&efawns_global,1,MPI_DOUBLE,MPI_SUM,comm); - // Phase averages - MPI_Allreduce(&vol_w,&vol_w_global,1,MPI_DOUBLE,MPI_SUM,comm); - MPI_Allreduce(&vol_n,&vol_n_global,1,MPI_DOUBLE,MPI_SUM,comm); - MPI_Allreduce(&paw,&paw_global,1,MPI_DOUBLE,MPI_SUM,comm); - MPI_Allreduce(&pan,&pan_global,1,MPI_DOUBLE,MPI_SUM,comm); - MPI_Allreduce(&vaw(0),&vaw_global(0),3,MPI_DOUBLE,MPI_SUM,comm); - MPI_Allreduce(&van(0),&van_global(0),3,MPI_DOUBLE,MPI_SUM,comm); - MPI_Allreduce(&vawn(0),&vawn_global(0),3,MPI_DOUBLE,MPI_SUM,comm); - MPI_Allreduce(&Gwn(0),&Gwn_global(0),6,MPI_DOUBLE,MPI_SUM,comm); - MPI_Allreduce(&Gns(0),&Gns_global(0),6,MPI_DOUBLE,MPI_SUM,comm); - MPI_Allreduce(&Gws(0),&Gws_global(0),6,MPI_DOUBLE,MPI_SUM,comm); - MPI_Barrier(comm); - //......................................................................... - // Compute the change in the total surface energy based on the defined interval - // See McClure, Prins and Miller (2013) - //......................................................................... - dAwn += awn_global; - dAns += ans_global; - dEs = 6.01603*alpha*(dAwn + 1.05332*Ps*dAns); - dAwn = -awn_global; // Get ready for the next analysis interval - dAns = -ans_global; + // Compute the surface orientation and the interfacial area + awn += pmmc_CubeSurfaceOrientation(Gwn, nw_pts, nw_tris, n_nw_tris); + ans += pmmc_CubeSurfaceOrientation(Gns, ns_pts, ns_tris, n_ns_tris); + aws += pmmc_CubeSurfaceOrientation(Gws, ws_pts, ws_tris, n_ws_tris); + lwns += pmmc_CubeCurveLength(local_nws_pts, n_local_nws_pts); + //........................................................................... + } + //........................................................................... + MPI_Barrier(comm); + MPI_Allreduce(&nwp_volume, &nwp_volume_global, 1, MPI_DOUBLE, MPI_SUM, + comm); + MPI_Allreduce(&awn, &awn_global, 1, MPI_DOUBLE, MPI_SUM, comm); + MPI_Allreduce(&ans, &ans_global, 1, MPI_DOUBLE, MPI_SUM, comm); + MPI_Allreduce(&aws, &aws_global, 1, MPI_DOUBLE, MPI_SUM, comm); + MPI_Allreduce(&lwns, &lwns_global, 1, MPI_DOUBLE, MPI_SUM, comm); + MPI_Allreduce(&As, &As_global, 1, MPI_DOUBLE, MPI_SUM, comm); + MPI_Allreduce(&Jwn, &Jwn_global, 1, MPI_DOUBLE, MPI_SUM, comm); + MPI_Allreduce(&efawns, &efawns_global, 1, MPI_DOUBLE, MPI_SUM, comm); + // Phase averages + MPI_Allreduce(&vol_w, &vol_w_global, 1, MPI_DOUBLE, MPI_SUM, comm); + MPI_Allreduce(&vol_n, &vol_n_global, 1, MPI_DOUBLE, MPI_SUM, comm); + MPI_Allreduce(&paw, &paw_global, 1, MPI_DOUBLE, MPI_SUM, comm); + MPI_Allreduce(&pan, &pan_global, 1, MPI_DOUBLE, MPI_SUM, comm); + MPI_Allreduce(&vaw(0), &vaw_global(0), 3, MPI_DOUBLE, MPI_SUM, comm); + MPI_Allreduce(&van(0), &van_global(0), 3, MPI_DOUBLE, MPI_SUM, comm); + MPI_Allreduce(&vawn(0), &vawn_global(0), 3, MPI_DOUBLE, MPI_SUM, comm); + MPI_Allreduce(&Gwn(0), &Gwn_global(0), 6, MPI_DOUBLE, MPI_SUM, comm); + MPI_Allreduce(&Gns(0), &Gns_global(0), 6, MPI_DOUBLE, MPI_SUM, comm); + MPI_Allreduce(&Gws(0), &Gws_global(0), 6, MPI_DOUBLE, MPI_SUM, comm); + MPI_Barrier(comm); + //......................................................................... + // Compute the change in the total surface energy based on the defined interval + // See McClure, Prins and Miller (2013) + //......................................................................... + dAwn += awn_global; + dAns += ans_global; + dEs = 6.01603 * alpha * (dAwn + 1.05332 * Ps * dAns); + dAwn = -awn_global; // Get ready for the next analysis interval + dAns = -ans_global; - // Normalize the phase averages - // (density of both components = 1.0) - paw_global = paw_global / vol_w_global; - vaw_global(0) = vaw_global(0) / vol_w_global; - vaw_global(1) = vaw_global(1) / vol_w_global; - vaw_global(2) = vaw_global(2) / vol_w_global; - pan_global = pan_global / vol_n_global; - van_global(0) = van_global(0) / vol_n_global; - van_global(1) = van_global(1) / vol_n_global; - van_global(2) = van_global(2) / vol_n_global; + // Normalize the phase averages + // (density of both components = 1.0) + paw_global = paw_global / vol_w_global; + vaw_global(0) = vaw_global(0) / vol_w_global; + vaw_global(1) = vaw_global(1) / vol_w_global; + vaw_global(2) = vaw_global(2) / vol_w_global; + pan_global = pan_global / vol_n_global; + van_global(0) = van_global(0) / vol_n_global; + van_global(1) = van_global(1) / vol_n_global; + van_global(2) = van_global(2) / vol_n_global; - // Normalize surface averages by the interfacial area - Jwn_global /= awn_global; - efawns_global /= lwns_global; + // Normalize surface averages by the interfacial area + Jwn_global /= awn_global; + efawns_global /= lwns_global; - if (awn_global > 0.0) for (i=0; i<3; i++) vawn_global(i) /= awn_global; - if (awn_global > 0.0) for (i=0; i<6; i++) Gwn_global(i) /= awn_global; - if (ans_global > 0.0) for (i=0; i<6; i++) Gns_global(i) /= ans_global; - if (aws_global > 0.0) for (i=0; i<6; i++) Gws_global(i) /= aws_global; + if (awn_global > 0.0) + for (i = 0; i < 3; i++) + vawn_global(i) /= awn_global; + if (awn_global > 0.0) + for (i = 0; i < 6; i++) + Gwn_global(i) /= awn_global; + if (ans_global > 0.0) + for (i = 0; i < 6; i++) + Gns_global(i) /= ans_global; + if (aws_global > 0.0) + for (i = 0; i < 6; i++) + Gws_global(i) /= aws_global; - sat_w = 1.0 - nwp_volume_global*iVol_global/porosity; - // Compute the specific interfacial areas and common line length (per unit volume) - awn_global = awn_global*iVol_global; - ans_global = ans_global*iVol_global; - aws_global = aws_global*iVol_global; - lwns_global = lwns_global*iVol_global; - dEs = dEs*iVol_global; + sat_w = 1.0 - nwp_volume_global * iVol_global / porosity; + // Compute the specific interfacial areas and common line length (per unit volume) + awn_global = awn_global * iVol_global; + ans_global = ans_global * iVol_global; + aws_global = aws_global * iVol_global; + lwns_global = lwns_global * iVol_global; + dEs = dEs * iVol_global; - //......................................................................... - if (rank==0){ - /* printf("-------------------------------- \n"); + //......................................................................... + if (rank == 0) { + /* printf("-------------------------------- \n"); printf("Timestep = %i \n", timestep); printf("NWP volume = %f \n", nwp_volume_global); printf("Area wn = %f \n", awn_global); @@ -2440,46 +3194,54 @@ int main(int argc, char **argv) vx_w_global, vy_w_global, vz_w_global, vx_n_global, vy_n_global, vz_n_global); */ - printf("%.5g ",BubbleRadius); // bubble radius - printf("%.5g %.5g %.5g ",sat_w,paw_global,pan_global); // saturation and pressure - printf("%.5g ",awn_global); // interfacial area - printf("%.5g ",Jwn_global); // curvature of wn interface - printf("%.5g %.5g %.5g %.5g %.5g %.5g \n", - Gwn_global(0),Gwn_global(1),Gwn_global(2),Gwn_global(3),Gwn_global(4),Gwn_global(5)); // orientation of wn interface - } - - } - //************************************************************************/ - dvc_Barrier(); - MPI_Barrier(comm); - stoptime = MPI_Wtime(); - if (rank==0) printf("-------------------------------------------------------------------\n"); - // Compute the walltime per timestep - cputime = (stoptime - starttime)/timestep; - // Performance obtained from each node - double MLUPS = double(Nx*Ny*Nz)/cputime/1000000; - - if (rank==0) printf("********************************************************\n"); - if (rank==0) printf("CPU time = %f \n", cputime); - if (rank==0) printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); - MLUPS *= nprocs; - if (rank==0) printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); - if (rank==0) printf("********************************************************\n"); - - //************************************************************************/ - // Write out the phase indicator field - //************************************************************************/ - sprintf(LocalRankFilename,"%s%s","Phase.",LocalRankString); - // printf("Local File Name = %s \n",LocalRankFilename); -// dvc_CopyToHost(Phase.data,Phi,N*sizeof(double)); + printf("%.5g ", BubbleRadius); // bubble radius + printf("%.5g %.5g %.5g ", sat_w, paw_global, + pan_global); // saturation and pressure + printf("%.5g ", awn_global); // interfacial area + printf("%.5g ", Jwn_global); // curvature of wn interface + printf("%.5g %.5g %.5g %.5g %.5g %.5g \n", Gwn_global(0), + Gwn_global(1), Gwn_global(2), Gwn_global(3), Gwn_global(4), + Gwn_global(5)); // orientation of wn interface + } + } + //************************************************************************/ + dvc_Barrier(); + MPI_Barrier(comm); + stoptime = MPI_Wtime(); + if (rank == 0) + printf("---------------------------------------------------------------" + "----\n"); + // Compute the walltime per timestep + cputime = (stoptime - starttime) / timestep; + // Performance obtained from each node + double MLUPS = double(Nx * Ny * Nz) / cputime / 1000000; - FILE *PHASE; - PHASE = fopen(LocalRankFilename,"wb"); - fwrite(Press.data,8,N,PHASE); -// fwrite(MeanCurvature.data,8,N,PHASE); - fclose(PHASE); - -/* double *DensityValues; + if (rank == 0) + printf("********************************************************\n"); + if (rank == 0) + printf("CPU time = %f \n", cputime); + if (rank == 0) + printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); + MLUPS *= nprocs; + if (rank == 0) + printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); + if (rank == 0) + printf("********************************************************\n"); + + //************************************************************************/ + // Write out the phase indicator field + //************************************************************************/ + sprintf(LocalRankFilename, "%s%s", "Phase.", LocalRankString); + // printf("Local File Name = %s \n",LocalRankFilename); + // dvc_CopyToHost(Phase.data,Phi,N*sizeof(double)); + + FILE *PHASE; + PHASE = fopen(LocalRankFilename, "wb"); + fwrite(Press.data, 8, N, PHASE); + // fwrite(MeanCurvature.data,8,N,PHASE); + fclose(PHASE); + + /* double *DensityValues; DensityValues = new double [2*N]; dvc_CopyToHost(DensityValues,Copy,2*N*sizeof(double)); FILE *PHASE; @@ -2488,8 +3250,8 @@ int main(int argc, char **argv) fclose(PHASE); */ //************************************************************************/ - // **************************************************** - MPI_Barrier(comm); - MPI_Finalize(); - // **************************************************** + // **************************************************** + MPI_Barrier(comm); + MPI_Finalize(); + // **************************************************** } diff --git a/cpu/thermal.cpp b/cpu/thermal.cpp index 9e1f81fb..5dae709d 100644 --- a/cpu/thermal.cpp +++ b/cpu/thermal.cpp @@ -30,6 +30,3 @@ */ // cpu implementation for thermal lattice boltzmann methods // copyright James McClure, 2014 - - - diff --git a/models/ColorModel.cpp b/models/ColorModel.cpp index b27efe93..90f0f157 100644 --- a/models/ColorModel.cpp +++ b/models/ColorModel.cpp @@ -17,6 +17,7 @@ /* color lattice boltzmann model */ + #include "models/ColorModel.h" #include "analysis/distance.h" #include "analysis/morphology.h" @@ -25,1112 +26,1197 @@ color lattice boltzmann model #include #include - -ScaLBL_ColorModel::ScaLBL_ColorModel(int RANK, int NP, const Utilities::MPI& COMM): - rank(RANK), nprocs(NP), Restart(0), timestep(0), timestepMax(0), - tauA(0), tauB(0), rhoA(0), rhoB(0), alpha(0), beta(0), - Fx(0), Fy(0), Fz(0), flux(0), din(0), dout(0), - inletA(0), inletB(0), outletA(0), outletB(0), - Nx(0), Ny(0), Nz(0), N(0), Np(0), nprocx(0), nprocy(0), nprocz(0), - BoundaryCondition(0), Lx(0), Ly(0), Lz(0), id(nullptr), - NeighborList(nullptr), dvcMap(nullptr), fq(nullptr), Aq(nullptr), Bq(nullptr), - Den(nullptr), Phi(nullptr), ColorGrad(nullptr), Velocity(nullptr), Pressure(nullptr), - comm(COMM) -{ - REVERSE_FLOW_DIRECTION = false; +ScaLBL_ColorModel::ScaLBL_ColorModel(int RANK, int NP, + const Utilities::MPI &COMM) + : rank(RANK), nprocs(NP), Restart(0), timestep(0), timestepMax(0), tauA(0), + tauB(0), rhoA(0), rhoB(0), alpha(0), beta(0), Fx(0), Fy(0), Fz(0), + flux(0), din(0), dout(0), inletA(0), inletB(0), outletA(0), outletB(0), + Nx(0), Ny(0), Nz(0), N(0), Np(0), nprocx(0), nprocy(0), nprocz(0), + BoundaryCondition(0), Lx(0), Ly(0), Lz(0), id(nullptr), + NeighborList(nullptr), dvcMap(nullptr), fq(nullptr), Aq(nullptr), + Bq(nullptr), Den(nullptr), Phi(nullptr), ColorGrad(nullptr), + Velocity(nullptr), Pressure(nullptr), comm(COMM) { + REVERSE_FLOW_DIRECTION = false; } -ScaLBL_ColorModel::~ScaLBL_ColorModel() -{ - delete [] id; - ScaLBL_FreeDeviceMemory( NeighborList ); - ScaLBL_FreeDeviceMemory( dvcMap ); - ScaLBL_FreeDeviceMemory( fq ); - ScaLBL_FreeDeviceMemory( Aq ); - ScaLBL_FreeDeviceMemory( Bq ); - ScaLBL_FreeDeviceMemory( Den ); - ScaLBL_FreeDeviceMemory( Phi ); - ScaLBL_FreeDeviceMemory( Pressure ); - ScaLBL_FreeDeviceMemory( Velocity ); - ScaLBL_FreeDeviceMemory( ColorGrad ); +ScaLBL_ColorModel::~ScaLBL_ColorModel() { + delete[] id; + ScaLBL_FreeDeviceMemory(NeighborList); + ScaLBL_FreeDeviceMemory(dvcMap); + ScaLBL_FreeDeviceMemory(fq); + ScaLBL_FreeDeviceMemory(Aq); + ScaLBL_FreeDeviceMemory(Bq); + ScaLBL_FreeDeviceMemory(Den); + ScaLBL_FreeDeviceMemory(Phi); + ScaLBL_FreeDeviceMemory(Pressure); + ScaLBL_FreeDeviceMemory(Velocity); + ScaLBL_FreeDeviceMemory(ColorGrad); } -/*void ScaLBL_ColorModel::WriteCheckpoint(const char *FILENAME, const double *cPhi, const double *cfq, int Np) -{ - int q,n; - double value; - ofstream File(FILENAME,ios::binary); - for (n=0; n(filename); + domain_db = db->getDatabase("Domain"); + color_db = db->getDatabase("Color"); + analysis_db = db->getDatabase("Analysis"); + vis_db = db->getDatabase("Visualization"); + + // set defaults + timestepMax = 100000; + tauA = tauB = 1.0; + rhoA = rhoB = 1.0; + Fx = Fy = Fz = 0.0; + alpha = 1e-3; + beta = 0.95; + Restart = false; + din = dout = 1.0; + flux = 0.0; + + // Color Model parameters + if (color_db->keyExists("timestepMax")) { + timestepMax = color_db->getScalar("timestepMax"); + } + if (color_db->keyExists("tauA")) { + tauA = color_db->getScalar("tauA"); + } + if (color_db->keyExists("tauB")) { + tauB = color_db->getScalar("tauB"); + } + if (color_db->keyExists("rhoA")) { + rhoA = color_db->getScalar("rhoA"); + } + if (color_db->keyExists("rhoB")) { + rhoB = color_db->getScalar("rhoB"); + } + if (color_db->keyExists("F")) { + Fx = color_db->getVector("F")[0]; + Fy = color_db->getVector("F")[1]; + Fz = color_db->getVector("F")[2]; + } + if (color_db->keyExists("alpha")) { + alpha = color_db->getScalar("alpha"); + } + if (color_db->keyExists("beta")) { + beta = color_db->getScalar("beta"); + } + if (color_db->keyExists("Restart")) { + Restart = color_db->getScalar("Restart"); + } + if (color_db->keyExists("din")) { + din = color_db->getScalar("din"); + } + if (color_db->keyExists("dout")) { + dout = color_db->getScalar("dout"); + } + if (color_db->keyExists("flux")) { + flux = color_db->getScalar("flux"); + } + inletA = 1.f; + inletB = 0.f; + outletA = 0.f; + outletB = 1.f; + + + BoundaryCondition = 0; + if (color_db->keyExists("BC")) { + BoundaryCondition = color_db->getScalar("BC"); + } else if (domain_db->keyExists("BC")) { + BoundaryCondition = domain_db->getScalar("BC"); + } + if (domain_db->keyExists("InletLayersPhase")) { + int inlet_layers_phase = domain_db->getScalar("InletLayersPhase"); + if (inlet_layers_phase == 2) { + inletA = 0.0; + inletB = 1.0; } } - File.close(); - -} - -void ScaLBL_ColorModel::ReadCheckpoint(char *FILENAME, double *cPhi, double *cfq, int Np) -{ - int q=0, n=0; - double value=0; - ifstream File(FILENAME,ios::binary); - for (n=0; nkeyExists("OutletLayersPhase")) { + int outlet_layers_phase = + domain_db->getScalar("OutletLayersPhase"); + if (outlet_layers_phase == 1) { + inletA = 1.0; + inletB = 0.0; } } - File.close(); -} - */ -void ScaLBL_ColorModel::ReadParams(string filename){ - // read the input database - db = std::make_shared( filename ); - domain_db = db->getDatabase( "Domain" ); - color_db = db->getDatabase( "Color" ); - analysis_db = db->getDatabase( "Analysis" ); - vis_db = db->getDatabase( "Visualization" ); - // set defaults - timestepMax = 100000; - tauA = tauB = 1.0; - rhoA = rhoB = 1.0; - Fx = Fy = Fz = 0.0; - alpha=1e-3; - beta=0.95; - Restart=false; - din=dout=1.0; - flux=0.0; - - // Color Model parameters - if (color_db->keyExists( "timestepMax" )){ - timestepMax = color_db->getScalar( "timestepMax" ); - } - if (color_db->keyExists( "tauA" )){ - tauA = color_db->getScalar( "tauA" ); - } - if (color_db->keyExists( "tauB" )){ - tauB = color_db->getScalar( "tauB" ); - } - if (color_db->keyExists( "rhoA" )){ - rhoA = color_db->getScalar( "rhoA" ); - } - if (color_db->keyExists( "rhoB" )){ - rhoB = color_db->getScalar( "rhoB" ); - } - if (color_db->keyExists( "F" )){ - Fx = color_db->getVector( "F" )[0]; - Fy = color_db->getVector( "F" )[1]; - Fz = color_db->getVector( "F" )[2]; - } - if (color_db->keyExists( "alpha" )){ - alpha = color_db->getScalar( "alpha" ); - } - if (color_db->keyExists( "beta" )){ - beta = color_db->getScalar( "beta" ); - } - if (color_db->keyExists( "Restart" )){ - Restart = color_db->getScalar( "Restart" ); - } - if (color_db->keyExists( "din" )){ - din = color_db->getScalar( "din" ); - } - if (color_db->keyExists( "dout" )){ - dout = color_db->getScalar( "dout" ); - } - if (color_db->keyExists( "flux" )){ - flux = color_db->getScalar( "flux" ); - } - inletA=1.f; - inletB=0.f; - outletA=0.f; - outletB=1.f; - //if (BoundaryCondition==4) flux *= rhoA; // mass flux must adjust for density (see formulation for details) - - BoundaryCondition = 0; - if (color_db->keyExists( "BC" )){ - BoundaryCondition = color_db->getScalar( "BC" ); - } - else if (domain_db->keyExists( "BC" )){ - BoundaryCondition = domain_db->getScalar( "BC" ); - } - if (domain_db->keyExists( "InletLayersPhase" )){ - int inlet_layers_phase = domain_db->getScalar( "InletLayersPhase" ); - if (inlet_layers_phase == 2 ) { - inletA = 0.0; - inletB = 1.0; - } - } - if (domain_db->keyExists( "OutletLayersPhase" )){ - int outlet_layers_phase = domain_db->getScalar( "OutletLayersPhase" ); - if (outlet_layers_phase == 1 ) { - inletA = 1.0; - inletB = 0.0; - } - } - - // Override user-specified boundary condition for specific protocols - auto protocol = color_db->getWithDefault( "protocol", "none" ); - if (protocol == "seed water"){ - if (BoundaryCondition != 0 && BoundaryCondition != 5){ - BoundaryCondition = 0; - if (rank==0) printf("WARNING: protocol (seed water) supports only full periodic boundary condition \n"); - } - domain_db->putScalar( "BC", BoundaryCondition ); - } - else if (protocol == "open connected oil"){ - if (BoundaryCondition != 0 && BoundaryCondition != 5){ - BoundaryCondition = 0; - if (rank==0) printf("WARNING: protocol (open connected oil) supports only full periodic boundary condition \n"); - } - domain_db->putScalar( "BC", BoundaryCondition ); - } - else if (protocol == "shell aggregation"){ - if (BoundaryCondition != 0 && BoundaryCondition != 5){ - BoundaryCondition = 0; - if (rank==0) printf("WARNING: protocol (shell aggregation) supports only full periodic boundary condition \n"); - } - domain_db->putScalar( "BC", BoundaryCondition ); - } - else if (protocol == "fractional flow"){ - if (BoundaryCondition != 0 && BoundaryCondition != 5){ - BoundaryCondition = 0; - if (rank==0) printf("WARNING: protocol (fractional flow) supports only full periodic boundary condition \n"); - } - domain_db->putScalar( "BC", BoundaryCondition ); - } - else if (protocol == "centrifuge"){ - if (BoundaryCondition != 3 ){ - BoundaryCondition = 3; - if (rank==0) printf("WARNING: protocol (centrifuge) supports only constant pressure boundary condition \n"); - } - domain_db->putScalar( "BC", BoundaryCondition ); - } - else if (protocol == "core flooding"){ - if (rank == 0) printf("Using core flooding protocol \n"); - if (BoundaryCondition != 4){ - BoundaryCondition = 4; - if (rank==0) printf("WARNING: protocol (core flooding) supports only volumetric flux boundary condition \n"); - } - domain_db->putScalar( "BC", BoundaryCondition ); - } + // Override user-specified boundary condition for specific protocols + auto protocol = color_db->getWithDefault("protocol", "none"); + if (protocol == "seed water") { + if (BoundaryCondition != 0 && BoundaryCondition != 5) { + BoundaryCondition = 0; + if (rank == 0) + printf("WARNING: protocol (seed water) supports only full " + "periodic boundary condition \n"); + } + domain_db->putScalar("BC", BoundaryCondition); + } else if (protocol == "open connected oil") { + if (BoundaryCondition != 0 && BoundaryCondition != 5) { + BoundaryCondition = 0; + if (rank == 0) + printf("WARNING: protocol (open connected oil) supports only " + "full periodic boundary condition \n"); + } + domain_db->putScalar("BC", BoundaryCondition); + } else if (protocol == "shell aggregation") { + if (BoundaryCondition != 0 && BoundaryCondition != 5) { + BoundaryCondition = 0; + if (rank == 0) + printf("WARNING: protocol (shell aggregation) supports only " + "full periodic boundary condition \n"); + } + domain_db->putScalar("BC", BoundaryCondition); + } else if (protocol == "fractional flow") { + if (BoundaryCondition != 0 && BoundaryCondition != 5) { + BoundaryCondition = 0; + if (rank == 0) + printf("WARNING: protocol (fractional flow) supports only full " + "periodic boundary condition \n"); + } + domain_db->putScalar("BC", BoundaryCondition); + } else if (protocol == "centrifuge") { + if (BoundaryCondition != 3) { + BoundaryCondition = 3; + if (rank == 0) + printf("WARNING: protocol (centrifuge) supports only constant " + "pressure boundary condition \n"); + } + domain_db->putScalar("BC", BoundaryCondition); + } else if (protocol == "core flooding") { + if (rank == 0) + printf("Using core flooding protocol \n"); + if (BoundaryCondition != 4) { + BoundaryCondition = 4; + if (rank == 0) + printf("WARNING: protocol (core flooding) supports only " + "volumetric flux boundary condition \n"); + } + domain_db->putScalar("BC", BoundaryCondition); + } } -void ScaLBL_ColorModel::SetDomain(){ - Dm = std::shared_ptr(new Domain(domain_db,comm)); // full domain for analysis - Mask = std::shared_ptr(new Domain(domain_db,comm)); // mask domain removes immobile phases - // domain parameters - Nx = Dm->Nx; - Ny = Dm->Ny; - Nz = Dm->Nz; - Lx = Dm->Lx; - Ly = Dm->Ly; - Lz = Dm->Lz; - N = Nx*Ny*Nz; - id = new signed char [N]; - for (int i=0; iid[i] = 1; // initialize this way - //Averages = std::shared_ptr ( new TwoPhase(Dm) ); // TwoPhase analysis object - Averages = std::shared_ptr ( new SubPhase(Dm) ); // TwoPhase analysis object - comm.barrier(); - Dm->CommInit(); - comm.barrier(); - // Read domain parameters - rank = Dm->rank(); - nprocx = Dm->nprocx(); - nprocy = Dm->nprocy(); - nprocz = Dm->nprocz(); + +void ScaLBL_ColorModel::SetDomain() { + Dm = std::shared_ptr( + new Domain(domain_db, comm)); // full domain for analysis + Mask = std::shared_ptr( + new Domain(domain_db, comm)); // mask domain removes immobile phases + // domain parameters + Nx = Dm->Nx; + Ny = Dm->Ny; + Nz = Dm->Nz; + Lx = Dm->Lx; + Ly = Dm->Ly; + Lz = Dm->Lz; + N = Nx * Ny * Nz; + id = new signed char[N]; + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = 1; // initialize this way + //Averages = std::shared_ptr ( new TwoPhase(Dm) ); // TwoPhase analysis object + Averages = + std::shared_ptr(new SubPhase(Dm)); // TwoPhase analysis object + comm.barrier(); + Dm->CommInit(); + comm.barrier(); + // Read domain parameters + rank = Dm->rank(); + nprocx = Dm->nprocx(); + nprocy = Dm->nprocy(); + nprocz = Dm->nprocz(); } -void ScaLBL_ColorModel::ReadInput(){ - - sprintf(LocalRankString,"%05d",rank); - sprintf(LocalRankFilename,"%s%s","ID.",LocalRankString); - sprintf(LocalRestartFile,"%s%s","Restart.",LocalRankString); - - if (color_db->keyExists( "image_sequence" )){ - auto ImageList = color_db->getVector( "image_sequence"); - int IMAGE_INDEX = color_db->getWithDefault( "image_index", 0 ); - std::string first_image = ImageList[IMAGE_INDEX]; - Mask->Decomp(first_image); - IMAGE_INDEX++; - } - else if (domain_db->keyExists( "GridFile" )){ +void ScaLBL_ColorModel::ReadInput() { + + sprintf(LocalRankString, "%05d", rank); + sprintf(LocalRankFilename, "%s%s", "ID.", LocalRankString); + sprintf(LocalRestartFile, "%s%s", "Restart.", LocalRankString); + + if (color_db->keyExists("image_sequence")) { + auto ImageList = color_db->getVector("image_sequence"); + int IMAGE_INDEX = color_db->getWithDefault("image_index", 0); + std::string first_image = ImageList[IMAGE_INDEX]; + Mask->Decomp(first_image); + IMAGE_INDEX++; + } else if (domain_db->keyExists("GridFile")) { // Read the local domain data - auto input_id = readMicroCT( *domain_db, MPI_COMM_WORLD ); + auto input_id = readMicroCT(*domain_db, MPI_COMM_WORLD); // Fill the halo (assuming GCW of 1) - array size0 = { (int) input_id.size(0), (int) input_id.size(1), (int) input_id.size(2) }; - ArraySize size1 = { (size_t) Mask->Nx, (size_t) Mask->Ny, (size_t) Mask->Nz }; - ASSERT( (int) size1[0] == size0[0]+2 && (int) size1[1] == size0[1]+2 && (int) size1[2] == size0[2]+2 ); - fillHalo fill( MPI_COMM_WORLD, Mask->rank_info, size0, { 1, 1, 1 }, 0, 1 ); + array size0 = {(int)input_id.size(0), (int)input_id.size(1), + (int)input_id.size(2)}; + ArraySize size1 = {(size_t)Mask->Nx, (size_t)Mask->Ny, + (size_t)Mask->Nz}; + ASSERT((int)size1[0] == size0[0] + 2 && (int)size1[1] == size0[1] + 2 && + (int)size1[2] == size0[2] + 2); + fillHalo fill(MPI_COMM_WORLD, Mask->rank_info, size0, + {1, 1, 1}, 0, 1); Array id_view; - id_view.viewRaw( size1, Mask->id.data() ); - fill.copy( input_id, id_view ); - fill.fill( id_view ); - } - else if (domain_db->keyExists( "Filename" )){ - auto Filename = domain_db->getScalar( "Filename" ); - Mask->Decomp(Filename); - } - else{ - Mask->ReadIDs(); - } - for (int i=0; iid[i]; // save what was read - - // Generate the signed distance map - // Initialize the domain and communication - Array id_solid(Nx,Ny,Nz); - // Solve for the position of the solid phase - for (int k=0;kid[n]; - if (label > 0) id_solid(i,j,k) = 1; - else id_solid(i,j,k) = 0; - } - } - } - // Initialize the signed distance function - for (int k=0;kSDs(i,j,k) = 2.0*double(id_solid(i,j,k))-1.0; - } - } - } -// MeanFilter(Averages->SDs); - Minkowski Solid(Dm); - if (rank==0) printf("Initialized solid phase -- Converting to Signed Distance function \n"); - CalcDist(Averages->SDs,id_solid,*Mask); - Solid.ComputeScalar(Averages->SDs,0.0); - /* save averages */ - Averages->solid.V = Solid.Vi; - Averages->solid.A = Solid.Ai; - Averages->solid.H = Solid.Ji; - Averages->solid.X = Solid.Xi; - Averages->gsolid.V = Solid.Vi_global; - Averages->gsolid.A = Solid.Ai_global; - Averages->gsolid.H = Solid.Ji_global; - Averages->gsolid.X = Solid.Xi_global; - /* write to file */ - if (rank == 0) { - FILE *SOLID = fopen("solid.csv","w"); - fprintf(SOLID,"Vs As Hs Xs\n"); - fprintf(SOLID,"%.8g %.8g %.8g %.8g\n",Solid.Vi_global,Solid.Ai_global,Solid.Ji_global,Solid.Xi_global); - fclose(SOLID); - } - if (rank == 0) cout << "Domain set." << endl; - - Averages->SetParams(rhoA,rhoB,tauA,tauB,Fx,Fy,Fz,alpha,beta); + id_view.viewRaw(size1, Mask->id.data()); + fill.copy(input_id, id_view); + fill.fill(id_view); + } else if (domain_db->keyExists("Filename")) { + auto Filename = domain_db->getScalar("Filename"); + Mask->Decomp(Filename); + } else { + Mask->ReadIDs(); + } + for (int i = 0; i < Nx * Ny * Nz; i++) + id[i] = Mask->id[i]; // save what was read + + // Generate the signed distance map + // Initialize the domain and communication + Array id_solid(Nx, Ny, Nz); + // Solve for the position of the solid phase + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + // Initialize the solid phase + signed char label = Mask->id[n]; + if (label > 0) + id_solid(i, j, k) = 1; + else + id_solid(i, j, k) = 0; + } + } + } + // Initialize the signed distance function + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + // Initialize distance to +/- 1 + Averages->SDs(i, j, k) = 2.0 * double(id_solid(i, j, k)) - 1.0; + } + } + } + // MeanFilter(Averages->SDs); + Minkowski Solid(Dm); + if (rank == 0) + printf("Initialized solid phase -- Converting to Signed Distance " + "function \n"); + CalcDist(Averages->SDs, id_solid, *Mask); + Solid.ComputeScalar(Averages->SDs, 0.0); + /* save averages */ + Averages->solid.V = Solid.Vi; + Averages->solid.A = Solid.Ai; + Averages->solid.H = Solid.Ji; + Averages->solid.X = Solid.Xi; + Averages->gsolid.V = Solid.Vi_global; + Averages->gsolid.A = Solid.Ai_global; + Averages->gsolid.H = Solid.Ji_global; + Averages->gsolid.X = Solid.Xi_global; + /* write to file */ + if (rank == 0) { + FILE *SOLID = fopen("solid.csv", "w"); + fprintf(SOLID, "Vs As Hs Xs\n"); + fprintf(SOLID, "%.8g %.8g %.8g %.8g\n", Solid.Vi_global, + Solid.Ai_global, Solid.Ji_global, Solid.Xi_global); + fclose(SOLID); + } + if (rank == 0) + cout << "Domain set." << endl; + + Averages->SetParams(rhoA, rhoB, tauA, tauB, Fx, Fy, Fz, alpha, beta); } -void ScaLBL_ColorModel::AssignComponentLabels(double *phase) -{ - size_t NLABELS=0; - signed char VALUE=0; - double AFFINITY=0.f; +void ScaLBL_ColorModel::AssignComponentLabels(double *phase) { + size_t NLABELS = 0; + signed char VALUE = 0; + double AFFINITY = 0.f; - auto LabelList = color_db->getVector( "ComponentLabels" ); - auto AffinityList = color_db->getVector( "ComponentAffinity" ); - auto WettingConvention = color_db->getWithDefault( "WettingConvention", "none" ); + auto LabelList = color_db->getVector("ComponentLabels"); + auto AffinityList = color_db->getVector("ComponentAffinity"); + auto WettingConvention = + color_db->getWithDefault("WettingConvention", "none"); - NLABELS=LabelList.size(); - if (NLABELS != AffinityList.size()){ - ERROR("Error: ComponentLabels and ComponentAffinity must be the same length! \n"); - } + NLABELS = LabelList.size(); + if (NLABELS != AffinityList.size()) { + ERROR("Error: ComponentLabels and ComponentAffinity must be the same " + "length! \n"); + } - if (WettingConvention == "SCAL"){ - for (size_t idx=0; idxid[n] = 0; // set mask to zero since this is an immobile component - } - } - // fluid labels are reserved - if (VALUE == 1) AFFINITY=1.0; - else if (VALUE == 2) AFFINITY=-1.0; - phase[n] = AFFINITY; - } - } - } + double *label_count; + double *label_count_global; + label_count = new double[NLABELS]; + label_count_global = new double[NLABELS]; + // Assign the labels + for (size_t idx = 0; idx < NLABELS; idx++) + label_count[idx] = 0; - // Set Dm to match Mask - for (int i=0; iid[i] = Mask->id[i]; - - for (size_t idx=0; idxComm.sumReduce( label_count[idx]); + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + VALUE = id[n]; + // Assign the affinity from the paired list + for (unsigned int idx = 0; idx < NLABELS; idx++) { + if (VALUE == LabelList[idx]) { + AFFINITY = AffinityList[idx]; + label_count[idx] += 1.0; + idx = NLABELS; + } + } + // fluid labels are reserved + if (VALUE == 1) + AFFINITY = 1.0; + else if (VALUE == 2) + AFFINITY = -1.0; + phase[n] = AFFINITY; + } + } + } - if (rank==0){ - printf("Component labels: %lu \n",NLABELS); - for (unsigned int idx=0; idxid[i] = Mask->id[i]; + for (size_t idx = 0; idx < NLABELS; idx++) + label_count_global[idx] = Dm->Comm.sumReduce(label_count[idx]); + + if (rank == 0) { + printf("Component labels: %lu \n", NLABELS); + for (unsigned int idx = 0; idx < NLABELS; idx++) { + VALUE = LabelList[idx]; + AFFINITY = AffinityList[idx]; + double volume_fraction = + double(label_count_global[idx]) / + double((Nx - 2) * (Ny - 2) * (Nz - 2) * nprocs); + printf(" label=%d, affinity=%f, volume fraction==%f\n", VALUE, + AFFINITY, volume_fraction); + } + } } +void ScaLBL_ColorModel::Create() { + /* + * This function creates the variables needed to run a LBM + */ + //......................................................... + // don't perform computations at the eight corners + //id[0] = id[Nx-1] = id[(Ny-1)*Nx] = id[(Ny-1)*Nx + Nx-1] = 0; + //id[(Nz-1)*Nx*Ny] = id[(Nz-1)*Nx*Ny+Nx-1] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx + Nx-1] = 0; -void ScaLBL_ColorModel::Create(){ - /* - * This function creates the variables needed to run a LBM - */ - //......................................................... - // don't perform computations at the eight corners - //id[0] = id[Nx-1] = id[(Ny-1)*Nx] = id[(Ny-1)*Nx + Nx-1] = 0; - //id[(Nz-1)*Nx*Ny] = id[(Nz-1)*Nx*Ny+Nx-1] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx + Nx-1] = 0; + //......................................................... + // Initialize communication structures in averaging domain + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = Mask->id[i]; + Mask->CommInit(); + Np = Mask->PoreCount(); + //........................................................................... + if (rank == 0) + printf("Create ScaLBL_Communicator \n"); + // Create a communicator for the device (will use optimized layout) + // ScaLBL_Communicator ScaLBL_Comm(Mask); // original + ScaLBL_Comm = + std::shared_ptr(new ScaLBL_Communicator(Mask)); + ScaLBL_Comm_Regular = + std::shared_ptr(new ScaLBL_Communicator(Mask)); - //......................................................... - // Initialize communication structures in averaging domain - for (int i=0; iid[i] = Mask->id[i]; - Mask->CommInit(); - Np=Mask->PoreCount(); - //........................................................................... - if (rank==0) printf ("Create ScaLBL_Communicator \n"); - // Create a communicator for the device (will use optimized layout) - // ScaLBL_Communicator ScaLBL_Comm(Mask); // original - ScaLBL_Comm = std::shared_ptr(new ScaLBL_Communicator(Mask)); - ScaLBL_Comm_Regular = std::shared_ptr(new ScaLBL_Communicator(Mask)); + int Npad = (Np / 16 + 2) * 16; + if (rank == 0) + printf("Set up memory efficient layout, %i | %i | %i \n", Np, Npad, N); + Map.resize(Nx, Ny, Nz); + Map.fill(-2); + auto neighborList = new int[18 * Npad]; + Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map, neighborList, + Mask->id.data(), Np, 1); + comm.barrier(); - int Npad=(Np/16 + 2)*16; - if (rank==0) printf ("Set up memory efficient layout, %i | %i | %i \n", Np, Npad, N); - Map.resize(Nx,Ny,Nz); Map.fill(-2); - auto neighborList= new int[18*Npad]; - Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map,neighborList,Mask->id.data(),Np,1); - comm.barrier(); + //........................................................................... + // MAIN VARIABLES ALLOCATED HERE + //........................................................................... + // LBM variables + if (rank == 0) + printf("Allocating distributions \n"); + //......................device distributions................................. + dist_mem_size = Np * sizeof(double); + neighborSize = 18 * (Np * sizeof(int)); + //........................................................................... + ScaLBL_AllocateDeviceMemory((void **)&NeighborList, neighborSize); + ScaLBL_AllocateDeviceMemory((void **)&dvcMap, sizeof(int) * Np); + ScaLBL_AllocateDeviceMemory((void **)&fq, 19 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Aq, 7 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Bq, 7 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Den, 2 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Phi, sizeof(double) * Nx * Ny * Nz); + ScaLBL_AllocateDeviceMemory((void **)&Pressure, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&Velocity, 3 * sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&ColorGrad, 3 * sizeof(double) * Np); + //........................................................................... + // Update GPU data structures + if (rank == 0) + printf("Setting up device map and neighbor list \n"); + fflush(stdout); + int *TmpMap; + TmpMap = new int[Np]; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + int idx = Map(i, j, k); + if (!(idx < 0)) + TmpMap[idx] = k * Nx * Ny + j * Nx + i; + } + } + } + // check that TmpMap is valid + for (int idx = 0; idx < ScaLBL_Comm->LastExterior(); idx++) { + auto n = TmpMap[idx]; + if (n > Nx * Ny * Nz) { + printf("Bad value! idx=%i \n", n); + TmpMap[idx] = Nx * Ny * Nz - 1; + } + } + for (int idx = ScaLBL_Comm->FirstInterior(); + idx < ScaLBL_Comm->LastInterior(); idx++) { + auto n = TmpMap[idx]; + if (n > Nx * Ny * Nz) { + printf("Bad value! idx=%i \n", n); + TmpMap[idx] = Nx * Ny * Nz - 1; + } + } + ScaLBL_CopyToDevice(dvcMap, TmpMap, sizeof(int) * Np); + ScaLBL_Comm->Barrier(); + delete[] TmpMap; - //........................................................................... - // MAIN VARIABLES ALLOCATED HERE - //........................................................................... - // LBM variables - if (rank==0) printf ("Allocating distributions \n"); - //......................device distributions................................. - dist_mem_size = Np*sizeof(double); - neighborSize=18*(Np*sizeof(int)); - //........................................................................... - ScaLBL_AllocateDeviceMemory((void **) &NeighborList, neighborSize); - ScaLBL_AllocateDeviceMemory((void **) &dvcMap, sizeof(int)*Np); - ScaLBL_AllocateDeviceMemory((void **) &fq, 19*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Aq, 7*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Bq, 7*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Den, 2*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Phi, sizeof(double)*Nx*Ny*Nz); - ScaLBL_AllocateDeviceMemory((void **) &Pressure, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &Velocity, 3*sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &ColorGrad, 3*sizeof(double)*Np); - //........................................................................... - // Update GPU data structures - if (rank==0) printf ("Setting up device map and neighbor list \n"); - fflush(stdout); - int *TmpMap; - TmpMap=new int[Np]; - for (int k=1; kLastExterior(); idx++){ - auto n = TmpMap[idx]; - if (n > Nx*Ny*Nz){ - printf("Bad value! idx=%i \n", n); - TmpMap[idx] = Nx*Ny*Nz-1; - } - } - for (int idx=ScaLBL_Comm->FirstInterior(); idxLastInterior(); idx++){ - auto n = TmpMap[idx]; - if ( n > Nx*Ny*Nz ){ - printf("Bad value! idx=%i \n",n); - TmpMap[idx] = Nx*Ny*Nz-1; - } - } - ScaLBL_CopyToDevice(dvcMap, TmpMap, sizeof(int)*Np); - ScaLBL_Comm->Barrier(); - delete [] TmpMap; - - // copy the neighbor list - ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); - delete [] neighborList; - // initialize phi based on PhaseLabel (include solid component labels) - double *PhaseLabel; - PhaseLabel = new double[N]; - AssignComponentLabels(PhaseLabel); - ScaLBL_CopyToDevice(Phi, PhaseLabel, N*sizeof(double)); - delete [] PhaseLabel; -} + // copy the neighbor list + ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); + delete[] neighborList; + // initialize phi based on PhaseLabel (include solid component labels) + double *PhaseLabel; + PhaseLabel = new double[N]; + AssignComponentLabels(PhaseLabel); + ScaLBL_CopyToDevice(Phi, PhaseLabel, N * sizeof(double)); + delete[] PhaseLabel; +} /******************************************************** * AssignComponentLabels * ********************************************************/ -void ScaLBL_ColorModel::Initialize(){ - - /* if both capillary number and flux BC are specified */ - if (color_db->keyExists( "capillary_number" ) && BoundaryCondition == 4){ - double capillary_number = color_db->getScalar( "capillary_number" ); - if (rank==0) printf(" set flux to achieve Ca=%f \n", capillary_number); - double MuB = rhoB*(tauB - 0.5)/3.0; - double IFT = 6.0*alpha; - double CrossSectionalArea = (double) (nprocx*(Nx-2)*nprocy*(Ny-2)); - flux = Mask->Porosity()*CrossSectionalArea*(Ny-2)*IFT*capillary_number/MuB; - if (rank==0) printf(" flux=%f \n",flux); - } - color_db->putScalar( "flux", flux ); - - if (rank==0) printf ("Initializing distributions \n"); - ScaLBL_D3Q19_Init(fq, Np); - /* - * This function initializes model - */ - if (Restart == true){ - if (rank==0){ - printf("Reading restart file! \n"); - } +void ScaLBL_ColorModel::Initialize() { - // Read in the restart file to CPU buffers - int *TmpMap; - TmpMap = new int[Np]; - - double *cPhi, *cDist, *cDen; - cPhi = new double[N]; - cDen = new double[2*Np]; - cDist = new double[19*Np]; - ScaLBL_CopyToHost(TmpMap, dvcMap, Np*sizeof(int)); - ScaLBL_CopyToHost(cPhi, Phi, N*sizeof(double)); - - ifstream File(LocalRestartFile,ios::binary); - int idx; - double value,va,vb; - for (int n=0; nLastExterior(); n++){ - va = cDen[n]; - vb = cDen[Np + n]; - value = (va-vb)/(va+vb); - idx = TmpMap[n]; - if (!(idx < 0) && idxFirstInterior(); nLastInterior(); n++){ - va = cDen[n]; - vb = cDen[Np + n]; - value = (va-vb)/(va+vb); - idx = TmpMap[n]; - if (!(idx < 0) && idxBarrier(); + /* if both capillary number and flux BC are specified */ + if (color_db->keyExists("capillary_number") && BoundaryCondition == 4) { + double capillary_number = + color_db->getScalar("capillary_number"); + if (rank == 0) + printf(" set flux to achieve Ca=%f \n", capillary_number); + double MuB = rhoB * (tauB - 0.5) / 3.0; + double IFT = 6.0 * alpha; + double CrossSectionalArea = + (double)(nprocx * (Nx - 2) * nprocy * (Ny - 2)); + flux = Mask->Porosity() * CrossSectionalArea * (Ny - 2) * IFT * + capillary_number / MuB; + if (rank == 0) + printf(" flux=%f \n", flux); + } + color_db->putScalar("flux", flux); - comm.barrier(); - } + if (rank == 0) + printf("Initializing distributions \n"); + ScaLBL_D3Q19_Init(fq, Np); + /* + * This function initializes model + */ + if (Restart == true) { + if (rank == 0) { + printf("Reading restart file! \n"); + } - if (rank==0) printf ("Initializing phase field \n"); - ScaLBL_PhaseField_Init(dvcMap, Phi, Den, Aq, Bq, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_PhaseField_Init(dvcMap, Phi, Den, Aq, Bq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + // Read in the restart file to CPU buffers + int *TmpMap; + TmpMap = new int[Np]; - // establish reservoirs for external bC - if (BoundaryCondition == 1 || BoundaryCondition == 2 || BoundaryCondition == 3 || BoundaryCondition == 4 ){ - if (Dm->kproc()==0){ - ScaLBL_SetSlice_z(Phi,1.0,Nx,Ny,Nz,0); - ScaLBL_SetSlice_z(Phi,1.0,Nx,Ny,Nz,1); - ScaLBL_SetSlice_z(Phi,1.0,Nx,Ny,Nz,2); - } - if (Dm->kproc() == nprocz-1){ - ScaLBL_SetSlice_z(Phi,-1.0,Nx,Ny,Nz,Nz-1); - ScaLBL_SetSlice_z(Phi,-1.0,Nx,Ny,Nz,Nz-2); - ScaLBL_SetSlice_z(Phi,-1.0,Nx,Ny,Nz,Nz-3); - } - } - ScaLBL_CopyToHost(Averages->Phi.data(),Phi,N*sizeof(double)); + double *cPhi, *cDist, *cDen; + cPhi = new double[N]; + cDen = new double[2 * Np]; + cDist = new double[19 * Np]; + ScaLBL_CopyToHost(TmpMap, dvcMap, Np * sizeof(int)); + ScaLBL_CopyToHost(cPhi, Phi, N * sizeof(double)); + + ifstream File(LocalRestartFile, ios::binary); + int idx; + double value, va, vb; + for (int n = 0; n < Np; n++) { + File.read((char *)&va, sizeof(va)); + File.read((char *)&vb, sizeof(vb)); + cDen[n] = va; + cDen[Np + n] = vb; + } + for (int n = 0; n < Np; n++) { + // Read the distributions + for (int q = 0; q < 19; q++) { + File.read((char *)&value, sizeof(value)); + cDist[q * Np + n] = value; + } + } + File.close(); + + for (int n = 0; n < ScaLBL_Comm->LastExterior(); n++) { + va = cDen[n]; + vb = cDen[Np + n]; + value = (va - vb) / (va + vb); + idx = TmpMap[n]; + if (!(idx < 0) && idx < N) + cPhi[idx] = value; + } + for (int n = ScaLBL_Comm->FirstInterior(); + n < ScaLBL_Comm->LastInterior(); n++) { + va = cDen[n]; + vb = cDen[Np + n]; + value = (va - vb) / (va + vb); + idx = TmpMap[n]; + if (!(idx < 0) && idx < N) + cPhi[idx] = value; + } + + // Copy the restart data to the GPU + ScaLBL_CopyToDevice(Den, cDen, 2 * Np * sizeof(double)); + ScaLBL_CopyToDevice(fq, cDist, 19 * Np * sizeof(double)); + ScaLBL_CopyToDevice(Phi, cPhi, N * sizeof(double)); + ScaLBL_Comm->Barrier(); + + comm.barrier(); + } + + if (rank == 0) + printf("Initializing phase field \n"); + ScaLBL_PhaseField_Init(dvcMap, Phi, Den, Aq, Bq, 0, + ScaLBL_Comm->LastExterior(), Np); + ScaLBL_PhaseField_Init(dvcMap, Phi, Den, Aq, Bq, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + + // establish reservoirs for external bC + if (BoundaryCondition == 1 || BoundaryCondition == 2 || + BoundaryCondition == 3 || BoundaryCondition == 4) { + if (Dm->kproc() == 0) { + ScaLBL_SetSlice_z(Phi, 1.0, Nx, Ny, Nz, 0); + ScaLBL_SetSlice_z(Phi, 1.0, Nx, Ny, Nz, 1); + ScaLBL_SetSlice_z(Phi, 1.0, Nx, Ny, Nz, 2); + } + if (Dm->kproc() == nprocz - 1) { + ScaLBL_SetSlice_z(Phi, -1.0, Nx, Ny, Nz, Nz - 1); + ScaLBL_SetSlice_z(Phi, -1.0, Nx, Ny, Nz, Nz - 2); + ScaLBL_SetSlice_z(Phi, -1.0, Nx, Ny, Nz, Nz - 3); + } + } + ScaLBL_CopyToHost(Averages->Phi.data(), Phi, N * sizeof(double)); } -double ScaLBL_ColorModel::Run(int returntime){ - int nprocs=nprocx*nprocy*nprocz; - const RankInfoStruct rank_info(rank,nprocx,nprocy,nprocz); - //************ MAIN ITERATION LOOP ***************************************/ - comm.barrier(); - PROFILE_START("Loop"); - //std::shared_ptr analysis_db; - bool Regular = false; - bool RESCALE_FORCE = false; - bool SET_CAPILLARY_NUMBER = false; - bool TRIGGER_FORCE_RESCALE = false; - double tolerance = 0.01; - auto current_db = db->cloneDatabase(); - auto flow_db = db->getDatabase( "FlowAdaptor" ); - int MIN_STEADY_TIMESTEPS = flow_db->getWithDefault( "min_steady_timesteps", 1000000 ); - int MAX_STEADY_TIMESTEPS = flow_db->getWithDefault( "max_steady_timesteps", 1000000 ); - int RESCALE_FORCE_AFTER_TIMESTEP = MAX_STEADY_TIMESTEPS*2; - int INITIAL_TIMESTEP = timestep; +double ScaLBL_ColorModel::Run(int returntime) { + int nprocs = nprocx * nprocy * nprocz; + const RankInfoStruct rank_info(rank, nprocx, nprocy, nprocz); + //************ MAIN ITERATION LOOP ***************************************/ + comm.barrier(); + PROFILE_START("Loop"); + bool Regular = false; + bool RESCALE_FORCE = false; + bool SET_CAPILLARY_NUMBER = false; + bool TRIGGER_FORCE_RESCALE = false; + double tolerance = 0.01; + auto current_db = db->cloneDatabase(); + auto flow_db = db->getDatabase("FlowAdaptor"); + int MIN_STEADY_TIMESTEPS = + flow_db->getWithDefault("min_steady_timesteps", 1000000); + int MAX_STEADY_TIMESTEPS = + flow_db->getWithDefault("max_steady_timesteps", 1000000); + int RESCALE_FORCE_AFTER_TIMESTEP = MAX_STEADY_TIMESTEPS * 2; + int INITIAL_TIMESTEP = timestep; - double capillary_number = 1.0e-5; - double Ca_previous = 0.0; - double minCa = 8.0e-6; - double maxCa = 1.0; - if (color_db->keyExists( "capillary_number" )){ - capillary_number = color_db->getScalar( "capillary_number" ); - SET_CAPILLARY_NUMBER=true; - maxCa = 2.0*capillary_number; - minCa = 0.8*capillary_number; - } - if (color_db->keyExists( "rescale_force_after_timestep" )){ - RESCALE_FORCE_AFTER_TIMESTEP = color_db->getScalar( "rescale_force_after_timestep" ); - RESCALE_FORCE = true; - } - if (analysis_db->keyExists( "tolerance" )){ - tolerance = analysis_db->getScalar( "tolerance" ); - } - - runAnalysis analysis( current_db, rank_info, ScaLBL_Comm, Dm, Np, Regular, Map ); - auto t1 = std::chrono::system_clock::now(); - int CURRENT_TIMESTEP = 0; - int EXIT_TIMESTEP = min(timestepMax,returntime); - while (timestep < EXIT_TIMESTEP ) { - //if ( rank==0 ) { printf("Running timestep %i (%i MB)\n",timestep+1,(int)(Utilities::getMemoryUsage()/1048576)); } - PROFILE_START("Update"); - // *************ODD TIMESTEP************* - timestep++; - // Compute the Phase indicator field - // Read for Aq, Bq happens in this routine (requires communication) - ScaLBL_Comm->BiSendD3Q7AA(Aq,Bq); //READ FROM NORMAL - ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->BiRecvD3Q7AA(Aq,Bq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, 0, ScaLBL_Comm->LastExterior(), Np); + double capillary_number = 1.0e-5; + double Ca_previous = 0.0; + double minCa = 8.0e-6; + double maxCa = 1.0; + if (color_db->keyExists("capillary_number")) { + capillary_number = color_db->getScalar("capillary_number"); + SET_CAPILLARY_NUMBER = true; + maxCa = 2.0 * capillary_number; + minCa = 0.8 * capillary_number; + } + if (color_db->keyExists("rescale_force_after_timestep")) { + RESCALE_FORCE_AFTER_TIMESTEP = + color_db->getScalar("rescale_force_after_timestep"); + RESCALE_FORCE = true; + } + if (analysis_db->keyExists("tolerance")) { + tolerance = analysis_db->getScalar("tolerance"); + } - // Perform the collision operation - ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL - if (BoundaryCondition > 0 && BoundaryCondition < 5){ - ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); - ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); - } - // Halo exchange for phase field - ScaLBL_Comm_Regular->SendHalo(Phi); - - ScaLBL_D3Q19_AAodd_Color(NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, Nx, Nx*Ny, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm_Regular->RecvHalo(Phi); - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - // Set BCs - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); - } - ScaLBL_D3Q19_AAodd_Color(NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, Nx, Nx*Ny, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->Barrier(); - - // *************EVEN TIMESTEP************* - timestep++; - // Compute the Phase indicator field - ScaLBL_Comm->BiSendD3Q7AA(Aq,Bq); //READ FROM NORMAL - ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->BiRecvD3Q7AA(Aq,Bq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, 0, ScaLBL_Comm->LastExterior(), Np); - - // Perform the collision operation - ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL - // Halo exchange for phase field - if (BoundaryCondition > 0 && BoundaryCondition < 5){ - ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); - ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); - } - ScaLBL_Comm_Regular->SendHalo(Phi); - ScaLBL_D3Q19_AAeven_Color(dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, Nx, Nx*Ny, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm_Regular->RecvHalo(Phi); - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - // Set boundary conditions - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); - } - ScaLBL_D3Q19_AAeven_Color(dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, Nx, Nx*Ny, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->Barrier(); - //************************************************************************ - analysis.basic(timestep, current_db, *Averages, Phi, Pressure, Velocity, fq, Den ); // allow initial ramp-up to get closer to steady state - - CURRENT_TIMESTEP += 2; - if (CURRENT_TIMESTEP > MIN_STEADY_TIMESTEPS && BoundaryCondition == 0){ - analysis.finish(); - - double volB = Averages->gwb.V; - double volA = Averages->gnb.V; - volA /= Dm->Volume; - volB /= Dm->Volume;; - //initial_volume = volA*Dm->Volume; - double vA_x = Averages->gnb.Px/Averages->gnb.M; - double vA_y = Averages->gnb.Py/Averages->gnb.M; - double vA_z = Averages->gnb.Pz/Averages->gnb.M; - double vB_x = Averages->gwb.Px/Averages->gwb.M; - double vB_y = Averages->gwb.Py/Averages->gwb.M; - double vB_z = Averages->gwb.Pz/Averages->gwb.M; - double muA = rhoA*(tauA-0.5)/3.f; - double muB = rhoB*(tauB-0.5)/3.f; - double force_mag = sqrt(Fx*Fx+Fy*Fy+Fz*Fz); - double dir_x = Fx/force_mag; - double dir_y = Fy/force_mag; - double dir_z = Fz/force_mag; - if (force_mag == 0.0){ - // default to z direction - dir_x = 0.0; - dir_y = 0.0; - dir_z = 1.0; - force_mag = 1.0; - } - double current_saturation = volB/(volA+volB); - double flow_rate_A = volA*(vA_x*dir_x + vA_y*dir_y + vA_z*dir_z); - double flow_rate_B = volB*(vB_x*dir_x + vB_y*dir_y + vB_z*dir_z); - double Ca = fabs(muA*flow_rate_A + muB*flow_rate_B)/(5.796*alpha); - - bool isSteady = false; - if ( (fabs((Ca - Ca_previous)/Ca) < tolerance && CURRENT_TIMESTEP > MIN_STEADY_TIMESTEPS)) - isSteady = true; - if (CURRENT_TIMESTEP >= MAX_STEADY_TIMESTEPS) - isSteady = true; - - if (isSteady && (Ca > maxCa || Ca < minCa) && SET_CAPILLARY_NUMBER ){ - /* re-run the point if the actual Ca is too far from the target Ca */ - isSteady = false; - RESCALE_FORCE = true; - t1 = std::chrono::system_clock::now(); - CURRENT_TIMESTEP = 0; - timestep = INITIAL_TIMESTEP; - TRIGGER_FORCE_RESCALE = true; - if (rank == 0) printf(" Capillary number missed target value = %f (measured value was Ca = %f) \n ",capillary_number, Ca); - } - - if (RESCALE_FORCE == true && SET_CAPILLARY_NUMBER == true && CURRENT_TIMESTEP > RESCALE_FORCE_AFTER_TIMESTEP){ - TRIGGER_FORCE_RESCALE = true; - } - - if (TRIGGER_FORCE_RESCALE){ - RESCALE_FORCE = false; - TRIGGER_FORCE_RESCALE = false; - double RESCALE_FORCE_FACTOR = capillary_number / Ca; - if (RESCALE_FORCE_FACTOR > 2.0) RESCALE_FORCE_FACTOR = 2.0; - if (RESCALE_FORCE_FACTOR < 0.5) RESCALE_FORCE_FACTOR = 0.5; - Fx *= RESCALE_FORCE_FACTOR; - Fy *= RESCALE_FORCE_FACTOR; - Fz *= RESCALE_FORCE_FACTOR; - force_mag = sqrt(Fx*Fx+Fy*Fy+Fz*Fz); - if (force_mag > 1e-3){ - Fx *= 1e-3/force_mag; // impose ceiling for stability - Fy *= 1e-3/force_mag; - Fz *= 1e-3/force_mag; - } - if (rank == 0) printf(" -- adjust force by factor %f \n ",capillary_number / Ca); - Averages->SetParams(rhoA,rhoB,tauA,tauB,Fx,Fy,Fz,alpha,beta); - color_db->putVector("F",{Fx,Fy,Fz}); - } - if ( isSteady ){ - Averages->Full(); - Averages->Write(timestep); - analysis.WriteVisData(timestep, current_db, *Averages, Phi, Pressure, Velocity, fq, Den ); - analysis.finish(); - - if (rank==0){ - printf("** WRITE STEADY POINT *** "); - printf("Ca = %f, (previous = %f) \n",Ca,Ca_previous); - double h = Dm->voxel_length; - // pressures - double pA = Averages->gnb.p; - double pB = Averages->gwb.p; - double pAc = Averages->gnc.p; - double pBc = Averages->gwc.p; - double pAB = (pA-pB)/(h*6.0*alpha); - double pAB_connected = (pAc-pBc)/(h*6.0*alpha); - // connected contribution - double Vol_nc = Averages->gnc.V/Dm->Volume; - double Vol_wc = Averages->gwc.V/Dm->Volume; - double Vol_nd = Averages->gnd.V/Dm->Volume; - double Vol_wd = Averages->gwd.V/Dm->Volume; - double Mass_n = Averages->gnc.M + Averages->gnd.M; - double Mass_w = Averages->gwc.M + Averages->gwd.M; - double vAc_x = Averages->gnc.Px/Mass_n; - double vAc_y = Averages->gnc.Py/Mass_n; - double vAc_z = Averages->gnc.Pz/Mass_n; - double vBc_x = Averages->gwc.Px/Mass_w; - double vBc_y = Averages->gwc.Py/Mass_w; - double vBc_z = Averages->gwc.Pz/Mass_w; - // disconnected contribution - double vAd_x = Averages->gnd.Px/Mass_n; - double vAd_y = Averages->gnd.Py/Mass_n; - double vAd_z = Averages->gnd.Pz/Mass_n; - double vBd_x = Averages->gwd.Px/Mass_w; - double vBd_y = Averages->gwd.Py/Mass_w; - double vBd_z = Averages->gwd.Pz/Mass_w; - - double flow_rate_A_connected = Vol_nc*(vAc_x*dir_x + vAc_y*dir_y + vAc_z*dir_z); - double flow_rate_B_connected = Vol_wc*(vBc_x*dir_x + vBc_y*dir_y + vBc_z*dir_z); - double flow_rate_A_disconnected = (Vol_nd)*(vAd_x*dir_x + vAd_y*dir_y + vAd_z*dir_z); - double flow_rate_B_disconnected = (Vol_wd)*(vBd_x*dir_x + vBd_y*dir_y + vBd_z*dir_z); - - double kAeff_connected = h*h*muA*flow_rate_A_connected/(force_mag); - double kBeff_connected = h*h*muB*flow_rate_B_connected/(force_mag); - - // Saturation normalized effective permeability to account for decoupled phases and - // effective porosity. - double kAeff_connected_low = (1.0 - current_saturation)*h*h*muA*flow_rate_A_connected/(force_mag); - double kBeff_connected_low = current_saturation*h*h*muB*flow_rate_B_connected/(force_mag); - - - double kAeff_disconnected = h*h*muA*flow_rate_A_disconnected/(force_mag); - double kBeff_disconnected = h*h*muB*flow_rate_B_disconnected/(force_mag); - - double kAeff = h*h*muA*(flow_rate_A)/(force_mag); - double kBeff = h*h*muB*(flow_rate_B)/(force_mag); - - // Saturation normalized effective permeability to account for decoupled phases and - // effective porosity. - double kAeff_low = (1.0 - current_saturation)*h*h*muA*(flow_rate_A)/(force_mag); - double kBeff_low = current_saturation*h*h*muB*(flow_rate_B)/(force_mag); - - /* flow rate = [volume of fluid] x [momentum of fluid] / [mass of fluid] */ - /* fluid A eats the films */ - //double flow_rate_A_filmA = (flow_rate_A*Averages->gnb.M + - // volA*(vfn_x*dir_x + vfn_y*dir_y + vfn_z*dir_z))/(Averages->gnb.M + Mfn); - //double flow_rate_B_filmA = (flow_rate_B*Averages->gwb.M - - // volB*(vfn_x*dir_x + vfn_y*dir_y + vfn_z*dir_z))/(Averages->gwb.M - (rhoB/rhoA)*Mfn); - /* fluid B eats the films */ - //double flow_rate_A_filmB = (flow_rate_A*Averages->gnb.M - volA*(vfw_x*dir_x + vfw_y*dir_y + vfw_z*dir_z))/(Averages->gnb.M - (rhoA/rhoB)*Mfw); - //double flow_rate_B_filmB = (flow_rate_B*Averages->gwb.M + volB*(vfw_x*dir_x + vfw_y*dir_y + vfw_z*dir_z))/(Averages->gwb.M + Mfw); - /* effective permeability uncertainty limits */ - //double kAeff_filmA = h*h*muA*(flow_rate_A_filmA)/(force_mag); - //double kBeff_filmA = h*h*muB*(flow_rate_B_filmA)/(force_mag); - //double kAeff_filmB = h*h*muA*(flow_rate_A_filmB)/(force_mag); - //double kBeff_filmB = h*h*muB*(flow_rate_B_filmB)/(force_mag); - - double viscous_pressure_drop = (rhoA*volA + rhoB*volB)*force_mag; - double Mobility = muA/muB; // visc contrast - double eff_pres = 1.0 / (kAeff + kBeff); // effective pressure drop - - bool WriteHeader=false; - FILE * kr_log_file = fopen("relperm.csv","r"); - if (kr_log_file != NULL) - fclose(kr_log_file); - else - WriteHeader=true; - kr_log_file = fopen("relperm.csv","a"); - if (WriteHeader){ - fprintf(kr_log_file, "timesteps sat.water "); - fprintf(kr_log_file, "eff.perm.oil.upper.bound eff.perm.water.upper.bound "); - fprintf(kr_log_file, "eff.perm.oil.lower.bound eff.perm.water.lower.bound "); - fprintf(kr_log_file, "eff.perm.oil.connected.upper.bound eff.perm.water.connected.upper.bound "); - fprintf(kr_log_file, "eff.perm.oil.connected.lower.bound eff.perm.water.connected.lower.bound "); - fprintf(kr_log_file, "eff.perm.oil.disconnected eff.perm.water.disconnected "); - fprintf(kr_log_file, "cap.pressure cap.pressure.connected pressure.drop Ca M eff.pressure\n"); - - } - fprintf(kr_log_file,"%i %.5g ", CURRENT_TIMESTEP,current_saturation); - fprintf(kr_log_file,"%.5g %.5g ", kAeff, kBeff); - fprintf(kr_log_file,"%.5g %.5g ", kAeff_low, kBeff_low); - fprintf(kr_log_file,"%.5g %.5g ", kAeff_connected, kBeff_connected); - fprintf(kr_log_file,"%.5g %.5g ", kAeff_connected_low, kBeff_connected_low); - fprintf(kr_log_file,"%.5g %.5g ", kAeff_disconnected, kBeff_disconnected); - fprintf(kr_log_file,"%.5g %.5g %.5g %.5g %.5g ", pAB, pAB_connected, viscous_pressure_drop, Ca, Mobility); - fprintf(kr_log_file,"%.5g\n", eff_pres); - fclose(kr_log_file); - - printf(" Measured capillary number %f \n ",Ca); - } - if (SET_CAPILLARY_NUMBER ){ - Fx *= capillary_number / Ca; - Fy *= capillary_number / Ca; - Fz *= capillary_number / Ca; - if (force_mag > 1e-3){ - Fx *= 1e-3/force_mag; // impose ceiling for stability - Fy *= 1e-3/force_mag; - Fz *= 1e-3/force_mag; - } - if (rank == 0) printf(" -- adjust force by factor %f \n ",capillary_number / Ca); - Averages->SetParams(rhoA,rhoB,tauA,tauB,Fx,Fy,Fz,alpha,beta); - color_db->putVector("F",{Fx,Fy,Fz}); - } - else{ - if (rank==0){ - printf("** Continue to simulate steady *** \n "); - printf("Ca = %f, (previous = %f) \n",Ca,Ca_previous); - } - } - } - } - } - analysis.finish(); - PROFILE_STOP("Update"); - - PROFILE_STOP("Loop"); - PROFILE_SAVE("lbpm_color_simulator",1); - //************************************************************************ - // Compute the walltime per timestep - auto t2 = std::chrono::system_clock::now(); - double cputime = std::chrono::duration( t2 - t1 ).count() / CURRENT_TIMESTEP; - // Performance obtained from each node - double MLUPS = double(Np)/cputime/1000000; - - if (rank==0) printf("********************************************************\n"); - if (rank==0) printf("CPU time = %f \n", cputime); - if (rank==0) printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); - return(MLUPS); - MLUPS *= nprocs; - -} - -void ScaLBL_ColorModel::Run(){ - int nprocs=nprocx*nprocy*nprocz; - const RankInfoStruct rank_info(rank,nprocx,nprocy,nprocz); - int analysis_interval = 1000; // number of timesteps in between in situ analysis - if (analysis_db->keyExists( "analysis_interval" )){ - analysis_interval = analysis_db->getScalar( "analysis_interval" ); - } - - //************ MAIN ITERATION LOOP ***************************************/ - comm.barrier(); - PROFILE_START("Loop"); - //std::shared_ptr analysis_db; - bool Regular = false; - auto current_db = db->cloneDatabase(); - runAnalysis analysis( current_db, rank_info, ScaLBL_Comm, Dm, Np, Regular, Map ); - //analysis.createThreads( analysis_method, 4 ); + runAnalysis analysis(current_db, rank_info, ScaLBL_Comm, Dm, Np, Regular, + Map); auto t1 = std::chrono::system_clock::now(); - while (timestep < timestepMax ) { - //if ( rank==0 ) { printf("Running timestep %i (%i MB)\n",timestep+1,(int)(Utilities::getMemoryUsage()/1048576)); } - PROFILE_START("Update"); - // *************ODD TIMESTEP************* - timestep++; - // Compute the Phase indicator field - // Read for Aq, Bq happens in this routine (requires communication) - ScaLBL_Comm->BiSendD3Q7AA(Aq,Bq); //READ FROM NORMAL - ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->BiRecvD3Q7AA(Aq,Bq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, 0, ScaLBL_Comm->LastExterior(), Np); + int CURRENT_TIMESTEP = 0; + int EXIT_TIMESTEP = min(timestepMax, returntime); + while (timestep < EXIT_TIMESTEP) { + PROFILE_START("Update"); + // *************ODD TIMESTEP************* + timestep++; + // Compute the Phase indicator field + // Read for Aq, Bq happens in this routine (requires communication) + ScaLBL_Comm->BiSendD3Q7AA(Aq, Bq); //READ FROM NORMAL + ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->BiRecvD3Q7AA(Aq, Bq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, 0, + ScaLBL_Comm->LastExterior(), Np); - // Perform the collision operation - ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL - if (BoundaryCondition > 0 && BoundaryCondition < 5){ - ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); - ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); - } - // Halo exchange for phase field - ScaLBL_Comm_Regular->SendHalo(Phi); + // Perform the collision operation + ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL + if (BoundaryCondition > 0 && BoundaryCondition < 5) { + ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); + ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); + } + // Halo exchange for phase field + ScaLBL_Comm_Regular->SendHalo(Phi); - ScaLBL_D3Q19_AAodd_Color(NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, Nx, Nx*Ny, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm_Regular->RecvHalo(Phi); - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - // Set BCs - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); - } - ScaLBL_D3Q19_AAodd_Color(NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, Nx, Nx*Ny, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->Barrier(); + ScaLBL_D3Q19_AAodd_Color( + NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, + tauA, tauB, alpha, beta, Fx, Fy, Fz, Nx, Nx * Ny, + ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm_Regular->RecvHalo(Phi); + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + // Set BCs + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } + if (BoundaryCondition == 4) { + din = + ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); + } + ScaLBL_D3Q19_AAodd_Color(NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, + Velocity, rhoA, rhoB, tauA, tauB, alpha, beta, + Fx, Fy, Fz, Nx, Nx * Ny, 0, + ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm->Barrier(); - // *************EVEN TIMESTEP************* - timestep++; - // Compute the Phase indicator field - ScaLBL_Comm->BiSendD3Q7AA(Aq,Bq); //READ FROM NORMAL - ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->BiRecvD3Q7AA(Aq,Bq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, 0, ScaLBL_Comm->LastExterior(), Np); + // *************EVEN TIMESTEP************* + timestep++; + // Compute the Phase indicator field + ScaLBL_Comm->BiSendD3Q7AA(Aq, Bq); //READ FROM NORMAL + ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->BiRecvD3Q7AA(Aq, Bq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, 0, + ScaLBL_Comm->LastExterior(), Np); - // Perform the collision operation - ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL - // Halo exchange for phase field - if (BoundaryCondition > 0 && BoundaryCondition < 5){ - ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); - ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); - } - ScaLBL_Comm_Regular->SendHalo(Phi); - ScaLBL_D3Q19_AAeven_Color(dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, Nx, Nx*Ny, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm_Regular->RecvHalo(Phi); - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - // Set boundary conditions - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); - } - ScaLBL_D3Q19_AAeven_Color(dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, Nx, Nx*Ny, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->Barrier(); - //************************************************************************ - PROFILE_STOP("Update"); + // Perform the collision operation + ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL + // Halo exchange for phase field + if (BoundaryCondition > 0 && BoundaryCondition < 5) { + ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); + ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); + } + ScaLBL_Comm_Regular->SendHalo(Phi); + ScaLBL_D3Q19_AAeven_Color(dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, + rhoB, tauA, tauB, alpha, beta, Fx, Fy, Fz, Nx, + Nx * Ny, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm_Regular->RecvHalo(Phi); + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + // Set boundary conditions + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 4) { + din = + ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); + } + ScaLBL_D3Q19_AAeven_Color(dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, + rhoB, tauA, tauB, alpha, beta, Fx, Fy, Fz, Nx, + Nx * Ny, 0, ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm->Barrier(); + //************************************************************************ + analysis.basic( + timestep, current_db, *Averages, Phi, Pressure, Velocity, fq, + Den); // allow initial ramp-up to get closer to steady state - if (rank==0 && timestep%analysis_interval == 0 && BoundaryCondition == 4){ - printf("%i %f \n",timestep,din); - } - // Run the analysis - analysis.basic(timestep, current_db, *Averages, Phi, Pressure, Velocity, fq, Den ); - } - analysis.finish(); - PROFILE_STOP("Loop"); - PROFILE_SAVE("lbpm_color_simulator",1); - //************************************************************************ - ScaLBL_Comm->Barrier(); - if (rank==0) printf("-------------------------------------------------------------------\n"); - // Compute the walltime per timestep + CURRENT_TIMESTEP += 2; + if (CURRENT_TIMESTEP > MIN_STEADY_TIMESTEPS && BoundaryCondition == 0) { + analysis.finish(); + + double volB = Averages->gwb.V; + double volA = Averages->gnb.V; + volA /= Dm->Volume; + volB /= Dm->Volume; + ; + //initial_volume = volA*Dm->Volume; + double vA_x = Averages->gnb.Px / Averages->gnb.M; + double vA_y = Averages->gnb.Py / Averages->gnb.M; + double vA_z = Averages->gnb.Pz / Averages->gnb.M; + double vB_x = Averages->gwb.Px / Averages->gwb.M; + double vB_y = Averages->gwb.Py / Averages->gwb.M; + double vB_z = Averages->gwb.Pz / Averages->gwb.M; + double muA = rhoA * (tauA - 0.5) / 3.f; + double muB = rhoB * (tauB - 0.5) / 3.f; + double force_mag = sqrt(Fx * Fx + Fy * Fy + Fz * Fz); + double dir_x = Fx / force_mag; + double dir_y = Fy / force_mag; + double dir_z = Fz / force_mag; + if (force_mag == 0.0) { + // default to z direction + dir_x = 0.0; + dir_y = 0.0; + dir_z = 1.0; + force_mag = 1.0; + } + double current_saturation = volB / (volA + volB); + double flow_rate_A = + volA * (vA_x * dir_x + vA_y * dir_y + vA_z * dir_z); + double flow_rate_B = + volB * (vB_x * dir_x + vB_y * dir_y + vB_z * dir_z); + double Ca = + fabs(muA * flow_rate_A + muB * flow_rate_B) / (5.796 * alpha); + + bool isSteady = false; + if ((fabs((Ca - Ca_previous) / Ca) < tolerance && + CURRENT_TIMESTEP > MIN_STEADY_TIMESTEPS)) + isSteady = true; + if (CURRENT_TIMESTEP >= MAX_STEADY_TIMESTEPS) + isSteady = true; + + if (isSteady && (Ca > maxCa || Ca < minCa) && + SET_CAPILLARY_NUMBER) { + /* re-run the point if the actual Ca is too far from the target Ca */ + isSteady = false; + RESCALE_FORCE = true; + t1 = std::chrono::system_clock::now(); + CURRENT_TIMESTEP = 0; + timestep = INITIAL_TIMESTEP; + TRIGGER_FORCE_RESCALE = true; + if (rank == 0) + printf(" Capillary number missed target value = %f " + "(measured value was Ca = %f) \n ", + capillary_number, Ca); + } + + if (RESCALE_FORCE == true && SET_CAPILLARY_NUMBER == true && + CURRENT_TIMESTEP > RESCALE_FORCE_AFTER_TIMESTEP) { + TRIGGER_FORCE_RESCALE = true; + } + + if (TRIGGER_FORCE_RESCALE) { + RESCALE_FORCE = false; + TRIGGER_FORCE_RESCALE = false; + double RESCALE_FORCE_FACTOR = capillary_number / Ca; + if (RESCALE_FORCE_FACTOR > 2.0) + RESCALE_FORCE_FACTOR = 2.0; + if (RESCALE_FORCE_FACTOR < 0.5) + RESCALE_FORCE_FACTOR = 0.5; + Fx *= RESCALE_FORCE_FACTOR; + Fy *= RESCALE_FORCE_FACTOR; + Fz *= RESCALE_FORCE_FACTOR; + force_mag = sqrt(Fx * Fx + Fy * Fy + Fz * Fz); + if (force_mag > 1e-3) { + Fx *= 1e-3 / force_mag; // impose ceiling for stability + Fy *= 1e-3 / force_mag; + Fz *= 1e-3 / force_mag; + } + if (rank == 0) + printf(" -- adjust force by factor %f \n ", + capillary_number / Ca); + Averages->SetParams(rhoA, rhoB, tauA, tauB, Fx, Fy, Fz, alpha, + beta); + color_db->putVector("F", {Fx, Fy, Fz}); + } + if (isSteady) { + Averages->Full(); + Averages->Write(timestep); + analysis.WriteVisData(timestep, current_db, *Averages, Phi, + Pressure, Velocity, fq, Den); + analysis.finish(); + + if (rank == 0) { + printf("** WRITE STEADY POINT *** "); + printf("Ca = %f, (previous = %f) \n", Ca, Ca_previous); + double h = Dm->voxel_length; + // pressures + double pA = Averages->gnb.p; + double pB = Averages->gwb.p; + double pAc = Averages->gnc.p; + double pBc = Averages->gwc.p; + double pAB = (pA - pB) / (h * 6.0 * alpha); + double pAB_connected = (pAc - pBc) / (h * 6.0 * alpha); + // connected contribution + double Vol_nc = Averages->gnc.V / Dm->Volume; + double Vol_wc = Averages->gwc.V / Dm->Volume; + double Vol_nd = Averages->gnd.V / Dm->Volume; + double Vol_wd = Averages->gwd.V / Dm->Volume; + double Mass_n = Averages->gnc.M + Averages->gnd.M; + double Mass_w = Averages->gwc.M + Averages->gwd.M; + double vAc_x = Averages->gnc.Px / Mass_n; + double vAc_y = Averages->gnc.Py / Mass_n; + double vAc_z = Averages->gnc.Pz / Mass_n; + double vBc_x = Averages->gwc.Px / Mass_w; + double vBc_y = Averages->gwc.Py / Mass_w; + double vBc_z = Averages->gwc.Pz / Mass_w; + // disconnected contribution + double vAd_x = Averages->gnd.Px / Mass_n; + double vAd_y = Averages->gnd.Py / Mass_n; + double vAd_z = Averages->gnd.Pz / Mass_n; + double vBd_x = Averages->gwd.Px / Mass_w; + double vBd_y = Averages->gwd.Py / Mass_w; + double vBd_z = Averages->gwd.Pz / Mass_w; + + double flow_rate_A_connected = + Vol_nc * + (vAc_x * dir_x + vAc_y * dir_y + vAc_z * dir_z); + double flow_rate_B_connected = + Vol_wc * + (vBc_x * dir_x + vBc_y * dir_y + vBc_z * dir_z); + double flow_rate_A_disconnected = + (Vol_nd) * + (vAd_x * dir_x + vAd_y * dir_y + vAd_z * dir_z); + double flow_rate_B_disconnected = + (Vol_wd) * + (vBd_x * dir_x + vBd_y * dir_y + vBd_z * dir_z); + + double kAeff_connected = + h * h * muA * flow_rate_A_connected / (force_mag); + double kBeff_connected = + h * h * muB * flow_rate_B_connected / (force_mag); + + // Saturation normalized effective permeability to account for decoupled phases and + // effective porosity. + double kAeff_connected_low = + (1.0 - current_saturation) * h * h * muA * + flow_rate_A_connected / (force_mag); + double kBeff_connected_low = current_saturation * h * h * + muB * flow_rate_B_connected / + (force_mag); + + double kAeff_disconnected = + h * h * muA * flow_rate_A_disconnected / (force_mag); + double kBeff_disconnected = + h * h * muB * flow_rate_B_disconnected / (force_mag); + + double kAeff = h * h * muA * (flow_rate_A) / (force_mag); + double kBeff = h * h * muB * (flow_rate_B) / (force_mag); + + // Saturation normalized effective permeability to account for decoupled phases and + // effective porosity. + double kAeff_low = (1.0 - current_saturation) * h * h * + muA * (flow_rate_A) / (force_mag); + double kBeff_low = current_saturation * h * h * muB * + (flow_rate_B) / (force_mag); + + double viscous_pressure_drop = + (rhoA * volA + rhoB * volB) * force_mag; + double Mobility = muA / muB; // visc contrast + double eff_pres = + 1.0 / (kAeff + kBeff); // effective pressure drop + + bool WriteHeader = false; + FILE *kr_log_file = fopen("relperm.csv", "r"); + if (kr_log_file != NULL) + fclose(kr_log_file); + else + WriteHeader = true; + kr_log_file = fopen("relperm.csv", "a"); + if (WriteHeader) { + fprintf(kr_log_file, "timesteps sat.water "); + fprintf(kr_log_file, "eff.perm.oil.upper.bound " + "eff.perm.water.upper.bound "); + fprintf(kr_log_file, "eff.perm.oil.lower.bound " + "eff.perm.water.lower.bound "); + fprintf(kr_log_file, + "eff.perm.oil.connected.upper.bound " + "eff.perm.water.connected.upper.bound "); + fprintf(kr_log_file, + "eff.perm.oil.connected.lower.bound " + "eff.perm.water.connected.lower.bound "); + fprintf(kr_log_file, "eff.perm.oil.disconnected " + "eff.perm.water.disconnected "); + fprintf(kr_log_file, + "cap.pressure cap.pressure.connected " + "pressure.drop Ca M eff.pressure\n"); + } + fprintf(kr_log_file, "%i %.5g ", CURRENT_TIMESTEP, + current_saturation); + fprintf(kr_log_file, "%.5g %.5g ", kAeff, kBeff); + fprintf(kr_log_file, "%.5g %.5g ", kAeff_low, kBeff_low); + fprintf(kr_log_file, "%.5g %.5g ", kAeff_connected, + kBeff_connected); + fprintf(kr_log_file, "%.5g %.5g ", kAeff_connected_low, + kBeff_connected_low); + fprintf(kr_log_file, "%.5g %.5g ", kAeff_disconnected, + kBeff_disconnected); + fprintf(kr_log_file, "%.5g %.5g %.5g %.5g %.5g ", pAB, + pAB_connected, viscous_pressure_drop, Ca, Mobility); + fprintf(kr_log_file, "%.5g\n", eff_pres); + fclose(kr_log_file); + + printf(" Measured capillary number %f \n ", Ca); + } + if (SET_CAPILLARY_NUMBER) { + Fx *= capillary_number / Ca; + Fy *= capillary_number / Ca; + Fz *= capillary_number / Ca; + if (force_mag > 1e-3) { + Fx *= 1e-3 / force_mag; // impose ceiling for stability + Fy *= 1e-3 / force_mag; + Fz *= 1e-3 / force_mag; + } + if (rank == 0) + printf(" -- adjust force by factor %f \n ", + capillary_number / Ca); + Averages->SetParams(rhoA, rhoB, tauA, tauB, Fx, Fy, Fz, + alpha, beta); + color_db->putVector("F", {Fx, Fy, Fz}); + } else { + if (rank == 0) { + printf("** Continue to simulate steady *** \n "); + printf("Ca = %f, (previous = %f) \n", Ca, Ca_previous); + } + } + } + } + } + analysis.finish(); + PROFILE_STOP("Update"); + + PROFILE_STOP("Loop"); + PROFILE_SAVE("lbpm_color_simulator", 1); + //************************************************************************ + // Compute the walltime per timestep auto t2 = std::chrono::system_clock::now(); - double cputime = std::chrono::duration( t2 - t1 ).count() / timestep; - // Performance obtained from each node - double MLUPS = double(Np)/cputime/1000000; + double cputime = + std::chrono::duration(t2 - t1).count() / CURRENT_TIMESTEP; + // Performance obtained from each node + double MLUPS = double(Np) / cputime / 1000000; - if (rank==0) printf("********************************************************\n"); - if (rank==0) printf("CPU time = %f \n", cputime); - if (rank==0) printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); - MLUPS *= nprocs; - if (rank==0) printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); - if (rank==0) printf("********************************************************\n"); - - // ************************************************************************ + if (rank == 0) + printf("********************************************************\n"); + if (rank == 0) + printf("CPU time = %f \n", cputime); + if (rank == 0) + printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); + return (MLUPS); + MLUPS *= nprocs; } -void ScaLBL_ColorModel::WriteDebug(){ - // Copy back final phase indicator field and convert to regular layout - DoubleArray PhaseField(Nx,Ny,Nz); - //ScaLBL_Comm->RegularLayout(Map,Phi,PhaseField); - ScaLBL_CopyToHost(PhaseField.data(), Phi, sizeof(double)*N); +void ScaLBL_ColorModel::Run() { + int nprocs = nprocx * nprocy * nprocz; + const RankInfoStruct rank_info(rank, nprocx, nprocy, nprocz); + int analysis_interval = + 1000; // number of timesteps in between in situ analysis + if (analysis_db->keyExists("analysis_interval")) { + analysis_interval = analysis_db->getScalar("analysis_interval"); + } - FILE *OUTFILE; - sprintf(LocalRankFilename,"Phase.%05i.raw",rank); - OUTFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,OUTFILE); - fclose(OUTFILE); + //************ MAIN ITERATION LOOP ***************************************/ + comm.barrier(); + PROFILE_START("Loop"); + //std::shared_ptr analysis_db; + bool Regular = false; + auto current_db = db->cloneDatabase(); + runAnalysis analysis(current_db, rank_info, ScaLBL_Comm, Dm, Np, Regular, + Map); + //analysis.createThreads( analysis_method, 4 ); + auto t1 = std::chrono::system_clock::now(); + while (timestep < timestepMax) { + PROFILE_START("Update"); - ScaLBL_Comm->RegularLayout(Map,&Den[0],PhaseField); - FILE *AFILE; - sprintf(LocalRankFilename,"A.%05i.raw",rank); - AFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,AFILE); - fclose(AFILE); + // *************ODD TIMESTEP************* + timestep++; + // Compute the Phase indicator field + // Read for Aq, Bq happens in this routine (requires communication) + ScaLBL_Comm->BiSendD3Q7AA(Aq, Bq); //READ FROM NORMAL + ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->BiRecvD3Q7AA(Aq, Bq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, 0, + ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->RegularLayout(Map,&Den[Np],PhaseField); - FILE *BFILE; - sprintf(LocalRankFilename,"B.%05i.raw",rank); - BFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,BFILE); - fclose(BFILE); + // Perform the collision operation + ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL + if (BoundaryCondition > 0 && BoundaryCondition < 5) { + ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); + ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); + } + // Halo exchange for phase field + ScaLBL_Comm_Regular->SendHalo(Phi); - ScaLBL_Comm->RegularLayout(Map,Pressure,PhaseField); - FILE *PFILE; - sprintf(LocalRankFilename,"Pressure.%05i.raw",rank); - PFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,PFILE); - fclose(PFILE); + ScaLBL_D3Q19_AAodd_Color( + NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, + tauA, tauB, alpha, beta, Fx, Fy, Fz, Nx, Nx * Ny, + ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm_Regular->RecvHalo(Phi); + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + // Set BCs + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } + if (BoundaryCondition == 4) { + din = + ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); + } + ScaLBL_D3Q19_AAodd_Color(NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, + Velocity, rhoA, rhoB, tauA, tauB, alpha, beta, + Fx, Fy, Fz, Nx, Nx * Ny, 0, + ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm->Barrier(); - ScaLBL_Comm->RegularLayout(Map,&Velocity[0],PhaseField); - FILE *VELX_FILE; - sprintf(LocalRankFilename,"Velocity_X.%05i.raw",rank); - VELX_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELX_FILE); - fclose(VELX_FILE); + // *************EVEN TIMESTEP************* + timestep++; + // Compute the Phase indicator field + ScaLBL_Comm->BiSendD3Q7AA(Aq, Bq); //READ FROM NORMAL + ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->BiRecvD3Q7AA(Aq, Bq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, 0, + ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->RegularLayout(Map,&Velocity[Np],PhaseField); - FILE *VELY_FILE; - sprintf(LocalRankFilename,"Velocity_Y.%05i.raw",rank); - VELY_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELY_FILE); - fclose(VELY_FILE); + // Perform the collision operation + ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL + // Halo exchange for phase field + if (BoundaryCondition > 0 && BoundaryCondition < 5) { + ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); + ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); + } + ScaLBL_Comm_Regular->SendHalo(Phi); + ScaLBL_D3Q19_AAeven_Color(dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, + rhoB, tauA, tauB, alpha, beta, Fx, Fy, Fz, Nx, + Nx * Ny, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm_Regular->RecvHalo(Phi); + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + // Set boundary conditions + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 4) { + din = + ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); + } + ScaLBL_D3Q19_AAeven_Color(dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, + rhoB, tauA, tauB, alpha, beta, Fx, Fy, Fz, Nx, + Nx * Ny, 0, ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm->Barrier(); + //************************************************************************ + PROFILE_STOP("Update"); - ScaLBL_Comm->RegularLayout(Map,&Velocity[2*Np],PhaseField); - FILE *VELZ_FILE; - sprintf(LocalRankFilename,"Velocity_Z.%05i.raw",rank); - VELZ_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELZ_FILE); - fclose(VELZ_FILE); + if (rank == 0 && timestep % analysis_interval == 0 && + BoundaryCondition == 4) { + printf("%i %f \n", timestep, din); + } + // Run the analysis + analysis.basic(timestep, current_db, *Averages, Phi, Pressure, Velocity, + fq, Den); + } + analysis.finish(); + PROFILE_STOP("Loop"); + PROFILE_SAVE("lbpm_color_simulator", 1); + //************************************************************************ + ScaLBL_Comm->Barrier(); + if (rank == 0) + printf("---------------------------------------------------------------" + "----\n"); + // Compute the walltime per timestep + auto t2 = std::chrono::system_clock::now(); + double cputime = std::chrono::duration(t2 - t1).count() / timestep; + // Performance obtained from each node + double MLUPS = double(Np) / cputime / 1000000; + if (rank == 0) + printf("********************************************************\n"); + if (rank == 0) + printf("CPU time = %f \n", cputime); + if (rank == 0) + printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); + MLUPS *= nprocs; + if (rank == 0) + printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); + if (rank == 0) + printf("********************************************************\n"); + + // ************************************************************************ } +void ScaLBL_ColorModel::WriteDebug() { + // Copy back final phase indicator field and convert to regular layout + DoubleArray PhaseField(Nx, Ny, Nz); + //ScaLBL_Comm->RegularLayout(Map,Phi,PhaseField); + ScaLBL_CopyToHost(PhaseField.data(), Phi, sizeof(double) * N); + + FILE *OUTFILE; + sprintf(LocalRankFilename, "Phase.%05i.raw", rank); + OUTFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, OUTFILE); + fclose(OUTFILE); + + ScaLBL_Comm->RegularLayout(Map, &Den[0], PhaseField); + FILE *AFILE; + sprintf(LocalRankFilename, "A.%05i.raw", rank); + AFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, AFILE); + fclose(AFILE); + + ScaLBL_Comm->RegularLayout(Map, &Den[Np], PhaseField); + FILE *BFILE; + sprintf(LocalRankFilename, "B.%05i.raw", rank); + BFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, BFILE); + fclose(BFILE); + + ScaLBL_Comm->RegularLayout(Map, Pressure, PhaseField); + FILE *PFILE; + sprintf(LocalRankFilename, "Pressure.%05i.raw", rank); + PFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, PFILE); + fclose(PFILE); + + ScaLBL_Comm->RegularLayout(Map, &Velocity[0], PhaseField); + FILE *VELX_FILE; + sprintf(LocalRankFilename, "Velocity_X.%05i.raw", rank); + VELX_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELX_FILE); + fclose(VELX_FILE); + + ScaLBL_Comm->RegularLayout(Map, &Velocity[Np], PhaseField); + FILE *VELY_FILE; + sprintf(LocalRankFilename, "Velocity_Y.%05i.raw", rank); + VELY_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELY_FILE); + fclose(VELY_FILE); + + ScaLBL_Comm->RegularLayout(Map, &Velocity[2 * Np], PhaseField); + FILE *VELZ_FILE; + sprintf(LocalRankFilename, "Velocity_Z.%05i.raw", rank); + VELZ_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELZ_FILE); + fclose(VELZ_FILE); +} diff --git a/models/ColorModel.h b/models/ColorModel.h index 57071d74..ed7bed63 100644 --- a/models/ColorModel.h +++ b/models/ColorModel.h @@ -33,7 +33,6 @@ Implementation of color lattice boltzmann model #include "ProfilerApp.h" #include "threadpool/thread_pool.h" - #ifndef ScaLBL_ColorModel_INC #define ScaLBL_ColorModel_INC @@ -46,7 +45,7 @@ Implementation of color lattice boltzmann model * Mass transport equations are described by D3Q7 scheme */ -class ScaLBL_ColorModel{ +class ScaLBL_ColorModel { public: /** * \brief Constructor @@ -54,81 +53,81 @@ public: * @param NP number of processors * @param COMM MPI communicator */ - ScaLBL_ColorModel(int RANK, int NP, const Utilities::MPI& COMM); - ~ScaLBL_ColorModel(); - + ScaLBL_ColorModel(int RANK, int NP, const Utilities::MPI &COMM); + ~ScaLBL_ColorModel(); + /** * \brief Read simulation parameters * @param filename input database file that includes "Color" section - */ - void ReadParams(string filename); - + */ + void ReadParams(string filename); + /** * \brief Read simulation parameters * @param db0 input database that includes "Color" section */ - void ReadParams(std::shared_ptr db0); - + void ReadParams(std::shared_ptr db0); + /** * \brief Create domain data structures */ - void SetDomain(); - + void SetDomain(); + /** * \brief Read image data */ - void ReadInput(); - + void ReadInput(); + /** * \brief Create color model data structures */ - void Create(); - + void Create(); + /** * \brief Initialize the simulation */ - void Initialize(); - + void Initialize(); + /** * \brief Run the simulation */ - void Run(); - + void Run(); + /** * \brief Run the simulation * @param returntime - timestep at which the routine will return */ - double Run(int returntime); - + double Run(int returntime); + /** * \brief Debugging function to dump simulation state to disk */ - void WriteDebug(); - + void WriteDebug(); + /** * \brief Copy the phase field for use by external methods * @param f - DoubleArray to hold the phase field */ - void getPhaseField(DoubleArray &f); - - bool Restart,pBC; - bool REVERSE_FLOW_DIRECTION; - int timestep,timestepMax; - int BoundaryCondition; - double tauA,tauB,rhoA,rhoB,alpha,beta; - double Fx,Fy,Fz,flux; - double din,dout,inletA,inletB,outletA,outletB; - - int Nx,Ny,Nz,N,Np; - int rank,nprocx,nprocy,nprocz,nprocs; - double Lx,Ly,Lz; + void getPhaseField(DoubleArray &f); - std::shared_ptr Dm; // this domain is for analysis - std::shared_ptr Mask; // this domain is for lbm - std::shared_ptr ScaLBL_Comm; - std::shared_ptr ScaLBL_Comm_Regular; + bool Restart, pBC; + bool REVERSE_FLOW_DIRECTION; + int timestep, timestepMax; + int BoundaryCondition; + double tauA, tauB, rhoA, rhoB, alpha, beta; + double Fx, Fy, Fz, flux; + double din, dout, inletA, inletB, outletA, outletB; + + int Nx, Ny, Nz, N, Np; + int rank, nprocx, nprocy, nprocz, nprocs; + double Lx, Ly, Lz; + + std::shared_ptr Dm; // this domain is for analysis + std::shared_ptr Mask; // this domain is for lbm + std::shared_ptr ScaLBL_Comm; + std::shared_ptr ScaLBL_Comm_Regular; std::shared_ptr Averages; - + // input database std::shared_ptr db; std::shared_ptr domain_db; @@ -137,33 +136,32 @@ public: std::shared_ptr vis_db; IntArray Map; - signed char *id; - int *NeighborList; - int *dvcMap; - double *fq, *Aq, *Bq; - double *Den, *Phi; - double *ColorGrad; - double *Velocity; - double *Pressure; + signed char *id; + int *NeighborList; + int *dvcMap; + double *fq, *Aq, *Bq; + double *Den, *Phi; + double *ColorGrad; + double *Velocity; + double *Pressure; /** * \brief Assign wetting affinity values */ - void AssignComponentLabels(double *phase); - -private: - Utilities::MPI comm; + void AssignComponentLabels(double *phase); - int dist_mem_size; - int neighborSize; - // filenames +private: + Utilities::MPI comm; + + int dist_mem_size; + int neighborSize; + // filenames char LocalRankString[8]; char LocalRankFilename[40]; char LocalRestartFile[40]; - + //int rank,nprocs; void LoadParams(std::shared_ptr db0); }; #endif - diff --git a/models/DFHModel.cpp b/models/DFHModel.cpp index 24639d3e..22467feb 100644 --- a/models/DFHModel.cpp +++ b/models/DFHModel.cpp @@ -3,16 +3,13 @@ color lattice boltzmann model */ #include "models/DFHModel.h" -ScaLBL_DFHModel::ScaLBL_DFHModel(int RANK, int NP, const Utilities::MPI& COMM): -rank(RANK), nprocs(NP), Restart(0),timestep(0),timestepMax(0),tauA(0),tauB(0),rhoA(0),rhoB(0),alpha(0),beta(0), -Fx(0),Fy(0),Fz(0),flux(0),din(0),dout(0),inletA(0),inletB(0),outletA(0),outletB(0), -Nx(0),Ny(0),Nz(0),N(0),Np(0),nprocx(0),nprocy(0),nprocz(0),BoundaryCondition(0),Lx(0),Ly(0),Lz(0),comm(COMM) -{ - -} -ScaLBL_DFHModel::~ScaLBL_DFHModel(){ - -} +ScaLBL_DFHModel::ScaLBL_DFHModel(int RANK, int NP, const Utilities::MPI &COMM) + : rank(RANK), nprocs(NP), Restart(0), timestep(0), timestepMax(0), tauA(0), + tauB(0), rhoA(0), rhoB(0), alpha(0), beta(0), Fx(0), Fy(0), Fz(0), + flux(0), din(0), dout(0), inletA(0), inletB(0), outletA(0), outletB(0), + Nx(0), Ny(0), Nz(0), N(0), Np(0), nprocx(0), nprocy(0), nprocz(0), + BoundaryCondition(0), Lx(0), Ly(0), Lz(0), comm(COMM) {} +ScaLBL_DFHModel::~ScaLBL_DFHModel() {} /*void ScaLBL_DFHModel::WriteCheckpoint(const char *FILENAME, const double *cPhi, const double *cfq, int Np) { @@ -51,323 +48,352 @@ void ScaLBL_DFHModel::ReadCheckpoint(char *FILENAME, double *cPhi, double *cfq, } */ +void ScaLBL_DFHModel::ReadParams(string filename) { + // read the input database + db = std::make_shared(filename); + domain_db = db->getDatabase("Domain"); + color_db = db->getDatabase("Color"); + analysis_db = db->getDatabase("Analysis"); -void ScaLBL_DFHModel::ReadParams(string filename){ - // read the input database - db = std::make_shared( filename ); - domain_db = db->getDatabase( "Domain" ); - color_db = db->getDatabase( "Color" ); - analysis_db = db->getDatabase( "Analysis" ); + // Color Model parameters + timestepMax = color_db->getWithDefault("timestepMax", 100); + tauA = color_db->getWithDefault("tauA", 1.0); + tauB = color_db->getWithDefault("tauB", 1.0); + rhoA = color_db->getWithDefault("rhoA", 1.0); + rhoB = color_db->getWithDefault("rhoB", 1.0); + alpha = color_db->getWithDefault("alpha", 0.001); + beta = color_db->getWithDefault("beta", 0.95); + Restart = color_db->getWithDefault("Restart", true); + din = color_db->getWithDefault("din", 1.0); + dout = color_db->getWithDefault("dout", 1.0); + flux = color_db->getWithDefault("flux", 0.0); + if (color_db->keyExists("F")) { + Fx = color_db->getVector("F")[0]; + Fy = color_db->getVector("F")[1]; + Fz = color_db->getVector("F")[2]; + } + inletA = 1.f; + inletB = 0.f; + outletA = 0.f; + outletB = 1.f; - // Color Model parameters - timestepMax = color_db->getWithDefault( "timestepMax", 100 ); - tauA = color_db->getWithDefault( "tauA", 1.0 ); - tauB = color_db->getWithDefault( "tauB", 1.0 ); - rhoA = color_db->getWithDefault( "rhoA", 1.0 ); - rhoB = color_db->getWithDefault( "rhoB", 1.0 ); - alpha = color_db->getWithDefault( "alpha", 0.001 ); - beta = color_db->getWithDefault( "beta", 0.95 ); - Restart = color_db->getWithDefault( "Restart", true ); - din = color_db->getWithDefault( "din", 1.0 ); - dout = color_db->getWithDefault( "dout", 1.0 ); - flux = color_db->getWithDefault( "flux", 0.0 ); - if (color_db->keyExists( "F" )){ - Fx = color_db->getVector( "F" )[0]; - Fy = color_db->getVector( "F" )[1]; - Fz = color_db->getVector( "F" )[2]; - } - inletA=1.f; - inletB=0.f; - outletA=0.f; - outletB=1.f; + BoundaryCondition = domain_db->getScalar("BC"); + if (color_db->keyExists("BC")) { + BoundaryCondition = color_db->getScalar("BC"); + } else if (domain_db->keyExists("BC")) { + BoundaryCondition = domain_db->getScalar("BC"); + } - BoundaryCondition = domain_db->getScalar( "BC" ); - if (color_db->keyExists( "BC" )){ - BoundaryCondition = color_db->getScalar( "BC" ); - } - else if (domain_db->keyExists( "BC" )){ - BoundaryCondition = domain_db->getScalar( "BC" ); - } - - // Read domain parameters - auto L = domain_db->getVector( "L" ); - auto size = domain_db->getVector( "n" ); - auto nproc = domain_db->getVector( "nproc" ); - Nx = size[0]; - Ny = size[1]; - Nz = size[2]; - Lx = L[0]; - Ly = L[1]; - Lz = L[2]; - nprocx = nproc[0]; - nprocy = nproc[1]; - nprocz = nproc[2]; - - if (BoundaryCondition==4) flux = din*rhoA; // mass flux must adjust for density (see formulation for details) + // Read domain parameters + auto L = domain_db->getVector("L"); + auto size = domain_db->getVector("n"); + auto nproc = domain_db->getVector("nproc"); + Nx = size[0]; + Ny = size[1]; + Nz = size[2]; + Lx = L[0]; + Ly = L[1]; + Lz = L[2]; + nprocx = nproc[0]; + nprocy = nproc[1]; + nprocz = nproc[2]; + if (BoundaryCondition == 4) + flux = + din * + rhoA; // mass flux must adjust for density (see formulation for details) } -void ScaLBL_DFHModel::SetDomain(){ - Dm = std::shared_ptr(new Domain(domain_db,comm)); // full domain for analysis - Mask = std::shared_ptr(new Domain(domain_db,comm)); // mask domain removes immobile phases - Nx+=2; Ny+=2; Nz += 2; - N = Nx*Ny*Nz; - id = new char [N]; - for (int i=0; iid[i] = 1; // initialize this way - Averages = std::shared_ptr ( new TwoPhase(Dm) ); // TwoPhase analysis object - comm.barrier(); - Dm->CommInit(); - comm.barrier(); - rank = Dm->rank(); +void ScaLBL_DFHModel::SetDomain() { + Dm = std::shared_ptr( + new Domain(domain_db, comm)); // full domain for analysis + Mask = std::shared_ptr( + new Domain(domain_db, comm)); // mask domain removes immobile phases + Nx += 2; + Ny += 2; + Nz += 2; + N = Nx * Ny * Nz; + id = new char[N]; + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = 1; // initialize this way + Averages = + std::shared_ptr(new TwoPhase(Dm)); // TwoPhase analysis object + comm.barrier(); + Dm->CommInit(); + comm.barrier(); + rank = Dm->rank(); } -void ScaLBL_DFHModel::ReadInput(){ - //....................................................................... - if (rank == 0) printf("Read input media... \n"); - //....................................................................... - Mask->ReadIDs(); - for (int i=0; iid[i]; // save what was read +void ScaLBL_DFHModel::ReadInput() { + //....................................................................... + if (rank == 0) + printf("Read input media... \n"); + //....................................................................... + Mask->ReadIDs(); + for (int i = 0; i < Nx * Ny * Nz; i++) + id[i] = Mask->id[i]; // save what was read - sprintf(LocalRankString,"%05d",rank); - sprintf(LocalRankFilename,"%s%s","ID.",LocalRankString); - sprintf(LocalRestartFile,"%s%s","Restart.",LocalRankString); + sprintf(LocalRankString, "%05d", rank); + sprintf(LocalRankFilename, "%s%s", "ID.", LocalRankString); + sprintf(LocalRestartFile, "%s%s", "Restart.", LocalRankString); - // .......... READ THE INPUT FILE ....................................... - //........................................................................... - if (rank == 0) cout << "Reading in signed distance function..." << endl; - //....................................................................... - sprintf(LocalRankString,"%05d",rank); - sprintf(LocalRankFilename,"%s%s","SignDist.",LocalRankString); - ReadBinaryFile(LocalRankFilename, Averages->SDs.data(), N); - comm.barrier(); - if (rank == 0) cout << "Domain set." << endl; + // .......... READ THE INPUT FILE ....................................... + //........................................................................... + if (rank == 0) + cout << "Reading in signed distance function..." << endl; + //....................................................................... + sprintf(LocalRankString, "%05d", rank); + sprintf(LocalRankFilename, "%s%s", "SignDist.", LocalRankString); + ReadBinaryFile(LocalRankFilename, Averages->SDs.data(), N); + comm.barrier(); + if (rank == 0) + cout << "Domain set." << endl; } -void ScaLBL_DFHModel::AssignComponentLabels(double *phase) -{ - size_t NLABELS=0; - char VALUE=0; - double AFFINITY=0.f; +void ScaLBL_DFHModel::AssignComponentLabels(double *phase) { + size_t NLABELS = 0; + char VALUE = 0; + double AFFINITY = 0.f; - auto LabelList = color_db->getVector( "ComponentLabels" ); - auto AffinityList = color_db->getVector( "ComponentAffinity" ); + auto LabelList = color_db->getVector("ComponentLabels"); + auto AffinityList = color_db->getVector("ComponentAffinity"); - NLABELS=LabelList.size(); - if (NLABELS != AffinityList.size()){ - ERROR("Error: ComponentLabels and ComponentAffinity must be the same length! \n"); - } + NLABELS = LabelList.size(); + if (NLABELS != AffinityList.size()) { + ERROR("Error: ComponentLabels and ComponentAffinity must be the same " + "length! \n"); + } - if (rank==0){ - printf("Components labels: %lu \n",NLABELS); - for (unsigned int idx=0; idxid[n] = 0; // set mask to zero since this is an immobile component - } - } - phase[n] = AFFINITY; - } - } - } - // Set Dm to match Mask - for (int i=0; iid[i] = Mask->id[i]; + if (rank == 0) { + printf("Components labels: %lu \n", NLABELS); + for (unsigned int idx = 0; idx < NLABELS; idx++) { + VALUE = LabelList[idx]; + AFFINITY = AffinityList[idx]; + printf(" label=%i, affinity=%f\n", int(VALUE), AFFINITY); + } + } + // Assign the labels + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + VALUE = id[n]; + // Assign the affinity from the paired list + for (unsigned int idx = 0; idx < NLABELS; idx++) { + //printf("rank=%i, idx=%i, value=%i, %i, \n",rank(),idx, VALUE,LabelList[idx]); + if (VALUE == LabelList[idx]) { + AFFINITY = AffinityList[idx]; + idx = NLABELS; + Mask->id[n] = + 0; // set mask to zero since this is an immobile component + } + } + phase[n] = AFFINITY; + } + } + } + // Set Dm to match Mask + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = Mask->id[i]; } - -void ScaLBL_DFHModel::Create(){ - /* +void ScaLBL_DFHModel::Create() { + /* * This function creates the variables needed to run a LBM */ - //......................................................... - // don't perform computations at the eight corners - //id[0] = id[Nx-1] = id[(Ny-1)*Nx] = id[(Ny-1)*Nx + Nx-1] = 0; - //id[(Nz-1)*Nx*Ny] = id[(Nz-1)*Nx*Ny+Nx-1] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx + Nx-1] = 0; + //......................................................... + // don't perform computations at the eight corners + //id[0] = id[Nx-1] = id[(Ny-1)*Nx] = id[(Ny-1)*Nx + Nx-1] = 0; + //id[(Nz-1)*Nx*Ny] = id[(Nz-1)*Nx*Ny+Nx-1] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx + Nx-1] = 0; - //......................................................... - // Initialize communication structures in averaging domain - for (int i=0; iid[i] = Mask->id[i]; - Mask->CommInit(); - Np=Mask->PoreCount(); - //........................................................................... - if (rank==0) printf ("Create ScaLBL_Communicator \n"); - // Create a communicator for the device (will use optimized layout) - // ScaLBL_Communicator ScaLBL_Comm(Mask); // original - ScaLBL_Comm = std::shared_ptr(new ScaLBL_Communicator(Mask)); + //......................................................... + // Initialize communication structures in averaging domain + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = Mask->id[i]; + Mask->CommInit(); + Np = Mask->PoreCount(); + //........................................................................... + if (rank == 0) + printf("Create ScaLBL_Communicator \n"); + // Create a communicator for the device (will use optimized layout) + // ScaLBL_Communicator ScaLBL_Comm(Mask); // original + ScaLBL_Comm = + std::shared_ptr(new ScaLBL_Communicator(Mask)); - int Npad=(Np/16 + 2)*16; - if (rank==0) printf ("Set up memory efficient layout, %i | %i | %i \n", Np, Npad, N); - Map.resize(Nx,Ny,Nz); Map.fill(-2); - auto neighborList= new int[18*Npad]; - Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map,neighborList,Mask->id.data(),Np,1); - ScaLBL_Comm->Barrier(); - //........................................................................... - // MAIN VARIABLES ALLOCATED HERE - //........................................................................... - // LBM variables - if (rank==0) printf ("Allocating distributions \n"); - //......................device distributions................................. - dist_mem_size = Np*sizeof(double); - neighborSize=18*(Np*sizeof(int)); + int Npad = (Np / 16 + 2) * 16; + if (rank == 0) + printf("Set up memory efficient layout, %i | %i | %i \n", Np, Npad, N); + Map.resize(Nx, Ny, Nz); + Map.fill(-2); + auto neighborList = new int[18 * Npad]; + Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map, neighborList, + Mask->id.data(), Np, 1); + ScaLBL_Comm->Barrier(); + //........................................................................... + // MAIN VARIABLES ALLOCATED HERE + //........................................................................... + // LBM variables + if (rank == 0) + printf("Allocating distributions \n"); + //......................device distributions................................. + dist_mem_size = Np * sizeof(double); + neighborSize = 18 * (Np * sizeof(int)); - //........................................................................... - ScaLBL_AllocateDeviceMemory((void **) &NeighborList, neighborSize); - ScaLBL_AllocateDeviceMemory((void **) &dvcMap, sizeof(int)*Np); - ScaLBL_AllocateDeviceMemory((void **) &fq, 19*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Aq, 7*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Bq, 7*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Den, 2*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Phi, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &Pressure, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &Velocity, 3*sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &Gradient, 3*sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &SolidPotential, 3*sizeof(double)*Np); + //........................................................................... + ScaLBL_AllocateDeviceMemory((void **)&NeighborList, neighborSize); + ScaLBL_AllocateDeviceMemory((void **)&dvcMap, sizeof(int) * Np); + ScaLBL_AllocateDeviceMemory((void **)&fq, 19 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Aq, 7 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Bq, 7 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Den, 2 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Phi, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&Pressure, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&Velocity, 3 * sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&Gradient, 3 * sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&SolidPotential, + 3 * sizeof(double) * Np); - //........................................................................... - // Update GPU data structures - if (rank==0) printf ("Setting up device map and neighbor list \n"); - // copy the neighbor list - ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); + //........................................................................... + // Update GPU data structures + if (rank == 0) + printf("Setting up device map and neighbor list \n"); + // copy the neighbor list + ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); - int *TmpMap; - TmpMap=new int[Np]; - for (int k=1; kUpdateMeshValues(); // this computes the gradient of distance field (among other things) - // Create the distance stencil - // Compute solid forces based on mean field approximation - double *Dst; - Dst = new double [3*3*3]; - for (int kk=0; kk<3; kk++){ - for (int jj=0; jj<3; jj++){ - for (int ii=0; ii<3; ii++){ - int index = kk*9+jj*3+ii; - Dst[index] = sqrt(double(ii-1)*double(ii-1) + double(jj-1)*double(jj-1)+ double(kk-1)*double(kk-1)); - } - } - } - double w_face = 1.0; //1.f/18.f; - double w_edge = 0.5; //1.f/36.f; - double w_corner = 0.f; - //local - Dst[13] = 0.f; - //faces - Dst[4] = w_face; - Dst[10] = w_face; - Dst[12] = w_face; - Dst[14] = w_face; - Dst[16] = w_face; - Dst[22] = w_face; - // corners - Dst[0] = w_corner; - Dst[2] = w_corner; - Dst[6] = w_corner; - Dst[8] = w_corner; - Dst[18] = w_corner; - Dst[20] = w_corner; - Dst[24] = w_corner; - Dst[26] = w_corner; - // edges - Dst[1] = w_edge; - Dst[3] = w_edge; - Dst[5] = w_edge; - Dst[7] = w_edge; - Dst[9] = w_edge; - Dst[11] = w_edge; - Dst[15] = w_edge; - Dst[17] = w_edge; - Dst[19] = w_edge; - Dst[21] = w_edge; - Dst[23] = w_edge; - Dst[25] = w_edge; +void ScaLBL_DFHModel::AssignSolidPotential() { + if (rank == 0) + printf("Computing solid interaction potential (Shan-Chen type) \n"); + double *PhaseLabel; + PhaseLabel = new double[Nx * Ny * Nz]; + AssignComponentLabels(PhaseLabel); + double *Tmp; + Tmp = new double[3 * Np]; + //Averages->UpdateMeshValues(); // this computes the gradient of distance field (among other things) + // Create the distance stencil + // Compute solid forces based on mean field approximation + double *Dst; + Dst = new double[3 * 3 * 3]; + for (int kk = 0; kk < 3; kk++) { + for (int jj = 0; jj < 3; jj++) { + for (int ii = 0; ii < 3; ii++) { + int index = kk * 9 + jj * 3 + ii; + Dst[index] = sqrt(double(ii - 1) * double(ii - 1) + + double(jj - 1) * double(jj - 1) + + double(kk - 1) * double(kk - 1)); + } + } + } + double w_face = 1.0; //1.f/18.f; + double w_edge = 0.5; //1.f/36.f; + double w_corner = 0.f; + //local + Dst[13] = 0.f; + //faces + Dst[4] = w_face; + Dst[10] = w_face; + Dst[12] = w_face; + Dst[14] = w_face; + Dst[16] = w_face; + Dst[22] = w_face; + // corners + Dst[0] = w_corner; + Dst[2] = w_corner; + Dst[6] = w_corner; + Dst[8] = w_corner; + Dst[18] = w_corner; + Dst[20] = w_corner; + Dst[24] = w_corner; + Dst[26] = w_corner; + // edges + Dst[1] = w_edge; + Dst[3] = w_edge; + Dst[5] = w_edge; + Dst[7] = w_edge; + Dst[9] = w_edge; + Dst[11] = w_edge; + Dst[15] = w_edge; + Dst[17] = w_edge; + Dst[19] = w_edge; + Dst[21] = w_edge; + Dst[23] = w_edge; + Dst[25] = w_edge; - for (int k=1; kid[nn] > 0)){ - double vec_x = double(ii-1); - double vec_y = double(jj-1); - double vec_z = double(kk-1); - double GWNS=PhaseLabel[nn]; - phi_x += GWNS*weight*vec_x; - phi_y += GWNS*weight*vec_y; - phi_z += GWNS*weight*vec_z; - /* + int nn = idk * Nx * Ny + idj * Nx + idi; + if (!(Mask->id[nn] > 0)) { + double vec_x = double(ii - 1); + double vec_y = double(jj - 1); + double vec_z = double(kk - 1); + double GWNS = PhaseLabel[nn]; + phi_x += GWNS * weight * vec_x; + phi_y += GWNS * weight * vec_y; + phi_z += GWNS * weight * vec_z; + /* double GAMMA=-2.f; if (distval > 2.f) ALPHA=0.f; // symmetric cutoff distance phi_x += ALPHA*exp(GAMMA*distval)*vec_x/distval; phi_y += ALPHA*exp(GAMMA*distval)*vec_y/distval; phi_z += ALPHA*exp(GAMMA*distval)*vec_z/distval; */ - } - } - } - } - Tmp[idx] = phi_x; - Tmp[idx+Np] = phi_y; - Tmp[idx+2*Np] = phi_z; + } + } + } + } + Tmp[idx] = phi_x; + Tmp[idx + Np] = phi_y; + Tmp[idx + 2 * Np] = phi_z; - /* double d = Averages->SDs(n); + /* double d = Averages->SDs(n); double dx = Averages->SDs_x(n); double dy = Averages->SDs_y(n); double dz = Averages->SDs_z(n); @@ -377,16 +403,16 @@ void ScaLBL_DFHModel::AssignSolidPotential(){ Tmp[idx+Np] = value*dy; Tmp[idx+2*Np] = value*dz; */ - } - } - } - } - ScaLBL_CopyToDevice(SolidPotential, Tmp, 3*sizeof(double)*Np); - ScaLBL_DeviceBarrier(); - delete [] Tmp; - delete [] Dst; + } + } + } + } + ScaLBL_CopyToDevice(SolidPotential, Tmp, 3 * sizeof(double) * Np); + ScaLBL_DeviceBarrier(); + delete[] Tmp; + delete[] Dst; - /* + /* DoubleArray Psx(Nx,Ny,Nz); DoubleArray Psy(Nx,Ny,Nz); DoubleArray Psz(Nx,Ny,Nz); @@ -403,226 +429,261 @@ void ScaLBL_DFHModel::AssignSolidPotential(){ fclose(PFILE); */ } -void ScaLBL_DFHModel::Initialize(){ - /* +void ScaLBL_DFHModel::Initialize() { + /* * This function initializes model */ - AssignSolidPotential(); - int rank=Dm->rank(); - double count_wet=0.f; - double count_wet_global; - double *PhaseLabel; - PhaseLabel=new double [Nx*Ny*Nz]; - for (int k=1; kid[n] == 1) - PhaseLabel[idx] = 1.0; - else { - PhaseLabel[idx] = -1.0; - count_wet+=1.f; - } - } - } - } - } - count_wet_global=Dm->Comm.sumReduce( count_wet); + AssignSolidPotential(); + int rank = Dm->rank(); + double count_wet = 0.f; + double count_wet_global; + double *PhaseLabel; + PhaseLabel = new double[Nx * Ny * Nz]; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + int idx = Map(i, j, k); + int n = k * Nx * Ny + j * Nx + i; + if (!(idx < 0)) { + if (Mask->id[n] == 1) + PhaseLabel[idx] = 1.0; + else { + PhaseLabel[idx] = -1.0; + count_wet += 1.f; + } + } + } + } + } + count_wet_global = Dm->Comm.sumReduce(count_wet); - if (rank==0) printf("Wetting phase volume fraction =%f \n",count_wet_global/double(Nx*Ny*Nz*nprocs)); - // initialize phi based on PhaseLabel (include solid component labels) - ScaLBL_CopyToDevice(Phi, PhaseLabel, Np*sizeof(double)); - //........................................................................... + if (rank == 0) + printf("Wetting phase volume fraction =%f \n", + count_wet_global / double(Nx * Ny * Nz * nprocs)); + // initialize phi based on PhaseLabel (include solid component labels) + ScaLBL_CopyToDevice(Phi, PhaseLabel, Np * sizeof(double)); + //........................................................................... - if (rank==0) printf ("Initializing distributions \n"); - ScaLBL_D3Q19_Init(fq, Np); + if (rank == 0) + printf("Initializing distributions \n"); + ScaLBL_D3Q19_Init(fq, Np); - if (Restart == true){ - if (rank==0){ - printf("Reading restart file! \n"); - ifstream restart("Restart.txt"); - if (restart.is_open()){ - restart >> timestep; - printf("Restarting from timestep =%i \n",timestep); - } - else{ - printf("WARNING:No Restart.txt file, setting timestep=0 \n"); - timestep=0; - } - } - //MPI_Bcast(×tep,1,MPI_INT,0,comm); - // Read in the restart file to CPU buffers - double *cPhi = new double[Np]; - double *cDist = new double[19*Np]; - ifstream File(LocalRestartFile,ios::binary); - double value; - for (int n=0; nLastExterior(), Np); - ScaLBL_DFH_Init(Phi, Den, Aq, Bq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + if (Restart == true) { + if (rank == 0) { + printf("Reading restart file! \n"); + ifstream restart("Restart.txt"); + if (restart.is_open()) { + restart >> timestep; + printf("Restarting from timestep =%i \n", timestep); + } else { + printf("WARNING:No Restart.txt file, setting timestep=0 \n"); + timestep = 0; + } + } + //MPI_Bcast(×tep,1,MPI_INT,0,comm); + // Read in the restart file to CPU buffers + double *cPhi = new double[Np]; + double *cDist = new double[19 * Np]; + ifstream File(LocalRestartFile, ios::binary); + double value; + for (int n = 0; n < Np; n++) { + File.read((char *)&value, sizeof(value)); + cPhi[n] = value; + // Read the distributions + for (int q = 0; q < 19; q++) { + File.read((char *)&value, sizeof(value)); + cDist[q * Np + n] = value; + } + } + File.close(); + // Copy the restart data to the GPU + ScaLBL_CopyToDevice(fq, cDist, 19 * Np * sizeof(double)); + ScaLBL_CopyToDevice(Phi, cPhi, Np * sizeof(double)); + ScaLBL_DeviceBarrier(); + delete[] cPhi; + delete[] cDist; + comm.barrier(); + } + if (rank == 0) + printf("Initializing phase field \n"); + ScaLBL_DFH_Init(Phi, Den, Aq, Bq, 0, ScaLBL_Comm->LastExterior(), Np); + ScaLBL_DFH_Init(Phi, Den, Aq, Bq, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); } -void ScaLBL_DFHModel::Run(){ - int nprocs=nprocx*nprocy*nprocz; - const RankInfoStruct rank_info(rank,nprocx,nprocy,nprocz); +void ScaLBL_DFHModel::Run() { + int nprocs = nprocx * nprocy * nprocz; + const RankInfoStruct rank_info(rank, nprocx, nprocy, nprocz); - if (rank==0) printf("********************************************************\n"); - if (rank==0) printf("No. of timesteps: %i \n", timestepMax); - ScaLBL_DeviceBarrier(); - comm.barrier(); - //************ MAIN ITERATION LOOP ***************************************/ + if (rank == 0) + printf("********************************************************\n"); + if (rank == 0) + printf("No. of timesteps: %i \n", timestepMax); + ScaLBL_DeviceBarrier(); + comm.barrier(); + //************ MAIN ITERATION LOOP ***************************************/ auto t1 = std::chrono::system_clock::now(); - bool Regular = true; - PROFILE_START("Loop"); - runAnalysis analysis( analysis_db, rank_info, ScaLBL_Comm, Dm, Np, Regular, Map ); - while (timestep < timestepMax ) { - //if ( rank==0 ) { printf("Running timestep %i (%i MB)\n",timestep+1,(int)(Utilities::getMemoryUsage()/1048576)); } - PROFILE_START("Update"); - // *************ODD TIMESTEP************* - timestep++; - // Compute the Phase indicator field - // Read for Aq, Bq happens in this routine (requires communication) - ScaLBL_Comm->BiSendD3Q7AA(Aq,Bq); //READ FROM NORMAL - ScaLBL_D3Q7_AAodd_DFH(NeighborList, Aq, Bq, Den, Phi, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->BiRecvD3Q7AA(Aq,Bq); //WRITE INTO OPPOSITE - ScaLBL_D3Q7_AAodd_DFH(NeighborList, Aq, Bq, Den, Phi, 0, ScaLBL_Comm->LastExterior(), Np); + bool Regular = true; + PROFILE_START("Loop"); + runAnalysis analysis(analysis_db, rank_info, ScaLBL_Comm, Dm, Np, Regular, + Map); + while (timestep < timestepMax) { + //if ( rank==0 ) { printf("Running timestep %i (%i MB)\n",timestep+1,(int)(Utilities::getMemoryUsage()/1048576)); } + PROFILE_START("Update"); + // *************ODD TIMESTEP************* + timestep++; + // Compute the Phase indicator field + // Read for Aq, Bq happens in this routine (requires communication) + ScaLBL_Comm->BiSendD3Q7AA(Aq, Bq); //READ FROM NORMAL + ScaLBL_D3Q7_AAodd_DFH(NeighborList, Aq, Bq, Den, Phi, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->BiRecvD3Q7AA(Aq, Bq); //WRITE INTO OPPOSITE + ScaLBL_D3Q7_AAodd_DFH(NeighborList, Aq, Bq, Den, Phi, 0, + ScaLBL_Comm->LastExterior(), Np); - // compute the gradient - ScaLBL_D3Q19_Gradient_DFH(NeighborList, Phi, Gradient, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->SendHalo(Phi); - ScaLBL_D3Q19_Gradient_DFH(NeighborList, Phi, Gradient, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->RecvGrad(Phi,Gradient); + // compute the gradient + ScaLBL_D3Q19_Gradient_DFH(NeighborList, Phi, Gradient, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->SendHalo(Phi); + ScaLBL_D3Q19_Gradient_DFH(NeighborList, Phi, Gradient, 0, + ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm->RecvGrad(Phi, Gradient); - // Perform the collision operation - ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL - ScaLBL_D3Q19_AAodd_DFH(NeighborList, fq, Aq, Bq, Den, Phi, Gradient, SolidPotential, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - // Set BCs - if (BoundaryCondition > 0){ - ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); - ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); - } - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - ScaLBL_D3Q19_AAodd_DFH(NeighborList, fq, Aq, Bq, Den, Phi, Gradient, SolidPotential, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_DeviceBarrier(); comm.barrier(); + // Perform the collision operation + ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL + ScaLBL_D3Q19_AAodd_DFH(NeighborList, fq, Aq, Bq, Den, Phi, Gradient, + SolidPotential, rhoA, rhoB, tauA, tauB, alpha, + beta, Fx, Fy, Fz, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + // Set BCs + if (BoundaryCondition > 0) { + ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); + ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); + } + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } + if (BoundaryCondition == 4) { + din = + ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } + ScaLBL_D3Q19_AAodd_DFH(NeighborList, fq, Aq, Bq, Den, Phi, Gradient, + SolidPotential, rhoA, rhoB, tauA, tauB, alpha, + beta, Fx, Fy, Fz, 0, ScaLBL_Comm->LastExterior(), + Np); + ScaLBL_DeviceBarrier(); + comm.barrier(); - // *************EVEN TIMESTEP************* - timestep++; - // Compute the Phase indicator field - ScaLBL_Comm->BiSendD3Q7AA(Aq,Bq); //READ FROM NORMAL - ScaLBL_D3Q7_AAeven_DFH(Aq, Bq, Den, Phi, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->BiRecvD3Q7AA(Aq,Bq); //WRITE INTO OPPOSITE - ScaLBL_D3Q7_AAeven_DFH(Aq, Bq, Den, Phi, 0, ScaLBL_Comm->LastExterior(), Np); + // *************EVEN TIMESTEP************* + timestep++; + // Compute the Phase indicator field + ScaLBL_Comm->BiSendD3Q7AA(Aq, Bq); //READ FROM NORMAL + ScaLBL_D3Q7_AAeven_DFH(Aq, Bq, Den, Phi, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->BiRecvD3Q7AA(Aq, Bq); //WRITE INTO OPPOSITE + ScaLBL_D3Q7_AAeven_DFH(Aq, Bq, Den, Phi, 0, ScaLBL_Comm->LastExterior(), + Np); - // compute the gradient - ScaLBL_D3Q19_Gradient_DFH(NeighborList, Phi, Gradient, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->SendHalo(Phi); - ScaLBL_D3Q19_Gradient_DFH(NeighborList, Phi, Gradient, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->RecvGrad(Phi,Gradient); + // compute the gradient + ScaLBL_D3Q19_Gradient_DFH(NeighborList, Phi, Gradient, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->SendHalo(Phi); + ScaLBL_D3Q19_Gradient_DFH(NeighborList, Phi, Gradient, 0, + ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm->RecvGrad(Phi, Gradient); - // Perform the collision operation - ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL - ScaLBL_D3Q19_AAeven_DFH(NeighborList, fq, Aq, Bq, Den, Phi, Gradient, SolidPotential, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - // Set boundary conditions - if (BoundaryCondition > 0){ - ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); - ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); - } - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - ScaLBL_D3Q19_AAeven_DFH(NeighborList, fq, Aq, Bq, Den, Phi, Gradient, SolidPotential, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_DeviceBarrier(); comm.barrier(); - //************************************************************************ - comm.barrier(); - PROFILE_STOP("Update"); + // Perform the collision operation + ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL + ScaLBL_D3Q19_AAeven_DFH(NeighborList, fq, Aq, Bq, Den, Phi, Gradient, + SolidPotential, rhoA, rhoB, tauA, tauB, alpha, + beta, Fx, Fy, Fz, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + // Set boundary conditions + if (BoundaryCondition > 0) { + ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); + ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); + } + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 4) { + din = + ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } + ScaLBL_D3Q19_AAeven_DFH(NeighborList, fq, Aq, Bq, Den, Phi, Gradient, + SolidPotential, rhoA, rhoB, tauA, tauB, alpha, + beta, Fx, Fy, Fz, 0, + ScaLBL_Comm->LastExterior(), Np); + ScaLBL_DeviceBarrier(); + comm.barrier(); + //************************************************************************ + comm.barrier(); + PROFILE_STOP("Update"); - // Run the analysis - analysis.run(timestep, analysis_db, *Averages, Phi, Pressure, Velocity, fq, Den ); - } - analysis.finish(); - PROFILE_STOP("Loop"); - PROFILE_SAVE("lbpm_color_simulator",1); - //************************************************************************ - ScaLBL_DeviceBarrier(); - comm.barrier(); - if (rank==0) printf("-------------------------------------------------------------------\n"); - // Compute the walltime per timestep + // Run the analysis + analysis.run(timestep, analysis_db, *Averages, Phi, Pressure, Velocity, + fq, Den); + } + analysis.finish(); + PROFILE_STOP("Loop"); + PROFILE_SAVE("lbpm_color_simulator", 1); + //************************************************************************ + ScaLBL_DeviceBarrier(); + comm.barrier(); + if (rank == 0) + printf("---------------------------------------------------------------" + "----\n"); + // Compute the walltime per timestep auto t2 = std::chrono::system_clock::now(); - double cputime = std::chrono::duration( t2 - t1 ).count() / timestep; - // Performance obtained from each node - double MLUPS = double(Np)/cputime/1000000; - if (rank==0) printf("********************************************************\n"); - if (rank==0) printf("CPU time = %f \n", cputime); - if (rank==0) printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); - MLUPS *= nprocs; - if (rank==0) printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); - if (rank==0) printf("********************************************************\n"); + double cputime = std::chrono::duration(t2 - t1).count() / timestep; + // Performance obtained from each node + double MLUPS = double(Np) / cputime / 1000000; + if (rank == 0) + printf("********************************************************\n"); + if (rank == 0) + printf("CPU time = %f \n", cputime); + if (rank == 0) + printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); + MLUPS *= nprocs; + if (rank == 0) + printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); + if (rank == 0) + printf("********************************************************\n"); - // ************************************************************************ + // ************************************************************************ } -void ScaLBL_DFHModel::WriteDebug(){ - // Copy back final phase indicator field and convert to regular layout - DoubleArray PhaseField(Nx,Ny,Nz); - ScaLBL_Comm->RegularLayout(Map,Phi,PhaseField); - FILE *OUTFILE; - sprintf(LocalRankFilename,"Phase.%05i.raw",rank); - OUTFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,OUTFILE); - fclose(OUTFILE); +void ScaLBL_DFHModel::WriteDebug() { + // Copy back final phase indicator field and convert to regular layout + DoubleArray PhaseField(Nx, Ny, Nz); + ScaLBL_Comm->RegularLayout(Map, Phi, PhaseField); + FILE *OUTFILE; + sprintf(LocalRankFilename, "Phase.%05i.raw", rank); + OUTFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, OUTFILE); + fclose(OUTFILE); - ScaLBL_Comm->RegularLayout(Map,&Den[0],PhaseField); - FILE *AFILE; - sprintf(LocalRankFilename,"A.%05i.raw",rank); - AFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,AFILE); - fclose(AFILE); - - ScaLBL_Comm->RegularLayout(Map,&Den[Np],PhaseField); - FILE *BFILE; - sprintf(LocalRankFilename,"B.%05i.raw",rank); - BFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,BFILE); - fclose(BFILE); + ScaLBL_Comm->RegularLayout(Map, &Den[0], PhaseField); + FILE *AFILE; + sprintf(LocalRankFilename, "A.%05i.raw", rank); + AFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, AFILE); + fclose(AFILE); + ScaLBL_Comm->RegularLayout(Map, &Den[Np], PhaseField); + FILE *BFILE; + sprintf(LocalRankFilename, "B.%05i.raw", rank); + BFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, BFILE); + fclose(BFILE); } diff --git a/models/DFHModel.h b/models/DFHModel.h index 00e6e6b3..d9509568 100644 --- a/models/DFHModel.h +++ b/models/DFHModel.h @@ -16,38 +16,38 @@ Implementation of color lattice boltzmann model #include "ProfilerApp.h" #include "threadpool/thread_pool.h" -class ScaLBL_DFHModel{ +class ScaLBL_DFHModel { public: - ScaLBL_DFHModel(int RANK, int NP, const Utilities::MPI& COMM); - ~ScaLBL_DFHModel(); - - // functions in they should be run - void ReadParams(string filename); - void ReadParams(std::shared_ptr db0); - void SetDomain(); - void ReadInput(); - void Create(); - void Initialize(); - void AssignSolidPotential(); - void Run(); - void WriteDebug(); - - bool Restart,pBC; - int timestep,timestepMax; - int BoundaryCondition; - double tauA,tauB,rhoA,rhoB,alpha,beta; - double Fx,Fy,Fz,flux; - double din,dout,inletA,inletB,outletA,outletB; - - int Nx,Ny,Nz,N,Np; - int rank,nprocx,nprocy,nprocz,nprocs; - double Lx,Ly,Lz; + ScaLBL_DFHModel(int RANK, int NP, const Utilities::MPI &COMM); + ~ScaLBL_DFHModel(); - std::shared_ptr Dm; // this domain is for analysis - std::shared_ptr Mask; // this domain is for lbm - std::shared_ptr ScaLBL_Comm; + // functions in they should be run + void ReadParams(string filename); + void ReadParams(std::shared_ptr db0); + void SetDomain(); + void ReadInput(); + void Create(); + void Initialize(); + void AssignSolidPotential(); + void Run(); + void WriteDebug(); + + bool Restart, pBC; + int timestep, timestepMax; + int BoundaryCondition; + double tauA, tauB, rhoA, rhoB, alpha, beta; + double Fx, Fy, Fz, flux; + double din, dout, inletA, inletB, outletA, outletB; + + int Nx, Ny, Nz, N, Np; + int rank, nprocx, nprocy, nprocz, nprocs; + double Lx, Ly, Lz; + + std::shared_ptr Dm; // this domain is for analysis + std::shared_ptr Mask; // this domain is for lbm + std::shared_ptr ScaLBL_Comm; std::shared_ptr Averages; - + // input database std::shared_ptr db; std::shared_ptr domain_db; @@ -64,20 +64,18 @@ public: double *Velocity; double *Gradient; double *Pressure; - + private: - Utilities::MPI comm; - - int dist_mem_size; - int neighborSize; - // filenames + Utilities::MPI comm; + + int dist_mem_size; + int neighborSize; + // filenames char LocalRankString[8]; char LocalRankFilename[40]; char LocalRestartFile[40]; - + //int rank,nprocs; void LoadParams(std::shared_ptr db0); void AssignComponentLabels(double *phase); - }; - diff --git a/models/FreeLeeModel.cpp b/models/FreeLeeModel.cpp index 19a2afa8..380d6cbc 100644 --- a/models/FreeLeeModel.cpp +++ b/models/FreeLeeModel.cpp @@ -9,971 +9,1087 @@ color lattice boltzmann model #include #include -ScaLBL_FreeLeeModel::ScaLBL_FreeLeeModel(int RANK, int NP, const Utilities::MPI& COMM): -rank(RANK), nprocs(NP), Restart(0),timestep(0),timestepMax(2),tauA(1.0),tauB(1.0),tauM(1.0),rhoA(1.0),rhoB(1.0),W(5.0),gamma(0.001),kappa(0.0075),beta(0.0024), -Fx(0),Fy(0),Fz(0),flux(0),din(0),dout(0),inletA(0),inletB(0),outletA(0),outletB(0), -tau(1.0),rho0(1.0), -Nx(0),Ny(0),Nz(0),N(0),Np(0),nprocx(0),nprocy(0),nprocz(0),BoundaryCondition(0),Lx(0),Ly(0),Lz(0),comm(COMM) -{ - -} -ScaLBL_FreeLeeModel::~ScaLBL_FreeLeeModel(){ +ScaLBL_FreeLeeModel::ScaLBL_FreeLeeModel(int RANK, int NP, + const Utilities::MPI &COMM) + : rank(RANK), nprocs(NP), Restart(0), timestep(0), timestepMax(2), + tauA(1.0), tauB(1.0), tauM(1.0), rhoA(1.0), rhoB(1.0), W(5.0), + gamma(0.001), kappa(0.0075), beta(0.0024), Fx(0), Fy(0), Fz(0), flux(0), + din(0), dout(0), inletA(0), inletB(0), outletA(0), outletB(0), tau(1.0), + rho0(1.0), Nx(0), Ny(0), Nz(0), N(0), Np(0), nprocx(0), nprocy(0), + nprocz(0), BoundaryCondition(0), Lx(0), Ly(0), Lz(0), comm(COMM) {} +ScaLBL_FreeLeeModel::~ScaLBL_FreeLeeModel() {} +void ScaLBL_FreeLeeModel::getPhase(DoubleArray &PhaseValues) { + + DoubleArray PhaseWideHalo(Nxh, Nyh, Nzh); + ScaLBL_CopyToHost(PhaseWideHalo.data(), Phi, sizeof(double) * Nh); + + // use halo width = 1 for analysis data + for (int k = 1; k < Nzh - 1; k++) { + for (int j = 1; j < Nyh - 1; j++) { + for (int i = 1; i < Nxh - 1; i++) { + PhaseValues(i - 1, j - 1, k - 1) = PhaseWideHalo(i, j, k); + } + } + } } +void ScaLBL_FreeLeeModel::getPotential(DoubleArray &PressureValues, + DoubleArray &MuValues) { -void ScaLBL_FreeLeeModel::getPhase(DoubleArray &PhaseValues){ - - DoubleArray PhaseWideHalo(Nxh,Nyh,Nzh); - ScaLBL_CopyToHost(PhaseWideHalo.data(), Phi, sizeof(double)*Nh); - - // use halo width = 1 for analysis data - for (int k=1; kRegularLayout(Map, Pressure, PressureValues); + ScaLBL_Comm->Barrier(); + comm.barrier(); + + ScaLBL_Comm->RegularLayout(Map, mu_phi, MuValues); + ScaLBL_Comm->Barrier(); + comm.barrier(); } -void ScaLBL_FreeLeeModel::getPotential(DoubleArray &PressureValues, DoubleArray &MuValues){ - - ScaLBL_Comm->RegularLayout(Map,Pressure,PressureValues); - ScaLBL_Comm->Barrier(); comm.barrier(); +void ScaLBL_FreeLeeModel::getVelocity(DoubleArray &Vel_x, DoubleArray &Vel_y, + DoubleArray &Vel_z) { - ScaLBL_Comm->RegularLayout(Map,mu_phi,MuValues); - ScaLBL_Comm->Barrier(); comm.barrier(); + ScaLBL_Comm->RegularLayout(Map, &Velocity[0], Vel_x); + ScaLBL_Comm->Barrier(); + comm.barrier(); + ScaLBL_Comm->RegularLayout(Map, &Velocity[Np], Vel_y); + ScaLBL_Comm->Barrier(); + comm.barrier(); + + ScaLBL_Comm->RegularLayout(Map, &Velocity[2 * Np], Vel_z); + ScaLBL_Comm->Barrier(); + comm.barrier(); } -void ScaLBL_FreeLeeModel::getVelocity(DoubleArray &Vel_x, DoubleArray &Vel_y, DoubleArray &Vel_z){ - - ScaLBL_Comm->RegularLayout(Map,&Velocity[0],Vel_x); - ScaLBL_Comm->Barrier(); comm.barrier(); - - ScaLBL_Comm->RegularLayout(Map,&Velocity[Np],Vel_y); - ScaLBL_Comm->Barrier(); comm.barrier(); - - ScaLBL_Comm->RegularLayout(Map,&Velocity[2*Np],Vel_z); - ScaLBL_Comm->Barrier(); comm.barrier(); -} - -void ScaLBL_FreeLeeModel::getData_RegularLayout(const double *data, DoubleArray ®data){ - // Gets data (in optimized layout) from the HOST and stores in regular layout +void ScaLBL_FreeLeeModel::getData_RegularLayout(const double *data, + DoubleArray ®data) { + // Gets data (in optimized layout) from the HOST and stores in regular layout // Primarly for debugging - int i,j,k,idx; + int i, j, k, idx; - // initialize the array - regdata.fill(0.f); - - double value; - for (k=0; k( filename ); - domain_db = db->getDatabase( "Domain" ); - freelee_db = db->getDatabase( "FreeLee" ); - analysis_db = db->getDatabase( "Analysis" ); - vis_db = db->getDatabase( "Visualization" ); +void ScaLBL_FreeLeeModel::ReadParams(string filename) { + // read the input database + db = std::make_shared(filename); + domain_db = db->getDatabase("Domain"); + freelee_db = db->getDatabase("FreeLee"); + analysis_db = db->getDatabase("Analysis"); + vis_db = db->getDatabase("Visualization"); - // set defaults - timestepMax = 100000; - tauA = tauB = 1.0; - tauM = 1.0;//relaxation time for phase field - rhoA = rhoB = 1.0; - tau = 1.0;//only for single-fluid Lee model - rho0 = 1.0;//only for single-fluid Lee model - Fx = Fy = Fz = 0.0; - gamma=1e-3;//surface tension - W=5.0;//interfacial thickness + // set defaults + timestepMax = 100000; + tauA = tauB = 1.0; + tauM = 1.0; //relaxation time for phase field + rhoA = rhoB = 1.0; + tau = 1.0; //only for single-fluid Lee model + rho0 = 1.0; //only for single-fluid Lee model + Fx = Fy = Fz = 0.0; + gamma = 1e-3; //surface tension + W = 5.0; //interfacial thickness //beta = 12.0*gamma/W; //kappa = 3.0*gamma*W/2.0;//beta and kappa are related to surface tension \gamma - beta = 0.75*gamma/W; - kappa = 0.375*gamma*W;//beta and kappa are related to surface tension \gamma - Restart=false; - din=dout=1.0; - flux=0.0; - - // Color Model parameters - if (freelee_db->keyExists( "timestepMax" )){ - timestepMax = freelee_db->getScalar( "timestepMax" ); - } - if (freelee_db->keyExists( "tau" )){//only for single-fluid Lee model - tau = freelee_db->getScalar( "tau" ); - } - if (freelee_db->keyExists( "tauA" )){ - tauA = freelee_db->getScalar( "tauA" ); - } - if (freelee_db->keyExists( "tauB" )){ - tauB = freelee_db->getScalar( "tauB" ); - } - if (freelee_db->keyExists( "tauM" )){ - tauM = freelee_db->getScalar( "tauM" ); - } - if (freelee_db->keyExists( "rho0" )){ - rho0 = freelee_db->getScalar( "rho0" ); - } - if (freelee_db->keyExists( "rhoA" )){ - rhoA = freelee_db->getScalar( "rhoA" ); - } - if (freelee_db->keyExists( "rhoB" )){ - rhoB = freelee_db->getScalar( "rhoB" ); - } - if (freelee_db->keyExists( "F" )){ - Fx = freelee_db->getVector( "F" )[0]; - Fy = freelee_db->getVector( "F" )[1]; - Fz = freelee_db->getVector( "F" )[2]; - } - if (freelee_db->keyExists( "gamma" )){ - gamma = freelee_db->getScalar( "gamma" ); - } - if (freelee_db->keyExists( "W" )){ - W = freelee_db->getScalar( "W" ); - } - if (freelee_db->keyExists( "Restart" )){ - Restart = freelee_db->getScalar( "Restart" ); - } - if (freelee_db->keyExists( "din" )){ - din = freelee_db->getScalar( "din" ); - } - if (freelee_db->keyExists( "dout" )){ - dout = freelee_db->getScalar( "dout" ); - } - if (freelee_db->keyExists( "flux" )){ - flux = freelee_db->getScalar( "flux" ); - } - inletA=1.f; - inletB=0.f; - outletA=0.f; - outletB=1.f; + beta = 0.75 * gamma / W; + kappa = 0.375 * gamma * + W; //beta and kappa are related to surface tension \gamma + Restart = false; + din = dout = 1.0; + flux = 0.0; + + // Color Model parameters + if (freelee_db->keyExists("timestepMax")) { + timestepMax = freelee_db->getScalar("timestepMax"); + } + if (freelee_db->keyExists("tau")) { //only for single-fluid Lee model + tau = freelee_db->getScalar("tau"); + } + if (freelee_db->keyExists("tauA")) { + tauA = freelee_db->getScalar("tauA"); + } + if (freelee_db->keyExists("tauB")) { + tauB = freelee_db->getScalar("tauB"); + } + if (freelee_db->keyExists("tauM")) { + tauM = freelee_db->getScalar("tauM"); + } + if (freelee_db->keyExists("rho0")) { + rho0 = freelee_db->getScalar("rho0"); + } + if (freelee_db->keyExists("rhoA")) { + rhoA = freelee_db->getScalar("rhoA"); + } + if (freelee_db->keyExists("rhoB")) { + rhoB = freelee_db->getScalar("rhoB"); + } + if (freelee_db->keyExists("F")) { + Fx = freelee_db->getVector("F")[0]; + Fy = freelee_db->getVector("F")[1]; + Fz = freelee_db->getVector("F")[2]; + } + if (freelee_db->keyExists("gamma")) { + gamma = freelee_db->getScalar("gamma"); + } + if (freelee_db->keyExists("W")) { + W = freelee_db->getScalar("W"); + } + if (freelee_db->keyExists("Restart")) { + Restart = freelee_db->getScalar("Restart"); + } + if (freelee_db->keyExists("din")) { + din = freelee_db->getScalar("din"); + } + if (freelee_db->keyExists("dout")) { + dout = freelee_db->getScalar("dout"); + } + if (freelee_db->keyExists("flux")) { + flux = freelee_db->getScalar("flux"); + } + inletA = 1.f; + inletB = 0.f; + outletA = 0.f; + outletB = 1.f; //update secondary parameters //beta = 12.0*gamma/W; //kappa = 3.0*gamma*W/2.0;//beta and kappa are related to surface tension \gamma - beta = 0.75*gamma/W; - kappa = 0.375*gamma*W;//beta and kappa are related to surface tension \gamma - //if (BoundaryCondition==4) flux *= rhoA; // mass flux must adjust for density (see formulation for details) + beta = 0.75 * gamma / W; + kappa = 0.375 * gamma * + W; //beta and kappa are related to surface tension \gamma + //if (BoundaryCondition==4) flux *= rhoA; // mass flux must adjust for density (see formulation for details) - BoundaryCondition = 0; - if (domain_db->keyExists( "BC" )){ - BoundaryCondition = domain_db->getScalar( "BC" ); - } + BoundaryCondition = 0; + if (domain_db->keyExists("BC")) { + BoundaryCondition = domain_db->getScalar("BC"); + } } -void ScaLBL_FreeLeeModel::SetDomain(){ - Dm = std::shared_ptr(new Domain(domain_db,comm)); // full domain for analysis - Mask = std::shared_ptr(new Domain(domain_db,comm)); // mask domain removes immobile phases - // domain parameters - Nx = Dm->Nx; - Ny = Dm->Ny; - Nz = Dm->Nz; - Lx = Dm->Lx; - Ly = Dm->Ly; - Lz = Dm->Lz; - N = Nx*Ny*Nz; - Nxh = Nx+2; - Nyh = Ny+2; - Nzh = Nz+2; - Nh = Nxh*Nyh*Nzh; - id = new signed char [N]; - for (int i=0; iid[i] = 1; // initialize this way +void ScaLBL_FreeLeeModel::SetDomain() { + Dm = std::shared_ptr( + new Domain(domain_db, comm)); // full domain for analysis + Mask = std::shared_ptr( + new Domain(domain_db, comm)); // mask domain removes immobile phases + // domain parameters + Nx = Dm->Nx; + Ny = Dm->Ny; + Nz = Dm->Nz; + Lx = Dm->Lx; + Ly = Dm->Ly; + Lz = Dm->Lz; + N = Nx * Ny * Nz; + Nxh = Nx + 2; + Nyh = Ny + 2; + Nzh = Nz + 2; + Nh = Nxh * Nyh * Nzh; + id = new signed char[N]; + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = 1; // initialize this way - comm.barrier(); - Dm->CommInit(); - comm.barrier(); - // Read domain parameters - rank = Dm->rank(); - nprocx = Dm->nprocx(); - nprocy = Dm->nprocy(); - nprocz = Dm->nprocz(); + comm.barrier(); + Dm->CommInit(); + comm.barrier(); + // Read domain parameters + rank = Dm->rank(); + nprocx = Dm->nprocx(); + nprocy = Dm->nprocy(); + nprocz = Dm->nprocz(); } -void ScaLBL_FreeLeeModel::ReadInput(){ - - sprintf(LocalRankString,"%05d",rank); - sprintf(LocalRankFilename,"%s%s","ID.",LocalRankString); - sprintf(LocalRestartFile,"%s%s","Restart.",LocalRankString); - - if (freelee_db->keyExists( "image_sequence" )){ - auto ImageList = freelee_db->getVector( "image_sequence"); - int IMAGE_INDEX = freelee_db->getWithDefault( "image_index", 0 ); - std::string first_image = ImageList[IMAGE_INDEX]; - Mask->Decomp(first_image); - IMAGE_INDEX++; - } - else if (domain_db->keyExists( "GridFile" )){ +void ScaLBL_FreeLeeModel::ReadInput() { + + sprintf(LocalRankString, "%05d", rank); + sprintf(LocalRankFilename, "%s%s", "ID.", LocalRankString); + sprintf(LocalRestartFile, "%s%s", "Restart.", LocalRankString); + + if (freelee_db->keyExists("image_sequence")) { + auto ImageList = freelee_db->getVector("image_sequence"); + int IMAGE_INDEX = freelee_db->getWithDefault("image_index", 0); + std::string first_image = ImageList[IMAGE_INDEX]; + Mask->Decomp(first_image); + IMAGE_INDEX++; + } else if (domain_db->keyExists("GridFile")) { // Read the local domain data - auto input_id = readMicroCT( *domain_db, MPI_COMM_WORLD ); + auto input_id = readMicroCT(*domain_db, MPI_COMM_WORLD); // Fill the halo (assuming GCW of 1) - array size0 = { (int) input_id.size(0), (int) input_id.size(1), (int) input_id.size(2) }; - ArraySize size1 = { (size_t) Mask->Nx, (size_t) Mask->Ny, (size_t) Mask->Nz }; - ASSERT( (int) size1[0] == size0[0]+2 && (int) size1[1] == size0[1]+2 && (int) size1[2] == size0[2]+2 ); - fillHalo fill( MPI_COMM_WORLD, Mask->rank_info, size0, { 1, 1, 1 }, 0, 1 ); + array size0 = {(int)input_id.size(0), (int)input_id.size(1), + (int)input_id.size(2)}; + ArraySize size1 = {(size_t)Mask->Nx, (size_t)Mask->Ny, + (size_t)Mask->Nz}; + ASSERT((int)size1[0] == size0[0] + 2 && (int)size1[1] == size0[1] + 2 && + (int)size1[2] == size0[2] + 2); + fillHalo fill(MPI_COMM_WORLD, Mask->rank_info, size0, + {1, 1, 1}, 0, 1); Array id_view; - id_view.viewRaw( size1, Mask->id.data() ); - fill.copy( input_id, id_view ); - fill.fill( id_view ); - } - else if (domain_db->keyExists( "Filename" )){ - auto Filename = domain_db->getScalar( "Filename" ); - Mask->Decomp(Filename); - } - else{ - Mask->ReadIDs(); - } - for (int i=0; iid[i]; // save what was read - - // Generate the signed distance map - // Initialize the domain and communication - Array id_solid(Nx,Ny,Nz); - // Solve for the position of the solid phase - for (int k=0;kid[n]; - if (label > 0) id_solid(i,j,k) = 1; - else id_solid(i,j,k) = 0; - } - } - } - SignDist.resize(Nx,Ny,Nz); - // Initialize the signed distance function - for (int k=0;kid.data()); + fill.copy(input_id, id_view); + fill.fill(id_view); + } else if (domain_db->keyExists("Filename")) { + auto Filename = domain_db->getScalar("Filename"); + Mask->Decomp(Filename); + } else { + Mask->ReadIDs(); + } + for (int i = 0; i < Nx * Ny * Nz; i++) + id[i] = Mask->id[i]; // save what was read + // Generate the signed distance map + // Initialize the domain and communication + Array id_solid(Nx, Ny, Nz); + // Solve for the position of the solid phase + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + // Initialize the solid phase + signed char label = Mask->id[n]; + if (label > 0) + id_solid(i, j, k) = 1; + else + id_solid(i, j, k) = 0; + } + } + } + SignDist.resize(Nx, Ny, Nz); + // Initialize the signed distance function + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + // Initialize distance to +/- 1 + SignDist(i, j, k) = 2.0 * double(id_solid(i, j, k)) - 1.0; + } + } + } + if (rank == 0) + printf("Initialized solid phase -- Converting to Signed Distance " + "function \n"); + CalcDist(SignDist, id_solid, *Mask); + + if (rank == 0) + cout << "Domain set." << endl; } -void ScaLBL_FreeLeeModel::Create_TwoFluid(){ - /* +void ScaLBL_FreeLeeModel::Create_TwoFluid() { + /* * This function creates the variables needed to run two-fluid Lee model */ - //......................................................... - // Initialize communication structures in averaging domain - for (int i=0; iid[i] = Mask->id[i]; - Mask->CommInit(); - Np=Mask->PoreCount(); - //........................................................................... - if (rank==0) printf ("Create ScaLBL_Communicator \n"); - // Create a communicator for the device (will use optimized layout) - // ScaLBL_Communicator ScaLBL_Comm(Mask); // original - ScaLBL_Comm = std::shared_ptr(new ScaLBL_Communicator(Mask)); - //ScaLBL_Comm_Regular = std::shared_ptr(new ScaLBL_Communicator(Mask)); - ScaLBL_Comm_WideHalo = std::shared_ptr(new ScaLBLWideHalo_Communicator(Mask,2)); + //......................................................... + // Initialize communication structures in averaging domain + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = Mask->id[i]; + Mask->CommInit(); + Np = Mask->PoreCount(); + //........................................................................... + if (rank == 0) + printf("Create ScaLBL_Communicator \n"); + // Create a communicator for the device (will use optimized layout) + // ScaLBL_Communicator ScaLBL_Comm(Mask); // original + ScaLBL_Comm = + std::shared_ptr(new ScaLBL_Communicator(Mask)); + //ScaLBL_Comm_Regular = std::shared_ptr(new ScaLBL_Communicator(Mask)); + ScaLBL_Comm_WideHalo = std::shared_ptr( + new ScaLBLWideHalo_Communicator(Mask, 2)); - // create the layout for the LBM - int Npad=(Np/16 + 2)*16; - if (rank==0) printf ("Set up memory efficient layout, %i | %i | %i \n", Np, Npad, N); - Map.resize(Nx,Ny,Nz); Map.fill(-2); - auto neighborList= new int[18*Npad]; - Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map,neighborList,Mask->id.data(),Np,2); - comm.barrier(); + // create the layout for the LBM + int Npad = (Np / 16 + 2) * 16; + if (rank == 0) + printf("Set up memory efficient layout, %i | %i | %i \n", Np, Npad, N); + Map.resize(Nx, Ny, Nz); + Map.fill(-2); + auto neighborList = new int[18 * Npad]; + Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map, neighborList, + Mask->id.data(), Np, 2); + comm.barrier(); - //........................................................................... - // MAIN VARIABLES ALLOCATED HERE - //........................................................................... - // LBM variables - if (rank==0) printf ("Allocating distributions \n"); - //......................device distributions................................. - dist_mem_size = Np*sizeof(double); - neighborSize=18*(Np*sizeof(int)); - //........................................................................... - ScaLBL_AllocateDeviceMemory((void **) &NeighborList, neighborSize); - ScaLBL_AllocateDeviceMemory((void **) &dvcMap, sizeof(int)*Np); - ScaLBL_AllocateDeviceMemory((void **) &gqbar, 19*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &hq, 7*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &mu_phi, dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Den, dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Phi, sizeof(double)*Nh); - ScaLBL_AllocateDeviceMemory((void **) &Pressure, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &Velocity, 3*sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &ColorGrad, 3*sizeof(double)*Np); - //........................................................................... - // Update GPU data structures - if (rank==0) printf ("Setting up device map and neighbor list \n"); - fflush(stdout); - int *TmpMap; - TmpMap=new int[Np]; - for (int k=1; kMap(i,j,k); - } - } - } - // check that TmpMap is valid - for (int idx=0; idxLastExterior(); idx++){ - auto n = TmpMap[idx]; - if (n > Nxh*Nyh*Nzh){ - printf("Bad value! idx=%i \n", n); - TmpMap[idx] = Nxh*Nyh*Nzh-1; - } - } - for (int idx=ScaLBL_Comm->FirstInterior(); idxLastInterior(); idx++){ - auto n = TmpMap[idx]; - if ( n > Nxh*Nyh*Nzh ){ - printf("Bad value! idx=%i \n",n); - TmpMap[idx] = Nxh*Nyh*Nzh-1; - } - } + //........................................................................... + // MAIN VARIABLES ALLOCATED HERE + //........................................................................... + // LBM variables + if (rank == 0) + printf("Allocating distributions \n"); + //......................device distributions................................. + dist_mem_size = Np * sizeof(double); + neighborSize = 18 * (Np * sizeof(int)); + //........................................................................... + ScaLBL_AllocateDeviceMemory((void **)&NeighborList, neighborSize); + ScaLBL_AllocateDeviceMemory((void **)&dvcMap, sizeof(int) * Np); + ScaLBL_AllocateDeviceMemory((void **)&gqbar, 19 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&hq, 7 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&mu_phi, dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Den, dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Phi, sizeof(double) * Nh); + ScaLBL_AllocateDeviceMemory((void **)&Pressure, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&Velocity, 3 * sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&ColorGrad, 3 * sizeof(double) * Np); + //........................................................................... + // Update GPU data structures + if (rank == 0) + printf("Setting up device map and neighbor list \n"); + fflush(stdout); + int *TmpMap; + TmpMap = new int[Np]; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + int idx = Map(i, j, k); + if (!(idx < 0)) + TmpMap[idx] = ScaLBL_Comm_WideHalo->Map(i, j, k); + } + } + } + // check that TmpMap is valid + for (int idx = 0; idx < ScaLBL_Comm->LastExterior(); idx++) { + auto n = TmpMap[idx]; + if (n > Nxh * Nyh * Nzh) { + printf("Bad value! idx=%i \n", n); + TmpMap[idx] = Nxh * Nyh * Nzh - 1; + } + } + for (int idx = ScaLBL_Comm->FirstInterior(); + idx < ScaLBL_Comm->LastInterior(); idx++) { + auto n = TmpMap[idx]; + if (n > Nxh * Nyh * Nzh) { + printf("Bad value! idx=%i \n", n); + TmpMap[idx] = Nxh * Nyh * Nzh - 1; + } + } // copy the device map - ScaLBL_CopyToDevice(dvcMap, TmpMap, sizeof(int)*Np); - // copy the neighbor list - ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); - comm.barrier(); - delete [] TmpMap; - delete [] neighborList; -} + ScaLBL_CopyToDevice(dvcMap, TmpMap, sizeof(int) * Np); + // copy the neighbor list + ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); + comm.barrier(); + delete[] TmpMap; + delete[] neighborList; +} -void ScaLBL_FreeLeeModel::Create_SingleFluid(){ - /* +void ScaLBL_FreeLeeModel::Create_SingleFluid() { + /* * This function creates the variables needed to run single-fluid Lee model */ - //......................................................... - // Initialize communication structures in averaging domain - for (int i=0; iid[i] = Mask->id[i]; - Mask->CommInit(); - Np=Mask->PoreCount(); - //........................................................................... - if (rank==0) printf ("Create ScaLBL_Communicator \n"); - // Create a communicator for the device (will use optimized layout) - // ScaLBL_Communicator ScaLBL_Comm(Mask); // original - ScaLBL_Comm = std::shared_ptr(new ScaLBL_Communicator(Mask)); + //......................................................... + // Initialize communication structures in averaging domain + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = Mask->id[i]; + Mask->CommInit(); + Np = Mask->PoreCount(); + //........................................................................... + if (rank == 0) + printf("Create ScaLBL_Communicator \n"); + // Create a communicator for the device (will use optimized layout) + // ScaLBL_Communicator ScaLBL_Comm(Mask); // original + ScaLBL_Comm = + std::shared_ptr(new ScaLBL_Communicator(Mask)); - // create the layout for the LBM - int Npad=(Np/16 + 2)*16; - if (rank==0) printf ("Set up memory efficient layout, %i | %i | %i \n", Np, Npad, N); - Map.resize(Nx,Ny,Nz); Map.fill(-2); - auto neighborList= new int[18*Npad]; - Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map,neighborList,Mask->id.data(),Np,1); - comm.barrier(); + // create the layout for the LBM + int Npad = (Np / 16 + 2) * 16; + if (rank == 0) + printf("Set up memory efficient layout, %i | %i | %i \n", Np, Npad, N); + Map.resize(Nx, Ny, Nz); + Map.fill(-2); + auto neighborList = new int[18 * Npad]; + Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map, neighborList, + Mask->id.data(), Np, 1); + comm.barrier(); - //........................................................................... - // MAIN VARIABLES ALLOCATED HERE - //........................................................................... - // LBM variables - if (rank==0) printf ("Allocating distributions \n"); - //......................device distributions................................. - dist_mem_size = Np*sizeof(double); - neighborSize=18*(Np*sizeof(int)); - //........................................................................... - ScaLBL_AllocateDeviceMemory((void **) &NeighborList, neighborSize); - ScaLBL_AllocateDeviceMemory((void **) &gqbar, 19*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Pressure, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &Velocity, 3*sizeof(double)*Np); - //........................................................................... - // Update GPU data structures - if (rank==0) printf ("Setting up device map and neighbor list \n"); - // copy the neighbor list - ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); - comm.barrier(); - delete [] neighborList; -} + //........................................................................... + // MAIN VARIABLES ALLOCATED HERE + //........................................................................... + // LBM variables + if (rank == 0) + printf("Allocating distributions \n"); + //......................device distributions................................. + dist_mem_size = Np * sizeof(double); + neighborSize = 18 * (Np * sizeof(int)); + //........................................................................... + ScaLBL_AllocateDeviceMemory((void **)&NeighborList, neighborSize); + ScaLBL_AllocateDeviceMemory((void **)&gqbar, 19 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Pressure, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&Velocity, 3 * sizeof(double) * Np); + //........................................................................... + // Update GPU data structures + if (rank == 0) + printf("Setting up device map and neighbor list \n"); + // copy the neighbor list + ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); + comm.barrier(); + delete[] neighborList; +} -void ScaLBL_FreeLeeModel::AssignComponentLabels_ChemPotential_ColorGrad() -{ - double *phase; - phase = new double[Nh]; +void ScaLBL_FreeLeeModel::AssignComponentLabels_ChemPotential_ColorGrad() { + double *phase; + phase = new double[Nh]; - size_t NLABELS=0; - signed char VALUE=0; - double AFFINITY=0.f; + size_t NLABELS = 0; + signed char VALUE = 0; + double AFFINITY = 0.f; - auto LabelList = freelee_db->getVector( "ComponentLabels" ); - auto AffinityList = freelee_db->getVector( "ComponentAffinity" ); + auto LabelList = freelee_db->getVector("ComponentLabels"); + auto AffinityList = freelee_db->getVector("ComponentAffinity"); - NLABELS=LabelList.size(); - if (NLABELS != AffinityList.size()){ - ERROR("Error: ComponentLabels and ComponentAffinity must be the same length! \n"); - } + NLABELS = LabelList.size(); + if (NLABELS != AffinityList.size()) { + ERROR("Error: ComponentLabels and ComponentAffinity must be the same " + "length! \n"); + } - double *label_count; - double *label_count_global; - label_count = new double [NLABELS]; - label_count_global = new double [NLABELS]; + double *label_count; + double *label_count_global; + label_count = new double[NLABELS]; + label_count_global = new double[NLABELS]; - // Assign the labels - for (size_t idx=0; idxid[n] - int x=i-1; - int y=j-1; - int z=k-1; - if (x<0) x=0; - if (y<0) y=0; - if (z<0) z=0; - if (x>=Nx) x=Nx-1; - if (y>=Ny) y=Ny-1; - if (z>=Nz) z=Nz-1; - int n = z*Nx*Ny+y*Nx+x; - VALUE=id[n]; + int x = i - 1; + int y = j - 1; + int z = k - 1; + if (x < 0) + x = 0; + if (y < 0) + y = 0; + if (z < 0) + z = 0; + if (x >= Nx) + x = Nx - 1; + if (y >= Ny) + y = Ny - 1; + if (z >= Nz) + z = Nz - 1; + int n = z * Nx * Ny + y * Nx + x; + VALUE = id[n]; - // Assign the affinity from the paired list - for (unsigned int idx=0; idx < NLABELS; idx++){ - //printf("idx=%i, value=%i, %i, \n",idx, VALUE,LabelList[idx]); - if (VALUE == LabelList[idx]){ - AFFINITY=AffinityList[idx]; - label_count[idx] += 1.0; - idx = NLABELS; - //Mask->id[n] = 0; // set mask to zero since this is an immobile component - } - } - // fluid labels are reserved - if (VALUE == 1) AFFINITY=1.0; - else if (VALUE == 2) AFFINITY=-1.0; - phase[nh] = AFFINITY; - } - } - } + // Assign the affinity from the paired list + for (unsigned int idx = 0; idx < NLABELS; idx++) { + //printf("idx=%i, value=%i, %i, \n",idx, VALUE,LabelList[idx]); + if (VALUE == LabelList[idx]) { + AFFINITY = AffinityList[idx]; + label_count[idx] += 1.0; + idx = NLABELS; + //Mask->id[n] = 0; // set mask to zero since this is an immobile component + } + } + // fluid labels are reserved + if (VALUE == 1) + AFFINITY = 1.0; + else if (VALUE == 2) + AFFINITY = -1.0; + phase[nh] = AFFINITY; + } + } + } - // Set Dm to match Mask - for (int i=0; iid[i] = Mask->id[i]; - - for (size_t idx=0; idxComm.sumReduce(label_count[idx]); + // Set Dm to match Mask + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = Mask->id[i]; - if (rank==0){ - printf("Number of component labels: %lu \n",NLABELS); - for (unsigned int idx=0; idxComm.sumReduce(label_count[idx]); + + if (rank == 0) { + printf("Number of component labels: %lu \n", NLABELS); + for (unsigned int idx = 0; idx < NLABELS; idx++) { + VALUE = LabelList[idx]; + AFFINITY = AffinityList[idx]; + double volume_fraction = + double(label_count_global[idx]) / + double((Nx - 2) * (Ny - 2) * (Nz - 2) * nprocs); + printf(" label=%d, affinity=%f, volume fraction==%f\n", VALUE, + AFFINITY, volume_fraction); + } + } //compute color gradient and laplacian of phase field - double *ColorGrad_host, *mu_phi_host; - ColorGrad_host = new double[3*Np]; - mu_phi_host = new double[Np]; + double *ColorGrad_host, *mu_phi_host; + ColorGrad_host = new double[3 * Np]; + mu_phi_host = new double[Np]; - double *Dst; - Dst = new double [3*3*3]; - for (int kk=0; kk<3; kk++){ - for (int jj=0; jj<3; jj++){ - for (int ii=0; ii<3; ii++){ - int index = kk*9+jj*3+ii; - Dst[index] = sqrt(double(ii-1)*double(ii-1) + double(jj-1)*double(jj-1)+ double(kk-1)*double(kk-1)); - } - } - } - double w_face = 1.0/18.0; - double w_edge = 1.0/36.0; - double w_corner = 0.f; - //local - Dst[13] = 0.f; - //faces - Dst[4] = w_face; - Dst[10] = w_face; - Dst[12] = w_face; - Dst[14] = w_face; - Dst[16] = w_face; - Dst[22] = w_face; - // corners - Dst[0] = w_corner; - Dst[2] = w_corner; - Dst[6] = w_corner; - Dst[8] = w_corner; - Dst[18] = w_corner; - Dst[20] = w_corner; - Dst[24] = w_corner; - Dst[26] = w_corner; - // edges - Dst[1] = w_edge; - Dst[3] = w_edge; - Dst[5] = w_edge; - Dst[7] = w_edge; - Dst[9] = w_edge; - Dst[11] = w_edge; - Dst[15] = w_edge; - Dst[17] = w_edge; - Dst[19] = w_edge; - Dst[21] = w_edge; - Dst[23] = w_edge; - Dst[25] = w_edge; + double *Dst; + Dst = new double[3 * 3 * 3]; + for (int kk = 0; kk < 3; kk++) { + for (int jj = 0; jj < 3; jj++) { + for (int ii = 0; ii < 3; ii++) { + int index = kk * 9 + jj * 3 + ii; + Dst[index] = sqrt(double(ii - 1) * double(ii - 1) + + double(jj - 1) * double(jj - 1) + + double(kk - 1) * double(kk - 1)); + } + } + } + double w_face = 1.0 / 18.0; + double w_edge = 1.0 / 36.0; + double w_corner = 0.f; + //local + Dst[13] = 0.f; + //faces + Dst[4] = w_face; + Dst[10] = w_face; + Dst[12] = w_face; + Dst[14] = w_face; + Dst[16] = w_face; + Dst[22] = w_face; + // corners + Dst[0] = w_corner; + Dst[2] = w_corner; + Dst[6] = w_corner; + Dst[8] = w_corner; + Dst[18] = w_corner; + Dst[20] = w_corner; + Dst[24] = w_corner; + Dst[26] = w_corner; + // edges + Dst[1] = w_edge; + Dst[3] = w_edge; + Dst[5] = w_edge; + Dst[7] = w_edge; + Dst[9] = w_edge; + Dst[11] = w_edge; + Dst[15] = w_edge; + Dst[17] = w_edge; + Dst[19] = w_edge; + Dst[21] = w_edge; + Dst[23] = w_edge; + Dst[25] = w_edge; - double cs2_inv = 3.0;//inverse of c_s^2 for D3Q19 lattice - int width = 2;//For better readability: make halo width explicity wherever possible - for (int k=width; kBarrier(); - comm.barrier(); + ScaLBL_CopyToDevice(Phi, phase, Nh * sizeof(double)); + ScaLBL_CopyToDevice(ColorGrad, ColorGrad_host, 3 * Np * sizeof(double)); + ScaLBL_CopyToDevice(mu_phi, mu_phi_host, Np * sizeof(double)); + ScaLBL_Comm->Barrier(); + comm.barrier(); //debug //save the phase field and check it - //FILE *OUTFILE; - //sprintf(LocalRankFilename,"Phase_Init.%05i.raw",rank); - //OUTFILE = fopen(LocalRankFilename,"wb"); - //fwrite(phase,8,Nh,OUTFILE); - //fclose(OUTFILE); + //FILE *OUTFILE; + //sprintf(LocalRankFilename,"Phase_Init.%05i.raw",rank); + //OUTFILE = fopen(LocalRankFilename,"wb"); + //fwrite(phase,8,Nh,OUTFILE); + //fclose(OUTFILE); - DoubleArray PhaseField(Nx,Ny,Nz); - FILE *OUTFILE; - - getData_RegularLayout(mu_phi_host,PhaseField); - sprintf(LocalRankFilename,"Chem_Init.%05i.raw",rank); - OUTFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,OUTFILE); - fclose(OUTFILE); + DoubleArray PhaseField(Nx, Ny, Nz); + FILE *OUTFILE; - getData_RegularLayout(&ColorGrad_host[0],PhaseField); - FILE *CGX_FILE; - sprintf(LocalRankFilename,"Gradient_X_Init.%05i.raw",rank); - CGX_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,CGX_FILE); - fclose(CGX_FILE); + getData_RegularLayout(mu_phi_host, PhaseField); + sprintf(LocalRankFilename, "Chem_Init.%05i.raw", rank); + OUTFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, OUTFILE); + fclose(OUTFILE); - getData_RegularLayout(&ColorGrad_host[Np],PhaseField); - FILE *CGY_FILE; - sprintf(LocalRankFilename,"Gradient_Y_Init.%05i.raw",rank); - CGY_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,CGY_FILE); - fclose(CGY_FILE); + getData_RegularLayout(&ColorGrad_host[0], PhaseField); + FILE *CGX_FILE; + sprintf(LocalRankFilename, "Gradient_X_Init.%05i.raw", rank); + CGX_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, CGX_FILE); + fclose(CGX_FILE); - getData_RegularLayout(&ColorGrad_host[2*Np],PhaseField); - FILE *CGZ_FILE; - sprintf(LocalRankFilename,"Gradient_Z_Init.%05i.raw",rank); - CGZ_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,CGZ_FILE); - fclose(CGZ_FILE); + getData_RegularLayout(&ColorGrad_host[Np], PhaseField); + FILE *CGY_FILE; + sprintf(LocalRankFilename, "Gradient_Y_Init.%05i.raw", rank); + CGY_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, CGY_FILE); + fclose(CGY_FILE); - delete [] phase; - delete [] ColorGrad_host; - delete [] mu_phi_host; - delete [] Dst; + getData_RegularLayout(&ColorGrad_host[2 * Np], PhaseField); + FILE *CGZ_FILE; + sprintf(LocalRankFilename, "Gradient_Z_Init.%05i.raw", rank); + CGZ_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, CGZ_FILE); + fclose(CGZ_FILE); + + delete[] phase; + delete[] ColorGrad_host; + delete[] mu_phi_host; + delete[] Dst; } -void ScaLBL_FreeLeeModel::Initialize_TwoFluid(){ - /* +void ScaLBL_FreeLeeModel::Initialize_TwoFluid() { + /* * This function initializes two-fluid Lee model */ - if (rank==0) printf ("Initializing phase field, chemical potential and color gradient\n"); - AssignComponentLabels_ChemPotential_ColorGrad();//initialize phase field Phi + if (rank == 0) + printf("Initializing phase field, chemical potential and color " + "gradient\n"); + AssignComponentLabels_ChemPotential_ColorGrad(); //initialize phase field Phi - if (rank==0) printf ("Initializing distributions for momentum transport\n"); - ScaLBL_D3Q19_FreeLeeModel_TwoFluid_Init(gqbar, mu_phi, ColorGrad, Fx, Fy, Fz, Np); + if (rank == 0) + printf("Initializing distributions for momentum transport\n"); + ScaLBL_D3Q19_FreeLeeModel_TwoFluid_Init(gqbar, mu_phi, ColorGrad, Fx, Fy, + Fz, Np); - if (rank==0) printf ("Initializing density field and distributions for phase-field transport\n"); - ScaLBL_FreeLeeModel_PhaseField_Init(dvcMap, Phi, Den, hq, ColorGrad, rhoA, rhoB, tauM, W, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_FreeLeeModel_PhaseField_Init(dvcMap, Phi, Den, hq, ColorGrad, rhoA, rhoB, tauM, W, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + if (rank == 0) + printf("Initializing density field and distributions for phase-field " + "transport\n"); + ScaLBL_FreeLeeModel_PhaseField_Init(dvcMap, Phi, Den, hq, ColorGrad, rhoA, + rhoB, tauM, W, 0, + ScaLBL_Comm->LastExterior(), Np); + ScaLBL_FreeLeeModel_PhaseField_Init( + dvcMap, Phi, Den, hq, ColorGrad, rhoA, rhoB, tauM, W, + ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - if (Restart == true){ + if (Restart == true) { //TODO need to revise this function - if (rank==0){ - printf("Reading restart file! \n"); - } + if (rank == 0) { + printf("Reading restart file! \n"); + } - // Read in the restart file to CPU buffers - int *TmpMap; - TmpMap = new int[Np]; - - double *cPhi, *cDist, *cDen; - cPhi = new double[N]; - cDen = new double[2*Np]; - cDist = new double[19*Np]; - ScaLBL_CopyToHost(TmpMap, dvcMap, Np*sizeof(int)); + // Read in the restart file to CPU buffers + int *TmpMap; + TmpMap = new int[Np]; + + double *cPhi, *cDist, *cDen; + cPhi = new double[N]; + cDen = new double[2 * Np]; + cDist = new double[19 * Np]; + ScaLBL_CopyToHost(TmpMap, dvcMap, Np * sizeof(int)); //ScaLBL_CopyToHost(cPhi, Phi, N*sizeof(double)); - - ifstream File(LocalRestartFile,ios::binary); - int idx; - double value,va,vb; - for (int n=0; nLastExterior(); n++){ - va = cDen[n]; - vb = cDen[Np + n]; - value = (va-vb)/(va+vb); - idx = TmpMap[n]; - if (!(idx < 0) && idxFirstInterior(); nLastInterior(); n++){ - va = cDen[n]; - vb = cDen[Np + n]; - value = (va-vb)/(va+vb); - idx = TmpMap[n]; - if (!(idx < 0) && idxBarrier(); - comm.barrier(); - if (rank==0) printf ("Initializing phase and density fields on device from Restart\n"); + ifstream File(LocalRestartFile, ios::binary); + int idx; + double value, va, vb; + for (int n = 0; n < Np; n++) { + File.read((char *)&va, sizeof(va)); + File.read((char *)&vb, sizeof(vb)); + cDen[n] = va; + cDen[Np + n] = vb; + } + for (int n = 0; n < Np; n++) { + // Read the distributions + for (int q = 0; q < 19; q++) { + File.read((char *)&value, sizeof(value)); + cDist[q * Np + n] = value; + } + } + File.close(); + + for (int n = 0; n < ScaLBL_Comm->LastExterior(); n++) { + va = cDen[n]; + vb = cDen[Np + n]; + value = (va - vb) / (va + vb); + idx = TmpMap[n]; + if (!(idx < 0) && idx < N) + cPhi[idx] = value; + } + for (int n = ScaLBL_Comm->FirstInterior(); + n < ScaLBL_Comm->LastInterior(); n++) { + va = cDen[n]; + vb = cDen[Np + n]; + value = (va - vb) / (va + vb); + idx = TmpMap[n]; + if (!(idx < 0) && idx < N) + cPhi[idx] = value; + } + + // Copy the restart data to the GPU + ScaLBL_CopyToDevice(Den, cDen, 2 * Np * sizeof(double)); + ScaLBL_CopyToDevice(gqbar, cDist, 19 * Np * sizeof(double)); + ScaLBL_CopyToDevice(Phi, cPhi, N * sizeof(double)); + ScaLBL_Comm->Barrier(); + comm.barrier(); + + if (rank == 0) + printf("Initializing phase and density fields on device from " + "Restart\n"); //TODO the following function is to be updated. //ScaLBL_FreeLeeModel_PhaseField_InitFromRestart(Den, hq, 0, ScaLBL_Comm->LastExterior(), Np); //ScaLBL_FreeLeeModel_PhaseField_InitFromRestart(Den, hq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - } + } - // establish reservoirs for external bC + // establish reservoirs for external bC // TODO to be revised - if (BoundaryCondition == 1 || BoundaryCondition == 2 || BoundaryCondition == 3 || BoundaryCondition == 4 ){ - if (Dm->kproc()==0){ - ScaLBL_SetSlice_z(Phi,1.0,Nx,Ny,Nz,0); - ScaLBL_SetSlice_z(Phi,1.0,Nx,Ny,Nz,1); - ScaLBL_SetSlice_z(Phi,1.0,Nx,Ny,Nz,2); - } - if (Dm->kproc() == nprocz-1){ - ScaLBL_SetSlice_z(Phi,-1.0,Nx,Ny,Nz,Nz-1); - ScaLBL_SetSlice_z(Phi,-1.0,Nx,Ny,Nz,Nz-2); - ScaLBL_SetSlice_z(Phi,-1.0,Nx,Ny,Nz,Nz-3); - } - } - //ScaLBL_CopyToHost(Averages->Phi.data(),Phi,N*sizeof(double)); + if (BoundaryCondition == 1 || BoundaryCondition == 2 || + BoundaryCondition == 3 || BoundaryCondition == 4) { + if (Dm->kproc() == 0) { + ScaLBL_SetSlice_z(Phi, 1.0, Nx, Ny, Nz, 0); + ScaLBL_SetSlice_z(Phi, 1.0, Nx, Ny, Nz, 1); + ScaLBL_SetSlice_z(Phi, 1.0, Nx, Ny, Nz, 2); + } + if (Dm->kproc() == nprocz - 1) { + ScaLBL_SetSlice_z(Phi, -1.0, Nx, Ny, Nz, Nz - 1); + ScaLBL_SetSlice_z(Phi, -1.0, Nx, Ny, Nz, Nz - 2); + ScaLBL_SetSlice_z(Phi, -1.0, Nx, Ny, Nz, Nz - 3); + } + } + //ScaLBL_CopyToHost(Averages->Phi.data(),Phi,N*sizeof(double)); } -void ScaLBL_FreeLeeModel::Initialize_SingleFluid(){ - /* +void ScaLBL_FreeLeeModel::Initialize_SingleFluid() { + /* * This function initializes single-fluid Lee model */ - if (rank==0) printf ("Initializing distributions for momentum transport\n"); - ScaLBL_D3Q19_FreeLeeModel_SingleFluid_Init(gqbar, Fx, Fy, Fz, Np); + if (rank == 0) + printf("Initializing distributions for momentum transport\n"); + ScaLBL_D3Q19_FreeLeeModel_SingleFluid_Init(gqbar, Fx, Fy, Fz, Np); - if (Restart == true){ + if (Restart == true) { //TODO need to revise this function //remove the phase-related part - } + } } -double ScaLBL_FreeLeeModel::Run_TwoFluid(int returntime){ - - int START_TIME = timestep; - int EXIT_TIME = min(returntime, timestepMax); - //************ MAIN ITERATION LOOP ***************************************/ - comm.barrier(); - auto t1 = std::chrono::system_clock::now(); - PROFILE_START("Loop"); - - while (timestep < EXIT_TIME ) { - //if ( rank==0 ) { printf("Running timestep %i (%i MB)\n",timestep+1,(int)(Utilities::getMemoryUsage()/1048576)); } - PROFILE_START("Update"); - // *************ODD TIMESTEP************* - timestep++; - //------------------------------------------------------------------------------------------------------------------- - // Compute the Phase indicator field - // Read for hq happens in this routine (requires communication) - ScaLBL_Comm->SendD3Q7AA(hq,0); //READ FROM NORMAL - ScaLBL_D3Q7_AAodd_FreeLeeModel_PhaseField(NeighborList, dvcMap, hq, Den, Phi, rhoA, rhoB, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - //ScaLBL_D3Q7_AAodd_FreeLee_PhaseField(NeighborList, dvcMap, hq, Den, Phi, ColorGrad, Velocity, rhoA, rhoB, tauM, W, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->RecvD3Q7AA(hq,0); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - ScaLBL_D3Q7_AAodd_FreeLeeModel_PhaseField(NeighborList, dvcMap, hq, Den, Phi, rhoA, rhoB, 0, ScaLBL_Comm->LastExterior(), Np); - //ScaLBL_D3Q7_AAodd_FreeLee_PhaseField(NeighborList, dvcMap, hq, Den, Phi, ColorGrad, Velocity, rhoA, rhoB, tauM, W, 0, ScaLBL_Comm->LastExterior(), Np); +double ScaLBL_FreeLeeModel::Run_TwoFluid(int returntime) { - // Perform the collision operation - //ScaLBL_D3Q7_ComputePhaseField(dvcMap, hq, Den, Phi, rhoA, rhoB, 0, ScaLBL_Comm->LastInterior(), Np); - //ScaLBL_Comm_WideHalo->Send(Phi); - //ScaLBL_Comm_WideHalo->Recv(Phi); - ScaLBL_Comm->SendD3Q19AA(gqbar); //READ FROM NORMAL - if (BoundaryCondition > 0 && BoundaryCondition < 5){ + int START_TIME = timestep; + int EXIT_TIME = min(returntime, timestepMax); + //************ MAIN ITERATION LOOP ***************************************/ + comm.barrier(); + auto t1 = std::chrono::system_clock::now(); + PROFILE_START("Loop"); + + while (timestep < EXIT_TIME) { + //if ( rank==0 ) { printf("Running timestep %i (%i MB)\n",timestep+1,(int)(Utilities::getMemoryUsage()/1048576)); } + PROFILE_START("Update"); + // *************ODD TIMESTEP************* + timestep++; + //------------------------------------------------------------------------------------------------------------------- + // Compute the Phase indicator field + // Read for hq happens in this routine (requires communication) + ScaLBL_Comm->SendD3Q7AA(hq, 0); //READ FROM NORMAL + ScaLBL_D3Q7_AAodd_FreeLeeModel_PhaseField( + NeighborList, dvcMap, hq, Den, Phi, rhoA, rhoB, + ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + //ScaLBL_D3Q7_AAodd_FreeLee_PhaseField(NeighborList, dvcMap, hq, Den, Phi, ColorGrad, Velocity, rhoA, rhoB, tauM, W, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->RecvD3Q7AA(hq, 0); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + ScaLBL_D3Q7_AAodd_FreeLeeModel_PhaseField( + NeighborList, dvcMap, hq, Den, Phi, rhoA, rhoB, 0, + ScaLBL_Comm->LastExterior(), Np); + //ScaLBL_D3Q7_AAodd_FreeLee_PhaseField(NeighborList, dvcMap, hq, Den, Phi, ColorGrad, Velocity, rhoA, rhoB, tauM, W, 0, ScaLBL_Comm->LastExterior(), Np); + + // Perform the collision operation + //ScaLBL_D3Q7_ComputePhaseField(dvcMap, hq, Den, Phi, rhoA, rhoB, 0, ScaLBL_Comm->LastInterior(), Np); + //ScaLBL_Comm_WideHalo->Send(Phi); + //ScaLBL_Comm_WideHalo->Recv(Phi); + ScaLBL_Comm->SendD3Q19AA(gqbar); //READ FROM NORMAL + if (BoundaryCondition > 0 && BoundaryCondition < 5) { //TODO to be revised - // Need to add BC for hq!!! - ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); - ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); - } - // Halo exchange for phase field - ScaLBL_Comm_WideHalo->Send(Phi); - //ScaLBL_D3Q19_AAodd_FreeLeeModel(NeighborList, dvcMap, gqbar, Den, Phi, mu_phi, Velocity, Pressure, ColorGrad, rhoA, rhoB, tauA, tauB, - // kappa, beta, W, Fx, Fy, Fz, Nxh, Nxh*Nyh, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_D3Q19_AAodd_FreeLeeModel_Combined(NeighborList, dvcMap, gqbar, hq, Den, Phi, mu_phi, Velocity, Pressure, ColorGrad, rhoA, rhoB, tauA, tauB, tauM, - kappa, beta, W, Fx, Fy, Fz, Nxh, Nxh*Nyh, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm_WideHalo->Recv(Phi); - ScaLBL_Comm->RecvD3Q19AA(gqbar); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - // Set BCs - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, gqbar, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, gqbar, dout, timestep); - } - if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, gqbar, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, gqbar, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(gqbar); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(gqbar); - } + // Need to add BC for hq!!! + ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); + ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); + } + // Halo exchange for phase field + ScaLBL_Comm_WideHalo->Send(Phi); + //ScaLBL_D3Q19_AAodd_FreeLeeModel(NeighborList, dvcMap, gqbar, Den, Phi, mu_phi, Velocity, Pressure, ColorGrad, rhoA, rhoB, tauA, tauB, + // kappa, beta, W, Fx, Fy, Fz, Nxh, Nxh*Nyh, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + ScaLBL_D3Q19_AAodd_FreeLeeModel_Combined( + NeighborList, dvcMap, gqbar, hq, Den, Phi, mu_phi, Velocity, + Pressure, ColorGrad, rhoA, rhoB, tauA, tauB, tauM, kappa, beta, W, + Fx, Fy, Fz, Nxh, Nxh * Nyh, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm_WideHalo->Recv(Phi); + ScaLBL_Comm->RecvD3Q19AA(gqbar); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + // Set BCs + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, gqbar, din, + timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, gqbar, dout, + timestep); + } + if (BoundaryCondition == 4) { + din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, gqbar, flux, + timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, gqbar, dout, + timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(gqbar); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(gqbar); + } - //ScaLBL_D3Q19_AAodd_FreeLeeModel(NeighborList, dvcMap, gqbar, Den, Phi, mu_phi, Velocity, Pressure, ColorGrad, rhoA, rhoB, tauA, tauB, - // kappa, beta, W, Fx, Fy, Fz, Nxh, Nxh*Nyh, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_D3Q19_AAodd_FreeLeeModel_Combined(NeighborList, dvcMap, gqbar, hq, Den, Phi, mu_phi, Velocity, Pressure, ColorGrad, rhoA, rhoB, tauA, tauB, tauM, - kappa, beta, W, Fx, Fy, Fz, Nxh, Nxh*Nyh, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->Barrier(); - - - // *************EVEN TIMESTEP************* - timestep++; - // Compute the Phase indicator field - ScaLBL_Comm->SendD3Q7AA(hq,0); //READ FROM NORMA - ScaLBL_D3Q7_AAeven_FreeLeeModel_PhaseField(dvcMap, hq, Den, Phi, rhoA, rhoB, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - //ScaLBL_D3Q7_AAeven_FreeLee_PhaseField(dvcMap, hq, Den, Phi, ColorGrad, Velocity, rhoA, rhoB, tauM, W, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->RecvD3Q7AA(hq,0); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - ScaLBL_D3Q7_AAeven_FreeLeeModel_PhaseField(dvcMap, hq, Den, Phi, rhoA, rhoB, 0, ScaLBL_Comm->LastExterior(), Np); - //ScaLBL_D3Q7_AAeven_FreeLee_PhaseField(dvcMap, hq, Den, Phi, ColorGrad, Velocity, rhoA, rhoB, tauM, W, 0, ScaLBL_Comm->LastExterior(), Np); + //ScaLBL_D3Q19_AAodd_FreeLeeModel(NeighborList, dvcMap, gqbar, Den, Phi, mu_phi, Velocity, Pressure, ColorGrad, rhoA, rhoB, tauA, tauB, + // kappa, beta, W, Fx, Fy, Fz, Nxh, Nxh*Nyh, 0, ScaLBL_Comm->LastExterior(), Np); + ScaLBL_D3Q19_AAodd_FreeLeeModel_Combined( + NeighborList, dvcMap, gqbar, hq, Den, Phi, mu_phi, Velocity, + Pressure, ColorGrad, rhoA, rhoB, tauA, tauB, tauM, kappa, beta, W, + Fx, Fy, Fz, Nxh, Nxh * Nyh, 0, ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm->Barrier(); - // Perform the collision operation - //ScaLBL_D3Q7_ComputePhaseField(dvcMap, hq, Den, Phi, rhoA, rhoB, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - //ScaLBL_Comm_WideHalo->Send(Phi); - //ScaLBL_Comm_WideHalo->Recv(Phi); - ScaLBL_Comm->SendD3Q19AA(gqbar); //READ FORM NORMAL - if (BoundaryCondition > 0 && BoundaryCondition < 5){ - ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); - ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); - } - // Halo exchange for phase field - ScaLBL_Comm_WideHalo->Send(Phi); - //ScaLBL_D3Q19_AAeven_FreeLeeModel(dvcMap, gqbar, Den, Phi, mu_phi, Velocity, Pressure, ColorGrad, rhoA, rhoB, tauA, tauB, - // kappa, beta, W, Fx, Fy, Fz, Nxh, Nxh*Nyh, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(dvcMap, gqbar, hq, Den, Phi, mu_phi, Velocity, Pressure, ColorGrad, rhoA, rhoB, tauA, tauB, tauM, - kappa, beta, W, Fx, Fy, Fz, Nxh, Nxh*Nyh, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm_WideHalo->Recv(Phi); - ScaLBL_Comm->RecvD3Q19AA(gqbar); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - // Set boundary conditions - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, gqbar, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, gqbar, dout, timestep); - } - else if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, gqbar, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, gqbar, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(gqbar); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(gqbar); - } - //ScaLBL_D3Q19_AAeven_FreeLeeModel(dvcMap, gqbar, Den, Phi, mu_phi, Velocity, Pressure, ColorGrad, rhoA, rhoB, tauA, tauB, - // kappa, beta, W, Fx, Fy, Fz, Nxh, Nxh*Nyh, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined(dvcMap, gqbar, hq, Den, Phi, mu_phi, Velocity, Pressure, ColorGrad, rhoA, rhoB, tauA, tauB, tauM, - kappa, beta, W, Fx, Fy, Fz, Nxh, Nxh*Nyh, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->Barrier(); - //************************************************************************ - PROFILE_STOP("Update"); - } - PROFILE_STOP("Loop"); - PROFILE_SAVE("lbpm_color_simulator",1); - //************************************************************************ - if (rank==0) printf("-------------------------------------------------------------------\n"); - // Compute the walltime per timestep + // *************EVEN TIMESTEP************* + timestep++; + // Compute the Phase indicator field + ScaLBL_Comm->SendD3Q7AA(hq, 0); //READ FROM NORMA + ScaLBL_D3Q7_AAeven_FreeLeeModel_PhaseField( + dvcMap, hq, Den, Phi, rhoA, rhoB, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + //ScaLBL_D3Q7_AAeven_FreeLee_PhaseField(dvcMap, hq, Den, Phi, ColorGrad, Velocity, rhoA, rhoB, tauM, W, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->RecvD3Q7AA(hq, 0); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + ScaLBL_D3Q7_AAeven_FreeLeeModel_PhaseField( + dvcMap, hq, Den, Phi, rhoA, rhoB, 0, ScaLBL_Comm->LastExterior(), + Np); + //ScaLBL_D3Q7_AAeven_FreeLee_PhaseField(dvcMap, hq, Den, Phi, ColorGrad, Velocity, rhoA, rhoB, tauM, W, 0, ScaLBL_Comm->LastExterior(), Np); + + // Perform the collision operation + //ScaLBL_D3Q7_ComputePhaseField(dvcMap, hq, Den, Phi, rhoA, rhoB, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + //ScaLBL_Comm_WideHalo->Send(Phi); + //ScaLBL_Comm_WideHalo->Recv(Phi); + ScaLBL_Comm->SendD3Q19AA(gqbar); //READ FORM NORMAL + if (BoundaryCondition > 0 && BoundaryCondition < 5) { + ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); + ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); + } + // Halo exchange for phase field + ScaLBL_Comm_WideHalo->Send(Phi); + //ScaLBL_D3Q19_AAeven_FreeLeeModel(dvcMap, gqbar, Den, Phi, mu_phi, Velocity, Pressure, ColorGrad, rhoA, rhoB, tauA, tauB, + // kappa, beta, W, Fx, Fy, Fz, Nxh, Nxh*Nyh, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined( + dvcMap, gqbar, hq, Den, Phi, mu_phi, Velocity, Pressure, ColorGrad, + rhoA, rhoB, tauA, tauB, tauM, kappa, beta, W, Fx, Fy, Fz, Nxh, + Nxh * Nyh, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm_WideHalo->Recv(Phi); + ScaLBL_Comm->RecvD3Q19AA(gqbar); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + // Set boundary conditions + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, gqbar, din, + timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, gqbar, dout, + timestep); + } else if (BoundaryCondition == 4) { + din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, gqbar, flux, + timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, gqbar, dout, + timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(gqbar); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(gqbar); + } + //ScaLBL_D3Q19_AAeven_FreeLeeModel(dvcMap, gqbar, Den, Phi, mu_phi, Velocity, Pressure, ColorGrad, rhoA, rhoB, tauA, tauB, + // kappa, beta, W, Fx, Fy, Fz, Nxh, Nxh*Nyh, 0, ScaLBL_Comm->LastExterior(), Np); + ScaLBL_D3Q19_AAeven_FreeLeeModel_Combined( + dvcMap, gqbar, hq, Den, Phi, mu_phi, Velocity, Pressure, ColorGrad, + rhoA, rhoB, tauA, tauB, tauM, kappa, beta, W, Fx, Fy, Fz, Nxh, + Nxh * Nyh, 0, ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm->Barrier(); + //************************************************************************ + PROFILE_STOP("Update"); + } + PROFILE_STOP("Loop"); + PROFILE_SAVE("lbpm_color_simulator", 1); + //************************************************************************ + if (rank == 0) + printf("---------------------------------------------------------------" + "----\n"); + // Compute the walltime per timestep auto t2 = std::chrono::system_clock::now(); - double cputime = std::chrono::duration( t2 - t1 ).count() / (EXIT_TIME-START_TIME); - // Performance obtained from each node - double MLUPS = double(Np)/cputime/1000000; + double cputime = std::chrono::duration(t2 - t1).count() / + (EXIT_TIME - START_TIME); + // Performance obtained from each node + double MLUPS = double(Np) / cputime / 1000000; - return MLUPS; + return MLUPS; } -void ScaLBL_FreeLeeModel::Run_SingleFluid(){ - int nprocs=nprocx*nprocy*nprocz; - const RankInfoStruct rank_info(rank,nprocx,nprocy,nprocz); - - if (rank==0){ - printf("********************************************************\n"); - printf("No. of timesteps: %i \n", timestepMax); - fflush(stdout); - } +void ScaLBL_FreeLeeModel::Run_SingleFluid() { + int nprocs = nprocx * nprocy * nprocz; + const RankInfoStruct rank_info(rank, nprocx, nprocy, nprocz); - //.......create and start timer............ - ScaLBL_Comm->Barrier(); - comm.barrier(); - //......................................... + if (rank == 0) { + printf("********************************************************\n"); + printf("No. of timesteps: %i \n", timestepMax); + fflush(stdout); + } - //************ MAIN ITERATION LOOP ***************************************/ - PROFILE_START("Loop"); + //.......create and start timer............ + ScaLBL_Comm->Barrier(); + comm.barrier(); + //......................................... + + //************ MAIN ITERATION LOOP ***************************************/ + PROFILE_START("Loop"); auto t1 = std::chrono::system_clock::now(); - while (timestep < timestepMax ) { - //if ( rank==0 ) { printf("Running timestep %i (%i MB)\n",timestep+1,(int)(Utilities::getMemoryUsage()/1048576)); } - PROFILE_START("Update"); - // *************ODD TIMESTEP************* - timestep++; + while (timestep < timestepMax) { + //if ( rank==0 ) { printf("Running timestep %i (%i MB)\n",timestep+1,(int)(Utilities::getMemoryUsage()/1048576)); } + PROFILE_START("Update"); + // *************ODD TIMESTEP************* + timestep++; //------------------------------------------------------------------------------------------------------------------- - // Perform the collision operation - ScaLBL_Comm->SendD3Q19AA(gqbar); //READ FROM NORMAL - ScaLBL_D3Q19_AAodd_FreeLeeModel_SingleFluid_BGK(NeighborList, gqbar, Velocity, Pressure, tau, rho0, Fx, Fy, Fz, - ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->RecvD3Q19AA(gqbar); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - // Set boundary conditions + // Perform the collision operation + ScaLBL_Comm->SendD3Q19AA(gqbar); //READ FROM NORMAL + ScaLBL_D3Q19_AAodd_FreeLeeModel_SingleFluid_BGK( + NeighborList, gqbar, Velocity, Pressure, tau, rho0, Fx, Fy, Fz, + ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->RecvD3Q19AA(gqbar); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + // Set boundary conditions // TODO to be revised! - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, gqbar, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, gqbar, dout, timestep); - } - if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, gqbar, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, gqbar, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(gqbar); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(gqbar); - } - ScaLBL_D3Q19_AAodd_FreeLeeModel_SingleFluid_BGK(NeighborList, gqbar, Velocity, Pressure, tau, rho0, Fx, Fy, Fz, - 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->Barrier(); + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, gqbar, din, + timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, gqbar, dout, + timestep); + } + if (BoundaryCondition == 4) { + din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, gqbar, flux, + timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, gqbar, dout, + timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(gqbar); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(gqbar); + } + ScaLBL_D3Q19_AAodd_FreeLeeModel_SingleFluid_BGK( + NeighborList, gqbar, Velocity, Pressure, tau, rho0, Fx, Fy, Fz, 0, + ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm->Barrier(); - - // *************EVEN TIMESTEP************* - timestep++; + // *************EVEN TIMESTEP************* + timestep++; //------------------------------------------------------------------------------------------------------------------- - // Perform the collision operation - ScaLBL_Comm->SendD3Q19AA(gqbar); //READ FORM NORMAL - ScaLBL_D3Q19_AAeven_FreeLeeModel_SingleFluid_BGK(gqbar, Velocity, Pressure, tau, rho0, Fx, Fy, Fz, - ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->RecvD3Q19AA(gqbar); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - // Set boundary conditions + // Perform the collision operation + ScaLBL_Comm->SendD3Q19AA(gqbar); //READ FORM NORMAL + ScaLBL_D3Q19_AAeven_FreeLeeModel_SingleFluid_BGK( + gqbar, Velocity, Pressure, tau, rho0, Fx, Fy, Fz, + ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->RecvD3Q19AA(gqbar); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + // Set boundary conditions // TODO to be revised! - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, gqbar, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, gqbar, dout, timestep); - } - else if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, gqbar, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, gqbar, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(gqbar); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(gqbar); - } - ScaLBL_D3Q19_AAeven_FreeLeeModel_SingleFluid_BGK(gqbar, Velocity, Pressure, tau, rho0, Fx, Fy, Fz, - 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->Barrier(); - //************************************************************************ - PROFILE_STOP("Update"); - } - PROFILE_STOP("Loop"); - PROFILE_SAVE("lbpm_color_simulator",1); - //************************************************************************ - if (rank==0) printf("-------------------------------------------------------------------\n"); - // Compute the walltime per timestep + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, gqbar, din, + timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, gqbar, dout, + timestep); + } else if (BoundaryCondition == 4) { + din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, gqbar, flux, + timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, gqbar, dout, + timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(gqbar); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(gqbar); + } + ScaLBL_D3Q19_AAeven_FreeLeeModel_SingleFluid_BGK( + gqbar, Velocity, Pressure, tau, rho0, Fx, Fy, Fz, 0, + ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm->Barrier(); + //************************************************************************ + PROFILE_STOP("Update"); + } + PROFILE_STOP("Loop"); + PROFILE_SAVE("lbpm_color_simulator", 1); + //************************************************************************ + if (rank == 0) + printf("---------------------------------------------------------------" + "----\n"); + // Compute the walltime per timestep auto t2 = std::chrono::system_clock::now(); - double cputime = std::chrono::duration( t2 - t1 ).count() / timestep; - // Performance obtained from each node - double MLUPS = double(Np)/cputime/1000000; + double cputime = std::chrono::duration(t2 - t1).count() / timestep; + // Performance obtained from each node + double MLUPS = double(Np) / cputime / 1000000; - if (rank==0) printf("********************************************************\n"); - if (rank==0) printf("CPU time = %f \n", cputime); - if (rank==0) printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); - MLUPS *= nprocs; - if (rank==0) printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); - if (rank==0) printf("********************************************************\n"); + if (rank == 0) + printf("********************************************************\n"); + if (rank == 0) + printf("CPU time = %f \n", cputime); + if (rank == 0) + printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); + MLUPS *= nprocs; + if (rank == 0) + printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); + if (rank == 0) + printf("********************************************************\n"); - // ************************************************************************ + // ************************************************************************ } -void ScaLBL_FreeLeeModel::WriteDebug_TwoFluid(){ - // Copy back final phase indicator field and convert to regular layout - DoubleArray PhaseData(Nxh,Nyh,Nzh); - //ScaLBL_Comm->RegularLayout(Map,Phi,PhaseField); - ScaLBL_CopyToHost(PhaseData.data(), Phi, sizeof(double)*Nh); - /* +void ScaLBL_FreeLeeModel::WriteDebug_TwoFluid() { + // Copy back final phase indicator field and convert to regular layout + DoubleArray PhaseData(Nxh, Nyh, Nzh); + //ScaLBL_Comm->RegularLayout(Map,Phi,PhaseField); + ScaLBL_CopyToHost(PhaseData.data(), Phi, sizeof(double) * Nh); + /* IntArray MapData(Np); ScaLBL_CopyToHost(MapData.data(), dvcMap, sizeof(int)*Np); FILE *MAP; @@ -1000,267 +1116,286 @@ void ScaLBL_FreeLeeModel::WriteDebug_TwoFluid(){ */ - FILE *OUTFILE; - sprintf(LocalRankFilename,"Phase.%05i.raw",rank); - OUTFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseData.data(),8,Nh,OUTFILE); - fclose(OUTFILE); + FILE *OUTFILE; + sprintf(LocalRankFilename, "Phase.%05i.raw", rank); + OUTFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseData.data(), 8, Nh, OUTFILE); + fclose(OUTFILE); - DoubleArray PhaseField(Nx,Ny,Nz); - FILE *DIST; - for (int q=0; q<7; q++){ - ScaLBL_Comm->RegularLayout(Map,&hq[q*Np],PhaseField); + DoubleArray PhaseField(Nx, Ny, Nz); + FILE *DIST; + for (int q = 0; q < 7; q++) { + ScaLBL_Comm->RegularLayout(Map, &hq[q * Np], PhaseField); - sprintf(LocalRankFilename,"h%i.%05i.raw",q,rank); - DIST = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,Nx*Ny*Nz,DIST); - fclose(DIST); + sprintf(LocalRankFilename, "h%i.%05i.raw", q, rank); + DIST = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, Nx * Ny * Nz, DIST); + fclose(DIST); + } - } + ScaLBL_Comm->RegularLayout(Map, Den, PhaseField); + FILE *AFILE; + sprintf(LocalRankFilename, "Density.%05i.raw", rank); + AFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, AFILE); + fclose(AFILE); - ScaLBL_Comm->RegularLayout(Map,Den,PhaseField); - FILE *AFILE; - sprintf(LocalRankFilename,"Density.%05i.raw",rank); - AFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,AFILE); - fclose(AFILE); + ScaLBL_Comm->RegularLayout(Map, Pressure, PhaseField); + FILE *PFILE; + sprintf(LocalRankFilename, "Pressure.%05i.raw", rank); + PFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, PFILE); + fclose(PFILE); - ScaLBL_Comm->RegularLayout(Map,Pressure,PhaseField); - FILE *PFILE; - sprintf(LocalRankFilename,"Pressure.%05i.raw",rank); - PFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,PFILE); - fclose(PFILE); + ScaLBL_Comm->RegularLayout(Map, mu_phi, PhaseField); + FILE *CHEMFILE; + sprintf(LocalRankFilename, "ChemPotential.%05i.raw", rank); + CHEMFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, CHEMFILE); + fclose(CHEMFILE); - ScaLBL_Comm->RegularLayout(Map,mu_phi,PhaseField); - FILE *CHEMFILE; - sprintf(LocalRankFilename,"ChemPotential.%05i.raw",rank); - CHEMFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,CHEMFILE); - fclose(CHEMFILE); + ScaLBL_Comm->RegularLayout(Map, &Velocity[0], PhaseField); + FILE *VELX_FILE; + sprintf(LocalRankFilename, "Velocity_X.%05i.raw", rank); + VELX_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELX_FILE); + fclose(VELX_FILE); - ScaLBL_Comm->RegularLayout(Map,&Velocity[0],PhaseField); - FILE *VELX_FILE; - sprintf(LocalRankFilename,"Velocity_X.%05i.raw",rank); - VELX_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELX_FILE); - fclose(VELX_FILE); + ScaLBL_Comm->RegularLayout(Map, &Velocity[Np], PhaseField); + FILE *VELY_FILE; + sprintf(LocalRankFilename, "Velocity_Y.%05i.raw", rank); + VELY_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELY_FILE); + fclose(VELY_FILE); - ScaLBL_Comm->RegularLayout(Map,&Velocity[Np],PhaseField); - FILE *VELY_FILE; - sprintf(LocalRankFilename,"Velocity_Y.%05i.raw",rank); - VELY_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELY_FILE); - fclose(VELY_FILE); + ScaLBL_Comm->RegularLayout(Map, &Velocity[2 * Np], PhaseField); + FILE *VELZ_FILE; + sprintf(LocalRankFilename, "Velocity_Z.%05i.raw", rank); + VELZ_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELZ_FILE); + fclose(VELZ_FILE); - ScaLBL_Comm->RegularLayout(Map,&Velocity[2*Np],PhaseField); - FILE *VELZ_FILE; - sprintf(LocalRankFilename,"Velocity_Z.%05i.raw",rank); - VELZ_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELZ_FILE); - fclose(VELZ_FILE); + ScaLBL_Comm->RegularLayout(Map, &ColorGrad[0], PhaseField); + FILE *CGX_FILE; + sprintf(LocalRankFilename, "Gradient_X.%05i.raw", rank); + CGX_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, CGX_FILE); + fclose(CGX_FILE); - ScaLBL_Comm->RegularLayout(Map,&ColorGrad[0],PhaseField); - FILE *CGX_FILE; - sprintf(LocalRankFilename,"Gradient_X.%05i.raw",rank); - CGX_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,CGX_FILE); - fclose(CGX_FILE); - - ScaLBL_Comm->RegularLayout(Map,&ColorGrad[Np],PhaseField); - FILE *CGY_FILE; - sprintf(LocalRankFilename,"Gradient_Y.%05i.raw",rank); - CGY_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,CGY_FILE); - fclose(CGY_FILE); - - ScaLBL_Comm->RegularLayout(Map,&ColorGrad[2*Np],PhaseField); - FILE *CGZ_FILE; - sprintf(LocalRankFilename,"Gradient_Z.%05i.raw",rank); - CGZ_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,CGZ_FILE); - fclose(CGZ_FILE); + ScaLBL_Comm->RegularLayout(Map, &ColorGrad[Np], PhaseField); + FILE *CGY_FILE; + sprintf(LocalRankFilename, "Gradient_Y.%05i.raw", rank); + CGY_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, CGY_FILE); + fclose(CGY_FILE); + ScaLBL_Comm->RegularLayout(Map, &ColorGrad[2 * Np], PhaseField); + FILE *CGZ_FILE; + sprintf(LocalRankFilename, "Gradient_Z.%05i.raw", rank); + CGZ_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, CGZ_FILE); + fclose(CGZ_FILE); } -void ScaLBL_FreeLeeModel::WriteDebug_SingleFluid(){ +void ScaLBL_FreeLeeModel::WriteDebug_SingleFluid() { - DoubleArray PhaseField(Nx,Ny,Nz); + DoubleArray PhaseField(Nx, Ny, Nz); - // Copy back final phase indicator field and convert to regular layout - ScaLBL_Comm->RegularLayout(Map,Pressure,PhaseField); - FILE *PFILE; - sprintf(LocalRankFilename,"Pressure.%05i.raw",rank); - PFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,PFILE); - fclose(PFILE); + // Copy back final phase indicator field and convert to regular layout + ScaLBL_Comm->RegularLayout(Map, Pressure, PhaseField); + FILE *PFILE; + sprintf(LocalRankFilename, "Pressure.%05i.raw", rank); + PFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, PFILE); + fclose(PFILE); - ScaLBL_Comm->RegularLayout(Map,&Velocity[0],PhaseField); - FILE *VELX_FILE; - sprintf(LocalRankFilename,"Velocity_X.%05i.raw",rank); - VELX_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELX_FILE); - fclose(VELX_FILE); + ScaLBL_Comm->RegularLayout(Map, &Velocity[0], PhaseField); + FILE *VELX_FILE; + sprintf(LocalRankFilename, "Velocity_X.%05i.raw", rank); + VELX_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELX_FILE); + fclose(VELX_FILE); - ScaLBL_Comm->RegularLayout(Map,&Velocity[Np],PhaseField); - FILE *VELY_FILE; - sprintf(LocalRankFilename,"Velocity_Y.%05i.raw",rank); - VELY_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELY_FILE); - fclose(VELY_FILE); + ScaLBL_Comm->RegularLayout(Map, &Velocity[Np], PhaseField); + FILE *VELY_FILE; + sprintf(LocalRankFilename, "Velocity_Y.%05i.raw", rank); + VELY_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELY_FILE); + fclose(VELY_FILE); - ScaLBL_Comm->RegularLayout(Map,&Velocity[2*Np],PhaseField); - FILE *VELZ_FILE; - sprintf(LocalRankFilename,"Velocity_Z.%05i.raw",rank); - VELZ_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELZ_FILE); - fclose(VELZ_FILE); + ScaLBL_Comm->RegularLayout(Map, &Velocity[2 * Np], PhaseField); + FILE *VELZ_FILE; + sprintf(LocalRankFilename, "Velocity_Z.%05i.raw", rank); + VELZ_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELZ_FILE); + fclose(VELZ_FILE); } -void ScaLBL_FreeLeeModel::Create_DummyPhase_MGTest(){ - // Initialize communication structures in averaging domain - for (int i=0; iid[i] = Mask->id[i]; - Mask->CommInit(); - Np=Mask->PoreCount(); - //........................................................................... - if (rank==0) printf ("Create ScaLBL_Communicator \n"); - // Create a communicator for the device (will use optimized layout) - // ScaLBL_Communicator ScaLBL_Comm(Mask); // original - ScaLBL_Comm = std::shared_ptr(new ScaLBL_Communicator(Mask)); - //ScaLBL_Comm_Regular = std::shared_ptr(new ScaLBL_Communicator(Mask)); - ScaLBL_Comm_WideHalo = std::shared_ptr(new ScaLBLWideHalo_Communicator(Mask,2)); +void ScaLBL_FreeLeeModel::Create_DummyPhase_MGTest() { + // Initialize communication structures in averaging domain + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = Mask->id[i]; + Mask->CommInit(); + Np = Mask->PoreCount(); + //........................................................................... + if (rank == 0) + printf("Create ScaLBL_Communicator \n"); + // Create a communicator for the device (will use optimized layout) + // ScaLBL_Communicator ScaLBL_Comm(Mask); // original + ScaLBL_Comm = + std::shared_ptr(new ScaLBL_Communicator(Mask)); + //ScaLBL_Comm_Regular = std::shared_ptr(new ScaLBL_Communicator(Mask)); + ScaLBL_Comm_WideHalo = std::shared_ptr( + new ScaLBLWideHalo_Communicator(Mask, 2)); - // create the layout for the LBM - int Npad=(Np/16 + 2)*16; - if (rank==0) printf ("Set up memory efficient layout, %i | %i | %i \n", Np, Npad, N); - Map.resize(Nx,Ny,Nz); Map.fill(-2); - auto neighborList= new int[18*Npad]; - Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map,neighborList,Mask->id.data(),Np,1); - comm.barrier(); + // create the layout for the LBM + int Npad = (Np / 16 + 2) * 16; + if (rank == 0) + printf("Set up memory efficient layout, %i | %i | %i \n", Np, Npad, N); + Map.resize(Nx, Ny, Nz); + Map.fill(-2); + auto neighborList = new int[18 * Npad]; + Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map, neighborList, + Mask->id.data(), Np, 1); + comm.barrier(); - //........................................................................... - // MAIN VARIABLES ALLOCATED HERE - //........................................................................... - // LBM variables - if (rank==0) printf ("Allocating distributions \n"); - //......................device distributions................................. - dist_mem_size = Np*sizeof(double); - neighborSize=18*(Np*sizeof(int)); - //........................................................................... - //ScaLBL_AllocateDeviceMemory((void **) &NeighborList, neighborSize); - ScaLBL_AllocateDeviceMemory((void **) &dvcMap, sizeof(int)*Np); - //ScaLBL_AllocateDeviceMemory((void **) &gqbar, 19*dist_mem_size); - //ScaLBL_AllocateDeviceMemory((void **) &hq, 7*dist_mem_size); - //ScaLBL_AllocateDeviceMemory((void **) &mu_phi, dist_mem_size); - //ScaLBL_AllocateDeviceMemory((void **) &Den, dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Phi, sizeof(double)*Nh); - //ScaLBL_AllocateDeviceMemory((void **) &Pressure, sizeof(double)*Np); - //ScaLBL_AllocateDeviceMemory((void **) &Velocity, 3*sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &ColorGrad, 3*sizeof(double)*Np); - //........................................................................... - // Update GPU data structures - if (rank==0) printf ("Setting up device map and neighbor list \n"); - fflush(stdout); - int *TmpMap; - TmpMap=new int[Np]; - for (int k=1; kMap(i,j,k); - } - } - } - // check that TmpMap is valid - for (int idx=0; idxLastExterior(); idx++){ - auto n = TmpMap[idx]; - if (n > Nxh*Nyh*Nzh){ - printf("Bad value! idx=%i \n", n); - TmpMap[idx] = Nxh*Nyh*Nzh-1; - } - } - for (int idx=ScaLBL_Comm->FirstInterior(); idxLastInterior(); idx++){ - auto n = TmpMap[idx]; - if ( n > Nxh*Nyh*Nzh ){ - printf("Bad value! idx=%i \n",n); - TmpMap[idx] = Nxh*Nyh*Nzh-1; - } - } + //........................................................................... + // MAIN VARIABLES ALLOCATED HERE + //........................................................................... + // LBM variables + if (rank == 0) + printf("Allocating distributions \n"); + //......................device distributions................................. + dist_mem_size = Np * sizeof(double); + neighborSize = 18 * (Np * sizeof(int)); + //........................................................................... + //ScaLBL_AllocateDeviceMemory((void **) &NeighborList, neighborSize); + ScaLBL_AllocateDeviceMemory((void **)&dvcMap, sizeof(int) * Np); + //ScaLBL_AllocateDeviceMemory((void **) &gqbar, 19*dist_mem_size); + //ScaLBL_AllocateDeviceMemory((void **) &hq, 7*dist_mem_size); + //ScaLBL_AllocateDeviceMemory((void **) &mu_phi, dist_mem_size); + //ScaLBL_AllocateDeviceMemory((void **) &Den, dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Phi, sizeof(double) * Nh); + //ScaLBL_AllocateDeviceMemory((void **) &Pressure, sizeof(double)*Np); + //ScaLBL_AllocateDeviceMemory((void **) &Velocity, 3*sizeof(double)*Np); + ScaLBL_AllocateDeviceMemory((void **)&ColorGrad, 3 * sizeof(double) * Np); + //........................................................................... + // Update GPU data structures + if (rank == 0) + printf("Setting up device map and neighbor list \n"); + fflush(stdout); + int *TmpMap; + TmpMap = new int[Np]; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + int idx = Map(i, j, k); + if (!(idx < 0)) + TmpMap[idx] = ScaLBL_Comm_WideHalo->Map(i, j, k); + } + } + } + // check that TmpMap is valid + for (int idx = 0; idx < ScaLBL_Comm->LastExterior(); idx++) { + auto n = TmpMap[idx]; + if (n > Nxh * Nyh * Nzh) { + printf("Bad value! idx=%i \n", n); + TmpMap[idx] = Nxh * Nyh * Nzh - 1; + } + } + for (int idx = ScaLBL_Comm->FirstInterior(); + idx < ScaLBL_Comm->LastInterior(); idx++) { + auto n = TmpMap[idx]; + if (n > Nxh * Nyh * Nzh) { + printf("Bad value! idx=%i \n", n); + TmpMap[idx] = Nxh * Nyh * Nzh - 1; + } + } // copy the device map - ScaLBL_CopyToDevice(dvcMap, TmpMap, sizeof(int)*Np); - // copy the neighbor list - //ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); - comm.barrier(); + ScaLBL_CopyToDevice(dvcMap, TmpMap, sizeof(int) * Np); + // copy the neighbor list + //ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); + comm.barrier(); - double *phase; - phase = new double[Nh]; + double *phase; + phase = new double[Nh]; - for (int k=0;kid[n] - int x=i-1; - int y=j-1; - int z=k-1; - if (x<0) x=0; - if (y<0) y=0; - if (z<0) z=0; - if (x>=Nx) x=Nx-1; - if (y>=Ny) y=Ny-1; - if (z>=Nz) z=Nz-1; - int n = z*Nx*Ny+y*Nx+x; - phase[nh]=id[n]; - } - } - } - ScaLBL_CopyToDevice(Phi, phase, Nh*sizeof(double)); - ScaLBL_Comm->Barrier(); - comm.barrier(); - delete [] TmpMap; - delete [] neighborList; - delete [] phase; + int x = i - 1; + int y = j - 1; + int z = k - 1; + if (x < 0) + x = 0; + if (y < 0) + y = 0; + if (z < 0) + z = 0; + if (x >= Nx) + x = Nx - 1; + if (y >= Ny) + y = Ny - 1; + if (z >= Nz) + z = Nz - 1; + int n = z * Nx * Ny + y * Nx + x; + phase[nh] = id[n]; + } + } + } + ScaLBL_CopyToDevice(Phi, phase, Nh * sizeof(double)); + ScaLBL_Comm->Barrier(); + comm.barrier(); + delete[] TmpMap; + delete[] neighborList; + delete[] phase; } -void ScaLBL_FreeLeeModel::MGTest(){ +void ScaLBL_FreeLeeModel::MGTest() { - comm.barrier(); + comm.barrier(); - ScaLBL_Comm_WideHalo->Send(Phi); - ScaLBL_D3Q9_MGTest(dvcMap,Phi,ColorGrad,Nxh,Nxh*Nyh, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm_WideHalo->Recv(Phi); - ScaLBL_D3Q9_MGTest(dvcMap,Phi,ColorGrad,Nxh,Nxh*Nyh, 0, ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm_WideHalo->Send(Phi); + ScaLBL_D3Q9_MGTest(dvcMap, Phi, ColorGrad, Nxh, Nxh * Nyh, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm_WideHalo->Recv(Phi); + ScaLBL_D3Q9_MGTest(dvcMap, Phi, ColorGrad, Nxh, Nxh * Nyh, 0, + ScaLBL_Comm->LastExterior(), Np); //check the sum of ColorGrad double cgx_loc = 0.0; double cgy_loc = 0.0; double cgz_loc = 0.0; - double cgx,cgy,cgz; + double cgx, cgy, cgz; double *ColorGrad_host; - ColorGrad_host = new double [3*Np]; - ScaLBL_CopyToHost(&ColorGrad_host[0],&ColorGrad[0], 3*Np*sizeof(double)); - for (int i = ScaLBL_Comm->FirstInterior(); iLastInterior();i++){ - cgx_loc+=ColorGrad_host[0*Np+i]; - cgy_loc+=ColorGrad_host[1*Np+i]; - cgz_loc+=ColorGrad_host[2*Np+i]; + ColorGrad_host = new double[3 * Np]; + ScaLBL_CopyToHost(&ColorGrad_host[0], &ColorGrad[0], + 3 * Np * sizeof(double)); + for (int i = ScaLBL_Comm->FirstInterior(); i < ScaLBL_Comm->LastInterior(); + i++) { + cgx_loc += ColorGrad_host[0 * Np + i]; + cgy_loc += ColorGrad_host[1 * Np + i]; + cgz_loc += ColorGrad_host[2 * Np + i]; } - for (int i = 0; iLastExterior();i++){ - cgx_loc+=ColorGrad_host[0*Np+i]; - cgy_loc+=ColorGrad_host[1*Np+i]; - cgz_loc+=ColorGrad_host[2*Np+i]; + for (int i = 0; i < ScaLBL_Comm->LastExterior(); i++) { + cgx_loc += ColorGrad_host[0 * Np + i]; + cgy_loc += ColorGrad_host[1 * Np + i]; + cgz_loc += ColorGrad_host[2 * Np + i]; } - cgx=Dm->Comm.sumReduce( cgx_loc); - cgy=Dm->Comm.sumReduce( cgy_loc); - cgz=Dm->Comm.sumReduce( cgz_loc); - if (rank==0){ - printf("Sum of all x-component of the mixed gradient = %.2g \n",cgx); - printf("Sum of all y-component of the mixed gradient = %.2g \n",cgy); - printf("Sum of all z-component of the mixed gradient = %.2g \n",cgz); + cgx = Dm->Comm.sumReduce(cgx_loc); + cgy = Dm->Comm.sumReduce(cgy_loc); + cgz = Dm->Comm.sumReduce(cgz_loc); + if (rank == 0) { + printf("Sum of all x-component of the mixed gradient = %.2g \n", cgx); + printf("Sum of all y-component of the mixed gradient = %.2g \n", cgy); + printf("Sum of all z-component of the mixed gradient = %.2g \n", cgz); } - delete [] ColorGrad_host; + delete[] ColorGrad_host; } diff --git a/models/FreeLeeModel.h b/models/FreeLeeModel.h index 9eff0e59..3a690a3c 100644 --- a/models/FreeLeeModel.h +++ b/models/FreeLeeModel.h @@ -19,50 +19,50 @@ Implementation of Lee et al JCP 2016 lattice boltzmann model #ifndef ScaLBL_FreeLeeModel_INC #define ScaLBL_FreeLeeModel_INC -class ScaLBL_FreeLeeModel{ +class ScaLBL_FreeLeeModel { public: - ScaLBL_FreeLeeModel(int RANK, int NP, const Utilities::MPI& COMM); - ~ScaLBL_FreeLeeModel(); - - // functions in they should be run - void ReadParams(string filename); - void ReadParams(std::shared_ptr db0); - void SetDomain(); - void ReadInput(); - void Create_TwoFluid(); - void Initialize_TwoFluid(); - double Run_TwoFluid(int returntime); + ScaLBL_FreeLeeModel(int RANK, int NP, const Utilities::MPI &COMM); + ~ScaLBL_FreeLeeModel(); - void WriteDebug_TwoFluid(); - void Create_SingleFluid(); - void Initialize_SingleFluid(); - void Run_SingleFluid(); - - void WriteDebug_SingleFluid(); + // functions in they should be run + void ReadParams(string filename); + void ReadParams(std::shared_ptr db0); + void SetDomain(); + void ReadInput(); + void Create_TwoFluid(); + void Initialize_TwoFluid(); + double Run_TwoFluid(int returntime); + + void WriteDebug_TwoFluid(); + void Create_SingleFluid(); + void Initialize_SingleFluid(); + void Run_SingleFluid(); + + void WriteDebug_SingleFluid(); // test utilities void Create_DummyPhase_MGTest(); void MGTest(); - - bool Restart,pBC; - int timestep,timestepMax; - int BoundaryCondition; - double tauA,tauB,rhoA,rhoB; - double tau, rho0;//only for single-fluid Lee model - double tauM;//relaxation time for phase field (or mass) - double W,gamma,kappa,beta; - double Fx,Fy,Fz,flux; - double din,dout,inletA,inletB,outletA,outletB; - - int Nx,Ny,Nz,N,Np; - int Nxh,Nyh,Nzh,Nh; // extra halo width - int rank,nprocx,nprocy,nprocz,nprocs; - double Lx,Ly,Lz; - std::shared_ptr Dm; // this domain is for analysis - std::shared_ptr Mask; // this domain is for lbm - std::shared_ptr ScaLBL_Comm; - std::shared_ptr ScaLBL_Comm_Regular; - std::shared_ptr ScaLBL_Comm_WideHalo; + bool Restart, pBC; + int timestep, timestepMax; + int BoundaryCondition; + double tauA, tauB, rhoA, rhoB; + double tau, rho0; //only for single-fluid Lee model + double tauM; //relaxation time for phase field (or mass) + double W, gamma, kappa, beta; + double Fx, Fy, Fz, flux; + double din, dout, inletA, inletB, outletA, outletB; + + int Nx, Ny, Nz, N, Np; + int Nxh, Nyh, Nzh, Nh; // extra halo width + int rank, nprocx, nprocy, nprocz, nprocs; + double Lx, Ly, Lz; + + std::shared_ptr Dm; // this domain is for analysis + std::shared_ptr Mask; // this domain is for lbm + std::shared_ptr ScaLBL_Comm; + std::shared_ptr ScaLBL_Comm_Regular; + std::shared_ptr ScaLBL_Comm_WideHalo; // input database std::shared_ptr db; @@ -72,35 +72,34 @@ public: std::shared_ptr vis_db; IntArray Map; - signed char *id; - int *NeighborList; - int *dvcMap; - double *gqbar, *hq; - double *mu_phi, *Den, *Phi; - double *ColorGrad; - double *Velocity; - double *Pressure; - - void getPhase(DoubleArray &PhaseValues); - void getPotential(DoubleArray &PressureValues, DoubleArray &MuValues); - void getVelocity(DoubleArray &Vx, DoubleArray &Vy, DoubleArray &Vz); + signed char *id; + int *NeighborList; + int *dvcMap; + double *gqbar, *hq; + double *mu_phi, *Den, *Phi; + double *ColorGrad; + double *Velocity; + double *Pressure; + + void getPhase(DoubleArray &PhaseValues); + void getPotential(DoubleArray &PressureValues, DoubleArray &MuValues); + void getVelocity(DoubleArray &Vx, DoubleArray &Vy, DoubleArray &Vz); void getData_RegularLayout(const double *data, DoubleArray ®data); - - DoubleArray SignDist; - + + DoubleArray SignDist; + private: - Utilities::MPI comm; - - int dist_mem_size; - int neighborSize; - // filenames + Utilities::MPI comm; + + int dist_mem_size; + int neighborSize; + // filenames char LocalRankString[8]; char LocalRankFilename[40]; char LocalRestartFile[40]; - + //int rank,nprocs; void LoadParams(std::shared_ptr db0); - void AssignComponentLabels_ChemPotential_ColorGrad(); - + void AssignComponentLabels_ChemPotential_ColorGrad(); }; #endif diff --git a/models/GreyscaleColorModel.cpp b/models/GreyscaleColorModel.cpp index 94f64dab..1d05e00b 100644 --- a/models/GreyscaleColorModel.cpp +++ b/models/GreyscaleColorModel.cpp @@ -9,217 +9,229 @@ Two-fluid greyscale color lattice boltzmann model #include #include -template -void DeleteArray( const TYPE *p ) -{ - delete [] p; -} +template void DeleteArray(const TYPE *p) { delete[] p; } -ScaLBL_GreyscaleColorModel::ScaLBL_GreyscaleColorModel(int RANK, int NP, const Utilities::MPI& COMM): -rank(RANK), nprocs(NP), Restart(0),timestep(0),timestepMax(0),tauA(0),tauB(0),tauA_eff(0),tauB_eff(0),rhoA(0),rhoB(0),alpha(0),beta(0), -Fx(0),Fy(0),Fz(0),flux(0),din(0),dout(0),inletA(0),inletB(0),outletA(0),outletB(0),GreyPorosity(0),RecoloringOff(0), -Nx(0),Ny(0),Nz(0),N(0),Np(0),nprocx(0),nprocy(0),nprocz(0),BoundaryCondition(0),Lx(0),Ly(0),Lz(0),comm(COMM) -{ - REVERSE_FLOW_DIRECTION = false; +ScaLBL_GreyscaleColorModel::ScaLBL_GreyscaleColorModel( + int RANK, int NP, const Utilities::MPI &COMM) + : rank(RANK), nprocs(NP), Restart(0), timestep(0), timestepMax(0), tauA(0), + tauB(0), tauA_eff(0), tauB_eff(0), rhoA(0), rhoB(0), alpha(0), beta(0), + Fx(0), Fy(0), Fz(0), flux(0), din(0), dout(0), inletA(0), inletB(0), + outletA(0), outletB(0), GreyPorosity(0), RecoloringOff(0), Nx(0), Ny(0), + Nz(0), N(0), Np(0), nprocx(0), nprocy(0), nprocz(0), BoundaryCondition(0), + Lx(0), Ly(0), Lz(0), comm(COMM) { + REVERSE_FLOW_DIRECTION = false; } -ScaLBL_GreyscaleColorModel::~ScaLBL_GreyscaleColorModel(){ +ScaLBL_GreyscaleColorModel::~ScaLBL_GreyscaleColorModel() {} +void ScaLBL_GreyscaleColorModel::ReadParams(string filename) { + // read the input database + db = std::make_shared(filename); + domain_db = db->getDatabase("Domain"); + greyscaleColor_db = db->getDatabase("Color"); + analysis_db = db->getDatabase("Analysis"); + vis_db = db->getDatabase("Visualization"); -} -void ScaLBL_GreyscaleColorModel::ReadParams(string filename){ - // read the input database - db = std::make_shared( filename ); - domain_db = db->getDatabase( "Domain" ); - greyscaleColor_db = db->getDatabase( "Color" ); - analysis_db = db->getDatabase( "Analysis" ); - vis_db = db->getDatabase( "Visualization" ); - - // set defaults - timestepMax = 100000; - tauA = tauB = 1.0; - rhoA = rhoB = 1.0; - Fx = Fy = Fz = 0.0; - alpha=1e-3; - beta=0.95; - Restart=false; - din=dout=1.0; - flux=0.0; + // set defaults + timestepMax = 100000; + tauA = tauB = 1.0; + rhoA = rhoB = 1.0; + Fx = Fy = Fz = 0.0; + alpha = 1e-3; + beta = 0.95; + Restart = false; + din = dout = 1.0; + flux = 0.0; RecoloringOff = false; //W=1.0; - - // Color Model parameters - if (greyscaleColor_db->keyExists( "timestepMax" )){ - timestepMax = greyscaleColor_db->getScalar( "timestepMax" ); - } - if (greyscaleColor_db->keyExists( "tauA" )){ - tauA = greyscaleColor_db->getScalar( "tauA" ); - } - if (greyscaleColor_db->keyExists( "tauB" )){ - tauB = greyscaleColor_db->getScalar( "tauB" ); - } - tauA_eff = greyscaleColor_db->getWithDefault( "tauA_eff", tauA ); - tauB_eff = greyscaleColor_db->getWithDefault( "tauB_eff", tauB ); - if (greyscaleColor_db->keyExists( "rhoA" )){ - rhoA = greyscaleColor_db->getScalar( "rhoA" ); - } - if (greyscaleColor_db->keyExists( "rhoB" )){ - rhoB = greyscaleColor_db->getScalar( "rhoB" ); - } - if (greyscaleColor_db->keyExists( "F" )){ - Fx = greyscaleColor_db->getVector( "F" )[0]; - Fy = greyscaleColor_db->getVector( "F" )[1]; - Fz = greyscaleColor_db->getVector( "F" )[2]; - } - if (greyscaleColor_db->keyExists( "alpha" )){ - alpha = greyscaleColor_db->getScalar( "alpha" ); - } - if (greyscaleColor_db->keyExists( "beta" )){ - beta = greyscaleColor_db->getScalar( "beta" ); - } - if (greyscaleColor_db->keyExists( "Restart" )){ - Restart = greyscaleColor_db->getScalar( "Restart" ); - } - if (greyscaleColor_db->keyExists( "din" )){ - din = greyscaleColor_db->getScalar( "din" ); - } - if (greyscaleColor_db->keyExists( "dout" )){ - dout = greyscaleColor_db->getScalar( "dout" ); - } - if (greyscaleColor_db->keyExists( "flux" )){ - flux = greyscaleColor_db->getScalar( "flux" ); - } - if (greyscaleColor_db->keyExists( "RecoloringOff" )){ - RecoloringOff = greyscaleColor_db->getScalar( "RecoloringOff" ); - } - inletA=1.f; - inletB=0.f; - outletA=0.f; - outletB=1.f; - //if (BoundaryCondition==4) flux *= rhoA; // mass flux must adjust for density (see formulation for details) - BoundaryCondition = 0; - if (domain_db->keyExists( "BC" )){ - BoundaryCondition = domain_db->getScalar( "BC" ); - } - - // Override user-specified boundary condition for specific protocols - auto protocol = greyscaleColor_db->getWithDefault( "protocol", "none" ); - if (protocol == "seed water"){ - if (BoundaryCondition != 0 && BoundaryCondition != 5){ - BoundaryCondition = 0; - if (rank==0) printf("WARNING: protocol (seed water) supports only full periodic boundary condition \n"); - } - domain_db->putScalar( "BC", BoundaryCondition ); - } - else if (protocol == "open connected oil"){ - if (BoundaryCondition != 0 && BoundaryCondition != 5){ - BoundaryCondition = 0; - if (rank==0) printf("WARNING: protocol (open connected oil) supports only full periodic boundary condition \n"); - } - domain_db->putScalar( "BC", BoundaryCondition ); - } - else if (protocol == "shell aggregation"){ - if (BoundaryCondition != 0 && BoundaryCondition != 5){ - BoundaryCondition = 0; - if (rank==0) printf("WARNING: protocol (shell aggregation) supports only full periodic boundary condition \n"); - } - domain_db->putScalar( "BC", BoundaryCondition ); - } + // Color Model parameters + if (greyscaleColor_db->keyExists("timestepMax")) { + timestepMax = greyscaleColor_db->getScalar("timestepMax"); + } + if (greyscaleColor_db->keyExists("tauA")) { + tauA = greyscaleColor_db->getScalar("tauA"); + } + if (greyscaleColor_db->keyExists("tauB")) { + tauB = greyscaleColor_db->getScalar("tauB"); + } + tauA_eff = greyscaleColor_db->getWithDefault("tauA_eff", tauA); + tauB_eff = greyscaleColor_db->getWithDefault("tauB_eff", tauB); + if (greyscaleColor_db->keyExists("rhoA")) { + rhoA = greyscaleColor_db->getScalar("rhoA"); + } + if (greyscaleColor_db->keyExists("rhoB")) { + rhoB = greyscaleColor_db->getScalar("rhoB"); + } + if (greyscaleColor_db->keyExists("F")) { + Fx = greyscaleColor_db->getVector("F")[0]; + Fy = greyscaleColor_db->getVector("F")[1]; + Fz = greyscaleColor_db->getVector("F")[2]; + } + if (greyscaleColor_db->keyExists("alpha")) { + alpha = greyscaleColor_db->getScalar("alpha"); + } + if (greyscaleColor_db->keyExists("beta")) { + beta = greyscaleColor_db->getScalar("beta"); + } + if (greyscaleColor_db->keyExists("Restart")) { + Restart = greyscaleColor_db->getScalar("Restart"); + } + if (greyscaleColor_db->keyExists("din")) { + din = greyscaleColor_db->getScalar("din"); + } + if (greyscaleColor_db->keyExists("dout")) { + dout = greyscaleColor_db->getScalar("dout"); + } + if (greyscaleColor_db->keyExists("flux")) { + flux = greyscaleColor_db->getScalar("flux"); + } + if (greyscaleColor_db->keyExists("RecoloringOff")) { + RecoloringOff = greyscaleColor_db->getScalar("RecoloringOff"); + } + inletA = 1.f; + inletB = 0.f; + outletA = 0.f; + outletB = 1.f; + //if (BoundaryCondition==4) flux *= rhoA; // mass flux must adjust for density (see formulation for details) + + BoundaryCondition = 0; + if (domain_db->keyExists("BC")) { + BoundaryCondition = domain_db->getScalar("BC"); + } + + // Override user-specified boundary condition for specific protocols + auto protocol = + greyscaleColor_db->getWithDefault("protocol", "none"); + if (protocol == "seed water") { + if (BoundaryCondition != 0 && BoundaryCondition != 5) { + BoundaryCondition = 0; + if (rank == 0) + printf("WARNING: protocol (seed water) supports only full " + "periodic boundary condition \n"); + } + domain_db->putScalar("BC", BoundaryCondition); + } else if (protocol == "open connected oil") { + if (BoundaryCondition != 0 && BoundaryCondition != 5) { + BoundaryCondition = 0; + if (rank == 0) + printf("WARNING: protocol (open connected oil) supports only " + "full periodic boundary condition \n"); + } + domain_db->putScalar("BC", BoundaryCondition); + } else if (protocol == "shell aggregation") { + if (BoundaryCondition != 0 && BoundaryCondition != 5) { + BoundaryCondition = 0; + if (rank == 0) + printf("WARNING: protocol (shell aggregation) supports only " + "full periodic boundary condition \n"); + } + domain_db->putScalar("BC", BoundaryCondition); + } } -void ScaLBL_GreyscaleColorModel::SetDomain(){ - Dm = std::shared_ptr(new Domain(domain_db,comm)); // full domain for analysis - Mask = std::shared_ptr(new Domain(domain_db,comm)); // mask domain removes immobile phases - // domain parameters - Nx = Dm->Nx; - Ny = Dm->Ny; - Nz = Dm->Nz; - Lx = Dm->Lx; - Ly = Dm->Ly; - Lz = Dm->Lz; - N = Nx*Ny*Nz; - id = new signed char [N]; - for (int i=0; iid[i] = 1; // initialize this way - Averages = std::shared_ptr ( new GreyPhaseAnalysis(Dm) ); // TwoPhase analysis object - comm.barrier(); - Dm->CommInit(); - comm.barrier(); - // Read domain parameters - rank = Dm->rank(); - nprocx = Dm->nprocx(); - nprocy = Dm->nprocy(); - nprocz = Dm->nprocz(); +void ScaLBL_GreyscaleColorModel::SetDomain() { + Dm = std::shared_ptr( + new Domain(domain_db, comm)); // full domain for analysis + Mask = std::shared_ptr( + new Domain(domain_db, comm)); // mask domain removes immobile phases + // domain parameters + Nx = Dm->Nx; + Ny = Dm->Ny; + Nz = Dm->Nz; + Lx = Dm->Lx; + Ly = Dm->Ly; + Lz = Dm->Lz; + N = Nx * Ny * Nz; + id = new signed char[N]; + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = 1; // initialize this way + Averages = std::shared_ptr( + new GreyPhaseAnalysis(Dm)); // TwoPhase analysis object + comm.barrier(); + Dm->CommInit(); + comm.barrier(); + // Read domain parameters + rank = Dm->rank(); + nprocx = Dm->nprocx(); + nprocy = Dm->nprocy(); + nprocz = Dm->nprocz(); } -void ScaLBL_GreyscaleColorModel::ReadInput(){ - - sprintf(LocalRankString,"%05d",rank); - sprintf(LocalRankFilename,"%s%s","ID.",LocalRankString); - sprintf(LocalRestartFile,"%s%s","Restart.",LocalRankString); - - if (greyscaleColor_db->keyExists( "image_sequence" )){ - auto ImageList = greyscaleColor_db->getVector( "image_sequence"); - int IMAGE_INDEX = greyscaleColor_db->getWithDefault( "image_index", 0 ); - std::string first_image = ImageList[IMAGE_INDEX]; - Mask->Decomp(first_image); - IMAGE_INDEX++; - } - else if (domain_db->keyExists( "GridFile" )){ +void ScaLBL_GreyscaleColorModel::ReadInput() { + + sprintf(LocalRankString, "%05d", rank); + sprintf(LocalRankFilename, "%s%s", "ID.", LocalRankString); + sprintf(LocalRestartFile, "%s%s", "Restart.", LocalRankString); + + if (greyscaleColor_db->keyExists("image_sequence")) { + auto ImageList = + greyscaleColor_db->getVector("image_sequence"); + int IMAGE_INDEX = + greyscaleColor_db->getWithDefault("image_index", 0); + std::string first_image = ImageList[IMAGE_INDEX]; + Mask->Decomp(first_image); + IMAGE_INDEX++; + } else if (domain_db->keyExists("GridFile")) { // Read the local domain data - auto input_id = readMicroCT( *domain_db, MPI_COMM_WORLD ); + auto input_id = readMicroCT(*domain_db, MPI_COMM_WORLD); // Fill the halo (assuming GCW of 1) - array size0 = { (int) input_id.size(0), (int) input_id.size(1), (int) input_id.size(2) }; - ArraySize size1 = { (size_t) Mask->Nx, (size_t) Mask->Ny, (size_t) Mask->Nz }; - ASSERT( (int) size1[0] == size0[0]+2 && (int) size1[1] == size0[1]+2 && (int) size1[2] == size0[2]+2 ); - fillHalo fill( MPI_COMM_WORLD, Mask->rank_info, size0, { 1, 1, 1 }, 0, 1 ); + array size0 = {(int)input_id.size(0), (int)input_id.size(1), + (int)input_id.size(2)}; + ArraySize size1 = {(size_t)Mask->Nx, (size_t)Mask->Ny, + (size_t)Mask->Nz}; + ASSERT((int)size1[0] == size0[0] + 2 && (int)size1[1] == size0[1] + 2 && + (int)size1[2] == size0[2] + 2); + fillHalo fill(MPI_COMM_WORLD, Mask->rank_info, size0, + {1, 1, 1}, 0, 1); Array id_view; - id_view.viewRaw( size1, Mask->id.data()); - fill.copy( input_id, id_view ); - fill.fill( id_view ); - } - else if (domain_db->keyExists( "Filename" )){ - auto Filename = domain_db->getScalar( "Filename" ); - Mask->Decomp(Filename); - } - else{ - Mask->ReadIDs(); - } - for (int i=0; iid[i]; // save what was read - - // Generate the signed distance map - // Initialize the domain and communication - Array id_solid(Nx,Ny,Nz); - // Solve for the position of the solid phase - for (int k=0;kid[n]; - if (label > 0) id_solid(i,j,k) = 1; - else id_solid(i,j,k) = 0; - } - } - } - // Initialize the signed distance function - for (int k=0;kSDs(i,j,k) = 2.0*double(id_solid(i,j,k))-1.0; - } - } - } -// MeanFilter(Averages->SDs); - if (rank==0) printf("Initialized solid phase -- Converting to Signed Distance function \n"); - CalcDist(Averages->SDs,id_solid,*Mask); - - if (rank == 0) cout << "Domain set." << endl; - + id_view.viewRaw(size1, Mask->id.data()); + fill.copy(input_id, id_view); + fill.fill(id_view); + } else if (domain_db->keyExists("Filename")) { + auto Filename = domain_db->getScalar("Filename"); + Mask->Decomp(Filename); + } else { + Mask->ReadIDs(); + } + for (int i = 0; i < Nx * Ny * Nz; i++) + id[i] = Mask->id[i]; // save what was read + + // Generate the signed distance map + // Initialize the domain and communication + Array id_solid(Nx, Ny, Nz); + // Solve for the position of the solid phase + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + // Initialize the solid phase + signed char label = Mask->id[n]; + if (label > 0) + id_solid(i, j, k) = 1; + else + id_solid(i, j, k) = 0; + } + } + } + // Initialize the signed distance function + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + // Initialize distance to +/- 1 + Averages->SDs(i, j, k) = 2.0 * double(id_solid(i, j, k)) - 1.0; + } + } + } + // MeanFilter(Averages->SDs); + if (rank == 0) + printf("Initialized solid phase -- Converting to Signed Distance " + "function \n"); + CalcDist(Averages->SDs, id_solid, *Mask); + + if (rank == 0) + cout << "Domain set." << endl; } - -void ScaLBL_GreyscaleColorModel::AssignComponentLabels() -{ +void ScaLBL_GreyscaleColorModel::AssignComponentLabels() { // Initialize impermeability solid nodes and grey nodes // Key input parameters: // 1. ComponentLabels @@ -231,75 +243,85 @@ void ScaLBL_GreyscaleColorModel::AssignComponentLabels() // (1) zero and negative integers are for impermeability minerals // (2) positive integers > 2 are for grey nodes // (3) label = 1 and 2 are always conserved for open node of non-wetting and wetting phase, respectively. - double *phase; - phase = new double[N]; + double *phase; + phase = new double[N]; - size_t NLABELS=0; - signed char VALUE=0; - double AFFINITY=0.f; + size_t NLABELS = 0; + signed char VALUE = 0; + double AFFINITY = 0.f; - auto LabelList = greyscaleColor_db->getVector( "ComponentLabels" ); - auto AffinityList = greyscaleColor_db->getVector( "ComponentAffinity" ); + auto LabelList = greyscaleColor_db->getVector("ComponentLabels"); + auto AffinityList = + greyscaleColor_db->getVector("ComponentAffinity"); - NLABELS=LabelList.size(); - if (NLABELS != AffinityList.size()){ - ERROR("Error: ComponentLabels and ComponentAffinity must be the same length! \n"); - } + NLABELS = LabelList.size(); + if (NLABELS != AffinityList.size()) { + ERROR("Error: ComponentLabels and ComponentAffinity must be the same " + "length! \n"); + } - double * label_count; - double *label_count_global; - label_count = new double [NLABELS]; - label_count_global = new double [NLABELS]; - // Assign the labels + double *label_count; + double *label_count_global; + label_count = new double[NLABELS]; + label_count_global = new double[NLABELS]; + // Assign the labels - for (size_t idx=0; idxid[n] = 0; // set mask to zero since this is an immobile component - } - } - // fluid labels are reserved - if (VALUE == 1) AFFINITY=1.0; - else if (VALUE == 2) AFFINITY=-1.0; - phase[n] = AFFINITY; - } - } - } + label_count[idx] += 1.0; + idx = NLABELS; + //Mask->id[n] = 0; // set mask to zero since this is an immobile component + } + } + // fluid labels are reserved + if (VALUE == 1) + AFFINITY = 1.0; + else if (VALUE == 2) + AFFINITY = -1.0; + phase[n] = AFFINITY; + } + } + } - // Set Dm to match Mask - for (int i=0; iid[i] = Mask->id[i]; - - for (size_t idx=0; idxComm.sumReduce( label_count[idx]); + // Set Dm to match Mask + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = Mask->id[i]; - if (rank==0){ - printf("Number of component labels: %lu \n",NLABELS); - for (unsigned int idx=0; idxComm.sumReduce(label_count[idx]); - ScaLBL_CopyToDevice(Phi, phase, N*sizeof(double)); - ScaLBL_Comm->Barrier(); - delete [] phase; + if (rank == 0) { + printf("Number of component labels: %lu \n", NLABELS); + for (unsigned int idx = 0; idx < NLABELS; idx++) { + VALUE = LabelList[idx]; + AFFINITY = AffinityList[idx]; + double volume_fraction = + double(label_count_global[idx]) / + double((Nx - 2) * (Ny - 2) * (Nz - 2) * nprocs); + printf(" label=%d, affinity=%f, volume fraction==%f\n", VALUE, + AFFINITY, volume_fraction); + } + } + + ScaLBL_CopyToDevice(Phi, phase, N * sizeof(double)); + ScaLBL_Comm->Barrier(); + delete[] phase; } -void ScaLBL_GreyscaleColorModel::AssignGreySolidLabels()//apply capillary penalty wetting strength W +void ScaLBL_GreyscaleColorModel:: + AssignGreySolidLabels() //apply capillary penalty wetting strength W { // ONLY initialize grey nodes // Key input parameters: @@ -309,1172 +331,1320 @@ void ScaLBL_GreyscaleColorModel::AssignGreySolidLabels()//apply capillary penalt // ranges [-1,1] // water-wet > 0 // oil-wet < 0 - // neutral = 0 (i.e. no penalty) - double *GreySolidW_host = new double [Np]; - double *GreySn_host = new double [Np]; - double *GreySw_host = new double [Np]; - double *GreyKn_host = new double [Np]; - double *GreyKw_host = new double [Np]; - - size_t NLABELS=0; - signed char VALUE=0; - double AFFINITY=0.f; - double Sn,Sw;//end-point saturation of greynodes set by users - double Kn,Kw; // endpoint effective permeability + // neutral = 0 (i.e. no penalty) + double *GreySolidW_host = new double[Np]; + double *GreySn_host = new double[Np]; + double *GreySw_host = new double[Np]; + double *GreyKn_host = new double[Np]; + double *GreyKw_host = new double[Np]; - auto LabelList = greyscaleColor_db->getVector( "GreySolidLabels" ); - auto AffinityList = greyscaleColor_db->getVector( "GreySolidAffinity" ); - auto SnList = greyscaleColor_db->getVector( "grey_endpoint_A" ); - auto SwList = greyscaleColor_db->getVector( "grey_endpoint_B" ); - auto KnList = greyscaleColor_db->getVector( "grey_endpoint_permeability_A" ); - auto KwList = greyscaleColor_db->getVector( "grey_endpoint_permeability_B" ); - - NLABELS=LabelList.size(); - if (NLABELS != AffinityList.size()){ - ERROR("Error: GreySolidLabels and GreySolidAffinity must be the same length! \n"); - } - if (NLABELS != SnList.size() || NLABELS != SwList.size()){ - ERROR("Error: GreySolidLabels, grey_endpoint_A, and grey_endpoint_B must be the same length! \n"); - } - if (NLABELS != KnList.size() || NLABELS != KwList.size()){ - ERROR("Error: GreySolidLabels, grey_endpoint_permeability_A, and grey_endpoint_permeability_B must be the same length! \n"); - } + size_t NLABELS = 0; + signed char VALUE = 0; + double AFFINITY = 0.f; + double Sn, Sw; //end-point saturation of greynodes set by users + double Kn, Kw; // endpoint effective permeability - for (int k=0;kgetVector("GreySolidLabels"); + auto AffinityList = + greyscaleColor_db->getVector("GreySolidAffinity"); + auto SnList = greyscaleColor_db->getVector("grey_endpoint_A"); + auto SwList = greyscaleColor_db->getVector("grey_endpoint_B"); + auto KnList = + greyscaleColor_db->getVector("grey_endpoint_permeability_A"); + auto KwList = + greyscaleColor_db->getVector("grey_endpoint_permeability_B"); + + NLABELS = LabelList.size(); + if (NLABELS != AffinityList.size()) { + ERROR("Error: GreySolidLabels and GreySolidAffinity must be the same " + "length! \n"); + } + if (NLABELS != SnList.size() || NLABELS != SwList.size()) { + ERROR("Error: GreySolidLabels, grey_endpoint_A, and grey_endpoint_B " + "must be the same length! \n"); + } + if (NLABELS != KnList.size() || NLABELS != KwList.size()) { + ERROR("Error: GreySolidLabels, grey_endpoint_permeability_A, and " + "grey_endpoint_permeability_B must be the same length! \n"); + } + + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + VALUE = id[n]; + AFFINITY = + 0.f; //all nodes except the specified grey nodes have grey-solid affinity = 0.0 + Sn = 99.0; + Sw = -99.0; Kn = 0.0; Kw = 0.0; - // Assign the affinity from the paired list - for (unsigned int idx=0; idx < NLABELS; idx++){ - if (VALUE == LabelList[idx]){ - AFFINITY=AffinityList[idx]; + // Assign the affinity from the paired list + for (unsigned int idx = 0; idx < NLABELS; idx++) { + if (VALUE == LabelList[idx]) { + AFFINITY = AffinityList[idx]; Sn = SnList[idx]; Sw = SwList[idx]; Kn = KnList[idx]; Kw = KwList[idx]; - idx = NLABELS; - } - } - int idx = Map(i,j,k); - if (!(idx < 0)){ - GreySolidW_host[idx] = AFFINITY; - GreySn_host[idx] = Sn; - GreySw_host[idx] = Sw; - GreyKn_host[idx] = Kn; - GreyKw_host[idx] = Kw; + idx = NLABELS; + } } - } - } - } + int idx = Map(i, j, k); + if (!(idx < 0)) { + GreySolidW_host[idx] = AFFINITY; + GreySn_host[idx] = Sn; + GreySw_host[idx] = Sw; + GreyKn_host[idx] = Kn; + GreyKw_host[idx] = Kw; + } + } + } + } - if (rank==0){ - printf("Number of Grey-solid labels: %lu \n",NLABELS); - for (unsigned int idx=0; idx0: water-wet || grey-solid affinity<0: oil-wet \n"); - } - - - ScaLBL_CopyToDevice(GreySolidW, GreySolidW_host, Np*sizeof(double)); - ScaLBL_CopyToDevice(GreySn, GreySn_host, Np*sizeof(double)); - ScaLBL_CopyToDevice(GreySw, GreySw_host, Np*sizeof(double)); - ScaLBL_CopyToDevice(GreyKn, GreySn_host, Np*sizeof(double)); - ScaLBL_CopyToDevice(GreyKw, GreySw_host, Np*sizeof(double)); - ScaLBL_Comm->Barrier(); - delete [] GreySolidW_host; - delete [] GreySn_host; - delete [] GreySw_host; + if (rank == 0) { + printf("Number of Grey-solid labels: %lu \n", NLABELS); + for (unsigned int idx = 0; idx < NLABELS; idx++) { + VALUE = LabelList[idx]; + AFFINITY = AffinityList[idx]; + Sn = SnList[idx]; + Sw = SwList[idx]; + //printf(" grey-solid label=%d, grey-solid affinity=%f\n",VALUE,AFFINITY); + printf(" grey-solid label=%d, grey-solid affinity=%.3g, " + "grey-solid Sn=%.3g, grey-solid Sw=%.3g\n", + VALUE, AFFINITY, Sn, Sw); + } + printf("NOTE: grey-solid affinity>0: water-wet || grey-solid " + "affinity<0: oil-wet \n"); + } + + ScaLBL_CopyToDevice(GreySolidW, GreySolidW_host, Np * sizeof(double)); + ScaLBL_CopyToDevice(GreySn, GreySn_host, Np * sizeof(double)); + ScaLBL_CopyToDevice(GreySw, GreySw_host, Np * sizeof(double)); + ScaLBL_CopyToDevice(GreyKn, GreySn_host, Np * sizeof(double)); + ScaLBL_CopyToDevice(GreyKw, GreySw_host, Np * sizeof(double)); + ScaLBL_Comm->Barrier(); + delete[] GreySolidW_host; + delete[] GreySn_host; + delete[] GreySw_host; } ////----------------------------------------------------------------------------------------------------------// -void ScaLBL_GreyscaleColorModel::AssignGreyPoroPermLabels() -{ +void ScaLBL_GreyscaleColorModel::AssignGreyPoroPermLabels() { - double *Porosity, *Permeability; - Porosity = new double[Np]; - Permeability = new double[Np]; + double *Porosity, *Permeability; + Porosity = new double[Np]; + Permeability = new double[Np]; - size_t NLABELS=0; - signed char VALUE=0; - double POROSITY=1.f;//default: label 1 or 2, i.e. open nodes and porosity=1.0 - double PERMEABILITY=1.f; + size_t NLABELS = 0; + signed char VALUE = 0; + double POROSITY = + 1.f; //default: label 1 or 2, i.e. open nodes and porosity=1.0 + double PERMEABILITY = 1.f; - auto LabelList = greyscaleColor_db->getVector( "GreySolidLabels" ); - auto PorosityList = greyscaleColor_db->getVector( "PorosityList" ); - auto PermeabilityList = greyscaleColor_db->getVector( "PermeabilityList" ); + auto LabelList = greyscaleColor_db->getVector("GreySolidLabels"); + auto PorosityList = greyscaleColor_db->getVector("PorosityList"); + auto PermeabilityList = + greyscaleColor_db->getVector("PermeabilityList"); - NLABELS=LabelList.size(); - if (LabelList.size() != PorosityList.size()){ - ERROR("Error: GreySolidLabels and PorosityList must be the same length! \n"); - } - - double * label_count; - double * label_count_global; - label_count = new double [NLABELS]; - label_count_global = new double [NLABELS]; - // Assign the labels - - for (size_t idx=0; idxid[n] = 0; // set mask to zero since this is an immobile component - } - } - int idx = Map(i,j,k); - if (!(idx < 0)){ - if (POROSITY<=0.0){ - ERROR("Error: Porosity for grey voxels must be 0.0 < Porosity <= 1.0 !\n"); - } - else{ - Porosity[idx] = POROSITY; - } - } - } - } - } - - if (NLABELS != PermeabilityList.size()){ - ERROR("Error: GreySolidLabels and PermeabilityList must be the same length! \n"); - } - for (int k=0;kid[n] = 0; // set mask to zero since this is an immobile component - } - } - int idx = Map(i,j,k); - if (!(idx < 0)){ - if (PERMEABILITY<=0.0){ - ERROR("Error: Permeability for grey voxel must be > 0.0 ! \n"); - } - else{ - Permeability[idx] = PERMEABILITY/Dm->voxel_length/Dm->voxel_length; - } - } - } - } - } - - - // Set Dm to match Mask - for (int i=0; iid[i] = Mask->id[i]; - - for (size_t idx=0; idxComm.sumReduce( label_count[idx]); - - //Initialize a weighted porosity after considering grey voxels - GreyPorosity=0.0; - for (unsigned int idx=0; idxvoxel_length); - printf("Number of Grey-fluid labels: %lu \n",NLABELS); - for (unsigned int idx=0; idxvoxel_length/Dm->voxel_length,volume_fraction); - printf(" effective porosity=%.3g\n",volume_fraction*POROSITY); - } - printf("The weighted porosity, considering both open and grey voxels, is %.3g\n",GreyPorosity); - } + double *label_count; + double *label_count_global; + label_count = new double[NLABELS]; + label_count_global = new double[NLABELS]; + // Assign the labels - ScaLBL_CopyToDevice(Porosity_dvc, Porosity, Np*sizeof(double)); - ScaLBL_CopyToDevice(Permeability_dvc, Permeability, Np*sizeof(double)); - ScaLBL_Comm->Barrier(); - delete [] Porosity; - delete [] Permeability; + for (size_t idx = 0; idx < NLABELS; idx++) + label_count[idx] = 0; + + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + VALUE = id[n]; + POROSITY = + 1.f; //default: label 1 or 2, i.e. open nodes and porosity=1.0 + // Assign the affinity from the paired list + for (size_t idx = 0; idx < NLABELS; idx++) { + //printf("idx=%i, value=%i, %i, \n",idx, VALUE,LabelList[idx]); + if (VALUE == LabelList[idx]) { + POROSITY = PorosityList[idx]; + label_count[idx] += 1.0; + idx = NLABELS; + //Mask->id[n] = 0; // set mask to zero since this is an immobile component + } + } + int idx = Map(i, j, k); + if (!(idx < 0)) { + if (POROSITY <= 0.0) { + ERROR("Error: Porosity for grey voxels must be 0.0 < " + "Porosity <= 1.0 !\n"); + } else { + Porosity[idx] = POROSITY; + } + } + } + } + } + + if (NLABELS != PermeabilityList.size()) { + ERROR("Error: GreySolidLabels and PermeabilityList must be the same " + "length! \n"); + } + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + VALUE = id[n]; + PERMEABILITY = 1.f; + // Assign the affinity from the paired list + for (unsigned int idx = 0; idx < NLABELS; idx++) { + //printf("idx=%i, value=%i, %i, \n",idx, VALUE,LabelList[idx]); + if (VALUE == LabelList[idx]) { + PERMEABILITY = PermeabilityList[idx]; + idx = NLABELS; + //Mask->id[n] = 0; // set mask to zero since this is an immobile component + } + } + int idx = Map(i, j, k); + if (!(idx < 0)) { + if (PERMEABILITY <= 0.0) { + ERROR("Error: Permeability for grey voxel must be > " + "0.0 ! \n"); + } else { + Permeability[idx] = + PERMEABILITY / Dm->voxel_length / Dm->voxel_length; + } + } + } + } + } + + // Set Dm to match Mask + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = Mask->id[i]; + + for (size_t idx = 0; idx < NLABELS; idx++) + label_count_global[idx] = Dm->Comm.sumReduce(label_count[idx]); + + //Initialize a weighted porosity after considering grey voxels + GreyPorosity = 0.0; + for (unsigned int idx = 0; idx < NLABELS; idx++) { + double volume_fraction = + double(label_count_global[idx]) / + double((Nx - 2) * (Ny - 2) * (Nz - 2) * nprocs); + GreyPorosity += volume_fraction * PorosityList[idx]; + } + + if (rank == 0) { + printf("Image resolution: %.5g [um/voxel]\n", Dm->voxel_length); + printf("Number of Grey-fluid labels: %lu \n", NLABELS); + for (unsigned int idx = 0; idx < NLABELS; idx++) { + VALUE = LabelList[idx]; + POROSITY = PorosityList[idx]; + PERMEABILITY = PermeabilityList[idx]; + double volume_fraction = + double(label_count_global[idx]) / + double((Nx - 2) * (Ny - 2) * (Nz - 2) * nprocs); + printf(" grey-fluid label=%d, porosity=%.3g, permeability=%.3g " + "[um^2] (=%.3g [voxel^2]), volume fraction=%.3g\n", + VALUE, POROSITY, PERMEABILITY, + PERMEABILITY / Dm->voxel_length / Dm->voxel_length, + volume_fraction); + printf(" effective porosity=%.3g\n", + volume_fraction * POROSITY); + } + printf("The weighted porosity, considering both open and grey voxels, " + "is %.3g\n", + GreyPorosity); + } + + ScaLBL_CopyToDevice(Porosity_dvc, Porosity, Np * sizeof(double)); + ScaLBL_CopyToDevice(Permeability_dvc, Permeability, Np * sizeof(double)); + ScaLBL_Comm->Barrier(); + delete[] Porosity; + delete[] Permeability; } -void ScaLBL_GreyscaleColorModel::Create(){ - /* +void ScaLBL_GreyscaleColorModel::Create() { + /* * This function creates the variables needed to run a LBM */ - //......................................................... - // don't perform computations at the eight corners - //id[0] = id[Nx-1] = id[(Ny-1)*Nx] = id[(Ny-1)*Nx + Nx-1] = 0; - //id[(Nz-1)*Nx*Ny] = id[(Nz-1)*Nx*Ny+Nx-1] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx + Nx-1] = 0; + //......................................................... + // don't perform computations at the eight corners + //id[0] = id[Nx-1] = id[(Ny-1)*Nx] = id[(Ny-1)*Nx + Nx-1] = 0; + //id[(Nz-1)*Nx*Ny] = id[(Nz-1)*Nx*Ny+Nx-1] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx + Nx-1] = 0; - //......................................................... - // Initialize communication structures in averaging domain - for (int i=0; iid[i] = Mask->id[i]; - Mask->CommInit(); - Np=Mask->PoreCount(); - //........................................................................... - if (rank==0) printf ("Create ScaLBL_Communicator \n"); - // Create a communicator for the device (will use optimized layout) - // ScaLBL_Communicator ScaLBL_Comm(Mask); // original - ScaLBL_Comm = std::shared_ptr(new ScaLBL_Communicator(Mask)); - ScaLBL_Comm_Regular = std::shared_ptr(new ScaLBL_Communicator(Mask)); + //......................................................... + // Initialize communication structures in averaging domain + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = Mask->id[i]; + Mask->CommInit(); + Np = Mask->PoreCount(); + //........................................................................... + if (rank == 0) + printf("Create ScaLBL_Communicator \n"); + // Create a communicator for the device (will use optimized layout) + // ScaLBL_Communicator ScaLBL_Comm(Mask); // original + ScaLBL_Comm = + std::shared_ptr(new ScaLBL_Communicator(Mask)); + ScaLBL_Comm_Regular = + std::shared_ptr(new ScaLBL_Communicator(Mask)); - int Npad=(Np/16 + 2)*16; - if (rank==0) printf ("Set up memory efficient layout, %i | %i | %i \n", Np, Npad, N); - Map.resize(Nx,Ny,Nz); Map.fill(-2); - auto neighborList= new int[18*Npad]; - Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map,neighborList,Mask->id.data(),Np,1); - comm.barrier(); + int Npad = (Np / 16 + 2) * 16; + if (rank == 0) + printf("Set up memory efficient layout, %i | %i | %i \n", Np, Npad, N); + Map.resize(Nx, Ny, Nz); + Map.fill(-2); + auto neighborList = new int[18 * Npad]; + Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map, neighborList, + Mask->id.data(), Np, 1); + comm.barrier(); - //........................................................................... - // MAIN VARIABLES ALLOCATED HERE - //........................................................................... - // LBM variables - if (rank==0) printf ("Allocating distributions \n"); - //......................device distributions................................. - dist_mem_size = Np*sizeof(double); - neighborSize=18*(Np*sizeof(int)); - //........................................................................... - ScaLBL_AllocateDeviceMemory((void **) &NeighborList, neighborSize); - ScaLBL_AllocateDeviceMemory((void **) &dvcMap, sizeof(int)*Np); - ScaLBL_AllocateDeviceMemory((void **) &fq, 19*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Aq, 7*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Bq, 7*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Den, 2*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Phi, sizeof(double)*Nx*Ny*Nz); - //ScaLBL_AllocateDeviceMemory((void **) &Psi, sizeof(double)*Nx*Ny*Nz);//greyscale potential - ScaLBL_AllocateDeviceMemory((void **) &Pressure, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &Velocity, 3*sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &MobilityRatio, sizeof(double)*Np); - //ScaLBL_AllocateDeviceMemory((void **) &GreySolidPhi, sizeof(double)*Nx*Ny*Nz); - //ScaLBL_AllocateDeviceMemory((void **) &GreySolidGrad, 3*sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &GreySolidW, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &GreySn, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &GreySw, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &GreyKn, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &GreyKw, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &Porosity_dvc, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &Permeability_dvc, sizeof(double)*Np); - //........................................................................... - // Update GPU data structures - if (rank==0) printf ("Setting up device map and neighbor list \n"); - fflush(stdout); - int *TmpMap; - TmpMap=new int[Np]; - for (int k=1; kLastExterior(); idx++){ - auto n = TmpMap[idx]; - if (n > Nx*Ny*Nz){ - printf("Bad value! idx=%i \n", n); - TmpMap[idx] = Nx*Ny*Nz-1; - } - } - for (int idx=ScaLBL_Comm->FirstInterior(); idxLastInterior(); idx++){ - auto n = TmpMap[idx]; - if ( n > Nx*Ny*Nz ){ - printf("Bad value! idx=%i \n",n); - TmpMap[idx] = Nx*Ny*Nz-1; - } - } - ScaLBL_CopyToDevice(dvcMap, TmpMap, sizeof(int)*Np); - ScaLBL_Comm->Barrier(); - delete [] TmpMap; - - // copy the neighbor list - ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); + //........................................................................... + // MAIN VARIABLES ALLOCATED HERE + //........................................................................... + // LBM variables + if (rank == 0) + printf("Allocating distributions \n"); + //......................device distributions................................. + dist_mem_size = Np * sizeof(double); + neighborSize = 18 * (Np * sizeof(int)); + //........................................................................... + ScaLBL_AllocateDeviceMemory((void **)&NeighborList, neighborSize); + ScaLBL_AllocateDeviceMemory((void **)&dvcMap, sizeof(int) * Np); + ScaLBL_AllocateDeviceMemory((void **)&fq, 19 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Aq, 7 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Bq, 7 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Den, 2 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Phi, sizeof(double) * Nx * Ny * Nz); + //ScaLBL_AllocateDeviceMemory((void **) &Psi, sizeof(double)*Nx*Ny*Nz);//greyscale potential + ScaLBL_AllocateDeviceMemory((void **)&Pressure, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&Velocity, 3 * sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&MobilityRatio, sizeof(double) * Np); + //ScaLBL_AllocateDeviceMemory((void **) &GreySolidPhi, sizeof(double)*Nx*Ny*Nz); + //ScaLBL_AllocateDeviceMemory((void **) &GreySolidGrad, 3*sizeof(double)*Np); + ScaLBL_AllocateDeviceMemory((void **)&GreySolidW, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&GreySn, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&GreySw, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&GreyKn, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&GreyKw, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&Porosity_dvc, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&Permeability_dvc, + sizeof(double) * Np); + //........................................................................... + // Update GPU data structures + if (rank == 0) + printf("Setting up device map and neighbor list \n"); + fflush(stdout); + int *TmpMap; + TmpMap = new int[Np]; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + int idx = Map(i, j, k); + if (!(idx < 0)) + TmpMap[idx] = k * Nx * Ny + j * Nx + i; + } + } + } + // check that TmpMap is valid + for (int idx = 0; idx < ScaLBL_Comm->LastExterior(); idx++) { + auto n = TmpMap[idx]; + if (n > Nx * Ny * Nz) { + printf("Bad value! idx=%i \n", n); + TmpMap[idx] = Nx * Ny * Nz - 1; + } + } + for (int idx = ScaLBL_Comm->FirstInterior(); + idx < ScaLBL_Comm->LastInterior(); idx++) { + auto n = TmpMap[idx]; + if (n > Nx * Ny * Nz) { + printf("Bad value! idx=%i \n", n); + TmpMap[idx] = Nx * Ny * Nz - 1; + } + } + ScaLBL_CopyToDevice(dvcMap, TmpMap, sizeof(int) * Np); + ScaLBL_Comm->Barrier(); + delete[] TmpMap; - // initialize phi based on PhaseLabel (include solid component labels) - AssignComponentLabels();//do open/black/grey nodes initialization + // copy the neighbor list + ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); + + // initialize phi based on PhaseLabel (include solid component labels) + AssignComponentLabels(); //do open/black/grey nodes initialization AssignGreySolidLabels(); - AssignGreyPoroPermLabels(); - Averages->SetParams(rhoA,rhoB,tauA,tauB,Fx,Fy,Fz,alpha,beta,GreyPorosity); - ScaLBL_Comm->RegularLayout(Map,Porosity_dvc,Averages->Porosity);//porosity doesn't change over time -} - -void ScaLBL_GreyscaleColorModel::Initialize(){ - /* - * This function initializes model - */ - if (rank==0) printf ("Initializing distributions \n"); - ScaLBL_D3Q19_Init(fq, Np); - //ScaLBL_D3Q19_GreyscaleColor_Init(fq, Porosity_dvc, Np); - - if (rank==0) printf ("Initializing phase field \n"); - ScaLBL_PhaseField_Init(dvcMap, Phi, Den, Aq, Bq, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_PhaseField_Init(dvcMap, Phi, Den, Aq, Bq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - - if (Restart == true){ - if (rank==0){ - printf("Reading restart file! \n"); - } - - // Read in the restart file to CPU buffers - int *TmpMap; - TmpMap = new int[Np]; - - double *cPhi, *cDist, *cDen; - cPhi = new double[N]; - cDen = new double[2*Np]; - cDist = new double[19*Np]; - ScaLBL_CopyToHost(TmpMap, dvcMap, Np*sizeof(int)); - ScaLBL_CopyToHost(cPhi, Phi, N*sizeof(double)); - - ifstream File(LocalRestartFile,ios::binary); - int idx; - double value,va,vb; - for (int n=0; nLastExterior(); n++){ - va = cDen[n]; - vb = cDen[Np + n]; - value = (va-vb)/(va+vb); - idx = TmpMap[n]; - if (!(idx < 0) && idxFirstInterior(); nLastInterior(); n++){ - va = cDen[n]; - vb = cDen[Np + n]; - value = (va-vb)/(va+vb); - idx = TmpMap[n]; - if (!(idx < 0) && idxBarrier(); - - comm.barrier(); - - if (rank==0) printf ("Initializing phase field from Restart\n"); - ScaLBL_PhaseField_InitFromRestart(Den, Aq, Bq, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_PhaseField_InitFromRestart(Den, Aq, Bq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - } - - // establish reservoirs for external bC - if (BoundaryCondition == 1 || BoundaryCondition == 2 || BoundaryCondition == 3 || BoundaryCondition == 4 ){ - if (Dm->kproc()==0){ - ScaLBL_SetSlice_z(Phi,1.0,Nx,Ny,Nz,0); - ScaLBL_SetSlice_z(Phi,1.0,Nx,Ny,Nz,1); - ScaLBL_SetSlice_z(Phi,1.0,Nx,Ny,Nz,2); - } - if (Dm->kproc() == nprocz-1){ - ScaLBL_SetSlice_z(Phi,-1.0,Nx,Ny,Nz,Nz-1); - ScaLBL_SetSlice_z(Phi,-1.0,Nx,Ny,Nz,Nz-2); - ScaLBL_SetSlice_z(Phi,-1.0,Nx,Ny,Nz,Nz-3); - } - } - //ScaLBL_CopyToHost(Averages->Phi.data(),Phi,N*sizeof(double)); + AssignGreyPoroPermLabels(); + Averages->SetParams(rhoA, rhoB, tauA, tauB, Fx, Fy, Fz, alpha, beta, + GreyPorosity); + ScaLBL_Comm->RegularLayout( + Map, Porosity_dvc, + Averages->Porosity); //porosity doesn't change over time } -void ScaLBL_GreyscaleColorModel::Run(){ - int nprocs=nprocx*nprocy*nprocz; - const RankInfoStruct rank_info(rank,nprocx,nprocy,nprocz); - - bool SET_CAPILLARY_NUMBER = false; - bool RESCALE_FORCE = false; - bool MORPH_ADAPT = false; - bool USE_MORPH = false; - bool USE_SEED = false; - bool USE_DIRECT = false; - int MAX_MORPH_TIMESTEPS = 50000; // maximum number of LBM timesteps to spend in morphological adaptation routine - int MIN_STEADY_TIMESTEPS = 100000; - int MAX_STEADY_TIMESTEPS = 200000; - int RESCALE_FORCE_AFTER_TIMESTEP = 0; - int RAMP_TIMESTEPS = 0;//50000; // number of timesteps to run initially (to get a reasonable velocity field before other pieces kick in) - int CURRENT_MORPH_TIMESTEPS=0; // counter for number of timesteps spent in morphological adaptation routine (reset each time) - int CURRENT_STEADY_TIMESTEPS=0; // counter for number of timesteps spent in morphological adaptation routine (reset each time) - int morph_interval = 100000; - int analysis_interval = 1000; // number of timesteps in between in situ analysis - int morph_timesteps = 0; - double morph_delta = 0.0; - double seed_water = 0.0; - double capillary_number = 0.0; - double tolerance = 0.01; - double Ca_previous = 0.f; - double initial_volume = 0.0; - double delta_volume = 0.0; - double delta_volume_target = 0.0; +void ScaLBL_GreyscaleColorModel::Initialize() { + /* + * This function initializes model + */ + if (rank == 0) + printf("Initializing distributions \n"); + ScaLBL_D3Q19_Init(fq, Np); + //ScaLBL_D3Q19_GreyscaleColor_Init(fq, Porosity_dvc, Np); + + if (rank == 0) + printf("Initializing phase field \n"); + ScaLBL_PhaseField_Init(dvcMap, Phi, Den, Aq, Bq, 0, + ScaLBL_Comm->LastExterior(), Np); + ScaLBL_PhaseField_Init(dvcMap, Phi, Den, Aq, Bq, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + + if (Restart == true) { + if (rank == 0) { + printf("Reading restart file! \n"); + } + + // Read in the restart file to CPU buffers + int *TmpMap; + TmpMap = new int[Np]; + + double *cPhi, *cDist, *cDen; + cPhi = new double[N]; + cDen = new double[2 * Np]; + cDist = new double[19 * Np]; + ScaLBL_CopyToHost(TmpMap, dvcMap, Np * sizeof(int)); + ScaLBL_CopyToHost(cPhi, Phi, N * sizeof(double)); + + ifstream File(LocalRestartFile, ios::binary); + int idx; + double value, va, vb; + for (int n = 0; n < Np; n++) { + File.read((char *)&va, sizeof(va)); + File.read((char *)&vb, sizeof(vb)); + cDen[n] = va; + cDen[Np + n] = vb; + } + for (int n = 0; n < Np; n++) { + // Read the distributions + for (int q = 0; q < 19; q++) { + File.read((char *)&value, sizeof(value)); + cDist[q * Np + n] = value; + } + } + File.close(); + + for (int n = 0; n < ScaLBL_Comm->LastExterior(); n++) { + va = cDen[n]; + vb = cDen[Np + n]; + value = (va - vb) / (va + vb); + idx = TmpMap[n]; + if (!(idx < 0) && idx < N) + cPhi[idx] = value; + } + for (int n = ScaLBL_Comm->FirstInterior(); + n < ScaLBL_Comm->LastInterior(); n++) { + va = cDen[n]; + vb = cDen[Np + n]; + value = (va - vb) / (va + vb); + idx = TmpMap[n]; + if (!(idx < 0) && idx < N) + cPhi[idx] = value; + } + + // Copy the restart data to the GPU + ScaLBL_CopyToDevice(Den, cDen, 2 * Np * sizeof(double)); + ScaLBL_CopyToDevice(fq, cDist, 19 * Np * sizeof(double)); + ScaLBL_CopyToDevice(Phi, cPhi, N * sizeof(double)); + ScaLBL_Comm->Barrier(); + + comm.barrier(); + + if (rank == 0) + printf("Initializing phase field from Restart\n"); + ScaLBL_PhaseField_InitFromRestart(Den, Aq, Bq, 0, + ScaLBL_Comm->LastExterior(), Np); + ScaLBL_PhaseField_InitFromRestart(Den, Aq, Bq, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + } + + // establish reservoirs for external bC + if (BoundaryCondition == 1 || BoundaryCondition == 2 || + BoundaryCondition == 3 || BoundaryCondition == 4) { + if (Dm->kproc() == 0) { + ScaLBL_SetSlice_z(Phi, 1.0, Nx, Ny, Nz, 0); + ScaLBL_SetSlice_z(Phi, 1.0, Nx, Ny, Nz, 1); + ScaLBL_SetSlice_z(Phi, 1.0, Nx, Ny, Nz, 2); + } + if (Dm->kproc() == nprocz - 1) { + ScaLBL_SetSlice_z(Phi, -1.0, Nx, Ny, Nz, Nz - 1); + ScaLBL_SetSlice_z(Phi, -1.0, Nx, Ny, Nz, Nz - 2); + ScaLBL_SetSlice_z(Phi, -1.0, Nx, Ny, Nz, Nz - 3); + } + } + //ScaLBL_CopyToHost(Averages->Phi.data(),Phi,N*sizeof(double)); +} + +void ScaLBL_GreyscaleColorModel::Run() { + int nprocs = nprocx * nprocy * nprocz; + const RankInfoStruct rank_info(rank, nprocx, nprocy, nprocz); + + bool SET_CAPILLARY_NUMBER = false; + bool RESCALE_FORCE = false; + bool MORPH_ADAPT = false; + bool USE_MORPH = false; + bool USE_SEED = false; + bool USE_DIRECT = false; + int MAX_MORPH_TIMESTEPS = + 50000; // maximum number of LBM timesteps to spend in morphological adaptation routine + int MIN_STEADY_TIMESTEPS = 100000; + int MAX_STEADY_TIMESTEPS = 200000; + int RESCALE_FORCE_AFTER_TIMESTEP = 0; + int RAMP_TIMESTEPS = + 0; //50000; // number of timesteps to run initially (to get a reasonable velocity field before other pieces kick in) + int CURRENT_MORPH_TIMESTEPS = + 0; // counter for number of timesteps spent in morphological adaptation routine (reset each time) + int CURRENT_STEADY_TIMESTEPS = + 0; // counter for number of timesteps spent in morphological adaptation routine (reset each time) + int morph_interval = 100000; + int analysis_interval = + 1000; // number of timesteps in between in situ analysis + int morph_timesteps = 0; + double morph_delta = 0.0; + double seed_water = 0.0; + double capillary_number = 0.0; + double tolerance = 0.01; + double Ca_previous = 0.f; + double initial_volume = 0.0; + double delta_volume = 0.0; + double delta_volume_target = 0.0; //TODO -------- For temporary use - should be included in the analysis framework later ------------- int visualization_interval = 50000; int restart_interval = 100000; - if (analysis_db->keyExists( "visualization_interval" )){ - visualization_interval = analysis_db->getScalar( "visualization_interval" ); - } - if (analysis_db->keyExists( "restart_interval" )){ - restart_interval = analysis_db->getScalar( "restart_interval" ); - } + if (analysis_db->keyExists("visualization_interval")) { + visualization_interval = + analysis_db->getScalar("visualization_interval"); + } + if (analysis_db->keyExists("restart_interval")) { + restart_interval = analysis_db->getScalar("restart_interval"); + } //------------------------------------------------------------------------------------------------- - - /* history for morphological algoirthm */ - double KRA_MORPH_FACTOR=0.5; - double volA_prev = 0.0; - double log_krA_prev = 1.0; - double log_krA_target = 1.0; - double log_krA = 1.0; - double slope_krA_volume = 0.0; - if (greyscaleColor_db->keyExists( "vol_A_previous" )){ - volA_prev = greyscaleColor_db->getScalar( "vol_A_previous" ); - } - if (greyscaleColor_db->keyExists( "log_krA_previous" )){ - log_krA_prev = greyscaleColor_db->getScalar( "log_krA_previous" ); - } - if (greyscaleColor_db->keyExists( "krA_morph_factor" )){ - KRA_MORPH_FACTOR = greyscaleColor_db->getScalar( "krA_morph_factor" ); - } - - /* defaults for simulation protocols */ - auto protocol = greyscaleColor_db->getWithDefault( "protocol", "none" ); - if (protocol == "seed water"){ - morph_delta = -0.05; - seed_water = 0.01; - USE_SEED = true; - USE_MORPH = true; - } - if (greyscaleColor_db->keyExists( "capillary_number" )){ - capillary_number = greyscaleColor_db->getScalar( "capillary_number" ); - SET_CAPILLARY_NUMBER=true; - } - if (greyscaleColor_db->keyExists( "rescale_force_after_timestep" )){ - RESCALE_FORCE_AFTER_TIMESTEP = greyscaleColor_db->getScalar( "rescale_force_after_timestep" ); - RESCALE_FORCE = true; - } - if (greyscaleColor_db->keyExists( "timestep" )){ - timestep = greyscaleColor_db->getScalar( "timestep" ); - } - if (BoundaryCondition != 0 && BoundaryCondition != 5 && SET_CAPILLARY_NUMBER==true){ - if (rank == 0) printf("WARINING: capillary number target only supported for BC = 0 or 5 \n"); - SET_CAPILLARY_NUMBER=false; - } - if (analysis_db->keyExists( "seed_water" )){ - seed_water = analysis_db->getScalar( "seed_water" ); - if (rank == 0) printf("Seed water in oil %f (seed_water) \n",seed_water); - USE_SEED = true; - } - if (analysis_db->keyExists( "morph_delta" )){ - morph_delta = analysis_db->getScalar( "morph_delta" ); - if (rank == 0) printf("Target volume change %f (morph_delta) \n",morph_delta); - } - if (analysis_db->keyExists( "morph_interval" )){ - morph_interval = analysis_db->getScalar( "morph_interval" ); - USE_MORPH = true; - } - if (analysis_db->keyExists( "tolerance" )){ - tolerance = analysis_db->getScalar( "tolerance" ); - } - if (analysis_db->keyExists( "analysis_interval" )){ - analysis_interval = analysis_db->getScalar( "analysis_interval" ); - } - if (analysis_db->keyExists( "min_steady_timesteps" )){ - MIN_STEADY_TIMESTEPS = analysis_db->getScalar( "min_steady_timesteps" ); - } - if (analysis_db->keyExists( "max_steady_timesteps" )){ - MAX_STEADY_TIMESTEPS = analysis_db->getScalar( "max_steady_timesteps" ); - } - if (analysis_db->keyExists( "max_morph_timesteps" )){ - MAX_MORPH_TIMESTEPS = analysis_db->getScalar( "max_morph_timesteps" ); - } + /* history for morphological algoirthm */ + double KRA_MORPH_FACTOR = 0.5; + double volA_prev = 0.0; + double log_krA_prev = 1.0; + double log_krA_target = 1.0; + double log_krA = 1.0; + double slope_krA_volume = 0.0; + if (greyscaleColor_db->keyExists("vol_A_previous")) { + volA_prev = greyscaleColor_db->getScalar("vol_A_previous"); + } + if (greyscaleColor_db->keyExists("log_krA_previous")) { + log_krA_prev = greyscaleColor_db->getScalar("log_krA_previous"); + } + if (greyscaleColor_db->keyExists("krA_morph_factor")) { + KRA_MORPH_FACTOR = + greyscaleColor_db->getScalar("krA_morph_factor"); + } + /* defaults for simulation protocols */ + auto protocol = + greyscaleColor_db->getWithDefault("protocol", "none"); + if (protocol == "seed water") { + morph_delta = -0.05; + seed_water = 0.01; + USE_SEED = true; + USE_MORPH = true; + } - if (rank==0){ - printf("********************************************************\n"); - if (protocol == "seed water"){ - printf(" using protocol = seed water \n"); - printf(" min_steady_timesteps = %i \n",MIN_STEADY_TIMESTEPS); - printf(" max_steady_timesteps = %i \n",MAX_STEADY_TIMESTEPS); - printf(" tolerance = %f \n",tolerance); - printf(" morph_delta = %f \n",morph_delta); - printf(" seed_water = %f \n",seed_water); - } - printf("No. of timesteps: %i \n", timestepMax); - fflush(stdout); - } + if (greyscaleColor_db->keyExists("capillary_number")) { + capillary_number = + greyscaleColor_db->getScalar("capillary_number"); + SET_CAPILLARY_NUMBER = true; + } + if (greyscaleColor_db->keyExists("rescale_force_after_timestep")) { + RESCALE_FORCE_AFTER_TIMESTEP = + greyscaleColor_db->getScalar("rescale_force_after_timestep"); + RESCALE_FORCE = true; + } + if (greyscaleColor_db->keyExists("timestep")) { + timestep = greyscaleColor_db->getScalar("timestep"); + } + if (BoundaryCondition != 0 && BoundaryCondition != 5 && + SET_CAPILLARY_NUMBER == true) { + if (rank == 0) + printf("WARINING: capillary number target only supported for BC = " + "0 or 5 \n"); + SET_CAPILLARY_NUMBER = false; + } + if (analysis_db->keyExists("seed_water")) { + seed_water = analysis_db->getScalar("seed_water"); + if (rank == 0) + printf("Seed water in oil %f (seed_water) \n", seed_water); + USE_SEED = true; + } + if (analysis_db->keyExists("morph_delta")) { + morph_delta = analysis_db->getScalar("morph_delta"); + if (rank == 0) + printf("Target volume change %f (morph_delta) \n", morph_delta); + } + if (analysis_db->keyExists("morph_interval")) { + morph_interval = analysis_db->getScalar("morph_interval"); + USE_MORPH = true; + } + if (analysis_db->keyExists("tolerance")) { + tolerance = analysis_db->getScalar("tolerance"); + } + if (analysis_db->keyExists("analysis_interval")) { + analysis_interval = analysis_db->getScalar("analysis_interval"); + } + if (analysis_db->keyExists("min_steady_timesteps")) { + MIN_STEADY_TIMESTEPS = + analysis_db->getScalar("min_steady_timesteps"); + } + if (analysis_db->keyExists("max_steady_timesteps")) { + MAX_STEADY_TIMESTEPS = + analysis_db->getScalar("max_steady_timesteps"); + } + if (analysis_db->keyExists("max_morph_timesteps")) { + MAX_MORPH_TIMESTEPS = + analysis_db->getScalar("max_morph_timesteps"); + } - //.......create and start timer............ - ScaLBL_Comm->Barrier(); - comm.barrier(); - //......................................... + if (rank == 0) { + printf("********************************************************\n"); + if (protocol == "seed water") { + printf(" using protocol = seed water \n"); + printf(" min_steady_timesteps = %i \n", MIN_STEADY_TIMESTEPS); + printf(" max_steady_timesteps = %i \n", MAX_STEADY_TIMESTEPS); + printf(" tolerance = %f \n", tolerance); + printf(" morph_delta = %f \n", morph_delta); + printf(" seed_water = %f \n", seed_water); + } + printf("No. of timesteps: %i \n", timestepMax); + fflush(stdout); + } - //************ MAIN ITERATION LOOP ***************************************/ - PROFILE_START("Loop"); + //.......create and start timer............ + ScaLBL_Comm->Barrier(); + comm.barrier(); + //......................................... + + //************ MAIN ITERATION LOOP ***************************************/ + PROFILE_START("Loop"); //std::shared_ptr analysis_db; - auto current_db = db->cloneDatabase(); - //runAnalysis analysis( current_db, rank_info, ScaLBL_Comm, Dm, Np, Regular, Map ); - //analysis.createThreads( analysis_method, 4 ); + auto current_db = db->cloneDatabase(); + //runAnalysis analysis( current_db, rank_info, ScaLBL_Comm, Dm, Np, Regular, Map ); + //analysis.createThreads( analysis_method, 4 ); auto t1 = std::chrono::system_clock::now(); - while (timestep < timestepMax ) { - //if ( rank==0 ) { printf("Running timestep %i (%i MB)\n",timestep+1,(int)(Utilities::getMemoryUsage()/1048576)); } - PROFILE_START("Update"); - // *************ODD TIMESTEP************* - timestep++; - // Compute the Phase indicator field - // Read for Aq, Bq happens in this routine (requires communication) - ScaLBL_Comm->BiSendD3Q7AA(Aq,Bq); //READ FROM NORMAL - ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + while (timestep < timestepMax) { + //if ( rank==0 ) { printf("Running timestep %i (%i MB)\n",timestep+1,(int)(Utilities::getMemoryUsage()/1048576)); } + PROFILE_START("Update"); + // *************ODD TIMESTEP************* + timestep++; + // Compute the Phase indicator field + // Read for Aq, Bq happens in this routine (requires communication) + ScaLBL_Comm->BiSendD3Q7AA(Aq, Bq); //READ FROM NORMAL + ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); //ScaLBL_Update_GreyscalePotential(dvcMap,Phi,Psi,Porosity_dvc,Permeability_dvc,alpha,W,ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->BiRecvD3Q7AA(Aq,Bq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, 0, ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm->BiRecvD3Q7AA(Aq, Bq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, 0, + ScaLBL_Comm->LastExterior(), Np); //ScaLBL_Update_GreyscalePotential(dvcMap,Phi,Psi,Porosity_dvc,Permeability_dvc,alpha,W,0,ScaLBL_Comm->LastExterior(), Np); - // Perform the collision operation - ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL - if (BoundaryCondition > 0 && BoundaryCondition < 5){ - ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); - ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); - } - // Halo exchange for phase field - ScaLBL_Comm_Regular->SendHalo(Phi); - ScaLBL_D3Q19_AAodd_GreyscaleColor_CP(NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, GreySolidW,GreySn,GreySw,GreyKn,GreyKw,Porosity_dvc,Permeability_dvc,Velocity,MobilityRatio,Pressure, - rhoA, rhoB, tauA, tauB,tauA_eff, tauB_eff, - alpha, beta, Fx, Fy, Fz, RecoloringOff, Nx, Nx*Ny, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm_Regular->RecvHalo(Phi); - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - // Set BCs - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); - } + // Perform the collision operation + ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL + if (BoundaryCondition > 0 && BoundaryCondition < 5) { + ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); + ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); + } + // Halo exchange for phase field + ScaLBL_Comm_Regular->SendHalo(Phi); + ScaLBL_D3Q19_AAodd_GreyscaleColor_CP( + NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, GreySolidW, GreySn, + GreySw, GreyKn, GreyKw, Porosity_dvc, Permeability_dvc, Velocity, + MobilityRatio, Pressure, rhoA, rhoB, tauA, tauB, tauA_eff, tauB_eff, + alpha, beta, Fx, Fy, Fz, RecoloringOff, Nx, Nx * Ny, + ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm_Regular->RecvHalo(Phi); + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + // Set BCs + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } + if (BoundaryCondition == 4) { + din = + ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); + } - ScaLBL_D3Q19_AAodd_GreyscaleColor_CP(NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, GreySolidW,GreySn,GreySw,GreyKn,GreyKw,Porosity_dvc,Permeability_dvc,Velocity,MobilityRatio,Pressure, - rhoA, rhoB, tauA, tauB,tauA_eff, tauB_eff, - alpha, beta, Fx, Fy, Fz, RecoloringOff, Nx, Nx*Ny, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->Barrier(); - - // *************EVEN TIMESTEP************* - timestep++; - // Compute the Phase indicator field - ScaLBL_Comm->BiSendD3Q7AA(Aq,Bq); //READ FROM NORMAL - ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + ScaLBL_D3Q19_AAodd_GreyscaleColor_CP( + NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, GreySolidW, GreySn, + GreySw, GreyKn, GreyKw, Porosity_dvc, Permeability_dvc, Velocity, + MobilityRatio, Pressure, rhoA, rhoB, tauA, tauB, tauA_eff, tauB_eff, + alpha, beta, Fx, Fy, Fz, RecoloringOff, Nx, Nx * Ny, 0, + ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm->Barrier(); + + // *************EVEN TIMESTEP************* + timestep++; + // Compute the Phase indicator field + ScaLBL_Comm->BiSendD3Q7AA(Aq, Bq); //READ FROM NORMAL + ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); //ScaLBL_Update_GreyscalePotential(dvcMap,Phi,Psi,Porosity_dvc,Permeability_dvc,alpha,W,ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->BiRecvD3Q7AA(Aq,Bq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, 0, ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm->BiRecvD3Q7AA(Aq, Bq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, 0, + ScaLBL_Comm->LastExterior(), Np); //ScaLBL_Update_GreyscalePotential(dvcMap,Phi,Psi,Porosity_dvc,Permeability_dvc,alpha,W,0,ScaLBL_Comm->LastExterior(), Np); - // Perform the collision operation - ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL - // Halo exchange for phase field - if (BoundaryCondition > 0 && BoundaryCondition < 5){ - ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); - ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); - } - ScaLBL_Comm_Regular->SendHalo(Phi); - ScaLBL_D3Q19_AAeven_GreyscaleColor_CP(dvcMap, fq, Aq, Bq, Den, Phi, GreySolidW,GreySn,GreySw,GreyKn,GreyKw,Porosity_dvc,Permeability_dvc,Velocity,MobilityRatio,Pressure, - rhoA, rhoB, tauA, tauB,tauA_eff, tauB_eff, - alpha, beta, Fx, Fy, Fz, RecoloringOff, Nx, Nx*Ny, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm_Regular->RecvHalo(Phi); - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - // Set boundary conditions - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); - } + // Perform the collision operation + ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL + // Halo exchange for phase field + if (BoundaryCondition > 0 && BoundaryCondition < 5) { + ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); + ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); + } + ScaLBL_Comm_Regular->SendHalo(Phi); + ScaLBL_D3Q19_AAeven_GreyscaleColor_CP( + dvcMap, fq, Aq, Bq, Den, Phi, GreySolidW, GreySn, GreySw, GreyKn, + GreyKw, Porosity_dvc, Permeability_dvc, Velocity, MobilityRatio, + Pressure, rhoA, rhoB, tauA, tauB, tauA_eff, tauB_eff, alpha, beta, + Fx, Fy, Fz, RecoloringOff, Nx, Nx * Ny, + ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm_Regular->RecvHalo(Phi); + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + // Set boundary conditions + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 4) { + din = + ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); + } - ScaLBL_D3Q19_AAeven_GreyscaleColor_CP(dvcMap, fq, Aq, Bq, Den, Phi, GreySolidW,GreySn,GreySw,GreyKn,GreyKw,Porosity_dvc,Permeability_dvc,Velocity,MobilityRatio,Pressure, - rhoA, rhoB, tauA, tauB,tauA_eff, tauB_eff, - alpha, beta, Fx, Fy, Fz, RecoloringOff, Nx, Nx*Ny, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->Barrier(); - //************************************************************************ - PROFILE_STOP("Update"); + ScaLBL_D3Q19_AAeven_GreyscaleColor_CP( + dvcMap, fq, Aq, Bq, Den, Phi, GreySolidW, GreySn, GreySw, GreyKn, + GreyKw, Porosity_dvc, Permeability_dvc, Velocity, MobilityRatio, + Pressure, rhoA, rhoB, tauA, tauB, tauA_eff, tauB_eff, alpha, beta, + Fx, Fy, Fz, RecoloringOff, Nx, Nx * Ny, 0, + ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm->Barrier(); + //************************************************************************ + PROFILE_STOP("Update"); //TODO For temporary use - writing Restart and Vis files should be included in the analysis framework in the future - if (timestep%restart_interval==0){ + if (timestep % restart_interval == 0) { //Use rank=0 write out Restart.db - if (rank==0) { - greyscaleColor_db->putScalar("timestep",timestep); - greyscaleColor_db->putScalar( "Restart", true ); + if (rank == 0) { + greyscaleColor_db->putScalar("timestep", timestep); + greyscaleColor_db->putScalar("Restart", true); current_db->putDatabase("Color", greyscaleColor_db); std::ofstream OutStream("Restart.db"); current_db->print(OutStream, ""); OutStream.close(); - } //Write out Restart data. std::shared_ptr cDen; std::shared_ptr cfq; - cDen = std::shared_ptr(new double[2*Np], DeleteArray); - cfq = std::shared_ptr(new double[19*Np],DeleteArray); - ScaLBL_CopyToHost(cDen.get(),Den,2*Np*sizeof(double));// Copy restart data to the CPU - ScaLBL_CopyToHost(cfq.get(), fq,19*Np*sizeof(double));// Copy restart data to the CPU + cDen = std::shared_ptr(new double[2 * Np], + DeleteArray); + cfq = std::shared_ptr(new double[19 * Np], + DeleteArray); + ScaLBL_CopyToHost( + cDen.get(), Den, + 2 * Np * sizeof(double)); // Copy restart data to the CPU + ScaLBL_CopyToHost( + cfq.get(), fq, + 19 * Np * sizeof(double)); // Copy restart data to the CPU - ofstream RESTARTFILE(LocalRestartFile,ios::binary); + ofstream RESTARTFILE(LocalRestartFile, ios::binary); double value; - for (int n=0; nRegularLayout(Map,Pressure,Averages->Pressure); - ScaLBL_Comm->RegularLayout(Map,MobilityRatio,Averages->MobilityRatio); - ScaLBL_Comm->RegularLayout(Map,&Den[0],Averages->Rho_n); - ScaLBL_Comm->RegularLayout(Map,&Den[Np],Averages->Rho_w); - ScaLBL_Comm->RegularLayout(Map,&Velocity[0],Averages->Vel_x); - ScaLBL_Comm->RegularLayout(Map,&Velocity[Np],Averages->Vel_y); - ScaLBL_Comm->RegularLayout(Map,&Velocity[2*Np],Averages->Vel_z); - - Averages->Basic(); + if (rank == 0 && timestep % analysis_interval == 0 && + BoundaryCondition == 4) { + printf("%i %.5g \n", timestep, din); } - // allow initial ramp-up to get closer to steady state - if (timestep > RAMP_TIMESTEPS && timestep%analysis_interval == 0 && USE_MORPH){ - //analysis.finish(); - CURRENT_STEADY_TIMESTEPS += analysis_interval; + if (timestep % analysis_interval == 0) { + ScaLBL_Comm->RegularLayout(Map, Pressure, Averages->Pressure); + ScaLBL_Comm->RegularLayout(Map, MobilityRatio, + Averages->MobilityRatio); + ScaLBL_Comm->RegularLayout(Map, &Den[0], Averages->Rho_n); + ScaLBL_Comm->RegularLayout(Map, &Den[Np], Averages->Rho_w); + ScaLBL_Comm->RegularLayout(Map, &Velocity[0], Averages->Vel_x); + ScaLBL_Comm->RegularLayout(Map, &Velocity[Np], Averages->Vel_y); + ScaLBL_Comm->RegularLayout(Map, &Velocity[2 * Np], Averages->Vel_z); - double muA = rhoA*(tauA-0.5)/3.f; - double muB = rhoB*(tauB-0.5)/3.f; - double force_mag = sqrt(Fx*Fx+Fy*Fy+Fz*Fz); - if (force_mag == 0.0){ - force_mag = 1.0; - } - double current_saturation = Averages->saturation; - double volA = current_saturation*GreyPorosity; - double volB = (1.0-current_saturation)*GreyPorosity; - double flow_rate_A = Averages->oil_flow_rate; - double flow_rate_B = Averages->water_flow_rate; - double Ca = fabs(muA*flow_rate_A + muB*flow_rate_B)/(6.0*alpha); - - if ( morph_timesteps > morph_interval ){ - - bool isSteady = false; - if ( (fabs((Ca - Ca_previous)/Ca) < tolerance && CURRENT_STEADY_TIMESTEPS > MIN_STEADY_TIMESTEPS)) - isSteady = true; - if (CURRENT_STEADY_TIMESTEPS > MAX_STEADY_TIMESTEPS) - isSteady = true; - if (RESCALE_FORCE == true && SET_CAPILLARY_NUMBER == true && CURRENT_STEADY_TIMESTEPS > RESCALE_FORCE_AFTER_TIMESTEP){ - RESCALE_FORCE = false; - double RESCALE_FORCE_FACTOR = capillary_number / Ca; - if (RESCALE_FORCE_FACTOR > 2.0) RESCALE_FORCE_FACTOR = 2.0; - if (RESCALE_FORCE_FACTOR < 0.5) RESCALE_FORCE_FACTOR = 0.5; - Fx *= RESCALE_FORCE_FACTOR; - Fy *= RESCALE_FORCE_FACTOR; - Fz *= RESCALE_FORCE_FACTOR; - force_mag = sqrt(Fx*Fx+Fy*Fy+Fz*Fz); - if (force_mag > 1e-3){ - Fx *= 1e-3/force_mag; // impose ceiling for stability - Fy *= 1e-3/force_mag; - Fz *= 1e-3/force_mag; - } - if (rank == 0) printf(" -- adjust force by factor %.5g \n ",capillary_number / Ca); - Averages->SetParams(rhoA,rhoB,tauA,tauB,Fx,Fy,Fz,alpha,beta,GreyPorosity); - greyscaleColor_db->putVector("F",{Fx,Fy,Fz}); - } - if ( isSteady ){ - MORPH_ADAPT = true; - CURRENT_MORPH_TIMESTEPS=0; - delta_volume_target = Dm->Volume*volA *morph_delta; // set target volume change - //****** ENDPOINT ADAPTATION ********/ - double krA_TMP= fabs(muA*flow_rate_A / force_mag); - double krB_TMP= fabs(muB*flow_rate_B / force_mag); - log_krA = log(krA_TMP); - if (krA_TMP < 0.0){ - // cannot do endpoint adaptation if kr is negative - log_krA = log_krA_prev; - } - else if (krA_TMP < krB_TMP && morph_delta > 0.0){ - /** morphological target based on relative permeability for A **/ - log_krA_target = log(KRA_MORPH_FACTOR*(krA_TMP)); - slope_krA_volume = (log_krA - log_krA_prev)/(Dm->Volume*(volA - volA_prev)); - delta_volume_target=min(delta_volume_target,Dm->Volume*(volA+(log_krA_target - log_krA)/slope_krA_volume)); - if (rank==0){ - printf(" Enabling endpoint adaptation: krA = %.5g, krB = %.5g \n",krA_TMP,krB_TMP); - printf(" log(kr)=%.5g, volume=%.5g, TARGET log(kr)=%.5g, volume change=%.5g \n",log_krA, volA, log_krA_target, delta_volume_target/(volA*Dm->Volume)); - } - } - log_krA_prev = log_krA; - volA_prev = volA; - //******************************** **/ - /** compute averages & write data **/ - /*Averages->Full(); + Averages->Basic(); + } + + // allow initial ramp-up to get closer to steady state + if (timestep > RAMP_TIMESTEPS && timestep % analysis_interval == 0 && + USE_MORPH) { + //analysis.finish(); + CURRENT_STEADY_TIMESTEPS += analysis_interval; + + double muA = rhoA * (tauA - 0.5) / 3.f; + double muB = rhoB * (tauB - 0.5) / 3.f; + double force_mag = sqrt(Fx * Fx + Fy * Fy + Fz * Fz); + if (force_mag == 0.0) { + force_mag = 1.0; + } + double current_saturation = Averages->saturation; + double volA = current_saturation * GreyPorosity; + double volB = (1.0 - current_saturation) * GreyPorosity; + double flow_rate_A = Averages->oil_flow_rate; + double flow_rate_B = Averages->water_flow_rate; + double Ca = + fabs(muA * flow_rate_A + muB * flow_rate_B) / (6.0 * alpha); + + if (morph_timesteps > morph_interval) { + + bool isSteady = false; + if ((fabs((Ca - Ca_previous) / Ca) < tolerance && + CURRENT_STEADY_TIMESTEPS > MIN_STEADY_TIMESTEPS)) + isSteady = true; + if (CURRENT_STEADY_TIMESTEPS > MAX_STEADY_TIMESTEPS) + isSteady = true; + if (RESCALE_FORCE == true && SET_CAPILLARY_NUMBER == true && + CURRENT_STEADY_TIMESTEPS > RESCALE_FORCE_AFTER_TIMESTEP) { + RESCALE_FORCE = false; + double RESCALE_FORCE_FACTOR = capillary_number / Ca; + if (RESCALE_FORCE_FACTOR > 2.0) + RESCALE_FORCE_FACTOR = 2.0; + if (RESCALE_FORCE_FACTOR < 0.5) + RESCALE_FORCE_FACTOR = 0.5; + Fx *= RESCALE_FORCE_FACTOR; + Fy *= RESCALE_FORCE_FACTOR; + Fz *= RESCALE_FORCE_FACTOR; + force_mag = sqrt(Fx * Fx + Fy * Fy + Fz * Fz); + if (force_mag > 1e-3) { + Fx *= 1e-3 / force_mag; // impose ceiling for stability + Fy *= 1e-3 / force_mag; + Fz *= 1e-3 / force_mag; + } + if (rank == 0) + printf(" -- adjust force by factor %.5g \n ", + capillary_number / Ca); + Averages->SetParams(rhoA, rhoB, tauA, tauB, Fx, Fy, Fz, + alpha, beta, GreyPorosity); + greyscaleColor_db->putVector("F", {Fx, Fy, Fz}); + } + if (isSteady) { + MORPH_ADAPT = true; + CURRENT_MORPH_TIMESTEPS = 0; + delta_volume_target = + Dm->Volume * volA * + morph_delta; // set target volume change + //****** ENDPOINT ADAPTATION ********/ + double krA_TMP = fabs(muA * flow_rate_A / force_mag); + double krB_TMP = fabs(muB * flow_rate_B / force_mag); + log_krA = log(krA_TMP); + if (krA_TMP < 0.0) { + // cannot do endpoint adaptation if kr is negative + log_krA = log_krA_prev; + } else if (krA_TMP < krB_TMP && morph_delta > 0.0) { + /** morphological target based on relative permeability for A **/ + log_krA_target = log(KRA_MORPH_FACTOR * (krA_TMP)); + slope_krA_volume = (log_krA - log_krA_prev) / + (Dm->Volume * (volA - volA_prev)); + delta_volume_target = min( + delta_volume_target, + Dm->Volume * (volA + (log_krA_target - log_krA) / + slope_krA_volume)); + if (rank == 0) { + printf(" Enabling endpoint adaptation: krA = " + "%.5g, krB = %.5g \n", + krA_TMP, krB_TMP); + printf(" log(kr)=%.5g, volume=%.5g, TARGET " + "log(kr)=%.5g, volume change=%.5g \n", + log_krA, volA, log_krA_target, + delta_volume_target / (volA * Dm->Volume)); + } + } + log_krA_prev = log_krA; + volA_prev = volA; + //******************************** **/ + /** compute averages & write data **/ + /*Averages->Full(); Averages->Write(timestep); analysis.WriteVisData(timestep, current_db, *Averages, Phi, Pressure, Velocity, fq, Den ); analysis.finish(); */ - if (rank==0){ - printf("** WRITE STEADY POINT *** "); - printf("Ca = %.5g, (previous = %.5g) \n",Ca,Ca_previous); - double h = Dm->voxel_length; + if (rank == 0) { + printf("** WRITE STEADY POINT *** "); + printf("Ca = %.5g, (previous = %.5g) \n", Ca, + Ca_previous); + double h = Dm->voxel_length; - // pressures - double pA = Averages->Oil.p; - double pB = Averages->Water.p; - double pAB = (pA-pB)/(h*6.0*alpha); + // pressures + double pA = Averages->Oil.p; + double pB = Averages->Water.p; + double pAB = (pA - pB) / (h * 6.0 * alpha); - double kAeff = h*h*muA*(flow_rate_A)/(force_mag); - double kBeff = h*h*muB*(flow_rate_B)/(force_mag); + double kAeff = + h * h * muA * (flow_rate_A) / (force_mag); + double kBeff = + h * h * muB * (flow_rate_B) / (force_mag); - double viscous_pressure_drop = (rhoA*volA + rhoB*volB)*force_mag; - double Mobility = muA/muB; - - bool WriteHeader=false; - FILE * kr_log_file = fopen("relperm.csv","r"); - if (kr_log_file != NULL) - fclose(kr_log_file); - else - WriteHeader=true; - kr_log_file = fopen("relperm.csv","a"); - if (WriteHeader) - fprintf(kr_log_file,"timesteps sat.water eff.perm.oil eff.perm.water cap.pressure.norm pressure.drop Ca M\n"); + double viscous_pressure_drop = + (rhoA * volA + rhoB * volB) * force_mag; + double Mobility = muA / muB; - fprintf(kr_log_file,"%i %.5g %.5g %.5g %.5g %.5g %.5g %.5g\n", - CURRENT_STEADY_TIMESTEPS,current_saturation,kAeff,kBeff,pAB,viscous_pressure_drop,Ca,Mobility); - fclose(kr_log_file); + bool WriteHeader = false; + FILE *kr_log_file = fopen("relperm.csv", "r"); + if (kr_log_file != NULL) + fclose(kr_log_file); + else + WriteHeader = true; + kr_log_file = fopen("relperm.csv", "a"); + if (WriteHeader) + fprintf(kr_log_file, + "timesteps sat.water eff.perm.oil " + "eff.perm.water cap.pressure.norm " + "pressure.drop Ca M\n"); - printf(" Measured capillary number %.5g \n ",Ca); - } - if (SET_CAPILLARY_NUMBER ){ - Fx *= capillary_number / Ca; - Fy *= capillary_number / Ca; - Fz *= capillary_number / Ca; - if (force_mag > 1e-3){ - Fx *= 1e-3/force_mag; // impose ceiling for stability - Fy *= 1e-3/force_mag; - Fz *= 1e-3/force_mag; - } - if (rank == 0) printf(" -- adjust force by factor %.5g \n ",capillary_number / Ca); - Averages->SetParams(rhoA,rhoB,tauA,tauB,Fx,Fy,Fz,alpha,beta,GreyPorosity); - greyscaleColor_db->putVector("F",{Fx,Fy,Fz}); - } - - CURRENT_STEADY_TIMESTEPS = 0; - } - else{ - if (rank==0){ - printf("** Continue to simulate steady *** \n "); - printf("Ca = %.5g, (previous = %.5g) \n",Ca,Ca_previous); - } - } - morph_timesteps=0; - Ca_previous = Ca; - } + fprintf(kr_log_file, + "%i %.5g %.5g %.5g %.5g %.5g %.5g %.5g\n", + CURRENT_STEADY_TIMESTEPS, current_saturation, + kAeff, kBeff, pAB, viscous_pressure_drop, Ca, + Mobility); + fclose(kr_log_file); - if (MORPH_ADAPT ){ - CURRENT_MORPH_TIMESTEPS += analysis_interval; - if (USE_SEED){ - delta_volume = volA*Dm->Volume - initial_volume; - CURRENT_MORPH_TIMESTEPS += analysis_interval; - double massChange = SeedPhaseField(seed_water); - if (rank==0) printf("***Seed water in oil %.5g, volume change %.5g / %.5g ***\n", massChange, delta_volume, delta_volume_target); - } + printf(" Measured capillary number %.5g \n ", Ca); + } + if (SET_CAPILLARY_NUMBER) { + Fx *= capillary_number / Ca; + Fy *= capillary_number / Ca; + Fz *= capillary_number / Ca; + if (force_mag > 1e-3) { + Fx *= 1e-3 / + force_mag; // impose ceiling for stability + Fy *= 1e-3 / force_mag; + Fz *= 1e-3 / force_mag; + } + if (rank == 0) + printf(" -- adjust force by factor %.5g \n ", + capillary_number / Ca); + Averages->SetParams(rhoA, rhoB, tauA, tauB, Fx, Fy, Fz, + alpha, beta, GreyPorosity); + greyscaleColor_db->putVector("F", {Fx, Fy, Fz}); + } - if ( (delta_volume - delta_volume_target)/delta_volume_target > 0.0 ){ - MORPH_ADAPT = false; - CURRENT_STEADY_TIMESTEPS=0; - initial_volume = volA*Dm->Volume; - delta_volume = 0.0; - if (RESCALE_FORCE_AFTER_TIMESTEP > 0) - RESCALE_FORCE = true; - } - else if (!(USE_DIRECT) && CURRENT_MORPH_TIMESTEPS > MAX_MORPH_TIMESTEPS) { - MORPH_ADAPT = false; - CURRENT_STEADY_TIMESTEPS=0; - initial_volume = volA*Dm->Volume; - delta_volume = 0.0; - RESCALE_FORCE = true; - if (RESCALE_FORCE_AFTER_TIMESTEP > 0) - RESCALE_FORCE = true; - } - } - morph_timesteps += analysis_interval; - } - ScaLBL_Comm->Barrier(); - } - //analysis.finish(); - PROFILE_STOP("Loop"); - PROFILE_SAVE("lbpm_color_simulator",1); - //************************************************************************ - ScaLBL_Comm->Barrier(); - if (rank==0) printf("-------------------------------------------------------------------\n"); - // Compute the walltime per timestep + CURRENT_STEADY_TIMESTEPS = 0; + } else { + if (rank == 0) { + printf("** Continue to simulate steady *** \n "); + printf("Ca = %.5g, (previous = %.5g) \n", Ca, + Ca_previous); + } + } + morph_timesteps = 0; + Ca_previous = Ca; + } + + if (MORPH_ADAPT) { + CURRENT_MORPH_TIMESTEPS += analysis_interval; + if (USE_SEED) { + delta_volume = volA * Dm->Volume - initial_volume; + CURRENT_MORPH_TIMESTEPS += analysis_interval; + double massChange = SeedPhaseField(seed_water); + if (rank == 0) + printf("***Seed water in oil %.5g, volume change %.5g " + "/ %.5g ***\n", + massChange, delta_volume, delta_volume_target); + } + + if ((delta_volume - delta_volume_target) / delta_volume_target > + 0.0) { + MORPH_ADAPT = false; + CURRENT_STEADY_TIMESTEPS = 0; + initial_volume = volA * Dm->Volume; + delta_volume = 0.0; + if (RESCALE_FORCE_AFTER_TIMESTEP > 0) + RESCALE_FORCE = true; + } else if (!(USE_DIRECT) && + CURRENT_MORPH_TIMESTEPS > MAX_MORPH_TIMESTEPS) { + MORPH_ADAPT = false; + CURRENT_STEADY_TIMESTEPS = 0; + initial_volume = volA * Dm->Volume; + delta_volume = 0.0; + RESCALE_FORCE = true; + if (RESCALE_FORCE_AFTER_TIMESTEP > 0) + RESCALE_FORCE = true; + } + } + morph_timesteps += analysis_interval; + } + ScaLBL_Comm->Barrier(); + } + //analysis.finish(); + PROFILE_STOP("Loop"); + PROFILE_SAVE("lbpm_color_simulator", 1); + //************************************************************************ + ScaLBL_Comm->Barrier(); + if (rank == 0) + printf("---------------------------------------------------------------" + "----\n"); + // Compute the walltime per timestep auto t2 = std::chrono::system_clock::now(); - double cputime = std::chrono::duration( t2 - t1 ).count() / timestep; - // Performance obtained from each node - double MLUPS = double(Np)/cputime/1000000; + double cputime = std::chrono::duration(t2 - t1).count() / timestep; + // Performance obtained from each node + double MLUPS = double(Np) / cputime / 1000000; - if (rank==0) printf("********************************************************\n"); - if (rank==0) printf("CPU time = %f \n", cputime); - if (rank==0) printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); - MLUPS *= nprocs; - if (rank==0) printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); - if (rank==0) printf("********************************************************\n"); + if (rank == 0) + printf("********************************************************\n"); + if (rank == 0) + printf("CPU time = %f \n", cputime); + if (rank == 0) + printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); + MLUPS *= nprocs; + if (rank == 0) + printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); + if (rank == 0) + printf("********************************************************\n"); - // ************************************************************************ + // ************************************************************************ } -double ScaLBL_GreyscaleColorModel::SeedPhaseField(const double seed_water_in_oil){ - srand(time(NULL)); - double mass_loss =0.f; - double count =0.f; - double *Aq_tmp, *Bq_tmp; - - Aq_tmp = new double [7*Np]; - Bq_tmp = new double [7*Np]; +double +ScaLBL_GreyscaleColorModel::SeedPhaseField(const double seed_water_in_oil) { + srand(time(NULL)); + double mass_loss = 0.f; + double count = 0.f; + double *Aq_tmp, *Bq_tmp; - ScaLBL_CopyToHost(Aq_tmp, Aq, 7*Np*sizeof(double)); - ScaLBL_CopyToHost(Bq_tmp, Bq, 7*Np*sizeof(double)); - - - for (int n=0; n < ScaLBL_Comm->LastExterior(); n++){ - double random_value = seed_water_in_oil*double(rand())/ RAND_MAX; - double dA = Aq_tmp[n] + Aq_tmp[n+Np] + Aq_tmp[n+2*Np] + Aq_tmp[n+3*Np] + Aq_tmp[n+4*Np] + Aq_tmp[n+5*Np] + Aq_tmp[n+6*Np]; - double dB = Bq_tmp[n] + Bq_tmp[n+Np] + Bq_tmp[n+2*Np] + Bq_tmp[n+3*Np] + Bq_tmp[n+4*Np] + Bq_tmp[n+5*Np] + Bq_tmp[n+6*Np]; - double phase_id = (dA - dB) / (dA + dB); - if (phase_id > 0.0){ - Aq_tmp[n] -= 0.3333333333333333*random_value; - Aq_tmp[n+Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+2*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+3*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+4*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+5*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+6*Np] -= 0.1111111111111111*random_value; - - Bq_tmp[n] += 0.3333333333333333*random_value; - Bq_tmp[n+Np] += 0.1111111111111111*random_value; - Bq_tmp[n+2*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+3*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+4*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+5*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+6*Np] += 0.1111111111111111*random_value; + Aq_tmp = new double[7 * Np]; + Bq_tmp = new double[7 * Np]; + + ScaLBL_CopyToHost(Aq_tmp, Aq, 7 * Np * sizeof(double)); + ScaLBL_CopyToHost(Bq_tmp, Bq, 7 * Np * sizeof(double)); + + for (int n = 0; n < ScaLBL_Comm->LastExterior(); n++) { + double random_value = seed_water_in_oil * double(rand()) / RAND_MAX; + double dA = Aq_tmp[n] + Aq_tmp[n + Np] + Aq_tmp[n + 2 * Np] + + Aq_tmp[n + 3 * Np] + Aq_tmp[n + 4 * Np] + + Aq_tmp[n + 5 * Np] + Aq_tmp[n + 6 * Np]; + double dB = Bq_tmp[n] + Bq_tmp[n + Np] + Bq_tmp[n + 2 * Np] + + Bq_tmp[n + 3 * Np] + Bq_tmp[n + 4 * Np] + + Bq_tmp[n + 5 * Np] + Bq_tmp[n + 6 * Np]; + double phase_id = (dA - dB) / (dA + dB); + if (phase_id > 0.0) { + Aq_tmp[n] -= 0.3333333333333333 * random_value; + Aq_tmp[n + Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 2 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 3 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 4 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 5 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 6 * Np] -= 0.1111111111111111 * random_value; + + Bq_tmp[n] += 0.3333333333333333 * random_value; + Bq_tmp[n + Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 2 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 3 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 4 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 5 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 6 * Np] += 0.1111111111111111 * random_value; + } + mass_loss += random_value * seed_water_in_oil; } - mass_loss += random_value*seed_water_in_oil; - } - for (int n=ScaLBL_Comm->FirstInterior(); n < ScaLBL_Comm->LastInterior(); n++){ - double random_value = seed_water_in_oil*double(rand())/ RAND_MAX; - double dA = Aq_tmp[n] + Aq_tmp[n+Np] + Aq_tmp[n+2*Np] + Aq_tmp[n+3*Np] + Aq_tmp[n+4*Np] + Aq_tmp[n+5*Np] + Aq_tmp[n+6*Np]; - double dB = Bq_tmp[n] + Bq_tmp[n+Np] + Bq_tmp[n+2*Np] + Bq_tmp[n+3*Np] + Bq_tmp[n+4*Np] + Bq_tmp[n+5*Np] + Bq_tmp[n+6*Np]; - double phase_id = (dA - dB) / (dA + dB); - if (phase_id > 0.0){ - Aq_tmp[n] -= 0.3333333333333333*random_value; - Aq_tmp[n+Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+2*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+3*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+4*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+5*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+6*Np] -= 0.1111111111111111*random_value; - - Bq_tmp[n] += 0.3333333333333333*random_value; - Bq_tmp[n+Np] += 0.1111111111111111*random_value; - Bq_tmp[n+2*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+3*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+4*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+5*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+6*Np] += 0.1111111111111111*random_value; + for (int n = ScaLBL_Comm->FirstInterior(); n < ScaLBL_Comm->LastInterior(); + n++) { + double random_value = seed_water_in_oil * double(rand()) / RAND_MAX; + double dA = Aq_tmp[n] + Aq_tmp[n + Np] + Aq_tmp[n + 2 * Np] + + Aq_tmp[n + 3 * Np] + Aq_tmp[n + 4 * Np] + + Aq_tmp[n + 5 * Np] + Aq_tmp[n + 6 * Np]; + double dB = Bq_tmp[n] + Bq_tmp[n + Np] + Bq_tmp[n + 2 * Np] + + Bq_tmp[n + 3 * Np] + Bq_tmp[n + 4 * Np] + + Bq_tmp[n + 5 * Np] + Bq_tmp[n + 6 * Np]; + double phase_id = (dA - dB) / (dA + dB); + if (phase_id > 0.0) { + Aq_tmp[n] -= 0.3333333333333333 * random_value; + Aq_tmp[n + Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 2 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 3 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 4 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 5 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 6 * Np] -= 0.1111111111111111 * random_value; + + Bq_tmp[n] += 0.3333333333333333 * random_value; + Bq_tmp[n + Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 2 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 3 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 4 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 5 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 6 * Np] += 0.1111111111111111 * random_value; + } + mass_loss += random_value * seed_water_in_oil; } - mass_loss += random_value*seed_water_in_oil; - } - count= Dm->Comm.sumReduce( count); - mass_loss= Dm->Comm.sumReduce( mass_loss); - if (rank == 0) printf("Remove mass %.5g from %.5g voxels \n",mass_loss,count); + count = Dm->Comm.sumReduce(count); + mass_loss = Dm->Comm.sumReduce(mass_loss); + if (rank == 0) + printf("Remove mass %.5g from %.5g voxels \n", mass_loss, count); - // Need to initialize Aq, Bq, Den, Phi directly - //ScaLBL_CopyToDevice(Phi,phase.data(),7*Np*sizeof(double)); - ScaLBL_CopyToDevice(Aq, Aq_tmp, 7*Np*sizeof(double)); - ScaLBL_CopyToDevice(Bq, Bq_tmp, 7*Np*sizeof(double)); + // Need to initialize Aq, Bq, Den, Phi directly + //ScaLBL_CopyToDevice(Phi,phase.data(),7*Np*sizeof(double)); + ScaLBL_CopyToDevice(Aq, Aq_tmp, 7 * Np * sizeof(double)); + ScaLBL_CopyToDevice(Bq, Bq_tmp, 7 * Np * sizeof(double)); - return(mass_loss); + return (mass_loss); } //TODO for temporary use - writing visualization files should be included in the analysis framework in the future -void ScaLBL_GreyscaleColorModel::WriteVisFiles(){ +void ScaLBL_GreyscaleColorModel::WriteVisFiles() { //NOTE: write_silo is always true - - std::vector visData; - fillHalo fillData(Dm->Comm,Dm->rank_info,{Dm->Nx-2,Dm->Ny-2,Dm->Nz-2},{1,1,1},0,1); - auto VxVar = std::make_shared(); - auto VyVar = std::make_shared(); - auto VzVar = std::make_shared(); - auto SignDistVar = std::make_shared(); - auto PressureVar = std::make_shared(); - auto PhaseVar = std::make_shared(); + std::vector visData; + fillHalo fillData(Dm->Comm, Dm->rank_info, + {Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2}, {1, 1, 1}, + 0, 1); - // Create the MeshDataStruct - IO::initialize("","silo","false"); - visData.resize(1); - visData[0].meshName = "domain"; - visData[0].mesh = std::make_shared( Dm->rank_info,Dm->Nx-2,Dm->Ny-2,Dm->Nz-2,Dm->Lx,Dm->Ly,Dm->Lz ); + auto VxVar = std::make_shared(); + auto VyVar = std::make_shared(); + auto VzVar = std::make_shared(); + auto SignDistVar = std::make_shared(); + auto PressureVar = std::make_shared(); + auto PhaseVar = std::make_shared(); + + // Create the MeshDataStruct + IO::initialize("", "silo", "false"); + visData.resize(1); + visData[0].meshName = "domain"; + visData[0].mesh = + std::make_shared(Dm->rank_info, Dm->Nx - 2, Dm->Ny - 2, + Dm->Nz - 2, Dm->Lx, Dm->Ly, Dm->Lz); // create a temp data for copy from device - DoubleArray DataTemp(Nx,Ny,Nz); + DoubleArray DataTemp(Nx, Ny, Nz); - if (vis_db->getWithDefault( "save_phase_field", true )){ + if (vis_db->getWithDefault("save_phase_field", true)) { PhaseVar->name = "Phase"; PhaseVar->type = IO::VariableType::VolumeVariable; PhaseVar->dim = 1; - PhaseVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + PhaseVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(PhaseVar); - ASSERT(visData[0].vars[0]->name=="Phase"); - Array& PhaseData = visData[0].vars[0]->data; - ScaLBL_CopyToHost(DataTemp.data(), Phi, sizeof(double)*Nx*Ny*Nz); - fillData.copy(DataTemp,PhaseData); + ASSERT(visData[0].vars[0]->name == "Phase"); + Array &PhaseData = visData[0].vars[0]->data; + ScaLBL_CopyToHost(DataTemp.data(), Phi, sizeof(double) * Nx * Ny * Nz); + fillData.copy(DataTemp, PhaseData); } - if (vis_db->getWithDefault( "save_pressure", false )){ + if (vis_db->getWithDefault("save_pressure", false)) { PressureVar->name = "Pressure"; PressureVar->type = IO::VariableType::VolumeVariable; PressureVar->dim = 1; - PressureVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + PressureVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(PressureVar); - ASSERT(visData[0].vars[1]->name=="Pressure"); - Array& PressData = visData[0].vars[1]->data; - ScaLBL_Comm->RegularLayout(Map,Pressure,DataTemp); - fillData.copy(DataTemp,PressData); + ASSERT(visData[0].vars[1]->name == "Pressure"); + Array &PressData = visData[0].vars[1]->data; + ScaLBL_Comm->RegularLayout(Map, Pressure, DataTemp); + fillData.copy(DataTemp, PressData); } - if (vis_db->getWithDefault( "save_velocity", false )){ + if (vis_db->getWithDefault("save_velocity", false)) { VxVar->name = "Velocity_x"; VxVar->type = IO::VariableType::VolumeVariable; VxVar->dim = 1; - VxVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + VxVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(VxVar); VyVar->name = "Velocity_y"; VyVar->type = IO::VariableType::VolumeVariable; VyVar->dim = 1; - VyVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + VyVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(VyVar); VzVar->name = "Velocity_z"; VzVar->type = IO::VariableType::VolumeVariable; VzVar->dim = 1; - VzVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + VzVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(VzVar); - ASSERT(visData[0].vars[2]->name=="Velocity_x"); - ASSERT(visData[0].vars[3]->name=="Velocity_y"); - ASSERT(visData[0].vars[4]->name=="Velocity_z"); - Array& VelxData = visData[0].vars[2]->data; - Array& VelyData = visData[0].vars[3]->data; - Array& VelzData = visData[0].vars[4]->data; - ScaLBL_Comm->RegularLayout(Map,&Velocity[0],DataTemp); - fillData.copy(DataTemp,VelxData); - ScaLBL_Comm->RegularLayout(Map,&Velocity[Np],DataTemp); - fillData.copy(DataTemp,VelyData); - ScaLBL_Comm->RegularLayout(Map,&Velocity[2*Np],DataTemp); - fillData.copy(DataTemp,VelzData); + ASSERT(visData[0].vars[2]->name == "Velocity_x"); + ASSERT(visData[0].vars[3]->name == "Velocity_y"); + ASSERT(visData[0].vars[4]->name == "Velocity_z"); + Array &VelxData = visData[0].vars[2]->data; + Array &VelyData = visData[0].vars[3]->data; + Array &VelzData = visData[0].vars[4]->data; + ScaLBL_Comm->RegularLayout(Map, &Velocity[0], DataTemp); + fillData.copy(DataTemp, VelxData); + ScaLBL_Comm->RegularLayout(Map, &Velocity[Np], DataTemp); + fillData.copy(DataTemp, VelyData); + ScaLBL_Comm->RegularLayout(Map, &Velocity[2 * Np], DataTemp); + fillData.copy(DataTemp, VelzData); } - if (vis_db->getWithDefault( "save_distance", false )){ + if (vis_db->getWithDefault("save_distance", false)) { SignDistVar->name = "SignDist"; SignDistVar->type = IO::VariableType::VolumeVariable; SignDistVar->dim = 1; - SignDistVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); + SignDistVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); visData[0].vars.push_back(SignDistVar); - ASSERT(visData[0].vars[5]->name=="SignDist"); - Array& SignData = visData[0].vars[5]->data; - fillData.copy(Averages->SDs,SignData); + ASSERT(visData[0].vars[5]->name == "SignDist"); + Array &SignData = visData[0].vars[5]->data; + fillData.copy(Averages->SDs, SignData); } - if (vis_db->getWithDefault( "write_silo", true )){ - IO::writeData( timestep, visData, Dm->Comm ); + if (vis_db->getWithDefault("write_silo", true)) { + IO::writeData(timestep, visData, Dm->Comm); } - if (vis_db->getWithDefault( "save_8bit_raw", true )){ + if (vis_db->getWithDefault("save_8bit_raw", true)) { //TODO //char CurrentIDFilename[40]; //sprintf(CurrentIDFilename,"id_t%d.raw",timestep); //Averages.AggregateLabels(CurrentIDFilename); } - } -void ScaLBL_GreyscaleColorModel::WriteDebug(){ - // Copy back final phase indicator field and convert to regular layout - DoubleArray PhaseField(Nx,Ny,Nz); - //ScaLBL_Comm->RegularLayout(Map,Phi,PhaseField); - ScaLBL_CopyToHost(PhaseField.data(), Phi, sizeof(double)*N); +void ScaLBL_GreyscaleColorModel::WriteDebug() { + // Copy back final phase indicator field and convert to regular layout + DoubleArray PhaseField(Nx, Ny, Nz); + //ScaLBL_Comm->RegularLayout(Map,Phi,PhaseField); + ScaLBL_CopyToHost(PhaseField.data(), Phi, sizeof(double) * N); - FILE *OUTFILE; - sprintf(LocalRankFilename,"Phase.%05i.raw",rank); - OUTFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,OUTFILE); - fclose(OUTFILE); + FILE *OUTFILE; + sprintf(LocalRankFilename, "Phase.%05i.raw", rank); + OUTFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, OUTFILE); + fclose(OUTFILE); - //ScaLBL_CopyToHost(PhaseField.data(), Psi, sizeof(double)*N); - //FILE *PSIFILE; - //sprintf(LocalRankFilename,"Psi.%05i.raw",rank); - //PSIFILE = fopen(LocalRankFilename,"wb"); - //fwrite(PhaseField.data(),8,N,PSIFILE); - //fclose(PSIFILE); + //ScaLBL_CopyToHost(PhaseField.data(), Psi, sizeof(double)*N); + //FILE *PSIFILE; + //sprintf(LocalRankFilename,"Psi.%05i.raw",rank); + //PSIFILE = fopen(LocalRankFilename,"wb"); + //fwrite(PhaseField.data(),8,N,PSIFILE); + //fclose(PSIFILE); - ScaLBL_Comm->RegularLayout(Map,&Den[0],PhaseField); - FILE *AFILE; - sprintf(LocalRankFilename,"A.%05i.raw",rank); - AFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,AFILE); - fclose(AFILE); + ScaLBL_Comm->RegularLayout(Map, &Den[0], PhaseField); + FILE *AFILE; + sprintf(LocalRankFilename, "A.%05i.raw", rank); + AFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, AFILE); + fclose(AFILE); - ScaLBL_Comm->RegularLayout(Map,&Den[Np],PhaseField); - FILE *BFILE; - sprintf(LocalRankFilename,"B.%05i.raw",rank); - BFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,BFILE); - fclose(BFILE); + ScaLBL_Comm->RegularLayout(Map, &Den[Np], PhaseField); + FILE *BFILE; + sprintf(LocalRankFilename, "B.%05i.raw", rank); + BFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, BFILE); + fclose(BFILE); - ScaLBL_Comm->RegularLayout(Map,Pressure,PhaseField); - FILE *PFILE; - sprintf(LocalRankFilename,"Pressure.%05i.raw",rank); - PFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,PFILE); - fclose(PFILE); + ScaLBL_Comm->RegularLayout(Map, Pressure, PhaseField); + FILE *PFILE; + sprintf(LocalRankFilename, "Pressure.%05i.raw", rank); + PFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, PFILE); + fclose(PFILE); - ScaLBL_Comm->RegularLayout(Map,&Velocity[0],PhaseField); - FILE *VELX_FILE; - sprintf(LocalRankFilename,"Velocity_X.%05i.raw",rank); - VELX_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELX_FILE); - fclose(VELX_FILE); + ScaLBL_Comm->RegularLayout(Map, &Velocity[0], PhaseField); + FILE *VELX_FILE; + sprintf(LocalRankFilename, "Velocity_X.%05i.raw", rank); + VELX_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELX_FILE); + fclose(VELX_FILE); - ScaLBL_Comm->RegularLayout(Map,&Velocity[Np],PhaseField); - FILE *VELY_FILE; - sprintf(LocalRankFilename,"Velocity_Y.%05i.raw",rank); - VELY_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELY_FILE); - fclose(VELY_FILE); + ScaLBL_Comm->RegularLayout(Map, &Velocity[Np], PhaseField); + FILE *VELY_FILE; + sprintf(LocalRankFilename, "Velocity_Y.%05i.raw", rank); + VELY_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELY_FILE); + fclose(VELY_FILE); - ScaLBL_Comm->RegularLayout(Map,&Velocity[2*Np],PhaseField); - FILE *VELZ_FILE; - sprintf(LocalRankFilename,"Velocity_Z.%05i.raw",rank); - VELZ_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELZ_FILE); - fclose(VELZ_FILE); + ScaLBL_Comm->RegularLayout(Map, &Velocity[2 * Np], PhaseField); + FILE *VELZ_FILE; + sprintf(LocalRankFilename, "Velocity_Z.%05i.raw", rank); + VELZ_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELZ_FILE); + fclose(VELZ_FILE); - ScaLBL_Comm->RegularLayout(Map,&Porosity_dvc[0],PhaseField); - FILE *POROS_FILE; - sprintf(LocalRankFilename,"Porosity.%05i.raw",rank); - POROS_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,POROS_FILE); - fclose(POROS_FILE); + ScaLBL_Comm->RegularLayout(Map, &Porosity_dvc[0], PhaseField); + FILE *POROS_FILE; + sprintf(LocalRankFilename, "Porosity.%05i.raw", rank); + POROS_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, POROS_FILE); + fclose(POROS_FILE); - ScaLBL_Comm->RegularLayout(Map,&Permeability_dvc[0],PhaseField); - FILE *PERM_FILE; - sprintf(LocalRankFilename,"Permeability.%05i.raw",rank); - PERM_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,PERM_FILE); - fclose(PERM_FILE); + ScaLBL_Comm->RegularLayout(Map, &Permeability_dvc[0], PhaseField); + FILE *PERM_FILE; + sprintf(LocalRankFilename, "Permeability.%05i.raw", rank); + PERM_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, PERM_FILE); + fclose(PERM_FILE); - //ScaLBL_Comm->RegularLayout(Map,&GreySolidGrad[0],PhaseField); - //FILE *GreySG_X_FILE; - //sprintf(LocalRankFilename,"GreySolidGrad_X.%05i.raw",rank); - //GreySG_X_FILE = fopen(LocalRankFilename,"wb"); - //fwrite(PhaseField.data(),8,N,GreySG_X_FILE); - //fclose(GreySG_X_FILE); + //ScaLBL_Comm->RegularLayout(Map,&GreySolidGrad[0],PhaseField); + //FILE *GreySG_X_FILE; + //sprintf(LocalRankFilename,"GreySolidGrad_X.%05i.raw",rank); + //GreySG_X_FILE = fopen(LocalRankFilename,"wb"); + //fwrite(PhaseField.data(),8,N,GreySG_X_FILE); + //fclose(GreySG_X_FILE); - //ScaLBL_Comm->RegularLayout(Map,&GreySolidGrad[Np],PhaseField); - //FILE *GreySG_Y_FILE; - //sprintf(LocalRankFilename,"GreySolidGrad_Y.%05i.raw",rank); - //GreySG_Y_FILE = fopen(LocalRankFilename,"wb"); - //fwrite(PhaseField.data(),8,N,GreySG_Y_FILE); - //fclose(GreySG_Y_FILE); + //ScaLBL_Comm->RegularLayout(Map,&GreySolidGrad[Np],PhaseField); + //FILE *GreySG_Y_FILE; + //sprintf(LocalRankFilename,"GreySolidGrad_Y.%05i.raw",rank); + //GreySG_Y_FILE = fopen(LocalRankFilename,"wb"); + //fwrite(PhaseField.data(),8,N,GreySG_Y_FILE); + //fclose(GreySG_Y_FILE); - //ScaLBL_Comm->RegularLayout(Map,&GreySolidGrad[2*Np],PhaseField); - //FILE *GreySG_Z_FILE; - //sprintf(LocalRankFilename,"GreySolidGrad_Z.%05i.raw",rank); - //GreySG_Z_FILE = fopen(LocalRankFilename,"wb"); - //fwrite(PhaseField.data(),8,N,GreySG_Z_FILE); - //fclose(GreySG_Z_FILE); + //ScaLBL_Comm->RegularLayout(Map,&GreySolidGrad[2*Np],PhaseField); + //FILE *GreySG_Z_FILE; + //sprintf(LocalRankFilename,"GreySolidGrad_Z.%05i.raw",rank); + //GreySG_Z_FILE = fopen(LocalRankFilename,"wb"); + //fwrite(PhaseField.data(),8,N,GreySG_Z_FILE); + //fclose(GreySG_Z_FILE); -/* ScaLBL_Comm->RegularLayout(Map,&ColorGrad[0],PhaseField); + /* ScaLBL_Comm->RegularLayout(Map,&ColorGrad[0],PhaseField); FILE *CGX_FILE; sprintf(LocalRankFilename,"Gradient_X.%05i.raw",rank); CGX_FILE = fopen(LocalRankFilename,"wb"); @@ -1496,4 +1666,3 @@ void ScaLBL_GreyscaleColorModel::WriteDebug(){ fclose(CGZ_FILE); */ } - diff --git a/models/GreyscaleColorModel.h b/models/GreyscaleColorModel.h index fb9dd9e6..0c183f8a 100644 --- a/models/GreyscaleColorModel.h +++ b/models/GreyscaleColorModel.h @@ -25,8 +25,7 @@ Implementation of two-fluid greyscale color lattice boltzmann model * Mass transport equations are described by D3Q7 scheme */ - -class ScaLBL_GreyscaleColorModel{ +class ScaLBL_GreyscaleColorModel { public: /** * \brief Constructor @@ -34,75 +33,76 @@ public: * @param NP number of processors * @param COMM MPI communicator */ - ScaLBL_GreyscaleColorModel(int RANK, int NP, const Utilities::MPI& COMM); - ~ScaLBL_GreyscaleColorModel(); - - // functions in they should be run + ScaLBL_GreyscaleColorModel(int RANK, int NP, const Utilities::MPI &COMM); + ~ScaLBL_GreyscaleColorModel(); + + // functions in they should be run /** * \brief Read simulation parameters * @param filename input database file that includes "Color" section - */ - void ReadParams(string filename); - + */ + void ReadParams(string filename); + /** * \brief Read simulation parameters * @param db0 input database that includes "Color" section */ - void ReadParams(std::shared_ptr db0); - + void ReadParams(std::shared_ptr db0); + /** * \brief Create domain data structures */ - void SetDomain(); - + void SetDomain(); + /** * \brief Read image data */ - void ReadInput(); - + void ReadInput(); + /** * \brief Create color model data structures */ - void Create(); - + void Create(); + /** * \brief Initialize the simulation */ - void Initialize(); - + void Initialize(); + /** * \brief Run the simulation */ - void Run(); - + void Run(); + /** * \brief Debugging function to dump simulation state to disk */ - void WriteDebug(); + void WriteDebug(); void WriteVisFiles(); - - bool Restart,pBC; - bool REVERSE_FLOW_DIRECTION; - int timestep,timestepMax; - int BoundaryCondition; - double tauA,tauB,rhoA,rhoB,alpha,beta; - double tauA_eff,tauB_eff; - double Fx,Fy,Fz,flux; - double din,dout,inletA,inletB,outletA,outletB; - double GreyPorosity; - bool RecoloringOff;//recoloring can be turn off for grey nodes if this is true - //double W;//wetting strength paramter for capillary pressure penalty for grey nodes - - int Nx,Ny,Nz,N,Np; - int rank,nprocx,nprocy,nprocz,nprocs; - double Lx,Ly,Lz; - std::shared_ptr Dm; // this domain is for analysis - std::shared_ptr Mask; // this domain is for lbm - std::shared_ptr ScaLBL_Comm; - std::shared_ptr ScaLBL_Comm_Regular; + bool Restart, pBC; + bool REVERSE_FLOW_DIRECTION; + int timestep, timestepMax; + int BoundaryCondition; + double tauA, tauB, rhoA, rhoB, alpha, beta; + double tauA_eff, tauB_eff; + double Fx, Fy, Fz, flux; + double din, dout, inletA, inletB, outletA, outletB; + double GreyPorosity; + bool + RecoloringOff; //recoloring can be turn off for grey nodes if this is true + //double W;//wetting strength paramter for capillary pressure penalty for grey nodes + + int Nx, Ny, Nz, N, Np; + int rank, nprocx, nprocy, nprocz, nprocs; + double Lx, Ly, Lz; + + std::shared_ptr Dm; // this domain is for analysis + std::shared_ptr Mask; // this domain is for lbm + std::shared_ptr ScaLBL_Comm; + std::shared_ptr ScaLBL_Comm_Regular; std::shared_ptr Averages; - + // input database std::shared_ptr db; std::shared_ptr domain_db; @@ -111,11 +111,11 @@ public: std::shared_ptr vis_db; IntArray Map; - signed char *id; - int *NeighborList; - int *dvcMap; - double *fq, *Aq, *Bq; - double *Den, *Phi; + signed char *id; + int *NeighborList; + int *dvcMap; + double *fq, *Aq, *Bq; + double *Den, *Phi; //double *GreySolidPhi; //Model 2 & 3 //double *GreySolidGrad;//Model 1 & 4 double *GreySolidW; @@ -123,31 +123,31 @@ public: double *GreySw; double *GreyKn; double *GreyKw; - double *MobilityRatio; - double *Velocity; - double *Pressure; + double *MobilityRatio; + double *Velocity; + double *Pressure; double *Porosity_dvc; double *Permeability_dvc; //double *Psi; - + private: - Utilities::MPI comm; - - int dist_mem_size; - int neighborSize; - // filenames + Utilities::MPI comm; + + int dist_mem_size; + int neighborSize; + // filenames char LocalRankString[8]; char LocalRankFilename[40]; char LocalRestartFile[40]; - + //int rank,nprocs; void LoadParams(std::shared_ptr db0); - + /** * \brief Assign wetting affinity values */ void AssignComponentLabels(); - + /** * \brief Assign wetting affinity values in greyscale regions */ @@ -161,4 +161,3 @@ private: */ double SeedPhaseField(const double seed_water_in_oil); }; - diff --git a/models/GreyscaleModel.cpp b/models/GreyscaleModel.cpp index 32f68197..f1df0809 100644 --- a/models/GreyscaleModel.cpp +++ b/models/GreyscaleModel.cpp @@ -22,876 +22,982 @@ #include #include -template -void DeleteArray( const TYPE *p ) -{ - delete [] p; -} +template void DeleteArray(const TYPE *p) { delete[] p; } -ScaLBL_GreyscaleModel::ScaLBL_GreyscaleModel(int RANK, int NP, const Utilities::MPI& COMM): -rank(RANK), nprocs(NP), Restart(0),timestep(0),timestepMax(0),tau(0),tau_eff(0),Den(0),Fx(0),Fy(0),Fz(0),flux(0),din(0),dout(0),GreyPorosity(0), -Nx(0),Ny(0),Nz(0),N(0),Np(0),nprocx(0),nprocy(0),nprocz(0),BoundaryCondition(0),Lx(0),Ly(0),Lz(0),comm(COMM) -{ - SignDist.resize(Nx,Ny,Nz); +ScaLBL_GreyscaleModel::ScaLBL_GreyscaleModel(int RANK, int NP, + const Utilities::MPI &COMM) + : rank(RANK), nprocs(NP), Restart(0), timestep(0), timestepMax(0), tau(0), + tau_eff(0), Den(0), Fx(0), Fy(0), Fz(0), flux(0), din(0), dout(0), + GreyPorosity(0), Nx(0), Ny(0), Nz(0), N(0), Np(0), nprocx(0), nprocy(0), + nprocz(0), BoundaryCondition(0), Lx(0), Ly(0), Lz(0), comm(COMM) { + SignDist.resize(Nx, Ny, Nz); SignDist.fill(0); - } -ScaLBL_GreyscaleModel::~ScaLBL_GreyscaleModel(){ +ScaLBL_GreyscaleModel::~ScaLBL_GreyscaleModel() {} -} +void ScaLBL_GreyscaleModel::ReadParams(string filename) { + // read the input database + db = std::make_shared(filename); + domain_db = db->getDatabase("Domain"); + greyscale_db = db->getDatabase("Greyscale"); + analysis_db = db->getDatabase("Analysis"); + vis_db = db->getDatabase("Visualization"); -void ScaLBL_GreyscaleModel::ReadParams(string filename){ - // read the input database - db = std::make_shared( filename ); - domain_db = db->getDatabase( "Domain" ); - greyscale_db = db->getDatabase( "Greyscale" ); - analysis_db = db->getDatabase( "Analysis" ); - vis_db = db->getDatabase( "Visualization" ); - - // set defaults - timestepMax = 100000; - tau = 1.0; + // set defaults + timestepMax = 100000; + tau = 1.0; tau_eff = tau; - Den = 1.0;//constant density - tolerance = 0.01; - Fx = Fy = Fz = 0.0; - Restart=false; - din=dout=1.0; - flux=0.0; - dp = 10.0; //unit of 'dp': voxel + Den = 1.0; //constant density + tolerance = 0.01; + Fx = Fy = Fz = 0.0; + Restart = false; + din = dout = 1.0; + flux = 0.0; + dp = 10.0; //unit of 'dp': voxel CollisionType = 1; //1: IMRT; 2: BGK; 3: MRT - - // ---------------------- Greyscale Model parameters -----------------------// - if (greyscale_db->keyExists( "timestepMax" )){ - timestepMax = greyscale_db->getScalar( "timestepMax" ); - } - if (greyscale_db->keyExists( "tau" )){ - tau = greyscale_db->getScalar( "tau" ); - } - tau_eff = greyscale_db->getWithDefault( "tau_eff", tau ); - if (greyscale_db->keyExists( "Den" )){ - Den = greyscale_db->getScalar( "Den" ); - } - if (greyscale_db->keyExists( "dp" )){ - dp = greyscale_db->getScalar( "dp" ); - } - if (greyscale_db->keyExists( "F" )){ - Fx = greyscale_db->getVector( "F" )[0]; - Fy = greyscale_db->getVector( "F" )[1]; - Fz = greyscale_db->getVector( "F" )[2]; - } - if (greyscale_db->keyExists( "Restart" )){ - Restart = greyscale_db->getScalar( "Restart" ); - } - if (greyscale_db->keyExists( "din" )){ - din = greyscale_db->getScalar( "din" ); - } - if (greyscale_db->keyExists( "dout" )){ - dout = greyscale_db->getScalar( "dout" ); - } - if (greyscale_db->keyExists( "flux" )){ - flux = greyscale_db->getScalar( "flux" ); - } - if (greyscale_db->keyExists( "tolerance" )){ - tolerance = greyscale_db->getScalar( "tolerance" ); - } - auto collision = greyscale_db->getWithDefault( "collision", "IMRT" ); - if (collision == "BGK"){ - CollisionType=2; - } - else if (collision == "MRT"){ - CollisionType=3; + + // ---------------------- Greyscale Model parameters -----------------------// + if (greyscale_db->keyExists("timestepMax")) { + timestepMax = greyscale_db->getScalar("timestepMax"); } - // ------------------------------------------------------------------------// - + if (greyscale_db->keyExists("tau")) { + tau = greyscale_db->getScalar("tau"); + } + tau_eff = greyscale_db->getWithDefault("tau_eff", tau); + if (greyscale_db->keyExists("Den")) { + Den = greyscale_db->getScalar("Den"); + } + if (greyscale_db->keyExists("dp")) { + dp = greyscale_db->getScalar("dp"); + } + if (greyscale_db->keyExists("F")) { + Fx = greyscale_db->getVector("F")[0]; + Fy = greyscale_db->getVector("F")[1]; + Fz = greyscale_db->getVector("F")[2]; + } + if (greyscale_db->keyExists("Restart")) { + Restart = greyscale_db->getScalar("Restart"); + } + if (greyscale_db->keyExists("din")) { + din = greyscale_db->getScalar("din"); + } + if (greyscale_db->keyExists("dout")) { + dout = greyscale_db->getScalar("dout"); + } + if (greyscale_db->keyExists("flux")) { + flux = greyscale_db->getScalar("flux"); + } + if (greyscale_db->keyExists("tolerance")) { + tolerance = greyscale_db->getScalar("tolerance"); + } + auto collision = + greyscale_db->getWithDefault("collision", "IMRT"); + if (collision == "BGK") { + CollisionType = 2; + } else if (collision == "MRT") { + CollisionType = 3; + } + // ------------------------------------------------------------------------// + //------------------------ Other Domain parameters ------------------------// - BoundaryCondition = 0; - if (greyscale_db->keyExists( "BC" )){ - BoundaryCondition = greyscale_db->getScalar( "BC" ); - } - else if (domain_db->keyExists( "BC" )){ - BoundaryCondition = domain_db->getScalar( "BC" ); - } - // ------------------------------------------------------------------------// + BoundaryCondition = 0; + if (greyscale_db->keyExists("BC")) { + BoundaryCondition = greyscale_db->getScalar("BC"); + } else if (domain_db->keyExists("BC")) { + BoundaryCondition = domain_db->getScalar("BC"); + } + // ------------------------------------------------------------------------// } -void ScaLBL_GreyscaleModel::SetDomain(){ - Dm = std::shared_ptr(new Domain(domain_db,comm)); // full domain for analysis - Mask = std::shared_ptr(new Domain(domain_db,comm)); // mask domain removes immobile phases - // domain parameters - Nx = Dm->Nx; - Ny = Dm->Ny; - Nz = Dm->Nz; - Lx = Dm->Lx; - Ly = Dm->Ly; - Lz = Dm->Lz; - N = Nx*Ny*Nz; +void ScaLBL_GreyscaleModel::SetDomain() { + Dm = std::shared_ptr( + new Domain(domain_db, comm)); // full domain for analysis + Mask = std::shared_ptr( + new Domain(domain_db, comm)); // mask domain removes immobile phases + // domain parameters + Nx = Dm->Nx; + Ny = Dm->Ny; + Nz = Dm->Nz; + Lx = Dm->Lx; + Ly = Dm->Ly; + Lz = Dm->Lz; + N = Nx * Ny * Nz; - SignDist.resize(Nx,Ny,Nz); - Velocity_x.resize(Nx,Ny,Nz); - Velocity_y.resize(Nx,Ny,Nz); - Velocity_z.resize(Nx,Ny,Nz); - PorosityMap.resize(Nx,Ny,Nz); - Pressure.resize(Nx,Ny,Nz); + SignDist.resize(Nx, Ny, Nz); + Velocity_x.resize(Nx, Ny, Nz); + Velocity_y.resize(Nx, Ny, Nz); + Velocity_z.resize(Nx, Ny, Nz); + PorosityMap.resize(Nx, Ny, Nz); + Pressure.resize(Nx, Ny, Nz); - id = new signed char [N]; - for (int i=0; iid[i] = 1; // initialize this way - comm.barrier(); - Dm->CommInit(); - comm.barrier(); - // Read domain parameters - rank = Dm->rank(); - nprocx = Dm->nprocx(); - nprocy = Dm->nprocy(); - nprocz = Dm->nprocz(); + id = new signed char[N]; + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = 1; // initialize this way + comm.barrier(); + Dm->CommInit(); + comm.barrier(); + // Read domain parameters + rank = Dm->rank(); + nprocx = Dm->nprocx(); + nprocy = Dm->nprocy(); + nprocz = Dm->nprocz(); } -void ScaLBL_GreyscaleModel::ReadInput(){ - - sprintf(LocalRankString,"%05d",rank); - sprintf(LocalRankFilename,"%s%s","ID.",LocalRankString); - sprintf(LocalRestartFile,"%s%s","Restart.",LocalRankString); - - if (domain_db->keyExists( "Filename" )){ - auto Filename = domain_db->getScalar( "Filename" ); - Mask->Decomp(Filename); - } - else{ - if (rank==0) printf("Filename of input image is not found, reading ID.0* instead."); - Mask->ReadIDs(); - } - for (int i=0; iid[i]; // save what was read - - // Generate the signed distance map - // Initialize the domain and communication - Array id_solid(Nx,Ny,Nz); - // Solve for the position of the solid phase - for (int k=0;kid[n]; - if (label > 0) id_solid(i,j,k) = 1; - else id_solid(i,j,k) = 0; - } - } - } - // Initialize the signed distance function - for (int k=0;kkeyExists("Filename")) { + auto Filename = domain_db->getScalar("Filename"); + Mask->Decomp(Filename); + } else { + if (rank == 0) + printf( + "Filename of input image is not found, reading ID.0* instead."); + Mask->ReadIDs(); + } + for (int i = 0; i < Nx * Ny * Nz; i++) + id[i] = Mask->id[i]; // save what was read + + // Generate the signed distance map + // Initialize the domain and communication + Array id_solid(Nx, Ny, Nz); + // Solve for the position of the solid phase + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + // Initialize the solid phase + signed char label = Mask->id[n]; + if (label > 0) + id_solid(i, j, k) = 1; + else + id_solid(i, j, k) = 0; + } + } + } + // Initialize the signed distance function + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + // Initialize distance to +/- 1 + SignDist(i, j, k) = 2.0 * double(id_solid(i, j, k)) - 1.0; + } + } + } + // MeanFilter(SignDist); + if (rank == 0) + printf("Initialized solid phase -- Converting to Signed Distance " + "function \n"); + CalcDist(SignDist, id_solid, *Mask); + + if (rank == 0) + cout << "Domain set." << endl; } /******************************************************** * AssignComponentLabels * ********************************************************/ -void ScaLBL_GreyscaleModel::AssignComponentLabels(double *Porosity, double *Permeability) -{ - size_t NLABELS=0; - signed char VALUE=0; - double POROSITY=0.f; - double PERMEABILITY=0.f; +void ScaLBL_GreyscaleModel::AssignComponentLabels(double *Porosity, + double *Permeability) { + size_t NLABELS = 0; + signed char VALUE = 0; + double POROSITY = 0.f; + double PERMEABILITY = 0.f; - auto LabelList = greyscale_db->getVector( "ComponentLabels" ); - auto PorosityList = greyscale_db->getVector( "PorosityList" ); - auto PermeabilityList = greyscale_db->getVector( "PermeabilityList" ); + auto LabelList = greyscale_db->getVector("ComponentLabels"); + auto PorosityList = greyscale_db->getVector("PorosityList"); + auto PermeabilityList = greyscale_db->getVector("PermeabilityList"); - NLABELS=LabelList.size(); - if (NLABELS != PorosityList.size()){ - ERROR("Error: ComponentLabels and PorosityList must be the same length! \n"); - } - - // Assign the labels - double *label_count; - double *label_count_global; - label_count = new double [NLABELS]; - label_count_global = new double [NLABELS]; - - for (size_t idx=0; idxid[n] = 0; // set mask to zero since this is an immobile component - } - } - int idx = Map(i,j,k); - if (!(idx < 0)){ - if (POROSITY<=0.0){ - ERROR("Error: Porosity for grey voxels must be 0.0 < Porosity <= 1.0 !\n"); - } - else{ - Porosity[idx] = POROSITY; - } - } - } - } - } - - if (NLABELS != PermeabilityList.size()){ - ERROR("Error: ComponentLabels and PermeabilityList must be the same length! \n"); - } - for (int k=0;kid[n] = 0; // set mask to zero since this is an immobile component - } - } - int idx = Map(i,j,k); - if (!(idx < 0)){ - if (PERMEABILITY<=0.0){ - ERROR("Error: Permeability for grey voxel must be > 0.0 ! \n"); - } - else{ - Permeability[idx] = PERMEABILITY/Dm->voxel_length/Dm->voxel_length; - } - } - } - } - } - - - // Set Dm to match Mask - for (int i=0; iid[i] = Mask->id[i]; - - for (size_t idx=0; idxComm.sumReduce( label_count[idx]); - //Initialize a weighted porosity after considering grey voxels - GreyPorosity=0.0; - for (unsigned int idx=0; idxvoxel_length); - printf("Number of component labels: %lu \n",NLABELS); - for (unsigned int idx=0; idxvoxel_length/Dm->voxel_length,volume_fraction); - printf(" effective porosity=%.3g\n",volume_fraction*POROSITY); - } - printf("The weighted porosity, considering both open and grey voxels, is %.3g\n",GreyPorosity); - } + // Assign the labels + double *label_count; + double *label_count_global; + label_count = new double[NLABELS]; + label_count_global = new double[NLABELS]; + + for (size_t idx = 0; idx < NLABELS; idx++) + label_count[idx] = 0; + + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + VALUE = id[n]; + // Assign the affinity from the paired list + for (size_t idx = 0; idx < NLABELS; idx++) { + //printf("idx=%i, value=%i, %i, \n",idx, VALUE,LabelList[idx]); + if (VALUE == LabelList[idx]) { + POROSITY = PorosityList[idx]; + label_count[idx] += 1.0; + idx = NLABELS; + //Mask->id[n] = 0; // set mask to zero since this is an immobile component + } + } + int idx = Map(i, j, k); + if (!(idx < 0)) { + if (POROSITY <= 0.0) { + ERROR("Error: Porosity for grey voxels must be 0.0 < " + "Porosity <= 1.0 !\n"); + } else { + Porosity[idx] = POROSITY; + } + } + } + } + } + + if (NLABELS != PermeabilityList.size()) { + ERROR("Error: ComponentLabels and PermeabilityList must be the same " + "length! \n"); + } + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + VALUE = id[n]; + // Assign the affinity from the paired list + for (size_t idx = 0; idx < NLABELS; idx++) { + //printf("idx=%i, value=%i, %i, \n",idx, VALUE,LabelList[idx]); + if (VALUE == LabelList[idx]) { + PERMEABILITY = PermeabilityList[idx]; + idx = NLABELS; + //Mask->id[n] = 0; // set mask to zero since this is an immobile component + } + } + int idx = Map(i, j, k); + if (!(idx < 0)) { + if (PERMEABILITY <= 0.0) { + ERROR("Error: Permeability for grey voxel must be > " + "0.0 ! \n"); + } else { + Permeability[idx] = + PERMEABILITY / Dm->voxel_length / Dm->voxel_length; + } + } + } + } + } + + // Set Dm to match Mask + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = Mask->id[i]; + + for (size_t idx = 0; idx < NLABELS; idx++) + label_count_global[idx] = Dm->Comm.sumReduce(label_count[idx]); + //Initialize a weighted porosity after considering grey voxels + GreyPorosity = 0.0; + for (unsigned int idx = 0; idx < NLABELS; idx++) { + double volume_fraction = + double(label_count_global[idx]) / + double((Nx - 2) * (Ny - 2) * (Nz - 2) * nprocs); + GreyPorosity += volume_fraction * PorosityList[idx]; + } + + if (rank == 0) { + printf("Image resolution: %.5g [um/voxel]\n", Dm->voxel_length); + printf("Number of component labels: %lu \n", NLABELS); + for (unsigned int idx = 0; idx < NLABELS; idx++) { + VALUE = LabelList[idx]; + POROSITY = PorosityList[idx]; + PERMEABILITY = PermeabilityList[idx]; + double volume_fraction = + double(label_count_global[idx]) / + double((Nx - 2) * (Ny - 2) * (Nz - 2) * nprocs); + printf(" label=%d: porosity=%.3g, permeability=%.3g [um^2] " + "(=%.3g [voxel^2]), volume fraction=%.3g\n", + VALUE, POROSITY, PERMEABILITY, + PERMEABILITY / Dm->voxel_length / Dm->voxel_length, + volume_fraction); + printf(" effective porosity=%.3g\n", + volume_fraction * POROSITY); + } + printf("The weighted porosity, considering both open and grey voxels, " + "is %.3g\n", + GreyPorosity); + } } -void ScaLBL_GreyscaleModel::AssignComponentLabels(double *Porosity,double *Permeability,const vector &File_poro,const vector &File_perm) -{ +void ScaLBL_GreyscaleModel::AssignComponentLabels( + double *Porosity, double *Permeability, + const vector &File_poro, + const vector &File_perm) { double *Porosity_host, *Permeability_host; Porosity_host = new double[N]; Permeability_host = new double[N]; - double POROSITY=0.f; - double PERMEABILITY=0.f; + double POROSITY = 0.f; + double PERMEABILITY = 0.f; //Initialize a weighted porosity after considering grey voxels - double GreyPorosity_loc=0.0; - GreyPorosity=0.0; + double GreyPorosity_loc = 0.0; + GreyPorosity = 0.0; //double label_count_loc = 0.0; //double label_count_glb = 0.0; - Mask->ReadFromFile(File_poro[0],File_poro[1],Porosity_host); - Mask->ReadFromFile(File_perm[0],File_perm[1],Permeability_host); + Mask->ReadFromFile(File_poro[0], File_poro[1], Porosity_host); + Mask->ReadFromFile(File_perm[0], File_perm[1], Permeability_host); - for (int k=0;k 0.0 ! \n"); - } - else{ + if (POROSITY <= 0.0) { + ERROR("Error: Porosity for grey voxels must be 0.0 < " + "Porosity <= 1.0 !\n"); + } else if (PERMEABILITY <= 0.0) { + ERROR("Error: Permeability for grey voxel must be > " + "0.0 ! \n"); + } else { Porosity[idx] = POROSITY; Permeability[idx] = PERMEABILITY; GreyPorosity_loc += POROSITY; //label_count_loc += 1.0; } } - } - } - } - GreyPorosity = Dm->Comm.sumReduce( GreyPorosity_loc); - GreyPorosity = GreyPorosity/double((Nx-2)*(Ny-2)*(Nz-2)*nprocs); + } + } + } + GreyPorosity = Dm->Comm.sumReduce(GreyPorosity_loc); + GreyPorosity = + GreyPorosity / double((Nx - 2) * (Ny - 2) * (Nz - 2) * nprocs); - if (rank==0){ - printf("Image resolution: %.5g [um/voxel]\n",Dm->voxel_length); - printf("The weighted porosity, considering both open and grey voxels, is %.3g\n",GreyPorosity); - } - delete [] Porosity_host; - delete [] Permeability_host; + if (rank == 0) { + printf("Image resolution: %.5g [um/voxel]\n", Dm->voxel_length); + printf("The weighted porosity, considering both open and grey voxels, " + "is %.3g\n", + GreyPorosity); + } + delete[] Porosity_host; + delete[] Permeability_host; } -void ScaLBL_GreyscaleModel::Create(){ - /* +void ScaLBL_GreyscaleModel::Create() { + /* * This function creates the variables needed to run a LBM */ - //......................................................... - // don't perform computations at the eight corners - //id[0] = id[Nx-1] = id[(Ny-1)*Nx] = id[(Ny-1)*Nx + Nx-1] = 0; - //id[(Nz-1)*Nx*Ny] = id[(Nz-1)*Nx*Ny+Nx-1] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx + Nx-1] = 0; + //......................................................... + // don't perform computations at the eight corners + //id[0] = id[Nx-1] = id[(Ny-1)*Nx] = id[(Ny-1)*Nx + Nx-1] = 0; + //id[(Nz-1)*Nx*Ny] = id[(Nz-1)*Nx*Ny+Nx-1] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx + Nx-1] = 0; - //......................................................... - // Initialize communication structures in averaging domain - for (int i=0; iid[i] = Mask->id[i]; - Mask->CommInit(); - Np=Mask->PoreCount(); - //........................................................................... - if (rank==0) printf ("Create ScaLBL_Communicator \n"); - // Create a communicator for the device (will use optimized layout) - // ScaLBL_Communicator ScaLBL_Comm(Mask); // original - ScaLBL_Comm = std::shared_ptr(new ScaLBL_Communicator(Mask)); + //......................................................... + // Initialize communication structures in averaging domain + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = Mask->id[i]; + Mask->CommInit(); + Np = Mask->PoreCount(); + //........................................................................... + if (rank == 0) + printf("Create ScaLBL_Communicator \n"); + // Create a communicator for the device (will use optimized layout) + // ScaLBL_Communicator ScaLBL_Comm(Mask); // original + ScaLBL_Comm = + std::shared_ptr(new ScaLBL_Communicator(Mask)); - int Npad=(Np/16 + 2)*16; - if (rank==0) printf ("Set up memory efficient layout, %i | %i | %i \n", Np, Npad, N); - Map.resize(Nx,Ny,Nz); Map.fill(-2); - auto neighborList= new int[18*Npad]; - Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map,neighborList,Mask->id.data(),Np,1); - comm.barrier(); + int Npad = (Np / 16 + 2) * 16; + if (rank == 0) + printf("Set up memory efficient layout, %i | %i | %i \n", Np, Npad, N); + Map.resize(Nx, Ny, Nz); + Map.fill(-2); + auto neighborList = new int[18 * Npad]; + Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map, neighborList, + Mask->id.data(), Np, 1); + comm.barrier(); - //........................................................................... - // MAIN VARIABLES ALLOCATED HERE - //........................................................................... - // LBM variables - if (rank==0) printf ("Allocating distributions \n"); - //......................device distributions................................. - dist_mem_size = Np*sizeof(double); - neighborSize=18*(Np*sizeof(int)); - //........................................................................... - ScaLBL_AllocateDeviceMemory((void **) &NeighborList, neighborSize); - ScaLBL_AllocateDeviceMemory((void **) &fq, 19*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Permeability, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &Porosity, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &Pressure_dvc, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &Velocity, 3*sizeof(double)*Np); - //........................................................................... - // Update GPU data structures - if (rank==0) printf ("Setting up device neighbor list \n"); - fflush(stdout); - // copy the neighbor list - ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); - // initialize phi based on PhaseLabel (include solid component labels) - double *Poros, *Perm; - Poros = new double[Np]; - Perm = new double[Np]; - if (greyscale_db->keyExists("FileVoxelPorosityMap")){ + //........................................................................... + // MAIN VARIABLES ALLOCATED HERE + //........................................................................... + // LBM variables + if (rank == 0) + printf("Allocating distributions \n"); + //......................device distributions................................. + dist_mem_size = Np * sizeof(double); + neighborSize = 18 * (Np * sizeof(int)); + //........................................................................... + ScaLBL_AllocateDeviceMemory((void **)&NeighborList, neighborSize); + ScaLBL_AllocateDeviceMemory((void **)&fq, 19 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Permeability, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&Porosity, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&Pressure_dvc, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&Velocity, 3 * sizeof(double) * Np); + //........................................................................... + // Update GPU data structures + if (rank == 0) + printf("Setting up device neighbor list \n"); + fflush(stdout); + // copy the neighbor list + ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); + // initialize phi based on PhaseLabel (include solid component labels) + double *Poros, *Perm; + Poros = new double[Np]; + Perm = new double[Np]; + if (greyscale_db->keyExists("FileVoxelPorosityMap")) { //NOTE: FileVoxel**Map is a vector, including "file_name, datatype" - auto File_poro = greyscale_db->getVector( "FileVoxelPorosityMap" ); - auto File_perm = greyscale_db->getVector( "FileVoxelPermeabilityMap" ); - AssignComponentLabels(Poros,Perm,File_poro,File_perm); - } - else if (greyscale_db->keyExists("PorosityList")){ + auto File_poro = + greyscale_db->getVector("FileVoxelPorosityMap"); + auto File_perm = + greyscale_db->getVector("FileVoxelPermeabilityMap"); + AssignComponentLabels(Poros, Perm, File_poro, File_perm); + } else if (greyscale_db->keyExists("PorosityList")) { //initialize voxel porosity and perm from the input list - AssignComponentLabels(Poros,Perm); + AssignComponentLabels(Poros, Perm); + } else { + ERROR("Error: PorosityList or FilenameVoxelPorosityMap cannot be " + "found! \n"); } - else { - ERROR("Error: PorosityList or FilenameVoxelPorosityMap cannot be found! \n"); - } - ScaLBL_CopyToDevice(Porosity, Poros, Np*sizeof(double)); - ScaLBL_CopyToDevice(Permeability, Perm, Np*sizeof(double)); - delete [] Poros; - delete [] Perm; -} + ScaLBL_CopyToDevice(Porosity, Poros, Np * sizeof(double)); + ScaLBL_CopyToDevice(Permeability, Perm, Np * sizeof(double)); + delete[] Poros; + delete[] Perm; +} - -void ScaLBL_GreyscaleModel::Initialize(){ - if (rank==0) printf ("Initializing distributions \n"); +void ScaLBL_GreyscaleModel::Initialize() { + if (rank == 0) + printf("Initializing distributions \n"); //TODO: for BGK, you need to consider voxel porosity // for IMRT, the whole set of feq is different // if in the future you have different collison mode, need to write two set of initialization functions - if (CollisionType==1){ - ScaLBL_D3Q19_GreyIMRT_Init(fq, Np, Den); - if (rank==0) printf("Collision model: Incompressible MRT.\n"); - } - else if (CollisionType==2){ - ScaLBL_D3Q19_Init(fq, Np); - if (rank==0) printf("Collision model: BGK.\n"); - } - else if (CollisionType==3){ - ScaLBL_D3Q19_Init(fq, Np); - if (rank==0) printf("Collision model: MRT.\n"); - } - else{ - if (rank==0) printf("Unknown collison type! IMRT collision is used.\n"); - ScaLBL_D3Q19_GreyIMRT_Init(fq, Np, Den); - CollisionType=1; - greyscale_db->putScalar( "collision", "IMRT" ); + if (CollisionType == 1) { + ScaLBL_D3Q19_GreyIMRT_Init(fq, Np, Den); + if (rank == 0) + printf("Collision model: Incompressible MRT.\n"); + } else if (CollisionType == 2) { + ScaLBL_D3Q19_Init(fq, Np); + if (rank == 0) + printf("Collision model: BGK.\n"); + } else if (CollisionType == 3) { + ScaLBL_D3Q19_Init(fq, Np); + if (rank == 0) + printf("Collision model: MRT.\n"); + } else { + if (rank == 0) + printf("Unknown collison type! IMRT collision is used.\n"); + ScaLBL_D3Q19_GreyIMRT_Init(fq, Np, Den); + CollisionType = 1; + greyscale_db->putScalar("collision", "IMRT"); } - if (Restart == true){ - if (rank==0){ - printf("Initializing distributions from Restart! \n"); - } - double value; + if (Restart == true) { + if (rank == 0) { + printf("Initializing distributions from Restart! \n"); + } + double value; double *cfq; - cfq = new double[19*Np]; - ifstream File(LocalRestartFile,ios::binary); - for (int n=0; nkeyExists( "analysis_interval" )){ - analysis_interval = analysis_db->getScalar( "analysis_interval" ); - } - if (analysis_db->keyExists( "visualization_interval" )){ - visualization_interval = analysis_db->getScalar( "visualization_interval" ); - } - if (analysis_db->keyExists( "restart_interval" )){ - restart_interval = analysis_db->getScalar( "restart_interval" ); - } - if (greyscale_db->keyExists( "timestep" )){ - timestep = greyscale_db->getScalar( "timestep" ); - } +void ScaLBL_GreyscaleModel::Run() { + int nprocs = nprocx * nprocy * nprocz; + const RankInfoStruct rank_info(rank, nprocx, nprocy, nprocz); - if (rank==0){ - printf("********************************************************\n"); - printf("No. of timesteps: %i \n", timestepMax); - fflush(stdout); - } + int analysis_interval = + 1000; // number of timesteps in between in situ analysis + int visualization_interval = 1000; + int restart_interval = + 10000; // number of timesteps in between in saving distributions for restart + if (analysis_db->keyExists("analysis_interval")) { + analysis_interval = analysis_db->getScalar("analysis_interval"); + } + if (analysis_db->keyExists("visualization_interval")) { + visualization_interval = + analysis_db->getScalar("visualization_interval"); + } + if (analysis_db->keyExists("restart_interval")) { + restart_interval = analysis_db->getScalar("restart_interval"); + } + if (greyscale_db->keyExists("timestep")) { + timestep = greyscale_db->getScalar("timestep"); + } - //.......create and start timer............ - ScaLBL_DeviceBarrier(); - comm.barrier(); - //......................................... - - Minkowski Morphology(Mask); + if (rank == 0) { + printf("********************************************************\n"); + printf("No. of timesteps: %i \n", timestepMax); + fflush(stdout); + } - //************ MAIN ITERATION LOOP ***************************************/ - PROFILE_START("Loop"); - auto current_db = db->cloneDatabase(); - double rlx = 1.0/tau; - double rlx_eff = 1.0/tau_eff; - double error = 1.0; - double flow_rate_previous = 0.0; + //.......create and start timer............ + ScaLBL_DeviceBarrier(); + comm.barrier(); + //......................................... + + Minkowski Morphology(Mask); + + //************ MAIN ITERATION LOOP ***************************************/ + PROFILE_START("Loop"); + auto current_db = db->cloneDatabase(); + double rlx = 1.0 / tau; + double rlx_eff = 1.0 / tau_eff; + double error = 1.0; + double flow_rate_previous = 0.0; auto t1 = std::chrono::system_clock::now(); - while (timestep < timestepMax && error > tolerance) { - //************************************************************************/ - // *************ODD TIMESTEP*************// - timestep++; - ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL - switch (CollisionType){ - case 1: - ScaLBL_D3Q19_AAodd_Greyscale_IMRT(NeighborList, fq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np, rlx, rlx_eff, Fx, Fy, Fz,Porosity,Permeability,Velocity,Den,Pressure_dvc); - break; - case 2: - ScaLBL_D3Q19_AAodd_Greyscale(NeighborList, fq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np, rlx, rlx_eff, Fx, Fy, Fz,Porosity,Permeability,Velocity,Pressure_dvc); - break; - case 3: - ScaLBL_D3Q19_AAodd_Greyscale_MRT(NeighborList, fq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np, rlx, rlx_eff, Fx, Fy, Fz,Porosity,Permeability,Velocity,Den,Pressure_dvc); - break; - default: - ScaLBL_D3Q19_AAodd_Greyscale_IMRT(NeighborList, fq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np, rlx, rlx_eff, Fx, Fy, Fz,Porosity,Permeability,Velocity,Den,Pressure_dvc); - break; - } - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - ScaLBL_DeviceBarrier(); - // Set BCs - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - switch (CollisionType){ - case 1: - ScaLBL_D3Q19_AAodd_Greyscale_IMRT(NeighborList, fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx, rlx_eff, Fx, Fy, Fz,Porosity,Permeability,Velocity,Den,Pressure_dvc); - break; - case 2: - ScaLBL_D3Q19_AAodd_Greyscale(NeighborList, fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx, rlx_eff, Fx, Fy, Fz,Porosity,Permeability,Velocity,Pressure_dvc); - break; - case 3: - ScaLBL_D3Q19_AAodd_Greyscale_MRT(NeighborList, fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx, rlx_eff, Fx, Fy, Fz,Porosity,Permeability,Velocity,Den,Pressure_dvc); - break; - default: - ScaLBL_D3Q19_AAodd_Greyscale_IMRT(NeighborList, fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx, rlx_eff, Fx, Fy, Fz,Porosity,Permeability,Velocity,Den,Pressure_dvc); - break; - } - ScaLBL_DeviceBarrier(); comm.barrier(); - - // *************EVEN TIMESTEP*************// - timestep++; - ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL - switch (CollisionType){ - case 1: - ScaLBL_D3Q19_AAeven_Greyscale_IMRT(fq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np, rlx, rlx_eff, Fx, Fy, Fz,Porosity,Permeability,Velocity,Den,Pressure_dvc); - break; - case 2: - ScaLBL_D3Q19_AAeven_Greyscale(fq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np, rlx, rlx_eff, Fx, Fy, Fz,Porosity,Permeability,Velocity,Pressure_dvc); - break; - case 3: - ScaLBL_D3Q19_AAeven_Greyscale_MRT(fq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np, rlx, rlx_eff, Fx, Fy, Fz,Porosity,Permeability,Velocity,Den,Pressure_dvc); - break; - default: - ScaLBL_D3Q19_AAeven_Greyscale_IMRT(fq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np, rlx, rlx_eff, Fx, Fy, Fz,Porosity,Permeability,Velocity,Den,Pressure_dvc); - break; + while (timestep < timestepMax && error > tolerance) { + //************************************************************************/ + // *************ODD TIMESTEP*************// + timestep++; + ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL + switch (CollisionType) { + case 1: + ScaLBL_D3Q19_AAodd_Greyscale_IMRT( + NeighborList, fq, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np, rlx, rlx_eff, Fx, Fy, Fz, + Porosity, Permeability, Velocity, Den, Pressure_dvc); + break; + case 2: + ScaLBL_D3Q19_AAodd_Greyscale( + NeighborList, fq, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np, rlx, rlx_eff, Fx, Fy, Fz, + Porosity, Permeability, Velocity, Pressure_dvc); + break; + case 3: + ScaLBL_D3Q19_AAodd_Greyscale_MRT( + NeighborList, fq, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np, rlx, rlx_eff, Fx, Fy, Fz, + Porosity, Permeability, Velocity, Den, Pressure_dvc); + break; + default: + ScaLBL_D3Q19_AAodd_Greyscale_IMRT( + NeighborList, fq, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np, rlx, rlx_eff, Fx, Fy, Fz, + Porosity, Permeability, Velocity, Den, Pressure_dvc); + break; } ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - ScaLBL_DeviceBarrier(); - // Set BCs - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - switch (CollisionType){ - case 1: - ScaLBL_D3Q19_AAeven_Greyscale_IMRT(fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx, rlx_eff, Fx, Fy, Fz,Porosity,Permeability,Velocity,Den,Pressure_dvc); - break; - case 2: - ScaLBL_D3Q19_AAeven_Greyscale(fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx, rlx_eff, Fx, Fy, Fz,Porosity,Permeability,Velocity,Pressure_dvc); - break; - case 3: - ScaLBL_D3Q19_AAeven_Greyscale_MRT(fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx, rlx_eff, Fx, Fy, Fz,Porosity,Permeability,Velocity,Den,Pressure_dvc); - break; - default: - ScaLBL_D3Q19_AAeven_Greyscale_IMRT(fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx, rlx_eff, Fx, Fy, Fz,Porosity,Permeability,Velocity,Den,Pressure_dvc); - break; + ScaLBL_DeviceBarrier(); + // Set BCs + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); } - ScaLBL_DeviceBarrier(); comm.barrier(); - //************************************************************************/ - - if (timestep%analysis_interval==0){ - ScaLBL_Comm->RegularLayout(Map,&Velocity[0],Velocity_x); - ScaLBL_Comm->RegularLayout(Map,&Velocity[Np],Velocity_y); - ScaLBL_Comm->RegularLayout(Map,&Velocity[2*Np],Velocity_z); - //ScaLBL_Comm->RegularLayout(Map,Porosity,PorosityMap); - //ScaLBL_Comm->RegularLayout(Map,Pressure_dvc,Pressure); - - double count_loc=0; - double count; - double vax,vay,vaz; - double vax_loc,vay_loc,vaz_loc; + switch (CollisionType) { + case 1: + ScaLBL_D3Q19_AAodd_Greyscale_IMRT( + NeighborList, fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx, + rlx_eff, Fx, Fy, Fz, Porosity, Permeability, Velocity, Den, + Pressure_dvc); + break; + case 2: + ScaLBL_D3Q19_AAodd_Greyscale(NeighborList, fq, 0, + ScaLBL_Comm->LastExterior(), Np, rlx, + rlx_eff, Fx, Fy, Fz, Porosity, + Permeability, Velocity, Pressure_dvc); + break; + case 3: + ScaLBL_D3Q19_AAodd_Greyscale_MRT( + NeighborList, fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx, + rlx_eff, Fx, Fy, Fz, Porosity, Permeability, Velocity, Den, + Pressure_dvc); + break; + default: + ScaLBL_D3Q19_AAodd_Greyscale_IMRT( + NeighborList, fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx, + rlx_eff, Fx, Fy, Fz, Porosity, Permeability, Velocity, Den, + Pressure_dvc); + break; + } + ScaLBL_DeviceBarrier(); + comm.barrier(); + + // *************EVEN TIMESTEP*************// + timestep++; + ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL + switch (CollisionType) { + case 1: + ScaLBL_D3Q19_AAeven_Greyscale_IMRT( + fq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), + Np, rlx, rlx_eff, Fx, Fy, Fz, Porosity, Permeability, Velocity, + Den, Pressure_dvc); + break; + case 2: + ScaLBL_D3Q19_AAeven_Greyscale(fq, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np, rlx, + rlx_eff, Fx, Fy, Fz, Porosity, + Permeability, Velocity, Pressure_dvc); + break; + case 3: + ScaLBL_D3Q19_AAeven_Greyscale_MRT( + fq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), + Np, rlx, rlx_eff, Fx, Fy, Fz, Porosity, Permeability, Velocity, + Den, Pressure_dvc); + break; + default: + ScaLBL_D3Q19_AAeven_Greyscale_IMRT( + fq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), + Np, rlx, rlx_eff, Fx, Fy, Fz, Porosity, Permeability, Velocity, + Den, Pressure_dvc); + break; + } + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + ScaLBL_DeviceBarrier(); + // Set BCs + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } + switch (CollisionType) { + case 1: + ScaLBL_D3Q19_AAeven_Greyscale_IMRT( + fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx, rlx_eff, Fx, Fy, + Fz, Porosity, Permeability, Velocity, Den, Pressure_dvc); + break; + case 2: + ScaLBL_D3Q19_AAeven_Greyscale( + fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx, rlx_eff, Fx, Fy, + Fz, Porosity, Permeability, Velocity, Pressure_dvc); + break; + case 3: + ScaLBL_D3Q19_AAeven_Greyscale_MRT( + fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx, rlx_eff, Fx, Fy, + Fz, Porosity, Permeability, Velocity, Den, Pressure_dvc); + break; + default: + ScaLBL_D3Q19_AAeven_Greyscale_IMRT( + fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx, rlx_eff, Fx, Fy, + Fz, Porosity, Permeability, Velocity, Den, Pressure_dvc); + break; + } + ScaLBL_DeviceBarrier(); + comm.barrier(); + //************************************************************************/ + + if (timestep % analysis_interval == 0) { + ScaLBL_Comm->RegularLayout(Map, &Velocity[0], Velocity_x); + ScaLBL_Comm->RegularLayout(Map, &Velocity[Np], Velocity_y); + ScaLBL_Comm->RegularLayout(Map, &Velocity[2 * Np], Velocity_z); + //ScaLBL_Comm->RegularLayout(Map,Porosity,PorosityMap); + //ScaLBL_Comm->RegularLayout(Map,Pressure_dvc,Pressure); + + double count_loc = 0; + double count; + double vax, vay, vaz; + double vax_loc, vay_loc, vaz_loc; //double px_loc,py_loc,pz_loc; //double px,py,pz; //double mass_loc,mass_glb; - - //parameters for domain average - int64_t imin,jmin,kmin,kmax; - // If external boundary conditions are set, do not average over the inlet and outlet - kmin=1; kmax=Nz-1; - //In case user forgets to specify the inlet/outlet buffer layers for BC>0 - if (BoundaryCondition > 0 && Dm->kproc() == 0) kmin=4; - if (BoundaryCondition > 0 && Dm->kproc() == Dm->nprocz()-1) kmax=Nz-4; - imin=jmin=1; + //parameters for domain average + int64_t imin, jmin, kmin, kmax; + // If external boundary conditions are set, do not average over the inlet and outlet + kmin = 1; + kmax = Nz - 1; + //In case user forgets to specify the inlet/outlet buffer layers for BC>0 + if (BoundaryCondition > 0 && Dm->kproc() == 0) + kmin = 4; + if (BoundaryCondition > 0 && Dm->kproc() == Dm->nprocz() - 1) + kmax = Nz - 4; + + imin = jmin = 1; // If inlet/outlet layers exist use these as default //if (Dm->inlet_layers_x > 0) imin = Dm->inlet_layers_x; //if (Dm->inlet_layers_y > 0) jmin = Dm->inlet_layers_y; - if (BoundaryCondition > 0 && Dm->inlet_layers_z > 0 && Dm->kproc() == 0) kmin = 1 + Dm->inlet_layers_z;//"1" indicates the halo layer - if (BoundaryCondition > 0 && Dm->outlet_layers_z > 0 && Dm->kproc() == Dm->nprocz()-1) kmax = Nz-1 - Dm->outlet_layers_z; + if (BoundaryCondition > 0 && Dm->inlet_layers_z > 0 && + Dm->kproc() == 0) + kmin = 1 + Dm->inlet_layers_z; //"1" indicates the halo layer + if (BoundaryCondition > 0 && Dm->outlet_layers_z > 0 && + Dm->kproc() == Dm->nprocz() - 1) + kmax = Nz - 1 - Dm->outlet_layers_z; - vax_loc = vay_loc = vaz_loc = 0.f; - for (int k=kmin; k 0){ - vax_loc += Velocity_x(i,j,k); - vay_loc += Velocity_y(i,j,k); - vaz_loc += Velocity_z(i,j,k); - count_loc+=1.0; - } - } - } - } - vax = Dm->Comm.sumReduce( vax_loc); - vay = Dm->Comm.sumReduce( vay_loc); - vaz = Dm->Comm.sumReduce( vaz_loc); - count = Dm->Comm.sumReduce( count_loc); + vax_loc = vay_loc = vaz_loc = 0.f; + for (int k = kmin; k < kmax; k++) { + for (int j = jmin; j < Ny - 1; j++) { + for (int i = imin; i < Nx - 1; i++) { + if (SignDist(i, j, k) > 0) { + vax_loc += Velocity_x(i, j, k); + vay_loc += Velocity_y(i, j, k); + vaz_loc += Velocity_z(i, j, k); + count_loc += 1.0; + } + } + } + } + vax = Dm->Comm.sumReduce(vax_loc); + vay = Dm->Comm.sumReduce(vay_loc); + vaz = Dm->Comm.sumReduce(vaz_loc); + count = Dm->Comm.sumReduce(count_loc); - vax /= count; - vay /= count; - vaz /= count; + vax /= count; + vay /= count; + vaz /= count; - double force_mag = sqrt(Fx*Fx+Fy*Fy+Fz*Fz); - double dir_x = Fx/force_mag; - double dir_y = Fy/force_mag; - double dir_z = Fz/force_mag; - if (force_mag == 0.0){ - // default to z direction - dir_x = 0.0; - dir_y = 0.0; - dir_z = 1.0; - force_mag = 1.0; - } - //double flow_rate = (px*dir_x + py*dir_y + pz*dir_z)/mass_glb; - double flow_rate = (vax*dir_x + vay*dir_y + vaz*dir_z); - - error = fabs(flow_rate - flow_rate_previous) / fabs(flow_rate); - flow_rate_previous = flow_rate; - - //if (rank==0) printf("Computing Minkowski functionals \n"); - Morphology.ComputeScalar(SignDist,0.f); - //Morphology.PrintAll(); - double mu = (tau-0.5)/3.f; - double Vs = Morphology.V(); - double As = Morphology.A(); - double Hs = Morphology.H(); - double Xs = Morphology.X(); - Vs = Dm->Comm.sumReduce( Vs); - As = Dm->Comm.sumReduce( As); - Hs = Dm->Comm.sumReduce( Hs); - Xs = Dm->Comm.sumReduce( Xs); + double force_mag = sqrt(Fx * Fx + Fy * Fy + Fz * Fz); + double dir_x = Fx / force_mag; + double dir_y = Fy / force_mag; + double dir_z = Fz / force_mag; + if (force_mag == 0.0) { + // default to z direction + dir_x = 0.0; + dir_y = 0.0; + dir_z = 1.0; + force_mag = 1.0; + } + //double flow_rate = (px*dir_x + py*dir_y + pz*dir_z)/mass_glb; + double flow_rate = (vax * dir_x + vay * dir_y + vaz * dir_z); - double h = Dm->voxel_length; - //double absperm = h*h*mu*Mask->Porosity()*flow_rate / force_mag; - double absperm = h*h*mu*GreyPorosity*flow_rate / force_mag; + error = fabs(flow_rate - flow_rate_previous) / fabs(flow_rate); + flow_rate_previous = flow_rate; - if (rank==0){ - printf(" AbsPerm = %.5g [micron^2]\n",absperm); - bool WriteHeader=false; - FILE * log_file = fopen("Permeability.csv","r"); - if (log_file != NULL) - fclose(log_file); - else - WriteHeader=true; - log_file = fopen("Permeability.csv","a"); - if (WriteHeader) - fprintf(log_file,"timestep Fx Fy Fz mu Vs As Hs Xs vax vay vaz AbsPerm \n"); + //if (rank==0) printf("Computing Minkowski functionals \n"); + Morphology.ComputeScalar(SignDist, 0.f); + //Morphology.PrintAll(); + double mu = (tau - 0.5) / 3.f; + double Vs = Morphology.V(); + double As = Morphology.A(); + double Hs = Morphology.H(); + double Xs = Morphology.X(); + Vs = Dm->Comm.sumReduce(Vs); + As = Dm->Comm.sumReduce(As); + Hs = Dm->Comm.sumReduce(Hs); + Xs = Dm->Comm.sumReduce(Xs); - fprintf(log_file,"%i %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g\n",timestep, Fx, Fy, Fz, mu, - h*h*h*Vs,h*h*As,h*Hs,Xs,vax,vay,vaz, absperm); - fclose(log_file); - } - } + double h = Dm->voxel_length; + //double absperm = h*h*mu*Mask->Porosity()*flow_rate / force_mag; + double absperm = h * h * mu * GreyPorosity * flow_rate / force_mag; - if (timestep%visualization_interval==0){ + if (rank == 0) { + printf(" AbsPerm = %.5g [micron^2]\n", absperm); + bool WriteHeader = false; + FILE *log_file = fopen("Permeability.csv", "r"); + if (log_file != NULL) + fclose(log_file); + else + WriteHeader = true; + log_file = fopen("Permeability.csv", "a"); + if (WriteHeader) + fprintf(log_file, "timestep Fx Fy Fz mu Vs As Hs Xs vax " + "vay vaz AbsPerm \n"); + + fprintf(log_file, + "%i %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g " + "%.8g %.8g\n", + timestep, Fx, Fy, Fz, mu, h * h * h * Vs, h * h * As, + h * Hs, Xs, vax, vay, vaz, absperm); + fclose(log_file); + } + } + + if (timestep % visualization_interval == 0) { VelocityField(); } - if (timestep%restart_interval==0){ + if (timestep % restart_interval == 0) { //Use rank=0 write out Restart.db - if (rank==0) { - greyscale_db->putScalar("timestep",timestep); - greyscale_db->putScalar( "Restart", true ); + if (rank == 0) { + greyscale_db->putScalar("timestep", timestep); + greyscale_db->putScalar("Restart", true); current_db->putDatabase("Greyscale", greyscale_db); std::ofstream OutStream("Restart.db"); current_db->print(OutStream, ""); OutStream.close(); - } //Write out Restart data. std::shared_ptr cfq; - cfq = std::shared_ptr(new double[19*Np],DeleteArray); - ScaLBL_CopyToHost(cfq.get(),fq,19*Np*sizeof(double));// Copy restart data to the CPU + cfq = std::shared_ptr(new double[19 * Np], + DeleteArray); + ScaLBL_CopyToHost( + cfq.get(), fq, + 19 * Np * sizeof(double)); // Copy restart data to the CPU FILE *RESTARTFILE; - RESTARTFILE=fopen(LocalRestartFile,"wb"); - fwrite(cfq.get(),sizeof(double),19*Np,RESTARTFILE); + RESTARTFILE = fopen(LocalRestartFile, "wb"); + fwrite(cfq.get(), sizeof(double), 19 * Np, RESTARTFILE); fclose(RESTARTFILE); - comm.barrier(); + comm.barrier(); } - } + } - PROFILE_STOP("Loop"); - PROFILE_SAVE("lbpm_greyscale_simulator",1); - //************************************************************************ - ScaLBL_DeviceBarrier(); - comm.barrier(); - if (rank==0) printf("-------------------------------------------------------------------\n"); - // Compute the walltime per timestep + PROFILE_STOP("Loop"); + PROFILE_SAVE("lbpm_greyscale_simulator", 1); + //************************************************************************ + ScaLBL_DeviceBarrier(); + comm.barrier(); + if (rank == 0) + printf("---------------------------------------------------------------" + "----\n"); + // Compute the walltime per timestep auto t2 = std::chrono::system_clock::now(); - double cputime = std::chrono::duration( t2 - t1 ).count() / timestep; - // Performance obtained from each node - double MLUPS = double(Np)/cputime/1000000; + double cputime = std::chrono::duration(t2 - t1).count() / timestep; + // Performance obtained from each node + double MLUPS = double(Np) / cputime / 1000000; - if (rank==0) printf("********************************************************\n"); - if (rank==0) printf("CPU time = %f \n", cputime); - if (rank==0) printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); - MLUPS *= nprocs; - if (rank==0) printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); - if (rank==0) printf("********************************************************\n"); + if (rank == 0) + printf("********************************************************\n"); + if (rank == 0) + printf("CPU time = %f \n", cputime); + if (rank == 0) + printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); + MLUPS *= nprocs; + if (rank == 0) + printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); + if (rank == 0) + printf("********************************************************\n"); - // ************************************************************************ + // ************************************************************************ } -void ScaLBL_GreyscaleModel::VelocityField(){ +void ScaLBL_GreyscaleModel::VelocityField() { - std::vector visData; - fillHalo fillData(Dm->Comm,Dm->rank_info,{Dm->Nx-2,Dm->Ny-2,Dm->Nz-2},{1,1,1},0,1); + std::vector visData; + fillHalo fillData(Dm->Comm, Dm->rank_info, + {Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2}, {1, 1, 1}, + 0, 1); - auto VxVar = std::make_shared(); - auto VyVar = std::make_shared(); - auto VzVar = std::make_shared(); - auto SignDistVar = std::make_shared(); - auto PressureVar = std::make_shared(); + auto VxVar = std::make_shared(); + auto VyVar = std::make_shared(); + auto VzVar = std::make_shared(); + auto SignDistVar = std::make_shared(); + auto PressureVar = std::make_shared(); - IO::initialize("","silo","false"); - // Create the MeshDataStruct - visData.resize(1); - visData[0].meshName = "domain"; - visData[0].mesh = std::make_shared( Dm->rank_info,Dm->Nx-2,Dm->Ny-2,Dm->Nz-2,Dm->Lx,Dm->Ly,Dm->Lz ); - SignDistVar->name = "SignDist"; - SignDistVar->type = IO::VariableType::VolumeVariable; - SignDistVar->dim = 1; - SignDistVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(SignDistVar); - - VxVar->name = "Velocity_x"; - VxVar->type = IO::VariableType::VolumeVariable; - VxVar->dim = 1; - VxVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(VxVar); - VyVar->name = "Velocity_y"; - VyVar->type = IO::VariableType::VolumeVariable; - VyVar->dim = 1; - VyVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(VyVar); - VzVar->name = "Velocity_z"; - VzVar->type = IO::VariableType::VolumeVariable; - VzVar->dim = 1; - VzVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(VzVar); - - PressureVar->name = "Pressure"; - PressureVar->type = IO::VariableType::VolumeVariable; - PressureVar->dim = 1; - PressureVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(PressureVar); + IO::initialize("", "silo", "false"); + // Create the MeshDataStruct + visData.resize(1); + visData[0].meshName = "domain"; + visData[0].mesh = + std::make_shared(Dm->rank_info, Dm->Nx - 2, Dm->Ny - 2, + Dm->Nz - 2, Dm->Lx, Dm->Ly, Dm->Lz); + SignDistVar->name = "SignDist"; + SignDistVar->type = IO::VariableType::VolumeVariable; + SignDistVar->dim = 1; + SignDistVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); + visData[0].vars.push_back(SignDistVar); - Array& SignData = visData[0].vars[0]->data; - Array& VelxData = visData[0].vars[1]->data; - Array& VelyData = visData[0].vars[2]->data; - Array& VelzData = visData[0].vars[3]->data; - Array& PressureData = visData[0].vars[4]->data; - - ASSERT(visData[0].vars[0]->name=="SignDist"); - ASSERT(visData[0].vars[1]->name=="Velocity_x"); - ASSERT(visData[0].vars[2]->name=="Velocity_y"); - ASSERT(visData[0].vars[3]->name=="Velocity_z"); - ASSERT(visData[0].vars[4]->name=="Pressure"); - - ScaLBL_Comm->RegularLayout(Map,&Velocity[0],Velocity_x); - ScaLBL_Comm->RegularLayout(Map,&Velocity[Np],Velocity_y); - ScaLBL_Comm->RegularLayout(Map,&Velocity[2*Np],Velocity_z); - ScaLBL_Comm->RegularLayout(Map,Pressure_dvc,Pressure); + VxVar->name = "Velocity_x"; + VxVar->type = IO::VariableType::VolumeVariable; + VxVar->dim = 1; + VxVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); + visData[0].vars.push_back(VxVar); + VyVar->name = "Velocity_y"; + VyVar->type = IO::VariableType::VolumeVariable; + VyVar->dim = 1; + VyVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); + visData[0].vars.push_back(VyVar); + VzVar->name = "Velocity_z"; + VzVar->type = IO::VariableType::VolumeVariable; + VzVar->dim = 1; + VzVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); + visData[0].vars.push_back(VzVar); - fillData.copy(SignDist,SignData); - fillData.copy(Velocity_x,VelxData); - fillData.copy(Velocity_y,VelyData); - fillData.copy(Velocity_z,VelzData); - fillData.copy(Pressure,PressureData); - - IO::writeData( timestep, visData, Dm->Comm ); + PressureVar->name = "Pressure"; + PressureVar->type = IO::VariableType::VolumeVariable; + PressureVar->dim = 1; + PressureVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); + visData[0].vars.push_back(PressureVar); + Array &SignData = visData[0].vars[0]->data; + Array &VelxData = visData[0].vars[1]->data; + Array &VelyData = visData[0].vars[2]->data; + Array &VelzData = visData[0].vars[3]->data; + Array &PressureData = visData[0].vars[4]->data; + + ASSERT(visData[0].vars[0]->name == "SignDist"); + ASSERT(visData[0].vars[1]->name == "Velocity_x"); + ASSERT(visData[0].vars[2]->name == "Velocity_y"); + ASSERT(visData[0].vars[3]->name == "Velocity_z"); + ASSERT(visData[0].vars[4]->name == "Pressure"); + + ScaLBL_Comm->RegularLayout(Map, &Velocity[0], Velocity_x); + ScaLBL_Comm->RegularLayout(Map, &Velocity[Np], Velocity_y); + ScaLBL_Comm->RegularLayout(Map, &Velocity[2 * Np], Velocity_z); + ScaLBL_Comm->RegularLayout(Map, Pressure_dvc, Pressure); + + fillData.copy(SignDist, SignData); + fillData.copy(Velocity_x, VelxData); + fillData.copy(Velocity_y, VelyData); + fillData.copy(Velocity_z, VelzData); + fillData.copy(Pressure, PressureData); + + IO::writeData(timestep, visData, Dm->Comm); } -void ScaLBL_GreyscaleModel::WriteDebug(){ - // Copy back final phase indicator field and convert to regular layout - DoubleArray PhaseField(Nx,Ny,Nz); +void ScaLBL_GreyscaleModel::WriteDebug() { + // Copy back final phase indicator field and convert to regular layout + DoubleArray PhaseField(Nx, Ny, Nz); - //ScaLBL_CopyToHost(Porosity.data(), Poros, sizeof(double)*N); + //ScaLBL_CopyToHost(Porosity.data(), Poros, sizeof(double)*N); -// FILE *OUTFILE; -// sprintf(LocalRankFilename,"Phase.%05i.raw",rank); -// OUTFILE = fopen(LocalRankFilename,"wb"); -// fwrite(PhaseField.data(),8,N,OUTFILE); -// fclose(OUTFILE); -// -// ScaLBL_Comm->RegularLayout(Map,&Den[0],PhaseField); -// FILE *AFILE; -// sprintf(LocalRankFilename,"A.%05i.raw",rank); -// AFILE = fopen(LocalRankFilename,"wb"); -// fwrite(PhaseField.data(),8,N,AFILE); -// fclose(AFILE); -// -// ScaLBL_Comm->RegularLayout(Map,&Den[Np],PhaseField); -// FILE *BFILE; -// sprintf(LocalRankFilename,"B.%05i.raw",rank); -// BFILE = fopen(LocalRankFilename,"wb"); -// fwrite(PhaseField.data(),8,N,BFILE); -// fclose(BFILE); -// -// ScaLBL_Comm->RegularLayout(Map,Pressure,PhaseField); -// FILE *PFILE; -// sprintf(LocalRankFilename,"Pressure.%05i.raw",rank); -// PFILE = fopen(LocalRankFilename,"wb"); -// fwrite(PhaseField.data(),8,N,PFILE); -// fclose(PFILE); + // FILE *OUTFILE; + // sprintf(LocalRankFilename,"Phase.%05i.raw",rank); + // OUTFILE = fopen(LocalRankFilename,"wb"); + // fwrite(PhaseField.data(),8,N,OUTFILE); + // fclose(OUTFILE); + // + // ScaLBL_Comm->RegularLayout(Map,&Den[0],PhaseField); + // FILE *AFILE; + // sprintf(LocalRankFilename,"A.%05i.raw",rank); + // AFILE = fopen(LocalRankFilename,"wb"); + // fwrite(PhaseField.data(),8,N,AFILE); + // fclose(AFILE); + // + // ScaLBL_Comm->RegularLayout(Map,&Den[Np],PhaseField); + // FILE *BFILE; + // sprintf(LocalRankFilename,"B.%05i.raw",rank); + // BFILE = fopen(LocalRankFilename,"wb"); + // fwrite(PhaseField.data(),8,N,BFILE); + // fclose(BFILE); + // + // ScaLBL_Comm->RegularLayout(Map,Pressure,PhaseField); + // FILE *PFILE; + // sprintf(LocalRankFilename,"Pressure.%05i.raw",rank); + // PFILE = fopen(LocalRankFilename,"wb"); + // fwrite(PhaseField.data(),8,N,PFILE); + // fclose(PFILE); - ScaLBL_Comm->RegularLayout(Map,&Velocity[0],PhaseField); - FILE *VELX_FILE; - sprintf(LocalRankFilename,"Velocity_X.%05i.raw",rank); - VELX_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELX_FILE); - fclose(VELX_FILE); + ScaLBL_Comm->RegularLayout(Map, &Velocity[0], PhaseField); + FILE *VELX_FILE; + sprintf(LocalRankFilename, "Velocity_X.%05i.raw", rank); + VELX_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELX_FILE); + fclose(VELX_FILE); - ScaLBL_Comm->RegularLayout(Map,&Velocity[Np],PhaseField); - FILE *VELY_FILE; - sprintf(LocalRankFilename,"Velocity_Y.%05i.raw",rank); - VELY_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELY_FILE); - fclose(VELY_FILE); + ScaLBL_Comm->RegularLayout(Map, &Velocity[Np], PhaseField); + FILE *VELY_FILE; + sprintf(LocalRankFilename, "Velocity_Y.%05i.raw", rank); + VELY_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELY_FILE); + fclose(VELY_FILE); - ScaLBL_Comm->RegularLayout(Map,&Velocity[2*Np],PhaseField); - FILE *VELZ_FILE; - sprintf(LocalRankFilename,"Velocity_Z.%05i.raw",rank); - VELZ_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELZ_FILE); - fclose(VELZ_FILE); + ScaLBL_Comm->RegularLayout(Map, &Velocity[2 * Np], PhaseField); + FILE *VELZ_FILE; + sprintf(LocalRankFilename, "Velocity_Z.%05i.raw", rank); + VELZ_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELZ_FILE); + fclose(VELZ_FILE); - ScaLBL_Comm->RegularLayout(Map,&Porosity[0],PhaseField); - FILE *POROS_FILE; - sprintf(LocalRankFilename,"Porosity.%05i.raw",rank); - POROS_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,POROS_FILE); - fclose(POROS_FILE); + ScaLBL_Comm->RegularLayout(Map, &Porosity[0], PhaseField); + FILE *POROS_FILE; + sprintf(LocalRankFilename, "Porosity.%05i.raw", rank); + POROS_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, POROS_FILE); + fclose(POROS_FILE); - ScaLBL_Comm->RegularLayout(Map,&Permeability[0],PhaseField); - FILE *PERM_FILE; - sprintf(LocalRankFilename,"Permeability.%05i.raw",rank); - PERM_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,PERM_FILE); - fclose(PERM_FILE); + ScaLBL_Comm->RegularLayout(Map, &Permeability[0], PhaseField); + FILE *PERM_FILE; + sprintf(LocalRankFilename, "Permeability.%05i.raw", rank); + PERM_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, PERM_FILE); + fclose(PERM_FILE); } diff --git a/models/GreyscaleModel.h b/models/GreyscaleModel.h index bd17602c..9feff45f 100644 --- a/models/GreyscaleModel.h +++ b/models/GreyscaleModel.h @@ -31,43 +31,43 @@ #include "ProfilerApp.h" #include "threadpool/thread_pool.h" -class ScaLBL_GreyscaleModel{ +class ScaLBL_GreyscaleModel { public: - ScaLBL_GreyscaleModel(int RANK, int NP, const Utilities::MPI& COMM); - ~ScaLBL_GreyscaleModel(); - - // functions in they should be run - void ReadParams(string filename); - void ReadParams(std::shared_ptr db0); - void SetDomain(); - void ReadInput(); - void Create(); - void Initialize(); - void Run(); - void WriteDebug(); - void VelocityField(); - - bool Restart,pBC; - int timestep,timestepMax; - int BoundaryCondition; - int CollisionType; - double tau; - double tau_eff; - double Den;//constant density - double tolerance; - double Fx,Fy,Fz,flux; - double din,dout; - double dp;//solid particle diameter, unit in voxel - double GreyPorosity; - - int Nx,Ny,Nz,N,Np; - int rank,nprocx,nprocy,nprocz,nprocs; - double Lx,Ly,Lz; + ScaLBL_GreyscaleModel(int RANK, int NP, const Utilities::MPI &COMM); + ~ScaLBL_GreyscaleModel(); + + // functions in they should be run + void ReadParams(string filename); + void ReadParams(std::shared_ptr db0); + void SetDomain(); + void ReadInput(); + void Create(); + void Initialize(); + void Run(); + void WriteDebug(); + void VelocityField(); + + bool Restart, pBC; + int timestep, timestepMax; + int BoundaryCondition; + int CollisionType; + double tau; + double tau_eff; + double Den; //constant density + double tolerance; + double Fx, Fy, Fz, flux; + double din, dout; + double dp; //solid particle diameter, unit in voxel + double GreyPorosity; + + int Nx, Ny, Nz, N, Np; + int rank, nprocx, nprocy, nprocz, nprocs; + double Lx, Ly, Lz; + + std::shared_ptr Dm; // this domain is for analysis + std::shared_ptr Mask; // this domain is for lbm + std::shared_ptr ScaLBL_Comm; - std::shared_ptr Dm; // this domain is for analysis - std::shared_ptr Mask; // this domain is for lbm - std::shared_ptr ScaLBL_Comm; - // input database std::shared_ptr db; std::shared_ptr domain_db; @@ -75,13 +75,13 @@ public: std::shared_ptr analysis_db; std::shared_ptr vis_db; - signed char *id; - int *NeighborList; - double *fq; - double *Permeability;//grey voxel permeability - double *Porosity; - double *Velocity; - double *Pressure_dvc; + signed char *id; + int *NeighborList; + double *fq; + double *Permeability; //grey voxel permeability + double *Porosity; + double *Velocity; + double *Pressure_dvc; IntArray Map; DoubleArray SignDist; DoubleArray Velocity_x; @@ -89,18 +89,19 @@ public: DoubleArray Velocity_z; DoubleArray PorosityMap; DoubleArray Pressure; - + private: - Utilities::MPI comm; - - int dist_mem_size; - int neighborSize; - // filenames + Utilities::MPI comm; + + int dist_mem_size; + int neighborSize; + // filenames char LocalRankString[8]; char LocalRankFilename[40]; char LocalRestartFile[40]; - - void AssignComponentLabels(double *Porosity, double *Permeablity); - void AssignComponentLabels(double *Porosity,double *Permeability,const vector &File_poro,const vector &File_perm); -}; + void AssignComponentLabels(double *Porosity, double *Permeablity); + void AssignComponentLabels(double *Porosity, double *Permeability, + const vector &File_poro, + const vector &File_perm); +}; diff --git a/models/IonModel.cpp b/models/IonModel.cpp index 100063ea..7213cfce 100644 --- a/models/IonModel.cpp +++ b/models/IonModel.cpp @@ -6,158 +6,169 @@ #include "analysis/distance.h" #include "common/ReadMicroCT.h" -ScaLBL_IonModel::ScaLBL_IonModel(int RANK, int NP, const Utilities::MPI& COMM): -rank(RANK),nprocs(NP),timestep(0),timestepMax(0),time_conv(0),kb(0),electron_charge(0),T(0),Vt(0),k2_inv(0),h(0), -tolerance(0),number_ion_species(0),Nx(0),Ny(0),Nz(0),N(0),Np(0),nprocx(0),nprocy(0),nprocz(0), -fluidVelx_dummy(0),fluidVely_dummy(0),fluidVelz_dummy(0), -BoundaryConditionInlet(0),BoundaryConditionOutlet(0),BoundaryConditionSolid(0),Lx(0),Ly(0),Lz(0),comm(COMM) -{ +ScaLBL_IonModel::ScaLBL_IonModel(int RANK, int NP, const Utilities::MPI &COMM) + : rank(RANK), nprocs(NP), timestep(0), timestepMax(0), time_conv(0), kb(0), + electron_charge(0), T(0), Vt(0), k2_inv(0), h(0), tolerance(0), + number_ion_species(0), Nx(0), Ny(0), Nz(0), N(0), Np(0), nprocx(0), + nprocy(0), nprocz(0), fluidVelx_dummy(0), fluidVely_dummy(0), + fluidVelz_dummy(0), BoundaryConditionInlet(0), BoundaryConditionOutlet(0), + BoundaryConditionSolid(0), Lx(0), Ly(0), Lz(0), comm(COMM) {} +ScaLBL_IonModel::~ScaLBL_IonModel() {} -} -ScaLBL_IonModel::~ScaLBL_IonModel(){ +void ScaLBL_IonModel::ReadParams(string filename, vector &num_iter) { -} - -void ScaLBL_IonModel::ReadParams(string filename,vector &num_iter){ - - // read the input database - db = std::make_shared( filename ); - domain_db = db->getDatabase( "Domain" ); - ion_db = db->getDatabase( "Ions" ); + // read the input database + db = std::make_shared(filename); + domain_db = db->getDatabase("Domain"); + ion_db = db->getDatabase("Ions"); // Universal constant - kb = 1.38e-23;//Boltzmann constant;unit [J/K] - electron_charge = 1.6e-19;//electron charge;unit [C] + kb = 1.38e-23; //Boltzmann constant;unit [J/K] + electron_charge = 1.6e-19; //electron charge;unit [C] - //---------------------- Default model parameters --------------------------// - T = 300.0;//temperature; unit [K] - Vt = kb*T/electron_charge;//thermal voltage; unit [Vy] - k2_inv = 4.0;//speed of sound for D3Q7 lattice - h = 1.0;//resolution; unit: um/lu - tolerance = 1.0e-8; - number_ion_species = 1; + //---------------------- Default model parameters --------------------------// + T = 300.0; //temperature; unit [K] + Vt = kb * T / electron_charge; //thermal voltage; unit [Vy] + k2_inv = 4.0; //speed of sound for D3Q7 lattice + h = 1.0; //resolution; unit: um/lu + tolerance = 1.0e-8; + number_ion_species = 1; tau.push_back(1.0); - IonDiffusivity.push_back(1.0e-9);//user-input diffusivity has physical unit [m^2/sec] - IonValence.push_back(1);//algebraic valence charge - IonConcentration.push_back(1.0e-3);//user-input ion concentration has physical unit [mol/m^3] + IonDiffusivity.push_back( + 1.0e-9); //user-input diffusivity has physical unit [m^2/sec] + IonValence.push_back(1); //algebraic valence charge + IonConcentration.push_back( + 1.0e-3); //user-input ion concentration has physical unit [mol/m^3] //tau.push_back(0.5+k2_inv*time_conv/(h*1.0e-6)/(h*1.0e-6)*IonDiffusivity[0]); - time_conv.push_back((tau[0]-0.5)/k2_inv*(h*h*1.0e-12)/IonDiffusivity[0]); - fluidVelx_dummy = 0.0;//for debugging, unit [m/sec] - fluidVely_dummy = 0.0;//for debugging, unit [m/sec] - fluidVelz_dummy = 0.0;//for debugging, unit [m/sec] - Ex_dummy = 0.0;//for debugging, unit [V/m] - Ey_dummy = 0.0;//for debugging, unit [V/m] - Ez_dummy = 0.0;//for debugging, unit [V/m] + time_conv.push_back((tau[0] - 0.5) / k2_inv * (h * h * 1.0e-12) / + IonDiffusivity[0]); + fluidVelx_dummy = 0.0; //for debugging, unit [m/sec] + fluidVely_dummy = 0.0; //for debugging, unit [m/sec] + fluidVelz_dummy = 0.0; //for debugging, unit [m/sec] + Ex_dummy = 0.0; //for debugging, unit [V/m] + Ey_dummy = 0.0; //for debugging, unit [V/m] + Ez_dummy = 0.0; //for debugging, unit [V/m] //--------------------------------------------------------------------------// - // Read domain parameters - if (domain_db->keyExists( "voxel_length" )){//default unit: um/lu - h = domain_db->getScalar( "voxel_length" ); - } - - // LB-Ion Model parameters - //if (ion_db->keyExists( "timestepMax" )){ - // timestepMax = ion_db->getScalar( "timestepMax" ); - //} - if (ion_db->keyExists( "tolerance" )){ - tolerance = ion_db->getScalar( "tolerance" ); - } - if (ion_db->keyExists( "temperature" )){ - T = ion_db->getScalar( "temperature" ); - //re-calculate thermal voltage - Vt = kb*T/electron_charge;//thermal voltage; unit [Vy] - } - if (ion_db->keyExists( "FluidVelDummy" )){ - fluidVelx_dummy = ion_db->getVector( "FluidVelDummy" )[0]; - fluidVely_dummy = ion_db->getVector( "FluidVelDummy" )[1]; - fluidVelz_dummy = ion_db->getVector( "FluidVelDummy" )[2]; - } - if (ion_db->keyExists( "ElectricFieldDummy" )){ - Ex_dummy = ion_db->getVector( "ElectricFieldDummy" )[0]; - Ey_dummy = ion_db->getVector( "ElectricFieldDummy" )[1]; - Ez_dummy = ion_db->getVector( "ElectricFieldDummy" )[2]; - } - if (ion_db->keyExists( "number_ion_species" )){ - number_ion_species = ion_db->getScalar( "number_ion_species" ); - } - //------ Load number of iteration from multiphysics controller ------// - if (num_iter.size()!=number_ion_species){ - ERROR("Error: number_ion_species and num_iter_Ion_List (from Multiphysics) must be of the same length! \n"); + // Read domain parameters + if (domain_db->keyExists("voxel_length")) { //default unit: um/lu + h = domain_db->getScalar("voxel_length"); } - else{ - timestepMax.assign(num_iter.begin(),num_iter.end()); + + // LB-Ion Model parameters + //if (ion_db->keyExists( "timestepMax" )){ + // timestepMax = ion_db->getScalar( "timestepMax" ); + //} + if (ion_db->keyExists("tolerance")) { + tolerance = ion_db->getScalar("tolerance"); + } + if (ion_db->keyExists("temperature")) { + T = ion_db->getScalar("temperature"); + //re-calculate thermal voltage + Vt = kb * T / electron_charge; //thermal voltage; unit [Vy] + } + if (ion_db->keyExists("FluidVelDummy")) { + fluidVelx_dummy = ion_db->getVector("FluidVelDummy")[0]; + fluidVely_dummy = ion_db->getVector("FluidVelDummy")[1]; + fluidVelz_dummy = ion_db->getVector("FluidVelDummy")[2]; + } + if (ion_db->keyExists("ElectricFieldDummy")) { + Ex_dummy = ion_db->getVector("ElectricFieldDummy")[0]; + Ey_dummy = ion_db->getVector("ElectricFieldDummy")[1]; + Ez_dummy = ion_db->getVector("ElectricFieldDummy")[2]; + } + if (ion_db->keyExists("number_ion_species")) { + number_ion_species = ion_db->getScalar("number_ion_species"); + } + //------ Load number of iteration from multiphysics controller ------// + if (num_iter.size() != number_ion_species) { + ERROR("Error: number_ion_species and num_iter_Ion_List (from " + "Multiphysics) must be of the same length! \n"); + } else { + timestepMax.assign(num_iter.begin(), num_iter.end()); } //-------------------------------------------------------------------// - if (ion_db->keyExists("tauList")){ + if (ion_db->keyExists("tauList")) { tau.clear(); - tau = ion_db->getVector( "tauList" ); - vectorDi = ion_db->getVector( "IonDiffusivityList" );//temp storing ion diffusivity in physical unit - if (tau.size()!=number_ion_species || Di.size()!=number_ion_species){ - ERROR("Error: number_ion_species, tauList and IonDiffusivityList must be of the same length! \n"); - } - else{ + tau = ion_db->getVector("tauList"); + vector Di = ion_db->getVector( + "IonDiffusivityList"); //temp storing ion diffusivity in physical unit + if (tau.size() != number_ion_species || + Di.size() != number_ion_species) { + ERROR("Error: number_ion_species, tauList and IonDiffusivityList " + "must be of the same length! \n"); + } else { time_conv.clear(); - for (size_t i=0; ikeyExists("IonDiffusivityList")){ + if (ion_db->keyExists("IonDiffusivityList")) { IonDiffusivity.clear(); - IonDiffusivity = ion_db->getVector( "IonDiffusivityList" ); - if (IonDiffusivity.size()!=number_ion_species){ - ERROR("Error: number_ion_species and IonDiffusivityList must be the same length! \n"); - } - else{ - for (size_t i=0; igetVector("IonDiffusivityList"); + if (IonDiffusivity.size() != number_ion_species) { + ERROR("Error: number_ion_species and IonDiffusivityList must be " + "the same length! \n"); + } else { + for (size_t i = 0; i < IonDiffusivity.size(); i++) { + IonDiffusivity[i] = + IonDiffusivity[i] * time_conv[i] / + (h * h * 1.0e-12); //LB diffusivity has unit [lu^2/lt] } } - } - else { - for (size_t i=0; ikeyExists("IonValenceList")){ + if (ion_db->keyExists("IonValenceList")) { IonValence.clear(); - IonValence = ion_db->getVector( "IonValenceList" ); - if (IonValence.size()!=number_ion_species){ - ERROR("Error: number_ion_species and IonValenceList must be the same length! \n"); + IonValence = ion_db->getVector("IonValenceList"); + if (IonValence.size() != number_ion_species) { + ERROR("Error: number_ion_species and IonValenceList must be the " + "same length! \n"); } } //read initial ion concentration list; INPUT unit [mol/m^3] //it must be converted to LB unit [mol/lu^3] - if (ion_db->keyExists("IonConcentrationList")){ + if (ion_db->keyExists("IonConcentrationList")) { IonConcentration.clear(); - IonConcentration = ion_db->getVector( "IonConcentrationList" ); - if (IonConcentration.size()!=number_ion_species){ - ERROR("Error: number_ion_species and IonConcentrationList must be the same length! \n"); - } - else{ - for (size_t i=0; igetVector("IonConcentrationList"); + if (IonConcentration.size() != number_ion_species) { + ERROR("Error: number_ion_species and IonConcentrationList must be " + "the same length! \n"); + } else { + for (size_t i = 0; i < IonConcentration.size(); i++) { + IonConcentration[i] = + IonConcentration[i] * + (h * h * h * + 1.0e-18); //LB ion concentration has unit [mol/lu^3] } } - } - else { - for (size_t i=0; ikeyExists( "BC_Solid" )){ - BoundaryConditionSolid = ion_db->getScalar( "BC_Solid" ); - } + if (ion_db->keyExists("BC_Solid")) { + BoundaryConditionSolid = ion_db->getScalar("BC_Solid"); + } // Read boundary condition for ion transport // BC = 0: normal periodic BC // BC = 1: fixed ion concentration; unit=[mol/m^3] @@ -165,221 +176,257 @@ void ScaLBL_IonModel::ReadParams(string filename,vector &num_iter){ BoundaryConditionInlet.push_back(0); BoundaryConditionOutlet.push_back(0); //Inlet - if (ion_db->keyExists( "BC_InletList" )){ - BoundaryConditionInlet = ion_db->getVector( "BC_InletList" ); - if (BoundaryConditionInlet.size()!=number_ion_species){ - ERROR("Error: number_ion_species and BC_InletList must be of the same length! \n"); + if (ion_db->keyExists("BC_InletList")) { + BoundaryConditionInlet = ion_db->getVector("BC_InletList"); + if (BoundaryConditionInlet.size() != number_ion_species) { + ERROR("Error: number_ion_species and BC_InletList must be of the " + "same length! \n"); } - unsigned short int BC_inlet_min = *min_element(BoundaryConditionInlet.begin(),BoundaryConditionInlet.end()); - unsigned short int BC_inlet_max = *max_element(BoundaryConditionInlet.begin(),BoundaryConditionInlet.end()); - if (BC_inlet_min == 0 && BC_inlet_max>0){ - ERROR("Error: BC_InletList: mix of periodic, ion concentration and flux BC is not supported! \n"); + unsigned short int BC_inlet_min = *min_element( + BoundaryConditionInlet.begin(), BoundaryConditionInlet.end()); + unsigned short int BC_inlet_max = *max_element( + BoundaryConditionInlet.begin(), BoundaryConditionInlet.end()); + if (BC_inlet_min == 0 && BC_inlet_max > 0) { + ERROR("Error: BC_InletList: mix of periodic, ion concentration and " + "flux BC is not supported! \n"); } - if (BC_inlet_min>0){ + if (BC_inlet_min > 0) { //read in inlet values Cin - if (ion_db->keyExists("InletValueList")){ - Cin = ion_db->getVector( "InletValueList" ); - if (Cin.size()!=number_ion_species){ - ERROR("Error: number_ion_species and InletValueList must be the same length! \n"); + if (ion_db->keyExists("InletValueList")) { + Cin = ion_db->getVector("InletValueList"); + if (Cin.size() != number_ion_species) { + ERROR("Error: number_ion_species and InletValueList must " + "be the same length! \n"); } + } else { + ERROR("Error: Non-periodic BCs are specified but " + "InletValueList cannot be found! \n"); } - else { - ERROR("Error: Non-periodic BCs are specified but InletValueList cannot be found! \n"); - } - for (size_t i=0;ikeyExists( "BC_OutletList" )){ - BoundaryConditionOutlet = ion_db->getVector( "BC_OutletList" ); - if (BoundaryConditionOutlet.size()!=number_ion_species){ - ERROR("Error: number_ion_species and BC_OutletList must be of the same length! \n"); + if (ion_db->keyExists("BC_OutletList")) { + BoundaryConditionOutlet = ion_db->getVector("BC_OutletList"); + if (BoundaryConditionOutlet.size() != number_ion_species) { + ERROR("Error: number_ion_species and BC_OutletList must be of the " + "same length! \n"); } - unsigned short int BC_outlet_min = *min_element(BoundaryConditionOutlet.begin(),BoundaryConditionOutlet.end()); - unsigned short int BC_outlet_max = *max_element(BoundaryConditionOutlet.begin(),BoundaryConditionOutlet.end()); - if (BC_outlet_min == 0 && BC_outlet_max>0){ - ERROR("Error: BC_OutletList: mix of periodic, ion concentration and flux BC is not supported! \n"); + unsigned short int BC_outlet_min = *min_element( + BoundaryConditionOutlet.begin(), BoundaryConditionOutlet.end()); + unsigned short int BC_outlet_max = *max_element( + BoundaryConditionOutlet.begin(), BoundaryConditionOutlet.end()); + if (BC_outlet_min == 0 && BC_outlet_max > 0) { + ERROR("Error: BC_OutletList: mix of periodic, ion concentration " + "and flux BC is not supported! \n"); } - if (BC_outlet_min>0){ + if (BC_outlet_min > 0) { //read in outlet values Cout - if (ion_db->keyExists("OutletValueList")){ - Cout = ion_db->getVector( "OutletValueList" ); - if (Cout.size()!=number_ion_species){ - ERROR("Error: number_ion_species and OutletValueList must be the same length! \n"); + if (ion_db->keyExists("OutletValueList")) { + Cout = ion_db->getVector("OutletValueList"); + if (Cout.size() != number_ion_species) { + ERROR("Error: number_ion_species and OutletValueList must " + "be the same length! \n"); } + } else { + ERROR("Error: Non-periodic BCs are specified but " + "OutletValueList cannot be found! \n"); } - else { - ERROR("Error: Non-periodic BCs are specified but OutletValueList cannot be found! \n"); - } - for (size_t i=0;i( filename ); - domain_db = db->getDatabase( "Domain" ); - ion_db = db->getDatabase( "Ions" ); + + // read the input database + db = std::make_shared(filename); + domain_db = db->getDatabase("Domain"); + ion_db = db->getDatabase("Ions"); // Universal constant - kb = 1.38e-23;//Boltzmann constant;unit [J/K] - electron_charge = 1.6e-19;//electron charge;unit [C] + kb = 1.38e-23; //Boltzmann constant;unit [J/K] + electron_charge = 1.6e-19; //electron charge;unit [C] - //---------------------- Default model parameters --------------------------// - T = 300.0;//temperature; unit [K] - Vt = kb*T/electron_charge;//thermal voltage; unit [Vy] - k2_inv = 4.0;//speed of sound for D3Q7 lattice - h = 1.0;//resolution; unit: um/lu - tolerance = 1.0e-8; - number_ion_species = 1; + //---------------------- Default model parameters --------------------------// + T = 300.0; //temperature; unit [K] + Vt = kb * T / electron_charge; //thermal voltage; unit [Vy] + k2_inv = 4.0; //speed of sound for D3Q7 lattice + h = 1.0; //resolution; unit: um/lu + tolerance = 1.0e-8; + number_ion_species = 1; tau.push_back(1.0); - IonDiffusivity.push_back(1.0e-9);//user-input diffusivity has physical unit [m^2/sec] - IonValence.push_back(1);//algebraic valence charge - IonConcentration.push_back(1.0e-3);//user-input ion concentration has physical unit [mol/m^3] + IonDiffusivity.push_back( + 1.0e-9); //user-input diffusivity has physical unit [m^2/sec] + IonValence.push_back(1); //algebraic valence charge + IonConcentration.push_back( + 1.0e-3); //user-input ion concentration has physical unit [mol/m^3] //tau.push_back(0.5+k2_inv*time_conv/(h*1.0e-6)/(h*1.0e-6)*IonDiffusivity[0]); - time_conv.push_back((tau[0]-0.5)/k2_inv*(h*h*1.0e-12)/IonDiffusivity[0]); - fluidVelx_dummy = 0.0;//for debugging, unit [m/sec] - fluidVely_dummy = 0.0;//for debugging, unit [m/sec] - fluidVelz_dummy = 0.0;//for debugging, unit [m/sec] - Ex_dummy = 0.0;//for debugging, unit [V/m] - Ey_dummy = 0.0;//for debugging, unit [V/m] - Ez_dummy = 0.0;//for debugging, unit [V/m] + time_conv.push_back((tau[0] - 0.5) / k2_inv * (h * h * 1.0e-12) / + IonDiffusivity[0]); + fluidVelx_dummy = 0.0; //for debugging, unit [m/sec] + fluidVely_dummy = 0.0; //for debugging, unit [m/sec] + fluidVelz_dummy = 0.0; //for debugging, unit [m/sec] + Ex_dummy = 0.0; //for debugging, unit [V/m] + Ey_dummy = 0.0; //for debugging, unit [V/m] + Ez_dummy = 0.0; //for debugging, unit [V/m] //--------------------------------------------------------------------------// - // Read domain parameters - if (domain_db->keyExists( "voxel_length" )){//default unit: um/lu - h = domain_db->getScalar( "voxel_length" ); - } + // Read domain parameters + if (domain_db->keyExists("voxel_length")) { //default unit: um/lu + h = domain_db->getScalar("voxel_length"); + } - // LB-Ion Model parameters - //if (ion_db->keyExists( "timestepMax" )){ - // timestepMax = ion_db->getScalar( "timestepMax" ); - //} - if (ion_db->keyExists( "tolerance" )){ - tolerance = ion_db->getScalar( "tolerance" ); - } - if (ion_db->keyExists( "temperature" )){ - T = ion_db->getScalar( "temperature" ); - //re-calculate thermal voltage - Vt = kb*T/electron_charge;//thermal voltage; unit [Vy] - } - if (ion_db->keyExists( "FluidVelDummy" )){ - fluidVelx_dummy = ion_db->getVector( "FluidVelDummy" )[0]; - fluidVely_dummy = ion_db->getVector( "FluidVelDummy" )[1]; - fluidVelz_dummy = ion_db->getVector( "FluidVelDummy" )[2]; - } - if (ion_db->keyExists( "ElectricFieldDummy" )){ - Ex_dummy = ion_db->getVector( "ElectricFieldDummy" )[0]; - Ey_dummy = ion_db->getVector( "ElectricFieldDummy" )[1]; - Ez_dummy = ion_db->getVector( "ElectricFieldDummy" )[2]; - } - if (ion_db->keyExists( "number_ion_species" )){ - number_ion_species = ion_db->getScalar( "number_ion_species" ); - } - if (ion_db->keyExists("tauList")){ + // LB-Ion Model parameters + //if (ion_db->keyExists( "timestepMax" )){ + // timestepMax = ion_db->getScalar( "timestepMax" ); + //} + if (ion_db->keyExists("tolerance")) { + tolerance = ion_db->getScalar("tolerance"); + } + if (ion_db->keyExists("temperature")) { + T = ion_db->getScalar("temperature"); + //re-calculate thermal voltage + Vt = kb * T / electron_charge; //thermal voltage; unit [Vy] + } + if (ion_db->keyExists("FluidVelDummy")) { + fluidVelx_dummy = ion_db->getVector("FluidVelDummy")[0]; + fluidVely_dummy = ion_db->getVector("FluidVelDummy")[1]; + fluidVelz_dummy = ion_db->getVector("FluidVelDummy")[2]; + } + if (ion_db->keyExists("ElectricFieldDummy")) { + Ex_dummy = ion_db->getVector("ElectricFieldDummy")[0]; + Ey_dummy = ion_db->getVector("ElectricFieldDummy")[1]; + Ez_dummy = ion_db->getVector("ElectricFieldDummy")[2]; + } + if (ion_db->keyExists("number_ion_species")) { + number_ion_species = ion_db->getScalar("number_ion_species"); + } + if (ion_db->keyExists("tauList")) { tau.clear(); - tau = ion_db->getVector( "tauList" ); - vectorDi = ion_db->getVector( "IonDiffusivityList" );//temp storing ion diffusivity in physical unit - if (tau.size()!=number_ion_species || Di.size()!=number_ion_species){ - ERROR("Error: number_ion_species, tauList and IonDiffusivityList must be of the same length! \n"); - } - else{ + tau = ion_db->getVector("tauList"); + vector Di = ion_db->getVector( + "IonDiffusivityList"); //temp storing ion diffusivity in physical unit + if (tau.size() != number_ion_species || + Di.size() != number_ion_species) { + ERROR("Error: number_ion_species, tauList and IonDiffusivityList " + "must be of the same length! \n"); + } else { time_conv.clear(); - for (size_t i=0; ikeyExists("IonDiffusivityList")){ + if (ion_db->keyExists("IonDiffusivityList")) { IonDiffusivity.clear(); - IonDiffusivity = ion_db->getVector( "IonDiffusivityList" ); - if (IonDiffusivity.size()!=number_ion_species){ - ERROR("Error: number_ion_species and IonDiffusivityList must be the same length! \n"); - } - else{ - for (size_t i=0; igetVector("IonDiffusivityList"); + if (IonDiffusivity.size() != number_ion_species) { + ERROR("Error: number_ion_species and IonDiffusivityList must be " + "the same length! \n"); + } else { + for (size_t i = 0; i < IonDiffusivity.size(); i++) { + IonDiffusivity[i] = + IonDiffusivity[i] * time_conv[i] / + (h * h * 1.0e-12); //LB diffusivity has unit [lu^2/lt] } } - } - else { - for (size_t i=0; ikeyExists("IonValenceList")){ + if (ion_db->keyExists("IonValenceList")) { IonValence.clear(); - IonValence = ion_db->getVector( "IonValenceList" ); - if (IonValence.size()!=number_ion_species){ - ERROR("Error: number_ion_species and IonValenceList must be the same length! \n"); + IonValence = ion_db->getVector("IonValenceList"); + if (IonValence.size() != number_ion_species) { + ERROR("Error: number_ion_species and IonValenceList must be the " + "same length! \n"); } } //read initial ion concentration list; INPUT unit [mol/m^3] //it must be converted to LB unit [mol/lu^3] - if (ion_db->keyExists("IonConcentrationList")){ + if (ion_db->keyExists("IonConcentrationList")) { IonConcentration.clear(); - IonConcentration = ion_db->getVector( "IonConcentrationList" ); - if (IonConcentration.size()!=number_ion_species){ - ERROR("Error: number_ion_species and IonConcentrationList must be the same length! \n"); - } - else{ - for (size_t i=0; igetVector("IonConcentrationList"); + if (IonConcentration.size() != number_ion_species) { + ERROR("Error: number_ion_species and IonConcentrationList must be " + "the same length! \n"); + } else { + for (size_t i = 0; i < IonConcentration.size(); i++) { + IonConcentration[i] = + IonConcentration[i] * + (h * h * h * + 1.0e-18); //LB ion concentration has unit [mol/lu^3] } } - } - else { - for (size_t i=0; ikeyExists( "BC_Solid" )){ - BoundaryConditionSolid = ion_db->getScalar( "BC_Solid" ); - } + if (ion_db->keyExists("BC_Solid")) { + BoundaryConditionSolid = ion_db->getScalar("BC_Solid"); + } // Read boundary condition for ion transport // BC = 0: normal periodic BC // BC = 1: fixed ion concentration; unit=[mol/m^3] @@ -387,434 +434,540 @@ void ScaLBL_IonModel::ReadParams(string filename){ BoundaryConditionInlet.push_back(0); BoundaryConditionOutlet.push_back(0); //Inlet - if (ion_db->keyExists( "BC_InletList" )){ - BoundaryConditionInlet = ion_db->getVector( "BC_InletList" ); - if (BoundaryConditionInlet.size()!=number_ion_species){ - ERROR("Error: number_ion_species and BC_InletList must be of the same length! \n"); + if (ion_db->keyExists("BC_InletList")) { + BoundaryConditionInlet = ion_db->getVector("BC_InletList"); + if (BoundaryConditionInlet.size() != number_ion_species) { + ERROR("Error: number_ion_species and BC_InletList must be of the " + "same length! \n"); } - unsigned short int BC_inlet_min = *min_element(BoundaryConditionInlet.begin(),BoundaryConditionInlet.end()); - unsigned short int BC_inlet_max = *max_element(BoundaryConditionInlet.begin(),BoundaryConditionInlet.end()); - if (BC_inlet_min == 0 && BC_inlet_max>0){ - ERROR("Error: BC_InletList: mix of periodic, ion concentration and flux BC is not supported! \n"); + unsigned short int BC_inlet_min = *min_element( + BoundaryConditionInlet.begin(), BoundaryConditionInlet.end()); + unsigned short int BC_inlet_max = *max_element( + BoundaryConditionInlet.begin(), BoundaryConditionInlet.end()); + if (BC_inlet_min == 0 && BC_inlet_max > 0) { + ERROR("Error: BC_InletList: mix of periodic, ion concentration and " + "flux BC is not supported! \n"); } - if (BC_inlet_min>0){ + if (BC_inlet_min > 0) { //read in inlet values Cin - if (ion_db->keyExists("InletValueList")){ - Cin = ion_db->getVector( "InletValueList" ); - if (Cin.size()!=number_ion_species){ - ERROR("Error: number_ion_species and InletValueList must be the same length! \n"); + if (ion_db->keyExists("InletValueList")) { + Cin = ion_db->getVector("InletValueList"); + if (Cin.size() != number_ion_species) { + ERROR("Error: number_ion_species and InletValueList must " + "be the same length! \n"); } + } else { + ERROR("Error: Non-periodic BCs are specified but " + "InletValueList cannot be found! \n"); } - else { - ERROR("Error: Non-periodic BCs are specified but InletValueList cannot be found! \n"); - } - for (size_t i=0;ikeyExists( "BC_OutletList" )){ - BoundaryConditionOutlet = ion_db->getVector( "BC_OutletList" ); - if (BoundaryConditionOutlet.size()!=number_ion_species){ - ERROR("Error: number_ion_species and BC_OutletList must be of the same length! \n"); + if (ion_db->keyExists("BC_OutletList")) { + BoundaryConditionOutlet = ion_db->getVector("BC_OutletList"); + if (BoundaryConditionOutlet.size() != number_ion_species) { + ERROR("Error: number_ion_species and BC_OutletList must be of the " + "same length! \n"); } - unsigned short int BC_outlet_min = *min_element(BoundaryConditionOutlet.begin(),BoundaryConditionOutlet.end()); - unsigned short int BC_outlet_max = *max_element(BoundaryConditionOutlet.begin(),BoundaryConditionOutlet.end()); - if (BC_outlet_min == 0 && BC_outlet_max>0){ - ERROR("Error: BC_OutletList: mix of periodic, ion concentration and flux BC is not supported! \n"); + unsigned short int BC_outlet_min = *min_element( + BoundaryConditionOutlet.begin(), BoundaryConditionOutlet.end()); + unsigned short int BC_outlet_max = *max_element( + BoundaryConditionOutlet.begin(), BoundaryConditionOutlet.end()); + if (BC_outlet_min == 0 && BC_outlet_max > 0) { + ERROR("Error: BC_OutletList: mix of periodic, ion concentration " + "and flux BC is not supported! \n"); } - if (BC_outlet_min>0){ + if (BC_outlet_min > 0) { //read in outlet values Cout - if (ion_db->keyExists("OutletValueList")){ - Cout = ion_db->getVector( "OutletValueList" ); - if (Cout.size()!=number_ion_species){ - ERROR("Error: number_ion_species and OutletValueList must be the same length! \n"); + if (ion_db->keyExists("OutletValueList")) { + Cout = ion_db->getVector("OutletValueList"); + if (Cout.size() != number_ion_species) { + ERROR("Error: number_ion_species and OutletValueList must " + "be the same length! \n"); } + } else { + ERROR("Error: Non-periodic BCs are specified but " + "OutletValueList cannot be found! \n"); } - else { - ERROR("Error: Non-periodic BCs are specified but OutletValueList cannot be found! \n"); - } - for (size_t i=0;i(new Domain(domain_db,comm)); // full domain for analysis - Mask = std::shared_ptr(new Domain(domain_db,comm)); // mask domain removes immobile phases +void ScaLBL_IonModel::SetDomain() { + Dm = std::shared_ptr( + new Domain(domain_db, comm)); // full domain for analysis + Mask = std::shared_ptr( + new Domain(domain_db, comm)); // mask domain removes immobile phases - // domain parameters - Nx = Dm->Nx; - Ny = Dm->Ny; - Nz = Dm->Nz; - Lx = Dm->Lx; - Ly = Dm->Ly; - Lz = Dm->Lz; - - N = Nx*Ny*Nz; - Distance.resize(Nx,Ny,Nz); - - for (int i=0; iid[i] = 1; // initialize this way - //Averages = std::shared_ptr ( new TwoPhase(Dm) ); // TwoPhase analysis object - comm.barrier(); - - unsigned short int BC_inlet_min = *min_element(BoundaryConditionInlet.begin(),BoundaryConditionInlet.end()); - unsigned short int BC_outlet_min = *min_element(BoundaryConditionOutlet.begin(),BoundaryConditionOutlet.end()); - if (BC_inlet_min==0 && BC_outlet_min==0){ - Dm->BoundaryCondition = 0; + // domain parameters + Nx = Dm->Nx; + Ny = Dm->Ny; + Nz = Dm->Nz; + Lx = Dm->Lx; + Ly = Dm->Ly; + Lz = Dm->Lz; + + N = Nx * Ny * Nz; + Distance.resize(Nx, Ny, Nz); + + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = 1; // initialize this way + //Averages = std::shared_ptr ( new TwoPhase(Dm) ); // TwoPhase analysis object + comm.barrier(); + + unsigned short int BC_inlet_min = *min_element( + BoundaryConditionInlet.begin(), BoundaryConditionInlet.end()); + unsigned short int BC_outlet_min = *min_element( + BoundaryConditionOutlet.begin(), BoundaryConditionOutlet.end()); + if (BC_inlet_min == 0 && BC_outlet_min == 0) { + Dm->BoundaryCondition = 0; Mask->BoundaryCondition = 0; - } - else if (BC_inlet_min>0 && BC_outlet_min>0){ - Dm->BoundaryCondition = 1; + } else if (BC_inlet_min > 0 && BC_outlet_min > 0) { + Dm->BoundaryCondition = 1; Mask->BoundaryCondition = 1; + } else { //i.e. periodic and non-periodic BCs are mixed + ERROR("Error: check the type of inlet and outlet boundary condition! " + "Mixed periodic and non-periodic BCs are found. \n"); } - else { //i.e. periodic and non-periodic BCs are mixed - ERROR("Error: check the type of inlet and outlet boundary condition! Mixed periodic and non-periodic BCs are found. \n"); - } - - Dm->CommInit(); - comm.barrier(); - - rank = Dm->rank(); - nprocx = Dm->nprocx(); - nprocy = Dm->nprocy(); - nprocz = Dm->nprocz(); + + Dm->CommInit(); + comm.barrier(); + + rank = Dm->rank(); + nprocx = Dm->nprocx(); + nprocy = Dm->nprocy(); + nprocz = Dm->nprocz(); } -void ScaLBL_IonModel::ReadInput(){ - - sprintf(LocalRankString,"%05d",Dm->rank()); - sprintf(LocalRankFilename,"%s%s","ID.",LocalRankString); - sprintf(LocalRestartFile,"%s%s","Restart.",LocalRankString); +void ScaLBL_IonModel::ReadInput() { - - if (domain_db->keyExists( "Filename" )){ - auto Filename = domain_db->getScalar( "Filename" ); - Mask->Decomp(Filename); - } - else if (domain_db->keyExists( "GridFile" )){ - // Read the local domain data - auto input_id = readMicroCT( *domain_db, comm ); - // Fill the halo (assuming GCW of 1) - array size0 = { (int) input_id.size(0), (int) input_id.size(1), (int) input_id.size(2) }; - ArraySize size1 = { (size_t) Mask->Nx, (size_t) Mask->Ny, (size_t) Mask->Nz }; - ASSERT( (int) size1[0] == size0[0]+2 && (int) size1[1] == size0[1]+2 && (int) size1[2] == size0[2]+2 ); - fillHalo fill( comm, Mask->rank_info, size0, { 1, 1, 1 }, 0, 1 ); - Array id_view; - id_view.viewRaw( size1, Mask->id.data() ); - fill.copy( input_id, id_view ); - fill.fill( id_view ); - } - else{ - Mask->ReadIDs(); + sprintf(LocalRankString, "%05d", Dm->rank()); + sprintf(LocalRankFilename, "%s%s", "ID.", LocalRankString); + sprintf(LocalRestartFile, "%s%s", "Restart.", LocalRankString); + + if (domain_db->keyExists("Filename")) { + auto Filename = domain_db->getScalar("Filename"); + Mask->Decomp(Filename); + } else if (domain_db->keyExists("GridFile")) { + // Read the local domain data + auto input_id = readMicroCT(*domain_db, comm); + // Fill the halo (assuming GCW of 1) + array size0 = {(int)input_id.size(0), (int)input_id.size(1), + (int)input_id.size(2)}; + ArraySize size1 = {(size_t)Mask->Nx, (size_t)Mask->Ny, + (size_t)Mask->Nz}; + ASSERT((int)size1[0] == size0[0] + 2 && (int)size1[1] == size0[1] + 2 && + (int)size1[2] == size0[2] + 2); + fillHalo fill(comm, Mask->rank_info, size0, {1, 1, 1}, 0, + 1); + Array id_view; + id_view.viewRaw(size1, Mask->id.data()); + fill.copy(input_id, id_view); + fill.fill(id_view); + } else { + Mask->ReadIDs(); } // Generate the signed distance map - // Initialize the domain and communication - Array id_solid(Nx,Ny,Nz); - // Solve for the position of the solid phase - for (int k=0;kid[n] > 0) id_solid(i,j,k) = 1; - else id_solid(i,j,k) = 0; - } - } - } - // Initialize the signed distance function - for (int k=0;kSDs); - if (rank==0) printf("LB Ion Solver: Initialized solid phase & converting to Signed Distance function \n"); - CalcDist(Distance,id_solid,*Dm); - if (rank == 0) cout << " Domain set." << endl; + // Initialize the domain and communication + Array id_solid(Nx, Ny, Nz); + // Solve for the position of the solid phase + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + // Initialize the solid phase + if (Mask->id[n] > 0) + id_solid(i, j, k) = 1; + else + id_solid(i, j, k) = 0; + } + } + } + // Initialize the signed distance function + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + // Initialize distance to +/- 1 + Distance(i, j, k) = 2.0 * double(id_solid(i, j, k)) - 1.0; + } + } + } + // MeanFilter(Averages->SDs); + if (rank == 0) + printf("LB Ion Solver: Initialized solid phase & converting to Signed " + "Distance function \n"); + CalcDist(Distance, id_solid, *Dm); + if (rank == 0) + cout << " Domain set." << endl; } +void ScaLBL_IonModel::AssignSolidBoundary(double *ion_solid) { + size_t NLABELS = 0; + signed char VALUE = 0; + double AFFINITY = 0.f; -void ScaLBL_IonModel::AssignSolidBoundary(double *ion_solid) -{ - size_t NLABELS=0; - signed char VALUE=0; - double AFFINITY=0.f; + auto LabelList = ion_db->getVector("SolidLabels"); + auto AffinityList = ion_db->getVector("SolidValues"); - auto LabelList = ion_db->getVector( "SolidLabels" ); - auto AffinityList = ion_db->getVector( "SolidValues" ); + NLABELS = LabelList.size(); + if (NLABELS != AffinityList.size()) { + ERROR("Error: LB Ion Solver: SolidLabels and SolidValues must be the " + "same length! \n"); + } - NLABELS=LabelList.size(); - if (NLABELS != AffinityList.size()){ - ERROR("Error: LB Ion Solver: SolidLabels and SolidValues must be the same length! \n"); - } + // Assign the labels + double *label_count; + double *label_count_global; + label_count = new double[NLABELS]; + label_count_global = new double[NLABELS]; + for (size_t idx = 0; idx < NLABELS; idx++) + label_count[idx] = 0; - - // Assign the labels - double *label_count; - double *label_count_global; - label_count = new double [NLABELS]; - label_count_global = new double [NLABELS]; - for (size_t idx=0; idxid[n]; - AFFINITY=0.f; - // Assign the affinity from the paired list - for (size_t idx=0; idx < NLABELS; idx++){ - //printf("idx=%i, value=%i, %i, \n",idx, VALUE,LabelList[idx]); - if (VALUE == LabelList[idx]){ - AFFINITY=AffinityList[idx]; + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + VALUE = Mask->id[n]; + AFFINITY = 0.f; + // Assign the affinity from the paired list + for (size_t idx = 0; idx < NLABELS; idx++) { + //printf("idx=%i, value=%i, %i, \n",idx, VALUE,LabelList[idx]); + if (VALUE == LabelList[idx]) { + AFFINITY = AffinityList[idx]; //NOTE need to convert the user input phys unit to LB unit - AFFINITY = AFFINITY*(h*h*h*1.0e-18); - label_count[idx] += 1.0; - idx = NLABELS; - //Mask->id[n] = 0; // set mask to zero since this is an immobile component - } - } - ion_solid[n] = AFFINITY; - } - } - } + AFFINITY = AFFINITY * (h * h * h * 1.0e-18); + label_count[idx] += 1.0; + idx = NLABELS; + //Mask->id[n] = 0; // set mask to zero since this is an immobile component + } + } + ion_solid[n] = AFFINITY; + } + } + } - for (size_t idx=0; idxComm.sumReduce( label_count[idx]); + for (size_t idx = 0; idx < NLABELS; idx++) + label_count_global[idx] = Dm->Comm.sumReduce(label_count[idx]); - if (rank==0){ - printf("LB Ion Solver: number of ion solid labels: %lu \n",NLABELS); - for (unsigned int idx=0; idx &File_ion, int ic) -{ +void ScaLBL_IonModel::AssignIonConcentration_FromFile( + double *Ci, const vector &File_ion, int ic) { double *Ci_host; Ci_host = new double[N]; - double VALUE=0.f; + double VALUE = 0.f; - Mask->ReadFromFile(File_ion[2*ic],File_ion[2*ic+1],Ci_host); + Mask->ReadFromFile(File_ion[2 * ic], File_ion[2 * ic + 1], Ci_host); - for (int k=0;krank(); - //......................................................... - // Initialize communication structures in averaging domain - for (int i=0; iid[i] = Mask->id[i]; - Mask->CommInit(); - Np=Mask->PoreCount(); - //........................................................................... - if (rank==0) printf ("LB Ion Solver: Create ScaLBL_Communicator \n"); - // Create a communicator for the device (will use optimized layout) - // ScaLBL_Communicator ScaLBL_Comm(Mask); // original - ScaLBL_Comm = std::shared_ptr(new ScaLBL_Communicator(Mask)); + int rank = Mask->rank(); + //......................................................... + // Initialize communication structures in averaging domain + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = Mask->id[i]; + Mask->CommInit(); + Np = Mask->PoreCount(); + //........................................................................... + if (rank == 0) + printf("LB Ion Solver: Create ScaLBL_Communicator \n"); + // Create a communicator for the device (will use optimized layout) + // ScaLBL_Communicator ScaLBL_Comm(Mask); // original + ScaLBL_Comm = + std::shared_ptr(new ScaLBL_Communicator(Mask)); - int Npad=(Np/16 + 2)*16; - if (rank==0) printf ("LB Ion Solver: Set up memory efficient layout \n"); - Map.resize(Nx,Ny,Nz); Map.fill(-2); - auto neighborList= new int[18*Npad]; - Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map,neighborList,Mask->id.data(),Np,1); - comm.barrier(); + int Npad = (Np / 16 + 2) * 16; + if (rank == 0) + printf("LB Ion Solver: Set up memory efficient layout \n"); + Map.resize(Nx, Ny, Nz); + Map.fill(-2); + auto neighborList = new int[18 * Npad]; + Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map, neighborList, + Mask->id.data(), Np, 1); + comm.barrier(); + + //........................................................................... + // MAIN VARIABLES ALLOCATED HERE + //........................................................................... + // LBM variables + if (rank == 0) + printf("LB Ion Solver: Allocating distributions \n"); + //......................device distributions................................. + int dist_mem_size = Np * sizeof(double); + int neighborSize = 18 * (Np * sizeof(int)); + //........................................................................... + ScaLBL_AllocateDeviceMemory((void **)&NeighborList, neighborSize); + ScaLBL_AllocateDeviceMemory((void **)&fq, + number_ion_species * 7 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Ci, + number_ion_species * sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&ChargeDensity, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&FluxDiffusive, + number_ion_species * 3 * sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&FluxAdvective, + number_ion_species * 3 * sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&FluxElectrical, + number_ion_species * 3 * sizeof(double) * Np); + //........................................................................... + // Update GPU data structures + if (rank == 0) + printf("LB Ion Solver: Setting up device map and neighbor list \n"); + // copy the neighbor list + ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); + comm.barrier(); - //........................................................................... - // MAIN VARIABLES ALLOCATED HERE - //........................................................................... - // LBM variables - if (rank==0) printf ("LB Ion Solver: Allocating distributions \n"); - //......................device distributions................................. - int dist_mem_size = Np*sizeof(double); - int neighborSize=18*(Np*sizeof(int)); - //........................................................................... - ScaLBL_AllocateDeviceMemory((void **) &NeighborList, neighborSize); - ScaLBL_AllocateDeviceMemory((void **) &fq, number_ion_species*7*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Ci, number_ion_species*sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &ChargeDensity, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &FluxDiffusive, number_ion_species*3*sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &FluxAdvective, number_ion_species*3*sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &FluxElectrical, number_ion_species*3*sizeof(double)*Np); - //........................................................................... - // Update GPU data structures - if (rank==0) printf ("LB Ion Solver: Setting up device map and neighbor list \n"); - // copy the neighbor list - ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); - comm.barrier(); - //Initialize solid boundary for electrical potential //if ion concentration at solid surface is specified - if (BoundaryConditionSolid==1){ + if (BoundaryConditionSolid == 1) { - ScaLBL_AllocateDeviceMemory((void **) &IonSolid, sizeof(double)*Nx*Ny*Nz); + ScaLBL_AllocateDeviceMemory((void **)&IonSolid, + sizeof(double) * Nx * Ny * Nz); ScaLBL_Comm->SetupBounceBackList(Map, Mask->id.data(), Np); comm.barrier(); double *IonSolid_host; - IonSolid_host = new double[Nx*Ny*Nz]; + IonSolid_host = new double[Nx * Ny * Nz]; AssignSolidBoundary(IonSolid_host); - ScaLBL_CopyToDevice(IonSolid, IonSolid_host, Nx*Ny*Nz*sizeof(double)); + ScaLBL_CopyToDevice(IonSolid, IonSolid_host, + Nx * Ny * Nz * sizeof(double)); ScaLBL_Comm->Barrier(); - delete [] IonSolid_host; + delete[] IonSolid_host; } -} - -void ScaLBL_IonModel::Initialize(){ - /* - * This function initializes model - */ - if (rank==0) printf ("LB Ion Solver: initializing D3Q7 distributions\n"); - if (ion_db->keyExists("IonConcentrationFile")){ - //NOTE: "IonConcentrationFile" is a vector, including "file_name, datatype" - auto File_ion = ion_db->getVector( "IonConcentrationFile" ); - if (File_ion.size()==2*number_ion_species){ - double *Ci_host; - Ci_host = new double[number_ion_species*Np]; - for (size_t ic=0; icFirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_D3Q7_Ion_ChargeDensity(Ci, ChargeDensity, IonValence[ic], ic, 0, ScaLBL_Comm->LastExterior(), Np); - } - - switch (BoundaryConditionSolid){ - case 0: - if (rank==0) printf("LB Ion Solver: solid boundary: non-flux boundary is assigned\n"); - break; - case 1: - if (rank==0) printf("LB Ion Solver: solid boundary: Dirichlet-type surfacen ion concentration is assigned\n"); - break; - default: - if (rank==0) printf("LB Ion Solver: solid boundary: non-flux boundary is assigned\n"); - break; - } - - for (size_t i=0; ikeyExists("IonConcentrationFile")) { + //NOTE: "IonConcentrationFile" is a vector, including "file_name, datatype" + auto File_ion = ion_db->getVector("IonConcentrationFile"); + if (File_ion.size() == 2 * number_ion_species) { + double *Ci_host; + Ci_host = new double[number_ion_species * Np]; + for (size_t ic = 0; ic < number_ion_species; ic++) { + AssignIonConcentration_FromFile(&Ci_host[ic * Np], File_ion, + ic); + } + ScaLBL_CopyToDevice(Ci, Ci_host, + number_ion_species * sizeof(double) * Np); + comm.barrier(); + for (size_t ic = 0; ic < number_ion_species; ic++) { + ScaLBL_D3Q7_Ion_Init_FromFile(&fq[ic * Np * 7], &Ci[ic * Np], + Np); + } + delete[] Ci_host; + } else { + ERROR("Error: Number of user-input ion concentration files should " + "be equal to number of ion species!\n"); + } + } else { + for (size_t ic = 0; ic < number_ion_species; ic++) { + ScaLBL_D3Q7_Ion_Init(&fq[ic * Np * 7], &Ci[ic * Np], + IonConcentration[ic], Np); + } + } + if (rank == 0) + printf("LB Ion Solver: initializing charge density\n"); + for (size_t ic = 0; ic < number_ion_species; ic++) { + ScaLBL_D3Q7_Ion_ChargeDensity(Ci, ChargeDensity, IonValence[ic], ic, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_D3Q7_Ion_ChargeDensity(Ci, ChargeDensity, IonValence[ic], ic, 0, + ScaLBL_Comm->LastExterior(), Np); + } + + switch (BoundaryConditionSolid) { + case 0: + if (rank == 0) + printf("LB Ion Solver: solid boundary: non-flux boundary is " + "assigned\n"); + break; + case 1: + if (rank == 0) + printf("LB Ion Solver: solid boundary: Dirichlet-type surfacen ion " + "concentration is assigned\n"); + break; + default: + if (rank == 0) + printf("LB Ion Solver: solid boundary: non-flux boundary is " + "assigned\n"); + break; + } + + for (size_t i = 0; i < number_ion_species; i++) { + switch (BoundaryConditionInlet[i]) { + case 0: + if (rank == 0) + printf( + "LB Ion Solver: inlet boundary for Ion %zu is periodic \n", + i + 1); + break; + case 1: + if (rank == 0) + printf("LB Ion Solver: inlet boundary for Ion %zu is " + "concentration = %.5g [mol/m^3] \n", + i + 1, Cin[i] / (h * h * h * 1.0e-18)); + break; + case 21: + if (rank == 0) + printf("LB Ion Solver: inlet boundary for Ion %zu is (inward) " + "flux = %.5g [mol/m^2/sec]; Diffusive flux only. \n", + i + 1, Cin[i] / (h * h * 1.0e-12) / time_conv[i]); + break; + case 22: + if (rank == 0) + printf( + "LB Ion Solver: inlet boundary for Ion %zu is (inward) " + "flux = %.5g [mol/m^2/sec]; Diffusive + advective flux. \n", + i + 1, Cin[i] / (h * h * 1.0e-12) / time_conv[i]); + break; + case 23: + if (rank == 0) + printf("LB Ion Solver: inlet boundary for Ion %zu is (inward) " + "flux = %.5g [mol/m^2/sec]; Diffusive + advective + " + "electric flux. \n", + i + 1, Cin[i] / (h * h * 1.0e-12) / time_conv[i]); + break; + } + switch (BoundaryConditionOutlet[i]) { + case 0: + if (rank == 0) + printf( + "LB Ion Solver: outlet boundary for Ion %zu is periodic \n", + i + 1); + break; + case 1: + if (rank == 0) + printf("LB Ion Solver: outlet boundary for Ion %zu is " + "concentration = %.5g [mol/m^3] \n", + i + 1, Cout[i] / (h * h * h * 1.0e-18)); + break; + case 21: + if (rank == 0) + printf("LB Ion Solver: outlet boundary for Ion %zu is (inward) " + "flux = %.5g [mol/m^2/sec]; Diffusive flux only. \n", + i + 1, Cout[i] / (h * h * 1.0e-12) / time_conv[i]); + break; + case 22: + if (rank == 0) + printf( + "LB Ion Solver: outlet boundary for Ion %zu is (inward) " + "flux = %.5g [mol/m^2/sec]; Diffusive + advective flux. \n", + i + 1, Cout[i] / (h * h * 1.0e-12) / time_conv[i]); + break; + case 23: + if (rank == 0) + printf("LB Ion Solver: outlet boundary for Ion %zu is (inward) " + "flux = %.5g [mol/m^2/sec]; Diffusive + advective + " + "electric flux. \n", + i + 1, Cout[i] / (h * h * 1.0e-12) / time_conv[i]); + break; + } + } + + if (rank == 0) + printf("*****************************************************\n"); + if (rank == 0) + printf("LB Ion Transport Solver: \n"); + for (size_t i = 0; i < number_ion_species; i++) { + if (rank == 0) + printf(" Ion %zu: LB relaxation tau = %.5g\n", i + 1, tau[i]); + if (rank == 0) + printf(" Time conversion factor: %.5g [sec/lt]\n", + time_conv[i]); + if (rank == 0) + printf(" Internal iteration: %i [lt]\n", + timestepMax[i]); + } + if (rank == 0) + printf("*****************************************************\n"); +} + +void ScaLBL_IonModel::Run(double *Velocity, double *ElectricField) { //Input parameter: //1. Velocity is from StokesModel @@ -822,447 +975,579 @@ void ScaLBL_IonModel::Run(double *Velocity, double *ElectricField){ //LB-related parameter vector rlx; - for (size_t ic=0;icBarrier(); comm.barrier(); + + //.......create and start timer............ + //double starttime,stoptime,cputime; + //ScaLBL_Comm->Barrier(); comm.barrier(); //auto t1 = std::chrono::system_clock::now(); - for (size_t ic=0; icSendD3Q7AA(fq, ic); //READ FROM NORMAL - ScaLBL_D3Q7_AAodd_IonConcentration(NeighborList, &fq[ic*Np*7],&Ci[ic*Np],ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + ScaLBL_D3Q7_AAodd_IonConcentration( + NeighborList, &fq[ic * Np * 7], &Ci[ic * Np], + ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); ScaLBL_Comm->RecvD3Q7AA(fq, ic); //WRITE INTO OPPOSITE ScaLBL_Comm->Barrier(); //--------------------------------------- Set boundary conditions -------------------------------------// - if (BoundaryConditionInlet[ic]>0){ - switch (BoundaryConditionInlet[ic]){ - case 1: - ScaLBL_Comm->D3Q7_Ion_Concentration_BC_z(NeighborList, &fq[ic*Np*7], Cin[ic], timestep); - break; - case 21: - ScaLBL_Comm->D3Q7_Ion_Flux_Diff_BC_z(NeighborList, &fq[ic*Np*7], Cin[ic], tau[ic], &Velocity[2*Np], timestep); - break; - case 22: - ScaLBL_Comm->D3Q7_Ion_Flux_DiffAdvc_BC_z(NeighborList, &fq[ic*Np*7], Cin[ic], tau[ic], &Velocity[2*Np], timestep); - break; - case 23: - ScaLBL_Comm->D3Q7_Ion_Flux_DiffAdvcElec_BC_z(NeighborList,&fq[ic*Np*7],Cin[ic],tau[ic],&Velocity[2*Np],&ElectricField[2*Np],IonDiffusivity[ic],IonValence[ic],Vt,timestep); - break; + if (BoundaryConditionInlet[ic] > 0) { + switch (BoundaryConditionInlet[ic]) { + case 1: + ScaLBL_Comm->D3Q7_Ion_Concentration_BC_z( + NeighborList, &fq[ic * Np * 7], Cin[ic], timestep); + break; + case 21: + ScaLBL_Comm->D3Q7_Ion_Flux_Diff_BC_z( + NeighborList, &fq[ic * Np * 7], Cin[ic], tau[ic], + &Velocity[2 * Np], timestep); + break; + case 22: + ScaLBL_Comm->D3Q7_Ion_Flux_DiffAdvc_BC_z( + NeighborList, &fq[ic * Np * 7], Cin[ic], tau[ic], + &Velocity[2 * Np], timestep); + break; + case 23: + ScaLBL_Comm->D3Q7_Ion_Flux_DiffAdvcElec_BC_z( + NeighborList, &fq[ic * Np * 7], Cin[ic], tau[ic], + &Velocity[2 * Np], &ElectricField[2 * Np], + IonDiffusivity[ic], IonValence[ic], Vt, timestep); + break; } } - if (BoundaryConditionOutlet[ic]>0){ - switch (BoundaryConditionOutlet[ic]){ - case 1: - ScaLBL_Comm->D3Q7_Ion_Concentration_BC_Z(NeighborList, &fq[ic*Np*7], Cout[ic], timestep); - break; - case 21: - ScaLBL_Comm->D3Q7_Ion_Flux_Diff_BC_Z(NeighborList, &fq[ic*Np*7], Cout[ic], tau[ic], &Velocity[2*Np], timestep); - break; - case 22: - ScaLBL_Comm->D3Q7_Ion_Flux_DiffAdvc_BC_Z(NeighborList, &fq[ic*Np*7], Cout[ic], tau[ic], &Velocity[2*Np], timestep); - break; - case 23: - ScaLBL_Comm->D3Q7_Ion_Flux_DiffAdvcElec_BC_Z(NeighborList,&fq[ic*Np*7],Cout[ic],tau[ic],&Velocity[2*Np],&ElectricField[2*Np],IonDiffusivity[ic],IonValence[ic],Vt,timestep); - break; + if (BoundaryConditionOutlet[ic] > 0) { + switch (BoundaryConditionOutlet[ic]) { + case 1: + ScaLBL_Comm->D3Q7_Ion_Concentration_BC_Z( + NeighborList, &fq[ic * Np * 7], Cout[ic], timestep); + break; + case 21: + ScaLBL_Comm->D3Q7_Ion_Flux_Diff_BC_Z( + NeighborList, &fq[ic * Np * 7], Cout[ic], tau[ic], + &Velocity[2 * Np], timestep); + break; + case 22: + ScaLBL_Comm->D3Q7_Ion_Flux_DiffAdvc_BC_Z( + NeighborList, &fq[ic * Np * 7], Cout[ic], tau[ic], + &Velocity[2 * Np], timestep); + break; + case 23: + ScaLBL_Comm->D3Q7_Ion_Flux_DiffAdvcElec_BC_Z( + NeighborList, &fq[ic * Np * 7], Cout[ic], tau[ic], + &Velocity[2 * Np], &ElectricField[2 * Np], + IonDiffusivity[ic], IonValence[ic], Vt, timestep); + break; } } //----------------------------------------------------------------------------------------------------// - ScaLBL_D3Q7_AAodd_IonConcentration(NeighborList, &fq[ic*Np*7],&Ci[ic*Np], 0, ScaLBL_Comm->LastExterior(), Np); - + ScaLBL_D3Q7_AAodd_IonConcentration(NeighborList, &fq[ic * Np * 7], + &Ci[ic * Np], 0, + ScaLBL_Comm->LastExterior(), Np); //LB-Ion collison - ScaLBL_D3Q7_AAodd_Ion(NeighborList, &fq[ic*Np*7],&Ci[ic*Np],&FluxDiffusive[3*ic*Np],&FluxAdvective[3*ic*Np],&FluxElectrical[3*ic*Np],Velocity,ElectricField,IonDiffusivity[ic],IonValence[ic], - rlx[ic],Vt,ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_D3Q7_AAodd_Ion(NeighborList, &fq[ic*Np*7],&Ci[ic*Np],&FluxDiffusive[3*ic*Np],&FluxAdvective[3*ic*Np],&FluxElectrical[3*ic*Np],Velocity,ElectricField,IonDiffusivity[ic],IonValence[ic], - rlx[ic],Vt,0, ScaLBL_Comm->LastExterior(), Np); - - if (BoundaryConditionSolid==1){ + ScaLBL_D3Q7_AAodd_Ion( + NeighborList, &fq[ic * Np * 7], &Ci[ic * Np], + &FluxDiffusive[3 * ic * Np], &FluxAdvective[3 * ic * Np], + &FluxElectrical[3 * ic * Np], Velocity, ElectricField, + IonDiffusivity[ic], IonValence[ic], rlx[ic], Vt, + ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + ScaLBL_D3Q7_AAodd_Ion( + NeighborList, &fq[ic * Np * 7], &Ci[ic * Np], + &FluxDiffusive[3 * ic * Np], &FluxAdvective[3 * ic * Np], + &FluxElectrical[3 * ic * Np], Velocity, ElectricField, + IonDiffusivity[ic], IonValence[ic], rlx[ic], Vt, 0, + ScaLBL_Comm->LastExterior(), Np); + + if (BoundaryConditionSolid == 1) { //TODO IonSolid may also be species-dependent - ScaLBL_Comm->SolidDirichletD3Q7(&fq[ic*Np*7], IonSolid); + ScaLBL_Comm->SolidDirichletD3Q7(&fq[ic * Np * 7], IonSolid); } - ScaLBL_Comm->Barrier(); comm.barrier(); + ScaLBL_Comm->Barrier(); + comm.barrier(); // *************EVEN TIMESTEP*************// timestep++; //Update ion concentration and charge density ScaLBL_Comm->SendD3Q7AA(fq, ic); //READ FORM NORMAL - ScaLBL_D3Q7_AAeven_IonConcentration(&fq[ic*Np*7],&Ci[ic*Np],ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + ScaLBL_D3Q7_AAeven_IonConcentration( + &fq[ic * Np * 7], &Ci[ic * Np], ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); ScaLBL_Comm->RecvD3Q7AA(fq, ic); //WRITE INTO OPPOSITE ScaLBL_Comm->Barrier(); //--------------------------------------- Set boundary conditions -------------------------------------// - if (BoundaryConditionInlet[ic]>0){ - switch (BoundaryConditionInlet[ic]){ - case 1: - ScaLBL_Comm->D3Q7_Ion_Concentration_BC_z(NeighborList, &fq[ic*Np*7], Cin[ic], timestep); - break; - case 21: - ScaLBL_Comm->D3Q7_Ion_Flux_Diff_BC_z(NeighborList, &fq[ic*Np*7], Cin[ic], tau[ic], &Velocity[2*Np], timestep); - break; - case 22: - ScaLBL_Comm->D3Q7_Ion_Flux_DiffAdvc_BC_z(NeighborList, &fq[ic*Np*7], Cin[ic], tau[ic], &Velocity[2*Np], timestep); - break; - case 23: - ScaLBL_Comm->D3Q7_Ion_Flux_DiffAdvcElec_BC_z(NeighborList,&fq[ic*Np*7],Cin[ic],tau[ic],&Velocity[2*Np],&ElectricField[2*Np],IonDiffusivity[ic],IonValence[ic],Vt,timestep); - break; + if (BoundaryConditionInlet[ic] > 0) { + switch (BoundaryConditionInlet[ic]) { + case 1: + ScaLBL_Comm->D3Q7_Ion_Concentration_BC_z( + NeighborList, &fq[ic * Np * 7], Cin[ic], timestep); + break; + case 21: + ScaLBL_Comm->D3Q7_Ion_Flux_Diff_BC_z( + NeighborList, &fq[ic * Np * 7], Cin[ic], tau[ic], + &Velocity[2 * Np], timestep); + break; + case 22: + ScaLBL_Comm->D3Q7_Ion_Flux_DiffAdvc_BC_z( + NeighborList, &fq[ic * Np * 7], Cin[ic], tau[ic], + &Velocity[2 * Np], timestep); + break; + case 23: + ScaLBL_Comm->D3Q7_Ion_Flux_DiffAdvcElec_BC_z( + NeighborList, &fq[ic * Np * 7], Cin[ic], tau[ic], + &Velocity[2 * Np], &ElectricField[2 * Np], + IonDiffusivity[ic], IonValence[ic], Vt, timestep); + break; } } - if (BoundaryConditionOutlet[ic]>0){ - switch (BoundaryConditionOutlet[ic]){ - case 1: - ScaLBL_Comm->D3Q7_Ion_Concentration_BC_Z(NeighborList, &fq[ic*Np*7], Cout[ic], timestep); - break; - case 21: - ScaLBL_Comm->D3Q7_Ion_Flux_Diff_BC_Z(NeighborList, &fq[ic*Np*7], Cout[ic], tau[ic], &Velocity[2*Np], timestep); - break; - case 22: - ScaLBL_Comm->D3Q7_Ion_Flux_DiffAdvc_BC_Z(NeighborList, &fq[ic*Np*7], Cout[ic], tau[ic], &Velocity[2*Np], timestep); - break; - case 23: - ScaLBL_Comm->D3Q7_Ion_Flux_DiffAdvcElec_BC_Z(NeighborList,&fq[ic*Np*7],Cout[ic],tau[ic],&Velocity[2*Np],&ElectricField[2*Np],IonDiffusivity[ic],IonValence[ic],Vt,timestep); - break; + if (BoundaryConditionOutlet[ic] > 0) { + switch (BoundaryConditionOutlet[ic]) { + case 1: + ScaLBL_Comm->D3Q7_Ion_Concentration_BC_Z( + NeighborList, &fq[ic * Np * 7], Cout[ic], timestep); + break; + case 21: + ScaLBL_Comm->D3Q7_Ion_Flux_Diff_BC_Z( + NeighborList, &fq[ic * Np * 7], Cout[ic], tau[ic], + &Velocity[2 * Np], timestep); + break; + case 22: + ScaLBL_Comm->D3Q7_Ion_Flux_DiffAdvc_BC_Z( + NeighborList, &fq[ic * Np * 7], Cout[ic], tau[ic], + &Velocity[2 * Np], timestep); + break; + case 23: + ScaLBL_Comm->D3Q7_Ion_Flux_DiffAdvcElec_BC_Z( + NeighborList, &fq[ic * Np * 7], Cout[ic], tau[ic], + &Velocity[2 * Np], &ElectricField[2 * Np], + IonDiffusivity[ic], IonValence[ic], Vt, timestep); + break; } } //----------------------------------------------------------------------------------------------------// - ScaLBL_D3Q7_AAeven_IonConcentration(&fq[ic*Np*7],&Ci[ic*Np], 0, ScaLBL_Comm->LastExterior(), Np); - + ScaLBL_D3Q7_AAeven_IonConcentration(&fq[ic * Np * 7], &Ci[ic * Np], + 0, ScaLBL_Comm->LastExterior(), + Np); //LB-Ion collison - ScaLBL_D3Q7_AAeven_Ion(&fq[ic*Np*7],&Ci[ic*Np],&FluxDiffusive[3*ic*Np],&FluxAdvective[3*ic*Np],&FluxElectrical[3*ic*Np],Velocity,ElectricField,IonDiffusivity[ic],IonValence[ic], - rlx[ic],Vt,ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_D3Q7_AAeven_Ion(&fq[ic*Np*7],&Ci[ic*Np],&FluxDiffusive[3*ic*Np],&FluxAdvective[3*ic*Np],&FluxElectrical[3*ic*Np],Velocity,ElectricField,IonDiffusivity[ic],IonValence[ic], - rlx[ic],Vt,0, ScaLBL_Comm->LastExterior(), Np); - - if (BoundaryConditionSolid==1){ + ScaLBL_D3Q7_AAeven_Ion( + &fq[ic * Np * 7], &Ci[ic * Np], &FluxDiffusive[3 * ic * Np], + &FluxAdvective[3 * ic * Np], &FluxElectrical[3 * ic * Np], + Velocity, ElectricField, IonDiffusivity[ic], IonValence[ic], + rlx[ic], Vt, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_D3Q7_AAeven_Ion( + &fq[ic * Np * 7], &Ci[ic * Np], &FluxDiffusive[3 * ic * Np], + &FluxAdvective[3 * ic * Np], &FluxElectrical[3 * ic * Np], + Velocity, ElectricField, IonDiffusivity[ic], IonValence[ic], + rlx[ic], Vt, 0, ScaLBL_Comm->LastExterior(), Np); + + if (BoundaryConditionSolid == 1) { //TODO IonSolid may also be species-dependent - ScaLBL_Comm->SolidDirichletD3Q7(&fq[ic*Np*7], IonSolid); + ScaLBL_Comm->SolidDirichletD3Q7(&fq[ic * Np * 7], IonSolid); } - ScaLBL_Comm->Barrier(); comm.barrier(); + ScaLBL_Comm->Barrier(); + comm.barrier(); } } //Compute charge density for Poisson equation - for (size_t ic=0; icFirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_D3Q7_Ion_ChargeDensity(Ci, ChargeDensity, IonValence[ic], ic, 0, ScaLBL_Comm->LastExterior(), Np); + for (size_t ic = 0; ic < number_ion_species; ic++) { + ScaLBL_D3Q7_Ion_ChargeDensity(Ci, ChargeDensity, IonValence[ic], ic, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_D3Q7_Ion_ChargeDensity(Ci, ChargeDensity, IonValence[ic], ic, 0, + ScaLBL_Comm->LastExterior(), Np); } - //************************************************************************/ - //if (rank==0) printf("-------------------------------------------------------------------\n"); - //// Compute the walltime per timestep + //************************************************************************/ + //if (rank==0) printf("-------------------------------------------------------------------\n"); + //// Compute the walltime per timestep //auto t2 = std::chrono::system_clock::now(); - //double cputime = std::chrono::duration( t2 - t1 ).count() / timestep; - //// Performance obtained from each node - //double MLUPS = double(Np)/cputime/1000000; + //double cputime = std::chrono::duration( t2 - t1 ).count() / timestep; + //// Performance obtained from each node + //double MLUPS = double(Np)/cputime/1000000; - //if (rank==0) printf("********************************************************\n"); - //if (rank==0) printf("CPU time = %f \n", cputime); - //if (rank==0) printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); - //MLUPS *= nprocs; - //if (rank==0) printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); - //if (rank==0) printf("********************************************************\n"); + //if (rank==0) printf("********************************************************\n"); + //if (rank==0) printf("CPU time = %f \n", cputime); + //if (rank==0) printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); + //MLUPS *= nprocs; + //if (rank==0) printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); + //if (rank==0) printf("********************************************************\n"); } -void ScaLBL_IonModel::getIonConcentration(DoubleArray &IonConcentration, const size_t ic){ - //This function wirte out the data in a normal layout (by aggregating all decomposed domains) +void ScaLBL_IonModel::getIonConcentration(DoubleArray &IonConcentration, + const size_t ic) { + //This function wirte out the data in a normal layout (by aggregating all decomposed domains) - ScaLBL_Comm->RegularLayout(Map,&Ci[ic*Np],IonConcentration); - ScaLBL_Comm->Barrier(); comm.barrier(); - IonConcentration_LB_to_Phys(IonConcentration); + ScaLBL_Comm->RegularLayout(Map, &Ci[ic * Np], IonConcentration); + ScaLBL_Comm->Barrier(); + comm.barrier(); + IonConcentration_LB_to_Phys(IonConcentration); } -void ScaLBL_IonModel::getIonFluxDiffusive(DoubleArray &IonFlux_x,DoubleArray &IonFlux_y,DoubleArray &IonFlux_z,const size_t ic){ - //This function wirte out the data in a normal layout (by aggregating all decomposed domains) +void ScaLBL_IonModel::getIonFluxDiffusive(DoubleArray &IonFlux_x, + DoubleArray &IonFlux_y, + DoubleArray &IonFlux_z, + const size_t ic) { + //This function wirte out the data in a normal layout (by aggregating all decomposed domains) - ScaLBL_Comm->RegularLayout(Map,&FluxDiffusive[ic*3*Np+0*Np],IonFlux_x); - IonFlux_LB_to_Phys(IonFlux_x,ic); - ScaLBL_Comm->Barrier(); comm.barrier(); + ScaLBL_Comm->RegularLayout(Map, &FluxDiffusive[ic * 3 * Np + 0 * Np], + IonFlux_x); + IonFlux_LB_to_Phys(IonFlux_x, ic); + ScaLBL_Comm->Barrier(); + comm.barrier(); - ScaLBL_Comm->RegularLayout(Map,&FluxDiffusive[ic*3*Np+1*Np],IonFlux_y); - IonFlux_LB_to_Phys(IonFlux_y,ic); - ScaLBL_Comm->Barrier(); comm.barrier(); + ScaLBL_Comm->RegularLayout(Map, &FluxDiffusive[ic * 3 * Np + 1 * Np], + IonFlux_y); + IonFlux_LB_to_Phys(IonFlux_y, ic); + ScaLBL_Comm->Barrier(); + comm.barrier(); - ScaLBL_Comm->RegularLayout(Map,&FluxDiffusive[ic*3*Np+2*Np],IonFlux_z); - IonFlux_LB_to_Phys(IonFlux_z,ic); - ScaLBL_Comm->Barrier(); comm.barrier(); + ScaLBL_Comm->RegularLayout(Map, &FluxDiffusive[ic * 3 * Np + 2 * Np], + IonFlux_z); + IonFlux_LB_to_Phys(IonFlux_z, ic); + ScaLBL_Comm->Barrier(); + comm.barrier(); } -void ScaLBL_IonModel::getIonFluxAdvective(DoubleArray &IonFlux_x,DoubleArray &IonFlux_y,DoubleArray &IonFlux_z,const size_t ic){ - //This function wirte out the data in a normal layout (by aggregating all decomposed domains) +void ScaLBL_IonModel::getIonFluxAdvective(DoubleArray &IonFlux_x, + DoubleArray &IonFlux_y, + DoubleArray &IonFlux_z, + const size_t ic) { + //This function wirte out the data in a normal layout (by aggregating all decomposed domains) - ScaLBL_Comm->RegularLayout(Map,&FluxAdvective[ic*3*Np+0*Np],IonFlux_x); - IonFlux_LB_to_Phys(IonFlux_x,ic); - ScaLBL_Comm->Barrier(); comm.barrier(); + ScaLBL_Comm->RegularLayout(Map, &FluxAdvective[ic * 3 * Np + 0 * Np], + IonFlux_x); + IonFlux_LB_to_Phys(IonFlux_x, ic); + ScaLBL_Comm->Barrier(); + comm.barrier(); - ScaLBL_Comm->RegularLayout(Map,&FluxAdvective[ic*3*Np+1*Np],IonFlux_y); - IonFlux_LB_to_Phys(IonFlux_y,ic); - ScaLBL_Comm->Barrier(); comm.barrier(); + ScaLBL_Comm->RegularLayout(Map, &FluxAdvective[ic * 3 * Np + 1 * Np], + IonFlux_y); + IonFlux_LB_to_Phys(IonFlux_y, ic); + ScaLBL_Comm->Barrier(); + comm.barrier(); - ScaLBL_Comm->RegularLayout(Map,&FluxAdvective[ic*3*Np+2*Np],IonFlux_z); - IonFlux_LB_to_Phys(IonFlux_z,ic); - ScaLBL_Comm->Barrier(); comm.barrier(); + ScaLBL_Comm->RegularLayout(Map, &FluxAdvective[ic * 3 * Np + 2 * Np], + IonFlux_z); + IonFlux_LB_to_Phys(IonFlux_z, ic); + ScaLBL_Comm->Barrier(); + comm.barrier(); } -void ScaLBL_IonModel::getIonFluxElectrical(DoubleArray &IonFlux_x,DoubleArray &IonFlux_y,DoubleArray &IonFlux_z,const size_t ic){ - //This function wirte out the data in a normal layout (by aggregating all decomposed domains) +void ScaLBL_IonModel::getIonFluxElectrical(DoubleArray &IonFlux_x, + DoubleArray &IonFlux_y, + DoubleArray &IonFlux_z, + const size_t ic) { + //This function wirte out the data in a normal layout (by aggregating all decomposed domains) - ScaLBL_Comm->RegularLayout(Map,&FluxElectrical[ic*3*Np+0*Np],IonFlux_x); - IonFlux_LB_to_Phys(IonFlux_x,ic); - ScaLBL_Comm->Barrier(); comm.barrier(); + ScaLBL_Comm->RegularLayout(Map, &FluxElectrical[ic * 3 * Np + 0 * Np], + IonFlux_x); + IonFlux_LB_to_Phys(IonFlux_x, ic); + ScaLBL_Comm->Barrier(); + comm.barrier(); - ScaLBL_Comm->RegularLayout(Map,&FluxElectrical[ic*3*Np+1*Np],IonFlux_y); - IonFlux_LB_to_Phys(IonFlux_y,ic); - ScaLBL_Comm->Barrier(); comm.barrier(); + ScaLBL_Comm->RegularLayout(Map, &FluxElectrical[ic * 3 * Np + 1 * Np], + IonFlux_y); + IonFlux_LB_to_Phys(IonFlux_y, ic); + ScaLBL_Comm->Barrier(); + comm.barrier(); - ScaLBL_Comm->RegularLayout(Map,&FluxElectrical[ic*3*Np+2*Np],IonFlux_z); - IonFlux_LB_to_Phys(IonFlux_z,ic); - ScaLBL_Comm->Barrier(); comm.barrier(); + ScaLBL_Comm->RegularLayout(Map, &FluxElectrical[ic * 3 * Np + 2 * Np], + IonFlux_z); + IonFlux_LB_to_Phys(IonFlux_z, ic); + ScaLBL_Comm->Barrier(); + comm.barrier(); } -void ScaLBL_IonModel::getIonConcentration_debug(int timestep){ +void ScaLBL_IonModel::getIonConcentration_debug(int timestep) { //This function write out decomposed data - DoubleArray PhaseField(Nx,Ny,Nz); - for (size_t ic=0; icRegularLayout(Map,&Ci[ic*Np],PhaseField); - ScaLBL_Comm->Barrier(); comm.barrier(); + DoubleArray PhaseField(Nx, Ny, Nz); + for (size_t ic = 0; ic < number_ion_species; ic++) { + ScaLBL_Comm->RegularLayout(Map, &Ci[ic * Np], PhaseField); + ScaLBL_Comm->Barrier(); + comm.barrier(); IonConcentration_LB_to_Phys(PhaseField); FILE *OUTFILE; - sprintf(LocalRankFilename,"Ion%02zu_Time_%i.%05i.raw",ic+1,timestep,rank); - OUTFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,OUTFILE); + sprintf(LocalRankFilename, "Ion%02zu_Time_%i.%05i.raw", ic + 1, + timestep, rank); + OUTFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, OUTFILE); fclose(OUTFILE); } } -void ScaLBL_IonModel::getIonFluxDiffusive_debug(int timestep){ +void ScaLBL_IonModel::getIonFluxDiffusive_debug(int timestep) { //This function write out decomposed data - DoubleArray PhaseField(Nx,Ny,Nz); - for (size_t ic=0; icRegularLayout(Map,&FluxDiffusive[ic*3*Np+0*Np],PhaseField); - ScaLBL_Comm->Barrier(); comm.barrier(); - IonFlux_LB_to_Phys(PhaseField,ic); + ScaLBL_Comm->RegularLayout(Map, &FluxDiffusive[ic * 3 * Np + 0 * Np], + PhaseField); + ScaLBL_Comm->Barrier(); + comm.barrier(); + IonFlux_LB_to_Phys(PhaseField, ic); FILE *OUTFILE_X; - sprintf(LocalRankFilename,"IonFluxDiffusive_X_%02zu_Time_%i.%05i.raw",ic+1,timestep,rank); - OUTFILE_X = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,OUTFILE_X); + sprintf(LocalRankFilename, "IonFluxDiffusive_X_%02zu_Time_%i.%05i.raw", + ic + 1, timestep, rank); + OUTFILE_X = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, OUTFILE_X); fclose(OUTFILE_X); //y-component - ScaLBL_Comm->RegularLayout(Map,&FluxDiffusive[ic*3*Np+1*Np],PhaseField); - ScaLBL_Comm->Barrier(); comm.barrier(); - IonFlux_LB_to_Phys(PhaseField,ic); + ScaLBL_Comm->RegularLayout(Map, &FluxDiffusive[ic * 3 * Np + 1 * Np], + PhaseField); + ScaLBL_Comm->Barrier(); + comm.barrier(); + IonFlux_LB_to_Phys(PhaseField, ic); FILE *OUTFILE_Y; - sprintf(LocalRankFilename,"IonFluxDiffusive_Y_%02zu_Time_%i.%05i.raw",ic+1,timestep,rank); - OUTFILE_Y = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,OUTFILE_Y); + sprintf(LocalRankFilename, "IonFluxDiffusive_Y_%02zu_Time_%i.%05i.raw", + ic + 1, timestep, rank); + OUTFILE_Y = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, OUTFILE_Y); fclose(OUTFILE_Y); //z-component - ScaLBL_Comm->RegularLayout(Map,&FluxDiffusive[ic*3*Np+2*Np],PhaseField); - ScaLBL_Comm->Barrier(); comm.barrier(); - IonFlux_LB_to_Phys(PhaseField,ic); + ScaLBL_Comm->RegularLayout(Map, &FluxDiffusive[ic * 3 * Np + 2 * Np], + PhaseField); + ScaLBL_Comm->Barrier(); + comm.barrier(); + IonFlux_LB_to_Phys(PhaseField, ic); FILE *OUTFILE_Z; - sprintf(LocalRankFilename,"IonFluxDiffusive_Z_%02zu_Time_%i.%05i.raw",ic+1,timestep,rank); - OUTFILE_Z = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,OUTFILE_Z); + sprintf(LocalRankFilename, "IonFluxDiffusive_Z_%02zu_Time_%i.%05i.raw", + ic + 1, timestep, rank); + OUTFILE_Z = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, OUTFILE_Z); fclose(OUTFILE_Z); } } -void ScaLBL_IonModel::getIonFluxAdvective_debug(int timestep){ +void ScaLBL_IonModel::getIonFluxAdvective_debug(int timestep) { //This function write out decomposed data - DoubleArray PhaseField(Nx,Ny,Nz); - for (size_t ic=0; icRegularLayout(Map,&FluxAdvective[ic*3*Np+0*Np],PhaseField); - ScaLBL_Comm->Barrier(); comm.barrier(); - IonFlux_LB_to_Phys(PhaseField,ic); + ScaLBL_Comm->RegularLayout(Map, &FluxAdvective[ic * 3 * Np + 0 * Np], + PhaseField); + ScaLBL_Comm->Barrier(); + comm.barrier(); + IonFlux_LB_to_Phys(PhaseField, ic); FILE *OUTFILE_X; - sprintf(LocalRankFilename,"IonFluxAdvective_X_%02zu_Time_%i.%05i.raw",ic+1,timestep,rank); - OUTFILE_X = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,OUTFILE_X); + sprintf(LocalRankFilename, "IonFluxAdvective_X_%02zu_Time_%i.%05i.raw", + ic + 1, timestep, rank); + OUTFILE_X = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, OUTFILE_X); fclose(OUTFILE_X); //y-component - ScaLBL_Comm->RegularLayout(Map,&FluxAdvective[ic*3*Np+1*Np],PhaseField); - ScaLBL_Comm->Barrier(); comm.barrier(); - IonFlux_LB_to_Phys(PhaseField,ic); + ScaLBL_Comm->RegularLayout(Map, &FluxAdvective[ic * 3 * Np + 1 * Np], + PhaseField); + ScaLBL_Comm->Barrier(); + comm.barrier(); + IonFlux_LB_to_Phys(PhaseField, ic); FILE *OUTFILE_Y; - sprintf(LocalRankFilename,"IonFluxAdvective_Y_%02zu_Time_%i.%05i.raw",ic+1,timestep,rank); - OUTFILE_Y = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,OUTFILE_Y); + sprintf(LocalRankFilename, "IonFluxAdvective_Y_%02zu_Time_%i.%05i.raw", + ic + 1, timestep, rank); + OUTFILE_Y = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, OUTFILE_Y); fclose(OUTFILE_Y); //z-component - ScaLBL_Comm->RegularLayout(Map,&FluxAdvective[ic*3*Np+2*Np],PhaseField); - ScaLBL_Comm->Barrier(); comm.barrier(); - IonFlux_LB_to_Phys(PhaseField,ic); + ScaLBL_Comm->RegularLayout(Map, &FluxAdvective[ic * 3 * Np + 2 * Np], + PhaseField); + ScaLBL_Comm->Barrier(); + comm.barrier(); + IonFlux_LB_to_Phys(PhaseField, ic); FILE *OUTFILE_Z; - sprintf(LocalRankFilename,"IonFluxAdvective_Z_%02zu_Time_%i.%05i.raw",ic+1,timestep,rank); - OUTFILE_Z = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,OUTFILE_Z); + sprintf(LocalRankFilename, "IonFluxAdvective_Z_%02zu_Time_%i.%05i.raw", + ic + 1, timestep, rank); + OUTFILE_Z = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, OUTFILE_Z); fclose(OUTFILE_Z); } } -void ScaLBL_IonModel::getIonFluxElectrical_debug(int timestep){ +void ScaLBL_IonModel::getIonFluxElectrical_debug(int timestep) { //This function write out decomposed data - DoubleArray PhaseField(Nx,Ny,Nz); - for (size_t ic=0; icRegularLayout(Map,&FluxElectrical[ic*3*Np+0*Np],PhaseField); - ScaLBL_Comm->Barrier(); comm.barrier(); - IonFlux_LB_to_Phys(PhaseField,ic); + ScaLBL_Comm->RegularLayout(Map, &FluxElectrical[ic * 3 * Np + 0 * Np], + PhaseField); + ScaLBL_Comm->Barrier(); + comm.barrier(); + IonFlux_LB_to_Phys(PhaseField, ic); FILE *OUTFILE_X; - sprintf(LocalRankFilename,"IonFluxElectrical_X_%02zu_Time_%i.%05i.raw",ic+1,timestep,rank); - OUTFILE_X = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,OUTFILE_X); + sprintf(LocalRankFilename, "IonFluxElectrical_X_%02zu_Time_%i.%05i.raw", + ic + 1, timestep, rank); + OUTFILE_X = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, OUTFILE_X); fclose(OUTFILE_X); //y-component - ScaLBL_Comm->RegularLayout(Map,&FluxElectrical[ic*3*Np+1*Np],PhaseField); - ScaLBL_Comm->Barrier(); comm.barrier(); - IonFlux_LB_to_Phys(PhaseField,ic); + ScaLBL_Comm->RegularLayout(Map, &FluxElectrical[ic * 3 * Np + 1 * Np], + PhaseField); + ScaLBL_Comm->Barrier(); + comm.barrier(); + IonFlux_LB_to_Phys(PhaseField, ic); FILE *OUTFILE_Y; - sprintf(LocalRankFilename,"IonFluxElectrical_Y_%02zu_Time_%i.%05i.raw",ic+1,timestep,rank); - OUTFILE_Y = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,OUTFILE_Y); + sprintf(LocalRankFilename, "IonFluxElectrical_Y_%02zu_Time_%i.%05i.raw", + ic + 1, timestep, rank); + OUTFILE_Y = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, OUTFILE_Y); fclose(OUTFILE_Y); //z-component - ScaLBL_Comm->RegularLayout(Map,&FluxElectrical[ic*3*Np+2*Np],PhaseField); - ScaLBL_Comm->Barrier(); comm.barrier(); - IonFlux_LB_to_Phys(PhaseField,ic); + ScaLBL_Comm->RegularLayout(Map, &FluxElectrical[ic * 3 * Np + 2 * Np], + PhaseField); + ScaLBL_Comm->Barrier(); + comm.barrier(); + IonFlux_LB_to_Phys(PhaseField, ic); FILE *OUTFILE_Z; - sprintf(LocalRankFilename,"IonFluxElectrical_Z_%02zu_Time_%i.%05i.raw",ic+1,timestep,rank); - OUTFILE_Z = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,OUTFILE_Z); + sprintf(LocalRankFilename, "IonFluxElectrical_Z_%02zu_Time_%i.%05i.raw", + ic + 1, timestep, rank); + OUTFILE_Z = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, OUTFILE_Z); fclose(OUTFILE_Z); } } -void ScaLBL_IonModel::IonConcentration_LB_to_Phys(DoubleArray &Den_reg){ - for (int k=0;kBarrier(); - delete [] FluidVelocity_host; + ScaLBL_AllocateDeviceMemory((void **)&FluidVelocityDummy, + sizeof(double) * 3 * Np); + ScaLBL_CopyToDevice(FluidVelocityDummy, FluidVelocity_host, + sizeof(double) * 3 * Np); + ScaLBL_Comm->Barrier(); + delete[] FluidVelocity_host; } -void ScaLBL_IonModel::DummyElectricField(){ +void ScaLBL_IonModel::DummyElectricField() { double *ElectricField_host; - ElectricField_host = new double[3*Np]; + ElectricField_host = new double[3 * Np]; - for (int k=0; kBarrier(); - delete [] ElectricField_host; + ScaLBL_AllocateDeviceMemory((void **)&ElectricFieldDummy, + sizeof(double) * 3 * Np); + ScaLBL_CopyToDevice(ElectricFieldDummy, ElectricField_host, + sizeof(double) * 3 * Np); + ScaLBL_Comm->Barrier(); + delete[] ElectricField_host; } -double ScaLBL_IonModel::CalIonDenConvergence(vector &ci_avg_previous){ +double ScaLBL_IonModel::CalIonDenConvergence(vector &ci_avg_previous) { double *Ci_host; Ci_host = new double[Np]; - vector error(number_ion_species,0.0); + vector error(number_ion_species, 0.0); - for (size_t ic=0; icLastExterior(); idx++){ - ci_loc +=Ci_host[idx]; - count_loc+=1.0; + for (int idx = 0; idx < ScaLBL_Comm->LastExterior(); idx++) { + ci_loc += Ci_host[idx]; + count_loc += 1.0; } - for (int idx=ScaLBL_Comm->FirstInterior(); idxLastInterior(); idx++){ - ci_loc +=Ci_host[idx]; - count_loc+=1.0; + for (int idx = ScaLBL_Comm->FirstInterior(); + idx < ScaLBL_Comm->LastInterior(); idx++) { + ci_loc += Ci_host[idx]; + count_loc += 1.0; } - ci_avg = Mask->Comm.sumReduce( ci_loc); - count = Mask->Comm.sumReduce( count_loc); - ci_avg /= count; - double ci_avg_mag=ci_avg; - if (ci_avg==0.0) ci_avg_mag=1.0; - error[ic] = fabs(ci_avg-ci_avg_previous[ic])/fabs(ci_avg_mag); - ci_avg_previous[ic] = ci_avg; + ci_avg = Mask->Comm.sumReduce(ci_loc); + count = Mask->Comm.sumReduce(count_loc); + ci_avg /= count; + double ci_avg_mag = ci_avg; + if (ci_avg == 0.0) + ci_avg_mag = 1.0; + error[ic] = fabs(ci_avg - ci_avg_previous[ic]) / fabs(ci_avg_mag); + ci_avg_previous[ic] = ci_avg; } double error_max; - error_max = *max_element(error.begin(),error.end()); - if (rank==0){ - printf("IonModel: error max: %.5g\n",error_max); + error_max = *max_element(error.begin(), error.end()); + if (rank == 0) { + printf("IonModel: error max: %.5g\n", error_max); } return error_max; } @@ -1286,4 +1571,3 @@ double ScaLBL_IonModel::CalIonDenConvergence(vector &ci_avg_previous){ // } // //} - diff --git a/models/IonModel.h b/models/IonModel.h index 5f3e390c..8930b1df 100644 --- a/models/IonModel.h +++ b/models/IonModel.h @@ -20,25 +20,28 @@ #include "analysis/Minkowski.h" #include "ProfilerApp.h" -class ScaLBL_IonModel{ +class ScaLBL_IonModel { public: - ScaLBL_IonModel(int RANK, int NP, const Utilities::MPI& COMM); - ~ScaLBL_IonModel(); - - // functions in they should be run - void ReadParams(string filename,vector &num_iter); - void ReadParams(string filename); - void ReadParams(std::shared_ptr db0); - void SetDomain(); - void ReadInput(); - void Create(); - void Initialize(); - void Run(double *Velocity, double *ElectricField); + ScaLBL_IonModel(int RANK, int NP, const Utilities::MPI &COMM); + ~ScaLBL_IonModel(); + + // functions in they should be run + void ReadParams(string filename, vector &num_iter); + void ReadParams(string filename); + void ReadParams(std::shared_ptr db0); + void SetDomain(); + void ReadInput(); + void Create(); + void Initialize(); + void Run(double *Velocity, double *ElectricField); void getIonConcentration(DoubleArray &IonConcentration, const size_t ic); void getIonConcentration_debug(int timestep); - void getIonFluxDiffusive(DoubleArray &IonFlux_x,DoubleArray &IonFlux_y,DoubleArray &IonFlux_z,const size_t ic); - void getIonFluxAdvective(DoubleArray &IonFlux_x,DoubleArray &IonFlux_y,DoubleArray &IonFlux_z,const size_t ic); - void getIonFluxElectrical(DoubleArray &IonFlux_x,DoubleArray &IonFlux_y,DoubleArray &IonFlux_z,const size_t ic); + void getIonFluxDiffusive(DoubleArray &IonFlux_x, DoubleArray &IonFlux_y, + DoubleArray &IonFlux_z, const size_t ic); + void getIonFluxAdvective(DoubleArray &IonFlux_x, DoubleArray &IonFlux_y, + DoubleArray &IonFlux_z, const size_t ic); + void getIonFluxElectrical(DoubleArray &IonFlux_x, DoubleArray &IonFlux_y, + DoubleArray &IonFlux_z, const size_t ic); void getIonFluxDiffusive_debug(int timestep); void getIonFluxAdvective_debug(int timestep); void getIonFluxElectrical_debug(int timestep); @@ -46,35 +49,37 @@ public: void DummyElectricField(); double CalIonDenConvergence(vector &ci_avg_previous); - //bool Restart,pBC; - int timestep; + //bool Restart,pBC; + int timestep; vector timestepMax; - int BoundaryConditionSolid; - double h;//domain resolution, unit [um/lu] - double kb,electron_charge,T,Vt; + int BoundaryConditionSolid; + double h; //domain resolution, unit [um/lu] + double kb, electron_charge, T, Vt; double k2_inv; double tolerance; - double fluidVelx_dummy,fluidVely_dummy,fluidVelz_dummy; - double Ex_dummy,Ey_dummy,Ez_dummy; - - size_t number_ion_species; - vector BoundaryConditionInlet; - vector BoundaryConditionOutlet; - vector IonDiffusivity;//User input unit [m^2/sec] - vector IonValence; - vector IonConcentration;//unit [mol/m^3] - vector Cin;//inlet boundary value, can be either concentration [mol/m^3] or flux [mol/m^2/sec] - vector Cout;//outlet boundary value, can be either concentration [mol/m^3] or flux [mol/m^2/sec] - vector tau; - vector time_conv; - - int Nx,Ny,Nz,N,Np; - int rank,nprocx,nprocy,nprocz,nprocs; - double Lx,Ly,Lz; + double fluidVelx_dummy, fluidVely_dummy, fluidVelz_dummy; + double Ex_dummy, Ey_dummy, Ez_dummy; - std::shared_ptr Dm; // this domain is for analysis - std::shared_ptr Mask; // this domain is for lbm - std::shared_ptr ScaLBL_Comm; + size_t number_ion_species; + vector BoundaryConditionInlet; + vector BoundaryConditionOutlet; + vector IonDiffusivity; //User input unit [m^2/sec] + vector IonValence; + vector IonConcentration; //unit [mol/m^3] + vector + Cin; //inlet boundary value, can be either concentration [mol/m^3] or flux [mol/m^2/sec] + vector + Cout; //outlet boundary value, can be either concentration [mol/m^3] or flux [mol/m^2/sec] + vector tau; + vector time_conv; + + int Nx, Ny, Nz, N, Np; + int rank, nprocx, nprocy, nprocz, nprocs; + double Lx, Ly, Lz; + + std::shared_ptr Dm; // this domain is for analysis + std::shared_ptr Mask; // this domain is for lbm + std::shared_ptr ScaLBL_Comm; // input database std::shared_ptr db; std::shared_ptr domain_db; @@ -84,8 +89,8 @@ public: DoubleArray Distance; int *NeighborList; double *fq; - double *Ci; - double *ChargeDensity; + double *Ci; + double *ChargeDensity; double *IonSolid; double *FluidVelocityDummy; double *ElectricFieldDummy; @@ -94,18 +99,20 @@ public: double *FluxElectrical; private: - Utilities::MPI comm; - - // filenames + Utilities::MPI comm; + + // filenames char LocalRankString[8]; char LocalRankFilename[40]; char LocalRestartFile[40]; char OutputFilename[200]; - + //int rank,nprocs; - void LoadParams(std::shared_ptr db0); + void LoadParams(std::shared_ptr db0); void AssignSolidBoundary(double *ion_solid); - void AssignIonConcentration_FromFile(double *Ci,const vector &File_ion,int ic); + void AssignIonConcentration_FromFile(double *Ci, + const vector &File_ion, + int ic); void IonConcentration_LB_to_Phys(DoubleArray &Den_reg); void IonFlux_LB_to_Phys(DoubleArray &Den_reg, const size_t ic); }; diff --git a/models/MRTModel.cpp b/models/MRTModel.cpp index 19105ce6..d65475fd 100644 --- a/models/MRTModel.cpp +++ b/models/MRTModel.cpp @@ -20,375 +20,408 @@ #include "models/MRTModel.h" #include "analysis/distance.h" #include "common/ReadMicroCT.h" -ScaLBL_MRTModel::ScaLBL_MRTModel(int RANK, int NP, const Utilities::MPI& COMM): -rank(RANK), nprocs(NP), Restart(0),timestep(0),timestepMax(0),tau(0), -Fx(0),Fy(0),Fz(0),flux(0),din(0),dout(0),mu(0), -Nx(0),Ny(0),Nz(0),N(0),Np(0),nprocx(0),nprocy(0),nprocz(0),BoundaryCondition(0),Lx(0),Ly(0),Lz(0),comm(COMM) -{ +ScaLBL_MRTModel::ScaLBL_MRTModel(int RANK, int NP, const Utilities::MPI &COMM) + : rank(RANK), nprocs(NP), Restart(0), timestep(0), timestepMax(0), tau(0), + Fx(0), Fy(0), Fz(0), flux(0), din(0), dout(0), mu(0), Nx(0), Ny(0), Nz(0), + N(0), Np(0), nprocx(0), nprocy(0), nprocz(0), BoundaryCondition(0), Lx(0), + Ly(0), Lz(0), comm(COMM) {} +ScaLBL_MRTModel::~ScaLBL_MRTModel() {} -} -ScaLBL_MRTModel::~ScaLBL_MRTModel(){ +void ScaLBL_MRTModel::ReadParams(string filename) { + // read the input database + db = std::make_shared(filename); + domain_db = db->getDatabase("Domain"); + mrt_db = db->getDatabase("MRT"); + vis_db = db->getDatabase("Visualization"); -} + tau = 1.0; + timestepMax = 100000; + tolerance = 1.0e-8; + Fx = Fy = 0.0; + Fz = 1.0e-5; + dout = 1.0; + din = 1.0; -void ScaLBL_MRTModel::ReadParams(string filename){ - // read the input database - db = std::make_shared( filename ); - domain_db = db->getDatabase( "Domain" ); - mrt_db = db->getDatabase( "MRT" ); - vis_db = db->getDatabase( "Visualization" ); - - tau = 1.0; - timestepMax = 100000; - tolerance = 1.0e-8; - Fx = Fy = 0.0; - Fz = 1.0e-5; - dout = 1.0; - din = 1.0; - - // Color Model parameters - if (mrt_db->keyExists( "timestepMax" )){ - timestepMax = mrt_db->getScalar( "timestepMax" ); - } - if (mrt_db->keyExists( "tolerance" )){ - tolerance = mrt_db->getScalar( "tolerance" ); - } - if (mrt_db->keyExists( "tau" )){ - tau = mrt_db->getScalar( "tau" ); - } - if (mrt_db->keyExists( "F" )){ - Fx = mrt_db->getVector( "F" )[0]; - Fy = mrt_db->getVector( "F" )[1]; - Fz = mrt_db->getVector( "F" )[2]; - } - if (mrt_db->keyExists( "Restart" )){ - Restart = mrt_db->getScalar( "Restart" ); - } - if (mrt_db->keyExists( "din" )){ - din = mrt_db->getScalar( "din" ); - } - if (mrt_db->keyExists( "dout" )){ - dout = mrt_db->getScalar( "dout" ); - } - if (mrt_db->keyExists( "flux" )){ - flux = mrt_db->getScalar( "flux" ); - } - - // Read domain parameters - if (mrt_db->keyExists( "BoundaryCondition" )){ - BoundaryCondition = mrt_db->getScalar( "BC" ); - } - else if (domain_db->keyExists( "BC" )){ - BoundaryCondition = domain_db->getScalar( "BC" ); - } - - mu=(tau-0.5)/3.0; -} -void ScaLBL_MRTModel::SetDomain(){ - Dm = std::shared_ptr(new Domain(domain_db,comm)); // full domain for analysis - Mask = std::shared_ptr(new Domain(domain_db,comm)); // mask domain removes immobile phases - - // domain parameters - Nx = Dm->Nx; - Ny = Dm->Ny; - Nz = Dm->Nz; - Lx = Dm->Lx; - Ly = Dm->Ly; - Lz = Dm->Lz; - - N = Nx*Ny*Nz; - Distance.resize(Nx,Ny,Nz); - Velocity_x.resize(Nx,Ny,Nz); - Velocity_y.resize(Nx,Ny,Nz); - Velocity_z.resize(Nx,Ny,Nz); - - for (int i=0; iid[i] = 1; // initialize this way - //Averages = std::shared_ptr ( new TwoPhase(Dm) ); // TwoPhase analysis object - comm.barrier(); - Dm->CommInit(); - comm.barrier(); - - rank = Dm->rank(); - nprocx = Dm->nprocx(); - nprocy = Dm->nprocy(); - nprocz = Dm->nprocz(); -} - -void ScaLBL_MRTModel::ReadInput(){ - - sprintf(LocalRankString,"%05d",Dm->rank()); - sprintf(LocalRankFilename,"%s%s","ID.",LocalRankString); - sprintf(LocalRestartFile,"%s%s","Restart.",LocalRankString); - - - if (domain_db->keyExists( "Filename" )){ - auto Filename = domain_db->getScalar( "Filename" ); - Mask->Decomp(Filename); + // Color Model parameters + if (mrt_db->keyExists("timestepMax")) { + timestepMax = mrt_db->getScalar("timestepMax"); } - else if (domain_db->keyExists( "GridFile" )){ - // Read the local domain data - auto input_id = readMicroCT( *domain_db, comm ); - // Fill the halo (assuming GCW of 1) - array size0 = { (int) input_id.size(0), (int) input_id.size(1), (int) input_id.size(2) }; - ArraySize size1 = { (size_t) Mask->Nx, (size_t) Mask->Ny, (size_t) Mask->Nz }; - ASSERT( (int) size1[0] == size0[0]+2 && (int) size1[1] == size0[1]+2 && (int) size1[2] == size0[2]+2 ); - fillHalo fill( comm, Mask->rank_info, size0, { 1, 1, 1 }, 0, 1 ); - Array id_view; - id_view.viewRaw( size1, Mask->id.data() ); - fill.copy( input_id, id_view ); - fill.fill( id_view ); + if (mrt_db->keyExists("tolerance")) { + tolerance = mrt_db->getScalar("tolerance"); } - else{ - Mask->ReadIDs(); + if (mrt_db->keyExists("tau")) { + tau = mrt_db->getScalar("tau"); + } + if (mrt_db->keyExists("F")) { + Fx = mrt_db->getVector("F")[0]; + Fy = mrt_db->getVector("F")[1]; + Fz = mrt_db->getVector("F")[2]; + } + if (mrt_db->keyExists("Restart")) { + Restart = mrt_db->getScalar("Restart"); + } + if (mrt_db->keyExists("din")) { + din = mrt_db->getScalar("din"); + } + if (mrt_db->keyExists("dout")) { + dout = mrt_db->getScalar("dout"); + } + if (mrt_db->keyExists("flux")) { + flux = mrt_db->getScalar("flux"); + } + + // Read domain parameters + if (mrt_db->keyExists("BoundaryCondition")) { + BoundaryCondition = mrt_db->getScalar("BC"); + } else if (domain_db->keyExists("BC")) { + BoundaryCondition = domain_db->getScalar("BC"); + } + + mu = (tau - 0.5) / 3.0; +} +void ScaLBL_MRTModel::SetDomain() { + Dm = std::shared_ptr( + new Domain(domain_db, comm)); // full domain for analysis + Mask = std::shared_ptr( + new Domain(domain_db, comm)); // mask domain removes immobile phases + + // domain parameters + Nx = Dm->Nx; + Ny = Dm->Ny; + Nz = Dm->Nz; + Lx = Dm->Lx; + Ly = Dm->Ly; + Lz = Dm->Lz; + + N = Nx * Ny * Nz; + Distance.resize(Nx, Ny, Nz); + Velocity_x.resize(Nx, Ny, Nz); + Velocity_y.resize(Nx, Ny, Nz); + Velocity_z.resize(Nx, Ny, Nz); + + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = 1; // initialize this way + //Averages = std::shared_ptr ( new TwoPhase(Dm) ); // TwoPhase analysis object + comm.barrier(); + Dm->CommInit(); + comm.barrier(); + + rank = Dm->rank(); + nprocx = Dm->nprocx(); + nprocy = Dm->nprocy(); + nprocz = Dm->nprocz(); +} + +void ScaLBL_MRTModel::ReadInput() { + + sprintf(LocalRankString, "%05d", Dm->rank()); + sprintf(LocalRankFilename, "%s%s", "ID.", LocalRankString); + sprintf(LocalRestartFile, "%s%s", "Restart.", LocalRankString); + + if (domain_db->keyExists("Filename")) { + auto Filename = domain_db->getScalar("Filename"); + Mask->Decomp(Filename); + } else if (domain_db->keyExists("GridFile")) { + // Read the local domain data + auto input_id = readMicroCT(*domain_db, comm); + // Fill the halo (assuming GCW of 1) + array size0 = {(int)input_id.size(0), (int)input_id.size(1), + (int)input_id.size(2)}; + ArraySize size1 = {(size_t)Mask->Nx, (size_t)Mask->Ny, + (size_t)Mask->Nz}; + ASSERT((int)size1[0] == size0[0] + 2 && (int)size1[1] == size0[1] + 2 && + (int)size1[2] == size0[2] + 2); + fillHalo fill(comm, Mask->rank_info, size0, {1, 1, 1}, 0, + 1); + Array id_view; + id_view.viewRaw(size1, Mask->id.data()); + fill.copy(input_id, id_view); + fill.fill(id_view); + } else { + Mask->ReadIDs(); } // Generate the signed distance map - // Initialize the domain and communication - Array id_solid(Nx,Ny,Nz); - // Solve for the position of the solid phase - for (int k=0;kid[n] > 0) id_solid(i,j,k) = 1; - else id_solid(i,j,k) = 0; - } - } - } - // Initialize the signed distance function - for (int k=0;kSDs); - if (rank==0) printf("Initialized solid phase -- Converting to Signed Distance function \n"); - CalcDist(Distance,id_solid,*Dm); - if (rank == 0) cout << "Domain set." << endl; + // Initialize the domain and communication + Array id_solid(Nx, Ny, Nz); + // Solve for the position of the solid phase + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + // Initialize the solid phase + if (Mask->id[n] > 0) + id_solid(i, j, k) = 1; + else + id_solid(i, j, k) = 0; + } + } + } + // Initialize the signed distance function + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + // Initialize distance to +/- 1 + Distance(i, j, k) = 2.0 * double(id_solid(i, j, k)) - 1.0; + } + } + } + // MeanFilter(Averages->SDs); + if (rank == 0) + printf("Initialized solid phase -- Converting to Signed Distance " + "function \n"); + CalcDist(Distance, id_solid, *Dm); + if (rank == 0) + cout << "Domain set." << endl; } -void ScaLBL_MRTModel::Create(){ - /* +void ScaLBL_MRTModel::Create() { + /* * This function creates the variables needed to run a LBM */ - int rank=Mask->rank(); - //......................................................... - // Initialize communication structures in averaging domain - for (int i=0; iid[i] = Mask->id[i]; - Mask->CommInit(); - Np=Mask->PoreCount(); - //........................................................................... - if (rank==0) printf ("Create ScaLBL_Communicator \n"); - // Create a communicator for the device (will use optimized layout) - // ScaLBL_Communicator ScaLBL_Comm(Mask); // original - ScaLBL_Comm = std::shared_ptr(new ScaLBL_Communicator(Mask)); + int rank = Mask->rank(); + //......................................................... + // Initialize communication structures in averaging domain + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = Mask->id[i]; + Mask->CommInit(); + Np = Mask->PoreCount(); + //........................................................................... + if (rank == 0) + printf("Create ScaLBL_Communicator \n"); + // Create a communicator for the device (will use optimized layout) + // ScaLBL_Communicator ScaLBL_Comm(Mask); // original + ScaLBL_Comm = + std::shared_ptr(new ScaLBL_Communicator(Mask)); - int Npad=(Np/16 + 2)*16; - if (rank==0) printf ("Set up memory efficient layout \n"); - Map.resize(Nx,Ny,Nz); Map.fill(-2); - auto neighborList= new int[18*Npad]; - Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map,neighborList,Mask->id.data(),Np,1); - comm.barrier(); + int Npad = (Np / 16 + 2) * 16; + if (rank == 0) + printf("Set up memory efficient layout \n"); + Map.resize(Nx, Ny, Nz); + Map.fill(-2); + auto neighborList = new int[18 * Npad]; + Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map, neighborList, + Mask->id.data(), Np, 1); + comm.barrier(); - //........................................................................... - // MAIN VARIABLES ALLOCATED HERE - //........................................................................... - // LBM variables - if (rank==0) printf ("Allocating distributions \n"); - //......................device distributions................................. - int dist_mem_size = Np*sizeof(double); - int neighborSize=18*(Np*sizeof(int)); - //........................................................................... - ScaLBL_AllocateDeviceMemory((void **) &NeighborList, neighborSize); - ScaLBL_AllocateDeviceMemory((void **) &fq, 19*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Pressure, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &Velocity, 3*sizeof(double)*Np); - //........................................................................... - // Update GPU data structures - if (rank==0) printf ("Setting up device map and neighbor list \n"); - // copy the neighbor list - ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); - comm.barrier(); - double MLUPS = ScaLBL_Comm->GetPerformance(NeighborList,fq,Np); - printf(" MLPUS=%f from rank %i\n",MLUPS,rank); -} + //........................................................................... + // MAIN VARIABLES ALLOCATED HERE + //........................................................................... + // LBM variables + if (rank == 0) + printf("Allocating distributions \n"); + //......................device distributions................................. + int dist_mem_size = Np * sizeof(double); + int neighborSize = 18 * (Np * sizeof(int)); + //........................................................................... + ScaLBL_AllocateDeviceMemory((void **)&NeighborList, neighborSize); + ScaLBL_AllocateDeviceMemory((void **)&fq, 19 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Pressure, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&Velocity, 3 * sizeof(double) * Np); + //........................................................................... + // Update GPU data structures + if (rank == 0) + printf("Setting up device map and neighbor list \n"); + // copy the neighbor list + ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); + comm.barrier(); + double MLUPS = ScaLBL_Comm->GetPerformance(NeighborList, fq, Np); + printf(" MLPUS=%f from rank %i\n", MLUPS, rank); +} -void ScaLBL_MRTModel::Initialize(){ - /* +void ScaLBL_MRTModel::Initialize() { + /* * This function initializes model */ - if (rank==0) printf ("Initializing distributions \n"); + if (rank == 0) + printf("Initializing distributions \n"); ScaLBL_D3Q19_Init(fq, Np); } -void ScaLBL_MRTModel::Run(){ - double rlx_setA=1.0/tau; - double rlx_setB = 8.f*(2.f-rlx_setA)/(8.f-rlx_setA); - - Minkowski Morphology(Mask); +void ScaLBL_MRTModel::Run() { + double rlx_setA = 1.0 / tau; + double rlx_setB = 8.f * (2.f - rlx_setA) / (8.f - rlx_setA); - if (rank==0){ - bool WriteHeader=false; - FILE *log_file = fopen("Permeability.csv","r"); - if (log_file != NULL) - fclose(log_file); - else - WriteHeader=true; + Minkowski Morphology(Mask); - if (WriteHeader){ - log_file = fopen("Permeability.csv","a+"); - fprintf(log_file,"time Fx Fy Fz mu Vs As Js Xs vx vy vz k\n"); - fclose(log_file); - } - } + if (rank == 0) { + bool WriteHeader = false; + FILE *log_file = fopen("Permeability.csv", "r"); + if (log_file != NULL) + fclose(log_file); + else + WriteHeader = true; - //.......create and start timer............ - ScaLBL_DeviceBarrier(); comm.barrier(); - if (rank==0) printf("Beginning AA timesteps, timestepMax = %i \n", timestepMax); - if (rank==0) printf("********************************************************\n"); - timestep=0; - double error = 1.0; - double flow_rate_previous = 0.0; + if (WriteHeader) { + log_file = fopen("Permeability.csv", "a+"); + fprintf(log_file, "time Fx Fy Fz mu Vs As Js Xs vx vy vz k\n"); + fclose(log_file); + } + } + + //.......create and start timer............ + ScaLBL_DeviceBarrier(); + comm.barrier(); + if (rank == 0) + printf("Beginning AA timesteps, timestepMax = %i \n", timestepMax); + if (rank == 0) + printf("********************************************************\n"); + timestep = 0; + double error = 1.0; + double flow_rate_previous = 0.0; auto t1 = std::chrono::system_clock::now(); - while (timestep < timestepMax && error > tolerance) { - //************************************************************************/ - timestep++; - ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL - ScaLBL_D3Q19_AAodd_MRT(NeighborList, fq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np, rlx_setA, rlx_setB, Fx, Fy, Fz); - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - // Set boundary conditions - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); - } - ScaLBL_D3Q19_AAodd_MRT(NeighborList, fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx_setA, rlx_setB, Fx, Fy, Fz); - ScaLBL_DeviceBarrier(); comm.barrier(); - timestep++; - ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL - ScaLBL_D3Q19_AAeven_MRT(fq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np, rlx_setA, rlx_setB, Fx, Fy, Fz); - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - // Set boundary conditions - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); - } - ScaLBL_D3Q19_AAeven_MRT(fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx_setA, rlx_setB, Fx, Fy, Fz); - ScaLBL_DeviceBarrier(); comm.barrier(); - //************************************************************************/ - - if (timestep%1000==0){ - ScaLBL_D3Q19_Momentum(fq,Velocity, Np); - ScaLBL_DeviceBarrier(); comm.barrier(); - ScaLBL_Comm->RegularLayout(Map,&Velocity[0],Velocity_x); - ScaLBL_Comm->RegularLayout(Map,&Velocity[Np],Velocity_y); - ScaLBL_Comm->RegularLayout(Map,&Velocity[2*Np],Velocity_z); - - double count_loc=0; - double count; - double vax,vay,vaz; - double vax_loc,vay_loc,vaz_loc; - vax_loc = vay_loc = vaz_loc = 0.f; - for (int k=1; k 0){ - vax_loc += Velocity_x(i,j,k); - vay_loc += Velocity_y(i,j,k); - vaz_loc += Velocity_z(i,j,k); - count_loc+=1.0; - } - } - } - } - vax=Dm->Comm.sumReduce( vax_loc); - vay=Dm->Comm.sumReduce( vay_loc); - vaz=Dm->Comm.sumReduce( vaz_loc); - count=Dm->Comm.sumReduce( count_loc); - - vax /= count; - vay /= count; - vaz /= count; - - double force_mag = sqrt(Fx*Fx+Fy*Fy+Fz*Fz); - double dir_x = Fx/force_mag; - double dir_y = Fy/force_mag; - double dir_z = Fz/force_mag; - if (force_mag == 0.0){ - // default to z direction - dir_x = 0.0; - dir_y = 0.0; - dir_z = 1.0; - force_mag = 1.0; - } - double flow_rate = (vax*dir_x + vay*dir_y + vaz*dir_z); - - error = fabs(flow_rate - flow_rate_previous) / fabs(flow_rate); - flow_rate_previous = flow_rate; - - //if (rank==0) printf("Computing Minkowski functionals \n"); - Morphology.ComputeScalar(Distance,0.f); - //Morphology.PrintAll(); - double mu = (tau-0.5)/3.f; - double Vs = Morphology.V(); - double As = Morphology.A(); - double Hs = Morphology.H(); - double Xs = Morphology.X(); - Vs=Dm->Comm.sumReduce( Vs); - As=Dm->Comm.sumReduce( As); - Hs=Dm->Comm.sumReduce( Hs); - Xs=Dm->Comm.sumReduce( Xs); + while (timestep < timestepMax && error > tolerance) { + //************************************************************************/ + timestep++; + ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL + ScaLBL_D3Q19_AAodd_MRT(NeighborList, fq, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np, rlx_setA, + rlx_setB, Fx, Fy, Fz); + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + // Set boundary conditions + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 4) { + din = + ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); + } + ScaLBL_D3Q19_AAodd_MRT(NeighborList, fq, 0, ScaLBL_Comm->LastExterior(), + Np, rlx_setA, rlx_setB, Fx, Fy, Fz); + ScaLBL_DeviceBarrier(); + comm.barrier(); + timestep++; + ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL + ScaLBL_D3Q19_AAeven_MRT(fq, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np, rlx_setA, + rlx_setB, Fx, Fy, Fz); + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + // Set boundary conditions + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 4) { + din = + ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); + } + ScaLBL_D3Q19_AAeven_MRT(fq, 0, ScaLBL_Comm->LastExterior(), Np, + rlx_setA, rlx_setB, Fx, Fy, Fz); + ScaLBL_DeviceBarrier(); + comm.barrier(); + //************************************************************************/ - double h = Dm->voxel_length; - double absperm = h*h*mu*Mask->Porosity()*flow_rate / force_mag; - if (rank==0) { - printf(" %f\n",absperm); - FILE * log_file = fopen("Permeability.csv","a"); - fprintf(log_file,"%i %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g\n",timestep, Fx, Fy, Fz, mu, - h*h*h*Vs,h*h*As,h*Hs,Xs,vax,vay,vaz, absperm); - fclose(log_file); - } - } - } - //************************************************************************/ - if (rank==0) printf("-------------------------------------------------------------------\n"); - // Compute the walltime per timestep + if (timestep % 1000 == 0) { + ScaLBL_D3Q19_Momentum(fq, Velocity, Np); + ScaLBL_DeviceBarrier(); + comm.barrier(); + ScaLBL_Comm->RegularLayout(Map, &Velocity[0], Velocity_x); + ScaLBL_Comm->RegularLayout(Map, &Velocity[Np], Velocity_y); + ScaLBL_Comm->RegularLayout(Map, &Velocity[2 * Np], Velocity_z); + + double count_loc = 0; + double count; + double vax, vay, vaz; + double vax_loc, vay_loc, vaz_loc; + vax_loc = vay_loc = vaz_loc = 0.f; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + if (Distance(i, j, k) > 0) { + vax_loc += Velocity_x(i, j, k); + vay_loc += Velocity_y(i, j, k); + vaz_loc += Velocity_z(i, j, k); + count_loc += 1.0; + } + } + } + } + vax = Dm->Comm.sumReduce(vax_loc); + vay = Dm->Comm.sumReduce(vay_loc); + vaz = Dm->Comm.sumReduce(vaz_loc); + count = Dm->Comm.sumReduce(count_loc); + + vax /= count; + vay /= count; + vaz /= count; + + double force_mag = sqrt(Fx * Fx + Fy * Fy + Fz * Fz); + double dir_x = Fx / force_mag; + double dir_y = Fy / force_mag; + double dir_z = Fz / force_mag; + if (force_mag == 0.0) { + // default to z direction + dir_x = 0.0; + dir_y = 0.0; + dir_z = 1.0; + force_mag = 1.0; + } + double flow_rate = (vax * dir_x + vay * dir_y + vaz * dir_z); + + error = fabs(flow_rate - flow_rate_previous) / fabs(flow_rate); + flow_rate_previous = flow_rate; + + //if (rank==0) printf("Computing Minkowski functionals \n"); + Morphology.ComputeScalar(Distance, 0.f); + //Morphology.PrintAll(); + double mu = (tau - 0.5) / 3.f; + double Vs = Morphology.V(); + double As = Morphology.A(); + double Hs = Morphology.H(); + double Xs = Morphology.X(); + Vs = Dm->Comm.sumReduce(Vs); + As = Dm->Comm.sumReduce(As); + Hs = Dm->Comm.sumReduce(Hs); + Xs = Dm->Comm.sumReduce(Xs); + + double h = Dm->voxel_length; + double absperm = + h * h * mu * Mask->Porosity() * flow_rate / force_mag; + if (rank == 0) { + printf(" %f\n", absperm); + FILE *log_file = fopen("Permeability.csv", "a"); + fprintf(log_file, + "%i %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g " + "%.8g %.8g\n", + timestep, Fx, Fy, Fz, mu, h * h * h * Vs, h * h * As, + h * Hs, Xs, vax, vay, vaz, absperm); + fclose(log_file); + } + } + } + //************************************************************************/ + if (rank == 0) + printf("---------------------------------------------------------------" + "----\n"); + // Compute the walltime per timestep auto t2 = std::chrono::system_clock::now(); - double cputime = std::chrono::duration( t2 - t1 ).count() / timestep; - // Performance obtained from each node - double MLUPS = double(Np)/cputime/1000000; - - if (rank==0) printf("********************************************************\n"); - if (rank==0) printf("CPU time = %f \n", cputime); - if (rank==0) printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); - MLUPS *= nprocs; - if (rank==0) printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); - if (rank==0) printf("********************************************************\n"); + double cputime = std::chrono::duration(t2 - t1).count() / timestep; + // Performance obtained from each node + double MLUPS = double(Np) / cputime / 1000000; + if (rank == 0) + printf("********************************************************\n"); + if (rank == 0) + printf("CPU time = %f \n", cputime); + if (rank == 0) + printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); + MLUPS *= nprocs; + if (rank == 0) + printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); + if (rank == 0) + printf("********************************************************\n"); } -void ScaLBL_MRTModel::VelocityField(){ +void ScaLBL_MRTModel::VelocityField() { - auto format = vis_db->getWithDefault( "format", "silo" ); + auto format = vis_db->getWithDefault("format", "silo"); /* memcpy(Morphology.SDn.data(), Distance.data(), Nx*Ny*Nz*sizeof(double)); Morphology.Initialize(); @@ -428,59 +461,63 @@ void ScaLBL_MRTModel::VelocityField(){ if (rank==0) printf("%.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g\n",Fx, Fy, Fz, mu, Morphology.V(),Morphology.A(),Morphology.J(),Morphology.X(),vax,vay,vaz); */ - vis_db = db->getDatabase( "Visualization" ); - if (vis_db->getWithDefault( "write_silo", false )){ - - std::vector visData; - fillHalo fillData(Dm->Comm,Dm->rank_info,{Dm->Nx-2,Dm->Ny-2,Dm->Nz-2},{1,1,1},0,1); + vis_db = db->getDatabase("Visualization"); + if (vis_db->getWithDefault("write_silo", false)) { - auto VxVar = std::make_shared(); - auto VyVar = std::make_shared(); - auto VzVar = std::make_shared(); - auto SignDistVar = std::make_shared(); + std::vector visData; + fillHalo fillData(Dm->Comm, Dm->rank_info, + {Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2}, + {1, 1, 1}, 0, 1); - IO::initialize("",format,"false"); - // Create the MeshDataStruct - visData.resize(1); - visData[0].meshName = "domain"; - visData[0].mesh = std::make_shared( Dm->rank_info,Dm->Nx-2,Dm->Ny-2,Dm->Nz-2,Dm->Lx,Dm->Ly,Dm->Lz ); - SignDistVar->name = "SignDist"; - SignDistVar->type = IO::VariableType::VolumeVariable; - SignDistVar->dim = 1; - SignDistVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(SignDistVar); - - VxVar->name = "Velocity_x"; - VxVar->type = IO::VariableType::VolumeVariable; - VxVar->dim = 1; - VxVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(VxVar); - VyVar->name = "Velocity_y"; - VyVar->type = IO::VariableType::VolumeVariable; - VyVar->dim = 1; - VyVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(VyVar); - VzVar->name = "Velocity_z"; - VzVar->type = IO::VariableType::VolumeVariable; - VzVar->dim = 1; - VzVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(VzVar); - - Array& SignData = visData[0].vars[0]->data; - Array& VelxData = visData[0].vars[1]->data; - Array& VelyData = visData[0].vars[2]->data; - Array& VelzData = visData[0].vars[3]->data; - - ASSERT(visData[0].vars[0]->name=="SignDist"); - ASSERT(visData[0].vars[1]->name=="Velocity_x"); - ASSERT(visData[0].vars[2]->name=="Velocity_y"); - ASSERT(visData[0].vars[3]->name=="Velocity_z"); - - fillData.copy(Distance,SignData); - fillData.copy(Velocity_x,VelxData); - fillData.copy(Velocity_y,VelyData); - fillData.copy(Velocity_z,VelzData); - - IO::writeData( timestep, visData, Dm->Comm ); + auto VxVar = std::make_shared(); + auto VyVar = std::make_shared(); + auto VzVar = std::make_shared(); + auto SignDistVar = std::make_shared(); + + IO::initialize("", format, "false"); + // Create the MeshDataStruct + visData.resize(1); + visData[0].meshName = "domain"; + visData[0].mesh = std::make_shared( + Dm->rank_info, Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2, Dm->Lx, Dm->Ly, + Dm->Lz); + SignDistVar->name = "SignDist"; + SignDistVar->type = IO::VariableType::VolumeVariable; + SignDistVar->dim = 1; + SignDistVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); + visData[0].vars.push_back(SignDistVar); + + VxVar->name = "Velocity_x"; + VxVar->type = IO::VariableType::VolumeVariable; + VxVar->dim = 1; + VxVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); + visData[0].vars.push_back(VxVar); + VyVar->name = "Velocity_y"; + VyVar->type = IO::VariableType::VolumeVariable; + VyVar->dim = 1; + VyVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); + visData[0].vars.push_back(VyVar); + VzVar->name = "Velocity_z"; + VzVar->type = IO::VariableType::VolumeVariable; + VzVar->dim = 1; + VzVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); + visData[0].vars.push_back(VzVar); + + Array &SignData = visData[0].vars[0]->data; + Array &VelxData = visData[0].vars[1]->data; + Array &VelyData = visData[0].vars[2]->data; + Array &VelzData = visData[0].vars[3]->data; + + ASSERT(visData[0].vars[0]->name == "SignDist"); + ASSERT(visData[0].vars[1]->name == "Velocity_x"); + ASSERT(visData[0].vars[2]->name == "Velocity_y"); + ASSERT(visData[0].vars[3]->name == "Velocity_z"); + + fillData.copy(Distance, SignData); + fillData.copy(Velocity_x, VelxData); + fillData.copy(Velocity_y, VelyData); + fillData.copy(Velocity_z, VelzData); + + IO::writeData(timestep, visData, Dm->Comm); } } diff --git a/models/MRTModel.h b/models/MRTModel.h index 722ff027..f1489aa8 100644 --- a/models/MRTModel.h +++ b/models/MRTModel.h @@ -31,36 +31,36 @@ #include "analysis/Minkowski.h" #include "ProfilerApp.h" -class ScaLBL_MRTModel{ +class ScaLBL_MRTModel { public: - ScaLBL_MRTModel(int RANK, int NP, const Utilities::MPI& COMM); - ~ScaLBL_MRTModel(); - - // functions in they should be run - void ReadParams(string filename); - void ReadParams(std::shared_ptr db0); - void SetDomain(); - void ReadInput(); - void Create(); - void Initialize(); - void Run(); - void VelocityField(); - - bool Restart,pBC; - int timestep,timestepMax; - int BoundaryCondition; - double tau,mu; - double Fx,Fy,Fz,flux; - double din,dout; - double tolerance; - - int Nx,Ny,Nz,N,Np; - int rank,nprocx,nprocy,nprocz,nprocs; - double Lx,Ly,Lz; + ScaLBL_MRTModel(int RANK, int NP, const Utilities::MPI &COMM); + ~ScaLBL_MRTModel(); - std::shared_ptr Dm; // this domain is for analysis - std::shared_ptr Mask; // this domain is for lbm - std::shared_ptr ScaLBL_Comm; + // functions in they should be run + void ReadParams(string filename); + void ReadParams(std::shared_ptr db0); + void SetDomain(); + void ReadInput(); + void Create(); + void Initialize(); + void Run(); + void VelocityField(); + + bool Restart, pBC; + int timestep, timestepMax; + int BoundaryCondition; + double tau, mu; + double Fx, Fy, Fz, flux; + double din, dout; + double tolerance; + + int Nx, Ny, Nz, N, Np; + int rank, nprocx, nprocy, nprocz, nprocs; + double Lx, Ly, Lz; + + std::shared_ptr Dm; // this domain is for analysis + std::shared_ptr Mask; // this domain is for lbm + std::shared_ptr ScaLBL_Comm; // input database std::shared_ptr db; std::shared_ptr domain_db; @@ -73,20 +73,21 @@ public: double *fq; double *Velocity; double *Pressure; - + //Minkowski Morphology; - + DoubleArray Velocity_x; DoubleArray Velocity_y; DoubleArray Velocity_z; + private: Utilities::MPI comm; - - // filenames + + // filenames char LocalRankString[8]; char LocalRankFilename[40]; char LocalRestartFile[40]; - + //int rank,nprocs; - void LoadParams(std::shared_ptr db0); + void LoadParams(std::shared_ptr db0); }; diff --git a/models/MultiPhysController.cpp b/models/MultiPhysController.cpp index b815383e..974de530 100644 --- a/models/MultiPhysController.cpp +++ b/models/MultiPhysController.cpp @@ -1,51 +1,48 @@ #include "models/MultiPhysController.h" -ScaLBL_Multiphys_Controller::ScaLBL_Multiphys_Controller(int RANK, int NP, const Utilities::MPI& COMM): -rank(RANK),nprocs(NP),Restart(0),timestepMax(0),num_iter_Stokes(0),num_iter_Ion(0), -analysis_interval(0),visualization_interval(0),tolerance(0),time_conv_max(0),comm(COMM) -{ +ScaLBL_Multiphys_Controller::ScaLBL_Multiphys_Controller( + int RANK, int NP, const Utilities::MPI &COMM) + : rank(RANK), nprocs(NP), Restart(0), timestepMax(0), num_iter_Stokes(0), + num_iter_Ion(0), analysis_interval(0), visualization_interval(0), + tolerance(0), time_conv_max(0), comm(COMM) {} +ScaLBL_Multiphys_Controller::~ScaLBL_Multiphys_Controller() {} -} -ScaLBL_Multiphys_Controller::~ScaLBL_Multiphys_Controller(){ +void ScaLBL_Multiphys_Controller::ReadParams(string filename) { -} - -void ScaLBL_Multiphys_Controller::ReadParams(string filename){ - - // read the input database - db = std::make_shared( filename ); - study_db = db->getDatabase( "MultiphysController" ); - + // read the input database + db = std::make_shared(filename); + study_db = db->getDatabase("MultiphysController"); // Default parameters timestepMax = 10000; Restart = false; - num_iter_Stokes=1; + num_iter_Stokes = 1; num_iter_Ion.push_back(1); analysis_interval = 500; visualization_interval = 10000; tolerance = 1.0e-6; time_conv_max = 0.0; - + // load input parameters - if (study_db->keyExists( "timestepMax" )){ - timestepMax = study_db->getScalar( "timestepMax" ); - } - if (study_db->keyExists( "analysis_interval" )){ - analysis_interval = study_db->getScalar( "analysis_interval" ); - } - if (study_db->keyExists( "visualization_interval" )){ - visualization_interval = study_db->getScalar( "visualization_interval" ); - } - if (study_db->keyExists( "tolerance" )){ - tolerance = study_db->getScalar( "tolerance" ); - } - //if (study_db->keyExists( "time_conv" )){ - // time_conv = study_db->getScalar( "time_conv" ); - //} - //if (study_db->keyExists( "Schmidt_Number" )){ - // SchmidtNum = study_db->getScalar( "Schmidt_Number" ); - //} + if (study_db->keyExists("timestepMax")) { + timestepMax = study_db->getScalar("timestepMax"); + } + if (study_db->keyExists("analysis_interval")) { + analysis_interval = study_db->getScalar("analysis_interval"); + } + if (study_db->keyExists("visualization_interval")) { + visualization_interval = + study_db->getScalar("visualization_interval"); + } + if (study_db->keyExists("tolerance")) { + tolerance = study_db->getScalar("tolerance"); + } + //if (study_db->keyExists( "time_conv" )){ + // time_conv = study_db->getScalar( "time_conv" ); + //} + //if (study_db->keyExists( "Schmidt_Number" )){ + // SchmidtNum = study_db->getScalar( "Schmidt_Number" ); + //} // recalculate relevant parameters //if (SchmidtNum>1){ @@ -61,87 +58,104 @@ void ScaLBL_Multiphys_Controller::ReadParams(string filename){ // num_iter_Ion = 1; //} //else{ - // ERROR("Error: SchmidtNum (Schmidt number) must be a positive number! \n"); + // ERROR("Error: SchmidtNum (Schmidt number) must be a positive number! \n"); //} // load input parameters // in case user wants to have an absolute control over the iternal iteration - if (study_db->keyExists( "num_iter_Ion_List" )){ + if (study_db->keyExists("num_iter_Ion_List")) { num_iter_Ion.clear(); - num_iter_Ion = study_db->getVector( "num_iter_Ion_List" ); + num_iter_Ion = study_db->getVector("num_iter_Ion_List"); } - if (study_db->keyExists( "num_iter_Stokes" )){ - num_iter_Stokes = study_db->getScalar( "num_iter_Stokes" ); + if (study_db->keyExists("num_iter_Stokes")) { + num_iter_Stokes = study_db->getScalar("num_iter_Stokes"); } - } -int ScaLBL_Multiphys_Controller::getStokesNumIter_PNP_coupling(double StokesTimeConv,const vector &IonTimeConv){ +int ScaLBL_Multiphys_Controller::getStokesNumIter_PNP_coupling( + double StokesTimeConv, const vector &IonTimeConv) { //Return number of internal iterations for the Stokes solver int num_iter_stokes; vector TimeConv; - TimeConv.assign(IonTimeConv.begin(),IonTimeConv.end()); - TimeConv.insert(TimeConv.begin(),StokesTimeConv); - vector::iterator it_max = max_element(TimeConv.begin(),TimeConv.end()); - int idx_max = distance(TimeConv.begin(),it_max); - if (idx_max==0){ + TimeConv.assign(IonTimeConv.begin(), IonTimeConv.end()); + TimeConv.insert(TimeConv.begin(), StokesTimeConv); + vector::iterator it_max = + max_element(TimeConv.begin(), TimeConv.end()); + int idx_max = distance(TimeConv.begin(), it_max); + if (idx_max == 0) { num_iter_stokes = 2; - } - else{ - double temp = 2*TimeConv[idx_max]/StokesTimeConv;//the factor 2 is the number of iterations for the element has max time_conv - num_iter_stokes = int(round(temp/2)*2); + } else { + double temp = + 2 * TimeConv[idx_max] / + StokesTimeConv; //the factor 2 is the number of iterations for the element has max time_conv + num_iter_stokes = int(round(temp / 2) * 2); } return num_iter_stokes; } -vector ScaLBL_Multiphys_Controller::getIonNumIter_PNP_coupling(double StokesTimeConv,const vector &IonTimeConv){ +vector ScaLBL_Multiphys_Controller::getIonNumIter_PNP_coupling( + double StokesTimeConv, const vector &IonTimeConv) { //Return number of internal iterations for the Ion transport solver vector num_iter_ion; vector TimeConv; - TimeConv.assign(IonTimeConv.begin(),IonTimeConv.end()); - TimeConv.insert(TimeConv.begin(),StokesTimeConv); - vector::iterator it_max = max_element(TimeConv.begin(),TimeConv.end()); - unsigned int idx_max = distance(TimeConv.begin(),it_max); - if (idx_max==0){ - for (unsigned int idx=1;idx::iterator it_max = + max_element(TimeConv.begin(), TimeConv.end()); + unsigned int idx_max = distance(TimeConv.begin(), it_max); + if (idx_max == 0) { + for (unsigned int idx = 1; idx < TimeConv.size(); idx++) { + double temp = + 2 * StokesTimeConv / + TimeConv + [idx]; //the factor 2 is the number of iterations for the element has max time_conv + num_iter_ion.push_back(int(round(temp / 2) * 2)); } - } - else if (idx_max==1){ + } else if (idx_max == 1) { num_iter_ion.push_back(2); - for (unsigned int idx=2;idx &IonTimeConv){ +void ScaLBL_Multiphys_Controller::getTimeConvMax_PNP_coupling( + double StokesTimeConv, const vector &IonTimeConv) { //Return maximum of the time converting factor from Stokes and ion solvers vector TimeConv; - TimeConv.assign(IonTimeConv.begin(),IonTimeConv.end()); - TimeConv.insert(TimeConv.begin(),StokesTimeConv); - time_conv_max = *max_element(TimeConv.begin(),TimeConv.end()); + TimeConv.assign(IonTimeConv.begin(), IonTimeConv.end()); + TimeConv.insert(TimeConv.begin(), StokesTimeConv); + time_conv_max = *max_element(TimeConv.begin(), TimeConv.end()); } diff --git a/models/MultiPhysController.h b/models/MultiPhysController.h index a9ea7a6b..9ce31d10 100644 --- a/models/MultiPhysController.h +++ b/models/MultiPhysController.h @@ -17,19 +17,22 @@ #include "analysis/Minkowski.h" #include "ProfilerApp.h" -class ScaLBL_Multiphys_Controller{ +class ScaLBL_Multiphys_Controller { public: - ScaLBL_Multiphys_Controller(int RANK, int NP, const Utilities::MPI& COMM); - ~ScaLBL_Multiphys_Controller(); - - void ReadParams(string filename); - void ReadParams(std::shared_ptr db0); - int getStokesNumIter_PNP_coupling(double StokesTimeConv,const vector &IonTimeConv); - vector getIonNumIter_PNP_coupling(double StokesTimeConv,const vector &IonTimeConv); + ScaLBL_Multiphys_Controller(int RANK, int NP, const Utilities::MPI &COMM); + ~ScaLBL_Multiphys_Controller(); + + void ReadParams(string filename); + void ReadParams(std::shared_ptr db0); + int getStokesNumIter_PNP_coupling(double StokesTimeConv, + const vector &IonTimeConv); + vector getIonNumIter_PNP_coupling(double StokesTimeConv, + const vector &IonTimeConv); //void getIonNumIter_PNP_coupling(double StokesTimeConv,vector &IonTimeConv,vector &IonTimeMax); - void getTimeConvMax_PNP_coupling(double StokesTimeConv,const vector &IonTimeConv); - - bool Restart; + void getTimeConvMax_PNP_coupling(double StokesTimeConv, + const vector &IonTimeConv); + + bool Restart; int timestepMax; int num_iter_Stokes; vector num_iter_Ion; @@ -39,20 +42,20 @@ public: double time_conv_max; //double SchmidtNum;//Schmidt number = kinematic_viscosity/mass_diffusivity - int rank,nprocs; + int rank, nprocs; // input database std::shared_ptr db; std::shared_ptr study_db; private: - Utilities::MPI comm; - - // filenames + Utilities::MPI comm; + + // filenames char LocalRankString[8]; char LocalRankFilename[40]; char LocalRestartFile[40]; - + //int rank,nprocs; - void LoadParams(std::shared_ptr db0); + void LoadParams(std::shared_ptr db0); }; diff --git a/models/PoissonSolver.cpp b/models/PoissonSolver.cpp index 37ee1e18..141f79e9 100644 --- a/models/PoissonSolver.cpp +++ b/models/PoissonSolver.cpp @@ -5,503 +5,589 @@ #include "analysis/distance.h" #include "common/ReadMicroCT.h" - -static inline bool fileExists( const std::string &filename ) -{ - std::ifstream ifile( filename.c_str() ); +static inline bool fileExists(const std::string &filename) { + std::ifstream ifile(filename.c_str()); return ifile.good(); } +ScaLBL_Poisson::ScaLBL_Poisson(int RANK, int NP, const Utilities::MPI &COMM) + : rank(RANK), TIMELOG(nullptr), nprocs(NP), timestep(0), timestepMax(0), + tau(0), k2_inv(0), tolerance(0), h(0), epsilon0(0), epsilon0_LB(0), + epsilonR(0), epsilon_LB(0), Vin(0), Vout(0), Nx(0), Ny(0), Nz(0), N(0), + Np(0), analysis_interval(0), chargeDen_dummy(0), WriteLog(0), nprocx(0), + nprocy(0), nprocz(0), BoundaryConditionInlet(0), + BoundaryConditionOutlet(0), BoundaryConditionSolid(0), Lx(0), Ly(0), + Lz(0), Vin0(0), freqIn(0), t0_In(0), Vin_Type(0), Vout0(0), freqOut(0), + t0_Out(0), Vout_Type(0), TestPeriodic(0), TestPeriodicTime(0), + TestPeriodicTimeConv(0), TestPeriodicSaveInterval(0), comm(COMM) { + if (rank == 0) { + bool WriteHeader = !fileExists("PoissonSolver_Convergence.csv"); -ScaLBL_Poisson::ScaLBL_Poisson(int RANK, int NP, const Utilities::MPI& COMM): - rank(RANK), TIMELOG(nullptr), nprocs(NP),timestep(0),timestepMax(0),tau(0),k2_inv(0),tolerance(0),h(0), - epsilon0(0),epsilon0_LB(0),epsilonR(0),epsilon_LB(0),Vin(0),Vout(0),Nx(0),Ny(0),Nz(0),N(0),Np(0),analysis_interval(0), - chargeDen_dummy(0),WriteLog(0),nprocx(0),nprocy(0),nprocz(0), - BoundaryConditionInlet(0),BoundaryConditionOutlet(0),BoundaryConditionSolid(0),Lx(0),Ly(0),Lz(0), - Vin0(0),freqIn(0),t0_In(0),Vin_Type(0),Vout0(0),freqOut(0),t0_Out(0),Vout_Type(0), - TestPeriodic(0),TestPeriodicTime(0),TestPeriodicTimeConv(0),TestPeriodicSaveInterval(0), - comm(COMM) -{ - if ( rank == 0 ) { - bool WriteHeader = !fileExists( "PoissonSolver_Convergence.csv" ); - - TIMELOG = fopen("PoissonSolver_Convergence.csv","a+"); - if (WriteHeader) - fprintf(TIMELOG,"Timestep Error\n"); + TIMELOG = fopen("PoissonSolver_Convergence.csv", "a+"); + if (WriteHeader) + fprintf(TIMELOG, "Timestep Error\n"); } } -ScaLBL_Poisson::~ScaLBL_Poisson() -{ - if ( TIMELOG ) - fclose( TIMELOG ); +ScaLBL_Poisson::~ScaLBL_Poisson() { + if (TIMELOG) + fclose(TIMELOG); } -void ScaLBL_Poisson::ReadParams(string filename){ - // read the input database - db = std::make_shared( filename ); - domain_db = db->getDatabase( "Domain" ); - electric_db = db->getDatabase( "Poisson" ); - - k2_inv = 4.0;//speed of sound for D3Q7 lattice - tau = 0.5+k2_inv; - timestepMax = 100000; - tolerance = 1.0e-6;//stopping criterion for obtaining steady-state electricla potential - h = 1.0;//resolution; unit: um/lu - epsilon0 = 8.85e-12;//electric permittivity of vaccum; unit:[C/(V*m)] - epsilon0_LB = epsilon0*(h*1.0e-6);//unit:[C/(V*lu)] - epsilonR = 78.4;//default dielectric constant of water - epsilon_LB = epsilon0_LB*epsilonR;//electric permittivity - analysis_interval = 1000; - chargeDen_dummy = 1.0e-3;//For debugging;unit=[C/m^3] +void ScaLBL_Poisson::ReadParams(string filename) { + // read the input database + db = std::make_shared(filename); + domain_db = db->getDatabase("Domain"); + electric_db = db->getDatabase("Poisson"); + + k2_inv = 4.0; //speed of sound for D3Q7 lattice + tau = 0.5 + k2_inv; + timestepMax = 100000; + tolerance = + 1.0e-6; //stopping criterion for obtaining steady-state electricla potential + h = 1.0; //resolution; unit: um/lu + epsilon0 = 8.85e-12; //electric permittivity of vaccum; unit:[C/(V*m)] + epsilon0_LB = epsilon0 * (h * 1.0e-6); //unit:[C/(V*lu)] + epsilonR = 78.4; //default dielectric constant of water + epsilon_LB = epsilon0_LB * epsilonR; //electric permittivity + analysis_interval = 1000; + chargeDen_dummy = 1.0e-3; //For debugging;unit=[C/m^3] WriteLog = false; TestPeriodic = false; - TestPeriodicTime = 1.0;//unit: [sec] - TestPeriodicTimeConv = 0.01; //unit [sec/lt] + TestPeriodicTime = 1.0; //unit: [sec] + TestPeriodicTimeConv = 0.01; //unit [sec/lt] TestPeriodicSaveInterval = 0.1; //unit [sec] - // LB-Poisson Model parameters - if (electric_db->keyExists( "timestepMax" )){ - timestepMax = electric_db->getScalar( "timestepMax" ); - } - if (electric_db->keyExists( "analysis_interval" )){ - analysis_interval = electric_db->getScalar( "analysis_interval" ); - } - if (electric_db->keyExists( "tolerance" )){ - tolerance = electric_db->getScalar( "tolerance" ); - } + // LB-Poisson Model parameters + if (electric_db->keyExists("timestepMax")) { + timestepMax = electric_db->getScalar("timestepMax"); + } + if (electric_db->keyExists("analysis_interval")) { + analysis_interval = electric_db->getScalar("analysis_interval"); + } + if (electric_db->keyExists("tolerance")) { + tolerance = electric_db->getScalar("tolerance"); + } //'tolerance_method' can be {"MSE","MSE_max"} - tolerance_method = electric_db->getWithDefault( "tolerance_method", "MSE" ); - if (electric_db->keyExists( "epsilonR" )){ - epsilonR = electric_db->getScalar( "epsilonR" ); - } - if (electric_db->keyExists( "DummyChargeDen" )){ - chargeDen_dummy = electric_db->getScalar( "DummyChargeDen" ); - } - if (electric_db->keyExists( "WriteLog" )){ - WriteLog = electric_db->getScalar( "WriteLog" ); - } - if (electric_db->keyExists( "TestPeriodic" )){ - TestPeriodic = electric_db->getScalar( "TestPeriodic" ); - } - if (electric_db->keyExists( "TestPeriodicTime" )){ - TestPeriodicTime = electric_db->getScalar( "TestPeriodicTime" ); - } - if (electric_db->keyExists( "TestPeriodicTimeConv" )){ - TestPeriodicTimeConv = electric_db->getScalar( "TestPeriodicTimeConv" ); - } - if (electric_db->keyExists( "TestPeriodicSaveInterval" )){ - TestPeriodicSaveInterval = electric_db->getScalar( "TestPeriodicSaveInterval" ); - } + tolerance_method = + electric_db->getWithDefault("tolerance_method", "MSE"); + if (electric_db->keyExists("epsilonR")) { + epsilonR = electric_db->getScalar("epsilonR"); + } + if (electric_db->keyExists("DummyChargeDen")) { + chargeDen_dummy = electric_db->getScalar("DummyChargeDen"); + } + if (electric_db->keyExists("WriteLog")) { + WriteLog = electric_db->getScalar("WriteLog"); + } + if (electric_db->keyExists("TestPeriodic")) { + TestPeriodic = electric_db->getScalar("TestPeriodic"); + } + if (electric_db->keyExists("TestPeriodicTime")) { + TestPeriodicTime = electric_db->getScalar("TestPeriodicTime"); + } + if (electric_db->keyExists("TestPeriodicTimeConv")) { + TestPeriodicTimeConv = + electric_db->getScalar("TestPeriodicTimeConv"); + } + if (electric_db->keyExists("TestPeriodicSaveInterval")) { + TestPeriodicSaveInterval = + electric_db->getScalar("TestPeriodicSaveInterval"); + } // Read solid boundary condition specific to Poisson equation BoundaryConditionSolid = 1; - if (electric_db->keyExists( "BC_Solid" )){ - BoundaryConditionSolid = electric_db->getScalar( "BC_Solid" ); - } + if (electric_db->keyExists("BC_Solid")) { + BoundaryConditionSolid = electric_db->getScalar("BC_Solid"); + } // Read boundary condition for electric potential // BC = 0: normal periodic BC // BC = 1: fixed electric potential // BC = 2: sine/cosine periodic electric potential (need extra input parameters) BoundaryConditionInlet = 0; BoundaryConditionOutlet = 0; - if (electric_db->keyExists( "BC_Inlet" )){ - BoundaryConditionInlet = electric_db->getScalar( "BC_Inlet" ); - } - if (electric_db->keyExists( "BC_Outlet" )){ - BoundaryConditionOutlet = electric_db->getScalar( "BC_Outlet" ); - } + if (electric_db->keyExists("BC_Inlet")) { + BoundaryConditionInlet = electric_db->getScalar("BC_Inlet"); + } + if (electric_db->keyExists("BC_Outlet")) { + BoundaryConditionOutlet = electric_db->getScalar("BC_Outlet"); + } - // Read domain parameters - if (domain_db->keyExists( "voxel_length" )){//default unit: um/lu - h = domain_db->getScalar( "voxel_length" ); - } + // Read domain parameters + if (domain_db->keyExists("voxel_length")) { //default unit: um/lu + h = domain_db->getScalar("voxel_length"); + } //Re-calcualte model parameters if user updates input - epsilon0_LB = epsilon0*(h*1.0e-6);//unit:[C/(V*lu)] - epsilon_LB = epsilon0_LB*epsilonR;//electric permittivity + epsilon0_LB = epsilon0 * (h * 1.0e-6); //unit:[C/(V*lu)] + epsilon_LB = epsilon0_LB * epsilonR; //electric permittivity - if (rank==0) printf("***********************************************************************************\n"); - if (rank==0) printf("LB-Poisson Solver: steady-state MaxTimeStep = %i; steady-state tolerance = %.3g \n", timestepMax,tolerance); - if (rank==0) printf(" LB relaxation tau = %.5g \n", tau); - if (rank==0) printf("***********************************************************************************\n"); - if (tolerance_method.compare("MSE")==0){ - if (rank==0) printf("LB-Poisson Solver: Use averaged MSE to check solution convergence.\n"); - } - else if (tolerance_method.compare("MSE_max")==0){ - if (rank==0) printf("LB-Poisson Solver: Use maximum MSE to check solution convergence.\n"); - } - else{ - if (rank==0) printf("LB-Poisson Solver: tolerance_method=%s cannot be identified!\n",tolerance_method.c_str()); + if (rank == 0) + printf("***************************************************************" + "********************\n"); + if (rank == 0) + printf("LB-Poisson Solver: steady-state MaxTimeStep = %i; steady-state " + "tolerance = %.3g \n", + timestepMax, tolerance); + if (rank == 0) + printf(" LB relaxation tau = %.5g \n", tau); + if (rank == 0) + printf("***************************************************************" + "********************\n"); + if (tolerance_method.compare("MSE") == 0) { + if (rank == 0) + printf("LB-Poisson Solver: Use averaged MSE to check solution " + "convergence.\n"); + } else if (tolerance_method.compare("MSE_max") == 0) { + if (rank == 0) + printf("LB-Poisson Solver: Use maximum MSE to check solution " + "convergence.\n"); + } else { + if (rank == 0) + printf("LB-Poisson Solver: tolerance_method=%s cannot be " + "identified!\n", + tolerance_method.c_str()); } - switch (BoundaryConditionSolid){ - case 1: - if (rank==0) printf("LB-Poisson Solver: solid boundary: Dirichlet-type surfacen potential is assigned\n"); - break; - case 2: - if (rank==0) printf("LB-Poisson Solver: solid boundary: Neumann-type surfacen charge density is assigned\n"); - break; - default: - if (rank==0) printf("LB-Poisson Solver: solid boundary: Dirichlet-type surfacen potential is assigned\n"); - break; + switch (BoundaryConditionSolid) { + case 1: + if (rank == 0) + printf("LB-Poisson Solver: solid boundary: Dirichlet-type surfacen " + "potential is assigned\n"); + break; + case 2: + if (rank == 0) + printf("LB-Poisson Solver: solid boundary: Neumann-type surfacen " + "charge density is assigned\n"); + break; + default: + if (rank == 0) + printf("LB-Poisson Solver: solid boundary: Dirichlet-type surfacen " + "potential is assigned\n"); + break; } } -void ScaLBL_Poisson::SetDomain(){ - Dm = std::shared_ptr(new Domain(domain_db,comm)); // full domain for analysis - Mask = std::shared_ptr(new Domain(domain_db,comm)); // mask domain removes immobile phases +void ScaLBL_Poisson::SetDomain() { + Dm = std::shared_ptr( + new Domain(domain_db, comm)); // full domain for analysis + Mask = std::shared_ptr( + new Domain(domain_db, comm)); // mask domain removes immobile phases - // domain parameters - Nx = Dm->Nx; - Ny = Dm->Ny; - Nz = Dm->Nz; - Lx = Dm->Lx; - Ly = Dm->Ly; - Lz = Dm->Lz; - - N = Nx*Ny*Nz; - Distance.resize(Nx,Ny,Nz); - Psi_host.resize(Nx,Ny,Nz); - Psi_previous.resize(Nx,Ny,Nz); + // domain parameters + Nx = Dm->Nx; + Ny = Dm->Ny; + Nz = Dm->Nz; + Lx = Dm->Lx; + Ly = Dm->Ly; + Lz = Dm->Lz; - for (int i=0; iid[i] = 1; // initialize this way - //Averages = std::shared_ptr ( new TwoPhase(Dm) ); // TwoPhase analysis object - comm.barrier(); - if (BoundaryConditionInlet==0 && BoundaryConditionOutlet==0){ - Dm->BoundaryCondition = 0; + N = Nx * Ny * Nz; + Distance.resize(Nx, Ny, Nz); + Psi_host.resize(Nx, Ny, Nz); + Psi_previous.resize(Nx, Ny, Nz); + + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = 1; // initialize this way + //Averages = std::shared_ptr ( new TwoPhase(Dm) ); // TwoPhase analysis object + comm.barrier(); + if (BoundaryConditionInlet == 0 && BoundaryConditionOutlet == 0) { + Dm->BoundaryCondition = 0; Mask->BoundaryCondition = 0; - } - else if (BoundaryConditionInlet>0 && BoundaryConditionOutlet>0){ - Dm->BoundaryCondition = 1; + } else if (BoundaryConditionInlet > 0 && BoundaryConditionOutlet > 0) { + Dm->BoundaryCondition = 1; Mask->BoundaryCondition = 1; + } else { //i.e. non-periodic and periodic BCs are mixed + ERROR("Error: check the type of inlet and outlet boundary condition! " + "Mixed periodic and non-periodic BCs are found!\n"); } - else {//i.e. non-periodic and periodic BCs are mixed - ERROR("Error: check the type of inlet and outlet boundary condition! Mixed periodic and non-periodic BCs are found!\n"); - } - Dm->CommInit(); - comm.barrier(); - - rank = Dm->rank(); - nprocx = Dm->nprocx(); - nprocy = Dm->nprocy(); - nprocz = Dm->nprocz(); + Dm->CommInit(); + comm.barrier(); + + rank = Dm->rank(); + nprocx = Dm->nprocx(); + nprocy = Dm->nprocy(); + nprocz = Dm->nprocz(); } -void ScaLBL_Poisson::ReadInput(){ - - sprintf(LocalRankString,"%05d",Dm->rank()); - sprintf(LocalRankFilename,"%s%s","ID.",LocalRankString); - sprintf(LocalRestartFile,"%s%s","Restart.",LocalRankString); +void ScaLBL_Poisson::ReadInput() { - - if (domain_db->keyExists( "Filename" )){ - auto Filename = domain_db->getScalar( "Filename" ); - Mask->Decomp(Filename); - } - else if (domain_db->keyExists( "GridFile" )){ - // Read the local domain data - auto input_id = readMicroCT( *domain_db, comm ); - // Fill the halo (assuming GCW of 1) - array size0 = { (int) input_id.size(0), (int) input_id.size(1), (int) input_id.size(2) }; - ArraySize size1 = { (size_t) Mask->Nx, (size_t) Mask->Ny, (size_t) Mask->Nz }; - ASSERT( (int) size1[0] == size0[0]+2 && (int) size1[1] == size0[1]+2 && (int) size1[2] == size0[2]+2 ); - fillHalo fill( comm, Mask->rank_info, size0, { 1, 1, 1 }, 0, 1 ); - Array id_view; - id_view.viewRaw( size1, Mask->id.data() ); - fill.copy( input_id, id_view ); - fill.fill( id_view ); - } - else{ - Mask->ReadIDs(); + sprintf(LocalRankString, "%05d", Dm->rank()); + sprintf(LocalRankFilename, "%s%s", "ID.", LocalRankString); + sprintf(LocalRestartFile, "%s%s", "Restart.", LocalRankString); + + if (domain_db->keyExists("Filename")) { + auto Filename = domain_db->getScalar("Filename"); + Mask->Decomp(Filename); + } else if (domain_db->keyExists("GridFile")) { + // Read the local domain data + auto input_id = readMicroCT(*domain_db, comm); + // Fill the halo (assuming GCW of 1) + array size0 = {(int)input_id.size(0), (int)input_id.size(1), + (int)input_id.size(2)}; + ArraySize size1 = {(size_t)Mask->Nx, (size_t)Mask->Ny, + (size_t)Mask->Nz}; + ASSERT((int)size1[0] == size0[0] + 2 && (int)size1[1] == size0[1] + 2 && + (int)size1[2] == size0[2] + 2); + fillHalo fill(comm, Mask->rank_info, size0, {1, 1, 1}, 0, + 1); + Array id_view; + id_view.viewRaw(size1, Mask->id.data()); + fill.copy(input_id, id_view); + fill.fill(id_view); + } else { + Mask->ReadIDs(); } // Generate the signed distance map - // Initialize the domain and communication - Array id_solid(Nx,Ny,Nz); - // Solve for the position of the solid phase - for (int k=0;kid[n] > 0) id_solid(i,j,k) = 1; - else id_solid(i,j,k) = 0; - } - } - } - // Initialize the signed distance function - for (int k=0;kSDs); - if (rank==0) printf("LB-Poisson Solver: Initialized solid phase & converting to Signed Distance function \n"); - CalcDist(Distance,id_solid,*Dm); - if (rank == 0) cout << " Domain set." << endl; -} - -void ScaLBL_Poisson::AssignSolidBoundary(double *poisson_solid) -{ - signed char VALUE=0; - double AFFINITY=0.f; - - auto LabelList = electric_db->getVector( "SolidLabels" ); - auto AffinityList = electric_db->getVector( "SolidValues" ); - - size_t NLABELS = LabelList.size(); - if (NLABELS != AffinityList.size()){ - ERROR("Error: LB-Poisson Solver: SolidLabels and SolidValues must be the same length! \n"); - } - - std::vector label_count( NLABELS, 0.0 ); - std::vector label_count_global( NLABELS, 0.0 ); - // Assign the labels - - for (size_t idx=0; idxid[n]; - AFFINITY=0.f; - // Assign the affinity from the paired list - for (unsigned int idx=0; idx < NLABELS; idx++){ - if (VALUE == LabelList[idx]){ - AFFINITY=AffinityList[idx]; - //NOTE need to convert the user input phys unit to LB unit - if (BoundaryConditionSolid==2){ - //for BCS=1, i.e. Dirichlet-type, no need for unit conversion - AFFINITY = AFFINITY*(h*h*1.0e-12)/epsilon_LB; - } - label_count[idx] += 1.0; - idx = NLABELS; - //Mask->id[n] = 0; // set mask to zero since this is an immobile component - } - } - poisson_solid[n] = AFFINITY; - } - } - } - - for (size_t idx=0; idxComm.sumReduce( label_count[idx]); - - if (rank==0){ - printf("LB-Poisson Solver: number of Poisson solid labels: %lu \n",NLABELS); - for (unsigned int idx=0; idx id_solid(Nx, Ny, Nz); + // Solve for the position of the solid phase + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + // Initialize the solid phase + if (Mask->id[n] > 0) + id_solid(i, j, k) = 1; + else + id_solid(i, j, k) = 0; } - } - } -} - -void ScaLBL_Poisson::Create(){ - /* - * This function creates the variables needed to run a LBM - */ - int rank=Mask->rank(); - //......................................................... - // Initialize communication structures in averaging domain - for (int i=0; iid[i] = Mask->id[i]; - Mask->CommInit(); - Np=Mask->PoreCount(); - //........................................................................... - if (rank==0) printf ("LB-Poisson Solver: Create ScaLBL_Communicator \n"); - // Create a communicator for the device (will use optimized layout) - // ScaLBL_Communicator ScaLBL_Comm(Mask); // original - ScaLBL_Comm = std::shared_ptr(new ScaLBL_Communicator(Mask)); - ScaLBL_Comm_Regular = std::shared_ptr(new ScaLBL_Communicator(Mask)); - - int Npad=(Np/16 + 2)*16; - if (rank==0) printf ("LB-Poisson Solver: Set up memory efficient layout \n"); - Map.resize(Nx,Ny,Nz); Map.fill(-2); - auto neighborList= new int[18*Npad]; - Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map,neighborList,Mask->id.data(),Np,1); - comm.barrier(); - - //........................................................................... - // MAIN VARIABLES ALLOCATED HERE - //........................................................................... - // LBM variables - if (rank==0) printf ("LB-Poisson Solver: Allocating distributions \n"); - //......................device distributions................................. - int dist_mem_size = Np*sizeof(double); - int neighborSize=18*(Np*sizeof(int)); - //........................................................................... - ScaLBL_AllocateDeviceMemory((void **) &NeighborList, neighborSize); - ScaLBL_AllocateDeviceMemory((void **) &dvcMap, sizeof(int)*Np); - //ScaLBL_AllocateDeviceMemory((void **) &dvcID, sizeof(signed char)*Nx*Ny*Nz); - ScaLBL_AllocateDeviceMemory((void **) &fq, 7*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Psi, sizeof(double)*Nx*Ny*Nz); - ScaLBL_AllocateDeviceMemory((void **) &ElectricField, 3*sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &ResidualError, sizeof(double)*Np); - //........................................................................... - - // Update GPU data structures - if (rank==0) printf ("LB-Poisson Solver: Setting up device map and neighbor list \n"); - fflush(stdout); - int *TmpMap; - TmpMap=new int[Np]; - for (int k=1; kLastExterior(); idx++){ - auto n = TmpMap[idx]; - if (n > Nx*Ny*Nz){ - printf("Bad value! idx=%i \n", n); - TmpMap[idx] = Nx*Ny*Nz-1; - } - } - for (int idx=ScaLBL_Comm->FirstInterior(); idxLastInterior(); idx++){ - auto n = TmpMap[idx]; - if ( n > Nx*Ny*Nz ){ - printf("Bad value! idx=%i \n",n); - TmpMap[idx] = Nx*Ny*Nz-1; - } - } - ScaLBL_CopyToDevice(dvcMap, TmpMap, sizeof(int)*Np); - ScaLBL_Comm->Barrier(); - delete [] TmpMap; - // copy the neighbor list - ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); - ScaLBL_Comm->Barrier(); - comm.barrier(); - delete [] neighborList; - // copy node ID - //ScaLBL_CopyToDevice(dvcID, Mask->id, sizeof(signed char)*Nx*Ny*Nz); - //ScaLBL_Comm->Barrier(); - - //Initialize solid boundary for electric potential - ScaLBL_Comm->SetupBounceBackList(Map, Mask->id.data(), Np); - comm.barrier(); -} - -void ScaLBL_Poisson::Potential_Init(double *psi_init){ - - //set up default boundary input parameters - Vin0 = Vout0 = 1.0; //unit: [V] - freqIn = freqOut = 50.0; //unit: [Hz] - t0_In = t0_Out = 0.0; //unit: [sec] - Vin_Type = Vout_Type = 1; //1->sin; 2->cos - Vin = 1.0; //Boundary-z (inlet) electric potential - Vout = 1.0; //Boundary-Z (outlet) electric potential - - if (BoundaryConditionInlet>0){ - switch (BoundaryConditionInlet){ - case 1: - if (electric_db->keyExists( "Vin" )){ - Vin = electric_db->getScalar( "Vin" ); - } - if (rank==0) printf("LB-Poisson Solver: inlet boundary; fixed electric potential Vin = %.3g [V]\n",Vin); - break; - case 2: - if (electric_db->keyExists( "Vin0" )){//voltage amplitude; unit: Volt - Vin0 = electric_db->getScalar( "Vin0" ); - } - if (electric_db->keyExists( "freqIn" )){//unit: Hz - freqIn = electric_db->getScalar( "freqIn" ); - } - if (electric_db->keyExists( "t0_In" )){//timestep shift, unit: lt - t0_In = electric_db->getScalar( "t0_In" ); - } - if (electric_db->keyExists( "Vin_Type" )){ - //type=1 -> sine - //tyep=2 -> cosine - Vin_Type = electric_db->getScalar( "Vin_Type" ); - if (Vin_Type>2 || Vin_Type<=0) ERROR("Error: user-input Vin_Type is currently not supported! \n"); - } - if (rank==0){ - if (Vin_Type==1){ - printf("LB-Poisson Solver: inlet boundary; periodic electric potential Vin = %.3g*Sin[2*pi*%.3g*(t+%.3g)] [V]\n",Vin0,freqIn,t0_In); - printf(" V0 = %.3g [V], frequency = %.3g [Hz], timestep shift = %.3g [sec] \n",Vin0,freqIn,t0_In); - } - else if (Vin_Type==2){ - printf("LB-Poisson Solver: inlet boundary; periodic electric potential Vin = %.3g*Cos[2*pi*%.3g*(t+%.3g)] [V] \n",Vin0,freqIn,t0_In); - printf(" V0 = %.3g [V], frequency = %.3g [Hz], timestep shift = %.3g [sec] \n",Vin0,freqIn,t0_In); - } - } - break; } } - if (BoundaryConditionOutlet>0){ - switch (BoundaryConditionOutlet){ - case 1: - if (electric_db->keyExists( "Vout" )){ - Vout = electric_db->getScalar( "Vout" ); + // Initialize the signed distance function + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + // Initialize distance to +/- 1 + Distance(i, j, k) = 2.0 * double(id_solid(i, j, k)) - 1.0; + } + } + } + // MeanFilter(Averages->SDs); + if (rank == 0) + printf("LB-Poisson Solver: Initialized solid phase & converting to " + "Signed Distance function \n"); + CalcDist(Distance, id_solid, *Dm); + if (rank == 0) + cout << " Domain set." << endl; +} + +void ScaLBL_Poisson::AssignSolidBoundary(double *poisson_solid) { + signed char VALUE = 0; + double AFFINITY = 0.f; + + auto LabelList = electric_db->getVector("SolidLabels"); + auto AffinityList = electric_db->getVector("SolidValues"); + + size_t NLABELS = LabelList.size(); + if (NLABELS != AffinityList.size()) { + ERROR("Error: LB-Poisson Solver: SolidLabels and SolidValues must be " + "the same length! \n"); + } + + std::vector label_count(NLABELS, 0.0); + std::vector label_count_global(NLABELS, 0.0); + // Assign the labels + + for (size_t idx = 0; idx < NLABELS; idx++) + label_count[idx] = 0; + + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + VALUE = Mask->id[n]; + AFFINITY = 0.f; + // Assign the affinity from the paired list + for (unsigned int idx = 0; idx < NLABELS; idx++) { + if (VALUE == LabelList[idx]) { + AFFINITY = AffinityList[idx]; + //NOTE need to convert the user input phys unit to LB unit + if (BoundaryConditionSolid == 2) { + //for BCS=1, i.e. Dirichlet-type, no need for unit conversion + AFFINITY = + AFFINITY * (h * h * 1.0e-12) / epsilon_LB; + } + label_count[idx] += 1.0; + idx = NLABELS; + //Mask->id[n] = 0; // set mask to zero since this is an immobile component + } } - if (rank==0) printf("LB-Poisson Solver: outlet boundary; fixed electric potential Vout = %.3g [V] \n",Vout); + poisson_solid[n] = AFFINITY; + } + } + } + + for (size_t idx = 0; idx < NLABELS; idx++) + label_count_global[idx] = Dm->Comm.sumReduce(label_count[idx]); + + if (rank == 0) { + printf("LB-Poisson Solver: number of Poisson solid labels: %lu \n", + NLABELS); + for (unsigned int idx = 0; idx < NLABELS; idx++) { + VALUE = LabelList[idx]; + AFFINITY = AffinityList[idx]; + double volume_fraction = + double(label_count_global[idx]) / + double((Nx - 2) * (Ny - 2) * (Nz - 2) * nprocs); + switch (BoundaryConditionSolid) { + case 1: + printf(" label=%d, surface potential=%.3g [V], volume " + "fraction=%.2g\n", + VALUE, AFFINITY, volume_fraction); break; case 2: - if (electric_db->keyExists( "Vout0" )){//voltage amplitude; unit: Volt - Vout0 = electric_db->getScalar( "Vout0" ); - } - if (electric_db->keyExists( "freqOut" )){//unit: Hz - freqOut = electric_db->getScalar( "freqOut" ); - } - if (electric_db->keyExists( "t0_Out" )){//timestep shift, unit: lt - t0_Out = electric_db->getScalar( "t0_Out" ); - } - if (electric_db->keyExists( "Vout_Type" )){ - //type=1 -> sine - //tyep=2 -> cosine - Vout_Type = electric_db->getScalar( "Vout_Type" ); - if (Vout_Type>2 || Vin_Type<=0) ERROR("Error: user-input Vout_Type is currently not supported! \n"); - } - if (rank==0){ - if (Vout_Type==1){ - printf("LB-Poisson Solver: outlet boundary; periodic electric potential Vout = %.3g*Sin[2*pi*%.3g*(t+%.3g)] [V]\n",Vout0,freqOut,t0_Out); - printf(" V0 = %.3g [V], frequency = %.3g [Hz], timestep shift = %.3g [sec] \n",Vout0,freqOut,t0_Out); - } - else if (Vout_Type==2){ - printf("LB-Poisson Solver: outlet boundary; periodic electric potential Vout = %.3g*Cos[2*pi*%.3g*(t+%.3g)] [V]\n",Vout0,freqOut,t0_Out); - printf(" V0 = %.3g [V], frequency = %.3g [Hz], timestep shift = %.3g [sec] \n",Vout0,freqOut,t0_Out); - } - } + printf(" label=%d, surface charge density=%.3g [C/m^2], " + "volume fraction=%.2g\n", + VALUE, AFFINITY, volume_fraction); break; + default: + printf(" label=%d, surface potential=%.3g [V], volume " + "fraction=%.2g\n", + VALUE, AFFINITY, volume_fraction); + break; + } + } + } +} + +void ScaLBL_Poisson::Create() { + /* + * This function creates the variables needed to run a LBM + */ + int rank = Mask->rank(); + //......................................................... + // Initialize communication structures in averaging domain + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = Mask->id[i]; + Mask->CommInit(); + Np = Mask->PoreCount(); + //........................................................................... + if (rank == 0) + printf("LB-Poisson Solver: Create ScaLBL_Communicator \n"); + // Create a communicator for the device (will use optimized layout) + // ScaLBL_Communicator ScaLBL_Comm(Mask); // original + ScaLBL_Comm = + std::shared_ptr(new ScaLBL_Communicator(Mask)); + ScaLBL_Comm_Regular = + std::shared_ptr(new ScaLBL_Communicator(Mask)); + + int Npad = (Np / 16 + 2) * 16; + if (rank == 0) + printf("LB-Poisson Solver: Set up memory efficient layout \n"); + Map.resize(Nx, Ny, Nz); + Map.fill(-2); + auto neighborList = new int[18 * Npad]; + Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map, neighborList, + Mask->id.data(), Np, 1); + comm.barrier(); + + //........................................................................... + // MAIN VARIABLES ALLOCATED HERE + //........................................................................... + // LBM variables + if (rank == 0) + printf("LB-Poisson Solver: Allocating distributions \n"); + //......................device distributions................................. + int dist_mem_size = Np * sizeof(double); + int neighborSize = 18 * (Np * sizeof(int)); + //........................................................................... + ScaLBL_AllocateDeviceMemory((void **)&NeighborList, neighborSize); + ScaLBL_AllocateDeviceMemory((void **)&dvcMap, sizeof(int) * Np); + //ScaLBL_AllocateDeviceMemory((void **) &dvcID, sizeof(signed char)*Nx*Ny*Nz); + ScaLBL_AllocateDeviceMemory((void **)&fq, 7 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Psi, sizeof(double) * Nx * Ny * Nz); + ScaLBL_AllocateDeviceMemory((void **)&ElectricField, + 3 * sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&ResidualError, sizeof(double) * Np); + //........................................................................... + + // Update GPU data structures + if (rank == 0) + printf("LB-Poisson Solver: Setting up device map and neighbor list \n"); + fflush(stdout); + int *TmpMap; + TmpMap = new int[Np]; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + int idx = Map(i, j, k); + if (!(idx < 0)) + TmpMap[idx] = k * Nx * Ny + j * Nx + i; + } + } + } + // check that TmpMap is valid + for (int idx = 0; idx < ScaLBL_Comm->LastExterior(); idx++) { + auto n = TmpMap[idx]; + if (n > Nx * Ny * Nz) { + printf("Bad value! idx=%i \n", n); + TmpMap[idx] = Nx * Ny * Nz - 1; + } + } + for (int idx = ScaLBL_Comm->FirstInterior(); + idx < ScaLBL_Comm->LastInterior(); idx++) { + auto n = TmpMap[idx]; + if (n > Nx * Ny * Nz) { + printf("Bad value! idx=%i \n", n); + TmpMap[idx] = Nx * Ny * Nz - 1; + } + } + ScaLBL_CopyToDevice(dvcMap, TmpMap, sizeof(int) * Np); + ScaLBL_Comm->Barrier(); + delete[] TmpMap; + // copy the neighbor list + ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); + ScaLBL_Comm->Barrier(); + comm.barrier(); + delete[] neighborList; + // copy node ID + //ScaLBL_CopyToDevice(dvcID, Mask->id, sizeof(signed char)*Nx*Ny*Nz); + //ScaLBL_Comm->Barrier(); + + //Initialize solid boundary for electric potential + ScaLBL_Comm->SetupBounceBackList(Map, Mask->id.data(), Np); + comm.barrier(); +} + +void ScaLBL_Poisson::Potential_Init(double *psi_init) { + + //set up default boundary input parameters + Vin0 = Vout0 = 1.0; //unit: [V] + freqIn = freqOut = 50.0; //unit: [Hz] + t0_In = t0_Out = 0.0; //unit: [sec] + Vin_Type = Vout_Type = 1; //1->sin; 2->cos + Vin = 1.0; //Boundary-z (inlet) electric potential + Vout = 1.0; //Boundary-Z (outlet) electric potential + + if (BoundaryConditionInlet > 0) { + switch (BoundaryConditionInlet) { + case 1: + if (electric_db->keyExists("Vin")) { + Vin = electric_db->getScalar("Vin"); + } + if (rank == 0) + printf("LB-Poisson Solver: inlet boundary; fixed electric " + "potential Vin = %.3g [V]\n", + Vin); + break; + case 2: + if (electric_db->keyExists( + "Vin0")) { //voltage amplitude; unit: Volt + Vin0 = electric_db->getScalar("Vin0"); + } + if (electric_db->keyExists("freqIn")) { //unit: Hz + freqIn = electric_db->getScalar("freqIn"); + } + if (electric_db->keyExists("t0_In")) { //timestep shift, unit: lt + t0_In = electric_db->getScalar("t0_In"); + } + if (electric_db->keyExists("Vin_Type")) { + //type=1 -> sine + //tyep=2 -> cosine + Vin_Type = electric_db->getScalar("Vin_Type"); + if (Vin_Type > 2 || Vin_Type <= 0) + ERROR("Error: user-input Vin_Type is currently not " + "supported! \n"); + } + if (rank == 0) { + if (Vin_Type == 1) { + printf( + "LB-Poisson Solver: inlet boundary; periodic electric " + "potential Vin = %.3g*Sin[2*pi*%.3g*(t+%.3g)] [V]\n", + Vin0, freqIn, t0_In); + printf( + " V0 = %.3g [V], " + "frequency = %.3g [Hz], timestep shift = %.3g [sec] \n", + Vin0, freqIn, t0_In); + } else if (Vin_Type == 2) { + printf( + "LB-Poisson Solver: inlet boundary; periodic electric " + "potential Vin = %.3g*Cos[2*pi*%.3g*(t+%.3g)] [V] \n", + Vin0, freqIn, t0_In); + printf( + " V0 = %.3g [V], " + "frequency = %.3g [Hz], timestep shift = %.3g [sec] \n", + Vin0, freqIn, t0_In); + } + } + break; + } + } + if (BoundaryConditionOutlet > 0) { + switch (BoundaryConditionOutlet) { + case 1: + if (electric_db->keyExists("Vout")) { + Vout = electric_db->getScalar("Vout"); + } + if (rank == 0) + printf("LB-Poisson Solver: outlet boundary; fixed electric " + "potential Vout = %.3g [V] \n", + Vout); + break; + case 2: + if (electric_db->keyExists( + "Vout0")) { //voltage amplitude; unit: Volt + Vout0 = electric_db->getScalar("Vout0"); + } + if (electric_db->keyExists("freqOut")) { //unit: Hz + freqOut = electric_db->getScalar("freqOut"); + } + if (electric_db->keyExists("t0_Out")) { //timestep shift, unit: lt + t0_Out = electric_db->getScalar("t0_Out"); + } + if (electric_db->keyExists("Vout_Type")) { + //type=1 -> sine + //tyep=2 -> cosine + Vout_Type = electric_db->getScalar("Vout_Type"); + if (Vout_Type > 2 || Vin_Type <= 0) + ERROR("Error: user-input Vout_Type is currently not " + "supported! \n"); + } + if (rank == 0) { + if (Vout_Type == 1) { + printf( + "LB-Poisson Solver: outlet boundary; periodic electric " + "potential Vout = %.3g*Sin[2*pi*%.3g*(t+%.3g)] [V]\n", + Vout0, freqOut, t0_Out); + printf( + " V0 = %.3g [V], " + "frequency = %.3g [Hz], timestep shift = %.3g [sec] \n", + Vout0, freqOut, t0_Out); + } else if (Vout_Type == 2) { + printf( + "LB-Poisson Solver: outlet boundary; periodic electric " + "potential Vout = %.3g*Cos[2*pi*%.3g*(t+%.3g)] [V]\n", + Vout0, freqOut, t0_Out); + printf( + " V0 = %.3g [V], " + "frequency = %.3g [Hz], timestep shift = %.3g [sec] \n", + Vout0, freqOut, t0_Out); + } + } + break; } } //By default only periodic BC is applied and Vin=Vout=1.0, i.e. there is no potential gradient along Z-axis - if (BoundaryConditionInlet==2) Vin = getBoundaryVoltagefromPeriodicBC(Vin0,freqIn,t0_In,Vin_Type,0); - if (BoundaryConditionOutlet==2) Vout = getBoundaryVoltagefromPeriodicBC(Vout0,freqOut,t0_Out,Vout_Type,0); - double slope = (Vout-Vin)/(Nz-2); + if (BoundaryConditionInlet == 2) + Vin = + getBoundaryVoltagefromPeriodicBC(Vin0, freqIn, t0_In, Vin_Type, 0); + if (BoundaryConditionOutlet == 2) + Vout = getBoundaryVoltagefromPeriodicBC(Vout0, freqOut, t0_Out, + Vout_Type, 0); + double slope = (Vout - Vin) / (Nz - 2); double psi_linearized; - for (int k=0;kid[n]>0){ + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + if (Mask->id[n] > 0) { psi_init[n] = psi_linearized; } } @@ -509,122 +595,142 @@ void ScaLBL_Poisson::Potential_Init(double *psi_init){ } } -double ScaLBL_Poisson::getBoundaryVoltagefromPeriodicBC(double V0, double freq, double t0, int V_type, int time_step){ - return V0*(V_type==1)*sin(2.0*M_PI*freq*time_conv*(time_step+t0/time_conv))+V0*(V_type==2)*cos(2.0*M_PI*freq*time_conv*(time_step+t0/time_conv)); +double ScaLBL_Poisson::getBoundaryVoltagefromPeriodicBC(double V0, double freq, + double t0, int V_type, + int time_step) { + return V0 * (V_type == 1) * + sin(2.0 * M_PI * freq * time_conv * + (time_step + t0 / time_conv)) + + V0 * (V_type == 2) * + cos(2.0 * M_PI * freq * time_conv * + (time_step + t0 / time_conv)); } -void ScaLBL_Poisson::Initialize(double time_conv_from_Study){ - /* +void ScaLBL_Poisson::Initialize(double time_conv_from_Study) { + /* * This function initializes model * "time_conv_from_Study" is the phys to LB time conversion factor, unit=[sec/lt] * which is used for periodic voltage input for inlet and outlet boundaries */ - if (rank==0) printf ("LB-Poisson Solver: initializing D3Q7 distributions\n"); + if (rank == 0) + printf("LB-Poisson Solver: initializing D3Q7 distributions\n"); //NOTE the initialization involves two steps: //1. assign solid boundary value (surface potential or surface change density) //2. Initialize electric potential for pore nodes double *psi_host; - psi_host = new double [Nx*Ny*Nz]; + psi_host = new double[Nx * Ny * Nz]; time_conv = time_conv_from_Study; - AssignSolidBoundary(psi_host);//step1 - Potential_Init(psi_host);//step2 - ScaLBL_CopyToDevice(Psi, psi_host, Nx*Ny*Nz*sizeof(double)); - ScaLBL_Comm->Barrier(); - ScaLBL_D3Q7_Poisson_Init(dvcMap, fq, Psi, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_D3Q7_Poisson_Init(dvcMap, fq, Psi, 0, ScaLBL_Comm->LastExterior(), Np); - delete [] psi_host; - + AssignSolidBoundary(psi_host); //step1 + Potential_Init(psi_host); //step2 + ScaLBL_CopyToDevice(Psi, psi_host, Nx * Ny * Nz * sizeof(double)); + ScaLBL_Comm->Barrier(); + ScaLBL_D3Q7_Poisson_Init(dvcMap, fq, Psi, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_D3Q7_Poisson_Init(dvcMap, fq, Psi, 0, ScaLBL_Comm->LastExterior(), + Np); + delete[] psi_host; + //extra treatment for halo layer //if (BoundaryCondition==1){ - // if (Dm->kproc()==0){ - // ScaLBL_SetSlice_z(Psi,Vin,Nx,Ny,Nz,0); - // } - // if (Dm->kproc() == nprocz-1){ - // ScaLBL_SetSlice_z(Psi,Vout,Nx,Ny,Nz,Nz-1); - // } + // if (Dm->kproc()==0){ + // ScaLBL_SetSlice_z(Psi,Vin,Nx,Ny,Nz,0); + // } + // if (Dm->kproc() == nprocz-1){ + // ScaLBL_SetSlice_z(Psi,Vout,Nx,Ny,Nz,Nz-1); + // } //} } -void ScaLBL_Poisson::Run(double *ChargeDensity, int timestep_from_Study){ - - //.......create and start timer............ - //double starttime,stoptime,cputime; - //comm.barrier(); +void ScaLBL_Poisson::Run(double *ChargeDensity, int timestep_from_Study) { + + //.......create and start timer............ + //double starttime,stoptime,cputime; + //comm.barrier(); //auto t1 = std::chrono::system_clock::now(); - timestep=0; - double error = 1.0; - while (timestep < timestepMax && error > tolerance) { - //************************************************************************/ - // *************ODD TIMESTEP*************// + timestep = 0; + double error = 1.0; + while (timestep < timestepMax && error > tolerance) { + //************************************************************************/ + // *************ODD TIMESTEP*************// timestep++; - SolveElectricPotentialAAodd(timestep_from_Study);//update electric potential - SolvePoissonAAodd(ChargeDensity);//perform collision - ScaLBL_Comm->Barrier(); comm.barrier(); + SolveElectricPotentialAAodd( + timestep_from_Study); //update electric potential + SolvePoissonAAodd(ChargeDensity); //perform collision + ScaLBL_Comm->Barrier(); + comm.barrier(); - // *************EVEN TIMESTEP*************// - timestep++; - SolveElectricPotentialAAeven(timestep_from_Study);//update electric potential - SolvePoissonAAeven(ChargeDensity);//perform collision - ScaLBL_Comm->Barrier(); comm.barrier(); - //************************************************************************/ + // *************EVEN TIMESTEP*************// + timestep++; + SolveElectricPotentialAAeven( + timestep_from_Study); //update electric potential + SolvePoissonAAeven(ChargeDensity); //perform collision + ScaLBL_Comm->Barrier(); + comm.barrier(); + //************************************************************************/ // Check convergence of steady-state solution - if (timestep==2){ + if (timestep == 2) { //save electric potential for convergence check - ScaLBL_CopyToHost(Psi_previous.data(),Psi,sizeof(double)*Nx*Ny*Nz); + ScaLBL_CopyToHost(Psi_previous.data(), Psi, + sizeof(double) * Nx * Ny * Nz); } - if (timestep%analysis_interval==0){ - if (tolerance_method.compare("MSE")==0){ - double count_loc=0; + if (timestep % analysis_interval == 0) { + if (tolerance_method.compare("MSE") == 0) { + double count_loc = 0; double count; - double MSE_loc=0.0; - ScaLBL_CopyToHost(Psi_host.data(),Psi,sizeof(double)*Nx*Ny*Nz); - for (int k=1; k 0){ - MSE_loc += (Psi_host(i,j,k) - Psi_previous(i,j,k))*(Psi_host(i,j,k) - Psi_previous(i,j,k)); - count_loc+=1.0; + double MSE_loc = 0.0; + ScaLBL_CopyToHost(Psi_host.data(), Psi, + sizeof(double) * Nx * Ny * Nz); + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + if (Distance(i, j, k) > 0) { + MSE_loc += + (Psi_host(i, j, k) - + Psi_previous(i, j, k)) * + (Psi_host(i, j, k) - Psi_previous(i, j, k)); + count_loc += 1.0; } } } } - error=Dm->Comm.sumReduce(MSE_loc); - count=Dm->Comm.sumReduce(count_loc); + error = Dm->Comm.sumReduce(MSE_loc); + count = Dm->Comm.sumReduce(count_loc); error /= count; - } - else if (tolerance_method.compare("MSE_max")==0){ - vectorMSE_loc; + } else if (tolerance_method.compare("MSE_max") == 0) { + vector MSE_loc; double MSE_loc_max; - ScaLBL_CopyToHost(Psi_host.data(),Psi,sizeof(double)*Nx*Ny*Nz); - for (int k=1; k 0){ - MSE_loc.push_back((Psi_host(i,j,k) - Psi_previous(i,j,k))*(Psi_host(i,j,k) - Psi_previous(i,j,k))); + ScaLBL_CopyToHost(Psi_host.data(), Psi, + sizeof(double) * Nx * Ny * Nz); + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + if (Distance(i, j, k) > 0) { + MSE_loc.push_back((Psi_host(i, j, k) - + Psi_previous(i, j, k)) * + (Psi_host(i, j, k) - + Psi_previous(i, j, k))); } } } } - vector::iterator it_max = max_element(MSE_loc.begin(),MSE_loc.end()); - unsigned int idx_max=distance(MSE_loc.begin(),it_max); - MSE_loc_max=MSE_loc[idx_max]; - error=Dm->Comm.maxReduce(MSE_loc_max); + vector::iterator it_max = + max_element(MSE_loc.begin(), MSE_loc.end()); + unsigned int idx_max = distance(MSE_loc.begin(), it_max); + MSE_loc_max = MSE_loc[idx_max]; + error = Dm->Comm.maxReduce(MSE_loc_max); + } else { + ERROR("Error: user-specified tolerance_method cannot be " + "identified; check you input database! \n"); } - else{ - ERROR("Error: user-specified tolerance_method cannot be identified; check you input database! \n"); - } - ScaLBL_CopyToHost(Psi_previous.data(),Psi,sizeof(double)*Nx*Ny*Nz); - - - - + ScaLBL_CopyToHost(Psi_previous.data(), Psi, + sizeof(double) * Nx * Ny * Nz); //legacy code that tried to use residual to check convergence //ScaLBL_D3Q7_PoissonResidualError(NeighborList,dvcMap,ResidualError,Psi,ChargeDensity,epsilon_LB,Nx,Nx*Ny,ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior()); //ScaLBL_D3Q7_PoissonResidualError(NeighborList,dvcMap,ResidualError,Psi,ChargeDensity,epsilon_LB,Nx,Nx*Ny,0, ScaLBL_Comm->LastExterior()); - //ScaLBL_Comm->Barrier(); comm.barrier(); + //ScaLBL_Comm->Barrier(); comm.barrier(); //vector ResidualError_host(Np); //double error_loc_max; @@ -649,217 +755,251 @@ void ScaLBL_Poisson::Run(double *ChargeDensity, int timestep_from_Study){ //else{ // error_loc_max=ResidualError_host[idx_max2]; //} - //error = Dm->Comm.maxReduce(error_loc_max); + //error = Dm->Comm.maxReduce(error_loc_max); } - } - if(WriteLog==true){ - getConvergenceLog(timestep,error); + } + if (WriteLog == true) { + getConvergenceLog(timestep, error); } - //************************************************************************/ - ////if (rank==0) printf("LB-Poission Solver: a steady-state solution is obtained\n"); - ////if (rank==0) printf("---------------------------------------------------------------------------\n"); - //// Compute the walltime per timestep - //auto t2 = std::chrono::system_clock::now(); - //double cputime = std::chrono::duration( t2 - t1 ).count() / timestep; - //// Performance obtained from each node - //double MLUPS = double(Np)/cputime/1000000; - - //if (rank==0) printf("******************* LB-Poisson Solver Statistics ********************\n"); - //if (rank==0) printf("CPU time = %f \n", cputime); - //if (rank==0) printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); - //MLUPS *= nprocs; - //if (rank==0) printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); - //if (rank==0) printf("*********************************************************************\n"); + //************************************************************************/ + ////if (rank==0) printf("LB-Poission Solver: a steady-state solution is obtained\n"); + ////if (rank==0) printf("---------------------------------------------------------------------------\n"); + //// Compute the walltime per timestep + //auto t2 = std::chrono::system_clock::now(); + //double cputime = std::chrono::duration( t2 - t1 ).count() / timestep; + //// Performance obtained from each node + //double MLUPS = double(Np)/cputime/1000000; + //if (rank==0) printf("******************* LB-Poisson Solver Statistics ********************\n"); + //if (rank==0) printf("CPU time = %f \n", cputime); + //if (rank==0) printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); + //MLUPS *= nprocs; + //if (rank==0) printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); + //if (rank==0) printf("*********************************************************************\n"); } - -void ScaLBL_Poisson::getConvergenceLog(int timestep,double error){ - if ( rank == 0 ) { - fprintf(TIMELOG,"%i %.5g\n",timestep,error); +void ScaLBL_Poisson::getConvergenceLog(int timestep, double error) { + if (rank == 0) { + fprintf(TIMELOG, "%i %.5g\n", timestep, error); fflush(TIMELOG); } } -void ScaLBL_Poisson::SolveElectricPotentialAAodd(int timestep_from_Study){ - ScaLBL_Comm->SendD3Q7AA(fq, 0); //READ FROM NORMAL - ScaLBL_D3Q7_AAodd_Poisson_ElectricPotential(NeighborList, dvcMap, fq, Psi, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->RecvD3Q7AA(fq, 0); //WRITE INTO OPPOSITE +void ScaLBL_Poisson::SolveElectricPotentialAAodd(int timestep_from_Study) { + ScaLBL_Comm->SendD3Q7AA(fq, 0); //READ FROM NORMAL + ScaLBL_D3Q7_AAodd_Poisson_ElectricPotential( + NeighborList, dvcMap, fq, Psi, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->RecvD3Q7AA(fq, 0); //WRITE INTO OPPOSITE ScaLBL_Comm->Barrier(); - // Set boundary conditions - if (BoundaryConditionInlet > 0){ - switch (BoundaryConditionInlet){ - case 1: - ScaLBL_Comm->D3Q7_Poisson_Potential_BC_z(NeighborList, fq, Vin, timestep); - break; - case 2: - Vin = getBoundaryVoltagefromPeriodicBC(Vin0,freqIn,t0_In,Vin_Type,timestep_from_Study); - ScaLBL_Comm->D3Q7_Poisson_Potential_BC_z(NeighborList, fq, Vin, timestep); - break; + // Set boundary conditions + if (BoundaryConditionInlet > 0) { + switch (BoundaryConditionInlet) { + case 1: + ScaLBL_Comm->D3Q7_Poisson_Potential_BC_z(NeighborList, fq, Vin, + timestep); + break; + case 2: + Vin = getBoundaryVoltagefromPeriodicBC( + Vin0, freqIn, t0_In, Vin_Type, timestep_from_Study); + ScaLBL_Comm->D3Q7_Poisson_Potential_BC_z(NeighborList, fq, Vin, + timestep); + break; } - } - if (BoundaryConditionOutlet > 0){ - switch (BoundaryConditionOutlet){ - case 1: - ScaLBL_Comm->D3Q7_Poisson_Potential_BC_Z(NeighborList, fq, Vout, timestep); - break; - case 2: - Vout = getBoundaryVoltagefromPeriodicBC(Vout0,freqOut,t0_Out,Vout_Type,timestep_from_Study); - ScaLBL_Comm->D3Q7_Poisson_Potential_BC_Z(NeighborList, fq, Vout, timestep); - break; + } + if (BoundaryConditionOutlet > 0) { + switch (BoundaryConditionOutlet) { + case 1: + ScaLBL_Comm->D3Q7_Poisson_Potential_BC_Z(NeighborList, fq, Vout, + timestep); + break; + case 2: + Vout = getBoundaryVoltagefromPeriodicBC( + Vout0, freqOut, t0_Out, Vout_Type, timestep_from_Study); + ScaLBL_Comm->D3Q7_Poisson_Potential_BC_Z(NeighborList, fq, Vout, + timestep); + break; } - } + } //-------------------------// - ScaLBL_D3Q7_AAodd_Poisson_ElectricPotential(NeighborList, dvcMap, fq, Psi, 0, ScaLBL_Comm->LastExterior(), Np); + ScaLBL_D3Q7_AAodd_Poisson_ElectricPotential( + NeighborList, dvcMap, fq, Psi, 0, ScaLBL_Comm->LastExterior(), Np); } -void ScaLBL_Poisson::SolveElectricPotentialAAeven(int timestep_from_Study){ - ScaLBL_Comm->SendD3Q7AA(fq, 0); //READ FORM NORMAL - ScaLBL_D3Q7_AAeven_Poisson_ElectricPotential(dvcMap, fq, Psi, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->RecvD3Q7AA(fq, 0); //WRITE INTO OPPOSITE +void ScaLBL_Poisson::SolveElectricPotentialAAeven(int timestep_from_Study) { + ScaLBL_Comm->SendD3Q7AA(fq, 0); //READ FORM NORMAL + ScaLBL_D3Q7_AAeven_Poisson_ElectricPotential( + dvcMap, fq, Psi, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->RecvD3Q7AA(fq, 0); //WRITE INTO OPPOSITE ScaLBL_Comm->Barrier(); - // Set boundary conditions - if (BoundaryConditionInlet > 0){ - switch (BoundaryConditionInlet){ - case 1: - ScaLBL_Comm->D3Q7_Poisson_Potential_BC_z(NeighborList, fq, Vin, timestep); - break; - case 2: - Vin = getBoundaryVoltagefromPeriodicBC(Vin0,freqIn,t0_In,Vin_Type,timestep_from_Study); - ScaLBL_Comm->D3Q7_Poisson_Potential_BC_z(NeighborList, fq, Vin, timestep); - break; + // Set boundary conditions + if (BoundaryConditionInlet > 0) { + switch (BoundaryConditionInlet) { + case 1: + ScaLBL_Comm->D3Q7_Poisson_Potential_BC_z(NeighborList, fq, Vin, + timestep); + break; + case 2: + Vin = getBoundaryVoltagefromPeriodicBC( + Vin0, freqIn, t0_In, Vin_Type, timestep_from_Study); + ScaLBL_Comm->D3Q7_Poisson_Potential_BC_z(NeighborList, fq, Vin, + timestep); + break; } - } - if (BoundaryConditionOutlet > 0){ - switch (BoundaryConditionOutlet){ - case 1: - ScaLBL_Comm->D3Q7_Poisson_Potential_BC_Z(NeighborList, fq, Vout, timestep); - break; - case 2: - Vout = getBoundaryVoltagefromPeriodicBC(Vout0,freqOut,t0_Out,Vout_Type,timestep_from_Study); - ScaLBL_Comm->D3Q7_Poisson_Potential_BC_Z(NeighborList, fq, Vout, timestep); - break; + } + if (BoundaryConditionOutlet > 0) { + switch (BoundaryConditionOutlet) { + case 1: + ScaLBL_Comm->D3Q7_Poisson_Potential_BC_Z(NeighborList, fq, Vout, + timestep); + break; + case 2: + Vout = getBoundaryVoltagefromPeriodicBC( + Vout0, freqOut, t0_Out, Vout_Type, timestep_from_Study); + ScaLBL_Comm->D3Q7_Poisson_Potential_BC_Z(NeighborList, fq, Vout, + timestep); + break; } - } + } //-------------------------// - ScaLBL_D3Q7_AAeven_Poisson_ElectricPotential(dvcMap, fq, Psi, 0, ScaLBL_Comm->LastExterior(), Np); + ScaLBL_D3Q7_AAeven_Poisson_ElectricPotential( + dvcMap, fq, Psi, 0, ScaLBL_Comm->LastExterior(), Np); } -void ScaLBL_Poisson::SolvePoissonAAodd(double *ChargeDensity){ - ScaLBL_D3Q7_AAodd_Poisson(NeighborList, dvcMap, fq, ChargeDensity, Psi, ElectricField, tau, epsilon_LB, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_D3Q7_AAodd_Poisson(NeighborList, dvcMap, fq, ChargeDensity, Psi, ElectricField, tau, epsilon_LB, 0, ScaLBL_Comm->LastExterior(), Np); - if (BoundaryConditionSolid==1){ - ScaLBL_Comm->SolidDirichletD3Q7(fq, Psi); - } - else if (BoundaryConditionSolid==2){ - ScaLBL_Comm->SolidNeumannD3Q7(fq, Psi); +void ScaLBL_Poisson::SolvePoissonAAodd(double *ChargeDensity) { + ScaLBL_D3Q7_AAodd_Poisson(NeighborList, dvcMap, fq, ChargeDensity, Psi, + ElectricField, tau, epsilon_LB, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_D3Q7_AAodd_Poisson(NeighborList, dvcMap, fq, ChargeDensity, Psi, + ElectricField, tau, epsilon_LB, 0, + ScaLBL_Comm->LastExterior(), Np); + if (BoundaryConditionSolid == 1) { + ScaLBL_Comm->SolidDirichletD3Q7(fq, Psi); + } else if (BoundaryConditionSolid == 2) { + ScaLBL_Comm->SolidNeumannD3Q7(fq, Psi); } } -void ScaLBL_Poisson::SolvePoissonAAeven(double *ChargeDensity){ - ScaLBL_D3Q7_AAeven_Poisson(dvcMap, fq, ChargeDensity, Psi, ElectricField, tau, epsilon_LB, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_D3Q7_AAeven_Poisson(dvcMap, fq, ChargeDensity, Psi, ElectricField, tau, epsilon_LB, 0, ScaLBL_Comm->LastExterior(), Np); - if (BoundaryConditionSolid==1){ - ScaLBL_Comm->SolidDirichletD3Q7(fq, Psi); - } - else if (BoundaryConditionSolid==2){ - ScaLBL_Comm->SolidNeumannD3Q7(fq, Psi); +void ScaLBL_Poisson::SolvePoissonAAeven(double *ChargeDensity) { + ScaLBL_D3Q7_AAeven_Poisson(dvcMap, fq, ChargeDensity, Psi, ElectricField, + tau, epsilon_LB, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_D3Q7_AAeven_Poisson(dvcMap, fq, ChargeDensity, Psi, ElectricField, + tau, epsilon_LB, 0, ScaLBL_Comm->LastExterior(), + Np); + if (BoundaryConditionSolid == 1) { + ScaLBL_Comm->SolidDirichletD3Q7(fq, Psi); + } else if (BoundaryConditionSolid == 2) { + ScaLBL_Comm->SolidNeumannD3Q7(fq, Psi); } } -void ScaLBL_Poisson::DummyChargeDensity(){ +void ScaLBL_Poisson::DummyChargeDensity() { double *ChargeDensity_host; ChargeDensity_host = new double[Np]; - for (int k=0; kBarrier(); - delete [] ChargeDensity_host; + ScaLBL_AllocateDeviceMemory((void **)&ChargeDensityDummy, + sizeof(double) * Np); + ScaLBL_CopyToDevice(ChargeDensityDummy, ChargeDensity_host, + sizeof(double) * Np); + ScaLBL_Comm->Barrier(); + delete[] ChargeDensity_host; } -void ScaLBL_Poisson::getElectricPotential_debug(int timestep){ +void ScaLBL_Poisson::getElectricPotential_debug(int timestep) { //This function write out decomposed data - DoubleArray PhaseField(Nx,Ny,Nz); - //ScaLBL_Comm->RegularLayout(Map,Psi,PhaseField); - ScaLBL_CopyToHost(PhaseField.data(),Psi,sizeof(double)*Nx*Ny*Nz); + DoubleArray PhaseField(Nx, Ny, Nz); + //ScaLBL_Comm->RegularLayout(Map,Psi,PhaseField); + ScaLBL_CopyToHost(PhaseField.data(), Psi, sizeof(double) * Nx * Ny * Nz); //ScaLBL_Comm->Barrier(); comm.barrier(); FILE *OUTFILE; - sprintf(LocalRankFilename,"Electric_Potential_Time_%i.%05i.raw",timestep,rank); - OUTFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,OUTFILE); + sprintf(LocalRankFilename, "Electric_Potential_Time_%i.%05i.raw", timestep, + rank); + OUTFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, OUTFILE); fclose(OUTFILE); } -void ScaLBL_Poisson::getElectricPotential(DoubleArray &ReturnValues){ +void ScaLBL_Poisson::getElectricPotential(DoubleArray &ReturnValues) { //This function wirte out the data in a normal layout (by aggregating all decomposed domains) - //ScaLBL_Comm->RegularLayout(Map,Psi,PhaseField); - ScaLBL_CopyToHost(ReturnValues.data(),Psi,sizeof(double)*Nx*Ny*Nz); + //ScaLBL_Comm->RegularLayout(Map,Psi,PhaseField); + ScaLBL_CopyToHost(ReturnValues.data(), Psi, sizeof(double) * Nx * Ny * Nz); } -void ScaLBL_Poisson::getElectricField(DoubleArray &Values_x, DoubleArray &Values_y, DoubleArray &Values_z){ +void ScaLBL_Poisson::getElectricField(DoubleArray &Values_x, + DoubleArray &Values_y, + DoubleArray &Values_z) { - ScaLBL_Comm->RegularLayout(Map,&ElectricField[0*Np],Values_x); - ElectricField_LB_to_Phys(Values_x); - ScaLBL_Comm->Barrier(); comm.barrier(); + ScaLBL_Comm->RegularLayout(Map, &ElectricField[0 * Np], Values_x); + ElectricField_LB_to_Phys(Values_x); + ScaLBL_Comm->Barrier(); + comm.barrier(); - ScaLBL_Comm->RegularLayout(Map,&ElectricField[1*Np],Values_y); - ElectricField_LB_to_Phys(Values_y); - ScaLBL_Comm->Barrier(); comm.barrier(); - - ScaLBL_Comm->RegularLayout(Map,&ElectricField[2*Np],Values_z); - ElectricField_LB_to_Phys(Values_z); - ScaLBL_Comm->Barrier(); comm.barrier(); + ScaLBL_Comm->RegularLayout(Map, &ElectricField[1 * Np], Values_y); + ElectricField_LB_to_Phys(Values_y); + ScaLBL_Comm->Barrier(); + comm.barrier(); + ScaLBL_Comm->RegularLayout(Map, &ElectricField[2 * Np], Values_z); + ElectricField_LB_to_Phys(Values_z); + ScaLBL_Comm->Barrier(); + comm.barrier(); } -void ScaLBL_Poisson::getElectricField_debug(int timestep){ +void ScaLBL_Poisson::getElectricField_debug(int timestep) { - //ScaLBL_D3Q7_Poisson_getElectricField(fq,ElectricField,tau,Np); - //ScaLBL_Comm->Barrier(); comm.barrier(); + //ScaLBL_D3Q7_Poisson_getElectricField(fq,ElectricField,tau,Np); + //ScaLBL_Comm->Barrier(); comm.barrier(); - DoubleArray PhaseField(Nx,Ny,Nz); - ScaLBL_Comm->RegularLayout(Map,&ElectricField[0*Np],PhaseField); - ElectricField_LB_to_Phys(PhaseField); - FILE *EX; - sprintf(LocalRankFilename,"ElectricField_X_Time_%i.%05i.raw",timestep,rank); - EX = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,EX); - fclose(EX); + DoubleArray PhaseField(Nx, Ny, Nz); + ScaLBL_Comm->RegularLayout(Map, &ElectricField[0 * Np], PhaseField); + ElectricField_LB_to_Phys(PhaseField); + FILE *EX; + sprintf(LocalRankFilename, "ElectricField_X_Time_%i.%05i.raw", timestep, + rank); + EX = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, EX); + fclose(EX); - ScaLBL_Comm->RegularLayout(Map,&ElectricField[1*Np],PhaseField); - ElectricField_LB_to_Phys(PhaseField); - FILE *EY; - sprintf(LocalRankFilename,"ElectricField_Y_Time_%i.%05i.raw",timestep,rank); - EY = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,EY); - fclose(EY); + ScaLBL_Comm->RegularLayout(Map, &ElectricField[1 * Np], PhaseField); + ElectricField_LB_to_Phys(PhaseField); + FILE *EY; + sprintf(LocalRankFilename, "ElectricField_Y_Time_%i.%05i.raw", timestep, + rank); + EY = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, EY); + fclose(EY); - ScaLBL_Comm->RegularLayout(Map,&ElectricField[2*Np],PhaseField); - ElectricField_LB_to_Phys(PhaseField); - FILE *EZ; - sprintf(LocalRankFilename,"ElectricField_Z_Time_%i.%05i.raw",timestep,rank); - EZ = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,EZ); - fclose(EZ); + ScaLBL_Comm->RegularLayout(Map, &ElectricField[2 * Np], PhaseField); + ElectricField_LB_to_Phys(PhaseField); + FILE *EZ; + sprintf(LocalRankFilename, "ElectricField_Z_Time_%i.%05i.raw", timestep, + rank); + EZ = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, EZ); + fclose(EZ); } -void ScaLBL_Poisson::ElectricField_LB_to_Phys(DoubleArray &Efield_reg){ - for (int k=0;k db0); - void SetDomain(); - void ReadInput(); - void Create(); - void Initialize(double time_conv_from_Study); - void Run(double *ChargeDensity,int timestep_from_Study); + ScaLBL_Poisson(int RANK, int NP, const Utilities::MPI &COMM); + ~ScaLBL_Poisson(); + + // functions in they should be run + void ReadParams(string filename); + void ReadParams(std::shared_ptr db0); + void SetDomain(); + void ReadInput(); + void Create(); + void Initialize(double time_conv_from_Study); + void Run(double *ChargeDensity, int timestep_from_Study); void getElectricPotential(DoubleArray &ReturnValues); void getElectricPotential_debug(int timestep); - void getElectricField(DoubleArray &Values_x, DoubleArray &Values_y, DoubleArray &Values_z); + void getElectricField(DoubleArray &Values_x, DoubleArray &Values_y, + DoubleArray &Values_z); void getElectricField_debug(int timestep); - void DummyChargeDensity();//for debugging + void DummyChargeDensity(); //for debugging - //bool Restart,pBC; - int timestep,timestepMax; + //bool Restart,pBC; + int timestep, timestepMax; int analysis_interval; - int BoundaryConditionInlet; - int BoundaryConditionOutlet; + int BoundaryConditionInlet; + int BoundaryConditionOutlet; int BoundaryConditionSolid; - double tau; - double tolerance; + double tau; + double tolerance; std::string tolerance_method; double k2_inv; - double epsilon0,epsilon0_LB,epsilonR,epsilon_LB; + double epsilon0, epsilon0_LB, epsilonR, epsilon_LB; double Vin, Vout; - double chargeDen_dummy;//for debugging + double chargeDen_dummy; //for debugging bool WriteLog; - double Vin0,freqIn,t0_In,Vin_Type; - double Vout0,freqOut,t0_Out,Vout_Type; - bool TestPeriodic; - double TestPeriodicTime;//unit: [sec] - double TestPeriodicTimeConv; //unit [sec/lt] + double Vin0, freqIn, t0_In, Vin_Type; + double Vout0, freqOut, t0_Out, Vout_Type; + bool TestPeriodic; + double TestPeriodicTime; //unit: [sec] + double TestPeriodicTimeConv; //unit [sec/lt] double TestPeriodicSaveInterval; //unit [sec] - - int Nx,Ny,Nz,N,Np; - int rank,nprocx,nprocy,nprocz,nprocs; - double Lx,Ly,Lz; - double h;//image resolution - double time_conv;//phys to LB time converting factor; unit=[sec/lt] - std::shared_ptr Dm; // this domain is for analysis - std::shared_ptr Mask; // this domain is for lbm - std::shared_ptr ScaLBL_Comm; - std::shared_ptr ScaLBL_Comm_Regular; + int Nx, Ny, Nz, N, Np; + int rank, nprocx, nprocy, nprocz, nprocs; + double Lx, Ly, Lz; + double h; //image resolution + double time_conv; //phys to LB time converting factor; unit=[sec/lt] + + std::shared_ptr Dm; // this domain is for analysis + std::shared_ptr Mask; // this domain is for lbm + std::shared_ptr ScaLBL_Comm; + std::shared_ptr ScaLBL_Comm_Regular; // input database std::shared_ptr db; std::shared_ptr domain_db; @@ -85,24 +86,24 @@ public: int *dvcMap; //signed char *dvcID; double *fq; - double *Psi; + double *Psi; double *ElectricField; - double *ChargeDensityDummy;// for debugging + double *ChargeDensityDummy; // for debugging double *ResidualError; private: - Utilities::MPI comm; - - FILE *TIMELOG; + Utilities::MPI comm; - // filenames + FILE *TIMELOG; + + // filenames char LocalRankString[8]; char LocalRankFilename[40]; char LocalRestartFile[40]; char OutputFilename[200]; - + //int rank,nprocs; - void LoadParams(std::shared_ptr db0); + void LoadParams(std::shared_ptr db0); void AssignSolidBoundary(double *poisson_solid); void Potential_Init(double *psi_init); void ElectricField_LB_to_Phys(DoubleArray &Efield_reg); @@ -111,8 +112,8 @@ private: //void SolveElectricField(); void SolvePoissonAAodd(double *ChargeDensity); void SolvePoissonAAeven(double *ChargeDensity); - void getConvergenceLog(int timestep,double error); - double getBoundaryVoltagefromPeriodicBC(double V0,double freq,double t0,int V_type,int time_step); - + void getConvergenceLog(int timestep, double error); + double getBoundaryVoltagefromPeriodicBC(double V0, double freq, double t0, + int V_type, int time_step); }; #endif diff --git a/models/StokesModel.cpp b/models/StokesModel.cpp index 8385cbff..1e395ba9 100644 --- a/models/StokesModel.cpp +++ b/models/StokesModel.cpp @@ -5,650 +5,729 @@ #include "analysis/distance.h" #include "common/ReadMicroCT.h" -ScaLBL_StokesModel::ScaLBL_StokesModel(int RANK, int NP, const Utilities::MPI& COMM): -rank(RANK), nprocs(NP), Restart(0),timestep(0),timestepMax(0),tau(0), -Fx(0),Fy(0),Fz(0),flux(0),din(0),dout(0),mu(0),h(0),nu_phys(0),rho_phys(0),rho0(0),den_scale(0),time_conv(0),tolerance(0), -epsilon0(0),epsilon0_LB(0),epsilonR(0),epsilon_LB(0),UseSlippingVelBC(0), -Nx(0),Ny(0),Nz(0),N(0),Np(0),nprocx(0),nprocy(0),nprocz(0),BoundaryCondition(0),Lx(0),Ly(0),Lz(0),comm(COMM) -{ +ScaLBL_StokesModel::ScaLBL_StokesModel(int RANK, int NP, + const Utilities::MPI &COMM) + : rank(RANK), nprocs(NP), Restart(0), timestep(0), timestepMax(0), tau(0), + Fx(0), Fy(0), Fz(0), flux(0), din(0), dout(0), mu(0), h(0), nu_phys(0), + rho_phys(0), rho0(0), den_scale(0), time_conv(0), tolerance(0), + epsilon0(0), epsilon0_LB(0), epsilonR(0), epsilon_LB(0), + UseSlippingVelBC(0), Nx(0), Ny(0), Nz(0), N(0), Np(0), nprocx(0), + nprocy(0), nprocz(0), BoundaryCondition(0), Lx(0), Ly(0), Lz(0), + comm(COMM) {} +ScaLBL_StokesModel::~ScaLBL_StokesModel() {} -} -ScaLBL_StokesModel::~ScaLBL_StokesModel(){ +void ScaLBL_StokesModel::ReadParams(string filename, int num_iter) { + // read the input database + db = std::make_shared(filename); + domain_db = db->getDatabase("Domain"); + stokes_db = db->getDatabase("Stokes"); -} - -void ScaLBL_StokesModel::ReadParams(string filename,int num_iter){ - // read the input database - db = std::make_shared( filename ); - domain_db = db->getDatabase( "Domain" ); - stokes_db = db->getDatabase( "Stokes" ); - //------ Load number of iteration from multiphysics controller ------// - timestepMax = num_iter; + timestepMax = num_iter; //-------------------------------------------------------------------// - //---------------------- Default model parameters --------------------------// + //---------------------- Default model parameters --------------------------// rho_phys = 1000.0; //by default use water density; unit [kg/m^3] - nu_phys = 1.004e-6;//by default use water kinematic viscosity at 20C; unit [m^2/sec] - h = 1.0;//image resolution;[um] - tau = 1.0; - mu = (tau-0.5)/3.0;//LB kinematic viscosity;unit [lu^2/lt] - time_conv = h*h*mu/nu_phys;//time conversion factor from physical to LB unit; [sec/lt] - rho0 = 1.0;//LB density - den_scale = rho_phys/rho0*(h*h*h*1.0e-18);//scale factor for density - tolerance = 1.0e-8; - Fx = Fy = 0.0; - Fz = 1.0e-5; + nu_phys = + 1.004e-6; //by default use water kinematic viscosity at 20C; unit [m^2/sec] + h = 1.0; //image resolution;[um] + tau = 1.0; + mu = (tau - 0.5) / 3.0; //LB kinematic viscosity;unit [lu^2/lt] + time_conv = + h * h * mu / + nu_phys; //time conversion factor from physical to LB unit; [sec/lt] + rho0 = 1.0; //LB density + den_scale = + rho_phys / rho0 * (h * h * h * 1.0e-18); //scale factor for density + tolerance = 1.0e-8; + Fx = Fy = 0.0; + Fz = 1.0e-5; //Stokes solver also needs the following parameters for slipping velocity BC - epsilon0 = 8.85e-12;//electric permittivity of vaccum; unit:[C/(V*m)] - epsilon0_LB = epsilon0*(h*1.0e-6);//unit:[C/(V*lu)] - epsilonR = 78.4;//default dielectric constant of water - epsilon_LB = epsilon0_LB*epsilonR;//electric permittivity + epsilon0 = 8.85e-12; //electric permittivity of vaccum; unit:[C/(V*m)] + epsilon0_LB = epsilon0 * (h * 1.0e-6); //unit:[C/(V*lu)] + epsilonR = 78.4; //default dielectric constant of water + epsilon_LB = epsilon0_LB * epsilonR; //electric permittivity UseSlippingVelBC = false; //--------------------------------------------------------------------------// - // Read domain parameters - if (domain_db->keyExists( "voxel_length" )){//default unit: um/lu - h = domain_db->getScalar( "voxel_length" ); - } + // Read domain parameters + if (domain_db->keyExists("voxel_length")) { //default unit: um/lu + h = domain_db->getScalar("voxel_length"); + } - // Single-fluid Navier-Stokes Model parameters - //if (stokes_db->keyExists( "timestepMax" )){ - // timestepMax = stokes_db->getScalar( "timestepMax" ); - //} + // Single-fluid Navier-Stokes Model parameters + //if (stokes_db->keyExists( "timestepMax" )){ + // timestepMax = stokes_db->getScalar( "timestepMax" ); + //} BoundaryCondition = 0; - if (stokes_db->keyExists( "BC" )){ - BoundaryCondition = stokes_db->getScalar( "BC" ); - - } - if (stokes_db->keyExists( "tolerance" )){ - tolerance = stokes_db->getScalar( "tolerance" ); - } - if (stokes_db->keyExists( "tau" )){ - tau = stokes_db->getScalar( "tau" ); - } - if (stokes_db->keyExists( "rho0" )){ - rho0 = stokes_db->getScalar( "rho0" ); - } - if (stokes_db->keyExists( "nu_phys" )){ - nu_phys = stokes_db->getScalar( "nu_phys" ); - } - if (stokes_db->keyExists( "rho_phys" )){ - rho_phys = stokes_db->getScalar( "rho_phys" ); - } - if (stokes_db->keyExists( "F" )){ - Fx = stokes_db->getVector( "F" )[0]; - Fy = stokes_db->getVector( "F" )[1]; - Fz = stokes_db->getVector( "F" )[2]; - } - if (stokes_db->keyExists( "Restart" )){ - Restart = stokes_db->getScalar( "Restart" ); - } - if (stokes_db->keyExists( "din" )){ - din = stokes_db->getScalar( "din" ); - } - if (stokes_db->keyExists( "dout" )){ - dout = stokes_db->getScalar( "dout" ); - } - if (stokes_db->keyExists( "flux" )){ - flux = stokes_db->getScalar( "flux" ); - } - if (stokes_db->keyExists( "UseElectroosmoticVelocityBC" )){ - UseSlippingVelBC = stokes_db->getScalar( "UseElectroosmoticVelocityBC" ); - } - if (stokes_db->keyExists( "epsilonR" )){ - epsilonR = stokes_db->getScalar( "epsilonR" ); - } + if (stokes_db->keyExists("BC")) { + BoundaryCondition = stokes_db->getScalar("BC"); + } + if (stokes_db->keyExists("tolerance")) { + tolerance = stokes_db->getScalar("tolerance"); + } + if (stokes_db->keyExists("tau")) { + tau = stokes_db->getScalar("tau"); + } + if (stokes_db->keyExists("rho0")) { + rho0 = stokes_db->getScalar("rho0"); + } + if (stokes_db->keyExists("nu_phys")) { + nu_phys = stokes_db->getScalar("nu_phys"); + } + if (stokes_db->keyExists("rho_phys")) { + rho_phys = stokes_db->getScalar("rho_phys"); + } + if (stokes_db->keyExists("F")) { + Fx = stokes_db->getVector("F")[0]; + Fy = stokes_db->getVector("F")[1]; + Fz = stokes_db->getVector("F")[2]; + } + if (stokes_db->keyExists("Restart")) { + Restart = stokes_db->getScalar("Restart"); + } + if (stokes_db->keyExists("din")) { + din = stokes_db->getScalar("din"); + } + if (stokes_db->keyExists("dout")) { + dout = stokes_db->getScalar("dout"); + } + if (stokes_db->keyExists("flux")) { + flux = stokes_db->getScalar("flux"); + } + if (stokes_db->keyExists("UseElectroosmoticVelocityBC")) { + UseSlippingVelBC = + stokes_db->getScalar("UseElectroosmoticVelocityBC"); + } + if (stokes_db->keyExists("epsilonR")) { + epsilonR = stokes_db->getScalar("epsilonR"); + } // Re-calculate model parameters due to parameter read - mu=(tau-0.5)/3.0; - time_conv = (h*h*1.0e-12)*mu/nu_phys;//time conversion factor from physical to LB unit; [sec/lt] - den_scale = rho_phys/rho0*(h*h*h*1.0e-18);//scale factor for density - epsilon0_LB = epsilon0*(h*1.0e-6);//unit:[C/(V*lu)] - epsilon_LB = epsilon0_LB*epsilonR;//electric permittivity + mu = (tau - 0.5) / 3.0; + time_conv = + (h * h * 1.0e-12) * mu / + nu_phys; //time conversion factor from physical to LB unit; [sec/lt] + den_scale = + rho_phys / rho0 * (h * h * h * 1.0e-18); //scale factor for density + epsilon0_LB = epsilon0 * (h * 1.0e-6); //unit:[C/(V*lu)] + epsilon_LB = epsilon0_LB * epsilonR; //electric permittivity } -void ScaLBL_StokesModel::ReadParams(string filename){ +void ScaLBL_StokesModel::ReadParams(string filename) { //NOTE the max time step is left unspecified + // read the input database + db = std::make_shared(filename); + domain_db = db->getDatabase("Domain"); + stokes_db = db->getDatabase("Stokes"); - // read the input database - db = std::make_shared( filename ); - domain_db = db->getDatabase( "Domain" ); - stokes_db = db->getDatabase( "Stokes" ); - - //---------------------- Default model parameters --------------------------// + //---------------------- Default model parameters --------------------------// rho_phys = 1000.0; //by default use water density; unit [kg/m^3] - nu_phys = 1.004e-6;//by default use water kinematic viscosity at 20C; unit [m^2/sec] - h = 1.0;//image resolution;[um] - tau = 1.0; - mu = (tau-0.5)/3.0;//LB kinematic viscosity;unit [lu^2/lt] - time_conv = h*h*mu/nu_phys;//time conversion factor from physical to LB unit; [sec/lt] - rho0 = 1.0;//LB density - den_scale = rho_phys/rho0*(h*h*h*1.0e-18);//scale factor for density - tolerance = 1.0e-8; - Fx = Fy = 0.0; - Fz = 1.0e-5; + nu_phys = + 1.004e-6; //by default use water kinematic viscosity at 20C; unit [m^2/sec] + h = 1.0; //image resolution;[um] + tau = 1.0; + mu = (tau - 0.5) / 3.0; //LB kinematic viscosity;unit [lu^2/lt] + time_conv = + h * h * mu / + nu_phys; //time conversion factor from physical to LB unit; [sec/lt] + rho0 = 1.0; //LB density + den_scale = + rho_phys / rho0 * (h * h * h * 1.0e-18); //scale factor for density + tolerance = 1.0e-8; + Fx = Fy = 0.0; + Fz = 1.0e-5; //Stokes solver also needs the following parameters for slipping velocity BC - epsilon0 = 8.85e-12;//electric permittivity of vaccum; unit:[C/(V*m)] - epsilon0_LB = epsilon0*(h*1.0e-6);//unit:[C/(V*lu)] - epsilonR = 78.4;//default dielectric constant of water - epsilon_LB = epsilon0_LB*epsilonR;//electric permittivity + epsilon0 = 8.85e-12; //electric permittivity of vaccum; unit:[C/(V*m)] + epsilon0_LB = epsilon0 * (h * 1.0e-6); //unit:[C/(V*lu)] + epsilonR = 78.4; //default dielectric constant of water + epsilon_LB = epsilon0_LB * epsilonR; //electric permittivity UseSlippingVelBC = false; //--------------------------------------------------------------------------// - // Read domain parameters - if (domain_db->keyExists( "voxel_length" )){//default unit: um/lu - h = domain_db->getScalar( "voxel_length" ); - } + // Read domain parameters + if (domain_db->keyExists("voxel_length")) { //default unit: um/lu + h = domain_db->getScalar("voxel_length"); + } - // Single-fluid Navier-Stokes Model parameters - //if (stokes_db->keyExists( "timestepMax" )){ - // timestepMax = stokes_db->getScalar( "timestepMax" ); - //} + // Single-fluid Navier-Stokes Model parameters + //if (stokes_db->keyExists( "timestepMax" )){ + // timestepMax = stokes_db->getScalar( "timestepMax" ); + //} BoundaryCondition = 0; - if (stokes_db->keyExists( "BC" )){ - BoundaryCondition = stokes_db->getScalar( "BC" ); - } - if (stokes_db->keyExists( "tolerance" )){ - tolerance = stokes_db->getScalar( "tolerance" ); - } - if (stokes_db->keyExists( "tau" )){ - tau = stokes_db->getScalar( "tau" ); - } - if (stokes_db->keyExists( "rho0" )){ - rho0 = stokes_db->getScalar( "rho0" ); - } - if (stokes_db->keyExists( "nu_phys" )){ - nu_phys = stokes_db->getScalar( "nu_phys" ); - } - if (stokes_db->keyExists( "rho_phys" )){ - rho_phys = stokes_db->getScalar( "rho_phys" ); - } - if (stokes_db->keyExists( "F" )){ - Fx = stokes_db->getVector( "F" )[0]; - Fy = stokes_db->getVector( "F" )[1]; - Fz = stokes_db->getVector( "F" )[2]; - } - if (stokes_db->keyExists( "Restart" )){ - Restart = stokes_db->getScalar( "Restart" ); - } - if (stokes_db->keyExists( "din" )){ - din = stokes_db->getScalar( "din" ); - } - if (stokes_db->keyExists( "dout" )){ - dout = stokes_db->getScalar( "dout" ); - } - if (stokes_db->keyExists( "flux" )){ - flux = stokes_db->getScalar( "flux" ); - } - if (stokes_db->keyExists( "UseElectroosmoticVelocityBC" )){ - UseSlippingVelBC = stokes_db->getScalar( "UseElectroosmoticVelocityBC" ); - } - if (stokes_db->keyExists( "epsilonR" )){ - epsilonR = stokes_db->getScalar( "epsilonR" ); - } + if (stokes_db->keyExists("BC")) { + BoundaryCondition = stokes_db->getScalar("BC"); + } + if (stokes_db->keyExists("tolerance")) { + tolerance = stokes_db->getScalar("tolerance"); + } + if (stokes_db->keyExists("tau")) { + tau = stokes_db->getScalar("tau"); + } + if (stokes_db->keyExists("rho0")) { + rho0 = stokes_db->getScalar("rho0"); + } + if (stokes_db->keyExists("nu_phys")) { + nu_phys = stokes_db->getScalar("nu_phys"); + } + if (stokes_db->keyExists("rho_phys")) { + rho_phys = stokes_db->getScalar("rho_phys"); + } + if (stokes_db->keyExists("F")) { + Fx = stokes_db->getVector("F")[0]; + Fy = stokes_db->getVector("F")[1]; + Fz = stokes_db->getVector("F")[2]; + } + if (stokes_db->keyExists("Restart")) { + Restart = stokes_db->getScalar("Restart"); + } + if (stokes_db->keyExists("din")) { + din = stokes_db->getScalar("din"); + } + if (stokes_db->keyExists("dout")) { + dout = stokes_db->getScalar("dout"); + } + if (stokes_db->keyExists("flux")) { + flux = stokes_db->getScalar("flux"); + } + if (stokes_db->keyExists("UseElectroosmoticVelocityBC")) { + UseSlippingVelBC = + stokes_db->getScalar("UseElectroosmoticVelocityBC"); + } + if (stokes_db->keyExists("epsilonR")) { + epsilonR = stokes_db->getScalar("epsilonR"); + } // Re-calculate model parameters due to parameter read - mu=(tau-0.5)/3.0; - time_conv = (h*h*1.0e-12)*mu/nu_phys;//time conversion factor from physical to LB unit; [sec/lt] - den_scale = rho_phys/rho0*(h*h*h*1.0e-18);//scale factor for density - epsilon0_LB = epsilon0*(h*1.0e-6);//unit:[C/(V*lu)] - epsilon_LB = epsilon0_LB*epsilonR;//electric permittivity + mu = (tau - 0.5) / 3.0; + time_conv = + (h * h * 1.0e-12) * mu / + nu_phys; //time conversion factor from physical to LB unit; [sec/lt] + den_scale = + rho_phys / rho0 * (h * h * h * 1.0e-18); //scale factor for density + epsilon0_LB = epsilon0 * (h * 1.0e-6); //unit:[C/(V*lu)] + epsilon_LB = epsilon0_LB * epsilonR; //electric permittivity } -void ScaLBL_StokesModel::SetDomain(){ - Dm = std::shared_ptr(new Domain(domain_db,comm)); // full domain for analysis - Mask = std::shared_ptr(new Domain(domain_db,comm)); // mask domain removes immobile phases +void ScaLBL_StokesModel::SetDomain() { + Dm = std::shared_ptr( + new Domain(domain_db, comm)); // full domain for analysis + Mask = std::shared_ptr( + new Domain(domain_db, comm)); // mask domain removes immobile phases - // domain parameters - Nx = Dm->Nx; - Ny = Dm->Ny; - Nz = Dm->Nz; - Lx = Dm->Lx; - Ly = Dm->Ly; - Lz = Dm->Lz; - - N = Nx*Ny*Nz; - Distance.resize(Nx,Ny,Nz); - Velocity_x.resize(Nx,Ny,Nz); - Velocity_y.resize(Nx,Ny,Nz); - Velocity_z.resize(Nx,Ny,Nz); - - for (int i=0; iid[i] = 1; // initialize this way - //Averages = std::shared_ptr ( new TwoPhase(Dm) ); // TwoPhase analysis object - comm.barrier(); - Dm->BoundaryCondition = BoundaryCondition; - Mask->BoundaryCondition = BoundaryCondition; - Dm->CommInit(); - comm.barrier(); - - rank = Dm->rank(); - nprocx = Dm->nprocx(); - nprocy = Dm->nprocy(); - nprocz = Dm->nprocz(); + // domain parameters + Nx = Dm->Nx; + Ny = Dm->Ny; + Nz = Dm->Nz; + Lx = Dm->Lx; + Ly = Dm->Ly; + Lz = Dm->Lz; + + N = Nx * Ny * Nz; + Distance.resize(Nx, Ny, Nz); + Velocity_x.resize(Nx, Ny, Nz); + Velocity_y.resize(Nx, Ny, Nz); + Velocity_z.resize(Nx, Ny, Nz); + + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = 1; // initialize this way + //Averages = std::shared_ptr ( new TwoPhase(Dm) ); // TwoPhase analysis object + comm.barrier(); + Dm->BoundaryCondition = BoundaryCondition; + Mask->BoundaryCondition = BoundaryCondition; + Dm->CommInit(); + comm.barrier(); + + rank = Dm->rank(); + nprocx = Dm->nprocx(); + nprocy = Dm->nprocy(); + nprocz = Dm->nprocz(); } -void ScaLBL_StokesModel::ReadInput(){ - - sprintf(LocalRankString,"%05d",Dm->rank()); - sprintf(LocalRankFilename,"%s%s","ID.",LocalRankString); - sprintf(LocalRestartFile,"%s%s","Restart.",LocalRankString); +void ScaLBL_StokesModel::ReadInput() { - - if (domain_db->keyExists( "Filename" )){ - auto Filename = domain_db->getScalar( "Filename" ); - Mask->Decomp(Filename); - } - else if (domain_db->keyExists( "GridFile" )){ - // Read the local domain data - auto input_id = readMicroCT( *domain_db, comm ); - // Fill the halo (assuming GCW of 1) - array size0 = { (int) input_id.size(0), (int) input_id.size(1), (int) input_id.size(2) }; - ArraySize size1 = { (size_t) Mask->Nx, (size_t) Mask->Ny, (size_t) Mask->Nz }; - ASSERT( (int) size1[0] == size0[0]+2 && (int) size1[1] == size0[1]+2 && (int) size1[2] == size0[2]+2 ); - fillHalo fill( comm, Mask->rank_info, size0, { 1, 1, 1 }, 0, 1 ); - Array id_view; - id_view.viewRaw( size1, Mask->id.data() ); - fill.copy( input_id, id_view ); - fill.fill( id_view ); - } - else{ - Mask->ReadIDs(); + sprintf(LocalRankString, "%05d", Dm->rank()); + sprintf(LocalRankFilename, "%s%s", "ID.", LocalRankString); + sprintf(LocalRestartFile, "%s%s", "Restart.", LocalRankString); + + if (domain_db->keyExists("Filename")) { + auto Filename = domain_db->getScalar("Filename"); + Mask->Decomp(Filename); + } else if (domain_db->keyExists("GridFile")) { + // Read the local domain data + auto input_id = readMicroCT(*domain_db, comm); + // Fill the halo (assuming GCW of 1) + array size0 = {(int)input_id.size(0), (int)input_id.size(1), + (int)input_id.size(2)}; + ArraySize size1 = {(size_t)Mask->Nx, (size_t)Mask->Ny, + (size_t)Mask->Nz}; + ASSERT((int)size1[0] == size0[0] + 2 && (int)size1[1] == size0[1] + 2 && + (int)size1[2] == size0[2] + 2); + fillHalo fill(comm, Mask->rank_info, size0, {1, 1, 1}, 0, + 1); + Array id_view; + id_view.viewRaw(size1, Mask->id.data()); + fill.copy(input_id, id_view); + fill.fill(id_view); + } else { + Mask->ReadIDs(); } // Generate the signed distance map - // Initialize the domain and communication - Array id_solid(Nx,Ny,Nz); - // Solve for the position of the solid phase - for (int k=0;kid[n] > 0) id_solid(i,j,k) = 1; - else id_solid(i,j,k) = 0; - } - } - } - // Initialize the signed distance function - for (int k=0;kSDs); - if (rank==0) printf("LB Single-Fluid Solver: initialized solid phase & converting to Signed Distance function \n"); - CalcDist(Distance,id_solid,*Dm); - if (rank == 0) cout << " Domain set." << endl; + // Initialize the domain and communication + Array id_solid(Nx, Ny, Nz); + // Solve for the position of the solid phase + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + // Initialize the solid phase + if (Mask->id[n] > 0) + id_solid(i, j, k) = 1; + else + id_solid(i, j, k) = 0; + } + } + } + // Initialize the signed distance function + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + // Initialize distance to +/- 1 + Distance(i, j, k) = 2.0 * double(id_solid(i, j, k)) - 1.0; + } + } + } + // MeanFilter(Averages->SDs); + if (rank == 0) + printf("LB Single-Fluid Solver: initialized solid phase & converting " + "to Signed Distance function \n"); + CalcDist(Distance, id_solid, *Dm); + if (rank == 0) + cout << " Domain set." << endl; } -void ScaLBL_StokesModel::AssignZetaPotentialSolid(double *zeta_potential_solid) -{ - size_t NLABELS=0; - signed char VALUE=0; - double AFFINITY=0.f; +void ScaLBL_StokesModel::AssignZetaPotentialSolid( + double *zeta_potential_solid) { + size_t NLABELS = 0; + signed char VALUE = 0; + double AFFINITY = 0.f; - auto LabelList = stokes_db->getVector( "SolidLabels" ); - auto AffinityList = stokes_db->getVector( "ZetaPotentialSolidList" ); + auto LabelList = stokes_db->getVector("SolidLabels"); + auto AffinityList = stokes_db->getVector("ZetaPotentialSolidList"); - NLABELS=LabelList.size(); - if (NLABELS != AffinityList.size()){ - ERROR("Error: LB Single-Fluid Solver: SolidLabels and ZetaPotentialSolidList must be the same length! \n"); - } + NLABELS = LabelList.size(); + if (NLABELS != AffinityList.size()) { + ERROR("Error: LB Single-Fluid Solver: SolidLabels and " + "ZetaPotentialSolidList must be the same length! \n"); + } - double *label_count; - double *label_count_global; - label_count = new double [NLABELS]; - label_count_global = new double [NLABELS]; + double *label_count; + double *label_count_global; + label_count = new double[NLABELS]; + label_count_global = new double[NLABELS]; - for (size_t idx=0; idxid[n]; - AFFINITY=0.f; - // Assign the affinity from the paired list - for (unsigned int idx=0; idx < NLABELS; idx++){ - if (VALUE == LabelList[idx]){ - AFFINITY=AffinityList[idx];//no need to convert unit for zeta potential (i.e. volt) - label_count[idx] += 1.0; - idx = NLABELS; - } - } - zeta_potential_solid[n] = AFFINITY; - } - } - } + // Assign the labels + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + VALUE = Mask->id[n]; + AFFINITY = 0.f; + // Assign the affinity from the paired list + for (unsigned int idx = 0; idx < NLABELS; idx++) { + if (VALUE == LabelList[idx]) { + AFFINITY = AffinityList + [idx]; //no need to convert unit for zeta potential (i.e. volt) + label_count[idx] += 1.0; + idx = NLABELS; + } + } + zeta_potential_solid[n] = AFFINITY; + } + } + } - for (size_t idx=0; idxComm.sumReduce( label_count[idx]); + for (size_t idx = 0; idx < NLABELS; idx++) + label_count_global[idx] = Dm->Comm.sumReduce(label_count[idx]); - if (rank==0){ - printf("LB Single-Fluid Solver: number of solid labels: %lu \n",NLABELS); - for (unsigned int idx=0; idxid[nn]); + int nn = idk * Nx * Ny + idj * Nx + idi; + double vec_x = double(ii - 1); + double vec_y = double(jj - 1); + double vec_z = double(kk - 1); + double GWNS = double(Mask->id[nn]); //Since the solid unit normal vector is wanted, treat //wet node as 0.0 and solid node as 1.0 - GWNS = (GWNS>0.0) ? 0.0:1.0; - phi_x += GWNS*weight*vec_x; - phi_y += GWNS*weight*vec_y; - phi_z += GWNS*weight*vec_z; - } - } - } + GWNS = (GWNS > 0.0) ? 0.0 : 1.0; + phi_x += GWNS * weight * vec_x; + phi_y += GWNS * weight * vec_y; + phi_z += GWNS * weight * vec_z; + } + } + } //solid_grad normalization - double phi_mag=sqrt(phi_x*phi_x+phi_y*phi_y+phi_z*phi_z); - if (phi_mag==0.0) phi_mag=1.0; - solid_grad[idx+0*Np] = phi_x/phi_mag; - solid_grad[idx+1*Np] = phi_y/phi_mag; - solid_grad[idx+2*Np] = phi_z/phi_mag; - } - } - } - } -} - -void ScaLBL_StokesModel::Create(){ - /* - * This function creates the variables needed to run a LBM - */ - int rank=Mask->rank(); - //......................................................... - // Initialize communication structures in averaging domain - for (int i=0; iid[i] = Mask->id[i]; - Mask->CommInit(); - Np=Mask->PoreCount(); - //........................................................................... - if (rank==0) printf ("LB Single-Fluid Solver: Create ScaLBL_Communicator \n"); - // Create a communicator for the device (will use optimized layout) - // ScaLBL_Communicator ScaLBL_Comm(Mask); // original - ScaLBL_Comm = std::shared_ptr(new ScaLBL_Communicator(Mask)); - - int Npad=(Np/16 + 2)*16; - if (rank==0) printf ("LB Single-Fluid Solver: Set up memory efficient layout \n"); - Map.resize(Nx,Ny,Nz); Map.fill(-2); - auto neighborList= new int[18*Npad]; - Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map,neighborList,Mask->id.data(),Np,1); - comm.barrier(); - - //........................................................................... - // MAIN VARIABLES ALLOCATED HERE - //........................................................................... - // LBM variables - if (rank==0) printf ("LB Single-Fluid Solver: Allocating distributions \n"); - //......................device distributions................................. - int dist_mem_size = Np*sizeof(double); - int neighborSize=18*(Np*sizeof(int)); - //........................................................................... - ScaLBL_AllocateDeviceMemory((void **) &NeighborList, neighborSize); - ScaLBL_AllocateDeviceMemory((void **) &fq, 19*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Pressure, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &Velocity, 3*sizeof(double)*Np); - //........................................................................... - // Update GPU data structures - if (rank==0) printf ("LB Single-Fluid Solver: Setting up device map and neighbor list \n"); - // copy the neighbor list - ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); - comm.barrier(); - - if (UseSlippingVelBC==true){ - ScaLBL_Comm->SetupBounceBackList(Map, Mask->id.data(), Np,1); - comm.barrier(); - - //For slipping velocity BC, need zeta potential and solid unit normal vector - ScaLBL_AllocateDeviceMemory((void **) &ZetaPotentialSolid, sizeof(double)*Nx*Ny*Nz); - ScaLBL_AllocateDeviceMemory((void **) &SolidGrad, sizeof(double)*3*Np); //unit normal vector of solid nodes - - double *ZetaPotentialSolid_host; - ZetaPotentialSolid_host = new double[Nx*Ny*Nz]; - AssignZetaPotentialSolid(ZetaPotentialSolid_host); - double *SolidGrad_host; - SolidGrad_host = new double[3*Np]; - AssignSolidGrad(SolidGrad_host); - ScaLBL_CopyToDevice(ZetaPotentialSolid, ZetaPotentialSolid_host, Nx*Ny*Nz*sizeof(double)); - ScaLBL_CopyToDevice(SolidGrad, SolidGrad_host, 3*Np*sizeof(double)); - ScaLBL_Comm->Barrier(); - delete [] ZetaPotentialSolid_host; - delete [] SolidGrad_host; - } -} - -void ScaLBL_StokesModel::Initialize(){ - /* - * This function initializes model - */ - if (rank==0) printf("LB Single-Fluid Solver: Initializing distributions \n"); - if (rank==0) printf("****************************************************************\n"); - ScaLBL_D3Q19_Init(fq, Np); - - if (rank==0) printf("*****************************************************\n"); - if (rank==0) printf("LB Single-Fluid Navier-Stokes Solver: \n"); - if (rank==0) printf(" Time conversion factor: %.5g [sec/lt]\n", time_conv); - if (rank==0) printf(" Internal iteration: %i [lt]\n", timestepMax); - if (rank==0) printf("*****************************************************\n"); -} - -void ScaLBL_StokesModel::Run_Lite(double *ChargeDensity, double *ElectricField){ - double rlx_setA=1.0/tau; - double rlx_setB = 8.f*(2.f-rlx_setA)/(8.f-rlx_setA); - timestep = 0; - while (timestep < timestepMax) { - //************************************************************************/ - //**************ODD TIMESTEP*************// - timestep++; - ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL - ScaLBL_D3Q19_AAodd_StokesMRT(NeighborList, fq, Velocity, ChargeDensity, ElectricField, rlx_setA, rlx_setB, Fx, Fy, Fz,rho0,den_scale,h,time_conv, - ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - // Set boundary conditions - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); - } - ScaLBL_D3Q19_AAodd_StokesMRT(NeighborList, fq, Velocity, ChargeDensity, ElectricField, rlx_setA, rlx_setB, Fx, Fy, Fz,rho0,den_scale,h,time_conv, - 0, ScaLBL_Comm->LastExterior(), Np); - - if (UseSlippingVelBC==true){ - ScaLBL_Comm->SolidSlippingVelocityBCD3Q19(fq, ZetaPotentialSolid, ElectricField, SolidGrad, - epsilon_LB, 1.0/rlx_setA, rho0, den_scale, h, time_conv); - } - ScaLBL_Comm->Barrier(); comm.barrier(); - - //**************EVEN TIMESTEP*************// - timestep++; - ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL - ScaLBL_D3Q19_AAeven_StokesMRT(fq, Velocity, ChargeDensity, ElectricField, rlx_setA, rlx_setB, Fx, Fy, Fz,rho0,den_scale,h,time_conv, - ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - // Set boundary conditions - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); - } - ScaLBL_D3Q19_AAeven_StokesMRT(fq, Velocity, ChargeDensity, ElectricField, rlx_setA, rlx_setB, Fx, Fy, Fz,rho0,den_scale,h,time_conv, - 0, ScaLBL_Comm->LastExterior(), Np); - if (UseSlippingVelBC==true){ - ScaLBL_Comm->SolidSlippingVelocityBCD3Q19(fq, ZetaPotentialSolid, ElectricField, SolidGrad, - epsilon_LB, 1.0/rlx_setA, rho0, den_scale, h, time_conv); - } - ScaLBL_Comm->Barrier(); comm.barrier(); - //************************************************************************/ - } -} - -void ScaLBL_StokesModel::getVelocity(DoubleArray &Vel_x, DoubleArray &Vel_y, DoubleArray &Vel_z){ - //get velocity in physical unit [m/sec] - ScaLBL_D3Q19_Momentum(fq, Velocity, Np); - ScaLBL_Comm->Barrier(); comm.barrier(); - - ScaLBL_Comm->RegularLayout(Map,&Velocity[0],Vel_x); - Velocity_LB_to_Phys(Vel_x); - ScaLBL_Comm->Barrier(); comm.barrier(); - - ScaLBL_Comm->RegularLayout(Map,&Velocity[Np],Vel_y); - Velocity_LB_to_Phys(Vel_y); - ScaLBL_Comm->Barrier(); comm.barrier(); - - ScaLBL_Comm->RegularLayout(Map,&Velocity[2*Np],Vel_z); - Velocity_LB_to_Phys(Vel_z); - ScaLBL_Comm->Barrier(); comm.barrier(); -} - -void ScaLBL_StokesModel::getVelocity_debug(int timestep){ - //get velocity in physical unit [m/sec] - ScaLBL_D3Q19_Momentum(fq, Velocity, Np); - ScaLBL_Comm->Barrier(); comm.barrier(); - - DoubleArray PhaseField(Nx,Ny,Nz); - ScaLBL_Comm->RegularLayout(Map,&Velocity[0],PhaseField); - Velocity_LB_to_Phys(PhaseField); - FILE *VELX_FILE; - sprintf(LocalRankFilename,"Velocity_X_Time_%i.%05i.raw",timestep,rank); - VELX_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELX_FILE); - fclose(VELX_FILE); - - ScaLBL_Comm->RegularLayout(Map,&Velocity[Np],PhaseField); - Velocity_LB_to_Phys(PhaseField); - FILE *VELY_FILE; - sprintf(LocalRankFilename,"Velocity_Y_Time_%i.%05i.raw",timestep,rank); - VELY_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELY_FILE); - fclose(VELY_FILE); - - ScaLBL_Comm->RegularLayout(Map,&Velocity[2*Np],PhaseField); - Velocity_LB_to_Phys(PhaseField); - FILE *VELZ_FILE; - sprintf(LocalRankFilename,"Velocity_Z_Time_%i.%05i.raw",timestep,rank); - VELZ_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELZ_FILE); - fclose(VELZ_FILE); - -} - -void ScaLBL_StokesModel::Velocity_LB_to_Phys(DoubleArray &Vel_reg){ - for (int k=0;k ScaLBL_StokesModel::computeElectricForceAvg(double *ChargeDensity, double *ElectricField){ +void ScaLBL_StokesModel::Create() { + /* + * This function creates the variables needed to run a LBM + */ + int rank = Mask->rank(); + //......................................................... + // Initialize communication structures in averaging domain + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = Mask->id[i]; + Mask->CommInit(); + Np = Mask->PoreCount(); + //........................................................................... + if (rank == 0) + printf("LB Single-Fluid Solver: Create ScaLBL_Communicator \n"); + // Create a communicator for the device (will use optimized layout) + // ScaLBL_Communicator ScaLBL_Comm(Mask); // original + ScaLBL_Comm = + std::shared_ptr(new ScaLBL_Communicator(Mask)); + + int Npad = (Np / 16 + 2) * 16; + if (rank == 0) + printf("LB Single-Fluid Solver: Set up memory efficient layout \n"); + Map.resize(Nx, Ny, Nz); + Map.fill(-2); + auto neighborList = new int[18 * Npad]; + Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map, neighborList, + Mask->id.data(), Np, 1); + comm.barrier(); + + //........................................................................... + // MAIN VARIABLES ALLOCATED HERE + //........................................................................... + // LBM variables + if (rank == 0) + printf("LB Single-Fluid Solver: Allocating distributions \n"); + //......................device distributions................................. + int dist_mem_size = Np * sizeof(double); + int neighborSize = 18 * (Np * sizeof(int)); + //........................................................................... + ScaLBL_AllocateDeviceMemory((void **)&NeighborList, neighborSize); + ScaLBL_AllocateDeviceMemory((void **)&fq, 19 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Pressure, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&Velocity, 3 * sizeof(double) * Np); + //........................................................................... + // Update GPU data structures + if (rank == 0) + printf("LB Single-Fluid Solver: Setting up device map and neighbor " + "list \n"); + // copy the neighbor list + ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); + comm.barrier(); + + if (UseSlippingVelBC == true) { + ScaLBL_Comm->SetupBounceBackList(Map, Mask->id.data(), Np, 1); + comm.barrier(); + + //For slipping velocity BC, need zeta potential and solid unit normal vector + ScaLBL_AllocateDeviceMemory((void **)&ZetaPotentialSolid, + sizeof(double) * Nx * Ny * Nz); + ScaLBL_AllocateDeviceMemory((void **)&SolidGrad, + sizeof(double) * 3 * + Np); //unit normal vector of solid nodes + + double *ZetaPotentialSolid_host; + ZetaPotentialSolid_host = new double[Nx * Ny * Nz]; + AssignZetaPotentialSolid(ZetaPotentialSolid_host); + double *SolidGrad_host; + SolidGrad_host = new double[3 * Np]; + AssignSolidGrad(SolidGrad_host); + ScaLBL_CopyToDevice(ZetaPotentialSolid, ZetaPotentialSolid_host, + Nx * Ny * Nz * sizeof(double)); + ScaLBL_CopyToDevice(SolidGrad, SolidGrad_host, 3 * Np * sizeof(double)); + ScaLBL_Comm->Barrier(); + delete[] ZetaPotentialSolid_host; + delete[] SolidGrad_host; + } +} + +void ScaLBL_StokesModel::Initialize() { + /* + * This function initializes model + */ + if (rank == 0) + printf("LB Single-Fluid Solver: Initializing distributions \n"); + if (rank == 0) + printf("***************************************************************" + "*\n"); + ScaLBL_D3Q19_Init(fq, Np); + + if (rank == 0) + printf("*****************************************************\n"); + if (rank == 0) + printf("LB Single-Fluid Navier-Stokes Solver: \n"); + if (rank == 0) + printf(" Time conversion factor: %.5g [sec/lt]\n", time_conv); + if (rank == 0) + printf(" Internal iteration: %i [lt]\n", timestepMax); + if (rank == 0) + printf("*****************************************************\n"); +} + +void ScaLBL_StokesModel::Run_Lite(double *ChargeDensity, + double *ElectricField) { + double rlx_setA = 1.0 / tau; + double rlx_setB = 8.f * (2.f - rlx_setA) / (8.f - rlx_setA); + timestep = 0; + while (timestep < timestepMax) { + //************************************************************************/ + //**************ODD TIMESTEP*************// + timestep++; + ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL + ScaLBL_D3Q19_AAodd_StokesMRT( + NeighborList, fq, Velocity, ChargeDensity, ElectricField, rlx_setA, + rlx_setB, Fx, Fy, Fz, rho0, den_scale, h, time_conv, + ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + // Set boundary conditions + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 4) { + din = + ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); + } + ScaLBL_D3Q19_AAodd_StokesMRT(NeighborList, fq, Velocity, ChargeDensity, + ElectricField, rlx_setA, rlx_setB, Fx, Fy, + Fz, rho0, den_scale, h, time_conv, 0, + ScaLBL_Comm->LastExterior(), Np); + + if (UseSlippingVelBC == true) { + ScaLBL_Comm->SolidSlippingVelocityBCD3Q19( + fq, ZetaPotentialSolid, ElectricField, SolidGrad, epsilon_LB, + 1.0 / rlx_setA, rho0, den_scale, h, time_conv); + } + ScaLBL_Comm->Barrier(); + comm.barrier(); + + //**************EVEN TIMESTEP*************// + timestep++; + ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL + ScaLBL_D3Q19_AAeven_StokesMRT( + fq, Velocity, ChargeDensity, ElectricField, rlx_setA, rlx_setB, Fx, + Fy, Fz, rho0, den_scale, h, time_conv, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + // Set boundary conditions + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 4) { + din = + ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); + } + ScaLBL_D3Q19_AAeven_StokesMRT(fq, Velocity, ChargeDensity, + ElectricField, rlx_setA, rlx_setB, Fx, Fy, + Fz, rho0, den_scale, h, time_conv, 0, + ScaLBL_Comm->LastExterior(), Np); + if (UseSlippingVelBC == true) { + ScaLBL_Comm->SolidSlippingVelocityBCD3Q19( + fq, ZetaPotentialSolid, ElectricField, SolidGrad, epsilon_LB, + 1.0 / rlx_setA, rho0, den_scale, h, time_conv); + } + ScaLBL_Comm->Barrier(); + comm.barrier(); + //************************************************************************/ + } +} + +void ScaLBL_StokesModel::getVelocity(DoubleArray &Vel_x, DoubleArray &Vel_y, + DoubleArray &Vel_z) { + //get velocity in physical unit [m/sec] + ScaLBL_D3Q19_Momentum(fq, Velocity, Np); + ScaLBL_Comm->Barrier(); + comm.barrier(); + + ScaLBL_Comm->RegularLayout(Map, &Velocity[0], Vel_x); + Velocity_LB_to_Phys(Vel_x); + ScaLBL_Comm->Barrier(); + comm.barrier(); + + ScaLBL_Comm->RegularLayout(Map, &Velocity[Np], Vel_y); + Velocity_LB_to_Phys(Vel_y); + ScaLBL_Comm->Barrier(); + comm.barrier(); + + ScaLBL_Comm->RegularLayout(Map, &Velocity[2 * Np], Vel_z); + Velocity_LB_to_Phys(Vel_z); + ScaLBL_Comm->Barrier(); + comm.barrier(); +} + +void ScaLBL_StokesModel::getVelocity_debug(int timestep) { + //get velocity in physical unit [m/sec] + ScaLBL_D3Q19_Momentum(fq, Velocity, Np); + ScaLBL_Comm->Barrier(); + comm.barrier(); + + DoubleArray PhaseField(Nx, Ny, Nz); + ScaLBL_Comm->RegularLayout(Map, &Velocity[0], PhaseField); + Velocity_LB_to_Phys(PhaseField); + FILE *VELX_FILE; + sprintf(LocalRankFilename, "Velocity_X_Time_%i.%05i.raw", timestep, rank); + VELX_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELX_FILE); + fclose(VELX_FILE); + + ScaLBL_Comm->RegularLayout(Map, &Velocity[Np], PhaseField); + Velocity_LB_to_Phys(PhaseField); + FILE *VELY_FILE; + sprintf(LocalRankFilename, "Velocity_Y_Time_%i.%05i.raw", timestep, rank); + VELY_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELY_FILE); + fclose(VELY_FILE); + + ScaLBL_Comm->RegularLayout(Map, &Velocity[2 * Np], PhaseField); + Velocity_LB_to_Phys(PhaseField); + FILE *VELZ_FILE; + sprintf(LocalRankFilename, "Velocity_Z_Time_%i.%05i.raw", timestep, rank); + VELZ_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELZ_FILE); + fclose(VELZ_FILE); +} + +void ScaLBL_StokesModel::Velocity_LB_to_Phys(DoubleArray &Vel_reg) { + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int idx = Map(i, j, k); + if (!(idx < 0)) { + Vel_reg(i, j, k) = + Vel_reg(i, j, k) * (h * 1.0e-6) / time_conv; + } + } + } + } +} + +vector +ScaLBL_StokesModel::computeElectricForceAvg(double *ChargeDensity, + double *ElectricField) { double *Ex_host; double *Ey_host; @@ -656,330 +735,366 @@ vector ScaLBL_StokesModel::computeElectricForceAvg(double *ChargeDensity Ex_host = new double[Np]; Ey_host = new double[Np]; Ez_host = new double[Np]; - + double *rhoE_host; rhoE_host = new double[Np]; - ScaLBL_CopyToHost(Ex_host,&ElectricField[0*Np],Np*sizeof(double)); - ScaLBL_CopyToHost(Ey_host,&ElectricField[1*Np],Np*sizeof(double)); - ScaLBL_CopyToHost(Ez_host,&ElectricField[2*Np],Np*sizeof(double)); - ScaLBL_CopyToHost(rhoE_host,ChargeDensity,Np*sizeof(double)); + ScaLBL_CopyToHost(Ex_host, &ElectricField[0 * Np], Np * sizeof(double)); + ScaLBL_CopyToHost(Ey_host, &ElectricField[1 * Np], Np * sizeof(double)); + ScaLBL_CopyToHost(Ez_host, &ElectricField[2 * Np], Np * sizeof(double)); + ScaLBL_CopyToHost(rhoE_host, ChargeDensity, Np * sizeof(double)); - double count_loc=0; - double count; - double Fx_avg,Fy_avg,Fz_avg;//average electric field induced force - double Fx_loc,Fy_loc,Fz_loc; + double count_loc = 0; + double count; + double Fx_avg, Fy_avg, Fz_avg; //average electric field induced force + double Fx_loc, Fy_loc, Fz_loc; Fx_loc = Fy_loc = Fz_loc = 0.0; - for (int idx=0; idxLastExterior(); idx++){ - Fx_loc += rhoE_host[idx]*Ex_host[idx]*(time_conv*time_conv)/(h*h*1.0e-12)/den_scale; - Fy_loc += rhoE_host[idx]*Ey_host[idx]*(time_conv*time_conv)/(h*h*1.0e-12)/den_scale; - Fz_loc += rhoE_host[idx]*Ez_host[idx]*(time_conv*time_conv)/(h*h*1.0e-12)/den_scale; - count_loc+=1.0; + for (int idx = 0; idx < ScaLBL_Comm->LastExterior(); idx++) { + Fx_loc += rhoE_host[idx] * Ex_host[idx] * (time_conv * time_conv) / + (h * h * 1.0e-12) / den_scale; + Fy_loc += rhoE_host[idx] * Ey_host[idx] * (time_conv * time_conv) / + (h * h * 1.0e-12) / den_scale; + Fz_loc += rhoE_host[idx] * Ez_host[idx] * (time_conv * time_conv) / + (h * h * 1.0e-12) / den_scale; + count_loc += 1.0; } - for (int idx=ScaLBL_Comm->FirstInterior(); idxLastInterior(); idx++){ - Fx_loc += rhoE_host[idx]*Ex_host[idx]*(time_conv*time_conv)/(h*h*1.0e-12)/den_scale; - Fy_loc += rhoE_host[idx]*Ey_host[idx]*(time_conv*time_conv)/(h*h*1.0e-12)/den_scale; - Fz_loc += rhoE_host[idx]*Ez_host[idx]*(time_conv*time_conv)/(h*h*1.0e-12)/den_scale; - count_loc+=1.0; + for (int idx = ScaLBL_Comm->FirstInterior(); + idx < ScaLBL_Comm->LastInterior(); idx++) { + Fx_loc += rhoE_host[idx] * Ex_host[idx] * (time_conv * time_conv) / + (h * h * 1.0e-12) / den_scale; + Fy_loc += rhoE_host[idx] * Ey_host[idx] * (time_conv * time_conv) / + (h * h * 1.0e-12) / den_scale; + Fz_loc += rhoE_host[idx] * Ez_host[idx] * (time_conv * time_conv) / + (h * h * 1.0e-12) / den_scale; + count_loc += 1.0; } - Fx_avg=Dm->Comm.sumReduce( Fx_loc); - Fy_avg=Dm->Comm.sumReduce( Fy_loc); - Fz_avg=Dm->Comm.sumReduce( Fz_loc); - count=Dm->Comm.sumReduce( count_loc); - - Fx_avg /= count; - Fy_avg /= count; - Fz_avg /= count; + Fx_avg = Dm->Comm.sumReduce(Fx_loc); + Fy_avg = Dm->Comm.sumReduce(Fy_loc); + Fz_avg = Dm->Comm.sumReduce(Fz_loc); + count = Dm->Comm.sumReduce(count_loc); - vectorF_avg{Fx_avg,Fy_avg,Fz_avg}; + Fx_avg /= count; + Fy_avg /= count; + Fz_avg /= count; - delete [] Ex_host; - delete [] Ey_host; - delete [] Ez_host; - delete [] rhoE_host; + vector F_avg{Fx_avg, Fy_avg, Fz_avg}; + + delete[] Ex_host; + delete[] Ey_host; + delete[] Ez_host; + delete[] rhoE_host; return F_avg; } -double ScaLBL_StokesModel::CalVelocityConvergence(double& flow_rate_previous,double *ChargeDensity, double *ElectricField){ - - //----------------------------------------------------- - ScaLBL_D3Q19_Momentum(fq,Velocity, Np); - ScaLBL_Comm->Barrier(); comm.barrier(); - ScaLBL_Comm->RegularLayout(Map,&Velocity[0],Velocity_x); - ScaLBL_Comm->RegularLayout(Map,&Velocity[Np],Velocity_y); - ScaLBL_Comm->RegularLayout(Map,&Velocity[2*Np],Velocity_z); - - double count_loc=0; - double count; - double vax,vay,vaz; - double vax_loc,vay_loc,vaz_loc; - vax_loc = vay_loc = vaz_loc = 0.f; - for (int k=1; k 0){ - vax_loc += Velocity_x(i,j,k); - vay_loc += Velocity_y(i,j,k); - vaz_loc += Velocity_z(i,j,k); - count_loc+=1.0; - } - } - } - } - vax=Dm->Comm.sumReduce( vax_loc); - vay=Dm->Comm.sumReduce( vay_loc); - vaz=Dm->Comm.sumReduce( vaz_loc); - count=Dm->Comm.sumReduce( count_loc); +double ScaLBL_StokesModel::CalVelocityConvergence(double &flow_rate_previous, + double *ChargeDensity, + double *ElectricField) { + + //----------------------------------------------------- + ScaLBL_D3Q19_Momentum(fq, Velocity, Np); + ScaLBL_Comm->Barrier(); + comm.barrier(); + ScaLBL_Comm->RegularLayout(Map, &Velocity[0], Velocity_x); + ScaLBL_Comm->RegularLayout(Map, &Velocity[Np], Velocity_y); + ScaLBL_Comm->RegularLayout(Map, &Velocity[2 * Np], Velocity_z); + + double count_loc = 0; + double count; + double vax, vay, vaz; + double vax_loc, vay_loc, vaz_loc; + vax_loc = vay_loc = vaz_loc = 0.f; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + if (Distance(i, j, k) > 0) { + vax_loc += Velocity_x(i, j, k); + vay_loc += Velocity_y(i, j, k); + vaz_loc += Velocity_z(i, j, k); + count_loc += 1.0; + } + } + } + } + vax = Dm->Comm.sumReduce(vax_loc); + vay = Dm->Comm.sumReduce(vay_loc); + vaz = Dm->Comm.sumReduce(vaz_loc); + count = Dm->Comm.sumReduce(count_loc); + + vax /= count; + vay /= count; + vaz /= count; - vax /= count; - vay /= count; - vaz /= count; - vector Eforce; - Eforce = computeElectricForceAvg(ChargeDensity,ElectricField); - double TFx = Fx+Eforce[0];//TF: total body force - double TFy = Fy+Eforce[1]; - double TFz = Fz+Eforce[2]; - double force_mag = sqrt(TFx*TFx+TFy*TFy+TFz*TFz); - double dir_x = TFx/force_mag; - double dir_y = TFy/force_mag; - double dir_z = TFz/force_mag; - if (force_mag == 0.0){ - // default to z direction - dir_x = 0.0; - dir_y = 0.0; - dir_z = 1.0; - force_mag = 1.0; - } - double flow_rate = (vax*dir_x + vay*dir_y + vaz*dir_z); - double error = fabs(flow_rate - flow_rate_previous) / fabs(flow_rate); - flow_rate_previous = flow_rate; + Eforce = computeElectricForceAvg(ChargeDensity, ElectricField); + double TFx = Fx + Eforce[0]; //TF: total body force + double TFy = Fy + Eforce[1]; + double TFz = Fz + Eforce[2]; + double force_mag = sqrt(TFx * TFx + TFy * TFy + TFz * TFz); + double dir_x = TFx / force_mag; + double dir_y = TFy / force_mag; + double dir_z = TFz / force_mag; + if (force_mag == 0.0) { + // default to z direction + dir_x = 0.0; + dir_y = 0.0; + dir_z = 1.0; + force_mag = 1.0; + } + double flow_rate = (vax * dir_x + vay * dir_y + vaz * dir_z); + double error = fabs(flow_rate - flow_rate_previous) / fabs(flow_rate); + flow_rate_previous = flow_rate; //---------------------------------------------------- - //for debugging - if (rank==0){ - printf("StokesModel: error: %.5g\n",error); + //for debugging + if (rank == 0) { + printf("StokesModel: error: %.5g\n", error); } return error; } -void ScaLBL_StokesModel::Run(){ - double rlx_setA=1.0/tau; - double rlx_setB = 8.f*(2.f-rlx_setA)/(8.f-rlx_setA); - - Minkowski Morphology(Mask); +void ScaLBL_StokesModel::Run() { + double rlx_setA = 1.0 / tau; + double rlx_setB = 8.f * (2.f - rlx_setA) / (8.f - rlx_setA); - if (rank==0){ - bool WriteHeader=false; - FILE *log_file = fopen("Permeability.csv","r"); - if (log_file != NULL) - fclose(log_file); - else - WriteHeader=true; + Minkowski Morphology(Mask); - if (WriteHeader){ - log_file = fopen("Permeability.csv","a+"); - fprintf(log_file,"time Fx Fy Fz mu Vs As Js Xs vx vy vz k\n"); - fclose(log_file); - } - } + if (rank == 0) { + bool WriteHeader = false; + FILE *log_file = fopen("Permeability.csv", "r"); + if (log_file != NULL) + fclose(log_file); + else + WriteHeader = true; - ScaLBL_Comm->Barrier(); comm.barrier(); - if (rank==0) printf("****************************************************************\n"); - if (rank==0) printf("LB Single-Fluid Navier-Stokes Solver: timestepMax = %i\n", timestepMax); - if (rank==0) printf("****************************************************************\n"); - timestep=0; - double error = 1.0; - double flow_rate_previous = 0.0; + if (WriteHeader) { + log_file = fopen("Permeability.csv", "a+"); + fprintf(log_file, "time Fx Fy Fz mu Vs As Js Xs vx vy vz k\n"); + fclose(log_file); + } + } + + ScaLBL_Comm->Barrier(); + comm.barrier(); + if (rank == 0) + printf("***************************************************************" + "*\n"); + if (rank == 0) + printf("LB Single-Fluid Navier-Stokes Solver: timestepMax = %i\n", + timestepMax); + if (rank == 0) + printf("***************************************************************" + "*\n"); + timestep = 0; + double error = 1.0; + double flow_rate_previous = 0.0; auto t1 = std::chrono::system_clock::now(); - while (timestep < timestepMax && error > tolerance) { - //************************************************************************/ - timestep++; - ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL - ScaLBL_D3Q19_AAodd_MRT(NeighborList, fq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np, rlx_setA, rlx_setB, Fx, Fy, Fz); - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - // Set boundary conditions - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); - } - ScaLBL_D3Q19_AAodd_MRT(NeighborList, fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx_setA, rlx_setB, Fx, Fy, Fz); - ScaLBL_Comm->Barrier(); comm.barrier(); - timestep++; - ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL - ScaLBL_D3Q19_AAeven_MRT(fq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np, rlx_setA, rlx_setB, Fx, Fy, Fz); - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - // Set boundary conditions - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); - } - ScaLBL_D3Q19_AAeven_MRT(fq, 0, ScaLBL_Comm->LastExterior(), Np, rlx_setA, rlx_setB, Fx, Fy, Fz); - ScaLBL_Comm->Barrier(); comm.barrier(); - //************************************************************************/ - - if (timestep%1000==0){ - ScaLBL_D3Q19_Momentum(fq,Velocity, Np); - ScaLBL_Comm->Barrier(); comm.barrier(); - ScaLBL_Comm->RegularLayout(Map,&Velocity[0],Velocity_x); - ScaLBL_Comm->RegularLayout(Map,&Velocity[Np],Velocity_y); - ScaLBL_Comm->RegularLayout(Map,&Velocity[2*Np],Velocity_z); - - double count_loc=0; - double count; - double vax,vay,vaz; - double vax_loc,vay_loc,vaz_loc; - vax_loc = vay_loc = vaz_loc = 0.f; - for (int k=1; k 0){ - vax_loc += Velocity_x(i,j,k); - vay_loc += Velocity_y(i,j,k); - vaz_loc += Velocity_z(i,j,k); - count_loc+=1.0; - } - } - } - } - - vax=Dm->Comm.sumReduce( vax_loc); - vay=Dm->Comm.sumReduce( vay_loc); - vaz=Dm->Comm.sumReduce( vaz_loc); - count=Dm->Comm.sumReduce( count_loc); + while (timestep < timestepMax && error > tolerance) { + //************************************************************************/ + timestep++; + ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL + ScaLBL_D3Q19_AAodd_MRT(NeighborList, fq, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np, rlx_setA, + rlx_setB, Fx, Fy, Fz); + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + // Set boundary conditions + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 4) { + din = + ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); + } + ScaLBL_D3Q19_AAodd_MRT(NeighborList, fq, 0, ScaLBL_Comm->LastExterior(), + Np, rlx_setA, rlx_setB, Fx, Fy, Fz); + ScaLBL_Comm->Barrier(); + comm.barrier(); + timestep++; + ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL + ScaLBL_D3Q19_AAeven_MRT(fq, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np, rlx_setA, + rlx_setB, Fx, Fy, Fz); + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + // Set boundary conditions + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 4) { + din = + ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); + } + ScaLBL_D3Q19_AAeven_MRT(fq, 0, ScaLBL_Comm->LastExterior(), Np, + rlx_setA, rlx_setB, Fx, Fy, Fz); + ScaLBL_Comm->Barrier(); + comm.barrier(); + //************************************************************************/ - - vax /= count; - vay /= count; - vaz /= count; - - double force_mag = sqrt(Fx*Fx+Fy*Fy+Fz*Fz); - double dir_x = Fx/force_mag; - double dir_y = Fy/force_mag; - double dir_z = Fz/force_mag; - if (force_mag == 0.0){ - // default to z direction - dir_x = 0.0; - dir_y = 0.0; - dir_z = 1.0; - force_mag = 1.0; - } - double flow_rate = (vax*dir_x + vay*dir_y + vaz*dir_z); - - error = fabs(flow_rate - flow_rate_previous) / fabs(flow_rate); - flow_rate_previous = flow_rate; - - //if (rank==0) printf("Computing Minkowski functionals \n"); - Morphology.ComputeScalar(Distance,0.f); - //Morphology.PrintAll(); - double mu = (tau-0.5)/3.f; - double Vs = Morphology.V(); - double As = Morphology.A(); - double Hs = Morphology.H(); - double Xs = Morphology.X(); - Vs=Dm->Comm.sumReduce( Vs); - As=Dm->Comm.sumReduce( As); - Hs=Dm->Comm.sumReduce( Hs); - Xs=Dm->Comm.sumReduce( Xs); - double h = Dm->voxel_length; - double absperm = h*h*mu*Mask->Porosity()*flow_rate / force_mag; - if (rank==0) { - printf(" %f\n",absperm); - FILE * log_file = fopen("Permeability.csv","a"); - fprintf(log_file,"%i %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g\n",timestep, Fx, Fy, Fz, mu, - h*h*h*Vs,h*h*As,h*Hs,Xs,vax,vay,vaz, absperm); - fclose(log_file); - } - } - } - //************************************************************************/ - if (rank==0) printf("-------------------------------------------------------------------\n"); - // Compute the walltime per timestep + if (timestep % 1000 == 0) { + ScaLBL_D3Q19_Momentum(fq, Velocity, Np); + ScaLBL_Comm->Barrier(); + comm.barrier(); + ScaLBL_Comm->RegularLayout(Map, &Velocity[0], Velocity_x); + ScaLBL_Comm->RegularLayout(Map, &Velocity[Np], Velocity_y); + ScaLBL_Comm->RegularLayout(Map, &Velocity[2 * Np], Velocity_z); + + double count_loc = 0; + double count; + double vax, vay, vaz; + double vax_loc, vay_loc, vaz_loc; + vax_loc = vay_loc = vaz_loc = 0.f; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + if (Distance(i, j, k) > 0) { + vax_loc += Velocity_x(i, j, k); + vay_loc += Velocity_y(i, j, k); + vaz_loc += Velocity_z(i, j, k); + count_loc += 1.0; + } + } + } + } + + vax = Dm->Comm.sumReduce(vax_loc); + vay = Dm->Comm.sumReduce(vay_loc); + vaz = Dm->Comm.sumReduce(vaz_loc); + count = Dm->Comm.sumReduce(count_loc); + + vax /= count; + vay /= count; + vaz /= count; + + double force_mag = sqrt(Fx * Fx + Fy * Fy + Fz * Fz); + double dir_x = Fx / force_mag; + double dir_y = Fy / force_mag; + double dir_z = Fz / force_mag; + if (force_mag == 0.0) { + // default to z direction + dir_x = 0.0; + dir_y = 0.0; + dir_z = 1.0; + force_mag = 1.0; + } + double flow_rate = (vax * dir_x + vay * dir_y + vaz * dir_z); + + error = fabs(flow_rate - flow_rate_previous) / fabs(flow_rate); + flow_rate_previous = flow_rate; + + //if (rank==0) printf("Computing Minkowski functionals \n"); + Morphology.ComputeScalar(Distance, 0.f); + //Morphology.PrintAll(); + double mu = (tau - 0.5) / 3.f; + double Vs = Morphology.V(); + double As = Morphology.A(); + double Hs = Morphology.H(); + double Xs = Morphology.X(); + Vs = Dm->Comm.sumReduce(Vs); + As = Dm->Comm.sumReduce(As); + Hs = Dm->Comm.sumReduce(Hs); + Xs = Dm->Comm.sumReduce(Xs); + double h = Dm->voxel_length; + double absperm = + h * h * mu * Mask->Porosity() * flow_rate / force_mag; + if (rank == 0) { + printf(" %f\n", absperm); + FILE *log_file = fopen("Permeability.csv", "a"); + fprintf(log_file, + "%i %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g %.8g " + "%.8g %.8g\n", + timestep, Fx, Fy, Fz, mu, h * h * h * Vs, h * h * As, + h * Hs, Xs, vax, vay, vaz, absperm); + fclose(log_file); + } + } + } + //************************************************************************/ + if (rank == 0) + printf("---------------------------------------------------------------" + "----\n"); + // Compute the walltime per timestep auto t2 = std::chrono::system_clock::now(); - double cputime = std::chrono::duration( t2 - t1 ).count() / timestep; - // Performance obtained from each node - double MLUPS = double(Np)/cputime/1000000; - - if (rank==0) printf("********************************************************\n"); - if (rank==0) printf("CPU time = %f \n", cputime); - if (rank==0) printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); - MLUPS *= nprocs; - if (rank==0) printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); - if (rank==0) printf("********************************************************\n"); + double cputime = std::chrono::duration(t2 - t1).count() / timestep; + // Performance obtained from each node + double MLUPS = double(Np) / cputime / 1000000; + if (rank == 0) + printf("********************************************************\n"); + if (rank == 0) + printf("CPU time = %f \n", cputime); + if (rank == 0) + printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); + MLUPS *= nprocs; + if (rank == 0) + printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); + if (rank == 0) + printf("********************************************************\n"); } -void ScaLBL_StokesModel::VelocityField(){ - - std::vector visData; - fillHalo fillData(Dm->Comm,Dm->rank_info,{Dm->Nx-2,Dm->Ny-2,Dm->Nz-2},{1,1,1},0,1); +void ScaLBL_StokesModel::VelocityField() { - auto VxVar = std::make_shared(); - auto VyVar = std::make_shared(); - auto VzVar = std::make_shared(); - auto SignDistVar = std::make_shared(); + std::vector visData; + fillHalo fillData(Dm->Comm, Dm->rank_info, + {Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2}, {1, 1, 1}, + 0, 1); - IO::initialize("","silo","false"); - // Create the MeshDataStruct - visData.resize(1); - visData[0].meshName = "domain"; - visData[0].mesh = std::make_shared( Dm->rank_info,Dm->Nx-2,Dm->Ny-2,Dm->Nz-2,Dm->Lx,Dm->Ly,Dm->Lz ); - SignDistVar->name = "SignDist"; - SignDistVar->type = IO::VariableType::VolumeVariable; - SignDistVar->dim = 1; - SignDistVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(SignDistVar); - - VxVar->name = "Velocity_x"; - VxVar->type = IO::VariableType::VolumeVariable; - VxVar->dim = 1; - VxVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(VxVar); - VyVar->name = "Velocity_y"; - VyVar->type = IO::VariableType::VolumeVariable; - VyVar->dim = 1; - VyVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(VyVar); - VzVar->name = "Velocity_z"; - VzVar->type = IO::VariableType::VolumeVariable; - VzVar->dim = 1; - VzVar->data.resize(Dm->Nx-2,Dm->Ny-2,Dm->Nz-2); - visData[0].vars.push_back(VzVar); - - Array& SignData = visData[0].vars[0]->data; - Array& VelxData = visData[0].vars[1]->data; - Array& VelyData = visData[0].vars[2]->data; - Array& VelzData = visData[0].vars[3]->data; - - ASSERT(visData[0].vars[0]->name=="SignDist"); - ASSERT(visData[0].vars[1]->name=="Velocity_x"); - ASSERT(visData[0].vars[2]->name=="Velocity_y"); - ASSERT(visData[0].vars[3]->name=="Velocity_z"); - - fillData.copy(Distance,SignData); - fillData.copy(Velocity_x,VelxData); - fillData.copy(Velocity_y,VelyData); - fillData.copy(Velocity_z,VelzData); - - IO::writeData( timestep, visData, Dm->Comm ); + auto VxVar = std::make_shared(); + auto VyVar = std::make_shared(); + auto VzVar = std::make_shared(); + auto SignDistVar = std::make_shared(); + IO::initialize("", "silo", "false"); + // Create the MeshDataStruct + visData.resize(1); + visData[0].meshName = "domain"; + visData[0].mesh = + std::make_shared(Dm->rank_info, Dm->Nx - 2, Dm->Ny - 2, + Dm->Nz - 2, Dm->Lx, Dm->Ly, Dm->Lz); + SignDistVar->name = "SignDist"; + SignDistVar->type = IO::VariableType::VolumeVariable; + SignDistVar->dim = 1; + SignDistVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); + visData[0].vars.push_back(SignDistVar); + + VxVar->name = "Velocity_x"; + VxVar->type = IO::VariableType::VolumeVariable; + VxVar->dim = 1; + VxVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); + visData[0].vars.push_back(VxVar); + VyVar->name = "Velocity_y"; + VyVar->type = IO::VariableType::VolumeVariable; + VyVar->dim = 1; + VyVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); + visData[0].vars.push_back(VyVar); + VzVar->name = "Velocity_z"; + VzVar->type = IO::VariableType::VolumeVariable; + VzVar->dim = 1; + VzVar->data.resize(Dm->Nx - 2, Dm->Ny - 2, Dm->Nz - 2); + visData[0].vars.push_back(VzVar); + + Array &SignData = visData[0].vars[0]->data; + Array &VelxData = visData[0].vars[1]->data; + Array &VelyData = visData[0].vars[2]->data; + Array &VelzData = visData[0].vars[3]->data; + + ASSERT(visData[0].vars[0]->name == "SignDist"); + ASSERT(visData[0].vars[1]->name == "Velocity_x"); + ASSERT(visData[0].vars[2]->name == "Velocity_y"); + ASSERT(visData[0].vars[3]->name == "Velocity_z"); + + fillData.copy(Distance, SignData); + fillData.copy(Velocity_x, VelxData); + fillData.copy(Velocity_y, VelyData); + fillData.copy(Velocity_z, VelzData); + + IO::writeData(timestep, visData, Dm->Comm); } diff --git a/models/StokesModel.h b/models/StokesModel.h index 1e80f269..1a34d1cb 100644 --- a/models/StokesModel.h +++ b/models/StokesModel.h @@ -18,49 +18,51 @@ #include "analysis/Minkowski.h" #include "ProfilerApp.h" -class ScaLBL_StokesModel{ +class ScaLBL_StokesModel { public: - ScaLBL_StokesModel(int RANK, int NP, const Utilities::MPI& COMM); - ~ScaLBL_StokesModel(); - - // functions in they should be run - void ReadParams(string filename,int num_iter); - void ReadParams(string filename); - void ReadParams(std::shared_ptr db0); - void SetDomain(); - void ReadInput(); - void Create(); - void Initialize(); - void Run(); - void Run_Lite(double *ChargeDensity, double *ElectricField); - void VelocityField(); + ScaLBL_StokesModel(int RANK, int NP, const Utilities::MPI &COMM); + ~ScaLBL_StokesModel(); + + // functions in they should be run + void ReadParams(string filename, int num_iter); + void ReadParams(string filename); + void ReadParams(std::shared_ptr db0); + void SetDomain(); + void ReadInput(); + void Create(); + void Initialize(); + void Run(); + void Run_Lite(double *ChargeDensity, double *ElectricField); + void VelocityField(); void getVelocity(DoubleArray &Velx, DoubleArray &Vel_y, DoubleArray &Vel_z); void getVelocity_debug(int timestep); - double CalVelocityConvergence(double& flow_rate_previous,double *ChargeDensity, double *ElectricField); - - bool Restart,pBC; - int timestep,timestepMax; - int BoundaryCondition; - double tau,mu; + double CalVelocityConvergence(double &flow_rate_previous, + double *ChargeDensity, double *ElectricField); + + bool Restart, pBC; + int timestep, timestepMax; + int BoundaryCondition; + double tau, mu; double rho0; - double Fx,Fy,Fz,flux; - double din,dout; - double tolerance; + double Fx, Fy, Fz, flux; + double din, dout; + double tolerance; double nu_phys; double rho_phys; double time_conv; - double h;//image resolution - double den_scale;//scale factor for density - double epsilon0,epsilon0_LB,epsilonR,epsilon_LB;//Stokes solver also needs this for slipping velocity BC + double h; //image resolution + double den_scale; //scale factor for density + double epsilon0, epsilon0_LB, epsilonR, + epsilon_LB; //Stokes solver also needs this for slipping velocity BC bool UseSlippingVelBC; - - int Nx,Ny,Nz,N,Np; - int rank,nprocx,nprocy,nprocz,nprocs; - double Lx,Ly,Lz; - std::shared_ptr Dm; // this domain is for analysis - std::shared_ptr Mask; // this domain is for lbm - std::shared_ptr ScaLBL_Comm; + int Nx, Ny, Nz, N, Np; + int rank, nprocx, nprocy, nprocz, nprocs; + double Lx, Ly, Lz; + + std::shared_ptr Dm; // this domain is for analysis + std::shared_ptr Mask; // this domain is for lbm + std::shared_ptr ScaLBL_Comm; // input database std::shared_ptr db; std::shared_ptr domain_db; @@ -74,24 +76,26 @@ public: double *Pressure; double *ZetaPotentialSolid; double *SolidGrad; - + //Minkowski Morphology; DoubleArray Velocity_x; DoubleArray Velocity_y; DoubleArray Velocity_z; + private: - Utilities::MPI comm; - - // filenames + Utilities::MPI comm; + + // filenames char LocalRankString[8]; char LocalRankFilename[40]; char LocalRestartFile[40]; char OutputFilename[200]; - + //int rank,nprocs; - void LoadParams(std::shared_ptr db0); + void LoadParams(std::shared_ptr db0); void Velocity_LB_to_Phys(DoubleArray &Vel_reg); - vector computeElectricForceAvg(double *ChargeDensity, double *ElectricField); + vector computeElectricForceAvg(double *ChargeDensity, + double *ElectricField); void AssignSolidGrad(double *solid_grad); void AssignZetaPotentialSolid(double *zeta_potential_solid); }; From 37c9ff3be1360981862e5d05824dd2c051f99363 Mon Sep 17 00:00:00 2001 From: Thomas Ramstad Date: Tue, 9 Nov 2021 19:14:00 +0100 Subject: [PATCH 42/54] Update c-cpp.yml --- .github/workflows/c-cpp.yml | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 6ab68898..e772f76d 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -27,11 +27,7 @@ jobs: echo $GITHUB_WORKSPACE sudo apt-get update -y - #sudo apt-get install -y libtool-ltdl - #sudo apt-get install -y libtool-ltdl-devel - - #curl "https://wci.llnl.gov/sites/wci/files/2021-01/silo-4.10.2.tgz" -o "silo-4.10.2.tar.gz" wget https://bitbucket.org/AdvancedMultiPhysics/tpl-builder/downloads/silo-4.10.2.tar.gz wget https://www.zlib.net/zlib-1.2.11.tar.gz wget https://support.hdfgroup.org/ftp/HDF5/releases/hdf5-1.8/hdf5-1.8.12/src/hdf5-1.8.12.tar.gz @@ -42,7 +38,6 @@ jobs: tar -xzvf silo-4.10.2.tar.gz - - name: check out commit uses: actions/checkout@v2 with: @@ -113,25 +108,6 @@ jobs: -D SILO_DIRECTORY=$LBPM_SILO_DIR \ -D USE_CUDA=0 \ $GITHUB_WORKSPACE/LBPM - - #-DCMAKE_C_COMPILER:PATH=$MPI_DIR/bin/mpicc \ - #-DCMAKE_CXX_COMPILER:PATH=$MPI_DIR/bin/mpicxx \ - #-DCMAKE_C_FLAGS="-O3 -fPIC" \ - #-DCMAKE_CXX_FLAGS="-O3 -fPIC " \ - #-DCMAKE_CXX_STANDARD=14 \ - #-DMPIEXEC=$MPI_DIR/mpirun \ - #-DUSE_EXT_MPI_FOR_SERIAL_TESTS:BOOL=TRUE \ - #-DCMAKE_BUILD_TYPE:STRING=Release \ - #-DHDF5_DIRECTORY=$LBPM_HDF5_DIR \ - #-DHDF5_LIB=$LBPM_HDF5_DIR/lib/libhdf5.a \ - #-DUSE_SILO=1 \ - #-DSILO_LIB=$LBPM_SILO_DIR/lib/libsiloh5.a \ - #-DSILO_DIRECTORY=$LBPM_SILO_DIR \ - #-DUSE_NETCDF=0 \ - #-DUSE_CUDA=0 \ - #-DUSE_TIMER=0 \ - #$GITHUB_WORKSPACE/LBPM - cd .. From e6fa7d40656ceb490140496f39955a2ee628e4ca Mon Sep 17 00:00:00 2001 From: James McClure Date: Wed, 1 Dec 2021 08:08:16 -0500 Subject: [PATCH 43/54] add SCAL file --- docs/source/examples/color/steadyState.rst | 123 ++++++++++++++++++++- docs/source/userGuide/models/mrt/mrt.rst | 3 +- models/ColorModel.cpp | 28 ++++- 3 files changed, 148 insertions(+), 6 deletions(-) diff --git a/docs/source/examples/color/steadyState.rst b/docs/source/examples/color/steadyState.rst index 997f882e..35eaf46b 100644 --- a/docs/source/examples/color/steadyState.rst +++ b/docs/source/examples/color/steadyState.rst @@ -1,5 +1,120 @@ -******************* -Steady-state flow -******************* +******************************** +Steady-state flow (color model) +******************************** + +In this example we simulate a steady-state flow with a constant driving force. This will enforce a periodic boundary condition +in all directions. While the driving force may be set in any direction, we will set it in the z-direction to be consistent +with the convention for pressure and velocity boundary conditions. + + +For the case considered in ``example/DiscPack`` we specify the following information in the input file + +.. code:: c + + Domain { + Filename = "discs_3x128x128.raw.morphdrain.raw" + ReadType = "8bit" // data type + N = 3, 128, 128 // size of original image + nproc = 1, 2, 2 // process grid + n = 3, 64, 64 // sub-domain size + voxel_length = 1.0 // voxel length (in microns) + ReadValues = 0, 1, 2 // labels within the original image + WriteValues = 0, 1, 2 // associated labels to be used by LBPM + BC = 0 // fully periodic BC + Sw = 0.35 // target saturation for morphological tools + } + + Color { + capillary_number = -1e-5 // capillary number for the displacement, positive="oil injection" + timestepMax = 20000 // maximum timtestep + alpha = 0.01 // controls interfacial tension + rhoA = 1.0 // controls the density of fluid A + rhoB = 1.0 // controls the density of fluid B + tauA = 0.7 // controls the viscosity of fluid A + tauB = 0.7 // controls the viscosity of fluid B + F = 0, 0, 1e-5 // body force + WettingConvention = "SCAL" + ComponentLabels = 0 // image labels for solid voxels + ComponentAffinity = 0.9 // controls the wetting affinity for each label + Restart = false + } + Analysis { + analysis_interval = 1000 // logging interval for timelog.csv + subphase_analysis_interval = 500000 // loggging interval for subphase.csv + N_threads = 4 // number of analysis threads (GPU version only) + visualization_interval = 10000 // interval to write visualization files + restart_interval = 10000000 // interval to write restart file + restart_file = "Restart" // base name of restart file + } + Visualization { + format = "hdf5" + write_silo = true // write SILO databases with assigned variables + save_8bit_raw = true // write labeled 8-bit binary files with phase assignments + save_phase_field = true // save phase field within SILO database + save_pressure = true // save pressure field within SILO database + save_velocity = false // save velocity field within SILO database + } + FlowAdaptor { + } + + +Once this has been set, we launch ``lbpm_color_simulator`` in the same way as other parallel tools + +.. code:: bash + + mpirun -np 4 $LBPM_BIN/lbpm_color_simulator input.db + +Successful output looks like the following + + +.. code:: bash + + ******************************************************** + Running Color LBM + ******************************************************** + voxel length = 1.000000 micron + voxel length = 1.000000 micron + Input media: discs_3x128x128.raw.morphdrain.raw + Relabeling 3 values + oldvalue=0, newvalue =0 + oldvalue=1, newvalue =1 + oldvalue=2, newvalue =2 + Dimensions of segmented image: 3 x 128 x 128 + Reading 8-bit input data + Read segmented data from discs_3x128x128.raw.morphdrain.raw + Label=0, Count=11862 + Label=1, Count=26430 + Label=2, Count=10860 + Distributing subdomains across 4 processors + Process grid: 1 x 2 x 2 + Subdomain size: 3 x 64 x 64 + Size of transition region: 0 + Media porosity = 0.758667 + Initialized solid phase -- Converting to Signed Distance function + Domain set. + Create ScaLBL_Communicator + Set up memory efficient layout, 9090 | 9120 | 21780 + Allocating distributions + Setting up device map and neighbor list + Component labels: 1 + label=0, affinity=-0.900000, volume fraction==0.417582 + Initializing distributions + Initializing phase field + Affinities - rank 0: + Main: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + Thread 1: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + Thread 2: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + Thread 3: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + Thread 4: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + Affinities - rank 0: + Main: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + Thread 1: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + Thread 2: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + Thread 3: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + Thread 4: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + ******************************************************** + CPU time = 0.001501 + Lattice update rate (per core)= 6.074861 MLUPS + Lattice update rate (per MPI process)= 6.074861 MLUPS + (flatten density field) -In this example we simulate a steady-state flow with a constant driving force. diff --git a/docs/source/userGuide/models/mrt/mrt.rst b/docs/source/userGuide/models/mrt/mrt.rst index eddf10d4..55b4f158 100644 --- a/docs/source/userGuide/models/mrt/mrt.rst +++ b/docs/source/userGuide/models/mrt/mrt.rst @@ -225,4 +225,5 @@ Example Input File InletLayers = 0, 0, 10 // specify 10 layers along the z-inlet BC = 0 // boundary condition type (0 for periodic) } - + Visualization { + } diff --git a/models/ColorModel.cpp b/models/ColorModel.cpp index c7caa292..c5eff406 100644 --- a/models/ColorModel.cpp +++ b/models/ColorModel.cpp @@ -184,6 +184,7 @@ void ScaLBL_ColorModel::ReadParams(string filename){ domain_db->putScalar( "BC", BoundaryCondition ); } else if (protocol == "fractional flow"){ + if (rank == 0) printf("Using fractional flow protocol \n"); if (BoundaryCondition != 0 && BoundaryCondition != 5){ BoundaryCondition = 0; if (rank==0) printf("WARNING: protocol (fractional flow) supports only full periodic boundary condition \n"); @@ -191,6 +192,7 @@ void ScaLBL_ColorModel::ReadParams(string filename){ domain_db->putScalar( "BC", BoundaryCondition ); } else if (protocol == "centrifuge"){ + if (rank == 0) printf("Using centrifuge protocol \n"); if (BoundaryCondition != 3 ){ BoundaryCondition = 3; if (rank==0) printf("WARNING: protocol (centrifuge) supports only constant pressure boundary condition \n"); @@ -617,6 +619,9 @@ double ScaLBL_ColorModel::Run(int returntime){ tolerance = analysis_db->getScalar( "tolerance" ); } + auto WettingConvention = color_db->getWithDefault( "WettingConvention", "none" ); + + runAnalysis analysis( current_db, rank_info, ScaLBL_Comm, Dm, Np, Regular, Map ); auto t1 = std::chrono::system_clock::now(); int CURRENT_TIMESTEP = 0; @@ -872,7 +877,28 @@ double ScaLBL_ColorModel::Run(int returntime){ fprintf(kr_log_file,"%.5g %.5g %.5g %.5g ",kAeff_filmA, kBeff_filmA, kAeff_filmB,kBeff_filmB); fprintf(kr_log_file,"%.5g %.5g %.5g %.5g %.5g\n",pAB,pAB_connected,viscous_pressure_drop,Ca,Mobility); fclose(kr_log_file); - + + /* SCAL file */ + if (WettingConvention == "SCAL"){ + WriteHeader=false; + FILE * scal_log_file = fopen("SCAL.csv","r"); + if (scal_log_file != NULL) + fclose(scal_log_file); + else + WriteHeader=true; + scal_log_file = fopen("SCAL.csv","a"); + if (WriteHeader){ + fprintf(scal_log_file,"timesteps sat.water eff.perm.oil eff.perm.water eff.perm.oil.connected eff.perm.water.connected eff.perm.oil.disconnected eff.perm.water.disconnected "); + fprintf(scal_log_file,"eff.perm.oil.upper.bound eff.perm.water.lower.bound eff.perm.oil.lower.bound eff.perm.water.upper.bound "); + fprintf(scal_log_file,"cap.pressure cap.pressure.connected pressure.drop Ca M\n"); + } + fprintf(scal_log_file,"%i %.5g %.5g %.5g %.5g %.5g %.5g %.5g ",CURRENT_TIMESTEP,current_saturation,kAeff,kBeff,kAeff_connected,kBeff_connected,kAeff_disconnected,kBeff_disconnected); + fprintf(scal_log_file,"%.5g %.5g %.5g %.5g ",kAeff_filmA, kBeff_filmA, kAeff_filmB,kBeff_filmB); + fprintf(scal_log_file,"%.5g %.5g %.5g %.5g %.5g\n",pAB,pAB_connected,viscous_pressure_drop,Ca,Mobility); + fclose(scal_log_file); + /****************/ + } + printf(" Measured capillary number %f \n ",Ca); } if (SET_CAPILLARY_NUMBER ){ From 7d525e999b2de7d779f83421bc117ad676d4708f Mon Sep 17 00:00:00 2001 From: Rex Zhe Li Date: Thu, 2 Dec 2021 23:32:57 +1100 Subject: [PATCH 44/54] enable different types of solid BC for Poisson solver; to be built and tested --- common/ScaLBL.cpp | 8 ++++ common/ScaLBL.h | 3 ++ cpu/D3Q7BC.cpp | 20 +++++++++ models/PoissonSolver.cpp | 89 +++++++++++++++++++++------------------- models/PoissonSolver.h | 3 +- 5 files changed, 80 insertions(+), 43 deletions(-) diff --git a/common/ScaLBL.cpp b/common/ScaLBL.cpp index 6a405c0a..420e9879 100644 --- a/common/ScaLBL.cpp +++ b/common/ScaLBL.cpp @@ -1356,6 +1356,14 @@ void ScaLBL_Communicator::SolidNeumannD3Q7(double *fq, double *BoundaryValue){ ScaLBL_Solid_Neumann_D3Q7(fq,BoundaryValue,bb_dist,bb_interactions,n_bb_d3q7); } +void ScaLBL_Communicator::SolidDirichletAndNeumannD3Q7(double *fq, double *BoundaryValue, int *BoundaryLabel){ + // fq is a D3Q7 distribution + // BoundaryValues is a list of values to assign at bounce-back solid sites + // BoundaryLabel: is a list of integer labels indicating the type of BCs + // 1-> Dirichlet BC; 2-> Neumann BC. + ScaLBL_Solid_DirichletAndNeumann_D3Q7(fq,BoundaryValue,BoundaryLabel,bb_dist,bb_interactions,n_bb_d3q7); +} + void ScaLBL_Communicator::SolidSlippingVelocityBCD3Q19(double *fq, double *zeta_potential, double *ElectricField, double *SolidGrad, double epsilon_LB, double tau, double rho0, double den_scale,double h, double time_conv){ // fq is a D3Q19 distribution diff --git a/common/ScaLBL.h b/common/ScaLBL.h index 816c1551..30a74113 100644 --- a/common/ScaLBL.h +++ b/common/ScaLBL.h @@ -593,6 +593,8 @@ extern "C" void ScaLBL_Solid_Dirichlet_D3Q7(double *dist,double *BoundaryValue,i extern "C" void ScaLBL_Solid_Neumann_D3Q7(double *dist,double *BoundaryValue,int *BounceBackDist_list,int *BounceBackSolid_list,int N); +extern "C" void ScaLBL_Solid_DirichletAndNeumann_D3Q7(double *dist,double *BoundaryValue,int *BoundaryLabel,int *BounceBackDist_list,int *BounceBackSolid_list,int N); + extern "C" void ScaLBL_Solid_SlippingVelocityBC_D3Q19(double *dist, double *zeta_potential, double *ElectricField, double *SolidGrad, double epsilon_LB, double tau, double rho0,double den_scale, double h, double time_conv, int *BounceBackDist_list, int *BounceBackSolid_list, int *FluidBoundary_list, @@ -700,6 +702,7 @@ public: void SetupBounceBackList(IntArray &Map, signed char *id, int Np, bool SlippingVelBC=false); void SolidDirichletD3Q7(double *fq, double *BoundaryValue); void SolidNeumannD3Q7(double *fq, double *BoundaryValue); + void SolidDirichletAndNeumannD3Q7(double *fq, double *BoundaryValue, int *BoundaryLabel); void SolidSlippingVelocityBCD3Q19(double *fq, double *zeta_potential, double *ElectricField, double *SolidGrad, double epslion_LB, double tau, double rho0, double den_scale,double h, double time_conv); diff --git a/cpu/D3Q7BC.cpp b/cpu/D3Q7BC.cpp index 1c255495..b2fa75f7 100644 --- a/cpu/D3Q7BC.cpp +++ b/cpu/D3Q7BC.cpp @@ -30,6 +30,26 @@ extern "C" void ScaLBL_Solid_Neumann_D3Q7(double *dist,double *BoundaryValue,int } } +extern "C" void ScaLBL_Solid_DirichletAndNeumann_D3Q7(double *dist,double *BoundaryValue,int* BoundaryLabel,int *BounceBackDist_list,int *BounceBackSolid_list,int N){ + + int idx; + int iq,ib; + double value_b,value_b_label,value_q; + for (idx=0; idxkeyExists( "BC_Solid" )){ - BoundaryConditionSolid = electric_db->getScalar( "BC_Solid" ); + // BC_solid=1: Dirichlet-type surfacen potential + // BC_solid=2: Neumann-type surfacen charge density + BoundaryConditionSolidList.push_back(1); + if (electric_db->keyExists( "BC_SolidList" )){ + BoundaryConditionSolidList.clear(); + BoundaryConditionSolidList = electric_db->getVector( "BC_SolidList" ); } // Read boundary condition for electric potential // BC = 0: normal periodic BC @@ -133,19 +136,8 @@ void ScaLBL_Poisson::ReadParams(string filename){ else{ if (rank==0) printf("LB-Poisson Solver: tolerance_method=%s cannot be identified!\n",tolerance_method.c_str()); } - - switch (BoundaryConditionSolid){ - case 1: - if (rank==0) printf("LB-Poisson Solver: solid boundary: Dirichlet-type surfacen potential is assigned\n"); - break; - case 2: - if (rank==0) printf("LB-Poisson Solver: solid boundary: Neumann-type surfacen charge density is assigned\n"); - break; - default: - if (rank==0) printf("LB-Poisson Solver: solid boundary: Dirichlet-type surfacen potential is assigned\n"); - break; - } } + void ScaLBL_Poisson::SetDomain(){ Dm = std::shared_ptr(new Domain(domain_db,comm)); // full domain for analysis Mask = std::shared_ptr(new Domain(domain_db,comm)); // mask domain removes immobile phases @@ -243,17 +235,18 @@ void ScaLBL_Poisson::ReadInput(){ if (rank == 0) cout << " Domain set." << endl; } -void ScaLBL_Poisson::AssignSolidBoundary(double *poisson_solid) +void ScaLBL_Poisson::AssignSolidBoundary(double *poisson_solid, int *poisson_solid_BClabel) { signed char VALUE=0; double AFFINITY=0.f; + int BoundaryConditionSolid=0; auto LabelList = electric_db->getVector( "SolidLabels" ); auto AffinityList = electric_db->getVector( "SolidValues" ); size_t NLABELS = LabelList.size(); - if (NLABELS != AffinityList.size()){ - ERROR("Error: LB-Poisson Solver: SolidLabels and SolidValues must be the same length! \n"); + if (NLABELS != AffinityList.size() || NLABELS != BoundaryConditionSolidList.size()){ + ERROR("Error: LB-Poisson Solver: BC_SolidList, SolidLabels and SolidValues all must be of the same length! \n"); } std::vector label_count( NLABELS, 0.0 ); @@ -268,10 +261,15 @@ void ScaLBL_Poisson::AssignSolidBoundary(double *poisson_solid) int n = k*Nx*Ny+j*Nx+i; VALUE=Mask->id[n]; AFFINITY=0.f; + BoundaryConditionSolid=0; // Assign the affinity from the paired list for (unsigned int idx=0; idx < NLABELS; idx++){ if (VALUE == LabelList[idx]){ AFFINITY=AffinityList[idx]; + BoundaryConditionSolid=BoundaryConditionSolidList[idx]; + if (BoundaryConditionSolid!=1 && BoundaryConditionSolid!=2){ + ERROR("Error: LB-Poisson Solver: Note only BC_SolidList of 1 or 2 is supported!\n"); + } //NOTE need to convert the user input phys unit to LB unit if (BoundaryConditionSolid==2){ //for BCS=1, i.e. Dirichlet-type, no need for unit conversion @@ -283,6 +281,7 @@ void ScaLBL_Poisson::AssignSolidBoundary(double *poisson_solid) } } poisson_solid[n] = AFFINITY; + poisson_solid_BClabel[n] = BoundaryConditionSolid; } } } @@ -295,17 +294,16 @@ void ScaLBL_Poisson::AssignSolidBoundary(double *poisson_solid) for (unsigned int idx=0; idxBarrier(); ScaLBL_D3Q7_Poisson_Init(dvcMap, fq, Psi, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); ScaLBL_D3Q7_Poisson_Init(dvcMap, fq, Psi, 0, ScaLBL_Comm->LastExterior(), Np); delete [] psi_host; + delete [] psi_BCLabel_host; //extra treatment for halo layer //if (BoundaryCondition==1){ @@ -749,23 +752,25 @@ void ScaLBL_Poisson::SolveElectricPotentialAAeven(int timestep_from_Study){ void ScaLBL_Poisson::SolvePoissonAAodd(double *ChargeDensity){ ScaLBL_D3Q7_AAodd_Poisson(NeighborList, dvcMap, fq, ChargeDensity, Psi, ElectricField, tau, epsilon_LB, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); ScaLBL_D3Q7_AAodd_Poisson(NeighborList, dvcMap, fq, ChargeDensity, Psi, ElectricField, tau, epsilon_LB, 0, ScaLBL_Comm->LastExterior(), Np); - if (BoundaryConditionSolid==1){ - ScaLBL_Comm->SolidDirichletD3Q7(fq, Psi); - } - else if (BoundaryConditionSolid==2){ - ScaLBL_Comm->SolidNeumannD3Q7(fq, Psi); - } + ScaLBL_Comm->SolidDirichletAndNeumannD3Q7(fq, Psi, Psi_BCLabel); + //if (BoundaryConditionSolid==1){ + // ScaLBL_Comm->SolidDirichletD3Q7(fq, Psi); + //} + //else if (BoundaryConditionSolid==2){ + // ScaLBL_Comm->SolidNeumannD3Q7(fq, Psi); + //} } void ScaLBL_Poisson::SolvePoissonAAeven(double *ChargeDensity){ ScaLBL_D3Q7_AAeven_Poisson(dvcMap, fq, ChargeDensity, Psi, ElectricField, tau, epsilon_LB, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); ScaLBL_D3Q7_AAeven_Poisson(dvcMap, fq, ChargeDensity, Psi, ElectricField, tau, epsilon_LB, 0, ScaLBL_Comm->LastExterior(), Np); - if (BoundaryConditionSolid==1){ - ScaLBL_Comm->SolidDirichletD3Q7(fq, Psi); - } - else if (BoundaryConditionSolid==2){ - ScaLBL_Comm->SolidNeumannD3Q7(fq, Psi); - } + ScaLBL_Comm->SolidDirichletAndNeumannD3Q7(fq, Psi, Psi_BCLabel); + //if (BoundaryConditionSolid==1){ + // ScaLBL_Comm->SolidDirichletD3Q7(fq, Psi); + //} + //else if (BoundaryConditionSolid==2){ + // ScaLBL_Comm->SolidNeumannD3Q7(fq, Psi); + //} } void ScaLBL_Poisson::DummyChargeDensity(){ diff --git a/models/PoissonSolver.h b/models/PoissonSolver.h index 81312cab..07e03b05 100644 --- a/models/PoissonSolver.h +++ b/models/PoissonSolver.h @@ -46,7 +46,7 @@ public: int analysis_interval; int BoundaryConditionInlet; int BoundaryConditionOutlet; - int BoundaryConditionSolid; + vector BoundaryConditionSolidList; double tau; double tolerance; std::string tolerance_method; @@ -86,6 +86,7 @@ public: //signed char *dvcID; double *fq; double *Psi; + int *Psi_BCLabel; double *ElectricField; double *ChargeDensityDummy;// for debugging double *ResidualError; From 95d01c4e286d8ae364aa932f4e45c465354248e4 Mon Sep 17 00:00:00 2001 From: Rex Zhe Li Date: Thu, 2 Dec 2021 07:47:49 -0500 Subject: [PATCH 45/54] fix bug --- models/PoissonSolver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/PoissonSolver.h b/models/PoissonSolver.h index 07e03b05..e36a1ea4 100644 --- a/models/PoissonSolver.h +++ b/models/PoissonSolver.h @@ -104,7 +104,7 @@ private: //int rank,nprocs; void LoadParams(std::shared_ptr db0); - void AssignSolidBoundary(double *poisson_solid); + void AssignSolidBoundary(double *poisson_solid, int *poisson_solid_label); void Potential_Init(double *psi_init); void ElectricField_LB_to_Phys(DoubleArray &Efield_reg); void SolveElectricPotentialAAodd(int timestep_from_Study); From 8800066c064819c4b9075c63eaa34cdd1d61c6c9 Mon Sep 17 00:00:00 2001 From: JamesEMcClure Date: Fri, 3 Dec 2021 08:53:36 -0500 Subject: [PATCH 46/54] Add "SCAL.csv" file for color model (#61) * add scal file * SCAL csv file * Update ColorModel.cpp Updated the SCAL output file, and removed some entries. Co-authored-by: Thomas Ramstad --- models/ColorModel.cpp | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/models/ColorModel.cpp b/models/ColorModel.cpp index 90f0f157..21315063 100644 --- a/models/ColorModel.cpp +++ b/models/ColorModel.cpp @@ -618,6 +618,7 @@ double ScaLBL_ColorModel::Run(int returntime) { bool SET_CAPILLARY_NUMBER = false; bool TRIGGER_FORCE_RESCALE = false; double tolerance = 0.01; + auto WettingConvention = color_db->getWithDefault( "WettingConvention", "none" ); auto current_db = db->cloneDatabase(); auto flow_db = db->getDatabase("FlowAdaptor"); int MIN_STEADY_TIMESTEPS = @@ -645,7 +646,7 @@ double ScaLBL_ColorModel::Run(int returntime) { if (analysis_db->keyExists("tolerance")) { tolerance = analysis_db->getScalar("tolerance"); } - + runAnalysis analysis(current_db, rank_info, ScaLBL_Comm, Dm, Np, Regular, Map); auto t1 = std::chrono::system_clock::now(); @@ -964,6 +965,41 @@ double ScaLBL_ColorModel::Run(int returntime) { fprintf(kr_log_file, "%.5g\n", eff_pres); fclose(kr_log_file); + if (WettingConvention == "SCAL"){ + WriteHeader = false; + FILE *scal_log_file = fopen("SCAL.csv", "r"); + if (scal_log_file != NULL) + fclose(scal_log_file); + else + WriteHeader = true; + scal_log_file = fopen("SCAL.csv", "a"); + if (WriteHeader) { + fprintf(scal_log_file, "timesteps sat.water "); + fprintf(scal_log_file, "eff.perm.oil.upper.bound " + "eff.perm.water.upper.bound "); + fprintf(scal_log_file, + "eff.perm.oil.lower.bound " + "eff.perm.water.lower.bound "); + fprintf(scal_log_file, "eff.perm.oil.disconnected " + "eff.perm.water.disconnected "); + fprintf(scal_log_file, + "cap.pressure cap.pressure.connected " + "Ca eff.pressure\n"); + } + fprintf(scal_log_file, "%i %.5g ", CURRENT_TIMESTEP, + current_saturation); + fprintf(scal_log_file, "%.5g %.5g ", kAeff_low, kBeff_low); + fprintf(scal_log_file, "%.5g %.5g ", kAeff_connected_low, + kBeff_connected_low); + fprintf(scal_log_file, "%.5g %.5g ", kAeff_disconnected, + kBeff_disconnected); + fprintf(scal_log_file, "%.5g %.5g %.5g ", pAB, + pAB_connected, Ca); + fprintf(scal_log_file, "%.5g\n", eff_pres); + fclose(scal_log_file); + + } + printf(" Measured capillary number %f \n ", Ca); } if (SET_CAPILLARY_NUMBER) { From 8fce93fc47600ad98ffb8b1f308f32b348133cc2 Mon Sep 17 00:00:00 2001 From: Rex Zhe Li Date: Mon, 6 Dec 2021 13:50:17 +1100 Subject: [PATCH 47/54] update solid BCs of Poisson solver in GPU;to be built and tested --- cuda/D3Q7BC.cu | 31 +++++++++++++++++++++++++++++++ hip/D3Q7BC.cu | 31 +++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/cuda/D3Q7BC.cu b/cuda/D3Q7BC.cu index b2af849d..0651694d 100644 --- a/cuda/D3Q7BC.cu +++ b/cuda/D3Q7BC.cu @@ -38,6 +38,28 @@ __global__ void dvc_ScaLBL_Solid_Neumann_D3Q7(double *dist, double *BoundaryValu } } +__global__ void dvc_ScaLBL_Solid_DirichletAndNeumann_D3Q7(double *dist, double *BoundaryValue,int *BoundaryLabel, int *BounceBackDist_list, int *BounceBackSolid_list, int count) +{ + + int idx; + int iq,ib; + double value_b,value_b_label,value_q; + idx = blockIdx.x*blockDim.x + threadIdx.x; + if (idx < count){ + iq = BounceBackDist_list[idx]; + ib = BounceBackSolid_list[idx]; + value_b = BoundaryValue[ib];//get boundary value from a solid site + value_b_label = BoundaryLabel[ib];//get boundary label (i.e. type of BC) from a solid site + value_q = dist[iq]; + if (value_b_label==1){//Dirichlet BC + dist[iq] = -1.0*value_q + value_b*0.25;//NOTE 0.25 is the speed of sound for D3Q7 lattice + } + if (value_b_label==2){//Neumann BC + dist[iq] = value_q + value_b; + } + } +} + __global__ void dvc_ScaLBL_Solid_SlippingVelocityBC_D3Q19(double *dist, double *zeta_potential, double *ElectricField, double *SolidGrad, double epsilon_LB, double tau, double rho0,double den_scale, double h, double time_conv, int *BounceBackDist_list, int *BounceBackSolid_list, int *FluidBoundary_list, @@ -733,6 +755,15 @@ extern "C" void ScaLBL_Solid_Neumann_D3Q7(double *dist, double *BoundaryValue, i } } +extern "C" void ScaLBL_Solid_DirichletAndNeumann_D3Q7(double *dist, double *BoundaryValue,int *BoundaryLabel, int *BounceBackDist_list, int *BounceBackSolid_list, int count){ + int GRID = count / 512 + 1; + dvc_ScaLBL_Solid_DirichletAndNeumann_D3Q7<<>>(dist, BoundaryValue, BoundaryLabel, BounceBackDist_list, BounceBackSolid_list, count); + cudaError_t err = cudaGetLastError(); + if (cudaSuccess != err){ + printf("CUDA error in ScaLBL_Solid_DirichletAndNeumann_D3Q7 (kernel): %s \n",cudaGetErrorString(err)); + } +} + extern "C" void ScaLBL_Solid_SlippingVelocityBC_D3Q19(double *dist, double *zeta_potential, double *ElectricField, double *SolidGrad, double epsilon_LB, double tau, double rho0,double den_scale, double h, double time_conv, int *BounceBackDist_list, int *BounceBackSolid_list, int *FluidBoundary_list, diff --git a/hip/D3Q7BC.cu b/hip/D3Q7BC.cu index 9413a68a..c594791a 100644 --- a/hip/D3Q7BC.cu +++ b/hip/D3Q7BC.cu @@ -37,6 +37,28 @@ __global__ void dvc_ScaLBL_Solid_Neumann_D3Q7(double *dist, double *BoundaryValu } } +__global__ void dvc_ScaLBL_Solid_DirichletAndNeumann_D3Q7(double *dist, double *BoundaryValue,int *BoundaryLabel, int *BounceBackDist_list, int *BounceBackSolid_list, int count) +{ + + int idx; + int iq,ib; + double value_b,value_b_label,value_q; + idx = blockIdx.x*blockDim.x + threadIdx.x; + if (idx < count){ + iq = BounceBackDist_list[idx]; + ib = BounceBackSolid_list[idx]; + value_b = BoundaryValue[ib];//get boundary value from a solid site + value_b_label = BoundaryLabel[ib];//get boundary label (i.e. type of BC) from a solid site + value_q = dist[iq]; + if (value_b_label==1){//Dirichlet BC + dist[iq] = -1.0*value_q + value_b*0.25;//NOTE 0.25 is the speed of sound for D3Q7 lattice + } + if (value_b_label==2){//Neumann BC + dist[iq] = value_q + value_b; + } + } +} + __global__ void dvc_ScaLBL_D3Q7_AAeven_Poisson_Potential_BC_z(int *list, double *dist, double Vin, int count, int Np) { int idx,n; @@ -409,6 +431,15 @@ extern "C" void ScaLBL_Solid_Neumann_D3Q7(double *dist, double *BoundaryValue, i } } +extern "C" void ScaLBL_Solid_DirichletAndNeumann_D3Q7(double *dist, double *BoundaryValue,int *BoundaryLabel, int *BounceBackDist_list, int *BounceBackSolid_list, int count){ + int GRID = count / 512 + 1; + dvc_ScaLBL_Solid_DirichletAndNeumann_D3Q7<<>>(dist, BoundaryValue, BoundaryLabel, BounceBackDist_list, BounceBackSolid_list, count); + cudaError_t err = cudaGetLastError(); + if (cudaSuccess != err){ + printf("hip error in ScaLBL_Solid_DirichletAndNeumann_D3Q7 (kernel): %s \n",cudaGetErrorString(err)); + } +} + extern "C" void ScaLBL_D3Q7_AAeven_Poisson_Potential_BC_z(int *list, double *dist, double Vin, int count, int Np){ int GRID = count / 512 + 1; dvc_ScaLBL_D3Q7_AAeven_Poisson_Potential_BC_z<<>>(list, dist, Vin, count, Np); From ba88a78afb0590009f73bc94c001f44fe15312b4 Mon Sep 17 00:00:00 2001 From: Rex Zhe Li Date: Mon, 6 Dec 2021 16:28:08 +1100 Subject: [PATCH 48/54] update periodic potential BC for inlet and outlet;to be built and tested --- models/PoissonSolver.cpp | 65 ++++++++++++++-------------------------- models/PoissonSolver.h | 6 ++-- 2 files changed, 25 insertions(+), 46 deletions(-) diff --git a/models/PoissonSolver.cpp b/models/PoissonSolver.cpp index a60433a5..b055b753 100644 --- a/models/PoissonSolver.cpp +++ b/models/PoissonSolver.cpp @@ -1,3 +1,4 @@ + sprintf(LocalRankFilename,"%s%s","ID.",LocalRankString); /* * Multi-relaxation time LBM Model */ @@ -18,7 +19,7 @@ ScaLBL_Poisson::ScaLBL_Poisson(int RANK, int NP, const Utilities::MPI& COMM): epsilon0(0),epsilon0_LB(0),epsilonR(0),epsilon_LB(0),Vin(0),Vout(0),Nx(0),Ny(0),Nz(0),N(0),Np(0),analysis_interval(0), chargeDen_dummy(0),WriteLog(0),nprocx(0),nprocy(0),nprocz(0), BoundaryConditionInlet(0),BoundaryConditionOutlet(0),BoundaryConditionSolidList(0),Lx(0),Ly(0),Lz(0), - Vin0(0),freqIn(0),t0_In(0),Vin_Type(0),Vout0(0),freqOut(0),t0_Out(0),Vout_Type(0), + Vin0(0),freqIn(0),PhaseShift_In(0),Vout0(0),freqOut(0),PhaseShift_Out(0), TestPeriodic(0),TestPeriodicTime(0),TestPeriodicTimeConv(0),TestPeriodicSaveInterval(0), comm(COMM) { @@ -403,8 +404,7 @@ void ScaLBL_Poisson::Potential_Init(double *psi_init){ //set up default boundary input parameters Vin0 = Vout0 = 1.0; //unit: [V] freqIn = freqOut = 50.0; //unit: [Hz] - t0_In = t0_Out = 0.0; //unit: [sec] - Vin_Type = Vout_Type = 1; //1->sin; 2->cos + PhaseShift_In = PhaseShift_Out = 0.0; //unit: [radian] Vin = 1.0; //Boundary-z (inlet) electric potential Vout = 1.0; //Boundary-Z (outlet) electric potential @@ -423,24 +423,12 @@ void ScaLBL_Poisson::Potential_Init(double *psi_init){ if (electric_db->keyExists( "freqIn" )){//unit: Hz freqIn = electric_db->getScalar( "freqIn" ); } - if (electric_db->keyExists( "t0_In" )){//timestep shift, unit: lt - t0_In = electric_db->getScalar( "t0_In" ); - } - if (electric_db->keyExists( "Vin_Type" )){ - //type=1 -> sine - //tyep=2 -> cosine - Vin_Type = electric_db->getScalar( "Vin_Type" ); - if (Vin_Type>2 || Vin_Type<=0) ERROR("Error: user-input Vin_Type is currently not supported! \n"); + if (electric_db->keyExists( "PhaseShift_In" )){//phase shift, unit: radian + PhaseShift_In = electric_db->getScalar( "PhaseShift_In" ); } if (rank==0){ - if (Vin_Type==1){ - printf("LB-Poisson Solver: inlet boundary; periodic electric potential Vin = %.3g*Sin[2*pi*%.3g*(t+%.3g)] [V]\n",Vin0,freqIn,t0_In); - printf(" V0 = %.3g [V], frequency = %.3g [Hz], timestep shift = %.3g [sec] \n",Vin0,freqIn,t0_In); - } - else if (Vin_Type==2){ - printf("LB-Poisson Solver: inlet boundary; periodic electric potential Vin = %.3g*Cos[2*pi*%.3g*(t+%.3g)] [V] \n",Vin0,freqIn,t0_In); - printf(" V0 = %.3g [V], frequency = %.3g [Hz], timestep shift = %.3g [sec] \n",Vin0,freqIn,t0_In); - } + printf("LB-Poisson Solver: inlet boundary; periodic electric potential Vin = %.3g*Cos[2*pi*%.3g*t+%.3g] [V] \n",Vin0,freqIn,PhaseShift_In); + printf(" V0 = %.3g [V], frequency = %.3g [Hz], phase shift = %.3g [radian] \n",Vin0,freqIn,PhaseShift_In); } break; } @@ -460,31 +448,19 @@ void ScaLBL_Poisson::Potential_Init(double *psi_init){ if (electric_db->keyExists( "freqOut" )){//unit: Hz freqOut = electric_db->getScalar( "freqOut" ); } - if (electric_db->keyExists( "t0_Out" )){//timestep shift, unit: lt - t0_Out = electric_db->getScalar( "t0_Out" ); - } - if (electric_db->keyExists( "Vout_Type" )){ - //type=1 -> sine - //tyep=2 -> cosine - Vout_Type = electric_db->getScalar( "Vout_Type" ); - if (Vout_Type>2 || Vin_Type<=0) ERROR("Error: user-input Vout_Type is currently not supported! \n"); + if (electric_db->keyExists( "PhaseShift_Out" )){//timestep shift, unit: lt + PhaseShift_Out = electric_db->getScalar( "PhaseShift_Out" ); } if (rank==0){ - if (Vout_Type==1){ - printf("LB-Poisson Solver: outlet boundary; periodic electric potential Vout = %.3g*Sin[2*pi*%.3g*(t+%.3g)] [V]\n",Vout0,freqOut,t0_Out); - printf(" V0 = %.3g [V], frequency = %.3g [Hz], timestep shift = %.3g [sec] \n",Vout0,freqOut,t0_Out); - } - else if (Vout_Type==2){ - printf("LB-Poisson Solver: outlet boundary; periodic electric potential Vout = %.3g*Cos[2*pi*%.3g*(t+%.3g)] [V]\n",Vout0,freqOut,t0_Out); - printf(" V0 = %.3g [V], frequency = %.3g [Hz], timestep shift = %.3g [sec] \n",Vout0,freqOut,t0_Out); - } + printf("LB-Poisson Solver: outlet boundary; periodic electric potential Vout = %.3g*Cos[2*pi*%.3g*t+%.3g] [V]\n",Vout0,freqOut,PhaseShift_Out); + printf(" V0 = %.3g [V], frequency = %.3g [Hz], timestep shift = %.3g [radian] \n",Vout0,freqOut,PhaseShift_Out); } break; } } //By default only periodic BC is applied and Vin=Vout=1.0, i.e. there is no potential gradient along Z-axis - if (BoundaryConditionInlet==2) Vin = getBoundaryVoltagefromPeriodicBC(Vin0,freqIn,t0_In,Vin_Type,0); - if (BoundaryConditionOutlet==2) Vout = getBoundaryVoltagefromPeriodicBC(Vout0,freqOut,t0_Out,Vout_Type,0); + if (BoundaryConditionInlet==2) Vin = getBoundaryVoltagefromPeriodicBC(Vin0,freqIn,PhaseShift_In,0); + if (BoundaryConditionOutlet==2) Vout = getBoundaryVoltagefromPeriodicBC(Vout0,freqOut,PhaseShift_Out,0); double slope = (Vout-Vin)/(Nz-2); double psi_linearized; for (int k=0;kD3Q7_Poisson_Potential_BC_z(NeighborList, fq, Vin, timestep); break; case 2: - Vin = getBoundaryVoltagefromPeriodicBC(Vin0,freqIn,t0_In,Vin_Type,timestep_from_Study); + Vin = getBoundaryVoltagefromPeriodicBC(Vin0,freqIn,PhaseShift_In,timestep_from_Study); ScaLBL_Comm->D3Q7_Poisson_Potential_BC_z(NeighborList, fq, Vin, timestep); break; } @@ -708,7 +684,7 @@ void ScaLBL_Poisson::SolveElectricPotentialAAodd(int timestep_from_Study){ ScaLBL_Comm->D3Q7_Poisson_Potential_BC_Z(NeighborList, fq, Vout, timestep); break; case 2: - Vout = getBoundaryVoltagefromPeriodicBC(Vout0,freqOut,t0_Out,Vout_Type,timestep_from_Study); + Vout = getBoundaryVoltagefromPeriodicBC(Vout0,freqOut,PhaseShift_Out,timestep_from_Study); ScaLBL_Comm->D3Q7_Poisson_Potential_BC_Z(NeighborList, fq, Vout, timestep); break; } @@ -729,7 +705,7 @@ void ScaLBL_Poisson::SolveElectricPotentialAAeven(int timestep_from_Study){ ScaLBL_Comm->D3Q7_Poisson_Potential_BC_z(NeighborList, fq, Vin, timestep); break; case 2: - Vin = getBoundaryVoltagefromPeriodicBC(Vin0,freqIn,t0_In,Vin_Type,timestep_from_Study); + Vin = getBoundaryVoltagefromPeriodicBC(Vin0,freqIn,PhaseShift_In,timestep_from_Study); ScaLBL_Comm->D3Q7_Poisson_Potential_BC_z(NeighborList, fq, Vin, timestep); break; } @@ -740,7 +716,7 @@ void ScaLBL_Poisson::SolveElectricPotentialAAeven(int timestep_from_Study){ ScaLBL_Comm->D3Q7_Poisson_Potential_BC_Z(NeighborList, fq, Vout, timestep); break; case 2: - Vout = getBoundaryVoltagefromPeriodicBC(Vout0,freqOut,t0_Out,Vout_Type,timestep_from_Study); + Vout = getBoundaryVoltagefromPeriodicBC(Vout0,freqOut,PhaseShift_Out,timestep_from_Study); ScaLBL_Comm->D3Q7_Poisson_Potential_BC_Z(NeighborList, fq, Vout, timestep); break; } @@ -752,6 +728,9 @@ void ScaLBL_Poisson::SolveElectricPotentialAAeven(int timestep_from_Study){ void ScaLBL_Poisson::SolvePoissonAAodd(double *ChargeDensity){ ScaLBL_D3Q7_AAodd_Poisson(NeighborList, dvcMap, fq, ChargeDensity, Psi, ElectricField, tau, epsilon_LB, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); ScaLBL_D3Q7_AAodd_Poisson(NeighborList, dvcMap, fq, ChargeDensity, Psi, ElectricField, tau, epsilon_LB, 0, ScaLBL_Comm->LastExterior(), Np); + //TODO: perhaps add another ScaLBL_Comm routine to update Psi values on solid boundary nodes. + //something like: + //ScaLBL_Comm->SolidDirichletBoundaryUpdates(Psi, Psi_BCLabel, timestep); ScaLBL_Comm->SolidDirichletAndNeumannD3Q7(fq, Psi, Psi_BCLabel); //if (BoundaryConditionSolid==1){ // ScaLBL_Comm->SolidDirichletD3Q7(fq, Psi); diff --git a/models/PoissonSolver.h b/models/PoissonSolver.h index e36a1ea4..94a6f36f 100644 --- a/models/PoissonSolver.h +++ b/models/PoissonSolver.h @@ -55,8 +55,8 @@ public: double Vin, Vout; double chargeDen_dummy;//for debugging bool WriteLog; - double Vin0,freqIn,t0_In,Vin_Type; - double Vout0,freqOut,t0_Out,Vout_Type; + double Vin0,freqIn,t0_In,PhaseShift_In; + double Vout0,freqOut,t0_Out,PhaseShift_Out; bool TestPeriodic; double TestPeriodicTime;//unit: [sec] double TestPeriodicTimeConv; //unit [sec/lt] @@ -113,7 +113,7 @@ private: void SolvePoissonAAodd(double *ChargeDensity); void SolvePoissonAAeven(double *ChargeDensity); void getConvergenceLog(int timestep,double error); - double getBoundaryVoltagefromPeriodicBC(double V0,double freq,double t0,int V_type,int time_step); + double getBoundaryVoltagefromPeriodicBC(double V0,double freq,double t0,int time_step); }; #endif From d61cb8571fd46359124e2d6c693cc0294e21da68 Mon Sep 17 00:00:00 2001 From: Rex Zhe Li Date: Mon, 6 Dec 2021 00:46:44 -0500 Subject: [PATCH 49/54] fix dumb typo --- models/PoissonSolver.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/models/PoissonSolver.cpp b/models/PoissonSolver.cpp index b055b753..8658ac07 100644 --- a/models/PoissonSolver.cpp +++ b/models/PoissonSolver.cpp @@ -1,4 +1,3 @@ - sprintf(LocalRankFilename,"%s%s","ID.",LocalRankString); /* * Multi-relaxation time LBM Model */ From 44965c17f38cd8eac413d0d9f2554bb0af9edcf4 Mon Sep 17 00:00:00 2001 From: James McClure Date: Thu, 9 Dec 2021 10:11:05 -0500 Subject: [PATCH 50/54] fix merge --- models/ColorModel.cpp | 2235 ++++++++++++++++++++++------------------- 1 file changed, 1177 insertions(+), 1058 deletions(-) diff --git a/models/ColorModel.cpp b/models/ColorModel.cpp index c5eff406..f63d7ee5 100644 --- a/models/ColorModel.cpp +++ b/models/ColorModel.cpp @@ -9,1123 +9,1242 @@ color lattice boltzmann model #include #include - -ScaLBL_ColorModel::ScaLBL_ColorModel(int RANK, int NP, const Utilities::MPI& COMM): - rank(RANK), nprocs(NP), Restart(0), timestep(0), timestepMax(0), - tauA(0), tauB(0), rhoA(0), rhoB(0), alpha(0), beta(0), - Fx(0), Fy(0), Fz(0), flux(0), din(0), dout(0), - inletA(0), inletB(0), outletA(0), outletB(0), - Nx(0), Ny(0), Nz(0), N(0), Np(0), nprocx(0), nprocy(0), nprocz(0), - BoundaryCondition(0), Lx(0), Ly(0), Lz(0), id(nullptr), - NeighborList(nullptr), dvcMap(nullptr), fq(nullptr), Aq(nullptr), Bq(nullptr), - Den(nullptr), Phi(nullptr), ColorGrad(nullptr), Velocity(nullptr), Pressure(nullptr), - comm(COMM) -{ - REVERSE_FLOW_DIRECTION = false; +ScaLBL_ColorModel::ScaLBL_ColorModel(int RANK, int NP, + const Utilities::MPI &COMM) + : rank(RANK), nprocs(NP), Restart(0), timestep(0), timestepMax(0), tauA(0), + tauB(0), rhoA(0), rhoB(0), alpha(0), beta(0), Fx(0), Fy(0), Fz(0), + flux(0), din(0), dout(0), inletA(0), inletB(0), outletA(0), outletB(0), + Nx(0), Ny(0), Nz(0), N(0), Np(0), nprocx(0), nprocy(0), nprocz(0), + BoundaryCondition(0), Lx(0), Ly(0), Lz(0), id(nullptr), + NeighborList(nullptr), dvcMap(nullptr), fq(nullptr), Aq(nullptr), + Bq(nullptr), Den(nullptr), Phi(nullptr), ColorGrad(nullptr), + Velocity(nullptr), Pressure(nullptr), comm(COMM) { + REVERSE_FLOW_DIRECTION = false; } -ScaLBL_ColorModel::~ScaLBL_ColorModel() -{ - delete [] id; - ScaLBL_FreeDeviceMemory( NeighborList ); - ScaLBL_FreeDeviceMemory( dvcMap ); - ScaLBL_FreeDeviceMemory( fq ); - ScaLBL_FreeDeviceMemory( Aq ); - ScaLBL_FreeDeviceMemory( Bq ); - ScaLBL_FreeDeviceMemory( Den ); - ScaLBL_FreeDeviceMemory( Phi ); - ScaLBL_FreeDeviceMemory( Pressure ); - ScaLBL_FreeDeviceMemory( Velocity ); - ScaLBL_FreeDeviceMemory( ColorGrad ); +ScaLBL_ColorModel::~ScaLBL_ColorModel() { + delete[] id; + ScaLBL_FreeDeviceMemory(NeighborList); + ScaLBL_FreeDeviceMemory(dvcMap); + ScaLBL_FreeDeviceMemory(fq); + ScaLBL_FreeDeviceMemory(Aq); + ScaLBL_FreeDeviceMemory(Bq); + ScaLBL_FreeDeviceMemory(Den); + ScaLBL_FreeDeviceMemory(Phi); + ScaLBL_FreeDeviceMemory(Pressure); + ScaLBL_FreeDeviceMemory(Velocity); + ScaLBL_FreeDeviceMemory(ColorGrad); } -/*void ScaLBL_ColorModel::WriteCheckpoint(const char *FILENAME, const double *cPhi, const double *cfq, int Np) -{ - int q,n; - double value; - ofstream File(FILENAME,ios::binary); - for (n=0; n(filename); + domain_db = db->getDatabase("Domain"); + color_db = db->getDatabase("Color"); + analysis_db = db->getDatabase("Analysis"); + vis_db = db->getDatabase("Visualization"); + + // set defaults + timestepMax = 100000; + tauA = tauB = 1.0; + rhoA = rhoB = 1.0; + Fx = Fy = Fz = 0.0; + alpha = 1e-3; + beta = 0.95; + Restart = false; + din = dout = 1.0; + flux = 0.0; + + // Color Model parameters + if (color_db->keyExists("timestepMax")) { + timestepMax = color_db->getScalar("timestepMax"); + } + if (color_db->keyExists("tauA")) { + tauA = color_db->getScalar("tauA"); + } + if (color_db->keyExists("tauB")) { + tauB = color_db->getScalar("tauB"); + } + if (color_db->keyExists("rhoA")) { + rhoA = color_db->getScalar("rhoA"); + } + if (color_db->keyExists("rhoB")) { + rhoB = color_db->getScalar("rhoB"); + } + if (color_db->keyExists("F")) { + Fx = color_db->getVector("F")[0]; + Fy = color_db->getVector("F")[1]; + Fz = color_db->getVector("F")[2]; + } + if (color_db->keyExists("alpha")) { + alpha = color_db->getScalar("alpha"); + } + if (color_db->keyExists("beta")) { + beta = color_db->getScalar("beta"); + } + if (color_db->keyExists("Restart")) { + Restart = color_db->getScalar("Restart"); + } + if (color_db->keyExists("din")) { + din = color_db->getScalar("din"); + } + if (color_db->keyExists("dout")) { + dout = color_db->getScalar("dout"); + } + if (color_db->keyExists("flux")) { + flux = color_db->getScalar("flux"); + } + inletA = 1.f; + inletB = 0.f; + outletA = 0.f; + outletB = 1.f; + + + BoundaryCondition = 0; + if (color_db->keyExists("BC")) { + BoundaryCondition = color_db->getScalar("BC"); + } else if (domain_db->keyExists("BC")) { + BoundaryCondition = domain_db->getScalar("BC"); + } + if (domain_db->keyExists("InletLayersPhase")) { + int inlet_layers_phase = domain_db->getScalar("InletLayersPhase"); + if (inlet_layers_phase == 2) { + inletA = 0.0; + inletB = 1.0; } } - File.close(); - -} - -void ScaLBL_ColorModel::ReadCheckpoint(char *FILENAME, double *cPhi, double *cfq, int Np) -{ - int q=0, n=0; - double value=0; - ifstream File(FILENAME,ios::binary); - for (n=0; nkeyExists("OutletLayersPhase")) { + int outlet_layers_phase = + domain_db->getScalar("OutletLayersPhase"); + if (outlet_layers_phase == 1) { + inletA = 1.0; + inletB = 0.0; } } - File.close(); -} - */ -void ScaLBL_ColorModel::ReadParams(string filename){ - // read the input database - db = std::make_shared( filename ); - domain_db = db->getDatabase( "Domain" ); - color_db = db->getDatabase( "Color" ); - analysis_db = db->getDatabase( "Analysis" ); - vis_db = db->getDatabase( "Visualization" ); - // set defaults - timestepMax = 100000; - tauA = tauB = 1.0; - rhoA = rhoB = 1.0; - Fx = Fy = Fz = 0.0; - alpha=1e-3; - beta=0.95; - Restart=false; - din=dout=1.0; - flux=0.0; - - // Color Model parameters - if (color_db->keyExists( "timestepMax" )){ - timestepMax = color_db->getScalar( "timestepMax" ); - } - if (color_db->keyExists( "tauA" )){ - tauA = color_db->getScalar( "tauA" ); - } - if (color_db->keyExists( "tauB" )){ - tauB = color_db->getScalar( "tauB" ); - } - if (color_db->keyExists( "rhoA" )){ - rhoA = color_db->getScalar( "rhoA" ); - } - if (color_db->keyExists( "rhoB" )){ - rhoB = color_db->getScalar( "rhoB" ); - } - if (color_db->keyExists( "F" )){ - Fx = color_db->getVector( "F" )[0]; - Fy = color_db->getVector( "F" )[1]; - Fz = color_db->getVector( "F" )[2]; - } - if (color_db->keyExists( "alpha" )){ - alpha = color_db->getScalar( "alpha" ); - } - if (color_db->keyExists( "beta" )){ - beta = color_db->getScalar( "beta" ); - } - if (color_db->keyExists( "Restart" )){ - Restart = color_db->getScalar( "Restart" ); - } - if (color_db->keyExists( "din" )){ - din = color_db->getScalar( "din" ); - } - if (color_db->keyExists( "dout" )){ - dout = color_db->getScalar( "dout" ); - } - if (color_db->keyExists( "flux" )){ - flux = color_db->getScalar( "flux" ); - } - inletA=1.f; - inletB=0.f; - outletA=0.f; - outletB=1.f; - //if (BoundaryCondition==4) flux *= rhoA; // mass flux must adjust for density (see formulation for details) - - BoundaryCondition = 0; - if (color_db->keyExists( "BC" )){ - BoundaryCondition = color_db->getScalar( "BC" ); - } - else if (domain_db->keyExists( "BC" )){ - BoundaryCondition = domain_db->getScalar( "BC" ); - } - if (domain_db->keyExists( "InletLayersPhase" )){ - int inlet_layers_phase = domain_db->getScalar( "InletLayersPhase" ); - if (inlet_layers_phase == 2 ) { - inletA = 0.0; - inletB = 1.0; - } - } - if (domain_db->keyExists( "OutletLayersPhase" )){ - int outlet_layers_phase = domain_db->getScalar( "OutletLayersPhase" ); - if (outlet_layers_phase == 1 ) { - inletA = 1.0; - inletB = 0.0; - } - } - - // Override user-specified boundary condition for specific protocols - auto protocol = color_db->getWithDefault( "protocol", "none" ); - if (protocol == "seed water"){ - if (BoundaryCondition != 0 && BoundaryCondition != 5){ - BoundaryCondition = 0; - if (rank==0) printf("WARNING: protocol (seed water) supports only full periodic boundary condition \n"); - } - domain_db->putScalar( "BC", BoundaryCondition ); - } - else if (protocol == "open connected oil"){ - if (BoundaryCondition != 0 && BoundaryCondition != 5){ - BoundaryCondition = 0; - if (rank==0) printf("WARNING: protocol (open connected oil) supports only full periodic boundary condition \n"); - } - domain_db->putScalar( "BC", BoundaryCondition ); - } - else if (protocol == "shell aggregation"){ - if (BoundaryCondition != 0 && BoundaryCondition != 5){ - BoundaryCondition = 0; - if (rank==0) printf("WARNING: protocol (shell aggregation) supports only full periodic boundary condition \n"); - } - domain_db->putScalar( "BC", BoundaryCondition ); - } - else if (protocol == "fractional flow"){ - if (rank == 0) printf("Using fractional flow protocol \n"); - if (BoundaryCondition != 0 && BoundaryCondition != 5){ - BoundaryCondition = 0; - if (rank==0) printf("WARNING: protocol (fractional flow) supports only full periodic boundary condition \n"); - } - domain_db->putScalar( "BC", BoundaryCondition ); - } - else if (protocol == "centrifuge"){ - if (rank == 0) printf("Using centrifuge protocol \n"); - if (BoundaryCondition != 3 ){ - BoundaryCondition = 3; - if (rank==0) printf("WARNING: protocol (centrifuge) supports only constant pressure boundary condition \n"); - } - domain_db->putScalar( "BC", BoundaryCondition ); - } - else if (protocol == "core flooding"){ - if (rank == 0) printf("Using core flooding protocol \n"); - if (BoundaryCondition != 4){ - BoundaryCondition = 4; - if (rank==0) printf("WARNING: protocol (core flooding) supports only volumetric flux boundary condition \n"); - } - domain_db->putScalar( "BC", BoundaryCondition ); - } + // Override user-specified boundary condition for specific protocols + auto protocol = color_db->getWithDefault("protocol", "none"); + if (protocol == "seed water") { + if (BoundaryCondition != 0 && BoundaryCondition != 5) { + BoundaryCondition = 0; + if (rank == 0) + printf("WARNING: protocol (seed water) supports only full " + "periodic boundary condition \n"); + } + domain_db->putScalar("BC", BoundaryCondition); + } else if (protocol == "open connected oil") { + if (BoundaryCondition != 0 && BoundaryCondition != 5) { + BoundaryCondition = 0; + if (rank == 0) + printf("WARNING: protocol (open connected oil) supports only " + "full periodic boundary condition \n"); + } + domain_db->putScalar("BC", BoundaryCondition); + } else if (protocol == "shell aggregation") { + if (BoundaryCondition != 0 && BoundaryCondition != 5) { + BoundaryCondition = 0; + if (rank == 0) + printf("WARNING: protocol (shell aggregation) supports only " + "full periodic boundary condition \n"); + } + domain_db->putScalar("BC", BoundaryCondition); + } else if (protocol == "fractional flow") { + if (BoundaryCondition != 0 && BoundaryCondition != 5) { + BoundaryCondition = 0; + if (rank == 0) + printf("WARNING: protocol (fractional flow) supports only full " + "periodic boundary condition \n"); + } + domain_db->putScalar("BC", BoundaryCondition); + } else if (protocol == "centrifuge") { + if (BoundaryCondition != 3) { + BoundaryCondition = 3; + if (rank == 0) + printf("WARNING: protocol (centrifuge) supports only constant " + "pressure boundary condition \n"); + } + domain_db->putScalar("BC", BoundaryCondition); + } else if (protocol == "core flooding") { + if (rank == 0) + printf("Using core flooding protocol \n"); + if (BoundaryCondition != 4) { + BoundaryCondition = 4; + if (rank == 0) + printf("WARNING: protocol (core flooding) supports only " + "volumetric flux boundary condition \n"); + } + domain_db->putScalar("BC", BoundaryCondition); + } } -void ScaLBL_ColorModel::SetDomain(){ - Dm = std::shared_ptr(new Domain(domain_db,comm)); // full domain for analysis - Mask = std::shared_ptr(new Domain(domain_db,comm)); // mask domain removes immobile phases - // domain parameters - Nx = Dm->Nx; - Ny = Dm->Ny; - Nz = Dm->Nz; - Lx = Dm->Lx; - Ly = Dm->Ly; - Lz = Dm->Lz; - N = Nx*Ny*Nz; - id = new signed char [N]; - for (int i=0; iid[i] = 1; // initialize this way - //Averages = std::shared_ptr ( new TwoPhase(Dm) ); // TwoPhase analysis object - Averages = std::shared_ptr ( new SubPhase(Dm) ); // TwoPhase analysis object - comm.barrier(); - Dm->CommInit(); - comm.barrier(); - // Read domain parameters - rank = Dm->rank(); - nprocx = Dm->nprocx(); - nprocy = Dm->nprocy(); - nprocz = Dm->nprocz(); + +void ScaLBL_ColorModel::SetDomain() { + Dm = std::shared_ptr( + new Domain(domain_db, comm)); // full domain for analysis + Mask = std::shared_ptr( + new Domain(domain_db, comm)); // mask domain removes immobile phases + // domain parameters + Nx = Dm->Nx; + Ny = Dm->Ny; + Nz = Dm->Nz; + Lx = Dm->Lx; + Ly = Dm->Ly; + Lz = Dm->Lz; + N = Nx * Ny * Nz; + id = new signed char[N]; + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = 1; // initialize this way + //Averages = std::shared_ptr ( new TwoPhase(Dm) ); // TwoPhase analysis object + Averages = + std::shared_ptr(new SubPhase(Dm)); // TwoPhase analysis object + comm.barrier(); + Dm->CommInit(); + comm.barrier(); + // Read domain parameters + rank = Dm->rank(); + nprocx = Dm->nprocx(); + nprocy = Dm->nprocy(); + nprocz = Dm->nprocz(); } -void ScaLBL_ColorModel::ReadInput(){ - - sprintf(LocalRankString,"%05d",rank); - sprintf(LocalRankFilename,"%s%s","ID.",LocalRankString); - sprintf(LocalRestartFile,"%s%s","Restart.",LocalRankString); - - if (color_db->keyExists( "image_sequence" )){ - auto ImageList = color_db->getVector( "image_sequence"); - int IMAGE_INDEX = color_db->getWithDefault( "image_index", 0 ); - std::string first_image = ImageList[IMAGE_INDEX]; - Mask->Decomp(first_image); - IMAGE_INDEX++; - } - else if (domain_db->keyExists( "GridFile" )){ +void ScaLBL_ColorModel::ReadInput() { + + sprintf(LocalRankString, "%05d", rank); + sprintf(LocalRankFilename, "%s%s", "ID.", LocalRankString); + sprintf(LocalRestartFile, "%s%s", "Restart.", LocalRankString); + + if (color_db->keyExists("image_sequence")) { + auto ImageList = color_db->getVector("image_sequence"); + int IMAGE_INDEX = color_db->getWithDefault("image_index", 0); + std::string first_image = ImageList[IMAGE_INDEX]; + Mask->Decomp(first_image); + IMAGE_INDEX++; + } else if (domain_db->keyExists("GridFile")) { // Read the local domain data - auto input_id = readMicroCT( *domain_db, MPI_COMM_WORLD ); + auto input_id = readMicroCT(*domain_db, MPI_COMM_WORLD); // Fill the halo (assuming GCW of 1) - array size0 = { (int) input_id.size(0), (int) input_id.size(1), (int) input_id.size(2) }; - ArraySize size1 = { (size_t) Mask->Nx, (size_t) Mask->Ny, (size_t) Mask->Nz }; - ASSERT( (int) size1[0] == size0[0]+2 && (int) size1[1] == size0[1]+2 && (int) size1[2] == size0[2]+2 ); - fillHalo fill( MPI_COMM_WORLD, Mask->rank_info, size0, { 1, 1, 1 }, 0, 1 ); + array size0 = {(int)input_id.size(0), (int)input_id.size(1), + (int)input_id.size(2)}; + ArraySize size1 = {(size_t)Mask->Nx, (size_t)Mask->Ny, + (size_t)Mask->Nz}; + ASSERT((int)size1[0] == size0[0] + 2 && (int)size1[1] == size0[1] + 2 && + (int)size1[2] == size0[2] + 2); + fillHalo fill(MPI_COMM_WORLD, Mask->rank_info, size0, + {1, 1, 1}, 0, 1); Array id_view; - id_view.viewRaw( size1, Mask->id.data() ); - fill.copy( input_id, id_view ); - fill.fill( id_view ); - } - else if (domain_db->keyExists( "Filename" )){ - auto Filename = domain_db->getScalar( "Filename" ); - Mask->Decomp(Filename); - } - else{ - Mask->ReadIDs(); - } - for (int i=0; iid[i]; // save what was read - - // Generate the signed distance map - // Initialize the domain and communication - Array id_solid(Nx,Ny,Nz); - // Solve for the position of the solid phase - for (int k=0;kid[n]; - if (label > 0) id_solid(i,j,k) = 1; - else id_solid(i,j,k) = 0; - } - } - } - // Initialize the signed distance function - for (int k=0;kSDs(i,j,k) = 2.0*double(id_solid(i,j,k))-1.0; - } - } - } -// MeanFilter(Averages->SDs); - Minkowski Solid(Dm); - if (rank==0) printf("Initialized solid phase -- Converting to Signed Distance function \n"); - CalcDist(Averages->SDs,id_solid,*Mask); - Solid.ComputeScalar(Averages->SDs,0.0); - /* save averages */ - Averages->solid.V = Solid.Vi; - Averages->solid.A = Solid.Ai; - Averages->solid.H = Solid.Ji; - Averages->solid.X = Solid.Xi; - Averages->gsolid.V = Solid.Vi_global; - Averages->gsolid.A = Solid.Ai_global; - Averages->gsolid.H = Solid.Ji_global; - Averages->gsolid.X = Solid.Xi_global; - /* write to file */ - if (rank == 0) { - FILE *SOLID = fopen("solid.csv","w"); - fprintf(SOLID,"Vs As Hs Xs\n"); - fprintf(SOLID,"%.8g %.8g %.8g %.8g\n",Solid.Vi_global,Solid.Ai_global,Solid.Ji_global,Solid.Xi_global); - fclose(SOLID); - } - if (rank == 0) cout << "Domain set." << endl; - - Averages->SetParams(rhoA,rhoB,tauA,tauB,Fx,Fy,Fz,alpha,beta); + id_view.viewRaw(size1, Mask->id.data()); + fill.copy(input_id, id_view); + fill.fill(id_view); + } else if (domain_db->keyExists("Filename")) { + auto Filename = domain_db->getScalar("Filename"); + Mask->Decomp(Filename); + } else { + Mask->ReadIDs(); + } + for (int i = 0; i < Nx * Ny * Nz; i++) + id[i] = Mask->id[i]; // save what was read + + // Generate the signed distance map + // Initialize the domain and communication + Array id_solid(Nx, Ny, Nz); + // Solve for the position of the solid phase + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + // Initialize the solid phase + signed char label = Mask->id[n]; + if (label > 0) + id_solid(i, j, k) = 1; + else + id_solid(i, j, k) = 0; + } + } + } + // Initialize the signed distance function + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + // Initialize distance to +/- 1 + Averages->SDs(i, j, k) = 2.0 * double(id_solid(i, j, k)) - 1.0; + } + } + } + // MeanFilter(Averages->SDs); + Minkowski Solid(Dm); + if (rank == 0) + printf("Initialized solid phase -- Converting to Signed Distance " + "function \n"); + CalcDist(Averages->SDs, id_solid, *Mask); + Solid.ComputeScalar(Averages->SDs, 0.0); + /* save averages */ + Averages->solid.V = Solid.Vi; + Averages->solid.A = Solid.Ai; + Averages->solid.H = Solid.Ji; + Averages->solid.X = Solid.Xi; + Averages->gsolid.V = Solid.Vi_global; + Averages->gsolid.A = Solid.Ai_global; + Averages->gsolid.H = Solid.Ji_global; + Averages->gsolid.X = Solid.Xi_global; + /* write to file */ + if (rank == 0) { + FILE *SOLID = fopen("solid.csv", "w"); + fprintf(SOLID, "Vs As Hs Xs\n"); + fprintf(SOLID, "%.8g %.8g %.8g %.8g\n", Solid.Vi_global, + Solid.Ai_global, Solid.Ji_global, Solid.Xi_global); + fclose(SOLID); + } + if (rank == 0) + cout << "Domain set." << endl; + + Averages->SetParams(rhoA, rhoB, tauA, tauB, Fx, Fy, Fz, alpha, beta); } -void ScaLBL_ColorModel::AssignComponentLabels(double *phase) -{ - size_t NLABELS=0; - signed char VALUE=0; - double AFFINITY=0.f; +void ScaLBL_ColorModel::AssignComponentLabels(double *phase) { + size_t NLABELS = 0; + signed char VALUE = 0; + double AFFINITY = 0.f; - auto LabelList = color_db->getVector( "ComponentLabels" ); - auto AffinityList = color_db->getVector( "ComponentAffinity" ); - auto WettingConvention = color_db->getWithDefault( "WettingConvention", "none" ); + auto LabelList = color_db->getVector("ComponentLabels"); + auto AffinityList = color_db->getVector("ComponentAffinity"); + auto WettingConvention = + color_db->getWithDefault("WettingConvention", "none"); - NLABELS=LabelList.size(); - if (NLABELS != AffinityList.size()){ - ERROR("Error: ComponentLabels and ComponentAffinity must be the same length! \n"); - } + NLABELS = LabelList.size(); + if (NLABELS != AffinityList.size()) { + ERROR("Error: ComponentLabels and ComponentAffinity must be the same " + "length! \n"); + } - if (WettingConvention == "SCAL"){ - for (size_t idx=0; idxid[n] = 0; // set mask to zero since this is an immobile component - } - } - // fluid labels are reserved - if (VALUE == 1) AFFINITY=1.0; - else if (VALUE == 2) AFFINITY=-1.0; - phase[n] = AFFINITY; - } - } - } + double *label_count; + double *label_count_global; + label_count = new double[NLABELS]; + label_count_global = new double[NLABELS]; + // Assign the labels + for (size_t idx = 0; idx < NLABELS; idx++) + label_count[idx] = 0; - // Set Dm to match Mask - for (int i=0; iid[i] = Mask->id[i]; - - for (size_t idx=0; idxComm.sumReduce( label_count[idx]); + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int n = k * Nx * Ny + j * Nx + i; + VALUE = id[n]; + // Assign the affinity from the paired list + for (unsigned int idx = 0; idx < NLABELS; idx++) { + if (VALUE == LabelList[idx]) { + AFFINITY = AffinityList[idx]; + label_count[idx] += 1.0; + idx = NLABELS; + } + } + // fluid labels are reserved + if (VALUE == 1) + AFFINITY = 1.0; + else if (VALUE == 2) + AFFINITY = -1.0; + phase[n] = AFFINITY; + } + } + } - if (rank==0){ - printf("Component labels: %lu \n",NLABELS); - for (unsigned int idx=0; idxid[i] = Mask->id[i]; + for (size_t idx = 0; idx < NLABELS; idx++) + label_count_global[idx] = Dm->Comm.sumReduce(label_count[idx]); + + if (rank == 0) { + printf("Component labels: %lu \n", NLABELS); + for (unsigned int idx = 0; idx < NLABELS; idx++) { + VALUE = LabelList[idx]; + AFFINITY = AffinityList[idx]; + double volume_fraction = + double(label_count_global[idx]) / + double((Nx - 2) * (Ny - 2) * (Nz - 2) * nprocs); + printf(" label=%d, affinity=%f, volume fraction==%f\n", VALUE, + AFFINITY, volume_fraction); + } + } } +void ScaLBL_ColorModel::Create() { + /* + * This function creates the variables needed to run a LBM + */ + //......................................................... + // don't perform computations at the eight corners + //id[0] = id[Nx-1] = id[(Ny-1)*Nx] = id[(Ny-1)*Nx + Nx-1] = 0; + //id[(Nz-1)*Nx*Ny] = id[(Nz-1)*Nx*Ny+Nx-1] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx + Nx-1] = 0; -void ScaLBL_ColorModel::Create(){ - /* - * This function creates the variables needed to run a LBM - */ - //......................................................... - // don't perform computations at the eight corners - //id[0] = id[Nx-1] = id[(Ny-1)*Nx] = id[(Ny-1)*Nx + Nx-1] = 0; - //id[(Nz-1)*Nx*Ny] = id[(Nz-1)*Nx*Ny+Nx-1] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx] = id[(Nz-1)*Nx*Ny+(Ny-1)*Nx + Nx-1] = 0; + //......................................................... + // Initialize communication structures in averaging domain + for (int i = 0; i < Nx * Ny * Nz; i++) + Dm->id[i] = Mask->id[i]; + Mask->CommInit(); + Np = Mask->PoreCount(); + //........................................................................... + if (rank == 0) + printf("Create ScaLBL_Communicator \n"); + // Create a communicator for the device (will use optimized layout) + // ScaLBL_Communicator ScaLBL_Comm(Mask); // original + ScaLBL_Comm = + std::shared_ptr(new ScaLBL_Communicator(Mask)); + ScaLBL_Comm_Regular = + std::shared_ptr(new ScaLBL_Communicator(Mask)); - //......................................................... - // Initialize communication structures in averaging domain - for (int i=0; iid[i] = Mask->id[i]; - Mask->CommInit(); - Np=Mask->PoreCount(); - //........................................................................... - if (rank==0) printf ("Create ScaLBL_Communicator \n"); - // Create a communicator for the device (will use optimized layout) - // ScaLBL_Communicator ScaLBL_Comm(Mask); // original - ScaLBL_Comm = std::shared_ptr(new ScaLBL_Communicator(Mask)); - ScaLBL_Comm_Regular = std::shared_ptr(new ScaLBL_Communicator(Mask)); + int Npad = (Np / 16 + 2) * 16; + if (rank == 0) + printf("Set up memory efficient layout, %i | %i | %i \n", Np, Npad, N); + Map.resize(Nx, Ny, Nz); + Map.fill(-2); + auto neighborList = new int[18 * Npad]; + Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map, neighborList, + Mask->id.data(), Np, 1); + comm.barrier(); - int Npad=(Np/16 + 2)*16; - if (rank==0) printf ("Set up memory efficient layout, %i | %i | %i \n", Np, Npad, N); - Map.resize(Nx,Ny,Nz); Map.fill(-2); - auto neighborList= new int[18*Npad]; - Np = ScaLBL_Comm->MemoryOptimizedLayoutAA(Map,neighborList,Mask->id.data(),Np,1); - comm.barrier(); + //........................................................................... + // MAIN VARIABLES ALLOCATED HERE + //........................................................................... + // LBM variables + if (rank == 0) + printf("Allocating distributions \n"); + //......................device distributions................................. + dist_mem_size = Np * sizeof(double); + neighborSize = 18 * (Np * sizeof(int)); + //........................................................................... + ScaLBL_AllocateDeviceMemory((void **)&NeighborList, neighborSize); + ScaLBL_AllocateDeviceMemory((void **)&dvcMap, sizeof(int) * Np); + ScaLBL_AllocateDeviceMemory((void **)&fq, 19 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Aq, 7 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Bq, 7 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Den, 2 * dist_mem_size); + ScaLBL_AllocateDeviceMemory((void **)&Phi, sizeof(double) * Nx * Ny * Nz); + ScaLBL_AllocateDeviceMemory((void **)&Pressure, sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&Velocity, 3 * sizeof(double) * Np); + ScaLBL_AllocateDeviceMemory((void **)&ColorGrad, 3 * sizeof(double) * Np); + //........................................................................... + // Update GPU data structures + if (rank == 0) + printf("Setting up device map and neighbor list \n"); + fflush(stdout); + int *TmpMap; + TmpMap = new int[Np]; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + int idx = Map(i, j, k); + if (!(idx < 0)) + TmpMap[idx] = k * Nx * Ny + j * Nx + i; + } + } + } + // check that TmpMap is valid + for (int idx = 0; idx < ScaLBL_Comm->LastExterior(); idx++) { + auto n = TmpMap[idx]; + if (n > Nx * Ny * Nz) { + printf("Bad value! idx=%i \n", n); + TmpMap[idx] = Nx * Ny * Nz - 1; + } + } + for (int idx = ScaLBL_Comm->FirstInterior(); + idx < ScaLBL_Comm->LastInterior(); idx++) { + auto n = TmpMap[idx]; + if (n > Nx * Ny * Nz) { + printf("Bad value! idx=%i \n", n); + TmpMap[idx] = Nx * Ny * Nz - 1; + } + } + ScaLBL_CopyToDevice(dvcMap, TmpMap, sizeof(int) * Np); + ScaLBL_Comm->Barrier(); + delete[] TmpMap; - //........................................................................... - // MAIN VARIABLES ALLOCATED HERE - //........................................................................... - // LBM variables - if (rank==0) printf ("Allocating distributions \n"); - //......................device distributions................................. - dist_mem_size = Np*sizeof(double); - neighborSize=18*(Np*sizeof(int)); - //........................................................................... - ScaLBL_AllocateDeviceMemory((void **) &NeighborList, neighborSize); - ScaLBL_AllocateDeviceMemory((void **) &dvcMap, sizeof(int)*Np); - ScaLBL_AllocateDeviceMemory((void **) &fq, 19*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Aq, 7*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Bq, 7*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Den, 2*dist_mem_size); - ScaLBL_AllocateDeviceMemory((void **) &Phi, sizeof(double)*Nx*Ny*Nz); - ScaLBL_AllocateDeviceMemory((void **) &Pressure, sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &Velocity, 3*sizeof(double)*Np); - ScaLBL_AllocateDeviceMemory((void **) &ColorGrad, 3*sizeof(double)*Np); - //........................................................................... - // Update GPU data structures - if (rank==0) printf ("Setting up device map and neighbor list \n"); - fflush(stdout); - int *TmpMap; - TmpMap=new int[Np]; - for (int k=1; kLastExterior(); idx++){ - auto n = TmpMap[idx]; - if (n > Nx*Ny*Nz){ - printf("Bad value! idx=%i \n", n); - TmpMap[idx] = Nx*Ny*Nz-1; - } - } - for (int idx=ScaLBL_Comm->FirstInterior(); idxLastInterior(); idx++){ - auto n = TmpMap[idx]; - if ( n > Nx*Ny*Nz ){ - printf("Bad value! idx=%i \n",n); - TmpMap[idx] = Nx*Ny*Nz-1; - } - } - ScaLBL_CopyToDevice(dvcMap, TmpMap, sizeof(int)*Np); - ScaLBL_Comm->Barrier(); - delete [] TmpMap; - - // copy the neighbor list - ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); - delete [] neighborList; - // initialize phi based on PhaseLabel (include solid component labels) - double *PhaseLabel; - PhaseLabel = new double[N]; - AssignComponentLabels(PhaseLabel); - ScaLBL_CopyToDevice(Phi, PhaseLabel, N*sizeof(double)); - delete [] PhaseLabel; -} + // copy the neighbor list + ScaLBL_CopyToDevice(NeighborList, neighborList, neighborSize); + delete[] neighborList; + // initialize phi based on PhaseLabel (include solid component labels) + double *PhaseLabel; + PhaseLabel = new double[N]; + AssignComponentLabels(PhaseLabel); + ScaLBL_CopyToDevice(Phi, PhaseLabel, N * sizeof(double)); + delete[] PhaseLabel; +} /******************************************************** * AssignComponentLabels * ********************************************************/ -void ScaLBL_ColorModel::Initialize(){ - - /* if both capillary number and flux BC are specified */ - if (color_db->keyExists( "capillary_number" ) && BoundaryCondition == 4){ - double capillary_number = color_db->getScalar( "capillary_number" ); - if (rank==0) printf(" set flux to achieve Ca=%f \n", capillary_number); - double MuB = rhoB*(tauB - 0.5)/3.0; - double IFT = 6.0*alpha; - double CrossSectionalArea = (double) (nprocx*(Nx-2)*nprocy*(Ny-2)); - flux = Mask->Porosity()*CrossSectionalArea*(Ny-2)*IFT*capillary_number/MuB; - if (rank==0) printf(" flux=%f \n",flux); - } - color_db->putScalar( "flux", flux ); - - if (rank==0) printf ("Initializing distributions \n"); - ScaLBL_D3Q19_Init(fq, Np); - /* - * This function initializes model - */ - if (Restart == true){ - if (rank==0){ - printf("Reading restart file! \n"); - } +void ScaLBL_ColorModel::Initialize() { - // Read in the restart file to CPU buffers - int *TmpMap; - TmpMap = new int[Np]; - - double *cPhi, *cDist, *cDen; - cPhi = new double[N]; - cDen = new double[2*Np]; - cDist = new double[19*Np]; - ScaLBL_CopyToHost(TmpMap, dvcMap, Np*sizeof(int)); - ScaLBL_CopyToHost(cPhi, Phi, N*sizeof(double)); - - ifstream File(LocalRestartFile,ios::binary); - int idx; - double value,va,vb; - for (int n=0; nLastExterior(); n++){ - va = cDen[n]; - vb = cDen[Np + n]; - value = (va-vb)/(va+vb); - idx = TmpMap[n]; - if (!(idx < 0) && idxFirstInterior(); nLastInterior(); n++){ - va = cDen[n]; - vb = cDen[Np + n]; - value = (va-vb)/(va+vb); - idx = TmpMap[n]; - if (!(idx < 0) && idxBarrier(); + /* if both capillary number and flux BC are specified */ + if (color_db->keyExists("capillary_number") && BoundaryCondition == 4) { + double capillary_number = + color_db->getScalar("capillary_number"); + if (rank == 0) + printf(" set flux to achieve Ca=%f \n", capillary_number); + double MuB = rhoB * (tauB - 0.5) / 3.0; + double IFT = 6.0 * alpha; + double CrossSectionalArea = + (double)(nprocx * (Nx - 2) * nprocy * (Ny - 2)); + flux = Mask->Porosity() * CrossSectionalArea * (Ny - 2) * IFT * + capillary_number / MuB; + if (rank == 0) + printf(" flux=%f \n", flux); + } + color_db->putScalar("flux", flux); - comm.barrier(); - } + if (rank == 0) + printf("Initializing distributions \n"); + ScaLBL_D3Q19_Init(fq, Np); + /* + * This function initializes model + */ + if (Restart == true) { + if (rank == 0) { + printf("Reading restart file! \n"); + } - if (rank==0) printf ("Initializing phase field \n"); - ScaLBL_PhaseField_Init(dvcMap, Phi, Den, Aq, Bq, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_PhaseField_Init(dvcMap, Phi, Den, Aq, Bq, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + // Read in the restart file to CPU buffers + int *TmpMap; + TmpMap = new int[Np]; - // establish reservoirs for external bC - if (BoundaryCondition == 1 || BoundaryCondition == 2 || BoundaryCondition == 3 || BoundaryCondition == 4 ){ - if (Dm->kproc()==0){ - ScaLBL_SetSlice_z(Phi,1.0,Nx,Ny,Nz,0); - ScaLBL_SetSlice_z(Phi,1.0,Nx,Ny,Nz,1); - ScaLBL_SetSlice_z(Phi,1.0,Nx,Ny,Nz,2); - } - if (Dm->kproc() == nprocz-1){ - ScaLBL_SetSlice_z(Phi,-1.0,Nx,Ny,Nz,Nz-1); - ScaLBL_SetSlice_z(Phi,-1.0,Nx,Ny,Nz,Nz-2); - ScaLBL_SetSlice_z(Phi,-1.0,Nx,Ny,Nz,Nz-3); - } - } - ScaLBL_CopyToHost(Averages->Phi.data(),Phi,N*sizeof(double)); + double *cPhi, *cDist, *cDen; + cPhi = new double[N]; + cDen = new double[2 * Np]; + cDist = new double[19 * Np]; + ScaLBL_CopyToHost(TmpMap, dvcMap, Np * sizeof(int)); + ScaLBL_CopyToHost(cPhi, Phi, N * sizeof(double)); + + ifstream File(LocalRestartFile, ios::binary); + int idx; + double value, va, vb; + for (int n = 0; n < Np; n++) { + File.read((char *)&va, sizeof(va)); + File.read((char *)&vb, sizeof(vb)); + cDen[n] = va; + cDen[Np + n] = vb; + } + for (int n = 0; n < Np; n++) { + // Read the distributions + for (int q = 0; q < 19; q++) { + File.read((char *)&value, sizeof(value)); + cDist[q * Np + n] = value; + } + } + File.close(); + + for (int n = 0; n < ScaLBL_Comm->LastExterior(); n++) { + va = cDen[n]; + vb = cDen[Np + n]; + value = (va - vb) / (va + vb); + idx = TmpMap[n]; + if (!(idx < 0) && idx < N) + cPhi[idx] = value; + } + for (int n = ScaLBL_Comm->FirstInterior(); + n < ScaLBL_Comm->LastInterior(); n++) { + va = cDen[n]; + vb = cDen[Np + n]; + value = (va - vb) / (va + vb); + idx = TmpMap[n]; + if (!(idx < 0) && idx < N) + cPhi[idx] = value; + } + + // Copy the restart data to the GPU + ScaLBL_CopyToDevice(Den, cDen, 2 * Np * sizeof(double)); + ScaLBL_CopyToDevice(fq, cDist, 19 * Np * sizeof(double)); + ScaLBL_CopyToDevice(Phi, cPhi, N * sizeof(double)); + ScaLBL_Comm->Barrier(); + + comm.barrier(); + } + + if (rank == 0) + printf("Initializing phase field \n"); + ScaLBL_PhaseField_Init(dvcMap, Phi, Den, Aq, Bq, 0, + ScaLBL_Comm->LastExterior(), Np); + ScaLBL_PhaseField_Init(dvcMap, Phi, Den, Aq, Bq, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + + // establish reservoirs for external bC + if (BoundaryCondition == 1 || BoundaryCondition == 2 || + BoundaryCondition == 3 || BoundaryCondition == 4) { + if (Dm->kproc() == 0) { + ScaLBL_SetSlice_z(Phi, 1.0, Nx, Ny, Nz, 0); + ScaLBL_SetSlice_z(Phi, 1.0, Nx, Ny, Nz, 1); + ScaLBL_SetSlice_z(Phi, 1.0, Nx, Ny, Nz, 2); + } + if (Dm->kproc() == nprocz - 1) { + ScaLBL_SetSlice_z(Phi, -1.0, Nx, Ny, Nz, Nz - 1); + ScaLBL_SetSlice_z(Phi, -1.0, Nx, Ny, Nz, Nz - 2); + ScaLBL_SetSlice_z(Phi, -1.0, Nx, Ny, Nz, Nz - 3); + } + } + ScaLBL_CopyToHost(Averages->Phi.data(), Phi, N * sizeof(double)); } -double ScaLBL_ColorModel::Run(int returntime){ - int nprocs=nprocx*nprocy*nprocz; - const RankInfoStruct rank_info(rank,nprocx,nprocy,nprocz); - //************ MAIN ITERATION LOOP ***************************************/ - comm.barrier(); - PROFILE_START("Loop"); - //std::shared_ptr analysis_db; - bool Regular = false; - bool RESCALE_FORCE = false; - bool SET_CAPILLARY_NUMBER = false; - bool TRIGGER_FORCE_RESCALE = false; - double tolerance = 0.01; - auto current_db = db->cloneDatabase(); - auto flow_db = db->getDatabase( "FlowAdaptor" ); - int MIN_STEADY_TIMESTEPS = flow_db->getWithDefault( "min_steady_timesteps", 1000000 ); - int MAX_STEADY_TIMESTEPS = flow_db->getWithDefault( "max_steady_timesteps", 1000000 ); - int RESCALE_FORCE_AFTER_TIMESTEP = MAX_STEADY_TIMESTEPS*2; - int INITIAL_TIMESTEP = timestep; +double ScaLBL_ColorModel::Run(int returntime) { + int nprocs = nprocx * nprocy * nprocz; + const RankInfoStruct rank_info(rank, nprocx, nprocy, nprocz); + //************ MAIN ITERATION LOOP ***************************************/ + comm.barrier(); + PROFILE_START("Loop"); + bool Regular = false; + bool RESCALE_FORCE = false; + bool SET_CAPILLARY_NUMBER = false; + bool TRIGGER_FORCE_RESCALE = false; + double tolerance = 0.01; + auto WettingConvention = color_db->getWithDefault( "WettingConvention", "none" ); + auto current_db = db->cloneDatabase(); + auto flow_db = db->getDatabase("FlowAdaptor"); + int MIN_STEADY_TIMESTEPS = + flow_db->getWithDefault("min_steady_timesteps", 1000000); + int MAX_STEADY_TIMESTEPS = + flow_db->getWithDefault("max_steady_timesteps", 1000000); + int RESCALE_FORCE_AFTER_TIMESTEP = MAX_STEADY_TIMESTEPS * 2; + int INITIAL_TIMESTEP = timestep; - double capillary_number = 1.0e-5; - double Ca_previous = 0.0; - double minCa = 8.0e-6; - double maxCa = 1.0; - if (color_db->keyExists( "capillary_number" )){ - capillary_number = color_db->getScalar( "capillary_number" ); - SET_CAPILLARY_NUMBER=true; - maxCa = 2.0*capillary_number; - minCa = 0.8*capillary_number; - } - if (color_db->keyExists( "rescale_force_after_timestep" )){ - RESCALE_FORCE_AFTER_TIMESTEP = color_db->getScalar( "rescale_force_after_timestep" ); - RESCALE_FORCE = true; - } - if (analysis_db->keyExists( "tolerance" )){ - tolerance = analysis_db->getScalar( "tolerance" ); - } - - auto WettingConvention = color_db->getWithDefault( "WettingConvention", "none" ); - - - runAnalysis analysis( current_db, rank_info, ScaLBL_Comm, Dm, Np, Regular, Map ); - auto t1 = std::chrono::system_clock::now(); - int CURRENT_TIMESTEP = 0; - int EXIT_TIMESTEP = min(timestepMax,returntime); - while (timestep < EXIT_TIMESTEP ) { - //if ( rank==0 ) { printf("Running timestep %i (%i MB)\n",timestep+1,(int)(Utilities::getMemoryUsage()/1048576)); } - PROFILE_START("Update"); - // *************ODD TIMESTEP************* - timestep++; - // Compute the Phase indicator field - // Read for Aq, Bq happens in this routine (requires communication) - ScaLBL_Comm->BiSendD3Q7AA(Aq,Bq); //READ FROM NORMAL - ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->BiRecvD3Q7AA(Aq,Bq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, 0, ScaLBL_Comm->LastExterior(), Np); - - // Perform the collision operation - ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL - if (BoundaryCondition > 0 && BoundaryCondition < 5){ - ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); - ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); - } - // Halo exchange for phase field - ScaLBL_Comm_Regular->SendHalo(Phi); - - ScaLBL_D3Q19_AAodd_Color(NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, Nx, Nx*Ny, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm_Regular->RecvHalo(Phi); - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - // Set BCs - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); - } - ScaLBL_D3Q19_AAodd_Color(NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, Nx, Nx*Ny, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->Barrier(); - - // *************EVEN TIMESTEP************* - timestep++; - // Compute the Phase indicator field - ScaLBL_Comm->BiSendD3Q7AA(Aq,Bq); //READ FROM NORMAL - ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->BiRecvD3Q7AA(Aq,Bq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, 0, ScaLBL_Comm->LastExterior(), Np); - - // Perform the collision operation - ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL - // Halo exchange for phase field - if (BoundaryCondition > 0 && BoundaryCondition < 5){ - ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); - ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); - } - ScaLBL_Comm_Regular->SendHalo(Phi); - ScaLBL_D3Q19_AAeven_Color(dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, Nx, Nx*Ny, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm_Regular->RecvHalo(Phi); - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - // Set boundary conditions - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); - } - ScaLBL_D3Q19_AAeven_Color(dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, Nx, Nx*Ny, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->Barrier(); - //************************************************************************ - analysis.basic(timestep, current_db, *Averages, Phi, Pressure, Velocity, fq, Den ); // allow initial ramp-up to get closer to steady state - - CURRENT_TIMESTEP += 2; - if (CURRENT_TIMESTEP > MIN_STEADY_TIMESTEPS && BoundaryCondition == 0){ - analysis.finish(); - - double volB = Averages->gwb.V; - double volA = Averages->gnb.V; - volA /= Dm->Volume; - volB /= Dm->Volume;; - //initial_volume = volA*Dm->Volume; - double vA_x = Averages->gnb.Px/Averages->gnb.M; - double vA_y = Averages->gnb.Py/Averages->gnb.M; - double vA_z = Averages->gnb.Pz/Averages->gnb.M; - double vB_x = Averages->gwb.Px/Averages->gwb.M; - double vB_y = Averages->gwb.Py/Averages->gwb.M; - double vB_z = Averages->gwb.Pz/Averages->gwb.M; - double muA = rhoA*(tauA-0.5)/3.f; - double muB = rhoB*(tauB-0.5)/3.f; - double force_mag = sqrt(Fx*Fx+Fy*Fy+Fz*Fz); - double dir_x = Fx/force_mag; - double dir_y = Fy/force_mag; - double dir_z = Fz/force_mag; - if (force_mag == 0.0){ - // default to z direction - dir_x = 0.0; - dir_y = 0.0; - dir_z = 1.0; - force_mag = 1.0; - } - double current_saturation = volB/(volA+volB); - double flow_rate_A = volA*(vA_x*dir_x + vA_y*dir_y + vA_z*dir_z); - double flow_rate_B = volB*(vB_x*dir_x + vB_y*dir_y + vB_z*dir_z); - double Ca = fabs(muA*flow_rate_A + muB*flow_rate_B)/(5.796*alpha); - - bool isSteady = false; - if ( (fabs((Ca - Ca_previous)/Ca) < tolerance && CURRENT_TIMESTEP > MIN_STEADY_TIMESTEPS)) - isSteady = true; - if (CURRENT_TIMESTEP >= MAX_STEADY_TIMESTEPS) - isSteady = true; - - if (isSteady && (Ca > maxCa || Ca < minCa) && SET_CAPILLARY_NUMBER ){ - /* re-run the point if the actual Ca is too far from the target Ca */ - isSteady = false; - RESCALE_FORCE = true; - t1 = std::chrono::system_clock::now(); - CURRENT_TIMESTEP = 0; - timestep = INITIAL_TIMESTEP; - TRIGGER_FORCE_RESCALE = true; - if (rank == 0) printf(" Capillary number missed target value = %f (measured value was Ca = %f) \n ",capillary_number, Ca); - } - - if (RESCALE_FORCE == true && SET_CAPILLARY_NUMBER == true && CURRENT_TIMESTEP > RESCALE_FORCE_AFTER_TIMESTEP){ - TRIGGER_FORCE_RESCALE = true; - } - - if (TRIGGER_FORCE_RESCALE){ - RESCALE_FORCE = false; - TRIGGER_FORCE_RESCALE = false; - double RESCALE_FORCE_FACTOR = capillary_number / Ca; - if (RESCALE_FORCE_FACTOR > 2.0) RESCALE_FORCE_FACTOR = 2.0; - if (RESCALE_FORCE_FACTOR < 0.5) RESCALE_FORCE_FACTOR = 0.5; - Fx *= RESCALE_FORCE_FACTOR; - Fy *= RESCALE_FORCE_FACTOR; - Fz *= RESCALE_FORCE_FACTOR; - force_mag = sqrt(Fx*Fx+Fy*Fy+Fz*Fz); - if (force_mag > 1e-3){ - Fx *= 1e-3/force_mag; // impose ceiling for stability - Fy *= 1e-3/force_mag; - Fz *= 1e-3/force_mag; - } - if (rank == 0) printf(" -- adjust force by factor %f \n ",capillary_number / Ca); - Averages->SetParams(rhoA,rhoB,tauA,tauB,Fx,Fy,Fz,alpha,beta); - color_db->putVector("F",{Fx,Fy,Fz}); - } - if ( isSteady ){ - Averages->Full(); - Averages->Write(timestep); - analysis.WriteVisData(timestep, current_db, *Averages, Phi, Pressure, Velocity, fq, Den ); - analysis.finish(); - - if (rank==0){ - printf("** WRITE STEADY POINT *** "); - printf("Ca = %f, (previous = %f) \n",Ca,Ca_previous); - double h = Dm->voxel_length; - // pressures - double pA = Averages->gnb.p; - double pB = Averages->gwb.p; - double pAc = Averages->gnc.p; - double pBc = Averages->gwc.p; - double pAB = (pA-pB)/(h*6.0*alpha); - double pAB_connected = (pAc-pBc)/(h*6.0*alpha); - // connected contribution - double Vol_nc = Averages->gnc.V/Dm->Volume; - double Vol_wc = Averages->gwc.V/Dm->Volume; - double Vol_nd = Averages->gnd.V/Dm->Volume; - double Vol_wd = Averages->gwd.V/Dm->Volume; - double Mass_n = Averages->gnc.M + Averages->gnd.M; - double Mass_w = Averages->gwc.M + Averages->gwd.M; - double vAc_x = Averages->gnc.Px/Mass_n; - double vAc_y = Averages->gnc.Py/Mass_n; - double vAc_z = Averages->gnc.Pz/Mass_n; - double vBc_x = Averages->gwc.Px/Mass_w; - double vBc_y = Averages->gwc.Py/Mass_w; - double vBc_z = Averages->gwc.Pz/Mass_w; - // disconnected contribution - double vAd_x = Averages->gnd.Px/Mass_n; - double vAd_y = Averages->gnd.Py/Mass_n; - double vAd_z = Averages->gnd.Pz/Mass_n; - double vBd_x = Averages->gwd.Px/Mass_w; - double vBd_y = Averages->gwd.Py/Mass_w; - double vBd_z = Averages->gwd.Pz/Mass_w; - // film contribution - double Mfn = Averages->gifs.Mn; - double Mfw = Averages->gifs.Mw; - double vfn_x = Averages->gifs.Pnx; - double vfn_y = Averages->gifs.Pny; - double vfn_z = Averages->gifs.Pnz; - double vfw_x = Averages->gifs.Pwx; - double vfw_y = Averages->gifs.Pwy; - double vfw_z = Averages->gifs.Pwz; - - double flow_rate_A_connected = Vol_nc*(vAc_x*dir_x + vAc_y*dir_y + vAc_z*dir_z); - double flow_rate_B_connected = Vol_wc*(vBc_x*dir_x + vBc_y*dir_y + vBc_z*dir_z); - double flow_rate_A_disconnected = (Vol_nd)*(vAd_x*dir_x + vAd_y*dir_y + vAd_z*dir_z); - double flow_rate_B_disconnected = (Vol_wd)*(vBd_x*dir_x + vBd_y*dir_y + vBd_z*dir_z); - - double kAeff_connected = h*h*muA*flow_rate_A_connected/(force_mag); - double kBeff_connected = h*h*muB*flow_rate_B_connected/(force_mag); - - double kAeff_disconnected = h*h*muA*flow_rate_A_disconnected/(force_mag); - double kBeff_disconnected = h*h*muB*flow_rate_B_disconnected/(force_mag); - - double kAeff = h*h*muA*(flow_rate_A)/(force_mag); - double kBeff = h*h*muB*(flow_rate_B)/(force_mag); - - /* flow rate = [volume of fluid] x [momentum of fluid] / [mass of fluid] */ - /* fluid A eats the films */ - double flow_rate_A_filmA = (flow_rate_A*Averages->gnb.M + volA*(vfn_x*dir_x + vfn_y*dir_y + vfn_z*dir_z))/(Averages->gnb.M + Mfn); - double flow_rate_B_filmA = (flow_rate_B*Averages->gwb.M - volB*(vfn_x*dir_x + vfn_y*dir_y + vfn_z*dir_z))/(Averages->gwb.M - (rhoB/rhoA)*Mfn); - /* fluid B eats the films */ - double flow_rate_A_filmB = (flow_rate_A*Averages->gnb.M - volA*(vfw_x*dir_x + vfw_y*dir_y + vfw_z*dir_z))/(Averages->gnb.M - (rhoA/rhoB)*Mfw); - double flow_rate_B_filmB = (flow_rate_B*Averages->gwb.M + volB*(vfw_x*dir_x + vfw_y*dir_y + vfw_z*dir_z))/(Averages->gwb.M + Mfw); - /* effective permeability uncertainty limits */ - double kAeff_filmA = h*h*muA*(flow_rate_A_filmA)/(force_mag); - double kBeff_filmA = h*h*muB*(flow_rate_B_filmA)/(force_mag); - double kAeff_filmB = h*h*muA*(flow_rate_A_filmB)/(force_mag); - double kBeff_filmB = h*h*muB*(flow_rate_B_filmB)/(force_mag); - - double viscous_pressure_drop = (rhoA*volA + rhoB*volB)*force_mag; - double Mobility = muA/muB; - - bool WriteHeader=false; - FILE * kr_log_file = fopen("relperm.csv","r"); - if (kr_log_file != NULL) - fclose(kr_log_file); - else - WriteHeader=true; - kr_log_file = fopen("relperm.csv","a"); - if (WriteHeader){ - fprintf(kr_log_file,"timesteps sat.water eff.perm.oil eff.perm.water eff.perm.oil.connected eff.perm.water.connected eff.perm.oil.disconnected eff.perm.water.disconnected "); - fprintf(kr_log_file,"eff.perm.oil.upper.bound eff.perm.water.lower.bound eff.perm.oil.lower.bound eff.perm.water.upper.bound "); - fprintf(kr_log_file,"cap.pressure cap.pressure.connected pressure.drop Ca M\n"); - } - fprintf(kr_log_file,"%i %.5g %.5g %.5g %.5g %.5g %.5g %.5g ",CURRENT_TIMESTEP,current_saturation,kAeff,kBeff,kAeff_connected,kBeff_connected,kAeff_disconnected,kBeff_disconnected); - fprintf(kr_log_file,"%.5g %.5g %.5g %.5g ",kAeff_filmA, kBeff_filmA, kAeff_filmB,kBeff_filmB); - fprintf(kr_log_file,"%.5g %.5g %.5g %.5g %.5g\n",pAB,pAB_connected,viscous_pressure_drop,Ca,Mobility); - fclose(kr_log_file); - - /* SCAL file */ - if (WettingConvention == "SCAL"){ - WriteHeader=false; - FILE * scal_log_file = fopen("SCAL.csv","r"); - if (scal_log_file != NULL) - fclose(scal_log_file); - else - WriteHeader=true; - scal_log_file = fopen("SCAL.csv","a"); - if (WriteHeader){ - fprintf(scal_log_file,"timesteps sat.water eff.perm.oil eff.perm.water eff.perm.oil.connected eff.perm.water.connected eff.perm.oil.disconnected eff.perm.water.disconnected "); - fprintf(scal_log_file,"eff.perm.oil.upper.bound eff.perm.water.lower.bound eff.perm.oil.lower.bound eff.perm.water.upper.bound "); - fprintf(scal_log_file,"cap.pressure cap.pressure.connected pressure.drop Ca M\n"); - } - fprintf(scal_log_file,"%i %.5g %.5g %.5g %.5g %.5g %.5g %.5g ",CURRENT_TIMESTEP,current_saturation,kAeff,kBeff,kAeff_connected,kBeff_connected,kAeff_disconnected,kBeff_disconnected); - fprintf(scal_log_file,"%.5g %.5g %.5g %.5g ",kAeff_filmA, kBeff_filmA, kAeff_filmB,kBeff_filmB); - fprintf(scal_log_file,"%.5g %.5g %.5g %.5g %.5g\n",pAB,pAB_connected,viscous_pressure_drop,Ca,Mobility); - fclose(scal_log_file); - /****************/ - } - - printf(" Measured capillary number %f \n ",Ca); - } - if (SET_CAPILLARY_NUMBER ){ - Fx *= capillary_number / Ca; - Fy *= capillary_number / Ca; - Fz *= capillary_number / Ca; - if (force_mag > 1e-3){ - Fx *= 1e-3/force_mag; // impose ceiling for stability - Fy *= 1e-3/force_mag; - Fz *= 1e-3/force_mag; - } - if (rank == 0) printf(" -- adjust force by factor %f \n ",capillary_number / Ca); - Averages->SetParams(rhoA,rhoB,tauA,tauB,Fx,Fy,Fz,alpha,beta); - color_db->putVector("F",{Fx,Fy,Fz}); - } - else{ - if (rank==0){ - printf("** Continue to simulate steady *** \n "); - printf("Ca = %f, (previous = %f) \n",Ca,Ca_previous); - } - } - } - } - } - analysis.finish(); - PROFILE_STOP("Update"); - - PROFILE_STOP("Loop"); - PROFILE_SAVE("lbpm_color_simulator",1); - //************************************************************************ - // Compute the walltime per timestep - auto t2 = std::chrono::system_clock::now(); - double cputime = std::chrono::duration( t2 - t1 ).count() / CURRENT_TIMESTEP; - // Performance obtained from each node - double MLUPS = double(Np)/cputime/1000000; - - if (rank==0) printf("********************************************************\n"); - if (rank==0) printf("CPU time = %f \n", cputime); - if (rank==0) printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); - return(MLUPS); - MLUPS *= nprocs; - -} - -void ScaLBL_ColorModel::Run(){ - int nprocs=nprocx*nprocy*nprocz; - const RankInfoStruct rank_info(rank,nprocx,nprocy,nprocz); - int analysis_interval = 1000; // number of timesteps in between in situ analysis - if (analysis_db->keyExists( "analysis_interval" )){ - analysis_interval = analysis_db->getScalar( "analysis_interval" ); - } - - //************ MAIN ITERATION LOOP ***************************************/ - comm.barrier(); - PROFILE_START("Loop"); - //std::shared_ptr analysis_db; - bool Regular = false; - auto current_db = db->cloneDatabase(); - runAnalysis analysis( current_db, rank_info, ScaLBL_Comm, Dm, Np, Regular, Map ); - //analysis.createThreads( analysis_method, 4 ); + double capillary_number = 1.0e-5; + double Ca_previous = 0.0; + double minCa = 8.0e-6; + double maxCa = 1.0; + if (color_db->keyExists("capillary_number")) { + capillary_number = color_db->getScalar("capillary_number"); + SET_CAPILLARY_NUMBER = true; + maxCa = 2.0 * capillary_number; + minCa = 0.8 * capillary_number; + } + if (color_db->keyExists("rescale_force_after_timestep")) { + RESCALE_FORCE_AFTER_TIMESTEP = + color_db->getScalar("rescale_force_after_timestep"); + RESCALE_FORCE = true; + } + if (analysis_db->keyExists("tolerance")) { + tolerance = analysis_db->getScalar("tolerance"); + } + + runAnalysis analysis(current_db, rank_info, ScaLBL_Comm, Dm, Np, Regular, + Map); auto t1 = std::chrono::system_clock::now(); - while (timestep < timestepMax ) { - //if ( rank==0 ) { printf("Running timestep %i (%i MB)\n",timestep+1,(int)(Utilities::getMemoryUsage()/1048576)); } - PROFILE_START("Update"); - // *************ODD TIMESTEP************* - timestep++; - // Compute the Phase indicator field - // Read for Aq, Bq happens in this routine (requires communication) - ScaLBL_Comm->BiSendD3Q7AA(Aq,Bq); //READ FROM NORMAL - ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->BiRecvD3Q7AA(Aq,Bq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, 0, ScaLBL_Comm->LastExterior(), Np); + int CURRENT_TIMESTEP = 0; + int EXIT_TIMESTEP = min(timestepMax, returntime); + while (timestep < EXIT_TIMESTEP) { + PROFILE_START("Update"); + // *************ODD TIMESTEP************* + timestep++; + // Compute the Phase indicator field + // Read for Aq, Bq happens in this routine (requires communication) + ScaLBL_Comm->BiSendD3Q7AA(Aq, Bq); //READ FROM NORMAL + ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->BiRecvD3Q7AA(Aq, Bq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, 0, + ScaLBL_Comm->LastExterior(), Np); - // Perform the collision operation - ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL - if (BoundaryCondition > 0 && BoundaryCondition < 5){ - ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); - ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); - } - // Halo exchange for phase field - ScaLBL_Comm_Regular->SendHalo(Phi); + // Perform the collision operation + ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL + if (BoundaryCondition > 0 && BoundaryCondition < 5) { + ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); + ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); + } + // Halo exchange for phase field + ScaLBL_Comm_Regular->SendHalo(Phi); - ScaLBL_D3Q19_AAodd_Color(NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, Nx, Nx*Ny, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm_Regular->RecvHalo(Phi); - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - // Set BCs - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); - } - ScaLBL_D3Q19_AAodd_Color(NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, Nx, Nx*Ny, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->Barrier(); + ScaLBL_D3Q19_AAodd_Color( + NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, + tauA, tauB, alpha, beta, Fx, Fy, Fz, Nx, Nx * Ny, + ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm_Regular->RecvHalo(Phi); + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + // Set BCs + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } + if (BoundaryCondition == 4) { + din = + ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); + } + ScaLBL_D3Q19_AAodd_Color(NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, + Velocity, rhoA, rhoB, tauA, tauB, alpha, beta, + Fx, Fy, Fz, Nx, Nx * Ny, 0, + ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm->Barrier(); - // *************EVEN TIMESTEP************* - timestep++; - // Compute the Phase indicator field - ScaLBL_Comm->BiSendD3Q7AA(Aq,Bq); //READ FROM NORMAL - ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm->BiRecvD3Q7AA(Aq,Bq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, 0, ScaLBL_Comm->LastExterior(), Np); + // *************EVEN TIMESTEP************* + timestep++; + // Compute the Phase indicator field + ScaLBL_Comm->BiSendD3Q7AA(Aq, Bq); //READ FROM NORMAL + ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->BiRecvD3Q7AA(Aq, Bq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, 0, + ScaLBL_Comm->LastExterior(), Np); - // Perform the collision operation - ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL - // Halo exchange for phase field - if (BoundaryCondition > 0 && BoundaryCondition < 5){ - ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); - ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); - } - ScaLBL_Comm_Regular->SendHalo(Phi); - ScaLBL_D3Q19_AAeven_Color(dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, Nx, Nx*Ny, ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); - ScaLBL_Comm_Regular->RecvHalo(Phi); - ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE - ScaLBL_Comm->Barrier(); - // Set boundary conditions - if (BoundaryCondition == 3){ - ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 4){ - din = ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); - ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); - } - else if (BoundaryCondition == 5){ - ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); - ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); - } - ScaLBL_D3Q19_AAeven_Color(dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, tauA, tauB, - alpha, beta, Fx, Fy, Fz, Nx, Nx*Ny, 0, ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->Barrier(); - //************************************************************************ - PROFILE_STOP("Update"); + // Perform the collision operation + ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL + // Halo exchange for phase field + if (BoundaryCondition > 0 && BoundaryCondition < 5) { + ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); + ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); + } + ScaLBL_Comm_Regular->SendHalo(Phi); + ScaLBL_D3Q19_AAeven_Color(dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, + rhoB, tauA, tauB, alpha, beta, Fx, Fy, Fz, Nx, + Nx * Ny, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm_Regular->RecvHalo(Phi); + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + // Set boundary conditions + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 4) { + din = + ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); + } + ScaLBL_D3Q19_AAeven_Color(dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, + rhoB, tauA, tauB, alpha, beta, Fx, Fy, Fz, Nx, + Nx * Ny, 0, ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm->Barrier(); + //************************************************************************ + analysis.basic( + timestep, current_db, *Averages, Phi, Pressure, Velocity, fq, + Den); // allow initial ramp-up to get closer to steady state - if (rank==0 && timestep%analysis_interval == 0 && BoundaryCondition == 4){ - printf("%i %f \n",timestep,din); - } - // Run the analysis - analysis.basic(timestep, current_db, *Averages, Phi, Pressure, Velocity, fq, Den ); - } - analysis.finish(); - PROFILE_STOP("Loop"); - PROFILE_SAVE("lbpm_color_simulator",1); - //************************************************************************ - ScaLBL_Comm->Barrier(); - if (rank==0) printf("-------------------------------------------------------------------\n"); - // Compute the walltime per timestep + CURRENT_TIMESTEP += 2; + if (CURRENT_TIMESTEP > MIN_STEADY_TIMESTEPS && BoundaryCondition == 0) { + analysis.finish(); + + double volB = Averages->gwb.V; + double volA = Averages->gnb.V; + volA /= Dm->Volume; + volB /= Dm->Volume; + ; + //initial_volume = volA*Dm->Volume; + double vA_x = Averages->gnb.Px / Averages->gnb.M; + double vA_y = Averages->gnb.Py / Averages->gnb.M; + double vA_z = Averages->gnb.Pz / Averages->gnb.M; + double vB_x = Averages->gwb.Px / Averages->gwb.M; + double vB_y = Averages->gwb.Py / Averages->gwb.M; + double vB_z = Averages->gwb.Pz / Averages->gwb.M; + double muA = rhoA * (tauA - 0.5) / 3.f; + double muB = rhoB * (tauB - 0.5) / 3.f; + double force_mag = sqrt(Fx * Fx + Fy * Fy + Fz * Fz); + double dir_x = Fx / force_mag; + double dir_y = Fy / force_mag; + double dir_z = Fz / force_mag; + if (force_mag == 0.0) { + // default to z direction + dir_x = 0.0; + dir_y = 0.0; + dir_z = 1.0; + force_mag = 1.0; + } + double current_saturation = volB / (volA + volB); + double flow_rate_A = + volA * (vA_x * dir_x + vA_y * dir_y + vA_z * dir_z); + double flow_rate_B = + volB * (vB_x * dir_x + vB_y * dir_y + vB_z * dir_z); + double Ca = + fabs(muA * flow_rate_A + muB * flow_rate_B) / (5.796 * alpha); + + bool isSteady = false; + if ((fabs((Ca - Ca_previous) / Ca) < tolerance && + CURRENT_TIMESTEP > MIN_STEADY_TIMESTEPS)) + isSteady = true; + if (CURRENT_TIMESTEP >= MAX_STEADY_TIMESTEPS) + isSteady = true; + + if (isSteady && (Ca > maxCa || Ca < minCa) && + SET_CAPILLARY_NUMBER) { + /* re-run the point if the actual Ca is too far from the target Ca */ + isSteady = false; + RESCALE_FORCE = true; + t1 = std::chrono::system_clock::now(); + CURRENT_TIMESTEP = 0; + timestep = INITIAL_TIMESTEP; + TRIGGER_FORCE_RESCALE = true; + if (rank == 0) + printf(" Capillary number missed target value = %f " + "(measured value was Ca = %f) \n ", + capillary_number, Ca); + } + + if (RESCALE_FORCE == true && SET_CAPILLARY_NUMBER == true && + CURRENT_TIMESTEP > RESCALE_FORCE_AFTER_TIMESTEP) { + TRIGGER_FORCE_RESCALE = true; + } + + if (TRIGGER_FORCE_RESCALE) { + RESCALE_FORCE = false; + TRIGGER_FORCE_RESCALE = false; + double RESCALE_FORCE_FACTOR = capillary_number / Ca; + if (RESCALE_FORCE_FACTOR > 2.0) + RESCALE_FORCE_FACTOR = 2.0; + if (RESCALE_FORCE_FACTOR < 0.5) + RESCALE_FORCE_FACTOR = 0.5; + Fx *= RESCALE_FORCE_FACTOR; + Fy *= RESCALE_FORCE_FACTOR; + Fz *= RESCALE_FORCE_FACTOR; + force_mag = sqrt(Fx * Fx + Fy * Fy + Fz * Fz); + if (force_mag > 1e-3) { + Fx *= 1e-3 / force_mag; // impose ceiling for stability + Fy *= 1e-3 / force_mag; + Fz *= 1e-3 / force_mag; + } + if (rank == 0) + printf(" -- adjust force by factor %f \n ", + capillary_number / Ca); + Averages->SetParams(rhoA, rhoB, tauA, tauB, Fx, Fy, Fz, alpha, + beta); + color_db->putVector("F", {Fx, Fy, Fz}); + } + if (isSteady) { + Averages->Full(); + Averages->Write(timestep); + analysis.WriteVisData(timestep, current_db, *Averages, Phi, + Pressure, Velocity, fq, Den); + analysis.finish(); + + if (rank == 0) { + printf("** WRITE STEADY POINT *** "); + printf("Ca = %f, (previous = %f) \n", Ca, Ca_previous); + double h = Dm->voxel_length; + // pressures + double pA = Averages->gnb.p; + double pB = Averages->gwb.p; + double pAc = Averages->gnc.p; + double pBc = Averages->gwc.p; + double pAB = (pA - pB) / (h * 6.0 * alpha); + double pAB_connected = (pAc - pBc) / (h * 6.0 * alpha); + // connected contribution + double Vol_nc = Averages->gnc.V / Dm->Volume; + double Vol_wc = Averages->gwc.V / Dm->Volume; + double Vol_nd = Averages->gnd.V / Dm->Volume; + double Vol_wd = Averages->gwd.V / Dm->Volume; + double Mass_n = Averages->gnc.M + Averages->gnd.M; + double Mass_w = Averages->gwc.M + Averages->gwd.M; + double vAc_x = Averages->gnc.Px / Mass_n; + double vAc_y = Averages->gnc.Py / Mass_n; + double vAc_z = Averages->gnc.Pz / Mass_n; + double vBc_x = Averages->gwc.Px / Mass_w; + double vBc_y = Averages->gwc.Py / Mass_w; + double vBc_z = Averages->gwc.Pz / Mass_w; + // disconnected contribution + double vAd_x = Averages->gnd.Px / Mass_n; + double vAd_y = Averages->gnd.Py / Mass_n; + double vAd_z = Averages->gnd.Pz / Mass_n; + double vBd_x = Averages->gwd.Px / Mass_w; + double vBd_y = Averages->gwd.Py / Mass_w; + double vBd_z = Averages->gwd.Pz / Mass_w; + + double flow_rate_A_connected = + Vol_nc * + (vAc_x * dir_x + vAc_y * dir_y + vAc_z * dir_z); + double flow_rate_B_connected = + Vol_wc * + (vBc_x * dir_x + vBc_y * dir_y + vBc_z * dir_z); + double flow_rate_A_disconnected = + (Vol_nd) * + (vAd_x * dir_x + vAd_y * dir_y + vAd_z * dir_z); + double flow_rate_B_disconnected = + (Vol_wd) * + (vBd_x * dir_x + vBd_y * dir_y + vBd_z * dir_z); + + double kAeff_connected = + h * h * muA * flow_rate_A_connected / (force_mag); + double kBeff_connected = + h * h * muB * flow_rate_B_connected / (force_mag); + + // Saturation normalized effective permeability to account for decoupled phases and + // effective porosity. + double kAeff_connected_low = + (1.0 - current_saturation) * h * h * muA * + flow_rate_A_connected / (force_mag); + double kBeff_connected_low = current_saturation * h * h * + muB * flow_rate_B_connected / + (force_mag); + + double kAeff_disconnected = + h * h * muA * flow_rate_A_disconnected / (force_mag); + double kBeff_disconnected = + h * h * muB * flow_rate_B_disconnected / (force_mag); + + double kAeff = h * h * muA * (flow_rate_A) / (force_mag); + double kBeff = h * h * muB * (flow_rate_B) / (force_mag); + + // Saturation normalized effective permeability to account for decoupled phases and + // effective porosity. + double kAeff_low = (1.0 - current_saturation) * h * h * + muA * (flow_rate_A) / (force_mag); + double kBeff_low = current_saturation * h * h * muB * + (flow_rate_B) / (force_mag); + + double viscous_pressure_drop = + (rhoA * volA + rhoB * volB) * force_mag; + double Mobility = muA / muB; // visc contrast + double eff_pres = + 1.0 / (kAeff + kBeff); // effective pressure drop + + bool WriteHeader = false; + FILE *kr_log_file = fopen("relperm.csv", "r"); + if (kr_log_file != NULL) + fclose(kr_log_file); + else + WriteHeader = true; + kr_log_file = fopen("relperm.csv", "a"); + if (WriteHeader) { + fprintf(kr_log_file, "timesteps sat.water "); + fprintf(kr_log_file, "eff.perm.oil.upper.bound " + "eff.perm.water.upper.bound "); + fprintf(kr_log_file, "eff.perm.oil.lower.bound " + "eff.perm.water.lower.bound "); + fprintf(kr_log_file, + "eff.perm.oil.connected.upper.bound " + "eff.perm.water.connected.upper.bound "); + fprintf(kr_log_file, + "eff.perm.oil.connected.lower.bound " + "eff.perm.water.connected.lower.bound "); + fprintf(kr_log_file, "eff.perm.oil.disconnected " + "eff.perm.water.disconnected "); + fprintf(kr_log_file, + "cap.pressure cap.pressure.connected " + "pressure.drop Ca M eff.pressure\n"); + } + fprintf(kr_log_file, "%i %.5g ", CURRENT_TIMESTEP, + current_saturation); + fprintf(kr_log_file, "%.5g %.5g ", kAeff, kBeff); + fprintf(kr_log_file, "%.5g %.5g ", kAeff_low, kBeff_low); + fprintf(kr_log_file, "%.5g %.5g ", kAeff_connected, + kBeff_connected); + fprintf(kr_log_file, "%.5g %.5g ", kAeff_connected_low, + kBeff_connected_low); + fprintf(kr_log_file, "%.5g %.5g ", kAeff_disconnected, + kBeff_disconnected); + fprintf(kr_log_file, "%.5g %.5g %.5g %.5g %.5g ", pAB, + pAB_connected, viscous_pressure_drop, Ca, Mobility); + fprintf(kr_log_file, "%.5g\n", eff_pres); + fclose(kr_log_file); + + if (WettingConvention == "SCAL"){ + WriteHeader = false; + FILE *scal_log_file = fopen("SCAL.csv", "r"); + if (scal_log_file != NULL) + fclose(scal_log_file); + else + WriteHeader = true; + scal_log_file = fopen("SCAL.csv", "a"); + if (WriteHeader) { + fprintf(scal_log_file, "timesteps sat.water "); + fprintf(scal_log_file, "eff.perm.oil.upper.bound " + "eff.perm.water.upper.bound "); + fprintf(scal_log_file, "eff.perm.oil.lower.bound " + "eff.perm.water.lower.bound "); + fprintf(scal_log_file, + "eff.perm.oil.connected.upper.bound " + "eff.perm.water.connected.upper.bound "); + fprintf(scal_log_file, + "eff.perm.oil.connected.lower.bound " + "eff.perm.water.connected.lower.bound "); + fprintf(scal_log_file, "eff.perm.oil.disconnected " + "eff.perm.water.disconnected "); + fprintf(scal_log_file, + "cap.pressure cap.pressure.connected " + "pressure.drop Ca M eff.pressure\n"); + } + fprintf(scal_log_file, "%i %.5g ", CURRENT_TIMESTEP, + current_saturation); + fprintf(scal_log_file, "%.5g %.5g ", kAeff, kBeff); + fprintf(scal_log_file, "%.5g %.5g ", kAeff_low, kBeff_low); + fprintf(scal_log_file, "%.5g %.5g ", kAeff_connected, + kBeff_connected); + fprintf(scal_log_file, "%.5g %.5g ", kAeff_connected_low, + kBeff_connected_low); + fprintf(scal_log_file, "%.5g %.5g ", kAeff_disconnected, + kBeff_disconnected); + fprintf(scal_log_file, "%.5g %.5g %.5g %.5g %.5g ", pAB, + pAB_connected, viscous_pressure_drop, Ca, Mobility); + fprintf(scal_log_file, "%.5g\n", eff_pres); + fclose(scal_log_file); + + } + + printf(" Measured capillary number %f \n ", Ca); + } + if (SET_CAPILLARY_NUMBER) { + Fx *= capillary_number / Ca; + Fy *= capillary_number / Ca; + Fz *= capillary_number / Ca; + if (force_mag > 1e-3) { + Fx *= 1e-3 / force_mag; // impose ceiling for stability + Fy *= 1e-3 / force_mag; + Fz *= 1e-3 / force_mag; + } + if (rank == 0) + printf(" -- adjust force by factor %f \n ", + capillary_number / Ca); + Averages->SetParams(rhoA, rhoB, tauA, tauB, Fx, Fy, Fz, + alpha, beta); + color_db->putVector("F", {Fx, Fy, Fz}); + } else { + if (rank == 0) { + printf("** Continue to simulate steady *** \n "); + printf("Ca = %f, (previous = %f) \n", Ca, Ca_previous); + } + } + } + } + } + analysis.finish(); + PROFILE_STOP("Update"); + + PROFILE_STOP("Loop"); + PROFILE_SAVE("lbpm_color_simulator", 1); + //************************************************************************ + // Compute the walltime per timestep auto t2 = std::chrono::system_clock::now(); - double cputime = std::chrono::duration( t2 - t1 ).count() / timestep; - // Performance obtained from each node - double MLUPS = double(Np)/cputime/1000000; + double cputime = + std::chrono::duration(t2 - t1).count() / CURRENT_TIMESTEP; + // Performance obtained from each node + double MLUPS = double(Np) / cputime / 1000000; - if (rank==0) printf("********************************************************\n"); - if (rank==0) printf("CPU time = %f \n", cputime); - if (rank==0) printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); - MLUPS *= nprocs; - if (rank==0) printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); - if (rank==0) printf("********************************************************\n"); - - // ************************************************************************ + if (rank == 0) + printf("********************************************************\n"); + if (rank == 0) + printf("CPU time = %f \n", cputime); + if (rank == 0) + printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); + return (MLUPS); + MLUPS *= nprocs; } -void ScaLBL_ColorModel::WriteDebug(){ - // Copy back final phase indicator field and convert to regular layout - DoubleArray PhaseField(Nx,Ny,Nz); - //ScaLBL_Comm->RegularLayout(Map,Phi,PhaseField); - ScaLBL_CopyToHost(PhaseField.data(), Phi, sizeof(double)*N); +void ScaLBL_ColorModel::Run() { + int nprocs = nprocx * nprocy * nprocz; + const RankInfoStruct rank_info(rank, nprocx, nprocy, nprocz); + int analysis_interval = + 1000; // number of timesteps in between in situ analysis + if (analysis_db->keyExists("analysis_interval")) { + analysis_interval = analysis_db->getScalar("analysis_interval"); + } - FILE *OUTFILE; - sprintf(LocalRankFilename,"Phase.%05i.raw",rank); - OUTFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,OUTFILE); - fclose(OUTFILE); + //************ MAIN ITERATION LOOP ***************************************/ + comm.barrier(); + PROFILE_START("Loop"); + //std::shared_ptr analysis_db; + bool Regular = false; + auto current_db = db->cloneDatabase(); + runAnalysis analysis(current_db, rank_info, ScaLBL_Comm, Dm, Np, Regular, + Map); + //analysis.createThreads( analysis_method, 4 ); + auto t1 = std::chrono::system_clock::now(); + while (timestep < timestepMax) { + PROFILE_START("Update"); - ScaLBL_Comm->RegularLayout(Map,&Den[0],PhaseField); - FILE *AFILE; - sprintf(LocalRankFilename,"A.%05i.raw",rank); - AFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,AFILE); - fclose(AFILE); + // *************ODD TIMESTEP************* + timestep++; + // Compute the Phase indicator field + // Read for Aq, Bq happens in this routine (requires communication) + ScaLBL_Comm->BiSendD3Q7AA(Aq, Bq); //READ FROM NORMAL + ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->BiRecvD3Q7AA(Aq, Bq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + ScaLBL_D3Q7_AAodd_PhaseField(NeighborList, dvcMap, Aq, Bq, Den, Phi, 0, + ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->RegularLayout(Map,&Den[Np],PhaseField); - FILE *BFILE; - sprintf(LocalRankFilename,"B.%05i.raw",rank); - BFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,BFILE); - fclose(BFILE); + // Perform the collision operation + ScaLBL_Comm->SendD3Q19AA(fq); //READ FROM NORMAL + if (BoundaryCondition > 0 && BoundaryCondition < 5) { + ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); + ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); + } + // Halo exchange for phase field + ScaLBL_Comm_Regular->SendHalo(Phi); - ScaLBL_Comm->RegularLayout(Map,Pressure,PhaseField); - FILE *PFILE; - sprintf(LocalRankFilename,"Pressure.%05i.raw",rank); - PFILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,PFILE); - fclose(PFILE); + ScaLBL_D3Q19_AAodd_Color( + NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, rhoB, + tauA, tauB, alpha, beta, Fx, Fy, Fz, Nx, Nx * Ny, + ScaLBL_Comm->FirstInterior(), ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm_Regular->RecvHalo(Phi); + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + // Set BCs + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } + if (BoundaryCondition == 4) { + din = + ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); + } + ScaLBL_D3Q19_AAodd_Color(NeighborList, dvcMap, fq, Aq, Bq, Den, Phi, + Velocity, rhoA, rhoB, tauA, tauB, alpha, beta, + Fx, Fy, Fz, Nx, Nx * Ny, 0, + ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm->Barrier(); - ScaLBL_Comm->RegularLayout(Map,&Velocity[0],PhaseField); - FILE *VELX_FILE; - sprintf(LocalRankFilename,"Velocity_X.%05i.raw",rank); - VELX_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELX_FILE); - fclose(VELX_FILE); + // *************EVEN TIMESTEP************* + timestep++; + // Compute the Phase indicator field + ScaLBL_Comm->BiSendD3Q7AA(Aq, Bq); //READ FROM NORMAL + ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, + ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm->BiRecvD3Q7AA(Aq, Bq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + ScaLBL_D3Q7_AAeven_PhaseField(dvcMap, Aq, Bq, Den, Phi, 0, + ScaLBL_Comm->LastExterior(), Np); - ScaLBL_Comm->RegularLayout(Map,&Velocity[Np],PhaseField); - FILE *VELY_FILE; - sprintf(LocalRankFilename,"Velocity_Y.%05i.raw",rank); - VELY_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELY_FILE); - fclose(VELY_FILE); + // Perform the collision operation + ScaLBL_Comm->SendD3Q19AA(fq); //READ FORM NORMAL + // Halo exchange for phase field + if (BoundaryCondition > 0 && BoundaryCondition < 5) { + ScaLBL_Comm->Color_BC_z(dvcMap, Phi, Den, inletA, inletB); + ScaLBL_Comm->Color_BC_Z(dvcMap, Phi, Den, outletA, outletB); + } + ScaLBL_Comm_Regular->SendHalo(Phi); + ScaLBL_D3Q19_AAeven_Color(dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, + rhoB, tauA, tauB, alpha, beta, Fx, Fy, Fz, Nx, + Nx * Ny, ScaLBL_Comm->FirstInterior(), + ScaLBL_Comm->LastInterior(), Np); + ScaLBL_Comm_Regular->RecvHalo(Phi); + ScaLBL_Comm->RecvD3Q19AA(fq); //WRITE INTO OPPOSITE + ScaLBL_Comm->Barrier(); + // Set boundary conditions + if (BoundaryCondition == 3) { + ScaLBL_Comm->D3Q19_Pressure_BC_z(NeighborList, fq, din, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 4) { + din = + ScaLBL_Comm->D3Q19_Flux_BC_z(NeighborList, fq, flux, timestep); + ScaLBL_Comm->D3Q19_Pressure_BC_Z(NeighborList, fq, dout, timestep); + } else if (BoundaryCondition == 5) { + ScaLBL_Comm->D3Q19_Reflection_BC_z(fq); + ScaLBL_Comm->D3Q19_Reflection_BC_Z(fq); + } + ScaLBL_D3Q19_AAeven_Color(dvcMap, fq, Aq, Bq, Den, Phi, Velocity, rhoA, + rhoB, tauA, tauB, alpha, beta, Fx, Fy, Fz, Nx, + Nx * Ny, 0, ScaLBL_Comm->LastExterior(), Np); + ScaLBL_Comm->Barrier(); + //************************************************************************ + PROFILE_STOP("Update"); - ScaLBL_Comm->RegularLayout(Map,&Velocity[2*Np],PhaseField); - FILE *VELZ_FILE; - sprintf(LocalRankFilename,"Velocity_Z.%05i.raw",rank); - VELZ_FILE = fopen(LocalRankFilename,"wb"); - fwrite(PhaseField.data(),8,N,VELZ_FILE); - fclose(VELZ_FILE); + if (rank == 0 && timestep % analysis_interval == 0 && + BoundaryCondition == 4) { + printf("%i %f \n", timestep, din); + } + // Run the analysis + analysis.basic(timestep, current_db, *Averages, Phi, Pressure, Velocity, + fq, Den); + } + analysis.finish(); + PROFILE_STOP("Loop"); + PROFILE_SAVE("lbpm_color_simulator", 1); + //************************************************************************ + ScaLBL_Comm->Barrier(); + if (rank == 0) + printf("---------------------------------------------------------------" + "----\n"); + // Compute the walltime per timestep + auto t2 = std::chrono::system_clock::now(); + double cputime = std::chrono::duration(t2 - t1).count() / timestep; + // Performance obtained from each node + double MLUPS = double(Np) / cputime / 1000000; + if (rank == 0) + printf("********************************************************\n"); + if (rank == 0) + printf("CPU time = %f \n", cputime); + if (rank == 0) + printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); + MLUPS *= nprocs; + if (rank == 0) + printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); + if (rank == 0) + printf("********************************************************\n"); + + // ************************************************************************ +} + +void ScaLBL_ColorModel::WriteDebug() { + // Copy back final phase indicator field and convert to regular layout + DoubleArray PhaseField(Nx, Ny, Nz); + //ScaLBL_Comm->RegularLayout(Map,Phi,PhaseField); + ScaLBL_CopyToHost(PhaseField.data(), Phi, sizeof(double) * N); + + FILE *OUTFILE; + sprintf(LocalRankFilename, "Phase.%05i.raw", rank); + OUTFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, OUTFILE); + fclose(OUTFILE); + + ScaLBL_Comm->RegularLayout(Map, &Den[0], PhaseField); + FILE *AFILE; + sprintf(LocalRankFilename, "A.%05i.raw", rank); + AFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, AFILE); + fclose(AFILE); + + ScaLBL_Comm->RegularLayout(Map, &Den[Np], PhaseField); + FILE *BFILE; + sprintf(LocalRankFilename, "B.%05i.raw", rank); + BFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, BFILE); + fclose(BFILE); + + ScaLBL_Comm->RegularLayout(Map, Pressure, PhaseField); + FILE *PFILE; + sprintf(LocalRankFilename, "Pressure.%05i.raw", rank); + PFILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, PFILE); + fclose(PFILE); + + ScaLBL_Comm->RegularLayout(Map, &Velocity[0], PhaseField); + FILE *VELX_FILE; + sprintf(LocalRankFilename, "Velocity_X.%05i.raw", rank); + VELX_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELX_FILE); + fclose(VELX_FILE); + + ScaLBL_Comm->RegularLayout(Map, &Velocity[Np], PhaseField); + FILE *VELY_FILE; + sprintf(LocalRankFilename, "Velocity_Y.%05i.raw", rank); + VELY_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELY_FILE); + fclose(VELY_FILE); + + ScaLBL_Comm->RegularLayout(Map, &Velocity[2 * Np], PhaseField); + FILE *VELZ_FILE; + sprintf(LocalRankFilename, "Velocity_Z.%05i.raw", rank); + VELZ_FILE = fopen(LocalRankFilename, "wb"); + fwrite(PhaseField.data(), 8, N, VELZ_FILE); + fclose(VELZ_FILE); } From a65ceef7d57dd70be6d59db57ece20df111a8ca3 Mon Sep 17 00:00:00 2001 From: James McClure Date: Thu, 9 Dec 2021 10:16:47 -0500 Subject: [PATCH 51/54] try 2 --- models/ColorModel.cpp | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/models/ColorModel.cpp b/models/ColorModel.cpp index f63d7ee5..7a356fc0 100644 --- a/models/ColorModel.cpp +++ b/models/ColorModel.cpp @@ -960,32 +960,24 @@ double ScaLBL_ColorModel::Run(int returntime) { fprintf(scal_log_file, "timesteps sat.water "); fprintf(scal_log_file, "eff.perm.oil.upper.bound " "eff.perm.water.upper.bound "); - fprintf(scal_log_file, "eff.perm.oil.lower.bound " + fprintf(scal_log_file, + "eff.perm.oil.lower.bound " "eff.perm.water.lower.bound "); - fprintf(scal_log_file, - "eff.perm.oil.connected.upper.bound " - "eff.perm.water.connected.upper.bound "); - fprintf(scal_log_file, - "eff.perm.oil.connected.lower.bound " - "eff.perm.water.connected.lower.bound "); fprintf(scal_log_file, "eff.perm.oil.disconnected " "eff.perm.water.disconnected "); fprintf(scal_log_file, "cap.pressure cap.pressure.connected " - "pressure.drop Ca M eff.pressure\n"); + "Ca eff.pressure\n"); } fprintf(scal_log_file, "%i %.5g ", CURRENT_TIMESTEP, current_saturation); - fprintf(scal_log_file, "%.5g %.5g ", kAeff, kBeff); fprintf(scal_log_file, "%.5g %.5g ", kAeff_low, kBeff_low); - fprintf(scal_log_file, "%.5g %.5g ", kAeff_connected, - kBeff_connected); fprintf(scal_log_file, "%.5g %.5g ", kAeff_connected_low, kBeff_connected_low); fprintf(scal_log_file, "%.5g %.5g ", kAeff_disconnected, kBeff_disconnected); - fprintf(scal_log_file, "%.5g %.5g %.5g %.5g %.5g ", pAB, - pAB_connected, viscous_pressure_drop, Ca, Mobility); + fprintf(scal_log_file, "%.5g %.5g %.5g ", pAB, + pAB_connected, Ca); fprintf(scal_log_file, "%.5g\n", eff_pres); fclose(scal_log_file); From ba67d29e5c14dcee2287a5534f41c9eecafcbd47 Mon Sep 17 00:00:00 2001 From: James McClure Date: Thu, 9 Dec 2021 14:01:27 -0500 Subject: [PATCH 52/54] fix memory leak --- analysis/FlowAdaptor.cpp | 1076 +++++++++++++++++++++----------------- 1 file changed, 583 insertions(+), 493 deletions(-) diff --git a/analysis/FlowAdaptor.cpp b/analysis/FlowAdaptor.cpp index bbd1efbd..f95d94a5 100644 --- a/analysis/FlowAdaptor.cpp +++ b/analysis/FlowAdaptor.cpp @@ -4,511 +4,601 @@ #include "analysis/distance.h" #include "analysis/morphology.h" -FlowAdaptor::FlowAdaptor(ScaLBL_ColorModel &M){ - Nx = M.Dm->Nx; - Ny = M.Dm->Ny; - Nz = M.Dm->Nz; - timestep=-1; - timestep_previous=-1; +FlowAdaptor::FlowAdaptor(ScaLBL_ColorModel &M) { + Nx = M.Dm->Nx; + Ny = M.Dm->Ny; + Nz = M.Dm->Nz; + timestep = -1; + timestep_previous = -1; - phi.resize(Nx,Ny,Nz); phi.fill(0); // phase indicator field - phi_t.resize(Nx,Ny,Nz); phi_t.fill(0); // time derivative for the phase indicator field + phi.resize(Nx, Ny, Nz); + phi.fill(0); // phase indicator field + phi_t.resize(Nx, Ny, Nz); + phi_t.fill(0); // time derivative for the phase indicator field } -FlowAdaptor::~FlowAdaptor(){ +FlowAdaptor::~FlowAdaptor() { -} - -double FlowAdaptor::ImageInit(ScaLBL_ColorModel &M, std::string Filename){ - int rank = M.rank; - int Nx = M.Nx; int Ny = M.Ny; int Nz = M.Nz; - if (rank==0) printf("Re-initializing fluids from file: %s \n", Filename.c_str()); - M.Mask->Decomp(Filename); - for (int i=0; iid[i]; // save what was read - for (int i=0; iid[i] = M.Mask->id[i]; // save what was read - - double *PhaseLabel; - PhaseLabel = new double[Nx*Ny*Nz]; - M.AssignComponentLabels(PhaseLabel); - - double Count = 0.0; - double PoreCount = 0.0; - for (int k=1; kComm.sumReduce( Count); - PoreCount=M.Dm->Comm.sumReduce( PoreCount); - if (rank==0) printf(" new saturation: %f (%f / %f) \n", Count / PoreCount, Count, PoreCount); - ScaLBL_CopyToDevice(M.Phi, PhaseLabel, Nx*Ny*Nz*sizeof(double)); - M.Dm->Comm.barrier(); - - ScaLBL_D3Q19_Init(M.fq, M.Np); - ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, 0, M.ScaLBL_Comm->LastExterior(), M.Np); - ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, M.ScaLBL_Comm->FirstInterior(), M.ScaLBL_Comm->LastInterior(), M.Np); - M.Dm->Comm.barrier(); - - ScaLBL_CopyToHost(M.Averages->Phi.data(),M.Phi,Nx*Ny*Nz*sizeof(double)); - - double saturation = Count/PoreCount; - return saturation; } +double FlowAdaptor::ImageInit(ScaLBL_ColorModel &M, std::string Filename) { + int rank = M.rank; + int Nx = M.Nx; + int Ny = M.Ny; + int Nz = M.Nz; + if (rank == 0) + printf("Re-initializing fluids from file: %s \n", Filename.c_str()); + M.Mask->Decomp(Filename); + for (int i = 0; i < Nx * Ny * Nz; i++) + M.id[i] = M.Mask->id[i]; // save what was read + for (int i = 0; i < Nx * Ny * Nz; i++) + M.Dm->id[i] = M.Mask->id[i]; // save what was read -double FlowAdaptor::UpdateFractionalFlow(ScaLBL_ColorModel &M){ + double *PhaseLabel; + PhaseLabel = new double[Nx * Ny * Nz]; + M.AssignComponentLabels(PhaseLabel); - double MASS_FRACTION_CHANGE = 0.006; - double FRACTIONAL_FLOW_EPSILON = 5e-6; - if (M.db->keyExists( "FlowAdaptor" )){ - auto flow_db = M.db->getDatabase( "FlowAdaptor" ); - MASS_FRACTION_CHANGE = flow_db->getWithDefault( "mass_fraction_factor", 0.006); - FRACTIONAL_FLOW_EPSILON = flow_db->getWithDefault( "fractional_flow_epsilon", 5e-6); - } - int Np = M.Np; - double dA, dB, phi; - double vx,vy,vz; - double mass_a, mass_b, mass_a_global, mass_b_global; - - double *Aq_tmp, *Bq_tmp; - double *Vel_x, *Vel_y, *Vel_z, *Phase; - - Aq_tmp = new double [7*Np]; - Bq_tmp = new double [7*Np]; - Phase = new double [Np]; - Vel_x = new double [Np]; - Vel_y = new double [Np]; - Vel_z = new double [Np]; - - ScaLBL_CopyToHost(Aq_tmp, M.Aq, 7*Np*sizeof(double)); - ScaLBL_CopyToHost(Bq_tmp, M.Bq, 7*Np*sizeof(double)); - ScaLBL_CopyToHost(Vel_x, &M.Velocity[0], Np*sizeof(double)); - ScaLBL_CopyToHost(Vel_y, &M.Velocity[Np], Np*sizeof(double)); - ScaLBL_CopyToHost(Vel_z, &M.Velocity[2*Np], Np*sizeof(double)); - - int Nx = M.Nx; int Ny = M.Ny; int Nz = M.Nz; - - mass_a = mass_b = 0.0; - double maxSpeed = 0.0; - double localMaxSpeed = 0.0; - /* compute mass change based on weights */ - double sum_weights_A = 0.0; - double sum_weights_B = 0.0; - for (int k=1; kSDs(i,j,k); - if (!(n<0) ){ - dA = Aq_tmp[n] + Aq_tmp[n+Np] + Aq_tmp[n+2*Np] + Aq_tmp[n+3*Np] + Aq_tmp[n+4*Np] + Aq_tmp[n+5*Np] + Aq_tmp[n+6*Np]; - dB = Bq_tmp[n] + Bq_tmp[n+Np] + Bq_tmp[n+2*Np] + Bq_tmp[n+3*Np] + Bq_tmp[n+4*Np] + Bq_tmp[n+5*Np] + Bq_tmp[n+6*Np]; - phi = (dA - dB) / (dA + dB); - Phase[n] = phi; - mass_a += dA; - mass_b += dB; - vx = Vel_x[n]; - vy = Vel_y[n]; - vz = Vel_z[n]; - double local_momentum = sqrt(vx*vx+vy*vy+vz*vz); - double local_weight = (FRACTIONAL_FLOW_EPSILON + local_momentum); - if (phi > 0.0){ - sum_weights_A += local_weight*dA; - } - else { - sum_weights_B += local_weight*dB; - } - if ( local_momentum > localMaxSpeed){ - localMaxSpeed = local_momentum; - } - } - } - } - } - maxSpeed = M.Dm->Comm.maxReduce(localMaxSpeed); - mass_a_global = M.Dm->Comm.sumReduce(mass_a); - mass_b_global = M.Dm->Comm.sumReduce(mass_b); - double sum_weights_A_global = M.Dm->Comm.sumReduce(sum_weights_A); - double sum_weights_B_global = M.Dm->Comm.sumReduce(sum_weights_B); - sum_weights_A_global /= (FRACTIONAL_FLOW_EPSILON + maxSpeed); - sum_weights_B_global /= (FRACTIONAL_FLOW_EPSILON + maxSpeed); - - //double total_momentum_A = sqrt(vax_global*vax_global+vay_global*vay_global+vaz_global*vaz_global); - //double total_momentum_B = sqrt(vbx_global*vbx_global+vby_global*vby_global+vbz_global*vbz_global); - /* compute the total mass change */ - double TOTAL_MASS_CHANGE = MASS_FRACTION_CHANGE*(mass_a_global + mass_b_global); - if (fabs(TOTAL_MASS_CHANGE) > 0.1*mass_a_global ) - TOTAL_MASS_CHANGE = 0.1*mass_a_global; - if (fabs(TOTAL_MASS_CHANGE) > 0.1*mass_b_global ) - TOTAL_MASS_CHANGE = 0.1*mass_b_global; - - double MASS_FACTOR_A = TOTAL_MASS_CHANGE / sum_weights_A_global; - double MASS_FACTOR_B = TOTAL_MASS_CHANGE / sum_weights_B_global; - - double LOCAL_MASS_CHANGE = 0.0; - for (int k=1; k maxSpeed) local_momentum = maxSpeed; - if (phi > 0.0){ - LOCAL_MASS_CHANGE = MASS_FACTOR_A*local_weight; - Aq_tmp[n] -= 0.3333333333333333*LOCAL_MASS_CHANGE; - Aq_tmp[n+Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE; - Aq_tmp[n+2*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE; - Aq_tmp[n+3*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE; - Aq_tmp[n+4*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE; - Aq_tmp[n+5*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE; - Aq_tmp[n+6*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE; - //DebugMassA[n] = (-1.0)*LOCAL_MASS_CHANGE; - } - else{ - LOCAL_MASS_CHANGE = MASS_FACTOR_B*local_weight; - Bq_tmp[n] += 0.3333333333333333*LOCAL_MASS_CHANGE; - Bq_tmp[n+Np] += 0.1111111111111111*LOCAL_MASS_CHANGE; - Bq_tmp[n+2*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE; - Bq_tmp[n+3*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE; - Bq_tmp[n+4*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE; - Bq_tmp[n+5*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE; - Bq_tmp[n+6*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE; - //DebugMassB[n] = LOCAL_MASS_CHANGE; - } - } - } - } - } - - if (M.rank == 0) printf("Update Fractional Flow: change mass of fluid B by %f \n",TOTAL_MASS_CHANGE/mass_b_global); - - // Need to initialize Aq, Bq, Den, Phi directly - //ScaLBL_CopyToDevice(Phi,phase.data(),7*Np*sizeof(double)); - ScaLBL_CopyToDevice(M.Aq, Aq_tmp, 7*Np*sizeof(double)); - ScaLBL_CopyToDevice(M.Bq, Bq_tmp, 7*Np*sizeof(double)); - - return(TOTAL_MASS_CHANGE); -} - -void FlowAdaptor::Flatten(ScaLBL_ColorModel &M){ - - ScaLBL_D3Q19_Init(M.fq, M.Np); - ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, 0, M.ScaLBL_Comm->LastExterior(), M.Np); - ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, M.ScaLBL_Comm->FirstInterior(), M.ScaLBL_Comm->LastInterior(), M.Np); -} - -double FlowAdaptor::MoveInterface(ScaLBL_ColorModel &M){ - - double INTERFACE_CUTOFF = M.color_db->getWithDefault( "move_interface_cutoff", 0.1 ); - double MOVE_INTERFACE_FACTOR = M.color_db->getWithDefault( "move_interface_factor", 10.0 ); - - ScaLBL_CopyToHost( phi.data(), M.Phi, Nx*Ny*Nz* sizeof( double ) ); - /* compute the local derivative of phase indicator field */ - double beta = M.beta; - double factor = 0.5/beta; - double total_interface_displacement = 0.0; - double total_interface_sites = 0.0; - for (int n=0; nPhi(n); - double dist1 = factor*log((1.0+value1)/(1.0-value1)); - double value2 = phi(n); - double dist2 = factor*log((1.0+value2)/(1.0-value2)); - phi_t(n) = value2; - if (value1 < INTERFACE_CUTOFF && value1 > -1*INTERFACE_CUTOFF && value2 < INTERFACE_CUTOFF && value2 > -1*INTERFACE_CUTOFF ){ - /* time derivative of distance */ - double dxdt = 0.125*(dist2-dist1); - /* extrapolate to move the distance further */ - double dist3 = dist2 + MOVE_INTERFACE_FACTOR*dxdt; - /* compute the new phase interface */ - phi_t(n) = (2.f*(exp(-2.f*beta*(dist3)))/(1.f+exp(-2.f*beta*(dist3))) - 1.f); - total_interface_displacement += fabs(MOVE_INTERFACE_FACTOR*dxdt); - total_interface_sites += 1.0; - } - } - ScaLBL_CopyToDevice( M.Phi, phi_t.data(), Nx*Ny*Nz* sizeof( double ) ); - return total_interface_sites; -} - -double FlowAdaptor::ShellAggregation(ScaLBL_ColorModel &M, const double target_delta_volume){ - - const RankInfoStruct rank_info(M.rank,M.nprocx,M.nprocy,M.nprocz); - auto rank = M.rank; - auto Nx = M.Nx; auto Ny = M.Ny; auto Nz = M.Nz; - auto N = Nx*Ny*Nz; - double vF = 0.f; - double vS = 0.f; - double delta_volume; - double WallFactor = 1.0; - bool USE_CONNECTED_NWP = false; - - DoubleArray phase(Nx,Ny,Nz); - IntArray phase_label(Nx,Ny,Nz);; - DoubleArray phase_distance(Nx,Ny,Nz); - Array phase_id(Nx,Ny,Nz); - fillHalo fillDouble(M.Dm->Comm,M.Dm->rank_info,{Nx-2,Ny-2,Nz-2},{1,1,1},0,1); - - // Basic algorithm to - // 1. Copy phase field to CPU - ScaLBL_CopyToHost(phase.data(), M.Phi, N*sizeof(double)); - - double count = 0.f; - for (int k=1; k 0.f && M.Averages->SDs(i,j,k) > 0.f) count+=1.f; - } - } - } - double volume_initial = M.Dm->Comm.sumReduce( count); - double PoreVolume = M.Dm->Volume*M.Dm->Porosity(); - /*ensure target isn't an absurdly small fraction of pore volume */ - if (volume_initial < target_delta_volume*PoreVolume){ - volume_initial = target_delta_volume*PoreVolume; - } - - // 2. Identify connected components of phase field -> phase_label - - double volume_connected = 0.0; - double second_biggest = 0.0; - if (USE_CONNECTED_NWP){ - ComputeGlobalBlobIDs(Nx-2,Ny-2,Nz-2,rank_info,phase,M.Averages->SDs,vF,vS,phase_label,M.Dm->Comm); - M.Dm->Comm.barrier(); - - // only operate on component "0"ScaLBL_ColorModel &M, - count = 0.0; - - for (int k=0; kComm.sumReduce( count); - second_biggest = M.Dm->Comm.sumReduce( second_biggest); - } - else { - // use the whole NWP - for (int k=0; kSDs(i,j,k) > 0.f){ - if (phase(i,j,k) > 0.f ){ - phase_id(i,j,k) = 0; - } - else { - phase_id(i,j,k) = 1; - } - } - else { - phase_id(i,j,k) = 1; - } - } - } - } - } - - // 3. Generate a distance map to the largest object -> phase_distance - CalcDist(phase_distance,phase_id,*M.Dm); - - double temp,value; - double factor=0.5/M.beta; - for (int k=0; k 1.f) value=1.f; - if (value < -1.f) value=-1.f; - // temp -- distance based on analytical form McClure, Prins et al, Comp. Phys. Comm. - temp = -factor*log((1.0+value)/(1.0-value)); - /// use this approximation close to the object - if (fabs(value) < 0.8 && M.Averages->SDs(i,j,k) > 1.f ){ - phase_distance(i,j,k) = temp; - } - // erase the original object - phase(i,j,k) = -1.0; - } - } - } - } - - - if (rank==0) printf("Pathway volume / next largest ganglion %f \n",volume_connected/second_biggest ); - - if (rank==0) printf("MorphGrow with target volume fraction change %f \n", target_delta_volume/volume_initial); - double target_delta_volume_incremental = target_delta_volume; - if (fabs(target_delta_volume) > 0.01*volume_initial) - target_delta_volume_incremental = 0.01*volume_initial*target_delta_volume/fabs(target_delta_volume); - - delta_volume = MorphGrow(M.Averages->SDs,phase_distance,phase_id,M.Averages->Dm, target_delta_volume_incremental, WallFactor); - - for (int k=0; kSDs(i,j,k) > 0.f){ - if (d < 3.f){ - //phase(i,j,k) = -1.0; - phase(i,j,k) = (2.f*(exp(-2.f*M.beta*d))/(1.f+exp(-2.f*M.beta*d))-1.f); - } - } - } - } - } - fillDouble.fill(phase); - - count = 0.f; - for (int k=1; k 0.f && M.Averages->SDs(i,j,k) > 0.f){ - count+=1.f; - } - } - } - } - double volume_final= M.Dm->Comm.sumReduce( count); - - delta_volume = (volume_final-volume_initial); - if (rank == 0) printf("Shell Aggregation: change fluid volume fraction by %f \n", delta_volume/volume_initial); - if (rank == 0) printf(" new saturation = %f \n", volume_final/(M.Mask->Porosity()*double((Nx-2)*(Ny-2)*(Nz-2)*M.nprocs))); - - // 6. copy back to the device - //if (rank==0) printf("MorphInit: copy data back to device\n"); - ScaLBL_CopyToDevice(M.Phi,phase.data(),N*sizeof(double)); - - // 7. Re-initialize phase field and density - ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, 0, M.ScaLBL_Comm->LastExterior(), M.Np); - ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, M.ScaLBL_Comm->FirstInterior(), M.ScaLBL_Comm->LastInterior(), M.Np); - auto BoundaryCondition = M.BoundaryCondition; - if (BoundaryCondition == 1 || BoundaryCondition == 2 || BoundaryCondition == 3 || BoundaryCondition == 4){ - if (M.Dm->kproc()==0){ - ScaLBL_SetSlice_z(M.Phi,1.0,Nx,Ny,Nz,0); - ScaLBL_SetSlice_z(M.Phi,1.0,Nx,Ny,Nz,1); - ScaLBL_SetSlice_z(M.Phi,1.0,Nx,Ny,Nz,2); - } - if (M.Dm->kproc() == M.nprocz-1){ - ScaLBL_SetSlice_z(M.Phi,-1.0,Nx,Ny,Nz,Nz-1); - ScaLBL_SetSlice_z(M.Phi,-1.0,Nx,Ny,Nz,Nz-2); - ScaLBL_SetSlice_z(M.Phi,-1.0,Nx,Ny,Nz,Nz-3); - } - } - return delta_volume; -} - - -double FlowAdaptor::SeedPhaseField(ScaLBL_ColorModel &M, const double seed_water_in_oil){ - srand(time(NULL)); - auto rank = M.rank; - auto Np = M.Np; - double mass_loss =0.f; - double count =0.f; - double *Aq_tmp, *Bq_tmp; - - Aq_tmp = new double [7*Np]; - Bq_tmp = new double [7*Np]; - - ScaLBL_CopyToHost(Aq_tmp, M.Aq, 7*Np*sizeof(double)); - ScaLBL_CopyToHost(Bq_tmp, M.Bq, 7*Np*sizeof(double)); - - - for (int n=0; n < M.ScaLBL_Comm->LastExterior(); n++){ - double random_value = seed_water_in_oil*double(rand())/ RAND_MAX; - double dA = Aq_tmp[n] + Aq_tmp[n+Np] + Aq_tmp[n+2*Np] + Aq_tmp[n+3*Np] + Aq_tmp[n+4*Np] + Aq_tmp[n+5*Np] + Aq_tmp[n+6*Np]; - double dB = Bq_tmp[n] + Bq_tmp[n+Np] + Bq_tmp[n+2*Np] + Bq_tmp[n+3*Np] + Bq_tmp[n+4*Np] + Bq_tmp[n+5*Np] + Bq_tmp[n+6*Np]; - double phase_id = (dA - dB) / (dA + dB); - if (phase_id > 0.0){ - Aq_tmp[n] -= 0.3333333333333333*random_value; - Aq_tmp[n+Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+2*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+3*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+4*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+5*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+6*Np] -= 0.1111111111111111*random_value; - - Bq_tmp[n] += 0.3333333333333333*random_value; - Bq_tmp[n+Np] += 0.1111111111111111*random_value; - Bq_tmp[n+2*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+3*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+4*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+5*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+6*Np] += 0.1111111111111111*random_value; + double Count = 0.0; + double PoreCount = 0.0; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + if (M.id[Nx * Ny * k + Nx * j + i] == 2) { + PoreCount++; + Count++; + } else if (M.id[Nx * Ny * k + Nx * j + i] == 1) { + PoreCount++; + } + } + } } - mass_loss += random_value*seed_water_in_oil; - } - for (int n=M.ScaLBL_Comm->FirstInterior(); n < M.ScaLBL_Comm->LastInterior(); n++){ - double random_value = seed_water_in_oil*double(rand())/ RAND_MAX; - double dA = Aq_tmp[n] + Aq_tmp[n+Np] + Aq_tmp[n+2*Np] + Aq_tmp[n+3*Np] + Aq_tmp[n+4*Np] + Aq_tmp[n+5*Np] + Aq_tmp[n+6*Np]; - double dB = Bq_tmp[n] + Bq_tmp[n+Np] + Bq_tmp[n+2*Np] + Bq_tmp[n+3*Np] + Bq_tmp[n+4*Np] + Bq_tmp[n+5*Np] + Bq_tmp[n+6*Np]; - double phase_id = (dA - dB) / (dA + dB); - if (phase_id > 0.0){ - Aq_tmp[n] -= 0.3333333333333333*random_value; - Aq_tmp[n+Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+2*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+3*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+4*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+5*Np] -= 0.1111111111111111*random_value; - Aq_tmp[n+6*Np] -= 0.1111111111111111*random_value; - - Bq_tmp[n] += 0.3333333333333333*random_value; - Bq_tmp[n+Np] += 0.1111111111111111*random_value; - Bq_tmp[n+2*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+3*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+4*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+5*Np] += 0.1111111111111111*random_value; - Bq_tmp[n+6*Np] += 0.1111111111111111*random_value; - } - mass_loss += random_value*seed_water_in_oil; - } + Count = M.Dm->Comm.sumReduce(Count); + PoreCount = M.Dm->Comm.sumReduce(PoreCount); - count= M.Dm->Comm.sumReduce( count); - mass_loss= M.Dm->Comm.sumReduce( mass_loss); - if (rank == 0) printf("Remove mass %f from %f voxels \n",mass_loss,count); + if (rank == 0) + printf(" new saturation: %f (%f / %f) \n", Count / PoreCount, Count, + PoreCount); + ScaLBL_CopyToDevice(M.Phi, PhaseLabel, Nx * Ny * Nz * sizeof(double)); + M.Dm->Comm.barrier(); - // Need to initialize Aq, Bq, Den, Phi directly - //ScaLBL_CopyToDevice(Phi,phase.data(),7*Np*sizeof(double)); - ScaLBL_CopyToDevice(M.Aq, Aq_tmp, 7*Np*sizeof(double)); - ScaLBL_CopyToDevice(M.Bq, Bq_tmp, 7*Np*sizeof(double)); + ScaLBL_D3Q19_Init(M.fq, M.Np); + ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, 0, + M.ScaLBL_Comm->LastExterior(), M.Np); + ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, + M.ScaLBL_Comm->FirstInterior(), + M.ScaLBL_Comm->LastInterior(), M.Np); + M.Dm->Comm.barrier(); - return(mass_loss); + ScaLBL_CopyToHost(M.Averages->Phi.data(), M.Phi, + Nx * Ny * Nz * sizeof(double)); + + delete PhaseLabel; + double saturation = Count / PoreCount; + return saturation; +} + +double FlowAdaptor::UpdateFractionalFlow(ScaLBL_ColorModel &M) { + + double MASS_FRACTION_CHANGE = 0.006; + double FRACTIONAL_FLOW_EPSILON = 5e-6; + if (M.db->keyExists("FlowAdaptor")) { + auto flow_db = M.db->getDatabase("FlowAdaptor"); + MASS_FRACTION_CHANGE = + flow_db->getWithDefault("mass_fraction_factor", 0.006); + FRACTIONAL_FLOW_EPSILON = + flow_db->getWithDefault("fractional_flow_epsilon", 5e-6); + } + int Np = M.Np; + double dA, dB, phi; + double vx, vy, vz; + double mass_a, mass_b, mass_a_global, mass_b_global; + + double *Aq_tmp, *Bq_tmp; + double *Vel_x, *Vel_y, *Vel_z, *Phase; + + Aq_tmp = new double[7 * Np]; + Bq_tmp = new double[7 * Np]; + Phase = new double[Np]; + Vel_x = new double[Np]; + Vel_y = new double[Np]; + Vel_z = new double[Np]; + + ScaLBL_CopyToHost(Aq_tmp, M.Aq, 7 * Np * sizeof(double)); + ScaLBL_CopyToHost(Bq_tmp, M.Bq, 7 * Np * sizeof(double)); + ScaLBL_CopyToHost(Vel_x, &M.Velocity[0], Np * sizeof(double)); + ScaLBL_CopyToHost(Vel_y, &M.Velocity[Np], Np * sizeof(double)); + ScaLBL_CopyToHost(Vel_z, &M.Velocity[2 * Np], Np * sizeof(double)); + + int Nx = M.Nx; + int Ny = M.Ny; + int Nz = M.Nz; + + mass_a = mass_b = 0.0; + double maxSpeed = 0.0; + double localMaxSpeed = 0.0; + /* compute mass change based on weights */ + double sum_weights_A = 0.0; + double sum_weights_B = 0.0; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + int n = M.Map(i, j, k); + //double distance = M.Averages->SDs(i,j,k); + if (!(n < 0)) { + dA = Aq_tmp[n] + Aq_tmp[n + Np] + Aq_tmp[n + 2 * Np] + + Aq_tmp[n + 3 * Np] + Aq_tmp[n + 4 * Np] + + Aq_tmp[n + 5 * Np] + Aq_tmp[n + 6 * Np]; + dB = Bq_tmp[n] + Bq_tmp[n + Np] + Bq_tmp[n + 2 * Np] + + Bq_tmp[n + 3 * Np] + Bq_tmp[n + 4 * Np] + + Bq_tmp[n + 5 * Np] + Bq_tmp[n + 6 * Np]; + phi = (dA - dB) / (dA + dB); + Phase[n] = phi; + mass_a += dA; + mass_b += dB; + vx = Vel_x[n]; + vy = Vel_y[n]; + vz = Vel_z[n]; + double local_momentum = sqrt(vx * vx + vy * vy + vz * vz); + double local_weight = + (FRACTIONAL_FLOW_EPSILON + local_momentum); + if (phi > 0.0) { + sum_weights_A += local_weight * dA; + } else { + sum_weights_B += local_weight * dB; + } + if (local_momentum > localMaxSpeed) { + localMaxSpeed = local_momentum; + } + } + } + } + } + maxSpeed = M.Dm->Comm.maxReduce(localMaxSpeed); + mass_a_global = M.Dm->Comm.sumReduce(mass_a); + mass_b_global = M.Dm->Comm.sumReduce(mass_b); + double sum_weights_A_global = M.Dm->Comm.sumReduce(sum_weights_A); + double sum_weights_B_global = M.Dm->Comm.sumReduce(sum_weights_B); + sum_weights_A_global /= (FRACTIONAL_FLOW_EPSILON + maxSpeed); + sum_weights_B_global /= (FRACTIONAL_FLOW_EPSILON + maxSpeed); + + //double total_momentum_A = sqrt(vax_global*vax_global+vay_global*vay_global+vaz_global*vaz_global); + //double total_momentum_B = sqrt(vbx_global*vbx_global+vby_global*vby_global+vbz_global*vbz_global); + /* compute the total mass change */ + double TOTAL_MASS_CHANGE = + MASS_FRACTION_CHANGE * (mass_a_global + mass_b_global); + if (fabs(TOTAL_MASS_CHANGE) > 0.1 * mass_a_global) + TOTAL_MASS_CHANGE = 0.1 * mass_a_global; + if (fabs(TOTAL_MASS_CHANGE) > 0.1 * mass_b_global) + TOTAL_MASS_CHANGE = 0.1 * mass_b_global; + + double MASS_FACTOR_A = TOTAL_MASS_CHANGE / sum_weights_A_global; + double MASS_FACTOR_B = TOTAL_MASS_CHANGE / sum_weights_B_global; + + double LOCAL_MASS_CHANGE = 0.0; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + int n = M.Map(i, j, k); + if (!(n < 0)) { + phi = Phase[n]; + vx = Vel_x[n]; + vy = Vel_y[n]; + vz = Vel_z[n]; + double local_momentum = sqrt(vx * vx + vy * vy + vz * vz); + double local_weight = + (FRACTIONAL_FLOW_EPSILON + local_momentum) / + (FRACTIONAL_FLOW_EPSILON + maxSpeed); + /* impose ceiling for spurious currents */ + //if (local_momentum > maxSpeed) local_momentum = maxSpeed; + if (phi > 0.0) { + LOCAL_MASS_CHANGE = MASS_FACTOR_A * local_weight; + Aq_tmp[n] -= 0.3333333333333333 * LOCAL_MASS_CHANGE; + Aq_tmp[n + Np] -= + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Aq_tmp[n + 2 * Np] -= + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Aq_tmp[n + 3 * Np] -= + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Aq_tmp[n + 4 * Np] -= + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Aq_tmp[n + 5 * Np] -= + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Aq_tmp[n + 6 * Np] -= + 0.1111111111111111 * LOCAL_MASS_CHANGE; + //DebugMassA[n] = (-1.0)*LOCAL_MASS_CHANGE; + } else { + LOCAL_MASS_CHANGE = MASS_FACTOR_B * local_weight; + Bq_tmp[n] += 0.3333333333333333 * LOCAL_MASS_CHANGE; + Bq_tmp[n + Np] += + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Bq_tmp[n + 2 * Np] += + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Bq_tmp[n + 3 * Np] += + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Bq_tmp[n + 4 * Np] += + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Bq_tmp[n + 5 * Np] += + 0.1111111111111111 * LOCAL_MASS_CHANGE; + Bq_tmp[n + 6 * Np] += + 0.1111111111111111 * LOCAL_MASS_CHANGE; + //DebugMassB[n] = LOCAL_MASS_CHANGE; + } + } + } + } + } + + if (M.rank == 0) + printf("Update Fractional Flow: change mass of fluid B by %f \n", + TOTAL_MASS_CHANGE / mass_b_global); + + // Need to initialize Aq, Bq, Den, Phi directly + //ScaLBL_CopyToDevice(Phi,phase.data(),7*Np*sizeof(double)); + ScaLBL_CopyToDevice(M.Aq, Aq_tmp, 7 * Np * sizeof(double)); + ScaLBL_CopyToDevice(M.Bq, Bq_tmp, 7 * Np * sizeof(double)); + + delete Aq_tmp; + delete Bq_tmp; + delete Vel_x; + delete Vel_y; + delete Vel_z; + delete Phase; + + return (TOTAL_MASS_CHANGE); +} + +void FlowAdaptor::Flatten(ScaLBL_ColorModel &M) { + + ScaLBL_D3Q19_Init(M.fq, M.Np); + ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, 0, + M.ScaLBL_Comm->LastExterior(), M.Np); + ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, + M.ScaLBL_Comm->FirstInterior(), + M.ScaLBL_Comm->LastInterior(), M.Np); +} + +double FlowAdaptor::MoveInterface(ScaLBL_ColorModel &M) { + + double INTERFACE_CUTOFF = + M.color_db->getWithDefault("move_interface_cutoff", 0.1); + double MOVE_INTERFACE_FACTOR = + M.color_db->getWithDefault("move_interface_factor", 10.0); + + ScaLBL_CopyToHost(phi.data(), M.Phi, Nx * Ny * Nz * sizeof(double)); + /* compute the local derivative of phase indicator field */ + double beta = M.beta; + double factor = 0.5 / beta; + double total_interface_displacement = 0.0; + double total_interface_sites = 0.0; + for (int n = 0; n < Nx * Ny * Nz; n++) { + /* compute the distance to the interface */ + double value1 = M.Averages->Phi(n); + double dist1 = factor * log((1.0 + value1) / (1.0 - value1)); + double value2 = phi(n); + double dist2 = factor * log((1.0 + value2) / (1.0 - value2)); + phi_t(n) = value2; + if (value1 < INTERFACE_CUTOFF && value1 > -1 * INTERFACE_CUTOFF && + value2 < INTERFACE_CUTOFF && value2 > -1 * INTERFACE_CUTOFF) { + /* time derivative of distance */ + double dxdt = 0.125 * (dist2 - dist1); + /* extrapolate to move the distance further */ + double dist3 = dist2 + MOVE_INTERFACE_FACTOR * dxdt; + /* compute the new phase interface */ + phi_t(n) = (2.f * (exp(-2.f * beta * (dist3))) / + (1.f + exp(-2.f * beta * (dist3))) - + 1.f); + total_interface_displacement += fabs(MOVE_INTERFACE_FACTOR * dxdt); + total_interface_sites += 1.0; + } + } + ScaLBL_CopyToDevice(M.Phi, phi_t.data(), Nx * Ny * Nz * sizeof(double)); + return total_interface_sites; +} + +double FlowAdaptor::ShellAggregation(ScaLBL_ColorModel &M, + const double target_delta_volume) { + + const RankInfoStruct rank_info(M.rank, M.nprocx, M.nprocy, M.nprocz); + auto rank = M.rank; + auto Nx = M.Nx; + auto Ny = M.Ny; + auto Nz = M.Nz; + auto N = Nx * Ny * Nz; + double vF = 0.f; + double vS = 0.f; + double delta_volume; + double WallFactor = 1.0; + bool USE_CONNECTED_NWP = false; + + DoubleArray phase(Nx, Ny, Nz); + IntArray phase_label(Nx, Ny, Nz); + ; + DoubleArray phase_distance(Nx, Ny, Nz); + Array phase_id(Nx, Ny, Nz); + fillHalo fillDouble(M.Dm->Comm, M.Dm->rank_info, + {Nx - 2, Ny - 2, Nz - 2}, {1, 1, 1}, 0, 1); + + // Basic algorithm to + // 1. Copy phase field to CPU + ScaLBL_CopyToHost(phase.data(), M.Phi, N * sizeof(double)); + + double count = 0.f; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + if (phase(i, j, k) > 0.f && M.Averages->SDs(i, j, k) > 0.f) + count += 1.f; + } + } + } + double volume_initial = M.Dm->Comm.sumReduce(count); + double PoreVolume = M.Dm->Volume * M.Dm->Porosity(); + /*ensure target isn't an absurdly small fraction of pore volume */ + if (volume_initial < target_delta_volume * PoreVolume) { + volume_initial = target_delta_volume * PoreVolume; + } + + // 2. Identify connected components of phase field -> phase_label + + double volume_connected = 0.0; + double second_biggest = 0.0; + if (USE_CONNECTED_NWP) { + ComputeGlobalBlobIDs(Nx - 2, Ny - 2, Nz - 2, rank_info, phase, + M.Averages->SDs, vF, vS, phase_label, M.Dm->Comm); + M.Dm->Comm.barrier(); + + // only operate on component "0"ScaLBL_ColorModel &M, + count = 0.0; + + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + int label = phase_label(i, j, k); + if (label == 0) { + phase_id(i, j, k) = 0; + count += 1.0; + } else + phase_id(i, j, k) = 1; + if (label == 1) { + second_biggest += 1.0; + } + } + } + } + volume_connected = M.Dm->Comm.sumReduce(count); + second_biggest = M.Dm->Comm.sumReduce(second_biggest); + } else { + // use the whole NWP + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + if (M.Averages->SDs(i, j, k) > 0.f) { + if (phase(i, j, k) > 0.f) { + phase_id(i, j, k) = 0; + } else { + phase_id(i, j, k) = 1; + } + } else { + phase_id(i, j, k) = 1; + } + } + } + } + } + + // 3. Generate a distance map to the largest object -> phase_distance + CalcDist(phase_distance, phase_id, *M.Dm); + + double temp, value; + double factor = 0.5 / M.beta; + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + if (phase_distance(i, j, k) < 3.f) { + value = phase(i, j, k); + if (value > 1.f) + value = 1.f; + if (value < -1.f) + value = -1.f; + // temp -- distance based on analytical form McClure, Prins et al, Comp. Phys. Comm. + temp = -factor * log((1.0 + value) / (1.0 - value)); + /// use this approximation close to the object + if (fabs(value) < 0.8 && M.Averages->SDs(i, j, k) > 1.f) { + phase_distance(i, j, k) = temp; + } + // erase the original object + phase(i, j, k) = -1.0; + } + } + } + } + + if (rank == 0) + printf("Pathway volume / next largest ganglion %f \n", + volume_connected / second_biggest); + + if (rank == 0) + printf("MorphGrow with target volume fraction change %f \n", + target_delta_volume / volume_initial); + double target_delta_volume_incremental = target_delta_volume; + if (fabs(target_delta_volume) > 0.01 * volume_initial) + target_delta_volume_incremental = 0.01 * volume_initial * + target_delta_volume / + fabs(target_delta_volume); + + delta_volume = + MorphGrow(M.Averages->SDs, phase_distance, phase_id, M.Averages->Dm, + target_delta_volume_incremental, WallFactor); + + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + if (phase_distance(i, j, k) < 0.0) + phase_id(i, j, k) = 0; + else + phase_id(i, j, k) = 1; + //if (phase_distance(i,j,k) < 0.0 ) phase(i,j,k) = 1.0; + } + } + } + + CalcDist(phase_distance, phase_id, *M.Dm); // re-calculate distance + + // 5. Update phase indicator field based on new distnace + for (int k = 0; k < Nz; k++) { + for (int j = 0; j < Ny; j++) { + for (int i = 0; i < Nx; i++) { + double d = phase_distance(i, j, k); + if (M.Averages->SDs(i, j, k) > 0.f) { + if (d < 3.f) { + //phase(i,j,k) = -1.0; + phase(i, j, k) = (2.f * (exp(-2.f * M.beta * d)) / + (1.f + exp(-2.f * M.beta * d)) - + 1.f); + } + } + } + } + } + fillDouble.fill(phase); + + count = 0.f; + for (int k = 1; k < Nz - 1; k++) { + for (int j = 1; j < Ny - 1; j++) { + for (int i = 1; i < Nx - 1; i++) { + if (phase(i, j, k) > 0.f && M.Averages->SDs(i, j, k) > 0.f) { + count += 1.f; + } + } + } + } + double volume_final = M.Dm->Comm.sumReduce(count); + + delta_volume = (volume_final - volume_initial); + if (rank == 0) + printf("Shell Aggregation: change fluid volume fraction by %f \n", + delta_volume / volume_initial); + if (rank == 0) + printf(" new saturation = %f \n", + volume_final / + (M.Mask->Porosity() * + double((Nx - 2) * (Ny - 2) * (Nz - 2) * M.nprocs))); + + // 6. copy back to the device + //if (rank==0) printf("MorphInit: copy data back to device\n"); + ScaLBL_CopyToDevice(M.Phi, phase.data(), N * sizeof(double)); + + // 7. Re-initialize phase field and density + ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, 0, + M.ScaLBL_Comm->LastExterior(), M.Np); + ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, + M.ScaLBL_Comm->FirstInterior(), + M.ScaLBL_Comm->LastInterior(), M.Np); + auto BoundaryCondition = M.BoundaryCondition; + if (BoundaryCondition == 1 || BoundaryCondition == 2 || + BoundaryCondition == 3 || BoundaryCondition == 4) { + if (M.Dm->kproc() == 0) { + ScaLBL_SetSlice_z(M.Phi, 1.0, Nx, Ny, Nz, 0); + ScaLBL_SetSlice_z(M.Phi, 1.0, Nx, Ny, Nz, 1); + ScaLBL_SetSlice_z(M.Phi, 1.0, Nx, Ny, Nz, 2); + } + if (M.Dm->kproc() == M.nprocz - 1) { + ScaLBL_SetSlice_z(M.Phi, -1.0, Nx, Ny, Nz, Nz - 1); + ScaLBL_SetSlice_z(M.Phi, -1.0, Nx, Ny, Nz, Nz - 2); + ScaLBL_SetSlice_z(M.Phi, -1.0, Nx, Ny, Nz, Nz - 3); + } + } + return delta_volume; +} + +double FlowAdaptor::SeedPhaseField(ScaLBL_ColorModel &M, + const double seed_water_in_oil) { + srand(time(NULL)); + auto rank = M.rank; + auto Np = M.Np; + double mass_loss = 0.f; + double count = 0.f; + double *Aq_tmp, *Bq_tmp; + + Aq_tmp = new double[7 * Np]; + Bq_tmp = new double[7 * Np]; + + ScaLBL_CopyToHost(Aq_tmp, M.Aq, 7 * Np * sizeof(double)); + ScaLBL_CopyToHost(Bq_tmp, M.Bq, 7 * Np * sizeof(double)); + + for (int n = 0; n < M.ScaLBL_Comm->LastExterior(); n++) { + double random_value = seed_water_in_oil * double(rand()) / RAND_MAX; + double dA = Aq_tmp[n] + Aq_tmp[n + Np] + Aq_tmp[n + 2 * Np] + + Aq_tmp[n + 3 * Np] + Aq_tmp[n + 4 * Np] + + Aq_tmp[n + 5 * Np] + Aq_tmp[n + 6 * Np]; + double dB = Bq_tmp[n] + Bq_tmp[n + Np] + Bq_tmp[n + 2 * Np] + + Bq_tmp[n + 3 * Np] + Bq_tmp[n + 4 * Np] + + Bq_tmp[n + 5 * Np] + Bq_tmp[n + 6 * Np]; + double phase_id = (dA - dB) / (dA + dB); + if (phase_id > 0.0) { + Aq_tmp[n] -= 0.3333333333333333 * random_value; + Aq_tmp[n + Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 2 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 3 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 4 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 5 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 6 * Np] -= 0.1111111111111111 * random_value; + + Bq_tmp[n] += 0.3333333333333333 * random_value; + Bq_tmp[n + Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 2 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 3 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 4 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 5 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 6 * Np] += 0.1111111111111111 * random_value; + } + mass_loss += random_value * seed_water_in_oil; + } + + for (int n = M.ScaLBL_Comm->FirstInterior(); + n < M.ScaLBL_Comm->LastInterior(); n++) { + double random_value = seed_water_in_oil * double(rand()) / RAND_MAX; + double dA = Aq_tmp[n] + Aq_tmp[n + Np] + Aq_tmp[n + 2 * Np] + + Aq_tmp[n + 3 * Np] + Aq_tmp[n + 4 * Np] + + Aq_tmp[n + 5 * Np] + Aq_tmp[n + 6 * Np]; + double dB = Bq_tmp[n] + Bq_tmp[n + Np] + Bq_tmp[n + 2 * Np] + + Bq_tmp[n + 3 * Np] + Bq_tmp[n + 4 * Np] + + Bq_tmp[n + 5 * Np] + Bq_tmp[n + 6 * Np]; + double phase_id = (dA - dB) / (dA + dB); + if (phase_id > 0.0) { + Aq_tmp[n] -= 0.3333333333333333 * random_value; + Aq_tmp[n + Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 2 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 3 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 4 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 5 * Np] -= 0.1111111111111111 * random_value; + Aq_tmp[n + 6 * Np] -= 0.1111111111111111 * random_value; + + Bq_tmp[n] += 0.3333333333333333 * random_value; + Bq_tmp[n + Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 2 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 3 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 4 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 5 * Np] += 0.1111111111111111 * random_value; + Bq_tmp[n + 6 * Np] += 0.1111111111111111 * random_value; + } + mass_loss += random_value * seed_water_in_oil; + } + + count = M.Dm->Comm.sumReduce(count); + mass_loss = M.Dm->Comm.sumReduce(mass_loss); + if (rank == 0) + printf("Remove mass %f from %f voxels \n", mass_loss, count); + + // Need to initialize Aq, Bq, Den, Phi directly + //ScaLBL_CopyToDevice(Phi,phase.data(),7*Np*sizeof(double)); + ScaLBL_CopyToDevice(M.Aq, Aq_tmp, 7 * Np * sizeof(double)); + ScaLBL_CopyToDevice(M.Bq, Bq_tmp, 7 * Np * sizeof(double)); + + delete Aq_tmp; + delete Bq_tmp; + + return (mass_loss); } From f0042bafea09c3be04ef4c107eb72e149088eae0 Mon Sep 17 00:00:00 2001 From: James McClure Date: Thu, 9 Dec 2021 14:02:32 -0500 Subject: [PATCH 53/54] update ubuntu sample script --- sample_scripts/configure_arden | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample_scripts/configure_arden b/sample_scripts/configure_arden index 3d0759aa..39bb2a03 100755 --- a/sample_scripts/configure_arden +++ b/sample_scripts/configure_arden @@ -19,5 +19,5 @@ cmake -D CMAKE_C_COMPILER:PATH=/opt/arden/openmpi/3.1.2/bin/mpicc \ -D NETCDF_DIRECTORY="/opt/arden/netcdf/4.6.1" \ -D USE_CUDA=0 \ -D USE_TIMER=0 \ - ~/Programs/LBPM-WIA + ~/Programs/LBPM From 44a7653c609fc57860614fa6696bd33306638593 Mon Sep 17 00:00:00 2001 From: James McClure Date: Fri, 10 Dec 2021 12:19:08 -0500 Subject: [PATCH 54/54] checkout right color model --- models/ColorModel.cpp | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/models/ColorModel.cpp b/models/ColorModel.cpp index 90f0f157..21315063 100644 --- a/models/ColorModel.cpp +++ b/models/ColorModel.cpp @@ -618,6 +618,7 @@ double ScaLBL_ColorModel::Run(int returntime) { bool SET_CAPILLARY_NUMBER = false; bool TRIGGER_FORCE_RESCALE = false; double tolerance = 0.01; + auto WettingConvention = color_db->getWithDefault( "WettingConvention", "none" ); auto current_db = db->cloneDatabase(); auto flow_db = db->getDatabase("FlowAdaptor"); int MIN_STEADY_TIMESTEPS = @@ -645,7 +646,7 @@ double ScaLBL_ColorModel::Run(int returntime) { if (analysis_db->keyExists("tolerance")) { tolerance = analysis_db->getScalar("tolerance"); } - + runAnalysis analysis(current_db, rank_info, ScaLBL_Comm, Dm, Np, Regular, Map); auto t1 = std::chrono::system_clock::now(); @@ -964,6 +965,41 @@ double ScaLBL_ColorModel::Run(int returntime) { fprintf(kr_log_file, "%.5g\n", eff_pres); fclose(kr_log_file); + if (WettingConvention == "SCAL"){ + WriteHeader = false; + FILE *scal_log_file = fopen("SCAL.csv", "r"); + if (scal_log_file != NULL) + fclose(scal_log_file); + else + WriteHeader = true; + scal_log_file = fopen("SCAL.csv", "a"); + if (WriteHeader) { + fprintf(scal_log_file, "timesteps sat.water "); + fprintf(scal_log_file, "eff.perm.oil.upper.bound " + "eff.perm.water.upper.bound "); + fprintf(scal_log_file, + "eff.perm.oil.lower.bound " + "eff.perm.water.lower.bound "); + fprintf(scal_log_file, "eff.perm.oil.disconnected " + "eff.perm.water.disconnected "); + fprintf(scal_log_file, + "cap.pressure cap.pressure.connected " + "Ca eff.pressure\n"); + } + fprintf(scal_log_file, "%i %.5g ", CURRENT_TIMESTEP, + current_saturation); + fprintf(scal_log_file, "%.5g %.5g ", kAeff_low, kBeff_low); + fprintf(scal_log_file, "%.5g %.5g ", kAeff_connected_low, + kBeff_connected_low); + fprintf(scal_log_file, "%.5g %.5g ", kAeff_disconnected, + kBeff_disconnected); + fprintf(scal_log_file, "%.5g %.5g %.5g ", pAB, + pAB_connected, Ca); + fprintf(scal_log_file, "%.5g\n", eff_pres); + fclose(scal_log_file); + + } + printf(" Measured capillary number %f \n ", Ca); } if (SET_CAPILLARY_NUMBER) {