tMerge pull request #5 from anders-dc/ns-cfd - sphere - GPU-based 3D discrete e… | |
git clone git://src.adamsgaard.dk/sphere | |
Log | |
Files | |
Refs | |
LICENSE | |
--- | |
commit bc41fb46fe0b5458ac8c6573cc573f5000076528 | |
parent ffb388df30a18fc5cd8b198a224fa6570e40fa40 | |
Author: Anders Damsgaard <[email protected]> | |
Date: Tue, 25 Mar 2014 14:17:04 +0100 | |
Merge pull request #5 from anders-dc/ns-cfd | |
Ns cfd merged into master | |
Diffstat: | |
A .gitignore | 59 +++++++++++++++++++++++++++++… | |
M README.rst | 125 +++++++++++------------------… | |
A conv-png.gp | 14 ++++++++++++++ | |
A conv-png.sh | 10 ++++++++++ | |
A conv.gp | 11 +++++++++++ | |
A conv.sh | 7 +++++++ | |
M doc/Makefile | 9 +++++++++ | |
M doc/html/.buildinfo | 2 +- | |
A doc/html/_images/math/0001d02b63ed… | 0 | |
A doc/html/_images/math/0027034d8a10… | 0 | |
A doc/html/_images/math/01be15303939… | 0 | |
A doc/html/_images/math/023a7668d083… | 0 | |
A doc/html/_images/math/038474380a07… | 0 | |
A doc/html/_images/math/048938dfaa2d… | 0 | |
A doc/html/_images/math/04e9e1fe54cb… | 0 | |
A doc/html/_images/math/07ff47f8ccf9… | 0 | |
A doc/html/_images/math/082bced08237… | 0 | |
A doc/html/_images/math/092e364e1d9d… | 0 | |
A doc/html/_images/math/0b557fe795a3… | 0 | |
A doc/html/_images/math/0dd32d7f3a00… | 0 | |
A doc/html/_images/math/0e0f0c7e69e0… | 0 | |
A doc/html/_images/math/0f06d8c03699… | 0 | |
A doc/html/_images/math/0f1c7bcfd657… | 0 | |
A doc/html/_images/math/0fdafcde7733… | 0 | |
A doc/html/_images/math/1053df745e2d… | 0 | |
A doc/html/_images/math/10e009bdb83f… | 0 | |
A doc/html/_images/math/11cfb4109b5e… | 0 | |
A doc/html/_images/math/13e4be57cf47… | 0 | |
A doc/html/_images/math/16ef95610462… | 0 | |
A doc/html/_images/math/17c2d34ded49… | 0 | |
A doc/html/_images/math/188c175aac0a… | 0 | |
A doc/html/_images/math/188c20d52c0d… | 0 | |
A doc/html/_images/math/18950ecc92ec… | 0 | |
A doc/html/_images/math/19ab21e013e1… | 0 | |
A doc/html/_images/math/19bc0073dde1… | 0 | |
A doc/html/_images/math/1aa208937d18… | 0 | |
A doc/html/_images/math/1beccd9a659c… | 0 | |
A doc/html/_images/math/1c7a111b8952… | 0 | |
A doc/html/_images/math/1cce9b2c774a… | 0 | |
A doc/html/_images/math/1d24d160d606… | 0 | |
A doc/html/_images/math/1dafb53fb664… | 0 | |
A doc/html/_images/math/1eb29f9de375… | 0 | |
A doc/html/_images/math/1ee5b5b2fe53… | 0 | |
A doc/html/_images/math/1ef7ab23d6fc… | 0 | |
A doc/html/_images/math/1f9530e95b98… | 0 | |
A doc/html/_images/math/213a272621ce… | 0 | |
A doc/html/_images/math/22b6d9dab340… | 0 | |
A doc/html/_images/math/249d76d0a5d3… | 0 | |
A doc/html/_images/math/24edc4190580… | 0 | |
A doc/html/_images/math/25586671e456… | 0 | |
A doc/html/_images/math/257ee6442468… | 0 | |
A doc/html/_images/math/259614407018… | 0 | |
A doc/html/_images/math/26eeb5258ca5… | 0 | |
A doc/html/_images/math/274ad002c530… | 0 | |
A doc/html/_images/math/2822d1531935… | 0 | |
A doc/html/_images/math/28d83ccd3281… | 0 | |
A doc/html/_images/math/29bbcc61ff8f… | 0 | |
A doc/html/_images/math/2ab5affb5906… | 0 | |
A doc/html/_images/math/2c175f60eece… | 0 | |
A doc/html/_images/math/2cb4dea80c94… | 0 | |
A doc/html/_images/math/2da5f268da80… | 0 | |
A doc/html/_images/math/2e399494a982… | 0 | |
A doc/html/_images/math/2e9b3638163a… | 0 | |
A doc/html/_images/math/2eb2a179a616… | 0 | |
A doc/html/_images/math/2ede365ad144… | 0 | |
A doc/html/_images/math/2feb7aaab806… | 0 | |
A doc/html/_images/math/30c34e7f1dd4… | 0 | |
A doc/html/_images/math/30d3be82eb9b… | 0 | |
A doc/html/_images/math/30d5d726338d… | 0 | |
A doc/html/_images/math/3118edbae1a8… | 0 | |
A doc/html/_images/math/32e2ba09618e… | 0 | |
A doc/html/_images/math/32e3fe27ac98… | 0 | |
A doc/html/_images/math/33deba163d5e… | 0 | |
A doc/html/_images/math/3426034f2cd7… | 0 | |
A doc/html/_images/math/34857b3ba74c… | 0 | |
A doc/html/_images/math/3536b174c0e2… | 0 | |
A doc/html/_images/math/35ff36083d20… | 0 | |
A doc/html/_images/math/3640b174f15a… | 0 | |
A doc/html/_images/math/36f73fc1312e… | 0 | |
A doc/html/_images/math/376d888029b6… | 0 | |
A doc/html/_images/math/377d34017898… | 0 | |
A doc/html/_images/math/37a9e7fca70e… | 0 | |
A doc/html/_images/math/37c89c22a072… | 0 | |
A doc/html/_images/math/38a6d1fc5607… | 0 | |
A doc/html/_images/math/3a94e291e910… | 0 | |
A doc/html/_images/math/3b432b0e58de… | 0 | |
A doc/html/_images/math/3b6d032ddaf9… | 0 | |
A doc/html/_images/math/3be873955a77… | 0 | |
A doc/html/_images/math/3c93d2ecf99a… | 0 | |
A doc/html/_images/math/3cbd1baf03f1… | 0 | |
A doc/html/_images/math/3cd61688849a… | 0 | |
A doc/html/_images/math/3d41adff4943… | 0 | |
A doc/html/_images/math/3eca8557203e… | 0 | |
A doc/html/_images/math/40075695a073… | 0 | |
A doc/html/_images/math/402b1a3c2564… | 0 | |
A doc/html/_images/math/41babe6999c8… | 0 | |
A doc/html/_images/math/42ea00932c36… | 0 | |
A doc/html/_images/math/43e42baf6cef… | 0 | |
A doc/html/_images/math/44fafcf5a158… | 0 | |
A doc/html/_images/math/458d3955e23e… | 0 | |
A doc/html/_images/math/45abe7dac102… | 0 | |
A doc/html/_images/math/46a84b389d8f… | 0 | |
A doc/html/_images/math/46bc9db8fc24… | 0 | |
A doc/html/_images/math/474638ec351b… | 0 | |
A doc/html/_images/math/47bc1263e44f… | 0 | |
A doc/html/_images/math/48431e4412db… | 0 | |
A doc/html/_images/math/486938d1819d… | 0 | |
A doc/html/_images/math/49a90589ae1e… | 0 | |
A doc/html/_images/math/4b1fcbd538ee… | 0 | |
A doc/html/_images/math/4d35bf3d410f… | 0 | |
A doc/html/_images/math/4d3f01b8ca8b… | 0 | |
A doc/html/_images/math/4f7d0530f193… | 0 | |
A doc/html/_images/math/507deb1ebb53… | 0 | |
A doc/html/_images/math/522c9c96bc63… | 0 | |
A doc/html/_images/math/552e279b5cf6… | 0 | |
A doc/html/_images/math/55f6b5380d46… | 0 | |
A doc/html/_images/math/571952df1029… | 0 | |
A doc/html/_images/math/587f41674d8c… | 0 | |
A doc/html/_images/math/5b4271afe7fc… | 0 | |
A doc/html/_images/math/5b46624e0dc3… | 0 | |
A doc/html/_images/math/5b7072008fcf… | 0 | |
A doc/html/_images/math/5c4e627793fb… | 0 | |
A doc/html/_images/math/5dfeda3ba68d… | 0 | |
A doc/html/_images/math/5ed37435199e… | 0 | |
A doc/html/_images/math/5fb7b1a39363… | 0 | |
A doc/html/_images/math/6089fb1f882f… | 0 | |
A doc/html/_images/math/6160e3f2050b… | 0 | |
A doc/html/_images/math/66a1d8738af5… | 0 | |
A doc/html/_images/math/68e0c54203bc… | 0 | |
A doc/html/_images/math/6922b3e4505e… | 0 | |
A doc/html/_images/math/697df1516660… | 0 | |
A doc/html/_images/math/69881326e514… | 0 | |
A doc/html/_images/math/69b1fdf87f9a… | 0 | |
A doc/html/_images/math/6adfb975053f… | 0 | |
A doc/html/_images/math/6b038d0d06f6… | 0 | |
A doc/html/_images/math/6ba1c18c095f… | 0 | |
A doc/html/_images/math/6d3199b492cb… | 0 | |
A doc/html/_images/math/6d9528f9b5d0… | 0 | |
A doc/html/_images/math/6d9a9e0ef32e… | 0 | |
A doc/html/_images/math/6e1feb8f41fb… | 0 | |
A doc/html/_images/math/6e62843666dc… | 0 | |
A doc/html/_images/math/6ed2751b928f… | 0 | |
A doc/html/_images/math/701df29c5482… | 0 | |
A doc/html/_images/math/70b8f6d66306… | 0 | |
A doc/html/_images/math/7181906596d0… | 0 | |
A doc/html/_images/math/73b4e0024361… | 0 | |
A doc/html/_images/math/74958bc18d01… | 0 | |
A doc/html/_images/math/74c081db590f… | 0 | |
A doc/html/_images/math/754decd65558… | 0 | |
A doc/html/_images/math/7572a9bb14a9… | 0 | |
A doc/html/_images/math/767762b2cd83… | 0 | |
A doc/html/_images/math/769bfdcb2a43… | 0 | |
A doc/html/_images/math/7727554a4dfa… | 0 | |
A doc/html/_images/math/7774004c6a33… | 0 | |
A doc/html/_images/math/778fcaedc9db… | 0 | |
A doc/html/_images/math/7851cf79108d… | 0 | |
A doc/html/_images/math/79c874e814b2… | 0 | |
A doc/html/_images/math/7ee03c7bfc1e… | 0 | |
A doc/html/_images/math/7fe490dcbc6c… | 0 | |
A doc/html/_images/math/80ba696b882e… | 0 | |
A doc/html/_images/math/8122aa89ea6e… | 0 | |
A doc/html/_images/math/8245434fa793… | 0 | |
A doc/html/_images/math/84bb71d071d3… | 0 | |
A doc/html/_images/math/852f6a447b7c… | 0 | |
A doc/html/_images/math/860ab918d3e4… | 0 | |
A doc/html/_images/math/88fd92ee9e46… | 0 | |
A doc/html/_images/math/89542f863347… | 0 | |
A doc/html/_images/math/8a2e0d0a28c9… | 0 | |
A doc/html/_images/math/8a89933fcdf0… | 0 | |
A doc/html/_images/math/8ad4d2540ea2… | 0 | |
A doc/html/_images/math/8bf1a4afd4cd… | 0 | |
M doc/html/_images/math/8c325612684d… | 0 | |
A doc/html/_images/math/8ce03f78ed94… | 0 | |
A doc/html/_images/math/8cfa62046b3a… | 0 | |
A doc/html/_images/math/8dbae01b96ab… | 0 | |
A doc/html/_images/math/8f28cef89dfa… | 0 | |
A doc/html/_images/math/900be94839e1… | 0 | |
A doc/html/_images/math/9035b87959dc… | 0 | |
A doc/html/_images/math/90c8bfc206db… | 0 | |
A doc/html/_images/math/910a79b832c4… | 0 | |
A doc/html/_images/math/93ddc61f81c0… | 0 | |
A doc/html/_images/math/945e8fc1c1e4… | 0 | |
A doc/html/_images/math/954afa1fc1c8… | 0 | |
A doc/html/_images/math/95b885fe932d… | 0 | |
A doc/html/_images/math/967116fa65cb… | 0 | |
A doc/html/_images/math/98a432864561… | 0 | |
A doc/html/_images/math/99b2691ddceb… | 0 | |
A doc/html/_images/math/9a0695dca61f… | 0 | |
A doc/html/_images/math/9c2a28916b33… | 0 | |
A doc/html/_images/math/9d0d9139c35e… | 0 | |
A doc/html/_images/math/9d3cd4785fd2… | 0 | |
A doc/html/_images/math/9d909a12ad66… | 0 | |
A doc/html/_images/math/9f60d76fad5e… | 0 | |
A doc/html/_images/math/a18e167e42f3… | 0 | |
A doc/html/_images/math/a1a25b9ae9dc… | 0 | |
A doc/html/_images/math/a1ffc0a01262… | 0 | |
A doc/html/_images/math/a31485573165… | 0 | |
M doc/html/_images/math/a32f407296c4… | 0 | |
A doc/html/_images/math/a581f053bbfa… | 0 | |
A doc/html/_images/math/a60e2d788aa8… | 0 | |
A doc/html/_images/math/a6c493a071c2… | 0 | |
A doc/html/_images/math/aad9832dd455… | 0 | |
A doc/html/_images/math/ab2209a9dd14… | 0 | |
A doc/html/_images/math/ac40b5914589… | 0 | |
A doc/html/_images/math/ac73c7c8c596… | 0 | |
A doc/html/_images/math/ad3d39eb3e45… | 0 | |
A doc/html/_images/math/b124ff74afb0… | 0 | |
A doc/html/_images/math/b143c2df6dd4… | 0 | |
A doc/html/_images/math/b2cd17a83bb8… | 0 | |
A doc/html/_images/math/b3cb421b6b32… | 0 | |
A doc/html/_images/math/b4104067dcc5… | 0 | |
A doc/html/_images/math/b4264aa4a69b… | 0 | |
A doc/html/_images/math/b4f82fd61e3f… | 0 | |
A doc/html/_images/math/b51a1f74e439… | 0 | |
A doc/html/_images/math/b531a865c773… | 0 | |
A doc/html/_images/math/b55687e1799d… | 0 | |
A doc/html/_images/math/b55ca7a0aa88… | 0 | |
A doc/html/_images/math/b588eea9cec4… | 0 | |
A doc/html/_images/math/b5e8dba2403c… | 0 | |
A doc/html/_images/math/b770d151d770… | 0 | |
A doc/html/_images/math/b7dd9f89843e… | 0 | |
A doc/html/_images/math/b95363838d60… | 0 | |
A doc/html/_images/math/baa542966f41… | 0 | |
A doc/html/_images/math/bb2c93730dbb… | 0 | |
A doc/html/_images/math/bdc15139139f… | 0 | |
A doc/html/_images/math/bf3f9bbcfd5e… | 0 | |
A doc/html/_images/math/bf7f4ceb6fce… | 0 | |
A doc/html/_images/math/bfdb965399e4… | 0 | |
A doc/html/_images/math/c15067d0b245… | 0 | |
A doc/html/_images/math/c305e13251e6… | 0 | |
A doc/html/_images/math/c323bf28746b… | 0 | |
A doc/html/_images/math/c42a32017c99… | 0 | |
A doc/html/_images/math/c4bb40dd65ea… | 0 | |
A doc/html/_images/math/c4f23b63471b… | 0 | |
A doc/html/_images/math/c51284f727b4… | 0 | |
A doc/html/_images/math/c5446f5064cc… | 0 | |
A doc/html/_images/math/c5671cc4a4d9… | 0 | |
A doc/html/_images/math/c65f07031d99… | 0 | |
A doc/html/_images/math/c6e0b5935589… | 0 | |
A doc/html/_images/math/c78863bcf0ad… | 0 | |
A doc/html/_images/math/c8094929c952… | 0 | |
A doc/html/_images/math/c8e734bf3818… | 0 | |
A doc/html/_images/math/c9264cc70365… | 0 | |
A doc/html/_images/math/cb8fa4f65175… | 0 | |
A doc/html/_images/math/cbac43e2c13c… | 0 | |
A doc/html/_images/math/cbe3f817ae97… | 0 | |
A doc/html/_images/math/cd19c19fc5bc… | 0 | |
A doc/html/_images/math/ce4f41d90f3f… | 0 | |
A doc/html/_images/math/cf699492db92… | 0 | |
A doc/html/_images/math/cfe0e96f0049… | 0 | |
A doc/html/_images/math/d0b4b390a480… | 0 | |
A doc/html/_images/math/d18b93994f64… | 0 | |
A doc/html/_images/math/d1c6b35072ca… | 0 | |
A doc/html/_images/math/d1e970327c74… | 0 | |
A doc/html/_images/math/d32c78b75990… | 0 | |
A doc/html/_images/math/d34109b1b68b… | 0 | |
A doc/html/_images/math/d3b6fc67523d… | 0 | |
A doc/html/_images/math/d4ba00bbbae4… | 0 | |
A doc/html/_images/math/d5a65ff91df0… | 0 | |
A doc/html/_images/math/d5ba56a40217… | 0 | |
A doc/html/_images/math/d6a7ccf879c4… | 0 | |
A doc/html/_images/math/d86c22303458… | 0 | |
A doc/html/_images/math/da419aab4b72… | 0 | |
A doc/html/_images/math/db94af9f89e3… | 0 | |
A doc/html/_images/math/dbcdbe7c53fa… | 0 | |
A doc/html/_images/math/dcf5762fe2b4… | 0 | |
A doc/html/_images/math/dd84cb843a3b… | 0 | |
A doc/html/_images/math/de27eb4f973d… | 0 | |
A doc/html/_images/math/dfa75235bbe9… | 0 | |
A doc/html/_images/math/dfc85915a4cd… | 0 | |
A doc/html/_images/math/dfebf8b683f7… | 0 | |
A doc/html/_images/math/e12eccdaacf4… | 0 | |
A doc/html/_images/math/e29be0a3b865… | 0 | |
A doc/html/_images/math/e31584cdce0b… | 0 | |
A doc/html/_images/math/e35ec9f12692… | 0 | |
A doc/html/_images/math/e3c6f9fd144c… | 0 | |
A doc/html/_images/math/e3de9b8fce0b… | 0 | |
A doc/html/_images/math/e4ca401f97df… | 0 | |
A doc/html/_images/math/e4cd0999828f… | 0 | |
A doc/html/_images/math/e4cd5b31b960… | 0 | |
A doc/html/_images/math/e5101484e2ac… | 0 | |
A doc/html/_images/math/e6eadc1093d6… | 0 | |
A doc/html/_images/math/e8522a3d29a2… | 0 | |
A doc/html/_images/math/e9203da50e10… | 0 | |
A doc/html/_images/math/e9e9403195bb… | 0 | |
A doc/html/_images/math/eaf4418fbe93… | 0 | |
A doc/html/_images/math/eb1910746ff1… | 0 | |
A doc/html/_images/math/ec1ce56da064… | 0 | |
A doc/html/_images/math/edd52ffa94a4… | 0 | |
A doc/html/_images/math/ee1817a60aae… | 0 | |
A doc/html/_images/math/eeae8ea2feac… | 0 | |
A doc/html/_images/math/eee8ed92791a… | 0 | |
A doc/html/_images/math/ef18200849af… | 0 | |
A doc/html/_images/math/f04823ba99f1… | 0 | |
M doc/html/_images/math/f5047d1e0cbb… | 0 | |
A doc/html/_images/math/f574498915fa… | 0 | |
A doc/html/_images/math/f5c280d551f6… | 0 | |
A doc/html/_images/math/f5d499efbd23… | 0 | |
A doc/html/_images/math/f8f0dc31c5c1… | 0 | |
A doc/html/_images/math/f9147d34f15a… | 0 | |
A doc/html/_images/math/f915ce7b55e8… | 0 | |
A doc/html/_images/math/f99450c7a885… | 0 | |
A doc/html/_images/math/fa8a7b2fb896… | 0 | |
A doc/html/_images/math/fc64cd1ec99f… | 0 | |
A doc/html/_images/math/fca2f0caf945… | 0 | |
A doc/html/_images/math/fd26dda3f4f5… | 0 | |
A doc/html/_images/math/fdb63b9e51ab… | 0 | |
A doc/html/_images/math/ff9078c25826… | 0 | |
A doc/html/_sources/cfd.txt | 578 +++++++++++++++++++++++++++++… | |
M doc/html/_sources/dem.txt | 223 +++++++++++++++++++++++++++++… | |
M doc/html/_sources/index.txt | 34 ++++++++++++++++-------------… | |
M doc/html/_sources/introduction.txt | 137 +++++++++++++++++++++++++----… | |
M doc/html/_sources/python_api.txt | 22 ++++++++++++++++++++++ | |
M doc/html/_sources/sphere_internals… | 2 -- | |
M doc/html/_static/jquery.js | 2 +- | |
M doc/html/_static/pygments.css | 4 ++-- | |
M doc/html/_static/searchtools.js | 33 +++++++++++++++++++----------… | |
M doc/html/_static/underscore.js | 959 ++++++++++++++++++++++-------… | |
A doc/html/cfd.html | 601 +++++++++++++++++++++++++++++… | |
M doc/html/dem.html | 205 +++++++++++++++++++++++++++--… | |
M doc/html/genindex.html | 238 +++++++++++++++++++++++++----… | |
M doc/html/index.html | 71 ++++++++++++++++++++---------… | |
M doc/html/introduction.html | 164 ++++++++++++++++++++++-------… | |
M doc/html/objects.inv | 0 | |
M doc/html/py-modindex.html | 12 ++++++------ | |
M doc/html/python_api.html | 1899 +++++++++++++++++++++++++++--… | |
M doc/html/search.html | 14 ++++++++------ | |
M doc/html/searchindex.js | 4 ++-- | |
M doc/html/sphere_internals.html | 104 ++++++++++++++++++++++-------… | |
M doc/pdf/sphere.pdf | 0 | |
A doc/sphinx/cfd.rst | 578 +++++++++++++++++++++++++++++… | |
M doc/sphinx/conf.py | 6 +++--- | |
M doc/sphinx/dem.rst | 223 +++++++++++++++++++++++++++++… | |
M doc/sphinx/index.rst | 34 ++++++++++++++++-------------… | |
M doc/sphinx/introduction.rst | 137 +++++++++++++++++++++++++----… | |
M doc/sphinx/python_api.rst | 22 ++++++++++++++++++++++ | |
M doc/sphinx/sphere_internals.rst | 2 -- | |
M python/collapse.py | 10 +++------- | |
M python/collision.py | 17 ++++++++--------- | |
D python/darcy.py | 87 -----------------------------… | |
A python/ns_cons_of_mass.py | 203 +++++++++++++++++++++++++++++… | |
A python/ns_cons_of_mass_run.sh | 4 ++++ | |
M python/segregation.py | 20 +++++++------------- | |
M python/shear-test.py | 132 ++++++++++++++++-------------… | |
M python/sphere.py | 3098 +++++++++++++++++++++++------… | |
M python/uniaxial.py | 12 ++++-------- | |
M src/CMakeLists.txt | 11 +++++++---- | |
M src/constants.h | 2 +- | |
M src/contactmodels.cuh | 74 ++++++++++++++++++++---------… | |
M src/contactsearch.cuh | 18 ++++++++++-------- | |
D src/darcy.cpp | 838 -----------------------------… | |
D src/darcy.cuh | 783 ------------------------------ | |
M src/datatypes.h | 147 +++++++++++++++++------------… | |
M src/debug.h | 23 +++++++++++++++++++++++ | |
M src/device.cu | 1244 ++++++++++++++++++++---------… | |
M src/file_io.cpp | 143 +++++++++++++++++------------… | |
M src/forcechains.cpp | 41 ++++++++++++++---------------… | |
M src/integration.cuh | 424 ++++++++++++++++++-----------… | |
D src/latticeboltzmann.cuh | 762 -----------------------------… | |
M src/main.cpp | 77 +++++++++++++++++------------… | |
A src/navierstokes.cpp | 349 +++++++++++++++++++++++++++++… | |
A src/navierstokes.cuh | 2317 +++++++++++++++++++++++++++++… | |
A src/navierstokes_solver_parameters… | 46 +++++++++++++++++++++++++++… | |
M src/porosity.cpp | 34 ++++++++++++-----------------… | |
M src/porousflow.cpp | 3 +++ | |
M src/sorting.cuh | 32 ++++++++++++++++++++---------… | |
M src/sphere.cpp | 90 ++++++++++++++++++++++-------… | |
M src/sphere.h | 292 ++++++++++++++++-------------… | |
M src/utility.cu | 47 +++++++++++++++++++++++++----… | |
D src/utility.cuh | 10 ---------- | |
M tests/CMakeLists.txt | 9 +++++++++ | |
M tests/bond_tests.py | 14 ++++---------- | |
A tests/cfd_tests.py | 222 ++++++++++++++++++++++++++++++ | |
A tests/cfd_tests_neumann.py | 65 +++++++++++++++++++++++++++++… | |
A tests/contactmodel_wall.py | 107 +++++++++++++++++++++++++++++… | |
A tests/dem_cfd_tests.py | 50 +++++++++++++++++++++++++++++… | |
M tests/io_tests.py | 15 ++++++++++----- | |
M tests/io_tests_fluid.py | 19 +++++++++++-------- | |
M tests/memcheck_tests.py | 6 +++--- | |
M tests/porosity_tests.py | 15 ++++++++------- | |
M tests/pytestutils.py | 47 +++++++++++++++++++----------… | |
T tests/sphere.py | 0 | |
M wc.sh | 2 +- | |
383 files changed, 13187 insertions(+), 5319 deletions(-) | |
--- | |
diff --git a/.gitignore b/.gitignore | |
t@@ -0,0 +1,59 @@ | |
+# Compiled source | |
+sphere | |
+porosity | |
+porousflow | |
+forcechains | |
+*.o | |
+ | |
+# Temporary editor files | |
+*.swp | |
+*.swo | |
+ | |
+# Python files | |
+*.pyc | |
+ | |
+# Cmake files | |
+Makefile | |
+*.cmake | |
+*.o.depend | |
+*.o.cmake | |
+ | |
+# Documentation temporary files | |
+*.ist | |
+*.aux | |
+*.idx | |
+*.ilg | |
+*.ind | |
+*.tex | |
+*.out | |
+*.log | |
+*.toc | |
+*.sty | |
+*.cls | |
+#*.png | |
+#*.txt | |
+#*.js | |
+#*.html | |
+#*.inv | |
+#*.css | |
+ | |
+# Rights prohibit inclusion of NVIDIA header file | |
+helper_math.h | |
+ | |
+# Sphere IO | |
+*.bin | |
+*.dat | |
+ | |
+# Folders | |
+output/ | |
+CMakeFiles | |
+doc/doctrees | |
+doc/doxygen/man | |
+doc/doxygen/xml | |
+#doc/html | |
+src/CMakeFiles | |
+src/forcechains.dir | |
+src/porosity.dir | |
+src/porousflow.dir | |
+src/sphere.dir | |
+tests/CMakeFiles | |
diff --git a/README.rst b/README.rst | |
t@@ -1,84 +1,45 @@ | |
============= | |
-SPHERE readme | |
+sphere readme | |
============= | |
-Sphere is a 3D discrete element method algorithm utilizing CUDA. | |
- | |
-Sphere is licensed under the GNU General Public License, v.3. | |
-See license.txt for more information. | |
- | |
-See the ``doc/`` folder for general reference, by default available in the `ht… | |
-<doc/html/index.html>`_ and `pdf <doc/pdf/sphere.pdf>`_ formats. | |
- | |
-**Update** (2013-03-13): Sphere has been updated to work with CUDA 5.0 or newer | |
-*only*. | |
- | |
-Requirements | |
------------- | |
-The build requirements are: | |
- * A Nvidia CUDA-supported version of Linux or Mac OS X (see the `CUDA toolki… | |
- release notes <http://docs.nvidia.com/cuda/cuda-toolkit-release-notes/inde… | |
- for more information) | |
- * `CMake <http://cmake.org>`_, version 2.8 or higher | |
- * A C/C++ compiler toolkit, e.g. the `GNU Compiler Collection | |
- <http://gcc.gnu.org/>`_ (GCC) | |
- * The `Nvidia CUDA toolkit and samples <https://developer.nvidia.com/cuda-do… | |
- | |
-The runtime requirements are: | |
- * A `CUDA-enabled GPU <http://www.nvidia.com/object/cuda_gpus.html>`_ | |
- with compute capability 2.0 or greater. | |
- * A Nvidia CUDA-enabled GPU and device driver | |
- | |
-Optional tools, required/useful for simulation setup and data processing: | |
- * `Python 2.7 <http://www.python.org/getit/releases/2.7/>`_ | |
- * `Numpy <http://numpy.scipy.org>`_ | |
- * `Matplotlib <http://matplotlib.org>`_ | |
- * `Imagemagick <http://www.imagemagick.org/script/index.php>`_ | |
- * `ffmpeg <http://ffmpeg.org/>`_ | |
- * `Paraview <http://www.paraview.org>`_ | |
- * `Python-VTK <http://www.vtk.org>`_ | |
- | |
-Optional tools, required for building the documentation: | |
- * `Sphinx <http://sphinx-doc.org>`_ | |
- * `Doxygen <http://www.stack.nl/~dimitri/doxygen/>`_ | |
- * `Breathe <http://michaeljones.github.com/breathe/>`_ | |
- | |
-Obtaining sphere | |
----------------- | |
-The best way to keep up to date with subsequent updates, bugfixes and | |
-development, is to use the Git version control system. To obtain a local | |
-copy, execute:: | |
- git clone https://github.com/anders-dc/sphere.git | |
- | |
-Build instructions | |
------------------- | |
-Sphere is built using `cmake`, the platform-specific c/c++ compilers, | |
-and `nvcc` from the cuda toolkit. | |
- | |
-If you plan to run sphere on a Kepler GPU, execute the following commands from | |
-the root directory:: | |
- cmake . && make | |
- | |
-If you instead plan to execute it o a Fermi GPU, change ``set(GPU_GENERATION | |
-1)`` to ``set(GPU_GENERATION 0`` in `CMakeLists.txt`. | |
- | |
-In some cases the CMake FindCUDA module will have troubles locating the | |
-CUDA samples directory, and will complain about `helper_math.h` not being | |
-found. | |
- | |
-In that case, modify the ``CUDA_SDK_ROOT_DIR`` variable in `src/CMakeLists.txt` | |
-to the path where you installed the CUDA samples, and run ``cmake . && make`` | |
-again. Alternatively, copy `helper_math.h` from the CUDA sample subdirectory | |
-`common/inc/helper_math.h` into the sphere `src/` directory, and run `cmake` | |
-and `make` again. Due to license restrictions, sphere cannot be distributed | |
-with this file. | |
- | |
-After a successfull installation, the `sphere` executable will be located | |
-in the root folder. To make sure that all components are working correctly, | |
-execute:: | |
- make test | |
- | |
-Updating sphere | |
---------------- | |
-To update your local version, type the following commands in the sphere root | |
-directory:: | |
- git pull && cmake . && make | |
+``sphere`` is a 3D discrete element method algorithm utilizing CUDA. | |
+ | |
+License | |
+------- | |
+``sphere`` is licensed under the GNU General Public License, v.3. | |
+See `LICENSE.txt <LICENSE.txt>`_ for more information. | |
+ | |
+About this branch | |
+----------------- | |
+The git branch ``ns-cfd`` is a work in progress on implementing a Newtonian | |
+fluid, fully coupled to the particle dynamics, by the Navier Stokes formulation | |
+of computational fluid dynamics. The merge into the main branch is planned to | |
+mark the 1.0 release of ``sphere``. | |
+ | |
+Important release notes | |
+----------------------- | |
+2013-03-13: Sphere has been updated to work with CUDA 5.0 or newer *only*. | |
+ | |
+2014-01-20: Version fingerprints have been added to the input/output binary | |
+files, and causes old files to be incompatible with either ``sphere`` or | |
+``sphere.py``. | |
+ | |
+2014-01-25: The description of the installation procedure is moved to the | |
+general documentation. | |
+ | |
+2014-03-09: The main sphere class (formerly ``spherebin``) has been renamed to | |
+``sim``. | |
+ | |
+2014-03-09: The ``writebin`` member function of the ``sim`` class is now | |
+implicitly called when calling the ``run`` member function. | |
+ | |
+Documentation | |
+------------- | |
+See the separate documentation for general reference and installation | |
+instructions. The documentation is by default available in | |
+the `html <doc/html/index.html>`_ and `pdf <doc/pdf/sphere.pdf>`_ formats. | |
+ | |
+Author | |
+------ | |
+Anders Damsgaard, `[email protected] <mailto:[email protected].… | |
+`blog <http://anders-dc.github.io>`_, | |
+`more contact information <https://cs.au.dk/~adc>`_. | |
diff --git a/conv-png.gp b/conv-png.gp | |
t@@ -0,0 +1,14 @@ | |
+#!/usr/bin/env gnuplot | |
+ | |
+#set terminal pngcairo | |
+set terminal pngcairo background "#002b36" | |
+set border linecolor rgbcolor "#657b83" | |
+set key textcolor rgbcolor "#657b83" | |
+set out 'output/'.sid.'-conv.png' | |
+ | |
+set title "Convergence evolution in CFD solver (".sid.")" textcolor rgb "#93a1… | |
+set xlabel "Time step" textcolor rgb "#657b83" | |
+set ylabel "Jacobi iterations" textcolor rgb "#657b83" | |
+set grid | |
+ | |
+plot 'output/'.sid.'-conv.log' with linespoints notitle linecolor rgb "#657b83" | |
diff --git a/conv-png.sh b/conv-png.sh | |
t@@ -0,0 +1,10 @@ | |
+#!/bin/bash | |
+INTERVAL=10 | |
+INTERVALDIV2=`expr $INTERVAL / 2` | |
+if [[ "$1" == "" ]]; then | |
+ echo "Usage: $0 <simulation id>" | |
+ exit 1 | |
+else | |
+ feh --reload $INTERVALDIV2 --auto-zoom --image-bg black --scale-down outpu… | |
+ watch --interval $INTERVAL --no-title gnuplot -e \"sid=\'$1\'\" conv-png.gp | |
+fi | |
diff --git a/conv.gp b/conv.gp | |
t@@ -0,0 +1,11 @@ | |
+#!/usr/bin/env gnuplot | |
+ | |
+set terminal dumb | |
+#set terminal pngcairo | |
+#set out 'output/'.sid.'-conv.png' | |
+ | |
+set title "Convergence evolution in CFD solver (".sid.")" | |
+set xlabel "Time step" | |
+set ylabel "Jacobi iterations" | |
+ | |
+plot 'output/'.sid.'-conv.log' with linespoints notitle | |
diff --git a/conv.sh b/conv.sh | |
t@@ -0,0 +1,7 @@ | |
+#!/bin/bash | |
+if [[ "$1" == "" ]]; then | |
+ echo "Usage: $0 <simulation id>" | |
+ exit 1 | |
+else | |
+ watch --interval 5 --no-title gnuplot -e \"sid=\'$1\'\" conv.gp | |
+fi | |
diff --git a/doc/Makefile b/doc/Makefile | |
t@@ -1,2 +1,11 @@ | |
doc: | |
$(MAKE) -C sphinx/ | |
+ | |
+install-debian-pkgs: | |
+ sudo apt-get update && \ | |
+ sudo apt-get install build-essential cmake \ | |
+ python python-numpy ipython python-matplotlib python-vtk \ | |
+ imagemagick libav-tools \ | |
+ python-sphinx python-pip doxygen dvipng \ | |
+ python-sphinxcontrib-programoutput texlive-full && \ | |
+ sudo pip install breathe | |
diff --git a/doc/html/.buildinfo b/doc/html/.buildinfo | |
t@@ -1,4 +1,4 @@ | |
# Sphinx build info version 1 | |
# This file hashes the configuration used when building these files. When it i… | |
-config: c1c1c2f2bea01e0223aaea2391f75c23 | |
+config: 572cfe56c7acc603f39739a18d514093 | |
tags: fbb0d17656682115ca4d033fb2f83ba1 | |
diff --git a/doc/html/_images/math/0001d02b63ede2fe3219e05a7cd09c82ae6298b6.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/0027034d8a10372a06deaf4f4084c01956587479.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/01be15303939c78ee40a848a717482d046a9ad48.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/023a7668d083165ce2aa65b49e876b83c9cb97f9.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/038474380a078000f31889a32a1dcf79ac38c223.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/048938dfaa2dd837058556eec573df345d984e62.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/04e9e1fe54cb1328e049a45b0acb8c3ccb0ab888.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/07ff47f8ccf9ac490e023817a9241de75c30340c.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/082bced08237667a772227c2e3eb8359ff9d4af8.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/092e364e1d9d19ad5fffb0b46ef4cc7f2da02c1c.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/0b557fe795a3a7d8b91ff1360e5610ffd6f1445f.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/0dd32d7f3a000398f4982bdbe7cb1da880e33298.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/0e0f0c7e69e0f315aed6d1762a66bf27dd9a155a.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/0f06d8c03699aa966cd6c94d11850ede3c97dc41.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/0f1c7bcfd65708b716b8817c2e31029f9af2eedf.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/0fdafcde7733804669f8df4846e5bfdb36c7afa7.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/1053df745e2d73fc7ee27d39a01ad228c360072e.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/10e009bdb83f96c5f47c58b34d5d4b12ef268d5b.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/11cfb4109b5e8ad4a3bc8b07fe844e437d139b46.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/13e4be57cf47de5302fc2e1746d965a6ee0f332c.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/16ef95610462b9c2c03cba4c272fe84208bf3c61.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/17c2d34ded49c3a5cca11a4f1f20d5fe7117f964.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/188c175aac0a8a9c22499336711b5d7256407254.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/188c20d52c0d5acf0ddac670950204c93791be45.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/18950ecc92ecc7397337b5f39323453647e6434a.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/19ab21e013e12d69147e0e08c69dc7acd69d1977.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/19bc0073dde1bcd1a8e6a32b251e80cced668f04.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/1aa208937d18ff999c2b86535c86f4dd4705c3a4.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/1beccd9a659ce57a6a8083a589fe91ad33fa3b06.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/1c7a111b8952b4162797cce2cbd426d74083b834.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/1cce9b2c774a84cb5198a2f138beddb46e6658b5.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/1d24d160d60604202abbae47aba869e96fb450aa.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/1dafb53fb664c1e6576fc49adc4ef6db8f31a789.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/1eb29f9de3753a59530941141fcb5c7aa3fa2e38.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/1ee5b5b2fe535711338cab9fde51ba86968186e2.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/1ef7ab23d6fc7d336379d457f66aa6ed57d72ec9.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/1f9530e95b98c17e8dd3ed5756dd58c4ca46d46f.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/213a272621ce146fc9f90ce519bc3dc1ac3337e8.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/22b6d9dab340bfe13993da20589e4720449fe391.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/249d76d0a5d3773b138ce498e5df46c870f402b5.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/24edc419058015022fc9ef15fb0f5d8ec3109f73.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/25586671e456c2d987cd75cefd64d9cee69f50b9.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/257ee6442468d7529f036a8a81e6050c3832fdbf.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/25961440701892ff5417a2474f7f52bb353e5d98.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/26eeb5258ca5099acf8fe96b2a1049c48c89a5e6.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/274ad002c5303a0a45fb61cc2949cfd0e65402cd.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/2822d1531935f7a072ebaa08533f4c5695731465.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/28d83ccd32811c0455355169ed8f6978d8d24dbe.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/29bbcc61ff8f993f8eca2f33c8faf0c7ad80defc.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/2ab5affb59066624277032c5076d7227db701fd9.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/2c175f60eecef1de7560c3bdea495d69f26f719d.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/2cb4dea80c947c94642b08f6a92ad5aa939dc9f3.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/2da5f268da80c66e332f2a8d4520f7d6685699b6.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/2e399494a98253eb4f7d0cfb51994059389bc825.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/2e9b3638163af9fc450acac1aba9911e57a5c691.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/2eb2a179a6165db84b51c4dc8066b31cd012594f.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/2ede365ad144ab396916ec60458da03860803078.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/2feb7aaab8068a8c48febc6a15a008f5dfcea9f7.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/30c34e7f1dd419d28236bdff05af10a01cdd635c.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/30d3be82eb9bf820400a3dc3e46e84fe58e9f2c9.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/30d5d726338d5b3ac9d9b0003c564594fd6176ce.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/3118edbae1a8f9f568f293112ccb85a253fb3608.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/32e2ba09618e2d303d91d673a25ef66e29e94750.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/32e3fe27ac9815b323560775c3f6fcb2b6111edc.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/33deba163d5e5027dea810eec538e06a1683ddf1.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/3426034f2cd72ab441922edec729ae5d5212f047.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/34857b3ba74ce5cd8607f3ebd23e9015908ada71.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/3536b174c0e21a837eb33434bbcdfc2ab566d161.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/35ff36083d208f671a3ed540e1b95395aa2ae10c.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/3640b174f15a44d33ad8f0622bc44dce8d8f1692.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/36f73fc1312ee0349b3f3a0f3bd9eb5504339011.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/376d888029b69634b51a843c71cf75ba713b4821.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/377d34017898a24f5ba5ee9eba0c1d4d8834d456.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/37a9e7fca70e2dce829d902af2088735306bc1a3.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/37c89c22a0720d39bbe2dbbc72749255662437dc.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/38a6d1fc56073806115322bcd179578f05c88c02.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/3a94e291e91082dc48a18009bdd3fd70ebedec76.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/3b432b0e58dea7c6cc143bf67184fc19b8cd4336.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/3b6d032ddaf923f920fc753c9259dfc5cb626837.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/3be873955a777b3d292f2477e3cc7ba40d1e7897.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/3c93d2ecf99a3c5ee161c98a3ac0040e1b446e09.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/3cbd1baf03f10cad8678746742cebc8176bdb593.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/3cd61688849a22b05ca6c56c937561798da99ef8.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/3d41adff4943d60236fa626d0bae8e238483278a.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/3eca8557203e86160952e1c0f735f7417f3285b1.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/40075695a073185f493a613d8dc7560042b2ae57.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/402b1a3c25643899cdefbe2e62d33eab43b4f616.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/41babe6999c89406f22db1ce5ed5e05ce939ed49.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/42ea00932c36940fb8965e6e7f4fb2de5d616ee7.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/43e42baf6cef7e9620082be300b7622d524b3a65.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/44fafcf5a158459730d0dd7c293b93cdcf62f0a4.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/458d3955e23ef7ba378c27f284a2e426f756bd3e.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/45abe7dac102080bf806c2bb40d20c9dab2e4fcb.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/46a84b389d8f594cd16c800ba4267f3cac5e2f30.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/46bc9db8fc244ad2bbb5de57bfd7e8a595298471.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/474638ec351b4bfa321564aa3ea75c6a6b16b24c.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/47bc1263e44f1b74fd90da02d9e7c26bf4b9edbe.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/48431e4412db6b2ffa7904f013daf5c0d6c61949.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/486938d1819df5972cb80ef2d101517b20412366.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/49a90589ae1e8a839b1ae6afb3dd1d162b6a094c.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/4b1fcbd538ee9de974b2fbde2b253dfe843ab0e7.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/4d35bf3d410f89d028f8652665f0950344b89f41.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/4d3f01b8ca8bdab3543eda695ff7e565bcb66381.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/4f7d0530f1939c40a7c9e65b7ecee0e476ac4817.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/507deb1ebb53483473057e3bcb372d5c8553148b.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/522c9c96bc63142fa2b83abe960622dcafd75875.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/552e279b5cf6a34bb7787589bb4d4e67df465e7d.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/55f6b5380d462f954b65d7a2cd05e02ff8cc832e.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/571952df10295a1251ad68112796e94f3fc54598.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/587f41674d8c1e5f151655cc5567c7f12cfea34a.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/5b4271afe7fc7c0ee84172e0ad19b82caf450c00.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/5b46624e0dc3d79b64f388898e2dff17d232656c.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/5b7072008fcf4dedaeed4d537fe5c808adedb826.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/5c4e627793fb72f14a14540abaf5b0789767498b.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/5dfeda3ba68db3f629c37ad0b4cc75095c2d6570.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/5ed37435199e280338e0e6dc0c225e4230719b0a.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/5fb7b1a39363304fc1fb9172ecd46e2f39274384.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/6089fb1f882f20c5b7ccbf543244d38b88c99494.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/6160e3f2050b4cd56b336ff552386a169aa51db8.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/66a1d8738af515286414e18d76a6e7f86b989afc.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/68e0c54203bc81b45ebe5dbf7d89fc61aa794c1e.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/6922b3e4505e825344dc326ade5f0c4be45ddd2f.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/697df15166603b27c129b6191e2c96c022ea7932.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/69881326e5149864bc6319edea6786188e8d9a94.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/69b1fdf87f9a78aaef8057a34aea7a6c17dad726.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/6adfb975053fd90151158bd3317e9b2a04f20d14.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/6b038d0d06f6fa38bcb85df9e6f8bed4155ded47.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/6ba1c18c095ff64c7d7b9eaa38630f7ae5dd31e3.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/6d3199b492cb754c4cc895caf129a6e9c0f82f44.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/6d9528f9b5d0a2feda7dca87d5d1e8523d7cc4b1.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/6d9a9e0ef32ea9506bb55413b682112d0f3308a6.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/6e1feb8f41fb33ffbf60f657c5e095c2bb780e4d.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/6e62843666dcc0fe9a45a1c69c532c82a95a9451.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/6ed2751b928fdd78923983eb490cb347a1eac8b8.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/701df29c5482cdf1e577052c537c176f5a12caf5.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/70b8f6d6630631ad7de5c587e511247f1ce2afe9.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/7181906596d0aaac14cbde44ec57dd7e5866c81b.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/73b4e0024361fb95f692baa527cca76a384fdae0.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/74958bc18d017328dd0fac99816d2184296bfd3d.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/74c081db590f3d35421c1f6b9afd4cdda36ee210.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/754decd655584a824e0979fc2df27ae24379558e.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/7572a9bb14a95da7f7b42fd4b5bec33c42800556.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/767762b2cd832672331c1421813b919298d7e3b8.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/769bfdcb2a43bde2cd368d82a6f64bd68c876c99.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/7727554a4dfa1572695dec332d6f6ee255815c96.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/7774004c6a3397305cc7616b705accbbbfb28632.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/778fcaedc9db6a87be6c2b7feac7164e55090c5b.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/7851cf79108da000ebe09e52f7a34a7fa2092517.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/79c874e814b2550448c61e2627dc072653b68197.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/7ee03c7bfc1e46255c9d2d47d9b733a068c9ec2b.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/7fe490dcbc6c9eb978f05b3334d2a5856dc66149.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/80ba696b882e52c7e4d833039710ff8b4005e777.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/8122aa89ea6e80784c6513d22787ad86e36ad0cc.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/8245434fa7935d53a0382eb3cc425ce09de36913.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/84bb71d071d3747a4a76292f75c6bb6c2ac15243.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/852f6a447b7ccf231f8e43dbcce1ae99280f8020.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/860ab918d3e4c8f25b17d691cca049073dab88a0.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/88fd92ee9e463bf55236c5ffe73c46480533b1dd.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/89542f86334781895f1f8146368f4a6a3fa58986.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/8a2e0d0a28c9ebec91bd12def7b3b36dd9e92da7.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/8a89933fcdf0cde2a59b18d267abc2ce124940d0.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/8ad4d2540ea2fe0c871b3eb896b64f22018d9bdf.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/8bf1a4afd4cdf42bf3f7b27c9d9c99377a5d1898.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/8c325612684d41304b9751c175df7bcc0f61f64f.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/8ce03f78ed945f2ef3dac87c8799b55b393527e7.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/8cfa62046b3a57f37068f027941acea702d9bd8a.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/8dbae01b96ab8dd591a50213f55caa3ac5958631.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/8f28cef89dfa1989be5e263ac228a61a92736da9.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/900be94839e1ee03a8aa1134961912314905eb27.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/9035b87959dc0646562a6d53308049cc922156bb.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/90c8bfc206db2d9f4d0dd102507c9646a70755db.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/910a79b832c4678163ca0d7ec5285ed700df12da.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/93ddc61f81c056650ee9c02c3e62ed84e4a5327f.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/945e8fc1c1e499c337c7dcdd142bb39604d56309.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/954afa1fc1c88d82f51ab22e64b4a7450a89f755.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/95b885fe932dd6fbab3f54e7ed819923deea95fb.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/967116fa65cb7eb073c2f71309b21af6aca77a09.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/98a432864561550105d4de621bc0a9b61068c043.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/99b2691ddceb12733ac3509c90c732bed0c96e77.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/9a0695dca61ffb4f70433158cdbfcd205c439e26.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/9c2a28916b33bc70a170729d2f237eabae96c0c0.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/9d0d9139c35e749d7b4698592c91045d086d8b99.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/9d3cd4785fd2621936d97aa60cb5005a7e95b5b7.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/9d909a12ad669a49e201a6d94b4211c7aeebb68c.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/9f60d76fad5e82e626deca0a8d7c986640b417f3.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/a18e167e42f3575a85b73c2e10114ff027067640.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/a1a25b9ae9dc5911e30e15d5ca2fc5ee8234f1f1.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/a1ffc0a012620941fe660cedabff822ce7162eca.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/a314855731651c7f7c4be200464c677d127f9015.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/a32f407296c4d7fd42fe568d2ad7e8fa81f2df27.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/a581f053bbfa5115f42c13094857cdd12a37ec49.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/a60e2d788aa8bf7ab909fe82a1cca56107dd12e5.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/a6c493a071c2c31758bc2bc9adc2beb10d7acfd0.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/aad9832dd45598090e4be6aaeeaa038dfc093751.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/ab2209a9dd144b06f80bd8b7426bd8b1549be50a.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/ac40b59145892a5a3c0141c5f35023ad85972855.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/ac73c7c8c596c32306e6ea203ebd039dd3538f06.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/ad3d39eb3e45536da2964b7ddf2bc114090510a6.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/b124ff74afb0914bb434e8fb849eb56d734412f8.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/b143c2df6dd4371fa6776f781e9b531868607b86.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/b2cd17a83bb89dc6b4ff954979dba3b2fbc8e043.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/b3cb421b6b325deff2bde26de2fa150f94c73246.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/b4104067dcc504da8eada0697129bcae9f7cf9b6.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/b4264aa4a69bcd11064747964ae4d69f61174136.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/b4f82fd61e3f0ad9fbe9067bcf126248eb498b74.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/b51a1f74e439e8b3b84c8fe25870d53a571391ef.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/b531a865c77331b7c227b22a91e1aa74a8746db3.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/b55687e1799df2d682b5ba0c207ca16fa9c014fe.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/b55ca7a0aa88ab7d58f4fc035317fdac39b17861.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/b588eea9cec4513a3be72255d8d3df214546bfe7.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/b5e8dba2403c0723e1ff60ac53116252af8aeb64.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/b770d151d770160624bb46bf874ebf6f508cf50f.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/b7dd9f89843eb79d04a50776ab962d81e72223e6.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/b95363838d60e863c4cb530ba409fa56dd735728.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/baa542966f410097c2b4894388e41db6aa3cd996.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/bb2c93730dbb48558bb3c4738c956c4e8f816437.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/bdc15139139f2cc22f5656d0456d7b432a72c4e8.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/bf3f9bbcfd5ec66f5d3cf9863e4ff0f76834a7ab.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/bf7f4ceb6fce455ca0347cd7b704321b92e04f8c.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/bfdb965399e4a77f861930aaf9123ed14f72c98c.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/c15067d0b2458f2c3bfd62743d7309f7e48213f9.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/c305e13251e69e8ce59b2908f84afbbd085b5103.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/c323bf28746b8e2634c8d9fcf563736b0fa3f8d7.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/c42a32017c99646f19bb5807728595d4526c3b30.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/c4bb40dd65eae6c11b325989b14e0b8d35e4e3ef.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/c4f23b63471bb0807346dc89db91e3a684cfa236.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/c51284f727b4cb7dac214d351742114146795074.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/c5446f5064ccacb0879e4de7125d4ba077989b35.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/c5671cc4a4d93628081c7bf7c0fd2cc6ff6c1cd3.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/c65f07031d99ff5cc529d9ed7df0c615e9a4f1d3.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/c6e0b5935589b4f8948c91faa0de425bf2791db4.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/c78863bcf0ada3d17f98c21c00ca8b115f6b61de.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/c8094929c9526b00e767a432d17e2668a910af82.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/c8e734bf3818506e254ebfa35047a7957546c2f8.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/c9264cc703654b5651cb89a1c9f5e178b5d15cd0.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/cb8fa4f651751e20465cefe0f4795667b8b948fa.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/cbac43e2c13c3cbcec9f19adc7d3fd94eaf6c15e.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/cbe3f817ae974accdf9d422c0df8f6c90721037c.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/cd19c19fc5bc5f4d74c11dbaf2e135eb5d6a0ed5.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/ce4f41d90f3fea6e5313ad3c0e8aac4f969ef16b.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/cf699492db927585a503d73d1c8a8e93d3fd1567.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/cfe0e96f004950d55bc7f58233eedae9f8b6abc1.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/d0b4b390a4806bb739c6b4adbdf572347ecda952.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/d18b93994f644beb72edf4196d4efed3caf5b19c.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/d1c6b35072ca09fd5e1086a11c0a176d2e3340bf.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/d1e970327c74ae0d8249c6283d2e3134d042f6ff.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/d32c78b759903e3f4bd4fd2ce0b86358f7500c5d.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/d34109b1b68b9bfbd209053492066ff04075aa16.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/d3b6fc67523d073c5f5a8ef0911bd24af5a24c7f.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/d4ba00bbbae4f7118119ee537685dcf90c0fc27c.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/d5a65ff91df0d520048831e9320e60b4b8d6ce50.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/d5ba56a402179916023ef587910f0beff5ea1608.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/d6a7ccf879c4a4fe694033606332cb83806db296.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/d86c223034583bbd6c0fb7533a09ea48e931cb9b.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/da419aab4b72e8996af952ad9802b6773a6e9135.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/db94af9f89e3d01956eca6e0dc1be147b0bd4246.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/dbcdbe7c53fa70f8517907ca1b3c440b28512dfc.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/dcf5762fe2b4b81adb93ee084951f42a2f1eadbc.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/dd84cb843a3b1f44c09b5e49fb02ad7ff526920d.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/de27eb4f973d27763dc4d176bf2453607ecaae42.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/dfa75235bbe9a4311f49a1644137fd212ea3f4bb.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/dfc85915a4cda974f3bd5c3c52b7d2e59598a1eb.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/dfebf8b683f78ab2f777eb73b12281d973e058a6.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/e12eccdaacf4bf3867a4e39d2f0be9eb7720060c.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/e29be0a3b86507644c82abbc0adfcb22f0edb5e3.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/e31584cdce0b50ee39c63081564f6e2ec5a7dcbf.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/e35ec9f12692dad5164f15f68fc16f969099e241.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/e3c6f9fd144c9ed47b5f4dcc9c8ee3574efa18e4.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/e3de9b8fce0b12c8459947bd22c727e379512fcc.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/e4ca401f97df40e3568bc0624774cafa02d77cc1.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/e4cd0999828f4c050af89db8654fce53c3f98231.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/e4cd5b31b960f1b94e98198a94b62e3680c17867.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/e5101484e2acde007f2471ef3379937d4bd30d14.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/e6eadc1093d6e4f5a41f86b8301c0d7559d2c609.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/e8522a3d29a2f0f886fe156c30351ba0489992b9.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/e9203da50e1059455123460d4e716c9c7f440cc3.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/e9e9403195bb7daf89830f82a342b060c4e39b45.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/eaf4418fbe935c15a606516d8f55dc380cd8e822.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/eb1910746ff128b185d49f778c89d74ca1df54d1.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/ec1ce56da06483e62a9456229761ff166b345ed2.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/edd52ffa94a411e3a7b271caf0a9d1ea6fda0f4c.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/ee1817a60aae189024fe864c85bf32c1e92a699a.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/eeae8ea2feacb63d810fdd882d2472f6323a6874.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/eee8ed92791a9c140bb435f75178642c8bc57146.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/ef18200849afb857a3845934c4967bf7c2366755.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/f04823ba99f1452d779894f7a0745da23b55fbef.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/f5047d1e0cbb50ec208923a22cd517c55100fa7b.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/f574498915fa9e02eeb5141c24835d077eba3e75.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/f5c280d551f620f1d0b5abc5b7e9c6bc481cf27b.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/f5d499efbd23360f6e9229bdccf14e0951954730.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/f8f0dc31c5c12bc6e3da6fc71519fa78ea04190b.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/f9147d34f15a498240d3232622111204005e72d3.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/f915ce7b55e81645409daf9d1458f32462dfcdf8.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/f99450c7a8851772907f28ffd58be080f18894ae.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/fa8a7b2fb896adf1c9f17077664691e511aeb8e5.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/fc64cd1ec99f3144a0bc0590d0f67e6411cb8a7d.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/fca2f0caf945233a44e5c1c1479f13d636edbf42.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/fd26dda3f4f507f3fdfaff4fe0b468bee54e7c09.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/fdb63b9e51abe6bbb16acfb5d7b773ddbb5bf4a8.png… | |
Binary files differ. | |
diff --git a/doc/html/_images/math/ff9078c258266635e46b9767b0c7e248c865e4ab.png… | |
Binary files differ. | |
diff --git a/doc/html/_sources/cfd.txt b/doc/html/_sources/cfd.txt | |
t@@ -0,0 +1,578 @@ | |
+Fluid simulation and particle-fluid interaction | |
+=============================================== | |
+A new and experimental addition to *sphere* is the ability to simulate a mixtu… | |
+of particles and a Newtonian fluid. The fluid is simulated using an Eulerian | |
+continuum approach, using a custom CUDA solver for GPU computation. This | |
+approach allows for fast simulations due to the limited need for GPU-CPU | |
+communications, as well as a flexible code base. | |
+ | |
+The following sections will describe the theoretical background, as well as the | |
+solution procedure and the numerical implementation. | |
+ | |
+Derivation of the Navier Stokes equations with porosity | |
+------------------------------------------------------- | |
+Following the outline presented by `Limache and Idelsohn (2006)`_, the | |
+continuity equation for an incompressible fluid material is given by: | |
+ | |
+.. math:: | |
+ \nabla \cdot \boldsymbol{v} = 0 | |
+ | |
+and the momentum equation: | |
+ | |
+.. math:: | |
+ \rho \frac{\partial \boldsymbol{v}}{\partial t} | |
+ + \rho (\boldsymbol{v} \cdot \nabla \boldsymbol{v}) | |
+ = \nabla \cdot \boldsymbol{\sigma} | |
+ + \rho \boldsymbol{f} | |
+ | |
+Here, :math:`\boldsymbol{v}` is the fluid velocity, :math:`\rho` is the | |
+fluid density, :math:`\boldsymbol{\sigma}` is the `Cauchy stress tensor`_, and | |
+:math:`\boldsymbol{f}` is a body force (e.g. gravity). For incompressible | |
+Newtonian fluids, the Cauchy stress is given by: | |
+ | |
+.. math:: | |
+ \boldsymbol{\sigma} = -p \boldsymbol{I} + \boldsymbol{\tau} | |
+ | |
+:math:`p` is the fluid pressure, :math:`\boldsymbol{I}` is the identity | |
+tensor, and :math:`\boldsymbol{\tau}` is the deviatoric stress tensor, given | |
+by: | |
+ | |
+.. math:: | |
+ \boldsymbol{\tau} = | |
+ \mu_f \nabla \boldsymbol{v} | |
+ + \mu_f (\nabla \boldsymbol{v})^T | |
+ | |
+By using the following vector identities: | |
+ | |
+.. math:: | |
+ \nabla \cdot (p \boldsymbol{I}) = \nabla p | |
+ | |
+ \nabla \cdot (\nabla \boldsymbol{v}) = \nabla^2 \boldsymbol{v} | |
+ | |
+ \nabla \cdot (\nabla \boldsymbol{v})^T | |
+ = \nabla (\nabla \cdot \boldsymbol{v}) | |
+ | |
+the deviatoric component of the Cauchy stress tensor simplifies to the | |
+following, assuming that spatial variations in the viscosity can be neglected: | |
+ | |
+.. math:: | |
+ = -\nabla p | |
+ + \mu_f \nabla^2 \boldsymbol{v} | |
+ | |
+Since we are dealing with fluid flow in a porous medium, additional terms are | |
+introduced to the equations for conservation of mass and momentum. In the | |
+following, the equations are derived for the first spatial component. The | |
+solution for the other components is trivial. | |
+ | |
+The porosity value (in the saturated porous medium the volumetric fraction of | |
+the fluid phase) denoted :math:`\phi` is incorporated in the continuity and | |
+momentum equations. The continuity equation becomes: | |
+ | |
+.. math:: | |
+ \frac{\partial \phi}{\partial t} | |
+ + \nabla \cdot (\phi \boldsymbol{v}) = 0 | |
+ | |
+For the :math:`x` component, the Lagrangian formulation of the momentum equati… | |
+with a body force :math:`\boldsymbol{f}` becomes: | |
+ | |
+.. math:: | |
+ \frac{D (\phi v_x)}{D t} | |
+ = \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\sigma}) \right]_x | |
+ + \phi f_x | |
+ | |
+In the Eulerian formulation, an advection term is added, and the Cauchy stress | |
+tensor is represented as isotropic and deviatoric components individually: | |
+ | |
+.. math:: | |
+ \frac{\partial (\phi v_x)}{\partial t} | |
+ + \boldsymbol{v} \cdot \nabla (\phi v_x) | |
+ = \frac{1}{\rho} \left[ \nabla \cdot (-\phi p \boldsymbol{I}) | |
+ + \phi \boldsymbol{\tau}) \right]_x | |
+ + \phi f_x | |
+ | |
+Using vector identities to rewrite the advection term, and expanding the fluid | |
+stress tensor term: | |
+ | |
+.. math:: | |
+ \frac{\partial (\phi v_x)}{\partial t} | |
+ + \nabla \cdot (\phi v_x \boldsymbol{v}) | |
+ - \phi v_x (\nabla \cdot \boldsymbol{v}) | |
+ = \frac{1}{\rho} \left[ -\nabla \phi p \right]_x | |
+ + \frac{1}{\rho} \left[ -\phi \nabla p \right]_x | |
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
+ + \phi f_x | |
+ | |
+Spatial variations in the porosity are neglected, | |
+ | |
+.. math:: | |
+ \nabla \phi := 0 | |
+ | |
+and the pressure is attributed to the fluid phase alone (model B in Zhu et al. | |
+2007 and Zhou et al. 2010). The divergence of fluid velocities is defined to be | |
+zero: | |
+ | |
+.. math:: | |
+ \nabla \cdot \boldsymbol{v} := 0 | |
+ | |
+With these assumptions, the momentum equation simplifies to: | |
+ | |
+.. math:: | |
+ \frac{\partial (\phi v_x)}{\partial t} | |
+ + \nabla \cdot (\phi v_x \boldsymbol{v}) | |
+ = -\frac{1}{\rho} \frac{\partial p}{\partial x} | |
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
+ + \phi f_x | |
+ | |
+The remaining part of the advection term is for the :math:`x` component | |
+found as: | |
+ | |
+.. math:: | |
+ \nabla \cdot (\phi v_x \boldsymbol{v}) = | |
+ \left[ | |
+ \frac{\partial}{\partial x}, | |
+ \frac{\partial}{\partial y}, | |
+ \frac{\partial}{\partial z} | |
+ \right] | |
+ \left[ | |
+ \begin{array}{c} | |
+ \phi v_x v_x\\ | |
+ \phi v_x v_y\\ | |
+ \phi v_x v_z\\ | |
+ \end{array} | |
+ \right] | |
+ = | |
+ \frac{\partial (\phi v_x v_x)}{\partial x} + | |
+ \frac{\partial (\phi v_x v_y)}{\partial y} + | |
+ \frac{\partial (\phi v_x v_z)}{\partial z} | |
+ | |
+The deviatoric stress tensor is in this case symmetrical, i.e. :math:`\tau_{ij} | |
+= \tau_{ji}`, and is found by: | |
+ | |
+.. math:: | |
+ \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
+ = \frac{1}{\rho} | |
+ \left[ | |
+ \left[ | |
+ \frac{\partial}{\partial x}, | |
+ \frac{\partial}{\partial y}, | |
+ \frac{\partial}{\partial z} | |
+ \right] | |
+ \phi | |
+ \left[ | |
+ \begin{matrix} | |
+ \tau_{xx} & \tau_{xy} & \tau_{xz}\\ | |
+ \tau_{yx} & \tau_{yy} & \tau_{yz}\\ | |
+ \tau_{zx} & \tau_{zy} & \tau_{zz}\\ | |
+ \end{matrix} | |
+ \right] | |
+ \right]_x | |
+ | |
+ = \frac{1}{\rho} | |
+ \left[ | |
+ \begin{array}{c} | |
+ \frac{\partial (\phi \tau_{xx})}{\partial x} | |
+ + \frac{\partial (\phi \tau_{xy})}{\partial y} | |
+ + \frac{\partial (\phi \tau_{xz})}{\partial z}\\ | |
+ \frac{\partial (\phi \tau_{yx})}{\partial x} | |
+ + \frac{\partial (\phi \tau_{yy})}{\partial y} | |
+ + \frac{\partial (\phi \tau_{yz})}{\partial z}\\ | |
+ \frac{\partial (\phi \tau_{zx})}{\partial x} | |
+ + \frac{\partial (\phi \tau_{zy})}{\partial y} | |
+ + \frac{\partial (\phi \tau_{zz})}{\partial z}\\ | |
+ \end{array} | |
+ \right]_x | |
+ = \frac{1}{\rho} | |
+ \left( | |
+ \frac{\partial (\phi \tau_{xx})}{\partial x} | |
+ + \frac{\partial (\phi \tau_{xy})}{\partial y} | |
+ + \frac{\partial (\phi \tau_{xz})}{\partial z} | |
+ \right) | |
+ | |
+In a linear viscous fluid, the stress and strain rate | |
+(:math:`\dot{\boldsymbol{\epsilon}}`) is linearly dependent, scaled by the | |
+viscosity parameter :math:`\mu_f`: | |
+ | |
+.. math:: | |
+ \tau_{ij} = 2 \mu_f \dot{\epsilon}_{ij} | |
+ = \mu_f \left( | |
+ \frac{\partial v_i}{\partial x_j} + \frac{\partial v_j}{\partial x_i} | |
+ \right) | |
+ | |
+With this relationship, the deviatoric stress tensor components can be | |
+calculated as: | |
+ | |
+.. math:: | |
+ \tau_{xx} = 2 \mu_f \frac{\partial v_x}{\partial x} \qquad | |
+ \tau_{yy} = 2 \mu_f \frac{\partial v_y}{\partial y} \qquad | |
+ \tau_{zz} = 2 \mu_f \frac{\partial v_z}{\partial z} | |
+ | |
+ \tau_{xy} = \mu_f \left( | |
+ \frac{\partial v_x}{\partial y} + \frac{\partial v_y}{\partial x} \right) | |
+ | |
+ \tau_{xz} = \mu_f \left( | |
+ \frac{\partial v_x}{\partial z} + \frac{\partial v_z}{\partial x} \right) | |
+ | |
+ \tau_{yz} = \mu_f \left( | |
+ \frac{\partial v_y}{\partial z} + \frac{\partial v_z}{\partial y} \right) | |
+ | |
+where :math:`\mu_f` is the dynamic viscosity. The above formulation of the | |
+fluid rheology assumes identical bulk and shear viscosities. The derivation of | |
+the equations for the other spatial components is trivial. | |
+ | |
+Porosity estimation | |
+------------------- | |
+The solid volume in each fluid cell is determined by the ratio of the | |
+a cell-centered spherical cell volume (:math:`V_c`) and the sum of intersecting | |
+particle volumes (:math:`V_s`). The spherical cell volume has a center at | |
+:math:`\boldsymbol{x}_i`, and a radius of :math:`R_i`, which is equal to half | |
+the fluid cell width. The nearby particles are characterized by position | |
+:math:`\boldsymbol{x}_j` and radius :math:`r_j`. The center distance is defined | |
+as: | |
+ | |
+.. math:: | |
+ d_{ij} = ||\boldsymbol{x}_i - \boldsymbol{x}_j|| | |
+ | |
+The common volume of the two intersecting spheres is zero if the volumes aren't | |
+intersecting, lens shaped if they are intersecting, and spherical if the | |
+particle is fully contained by the spherical cell volume: | |
+ | |
+.. math:: | |
+ V^s_{i} = \sum_j | |
+ \begin{cases} | |
+ 0 & \textit{if } R_i + r_j \leq d_{ij} \\ | |
+ \frac{1}{12d_{ij}} \left[ \pi (R_i + r_j - d_{ij})^2 | |
+ (d_{ij}^2 + 2d_{ij}r_j - 3r_j^2 + 2d_{ij} R_i + 6r_j R_i - 3R_i^2) | |
+ \right] & \textit{if } R_i - r_j < d_{ij} < R_i + r_j \\ | |
+ \frac{4}{3} \pi r^3_j & \textit{if } d_{ij} \leq R_i - r_j | |
+ \end{cases} | |
+ | |
+Using this method, the cell porosity values are continuous through time as | |
+particles enter and exit the cell volume. The rate of porosity change | |
+(:math:`d\phi/dt`) is estimated by the backwards Euler method | |
+by considering the previous and current porosity. | |
+ | |
+Particle-fluid interaction | |
+-------------------------- | |
+The momentum exchange of the granular and fluid phases follows the procedure | |
+outlined by Gidaspow 1992 and Shamy and Zhegal 2005. The fluid and particle | |
+interaction is based on the concept of drag, where the magnitude is based on | |
+semi-empirical relationships. The drag force scales linearly with the relative | |
+difference in velocity between the fluid and particle phase. On the base of | |
+Newton's third law, the resulting drag force is applied with opposite signs to | |
+the particle and fluid. | |
+ | |
+For fluid cells with porosities (:math:`\phi`) less or equal to 0.8, the drag | |
+force is based on the Ergun (1952) equation: | |
+ | |
+.. math:: | |
+ \bar{\boldsymbol{f}}_d = \left( | |
+ 150 \frac{\mu_f (1-\phi)^2}{\phi\bar{d}^2} | |
+ + 1.75 \frac{(1-\phi)\rho_f | |
+ ||\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p||}{\bar{d}} | |
+ \right) | |
+ (\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p) | |
+ | |
+here, :math:`\bar{d}` denotes the average particle diameter in the cell, | |
+:math:`\boldsymbol{v}_f` is the fluid flow velocity, and | |
+:math:`\bar{\boldsymbol{v}}_p` is the average particle velocity in the cell. A… | |
+particles in contact with the previously mentioned cell-centered sphere for | |
+porosity estimation contribute to the average particle velocity and diameter in | |
+the fluid cell. | |
+ | |
+If the porosity is greater than 0.8, the cell-averaged drag force | |
+(:math:`\bar{\boldsymbol{f}}_d` is found from the Wen and Yu (1966) equation, | |
+which considers the fluid flow situation: | |
+ | |
+.. math:: | |
+ \bar{\boldsymbol{f}}_d = \left( | |
+ \frac{3}{4} | |
+ \frac{C_d (1-\phi) \phi^{-2.65} \mu_f \rho_f | |
+ ||\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p||}{\bar{d}} | |
+ \right) | |
+ (\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p) | |
+ | |
+The drag coefficient :math:`C_d` is evaluated depending on the magnitude of the | |
+Reynolds number :math:`Re`: | |
+ | |
+.. math:: | |
+ C_d = | |
+ \begin{cases} | |
+ \frac{24}{Re} (1+0.15 (Re)^{0.687} & \textit{if } Re < 1,000 \\ | |
+ 0.44 & \textit{if } Re \geq 1,000 | |
+ \end{cases} | |
+ | |
+where the Reynold's number is found by: | |
+ | |
+.. math:: | |
+ Re = \frac{\phi\rho_f\bar{d}}{\mu_f} | |
+ ||\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p|| | |
+ | |
+The interaction force is applied to the fluid with negative sign as a | |
+contribution to the body force :math:`\boldsymbol{f}`. The fluid interaction | |
+force applied particles in the fluid cell is: | |
+ | |
+.. math:: | |
+ \boldsymbol{f}_i = \frac{\bar{\boldsymbol{f}}_d V_p}{1-\phi} | |
+ | |
+where :math:`V_p` denotes the particle volume. Optionally, the above | |
+interaction force could be expanded to include the force induced by the fluid | |
+pressure gradient: | |
+ | |
+.. math:: | |
+ \boldsymbol{f}_i = \left( | |
+ -\nabla p + | |
+ \frac{\bar{\boldsymbol{f}}_d}{1-\phi} | |
+ \right) V_p | |
+ | |
+ | |
+Fluid dynamics solution procedure by operator splitting | |
+------------------------------------------------------- | |
+The partial differential terms in the previously described equations are found | |
+using finite central differences. Modifying the operator splitting methodology | |
+presented by Langtangen et al. (2002), the predicted velocity | |
+:math:`\boldsymbol{v}^*` after a finite time step | |
+:math:`\Delta t` is found by explicit integration of the momentum equation. | |
+ | |
+.. math:: | |
+ \frac{\Delta (\phi v_x)}{\Delta t} | |
+ + \nabla \cdot (\phi v_x \boldsymbol{v}) | |
+ = - \frac{1}{\rho} \frac{\Delta p}{\Delta x} | |
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
+ + \phi f_x | |
+ | |
+ \Downarrow | |
+ | |
+ \phi \frac{\Delta v_x}{\Delta t} | |
+ + v_x \frac{\Delta \phi}{\Delta t} | |
+ + \nabla \cdot (\phi v_x \boldsymbol{v}) | |
+ = - \frac{1}{\rho} \frac{\Delta p}{\Delta x} | |
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
+ + \phi f_x | |
+ | |
+We want to isolate :math:`\Delta v_x` in the above equation in order to project | |
+the new velocity. | |
+ | |
+.. math:: | |
+ \phi \frac{\Delta v_x}{\Delta t} | |
+ = - \frac{1}{\rho} \frac{\Delta p}{\Delta x} | |
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
+ + \phi f_x | |
+ - v_x \frac{\Delta \phi}{\Delta t} | |
+ - \nabla \cdot (\phi v_x \boldsymbol{v}) | |
+ | |
+ \Delta v_x | |
+ = - \frac{1}{\rho} \frac{\Delta p}{\Delta x} \frac{\Delta t}{\phi} | |
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
+ \frac{\Delta t}{\phi} | |
+ + \Delta t f_x | |
+ - v_x \frac{\Delta \phi}{\phi} | |
+ - \nabla \cdot (\phi v_x \boldsymbol{v}) \frac{\Delta t}{\phi} | |
+ | |
+The term :math:`\beta` is introduced as an adjustable, dimensionless parameter | |
+in the range :math:`[0;1]`, and determines the importance of the old pressure | |
+values in the solution procedure (Langtangen et al. 2002). A value of 0 | |
+corresponds to `Chorin's projection method`_ originally described | |
+in `Chorin (1968)`_. | |
+ | |
+.. math:: | |
+ v_x^* = v_x^t + \Delta v_x | |
+ | |
+ v_x^* = v_x^t | |
+ - \frac{\beta}{\rho} \frac{\Delta p^t}{\Delta x} \frac{\Delta t}{\phi^t} | |
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi^t \boldsymbol{\tau}^t) \right]_x | |
+ \frac{\Delta t}{\phi} | |
+ + \Delta t f_x | |
+ - v^t_x \frac{\Delta \phi}{\phi^t} | |
+ - \nabla \cdot (\phi^t v_x^t \boldsymbol{v}^t) \frac{\Delta t}{\phi^t} | |
+ | |
+Here, :math:`\Delta x` denotes the cell spacing. The velocity found | |
+(:math:`v_x^*`) is only a prediction of the fluid velocity at time | |
+:math:`t+\Delta t`, since the estimate isn't constrained by the continuity | |
+equation: | |
+ | |
+.. math:: | |
+ \frac{\Delta \phi^t}{\Delta t} + \nabla \cdot (\phi^t | |
+ \boldsymbol{v}^{t+\Delta t}) = 0 | |
+ | |
+The divergence of a scalar and vector can be `split`_: | |
+ | |
+.. math:: | |
+ \phi^t \nabla \cdot \boldsymbol{v}^{t+\Delta t} + | |
+ \boldsymbol{v}^{t+\Delta t} \cdot \nabla \phi^t | |
+ + \frac{\Delta \phi^t}{\Delta t} = 0 | |
+ | |
+The predicted velocity is corrected using the new pressure (Langtangen et al. | |
+2002): | |
+ | |
+.. math:: | |
+ \boldsymbol{v}^{t+\Delta t} = \boldsymbol{v}^* | |
+ - \frac{\Delta t}{\rho} \nabla \epsilon | |
+ \quad \text{where} \quad | |
+ \epsilon = p^{t+\Delta t} - \beta p^t | |
+ | |
+The above formulation of the future velocity is put into the continuity | |
+equation: | |
+ | |
+.. math:: | |
+ \Rightarrow | |
+ \phi^t \nabla \cdot | |
+ \left( \boldsymbol{v}^* - \frac{\Delta t}{\rho} \nabla \epsilon \right) | |
+ + | |
+ \left( \boldsymbol{v}^* - \frac{\Delta t}{\rho} \nabla \epsilon \right) | |
+ \cdot \nabla \phi^t + \frac{\Delta \phi^t}{\Delta t} = 0 | |
+ | |
+.. math:: | |
+ \Rightarrow | |
+ \phi^t \nabla \cdot | |
+ \boldsymbol{v}^* - \frac{\Delta t}{\rho} \phi^t \nabla^2 \epsilon | |
+ + \nabla \phi^t \cdot \boldsymbol{v}^* | |
+ - \nabla \phi^t \cdot \nabla \epsilon \frac{\Delta t}{\rho} | |
+ + \frac{\Delta \phi^t}{\Delta t} = 0 | |
+ | |
+.. math:: | |
+ \Rightarrow | |
+ \frac{\Delta t}{\rho} \phi^t \nabla^2 \epsilon | |
+ = \phi^t \nabla \cdot \boldsymbol{v}^* | |
+ + \nabla \phi^t \cdot \boldsymbol{v}^* | |
+ - \nabla \phi^t \cdot \nabla \epsilon \frac{\Delta t}{\rho} | |
+ + \frac{\Delta \phi^t}{\Delta t} | |
+ | |
+The pressure difference in time becomes a `Poisson equation`_ with added terms: | |
+ | |
+.. math:: | |
+ \Rightarrow | |
+ \nabla^2 \epsilon | |
+ = \frac{\nabla \cdot \boldsymbol{v}^* \rho}{\Delta t} | |
+ + \frac{\nabla \phi^t \cdot \boldsymbol{v}^* \rho}{\Delta t \phi^t} | |
+ - \frac{\nabla \phi^t \cdot \nabla \epsilon}{\phi^t} | |
+ + \frac{\Delta \phi^t \rho}{\Delta t^2 \phi^t} | |
+ | |
+The right hand side of the above equation is termed the *forcing function* | |
+:math:`f`, which is decomposed into two terms, :math:`f_1` and :math:`f_2`: | |
+ | |
+.. math:: | |
+ f_1 | |
+ = \frac{\nabla \cdot \boldsymbol{v}^* \rho}{\Delta t} | |
+ + \frac{\nabla \phi^t \cdot \boldsymbol{v}^* \rho}{\Delta t \phi^t} | |
+ + \frac{\Delta \phi^t \rho}{\Delta t^2 \phi^t} | |
+ | |
+ f_2 = | |
+ \frac{\nabla \phi^t \cdot \nabla \epsilon}{\phi^t} | |
+ | |
+ | |
+During the `Jacobi iterative solution procedure`_ :math:`f_1` remains constant, | |
+while :math:`f_2` changes value. For this reason, :math:`f_1` is found only | |
+during the first iteration, while :math:`f_2` is updated every time. The value | |
+of the forcing function is found as: | |
+ | |
+.. math:: | |
+ f = f_1 - f_2 | |
+ | |
+Using second-order finite difference approximations of the Laplace operator | |
+second-order partial derivatives, the differential equations become a system of | |
+equations that is solved using `iteratively`_ using Jacobi updates. The total | |
+number of unknowns is :math:`(n_x - 1)(n_y - 1)(n_z - 1)`. | |
+ | |
+The discrete Laplacian (approximation of the Laplace operator) can be obtained | |
+by a finite-difference seven-point stencil in a three-dimensional, cubic | |
+grid with cell spacing :math:`\Delta x, \Delta y, \Delta z`, considering the s… | |
+face neighbors: | |
+ | |
+.. math:: | |
+ \nabla^2 \epsilon_{i_x,i_y,i_z} \approx | |
+ \frac{\epsilon_{i_x-1,i_y,i_z} - 2 \epsilon_{i_x,i_y,i_z} | |
+ + \epsilon_{i_x+1,i_y,i_z}}{\Delta x^2} | |
+ + \frac{\epsilon_{i_x,i_y-1,i_z} - 2 \epsilon_{i_x,i_y,i_z} | |
+ + \epsilon_{i_x,i_y+1,i_z}}{\Delta y^2} | |
+ | |
+ + \frac{\epsilon_{i_x,i_y,i_z-1} - 2 \epsilon_{i_x,i_y,i_z} | |
+ + \epsilon_{i_x,i_y,i_z+1}}{\Delta z^2} | |
+ \approx f_{i_x,i_y,i_z} | |
+ | |
+Within a Jacobi iteration, the value of the unknowns (:math:`\epsilon^n`) is | |
+used to find an updated solution estimate (:math:`\epsilon^{n+1}`). | |
+The solution for the updated value takes the form: | |
+ | |
+.. math:: | |
+ \epsilon^{n+1}_{i_x,i_y,i_z} | |
+ = \frac{-\Delta x^2 \Delta y^2 \Delta z^2 f_{i_x,i_y,i_z} | |
+ + \Delta y^2 \Delta z^2 (\epsilon^n_{i_x-1,i_y,i_z} + | |
+ \epsilon^n_{i_x+1,i_y,i_z}) | |
+ + \Delta x^2 \Delta z^2 (\epsilon^n_{i_x,i_y-1,i_z} + | |
+ \epsilon^n_{i_x,i_y+1,i_z}) | |
+ + \Delta x^2 \Delta y^2 (\epsilon^n_{i_x,i_y,i_z-1} + | |
+ \epsilon^n_{i_x,i_y,i_z+1})} | |
+ {2 (\Delta x^2 \Delta y^2 | |
+ + \Delta x^2 \Delta z^2 | |
+ + \Delta y^2 \Delta z^2) } | |
+ | |
+The difference between the current and updated value is termed the *normalized | |
+residual*: | |
+ | |
+.. math:: | |
+ r_{i_x,i_y,i_z} = \frac{(\epsilon^{n+1}_{i_x,i_y,i_z} | |
+ - \epsilon^n_{i_x,i_y,i_z})^2}{(\epsilon^{n+1}_{i_x,i_y,i_z})^2} | |
+ | |
+Note that the :math:`\epsilon` values cannot be 0 due to the above normalizati… | |
+of the residual. | |
+ | |
+The updated values are at the end of the iteration stored as the current value… | |
+and the maximal value of the normalized residual is found. If this value is | |
+larger than a tolerance criteria, the procedure is repeated. The iterative | |
+procedure is ended if the number of iterations exceeds a defined limit. | |
+ | |
+After the values of :math:`\epsilon` are found, they are used to find the new | |
+pressures and velocities: | |
+ | |
+.. math:: | |
+ \bar{p}^{t+\Delta t} = \beta \bar{p}^t + \epsilon | |
+ | |
+.. math:: | |
+ \bar{\boldsymbol{v}}^{t+\Delta t} = | |
+ \bar{\boldsymbol{v}}^* - \frac{\Delta t}{\rho} \nabla \epsilon | |
+ | |
+ | |
+Boundary conditions | |
+------------------- | |
+The lateral boundaries are periodic. This cannot be changed in the current | |
+version of ``sphere``. This means that the fluid properties at the paired, | |
+parallel lateral (:math:`x` and :math:`y`) boundaries are identical. A flow | |
+leaving through one side reappears on the opposite side. | |
+ | |
+The top and bottom boundary conditions of the fluid grid can be either: | |
+prescribed pressure (Dirichlet), or prescribed velocity (Neumann). The | |
+(horizontal) velocities parallel to the boundaries are free to attain other | |
+values (free slip). The Dirichlet boundary condition is enforced by keeping the | |
+value of :math:`\epsilon` constant at the boundaries, e.g.: | |
+ | |
+.. math:: | |
+ \epsilon^{n+1}_{i_x,i_y,i_z = 1 \vee n_z} | |
+ = | |
+ \epsilon^{n}_{i_x,i_y,i_z = 1 \vee n_z} | |
+ | |
+The Neumann boundary condition of no flow across the boundary is enforced by | |
+setting the gradient of :math:`\epsilon` perpendicular to the boundary to zero, | |
+e.g.: | |
+ | |
+.. math:: | |
+ \nabla_z \epsilon^{n+1}_{i_x,i_y,i_z = 1 \vee n_z} = 0 | |
+ | |
+ | |
+Numerical implementation | |
+------------------------ | |
+Ghost nodes | |
+ | |
+--- | |
+ | |
+ | |
+ | |
+ | |
+.. _Limache and Idelsohn (2006): http://www.cimec.org.ar/ojs/index.php/mc/arti… | |
+.. _Cauchy stress tensor: https://en.wikipedia.org/wiki/Cauchy_stress_tensor | |
+.. _`Chorin's projection method`: https://en.wikipedia.org/wiki/Projection_met… | |
+.. _`Chorin (1968)`: http://www.ams.org/journals/mcom/1968-22-104/S0025-5718-1… | |
+.. _split: http://www.wolframalpha.com/input/?i=div(p+v) | |
+.. _Poisson equation: https://en.wikipedia.org/wiki/Poisson's_equation | |
+.. _`Jacobi iterative solution procedure`: http://www.rsmas.miami.edu/personal… | |
+.. _iteratively: https://en.wikipedia.org/wiki/Relaxation_(iterative_method) | |
+ | |
diff --git a/doc/html/_sources/dem.txt b/doc/html/_sources/dem.txt | |
t@@ -1,16 +1,217 @@ | |
Discrete element method | |
======================= | |
-The discrete element method (or distinct element method) was initially | |
-formulated by Cundall and Strack (1979). It simulates the physical behavior and | |
-interaction of discrete, unbreakable particles, with their own mass and inerti… | |
-under the influence of e.g. gravity and boundary conditions such as moving | |
-walls. By discretizing time into small time steps, explicit integration of | |
-Newton's second law of motion is used to predict the new position and kinematic | |
-values for each particle from the previous sums of forces. This Lagrangian | |
-approach is ideal for simulating discontinuous materials, such as granular | |
-matter. | |
-The complexity of the computations is kept low by representing the particles as | |
-spheres, which keeps contact-searching algorithms simple. | |
+Granular material is a very common form of matter, both in nature and industry. | |
+It can be defined as material consisting of interacting, discrete particles. | |
+Common granular materials include gravels, sands and soils, ice bergs, | |
+asteroids, powders, seeds, and other foods. Over 75% of the raw materials that | |
+pass through industry are granular. This wide occurrence has driven the desire | |
+to understand the fundamental mechanics of the material. | |
+Contrary to other common materials such as gases, liquids and solids, a general | |
+mathematical formulation of it's behavior hasn't yet been found. Granular | |
+material can, however, display states that somewhat resemble gases, fluids and | |
+solids. | |
+.. The discrete element method (or distinct element method) was initially | |
+ formulated by Cundall and Strack (1979). It simulates the physical behavio… | |
+ interaction of discrete, unbreakable particles, with their own mass and in… | |
+ under the influence of e.g. gravity and boundary conditions such as moving | |
+ walls. By discretizing time into small time steps, explicit integration of | |
+ Newton's second law of motion is used to predict the new position and kine… | |
+ values for each particle from the previous sums of forces. This Lagrangian | |
+ approach is ideal for simulating discontinuous materials, such as granular | |
+ matter. | |
+ The complexity of the computations is kept low by representing the particl… | |
+ spheres, which keeps contact-searching algorithms simple. | |
+ | |
+The `Discrete Element Method | |
+<https://en.wikipedia.org/wiki/Discrete_element_method>`_ (DEM) is a numerical | |
+method that can be used to | |
+simulate the interaction of particles. Originally derived from | |
+`Molecular Dynamics <https://en.wikipedia.org/wiki/Molecular_dynamics>`_, | |
+it simulates particles as separate entities, and calculates their positions, | |
+velocities, and accelerations through time. See Cundall and Strack (1979) and | |
+`this blog post | |
+<http://anders-dc.github.io/2013/10/16/the-discrete-element-method/>`_ for | |
+general introduction to the DEM. The following sections will highlight the | |
+DEM implementation in ``sphere``. Some of the details are also described in | |
+Damsgaard et al. 2013. In the used notation, a bold symbol denotes a | |
+three-dimensional vector, and a dot denotes that the entity is a temporal | |
+derivative. | |
+ | |
+Contact search | |
+-------------- | |
+Homogeneous cubic grid. | |
+ | |
+.. math:: | |
+ \delta_n^{ij} = ||\boldsymbol{x}^i - \boldsymbol{x}^j|| - (r^i + r^j) | |
+ | |
+where :math:`r` is the particle radius, and :math:`\boldsymbol{x}` denotes the | |
+positional vector of a particle, and :math:`i` and :math:`j` denote the indexes | |
+of two particles. Negative values of :math:`\delta_n` denote that the particles | |
+are overlapping. | |
+ | |
+ | |
+Contact interaction | |
+------------------- | |
+Now that the inter-particle contacts have been identified and characterized by | |
+their overlap, the resulting forces from the interaction can be resolved. The | |
+interaction is decomposed into normal and tangential components, relative to t… | |
+contact interface orientation. The normal vector to the contact interface is | |
+found by: | |
+ | |
+.. math:: | |
+ \boldsymbol{n}^{ij} = | |
+ \frac{\boldsymbol{x}^i - \boldsymbol{x}^j} | |
+ {||\boldsymbol{x}^i - \boldsymbol{x}^j||} | |
+ | |
+The contact velocity :math:`\dot{\boldsymbol{\delta}}` is found by: | |
+ | |
+.. math:: | |
+ \dot{\boldsymbol{\delta}}^{ij} = | |
+ (\boldsymbol{x}^i - \boldsymbol{x}^j) | |
+ + (r^i + \frac{\delta_n^{ij}}{2}) | |
+ (\boldsymbol{n}^{ij} \times \boldsymbol{\omega}^{i}) | |
+ + (r^j + \frac{\delta_n^{ij}}{2}) | |
+ (\boldsymbol{n}^{ij} \times \boldsymbol{\omega}^{j}) | |
+ | |
+The contact velocity is decomposed into normal and tangential components, | |
+relative to the contact interface. The normal component is: | |
+ | |
+.. math:: | |
+ \dot{\delta}^{ij}_n = | |
+ -(\dot{\boldsymbol{\delta}}^{ij} \cdot \boldsymbol{n}^{ij}) | |
+ | |
+and the tangential velocity component is found as: | |
+ | |
+.. math:: | |
+ \dot{\boldsymbol{\delta}}^{ij}_t = | |
+ \dot{\boldsymbol{\delta}}^{ij} | |
+ - \boldsymbol{n}^{ij} | |
+ (\boldsymbol{n}^{ij} \cdot \dot{\boldsymbol{\delta}}^{ij}) | |
+ | |
+where :math:`\boldsymbol{\omega}` is the rotational velocity vector of a | |
+particle. The total tangential displacement on the contact plane is found | |
+incrementally: | |
+ | |
+.. math:: | |
+ \boldsymbol{\delta}_{t,\text{uncorrected}}^{ij} = | |
+ \int_0^{t_c} | |
+ \dot{\boldsymbol{\delta}}^{ij}_t \Delta t | |
+ | |
+where :math:`t_c` is the duration of the contact and :math:`\Delta t` is the | |
+computational time step length. The tangential contact interface displacement … | |
+set to zero when a contact pair no longer overlaps. At each time step, the val… | |
+of :math:`\boldsymbol{\delta}_t` is corrected for rotation of the contact | |
+interface: | |
+ | |
+.. math:: | |
+ \boldsymbol{\delta}_t^{ij} = \boldsymbol{\delta}_{t,\text{uncorrected}}^{ij} | |
+ - (\boldsymbol{n} | |
+ (\boldsymbol{n} \cdot \boldsymbol{\delta}_{t,\text{uncorrected}}^{ij}) | |
+ | |
+With all the geometrical and kinetic components determined, the resulting forc… | |
+of the particle interaction can be determined using a contact model. ``sphere`` | |
+features only one contact model in the normal direction to the contact; the | |
+linear-elastic-viscous (*Hookean* with viscous damping, or *Kelvin-Voigt*) | |
+contact model. The resulting force in the normal direction of the contact | |
+interface on particle :math:`i` is: | |
+ | |
+.. math:: | |
+ \boldsymbol{f}_n^{ij} = \left( | |
+ -k_n \delta_n^{ij} -\gamma_n \dot{\delta_n}^{ij} | |
+ \right) \boldsymbol{n}^{ij} | |
+ | |
+The parameter :math:`k_n` is the defined `spring coefficient | |
+<https://en.wikipedia.org/wiki/Hooke's_law>`_ in the normal direction of the | |
+contact interface, and :math:`\gamma_n` is the defined contact interface | |
+viscosity, also in the normal direction. The loss of energy in this interaction | |
+due to the viscous component is for particle :math:`i` calculated as: | |
+ | |
+.. math:: | |
+ \dot{e}^i_v = \gamma_n (\dot{\delta}^{ij}_n)^2 | |
+ | |
+The tangential force is determined by either a viscous-frictional contact mode… | |
+or a elastic-viscous-frictional contact model. The former contact model is very | |
+computationally efficient, but somewhat inaccurate relative to the mechanics of | |
+real materials. The latter contact model is therefore the default, even though | |
+it results in longer computational times. The tangential force in the | |
+visco-frictional contact model: | |
+ | |
+.. math:: | |
+ \boldsymbol{f}_t^{ij} = -\gamma_t \dot{\boldsymbol{\delta}_t}^{ij} | |
+ | |
+:math:`\gamma_n` is the defined contact interface viscosity in the tangential | |
+direction. The tangential displacement along the contact interface | |
+(:math:`\boldsymbol{\delta}_t`) is not calculated and stored for this contact | |
+model. The tangential force in the more realistic elastic-viscous-frictional | |
+contact model: | |
+ | |
+.. math:: | |
+ \boldsymbol{f}_t^{ij} = | |
+ -k_t \boldsymbol{\delta}_t^{ij} -\gamma_t \dot{\boldsymbol{\delta}_t}^{ij} | |
+ | |
+The parameter :math:`k_n` is the defined spring coefficient in the tangential | |
+direction of the contact interface. Note that the tangential force is only | |
+found if the tangential displacement (:math:`\delta_t`) or the tangential | |
+velocity (:math:`\dot{\delta}_t`) is non-zero, in order to avoid division by | |
+zero. Otherwise it is defined as being :math:`[0,0,0]`. | |
+ | |
+For both types of contact model, the tangential force is limited by the Coulomb | |
+criterion of static and dynamic friction: | |
+ | |
+.. math:: | |
+ ||\boldsymbol{f}^{ij}_t|| \leq | |
+ \begin{cases} | |
+ \mu_s ||\boldsymbol{f}^{ij}_n|| & | |
+ \text{if} \quad ||\boldsymbol{f}_t^{ij}|| = 0 \\ | |
+ \mu_d ||\boldsymbol{f}^{ij}_n|| & | |
+ \text{if} \quad ||\boldsymbol{f}_t^{ij}|| > 0 | |
+ \end{cases} | |
+ | |
+If the elastic-viscous-frictional contact model is used and the Coulomb limit … | |
+reached, the tangential displacement along the contact interface is limited to | |
+this value: | |
+ | |
+.. math:: | |
+ \boldsymbol{\delta}_t^{ij} = | |
+ \frac{1}{k_t} \left( | |
+ \mu_d ||\boldsymbol{f}_n^{ij}|| | |
+ \frac{\boldsymbol{f}^{ij}_t}{||\boldsymbol{f}^{ij}_t||} | |
+ + \gamma_t \dot{\boldsymbol{\delta}}_t^{ij} \right) | |
+ | |
+If the tangential force reaches the Coulomb limit, the energy lost due to | |
+frictional dissipation is calculated as: | |
+ | |
+.. math:: | |
+ \dot{e}^i_s = \frac{||\boldsymbol{f}^{ij}_t | |
+ \dot{\boldsymbol{\delta}}_t^{ij} \Delta t||}{\Delta t} | |
+ | |
+The loss of energy by viscous dissipation in the tangential direction is not | |
+found. | |
+ | |
+ | |
+Temporal integration | |
+-------------------- | |
+In the DEM, the time is discretized into small steps (:math:`\Delta t`). For e… | |
+step, the entire network of contacts is resolved, and the resulting forces and | |
+torques for each particle are found. With these values at hand, the new | |
+linear and rotational accelerations can be found using | |
+`Newton's second law <https://en.wikipedia.org/wiki/Newton%27s_laws_of_motion>… | |
+of the motion of solid bodies. If a particle with mass :math:`m` at a point in… | |
+experiences a sum of forces denoted :math:`\boldsymbol{F}`, the resultant acce… | |
+(:math:`\boldsymbol{a}`) can be found by rearranging Newton's second law: | |
+ | |
+.. math:: | |
+ \boldsymbol{F} = m \boldsymbol{a} \Rightarrow \boldsymbol{a} = \frac{\bolds… | |
+ | |
+The new velocity and position is found by integrating the above equation | |
+with regards to time. The simplest integration scheme in this regard is the | |
+`Euler method <https://en.wikipedia.org/wiki/Euler_method>`_: | |
+ | |
+.. math:: | |
+ \boldsymbol{v} = \boldsymbol{v}_{old} + \boldsymbol{a} \Delta t | |
+ | |
+.. math:: | |
+ \boldsymbol{p} = \boldsymbol{p}_{old} + \boldsymbol{v} \Delta t | |
diff --git a/doc/html/_sources/index.txt b/doc/html/_sources/index.txt | |
t@@ -3,36 +3,36 @@ | |
You can adapt this file completely to your liking, but it should at least | |
contain the root `toctree` directive. | |
-Welcome to sphere's documentation! | |
-================================== | |
-This is the official documentation for the *sphere* discrete element modelling | |
-software. It presents the theory behind the discrete element method (DEM), the | |
-structure of the software source code, and the Python API for handling | |
-simulation setup and data analysis. | |
- | |
-*sphere* is developed by Anders Damsgaard Christensen under supervision of Dav… | |
-Lunbek Egholm and Jan A. Piotrowski, all of the department of Geoscience, Aarh… | |
-University, Denmark. This document is a work in progress, and is still in an | |
-early state. | |
- | |
-Contact: Anders Damsgaard Christensen, `<http://cs.au.dk/~adc>`_, | |
+The sphere documentation | |
+======================== | |
+This is the official documentation for the ``sphere`` discrete element modelli… | |
+software. This document aims at guiding the installation process, documenting | |
+the usage, and explaining the relevant theory. | |
+ | |
+``sphere`` is developed by Anders Damsgaard as part as his Ph.D. project, under | |
+supervision of David Lundbek Egholm and Jan A. Piotrowski, all of the Departme… | |
+of Geoscience, Aarhus University, Denmark. The author welcomes interested third | |
+party developers. This document is a work in progress. | |
+ | |
+Contact: Anders Damsgaard, `<http://cs.au.dk/~adc>`_, | |
`<[email protected]>`_ | |
-Contents: | |
- | |
+Contents | |
+-------- | |
.. toctree:: | |
:maxdepth: 2 | |
introduction | |
dem | |
+ cfd | |
python_api | |
- sphere_internals | |
+ .. sphere_internals | |
Indices and tables | |
-================== | |
+------------------ | |
.. * :ref:`modindex` | |
diff --git a/doc/html/_sources/introduction.txt b/doc/html/_sources/introductio… | |
t@@ -1,49 +1,75 @@ | |
Introduction | |
============ | |
-The *sphere*-software is used for three-dimensional discrete element method | |
+ | |
+The ``sphere``-software is used for three-dimensional discrete element method | |
(DEM) particle simulations. The source code is written in C++, CUDA C and | |
Python, and is compiled by the user. The main computations are performed on the | |
graphics processing unit (GPU) using NVIDIA's general purpose parallel computi… | |
architecture, CUDA. Simulation setup and data analysis is performed with the | |
included Python API. | |
-The ultimate aim of the *sphere* software is to simulate soft-bedded subglacial | |
+The ultimate aim of the ``sphere`` software is to simulate soft-bedded subglac… | |
conditions, while retaining the flexibility to perform simulations of granular | |
material in other environments. | |
The purpose of this documentation is to provide the user with a walk-through of | |
the installation, work-flow, data-analysis and visualization methods of | |
-*sphere*. In addition, the *sphere* internals are exposed to provide a way of | |
+``sphere``. In addition, the ``sphere`` internals are exposed to provide a way… | |
understanding of the discrete element method numerical routines taking place. | |
-.. note:: Command examples in this document starting with the symbol ``$`` are… | |
+.. note:: Command examples in this document starting with the symbol ``$`` are | |
+ meant to be executed in the shell of the operational system, and ``>>>`` | |
+ means execution in Python. `IPython <http://ipython.org>`_ is an excellent, | |
+ interactive Python shell. | |
All numerical values in this document, the source code, and the configuration | |
files are typeset with strict respect to the SI unit system. | |
+ | |
Requirements | |
------------ | |
+ | |
The build requirements are: | |
+ | |
* A Nvidia CUDA-supported version of Linux or Mac OS X (see the `CUDA toolkit | |
release notes <http://docs.nvidia.com/cuda/cuda-toolkit-release-notes/inde… | |
* `GNU Make <https://www.gnu.org/software/make/>`_ | |
- * `CMake <http://www.cmake.org>`_ | |
+ * `CMake <http://www.cmake.org>`_, version 2.8 or newer | |
* The `GNU Compiler Collection <http://gcc.gnu.org/>`_ (GCC) | |
- * The `Nvidia CUDA toolkit and SDK <https://developer.nvidia.com/cuda-downlo… | |
+ * The `Nvidia CUDA toolkit <https://developer.nvidia.com/cuda-downloads>`_, | |
+ version 5.0 or newer | |
+ | |
+In Debian GNU/Linux, these dependencies can be installed by running:: | |
+ | |
+ $ sudo apt-get install build-essential cmake nvidia-cuda-toolkit | |
+ | |
+Unfortunately, the Nvidia Toolkit is shipped under a non-free license. In order | |
+to install it in Debian GNU/Linux, add ``non-free`` archives to your | |
+``/etc/apt/sources.list``. | |
The runtime requirements are: | |
+ | |
* A `CUDA-enabled GPU <http://www.nvidia.com/object/cuda_gpus.html>`_ with | |
compute capability 1.1 or greater. | |
* A Nvidia CUDA-enabled GPU and device driver | |
Optional tools, required for simulation setup and data processing: | |
- * `Python 2.7 <http://www.python.org/getit/releases/2.7/>`_ | |
+ | |
+ * `Python <http://www.python.org/>`_ | |
* `Numpy <http://numpy.scipy.org>`_ | |
* `Matplotlib <http://matplotlib.org>`_ | |
+ * `Python bindings for VTK <http://www.vtk.org>`_ | |
* `Imagemagick <http://www.imagemagick.org/script/index.php>`_ | |
- * `ffmpeg <http://ffmpeg.org/>`_ | |
+ * `ffmpeg <http://ffmpeg.org/>`_. Soon to be replaced by avconv! | |
+ | |
+In Debian GNU/Linux, these dependencies can be installed by running:: | |
+ | |
+ $ sudo apt-get install python python-numpy python-matplotlib python-vtk \ | |
+ imagemagick libav-tools | |
+ | |
+``sphere`` is distributed with a HTML and PDF build of the documentation. The | |
+following tools are required for building the documentation: | |
-Optional tools, required for building the documentation: | |
* `Sphinx <http://sphinx-doc.org>`_ | |
* `sphinxcontrib-programoutput <http://packages.python.org/sphinxcontrib-p… | |
t@@ -51,23 +77,75 @@ Optional tools, required for building the documentation: | |
* `Doxygen <http://www.stack.nl/~dimitri/doxygen/>`_ | |
* `Breathe <http://michaeljones.github.com/breathe/>`_ | |
* `dvipng <http://www.nongnu.org/dvipng/>`_ | |
+ * `TeX Live <http://www.tug.org/texlive/>`_, including ``pdflatex`` | |
+ | |
+In Debian GNU/Linux, these dependencies can be installed by running:: | |
+ | |
+ $ sudo apt-get install python-sphinx python-pip doxygen dvipng \ | |
+ python-sphinxcontrib-programoutput texlive-full | |
+ $ sudo pip install breathe | |
`Git <http://git-scm.com>`_ is used as the distributed version control system | |
platform, and the source code is maintained at `Github | |
-<https://github.com/anders-dc/sphere/>`_. *sphere* is licensed under the `GNU | |
+<https://github.com/anders-dc/sphere/>`_. ``sphere`` is licensed under the `GNU | |
Public License, v.3 <https://www.gnu.org/licenses/gpl.html>`_. | |
+.. note:: All Debian GNU/Linux runtime, optional, and documentation dependenci… | |
+ mentioned above can be installed by executing the following command from the | |
+ ``doc/`` folder:: | |
+ | |
+ $ make install-debian-pkgs | |
+ | |
+ | |
+Obtaining sphere | |
+---------------- | |
+ | |
+The best way to keep up to date with subsequent updates, bugfixes and | |
+development, is to use the Git version control system. To obtain a local | |
+copy, execute:: | |
+ | |
+ $ git clone [email protected]:anders-dc/sphere.git | |
-Building *sphere* | |
------------------ | |
-All instructions required for building *sphere* are provided in a number of | |
-``Makefile``'s. To generate the main *sphere* command-line executable, go to t… | |
-root directory, and invoke CMake and GNU Make:: | |
+ | |
+Building ``sphere`` | |
+------------------- | |
+ | |
+``sphere`` is built using ``cmake``, the platform-specific C/C++ compilers, | |
+and ``nvcc`` from the Nvidia CUDA toolkit. | |
+ | |
+If you plan to run ``sphere`` on a Kepler GPU, execute the following commands | |
+from the root directory:: | |
+ | |
+ $ cmake . && make | |
+ | |
+If you instead plan to execute it on a Fermi GPU, change ``set(GPU_GENERATION | |
+1)`` to ``set(GPU_GENERATION 0`` in ``CMakeLists.txt``. | |
+ | |
+In some cases the CMake FindCUDA module will have troubles locating the | |
+CUDA samples directory, and will complain about ``helper_math.h`` not being | |
+found. | |
+ | |
+In that case, modify the ``CUDA_SDK_ROOT_DIR`` variable in | |
+``src/CMakeLists.txt`` to the path where you installed the CUDA samples, and r… | |
+``cmake . && make`` again. Alternatively, copy ``helper_math.h`` from the CUDA | |
+sample subdirectory ``common/inc/helper_math.h`` into the sphere ``src/`` | |
+directory, and run ``cmake`` and ``make`` again. Due to license restrictions, | |
+sphere cannot be distributed with this file. | |
+ | |
+After a successfull installation, the ``sphere`` executable will be located | |
+in the root folder. To make sure that all components are working correctly, | |
+execute:: | |
+ | |
+ $ make test | |
+ | |
+All instructions required for building ``sphere`` are provided in a number of | |
+``Makefile``'s. To generate the main ``sphere`` command-line executable, go to | |
+the root directory, and invoke CMake and GNU Make:: | |
$ cmake . && make | |
If successfull, the Makefiles will create the required data folders, object | |
-files, as well as the *sphere* executable in the root folder. Issue the | |
+files, as well as the ``sphere`` executable in the root folder. Issue the | |
following commands to check the executable:: | |
$ ./sphere --version | |
t@@ -82,8 +160,10 @@ The build can be verified by running a number of automated… | |
The documentation can be read in the `reStructuredText | |
<http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html>`_-format … | |
-the ``doc/sphinx/`` folder, or build into e.g. HTML or PDF format with the | |
-following commands:: | |
+the ``doc/sphinx/`` folder, or in the HTML or PDF formats in the folders | |
+``doc/html`` and ``doc/pdf``. | |
+ | |
+Optionally, the documentation can be built using the following commands:: | |
$ cd doc/sphinx | |
$ make html | |
t@@ -94,15 +174,26 @@ To see all available output formats, execute:: | |
$ make help | |
+Updating sphere | |
+--------------- | |
+ | |
+To update your local version, type the following commands in the ``sphere`` ro… | |
+directory:: | |
+ | |
+ $ git pull && cmake . && make | |
+ | |
+ | |
Work flow | |
--------- | |
-After compiling the *sphere* binary, the procedure of a creating and handling a | |
-simulation is typically arranged in the following order: | |
+ | |
+After compiling the ``sphere`` binary, the procedure of a creating and handling | |
+a simulation is typically arranged in the following order: | |
+ | |
* Setup of particle assemblage, physical properties and conditions using the | |
Python API. | |
- * Execution of *sphere* software, which simulates the particle behavior as a | |
+ * Execution of ``sphere`` software, which simulates the particle behavior as… | |
function of time, as a result of the conditions initially specified in the | |
input file. | |
- * Inspection, analysis, interpretation and visualization of *sphere* output … | |
- Python, and/or scene rendering using the built-in ray tracer. | |
+ * Inspection, analysis, interpretation and visualization of ``sphere`` output | |
+ in Python, and/or scene rendering using the built-in ray tracer. | |
diff --git a/doc/html/_sources/python_api.txt b/doc/html/_sources/python_api.txt | |
t@@ -1,5 +1,27 @@ | |
Python API | |
========== | |
+The Python module ``sphere`` is intended as the main interface to the ``sphere… | |
+application. It is recommended to use this module for simulation setup, | |
+simulation execution, and analysis of the simulation output data. | |
+ | |
+In order to use the API, the file ``sphere.py`` must be placed in the same | |
+directory as the Python files. | |
+ | |
+Sample usage | |
+------------ | |
+Below is a simple, annotated example of how to setup, execute, and post-process | |
+a ``sphere`` simulation. The example is also found in the ``python/`` folder … | |
+``collision.py``. | |
+ | |
+.. literalinclude:: ../../python/collision.py | |
+ :language: python | |
+ :linenos: | |
+ | |
+The full documentation of the ``sphere`` Python API can be found below. | |
+ | |
+ | |
+The ``sphere`` module | |
+--------------------- | |
.. automodule:: sphere | |
:members: | |
diff --git a/doc/html/_sources/sphere_internals.txt b/doc/html/_sources/sphere_… | |
t@@ -207,8 +207,6 @@ An important note is that the \texttt{C} examples of the N… | |
*sphere* is supplied with several Makefiles, which automate the compilation pr… | |
- | |
- | |
C++ reference | |
------------- | |
.. doxygenclass:: DEM | |
diff --git a/doc/html/_static/jquery.js b/doc/html/_static/jquery.js | |
t@@ -11,7 +11,7 @@ | |
* Copyright 2011, The Dojo Foundation | |
* Released under the MIT, BSD, and GPL Licenses. | |
* | |
- * Date: Thu Nov 15 18:28:24 BRST 2012 | |
+ * Date: Fri Jul 5 14:07:58 UTC 2013 | |
*/ | |
(function( window, undefined ) { | |
diff --git a/doc/html/_static/pygments.css b/doc/html/_static/pygments.css | |
t@@ -13,11 +13,11 @@ | |
.highlight .gr { color: #FF0000 } /* Generic.Error */ | |
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ | |
.highlight .gi { color: #00A000 } /* Generic.Inserted */ | |
-.highlight .go { color: #303030 } /* Generic.Output */ | |
+.highlight .go { color: #333333 } /* Generic.Output */ | |
.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ | |
.highlight .gs { font-weight: bold } /* Generic.Strong */ | |
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ | |
-.highlight .gt { color: #0040D0 } /* Generic.Traceback */ | |
+.highlight .gt { color: #0044DD } /* Generic.Traceback */ | |
.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ | |
.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ | |
.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ | |
diff --git a/doc/html/_static/searchtools.js b/doc/html/_static/searchtools.js | |
t@@ -239,8 +239,13 @@ var Search = { | |
}, | |
loadIndex : function(url) { | |
- $.ajax({type: "GET", url: url, data: null, success: null, | |
- dataType: "script", cache: true}); | |
+ $.ajax({type: "GET", url: url, data: null, | |
+ dataType: "script", cache: true, | |
+ complete: function(jqxhr, textstatus) { | |
+ if (textstatus != "success") { | |
+ document.getElementById("searchindexloader").src = url; | |
+ } | |
+ }}); | |
}, | |
setIndex : function(index) { | |
t@@ -301,7 +306,7 @@ var Search = { | |
}, | |
query : function(query) { | |
- var stopwords = ["and","then","into","it","as","are","in","if","for","no",… | |
+ var stopwords = ["a","and","are","as","at","be","but","by","for","if","in"… | |
// Stem the searchterms and add them to the correct list | |
var stemmer = new Stemmer(); | |
t@@ -457,16 +462,18 @@ var Search = { | |
displayNextItem(); | |
}); | |
} else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) { | |
- $.get(DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' + | |
- item[0] + '.txt', function(data) { | |
- if (data != '') { | |
- listItem.append($.makeSearchSummary(data, searchterms, hlterms)); | |
- Search.output.append(listItem); | |
- } | |
- listItem.slideDown(5, function() { | |
- displayNextItem(); | |
- }); | |
- }, "text"); | |
+ $.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' + item[0] … | |
+ dataType: "text", | |
+ complete: function(jqxhr, textstatus) { | |
+ var data = jqxhr.responseText; | |
+ if (data !== '') { | |
+ listItem.append($.makeSearchSummary(data, searchterms, h… | |
+ } | |
+ Search.output.append(listItem); | |
+ listItem.slideDown(5, function() { | |
+ displayNextItem(); | |
+ }); | |
+ }}); | |
} else { | |
// no source available, just display title | |
Search.output.append(listItem); | |
diff --git a/doc/html/_static/underscore.js b/doc/html/_static/underscore.js | |
t@@ -1,10 +1,7 @@ | |
-// Underscore.js 1.1.6 | |
-// (c) 2011 Jeremy Ashkenas, DocumentCloud Inc. | |
-// Underscore is freely distributable under the MIT license. | |
-// Portions of Underscore are inspired or borrowed from Prototype, | |
-// Oliver Steele's Functional, and John Resig's Micro-Templating. | |
-// For all details and documentation: | |
-// http://documentcloud.github.com/underscore | |
+// Underscore.js 1.4.4 | |
+// http://underscorejs.org | |
+// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. | |
+// Underscore may be freely distributed under the MIT license. | |
(function() { | |
t@@ -24,8 +21,9 @@ | |
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = F… | |
// Create quick reference variables for speed access to core prototypes. | |
- var slice = ArrayProto.slice, | |
- unshift = ArrayProto.unshift, | |
+ var push = ArrayProto.push, | |
+ slice = ArrayProto.slice, | |
+ concat = ArrayProto.concat, | |
toString = ObjProto.toString, | |
hasOwnProperty = ObjProto.hasOwnProperty; | |
t@@ -46,38 +44,45 @@ | |
nativeBind = FuncProto.bind; | |
// Create a safe reference to the Underscore object for use below. | |
- var _ = function(obj) { return new wrapper(obj); }; | |
- | |
- // Export the Underscore object for **CommonJS**, with backwards-compatibili… | |
- // for the old `require()` API. If we're not in CommonJS, add `_` to the | |
- // global object. | |
- if (typeof module !== 'undefined' && module.exports) { | |
- module.exports = _; | |
- _._ = _; | |
+ var _ = function(obj) { | |
+ if (obj instanceof _) return obj; | |
+ if (!(this instanceof _)) return new _(obj); | |
+ this._wrapped = obj; | |
+ }; | |
+ | |
+ // Export the Underscore object for **Node.js**, with | |
+ // backwards-compatibility for the old `require()` API. If we're in | |
+ // the browser, add `_` as a global object via a string identifier, | |
+ // for Closure Compiler "advanced" mode. | |
+ if (typeof exports !== 'undefined') { | |
+ if (typeof module !== 'undefined' && module.exports) { | |
+ exports = module.exports = _; | |
+ } | |
+ exports._ = _; | |
} else { | |
root._ = _; | |
} | |
// Current version. | |
- _.VERSION = '1.1.6'; | |
+ _.VERSION = '1.4.4'; | |
// Collection Functions | |
// -------------------- | |
// The cornerstone, an `each` implementation, aka `forEach`. | |
- // Handles objects implementing `forEach`, arrays, and raw objects. | |
+ // Handles objects with the built-in `forEach`, arrays, and raw objects. | |
// Delegates to **ECMAScript 5**'s native `forEach` if available. | |
var each = _.each = _.forEach = function(obj, iterator, context) { | |
if (obj == null) return; | |
if (nativeForEach && obj.forEach === nativeForEach) { | |
obj.forEach(iterator, context); | |
- } else if (_.isNumber(obj.length)) { | |
+ } else if (obj.length === +obj.length) { | |
for (var i = 0, l = obj.length; i < l; i++) { | |
if (iterator.call(context, obj[i], i, obj) === breaker) return; | |
} | |
} else { | |
for (var key in obj) { | |
- if (hasOwnProperty.call(obj, key)) { | |
+ if (_.has(obj, key)) { | |
if (iterator.call(context, obj[key], key, obj) === breaker) return; | |
} | |
} | |
t@@ -86,7 +91,7 @@ | |
// Return the results of applying the iterator to each element. | |
// Delegates to **ECMAScript 5**'s native `map` if available. | |
- _.map = function(obj, iterator, context) { | |
+ _.map = _.collect = function(obj, iterator, context) { | |
var results = []; | |
if (obj == null) return results; | |
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); | |
t@@ -96,37 +101,54 @@ | |
return results; | |
}; | |
+ var reduceError = 'Reduce of empty array with no initial value'; | |
+ | |
// **Reduce** builds up a single result from a list of values, aka `inject`, | |
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. | |
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { | |
- var initial = memo !== void 0; | |
+ var initial = arguments.length > 2; | |
if (obj == null) obj = []; | |
if (nativeReduce && obj.reduce === nativeReduce) { | |
if (context) iterator = _.bind(iterator, context); | |
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); | |
} | |
each(obj, function(value, index, list) { | |
- if (!initial && index === 0) { | |
+ if (!initial) { | |
memo = value; | |
initial = true; | |
} else { | |
memo = iterator.call(context, memo, value, index, list); | |
} | |
}); | |
- if (!initial) throw new TypeError("Reduce of empty array with no initial v… | |
+ if (!initial) throw new TypeError(reduceError); | |
return memo; | |
}; | |
// The right-associative version of reduce, also known as `foldr`. | |
// Delegates to **ECMAScript 5**'s native `reduceRight` if available. | |
_.reduceRight = _.foldr = function(obj, iterator, memo, context) { | |
+ var initial = arguments.length > 2; | |
if (obj == null) obj = []; | |
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { | |
if (context) iterator = _.bind(iterator, context); | |
- return memo !== void 0 ? obj.reduceRight(iterator, memo) : obj.reduceRig… | |
+ return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(itera… | |
+ } | |
+ var length = obj.length; | |
+ if (length !== +length) { | |
+ var keys = _.keys(obj); | |
+ length = keys.length; | |
} | |
- var reversed = (_.isArray(obj) ? obj.slice() : _.toArray(obj)).reverse(); | |
- return _.reduce(reversed, iterator, memo, context); | |
+ each(obj, function(value, index, list) { | |
+ index = keys ? keys[--length] : --length; | |
+ if (!initial) { | |
+ memo = obj[index]; | |
+ initial = true; | |
+ } else { | |
+ memo = iterator.call(context, memo, obj[index], index, list); | |
+ } | |
+ }); | |
+ if (!initial) throw new TypeError(reduceError); | |
+ return memo; | |
}; | |
// Return the first value which passes a truth test. Aliased as `detect`. | |
t@@ -156,25 +178,23 @@ | |
// Return all the elements for which a truth test fails. | |
_.reject = function(obj, iterator, context) { | |
- var results = []; | |
- if (obj == null) return results; | |
- each(obj, function(value, index, list) { | |
- if (!iterator.call(context, value, index, list)) results[results.length]… | |
- }); | |
- return results; | |
+ return _.filter(obj, function(value, index, list) { | |
+ return !iterator.call(context, value, index, list); | |
+ }, context); | |
}; | |
// Determine whether all of the elements match a truth test. | |
// Delegates to **ECMAScript 5**'s native `every` if available. | |
// Aliased as `all`. | |
_.every = _.all = function(obj, iterator, context) { | |
+ iterator || (iterator = _.identity); | |
var result = true; | |
if (obj == null) return result; | |
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, c… | |
each(obj, function(value, index, list) { | |
if (!(result = result && iterator.call(context, value, index, list))) re… | |
}); | |
- return result; | |
+ return !!result; | |
}; | |
// Determine if at least one element in the object matches a truth test. | |
t@@ -186,28 +206,27 @@ | |
if (obj == null) return result; | |
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, conte… | |
each(obj, function(value, index, list) { | |
- if (result = iterator.call(context, value, index, list)) return breaker; | |
+ if (result || (result = iterator.call(context, value, index, list))) ret… | |
}); | |
- return result; | |
+ return !!result; | |
}; | |
- // Determine if a given value is included in the array or object using `===`. | |
- // Aliased as `contains`. | |
- _.include = _.contains = function(obj, target) { | |
- var found = false; | |
- if (obj == null) return found; | |
+ // Determine if the array or object contains a given value (using `===`). | |
+ // Aliased as `include`. | |
+ _.contains = _.include = function(obj, target) { | |
+ if (obj == null) return false; | |
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(tar… | |
- any(obj, function(value) { | |
- if (found = value === target) return true; | |
+ return any(obj, function(value) { | |
+ return value === target; | |
}); | |
- return found; | |
}; | |
// Invoke a method (with arguments) on every item in a collection. | |
_.invoke = function(obj, method) { | |
var args = slice.call(arguments, 2); | |
+ var isFunc = _.isFunction(method); | |
return _.map(obj, function(value) { | |
- return (method.call ? method || value : value[method]).apply(value, args… | |
+ return (isFunc ? method : value[method]).apply(value, args); | |
}); | |
}; | |
t@@ -216,10 +235,33 @@ | |
return _.map(obj, function(value){ return value[key]; }); | |
}; | |
+ // Convenience version of a common use case of `filter`: selecting only obje… | |
+ // containing specific `key:value` pairs. | |
+ _.where = function(obj, attrs, first) { | |
+ if (_.isEmpty(attrs)) return first ? null : []; | |
+ return _[first ? 'find' : 'filter'](obj, function(value) { | |
+ for (var key in attrs) { | |
+ if (attrs[key] !== value[key]) return false; | |
+ } | |
+ return true; | |
+ }); | |
+ }; | |
+ | |
+ // Convenience version of a common use case of `find`: getting the first obj… | |
+ // containing specific `key:value` pairs. | |
+ _.findWhere = function(obj, attrs) { | |
+ return _.where(obj, attrs, true); | |
+ }; | |
+ | |
// Return the maximum element or (element-based computation). | |
+ // Can't optimize arrays of integers longer than 65,535 elements. | |
+ // See: https://bugs.webkit.org/show_bug.cgi?id=80797 | |
_.max = function(obj, iterator, context) { | |
- if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj); | |
- var result = {computed : -Infinity}; | |
+ if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 6553… | |
+ return Math.max.apply(Math, obj); | |
+ } | |
+ if (!iterator && _.isEmpty(obj)) return -Infinity; | |
+ var result = {computed : -Infinity, value: -Infinity}; | |
each(obj, function(value, index, list) { | |
var computed = iterator ? iterator.call(context, value, index, list) : v… | |
computed >= result.computed && (result = {value : value, computed : comp… | |
t@@ -229,8 +271,11 @@ | |
// Return the minimum element (or element-based computation). | |
_.min = function(obj, iterator, context) { | |
- if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj); | |
- var result = {computed : Infinity}; | |
+ if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 6553… | |
+ return Math.min.apply(Math, obj); | |
+ } | |
+ if (!iterator && _.isEmpty(obj)) return Infinity; | |
+ var result = {computed : Infinity, value: Infinity}; | |
each(obj, function(value, index, list) { | |
var computed = iterator ? iterator.call(context, value, index, list) : v… | |
computed < result.computed && (result = {value : value, computed : compu… | |
t@@ -238,101 +283,195 @@ | |
return result.value; | |
}; | |
+ // Shuffle an array. | |
+ _.shuffle = function(obj) { | |
+ var rand; | |
+ var index = 0; | |
+ var shuffled = []; | |
+ each(obj, function(value) { | |
+ rand = _.random(index++); | |
+ shuffled[index - 1] = shuffled[rand]; | |
+ shuffled[rand] = value; | |
+ }); | |
+ return shuffled; | |
+ }; | |
+ | |
+ // An internal function to generate lookup iterators. | |
+ var lookupIterator = function(value) { | |
+ return _.isFunction(value) ? value : function(obj){ return obj[value]; }; | |
+ }; | |
+ | |
// Sort the object's values by a criterion produced by an iterator. | |
- _.sortBy = function(obj, iterator, context) { | |
+ _.sortBy = function(obj, value, context) { | |
+ var iterator = lookupIterator(value); | |
return _.pluck(_.map(obj, function(value, index, list) { | |
return { | |
value : value, | |
+ index : index, | |
criteria : iterator.call(context, value, index, list) | |
}; | |
}).sort(function(left, right) { | |
- var a = left.criteria, b = right.criteria; | |
- return a < b ? -1 : a > b ? 1 : 0; | |
+ var a = left.criteria; | |
+ var b = right.criteria; | |
+ if (a !== b) { | |
+ if (a > b || a === void 0) return 1; | |
+ if (a < b || b === void 0) return -1; | |
+ } | |
+ return left.index < right.index ? -1 : 1; | |
}), 'value'); | |
}; | |
- // Use a comparator function to figure out at what index an object should | |
- // be inserted so as to maintain order. Uses binary search. | |
- _.sortedIndex = function(array, obj, iterator) { | |
- iterator || (iterator = _.identity); | |
+ // An internal function used for aggregate "group by" operations. | |
+ var group = function(obj, value, context, behavior) { | |
+ var result = {}; | |
+ var iterator = lookupIterator(value || _.identity); | |
+ each(obj, function(value, index) { | |
+ var key = iterator.call(context, value, index, obj); | |
+ behavior(result, key, value); | |
+ }); | |
+ return result; | |
+ }; | |
+ | |
+ // Groups the object's values by a criterion. Pass either a string attribute | |
+ // to group by, or a function that returns the criterion. | |
+ _.groupBy = function(obj, value, context) { | |
+ return group(obj, value, context, function(result, key, value) { | |
+ (_.has(result, key) ? result[key] : (result[key] = [])).push(value); | |
+ }); | |
+ }; | |
+ | |
+ // Counts instances of an object that group by a certain criterion. Pass | |
+ // either a string attribute to count by, or a function that returns the | |
+ // criterion. | |
+ _.countBy = function(obj, value, context) { | |
+ return group(obj, value, context, function(result, key) { | |
+ if (!_.has(result, key)) result[key] = 0; | |
+ result[key]++; | |
+ }); | |
+ }; | |
+ | |
+ // Use a comparator function to figure out the smallest index at which | |
+ // an object should be inserted so as to maintain order. Uses binary search. | |
+ _.sortedIndex = function(array, obj, iterator, context) { | |
+ iterator = iterator == null ? _.identity : lookupIterator(iterator); | |
+ var value = iterator.call(context, obj); | |
var low = 0, high = array.length; | |
while (low < high) { | |
- var mid = (low + high) >> 1; | |
- iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid; | |
+ var mid = (low + high) >>> 1; | |
+ iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; | |
} | |
return low; | |
}; | |
// Safely convert anything iterable into a real, live array. | |
- _.toArray = function(iterable) { | |
- if (!iterable) return []; | |
- if (iterable.toArray) return iterable.toArray(); | |
- if (_.isArray(iterable)) return iterable; | |
- if (_.isArguments(iterable)) return slice.call(iterable); | |
- return _.values(iterable); | |
+ _.toArray = function(obj) { | |
+ if (!obj) return []; | |
+ if (_.isArray(obj)) return slice.call(obj); | |
+ if (obj.length === +obj.length) return _.map(obj, _.identity); | |
+ return _.values(obj); | |
}; | |
// Return the number of elements in an object. | |
_.size = function(obj) { | |
- return _.toArray(obj).length; | |
+ if (obj == null) return 0; | |
+ return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; | |
}; | |
// Array Functions | |
// --------------- | |
// Get the first element of an array. Passing **n** will return the first N | |
- // values in the array. Aliased as `head`. The **guard** check allows it to … | |
- // with `_.map`. | |
- _.first = _.head = function(array, n, guard) { | |
+ // values in the array. Aliased as `head` and `take`. The **guard** check | |
+ // allows it to work with `_.map`. | |
+ _.first = _.head = _.take = function(array, n, guard) { | |
+ if (array == null) return void 0; | |
return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; | |
}; | |
- // Returns everything but the first entry of the array. Aliased as `tail`. | |
- // Especially useful on the arguments object. Passing an **index** will retu… | |
- // the rest of the values in the array from that index onward. The **guard** | |
- // check allows it to work with `_.map`. | |
- _.rest = _.tail = function(array, index, guard) { | |
- return slice.call(array, (index == null) || guard ? 1 : index); | |
+ // Returns everything but the last entry of the array. Especially useful on | |
+ // the arguments object. Passing **n** will return all the values in | |
+ // the array, excluding the last N. The **guard** check allows it to work wi… | |
+ // `_.map`. | |
+ _.initial = function(array, n, guard) { | |
+ return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); | |
+ }; | |
+ | |
+ // Get the last element of an array. Passing **n** will return the last N | |
+ // values in the array. The **guard** check allows it to work with `_.map`. | |
+ _.last = function(array, n, guard) { | |
+ if (array == null) return void 0; | |
+ if ((n != null) && !guard) { | |
+ return slice.call(array, Math.max(array.length - n, 0)); | |
+ } else { | |
+ return array[array.length - 1]; | |
+ } | |
}; | |
- // Get the last element of an array. | |
- _.last = function(array) { | |
- return array[array.length - 1]; | |
+ // Returns everything but the first entry of the array. Aliased as `tail` an… | |
+ // Especially useful on the arguments object. Passing an **n** will return | |
+ // the rest N values in the array. The **guard** | |
+ // check allows it to work with `_.map`. | |
+ _.rest = _.tail = _.drop = function(array, n, guard) { | |
+ return slice.call(array, (n == null) || guard ? 1 : n); | |
}; | |
// Trim out all falsy values from an array. | |
_.compact = function(array) { | |
- return _.filter(array, function(value){ return !!value; }); | |
+ return _.filter(array, _.identity); | |
+ }; | |
+ | |
+ // Internal implementation of a recursive `flatten` function. | |
+ var flatten = function(input, shallow, output) { | |
+ each(input, function(value) { | |
+ if (_.isArray(value)) { | |
+ shallow ? push.apply(output, value) : flatten(value, shallow, output); | |
+ } else { | |
+ output.push(value); | |
+ } | |
+ }); | |
+ return output; | |
}; | |
// Return a completely flattened version of an array. | |
- _.flatten = function(array) { | |
- return _.reduce(array, function(memo, value) { | |
- if (_.isArray(value)) return memo.concat(_.flatten(value)); | |
- memo[memo.length] = value; | |
- return memo; | |
- }, []); | |
+ _.flatten = function(array, shallow) { | |
+ return flatten(array, shallow, []); | |
}; | |
// Return a version of the array that does not contain the specified value(s… | |
_.without = function(array) { | |
- var values = slice.call(arguments, 1); | |
- return _.filter(array, function(value){ return !_.include(values, value); … | |
+ return _.difference(array, slice.call(arguments, 1)); | |
}; | |
// Produce a duplicate-free version of the array. If the array has already | |
// been sorted, you have the option of using a faster algorithm. | |
// Aliased as `unique`. | |
- _.uniq = _.unique = function(array, isSorted) { | |
- return _.reduce(array, function(memo, el, i) { | |
- if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo,… | |
- return memo; | |
- }, []); | |
+ _.uniq = _.unique = function(array, isSorted, iterator, context) { | |
+ if (_.isFunction(isSorted)) { | |
+ context = iterator; | |
+ iterator = isSorted; | |
+ isSorted = false; | |
+ } | |
+ var initial = iterator ? _.map(array, iterator, context) : array; | |
+ var results = []; | |
+ var seen = []; | |
+ each(initial, function(value, index) { | |
+ if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains… | |
+ seen.push(value); | |
+ results.push(array[index]); | |
+ } | |
+ }); | |
+ return results; | |
+ }; | |
+ | |
+ // Produce an array that contains the union: each distinct element from all … | |
+ // the passed-in arrays. | |
+ _.union = function() { | |
+ return _.uniq(concat.apply(ArrayProto, arguments)); | |
}; | |
// Produce an array that contains every item shared between all the | |
// passed-in arrays. | |
- _.intersect = function(array) { | |
+ _.intersection = function(array) { | |
var rest = slice.call(arguments, 1); | |
return _.filter(_.uniq(array), function(item) { | |
return _.every(rest, function(other) { | |
t@@ -341,16 +480,41 @@ | |
}); | |
}; | |
+ // Take the difference between one array and a number of other arrays. | |
+ // Only the elements present in just the first array will remain. | |
+ _.difference = function(array) { | |
+ var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); | |
+ return _.filter(array, function(value){ return !_.contains(rest, value); }… | |
+ }; | |
+ | |
// Zip together multiple lists into a single array -- elements that share | |
// an index go together. | |
_.zip = function() { | |
var args = slice.call(arguments); | |
var length = _.max(_.pluck(args, 'length')); | |
var results = new Array(length); | |
- for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i); | |
+ for (var i = 0; i < length; i++) { | |
+ results[i] = _.pluck(args, "" + i); | |
+ } | |
return results; | |
}; | |
+ // Converts lists into objects. Pass either a single array of `[key, value]` | |
+ // pairs, or two parallel arrays of the same length -- one of keys, and one … | |
+ // the corresponding values. | |
+ _.object = function(list, values) { | |
+ if (list == null) return {}; | |
+ var result = {}; | |
+ for (var i = 0, l = list.length; i < l; i++) { | |
+ if (values) { | |
+ result[list[i]] = values[i]; | |
+ } else { | |
+ result[list[i][0]] = list[i][1]; | |
+ } | |
+ } | |
+ return result; | |
+ }; | |
+ | |
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE… | |
// we need this function. Return the position of the first occurrence of an | |
// item in an array, or -1 if the item is not included in the array. | |
t@@ -359,22 +523,28 @@ | |
// for **isSorted** to use binary search. | |
_.indexOf = function(array, item, isSorted) { | |
if (array == null) return -1; | |
- var i, l; | |
+ var i = 0, l = array.length; | |
if (isSorted) { | |
- i = _.sortedIndex(array, item); | |
- return array[i] === item ? i : -1; | |
+ if (typeof isSorted == 'number') { | |
+ i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); | |
+ } else { | |
+ i = _.sortedIndex(array, item); | |
+ return array[i] === item ? i : -1; | |
+ } | |
} | |
- if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf… | |
- for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i; | |
+ if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf… | |
+ for (; i < l; i++) if (array[i] === item) return i; | |
return -1; | |
}; | |
- | |
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. | |
- _.lastIndexOf = function(array, item) { | |
+ _.lastIndexOf = function(array, item, from) { | |
if (array == null) return -1; | |
- if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return a… | |
- var i = array.length; | |
+ var hasIndex = from != null; | |
+ if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { | |
+ return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item… | |
+ } | |
+ var i = (hasIndex ? from : array.length); | |
while (i--) if (array[i] === item) return i; | |
return -1; | |
}; | |
t@@ -405,14 +575,22 @@ | |
// ------------------ | |
// Create a function bound to a given object (assigning `this`, and argument… | |
- // optionally). Binding with arguments is also known as `curry`. | |
- // Delegates to **ECMAScript 5**'s native `Function.bind` if available. | |
- // We check for `func.bind` first, to fail fast when `func` is undefined. | |
- _.bind = function(func, obj) { | |
+ // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if | |
+ // available. | |
+ _.bind = function(func, context) { | |
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, … | |
var args = slice.call(arguments, 2); | |
return function() { | |
- return func.apply(obj, args.concat(slice.call(arguments))); | |
+ return func.apply(context, args.concat(slice.call(arguments))); | |
+ }; | |
+ }; | |
+ | |
+ // Partially apply a function by creating a version that has had some of its | |
+ // arguments pre-filled, without changing its dynamic `this` context. | |
+ _.partial = function(func) { | |
+ var args = slice.call(arguments, 1); | |
+ return function() { | |
+ return func.apply(this, args.concat(slice.call(arguments))); | |
}; | |
}; | |
t@@ -420,7 +598,7 @@ | |
// all callbacks defined on an object belong to it. | |
_.bindAll = function(obj) { | |
var funcs = slice.call(arguments, 1); | |
- if (funcs.length == 0) funcs = _.functions(obj); | |
+ if (funcs.length === 0) funcs = _.functions(obj); | |
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); | |
return obj; | |
}; | |
t@@ -431,7 +609,7 @@ | |
hasher || (hasher = _.identity); | |
return function() { | |
var key = hasher.apply(this, arguments); | |
- return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.ap… | |
+ return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, argu… | |
}; | |
}; | |
t@@ -439,7 +617,7 @@ | |
// it with the arguments supplied. | |
_.delay = function(func, wait) { | |
var args = slice.call(arguments, 2); | |
- return setTimeout(function(){ return func.apply(func, args); }, wait); | |
+ return setTimeout(function(){ return func.apply(null, args); }, wait); | |
}; | |
// Defers a function, scheduling it to run after the current call stack has | |
t@@ -448,31 +626,51 @@ | |
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); | |
}; | |
- // Internal function used to implement `_.throttle` and `_.debounce`. | |
- var limit = function(func, wait, debounce) { | |
- var timeout; | |
- return function() { | |
- var context = this, args = arguments; | |
- var throttler = function() { | |
- timeout = null; | |
- func.apply(context, args); | |
- }; | |
- if (debounce) clearTimeout(timeout); | |
- if (debounce || !timeout) timeout = setTimeout(throttler, wait); | |
- }; | |
- }; | |
- | |
// Returns a function, that, when invoked, will only be triggered at most on… | |
// during a given window of time. | |
_.throttle = function(func, wait) { | |
- return limit(func, wait, false); | |
+ var context, args, timeout, result; | |
+ var previous = 0; | |
+ var later = function() { | |
+ previous = new Date; | |
+ timeout = null; | |
+ result = func.apply(context, args); | |
+ }; | |
+ return function() { | |
+ var now = new Date; | |
+ var remaining = wait - (now - previous); | |
+ context = this; | |
+ args = arguments; | |
+ if (remaining <= 0) { | |
+ clearTimeout(timeout); | |
+ timeout = null; | |
+ previous = now; | |
+ result = func.apply(context, args); | |
+ } else if (!timeout) { | |
+ timeout = setTimeout(later, remaining); | |
+ } | |
+ return result; | |
+ }; | |
}; | |
// Returns a function, that, as long as it continues to be invoked, will not | |
// be triggered. The function will be called after it stops being called for | |
- // N milliseconds. | |
- _.debounce = function(func, wait) { | |
- return limit(func, wait, true); | |
+ // N milliseconds. If `immediate` is passed, trigger the function on the | |
+ // leading edge, instead of the trailing. | |
+ _.debounce = function(func, wait, immediate) { | |
+ var timeout, result; | |
+ return function() { | |
+ var context = this, args = arguments; | |
+ var later = function() { | |
+ timeout = null; | |
+ if (!immediate) result = func.apply(context, args); | |
+ }; | |
+ var callNow = immediate && !timeout; | |
+ clearTimeout(timeout); | |
+ timeout = setTimeout(later, wait); | |
+ if (callNow) result = func.apply(context, args); | |
+ return result; | |
+ }; | |
}; | |
// Returns a function that will be executed at most one time, no matter how | |
t@@ -482,7 +680,9 @@ | |
return function() { | |
if (ran) return memo; | |
ran = true; | |
- return memo = func.apply(this, arguments); | |
+ memo = func.apply(this, arguments); | |
+ func = null; | |
+ return memo; | |
}; | |
}; | |
t@@ -491,7 +691,8 @@ | |
// conditionally execute the original function. | |
_.wrap = function(func, wrapper) { | |
return function() { | |
- var args = [func].concat(slice.call(arguments)); | |
+ var args = [func]; | |
+ push.apply(args, arguments); | |
return wrapper.apply(this, args); | |
}; | |
}; | |
t@@ -499,10 +700,10 @@ | |
// Returns a function that is the composition of a list of functions, each | |
// consuming the return value of the function that follows. | |
_.compose = function() { | |
- var funcs = slice.call(arguments); | |
+ var funcs = arguments; | |
return function() { | |
- var args = slice.call(arguments); | |
- for (var i=funcs.length-1; i >= 0; i--) { | |
+ var args = arguments; | |
+ for (var i = funcs.length - 1; i >= 0; i--) { | |
args = [funcs[i].apply(this, args)]; | |
} | |
return args[0]; | |
t@@ -511,12 +712,14 @@ | |
// Returns a function that will only be executed after being called N times. | |
_.after = function(times, func) { | |
+ if (times <= 0) return func(); | |
return function() { | |
- if (--times < 1) { return func.apply(this, arguments); } | |
+ if (--times < 1) { | |
+ return func.apply(this, arguments); | |
+ } | |
}; | |
}; | |
- | |
// Object Functions | |
// ---------------- | |
t@@ -525,36 +728,80 @@ | |
_.keys = nativeKeys || function(obj) { | |
if (obj !== Object(obj)) throw new TypeError('Invalid object'); | |
var keys = []; | |
- for (var key in obj) if (hasOwnProperty.call(obj, key)) keys[keys.length] … | |
+ for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; | |
return keys; | |
}; | |
// Retrieve the values of an object's properties. | |
_.values = function(obj) { | |
- return _.map(obj, _.identity); | |
+ var values = []; | |
+ for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); | |
+ return values; | |
+ }; | |
+ | |
+ // Convert an object into a list of `[key, value]` pairs. | |
+ _.pairs = function(obj) { | |
+ var pairs = []; | |
+ for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); | |
+ return pairs; | |
+ }; | |
+ | |
+ // Invert the keys and values of an object. The values must be serializable. | |
+ _.invert = function(obj) { | |
+ var result = {}; | |
+ for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; | |
+ return result; | |
}; | |
// Return a sorted list of the function names available on the object. | |
// Aliased as `methods` | |
_.functions = _.methods = function(obj) { | |
- return _.filter(_.keys(obj), function(key){ return _.isFunction(obj[key]);… | |
+ var names = []; | |
+ for (var key in obj) { | |
+ if (_.isFunction(obj[key])) names.push(key); | |
+ } | |
+ return names.sort(); | |
}; | |
// Extend a given object with all the properties in passed-in object(s). | |
_.extend = function(obj) { | |
each(slice.call(arguments, 1), function(source) { | |
- for (var prop in source) { | |
- if (source[prop] !== void 0) obj[prop] = source[prop]; | |
+ if (source) { | |
+ for (var prop in source) { | |
+ obj[prop] = source[prop]; | |
+ } | |
} | |
}); | |
return obj; | |
}; | |
+ // Return a copy of the object only containing the whitelisted properties. | |
+ _.pick = function(obj) { | |
+ var copy = {}; | |
+ var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); | |
+ each(keys, function(key) { | |
+ if (key in obj) copy[key] = obj[key]; | |
+ }); | |
+ return copy; | |
+ }; | |
+ | |
+ // Return a copy of the object without the blacklisted properties. | |
+ _.omit = function(obj) { | |
+ var copy = {}; | |
+ var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); | |
+ for (var key in obj) { | |
+ if (!_.contains(keys, key)) copy[key] = obj[key]; | |
+ } | |
+ return copy; | |
+ }; | |
+ | |
// Fill in a given object with default properties. | |
_.defaults = function(obj) { | |
each(slice.call(arguments, 1), function(source) { | |
- for (var prop in source) { | |
- if (obj[prop] == null) obj[prop] = source[prop]; | |
+ if (source) { | |
+ for (var prop in source) { | |
+ if (obj[prop] == null) obj[prop] = source[prop]; | |
+ } | |
} | |
}); | |
return obj; | |
t@@ -562,6 +809,7 @@ | |
// Create a (shallow-cloned) duplicate of an object. | |
_.clone = function(obj) { | |
+ if (!_.isObject(obj)) return obj; | |
return _.isArray(obj) ? obj.slice() : _.extend({}, obj); | |
}; | |
t@@ -573,102 +821,162 @@ | |
return obj; | |
}; | |
+ // Internal recursive comparison function for `isEqual`. | |
+ var eq = function(a, b, aStack, bStack) { | |
+ // Identical objects are equal. `0 === -0`, but they aren't identical. | |
+ // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id… | |
+ if (a === b) return a !== 0 || 1 / a == 1 / b; | |
+ // A strict comparison is necessary because `null == undefined`. | |
+ if (a == null || b == null) return a === b; | |
+ // Unwrap any wrapped objects. | |
+ if (a instanceof _) a = a._wrapped; | |
+ if (b instanceof _) b = b._wrapped; | |
+ // Compare `[[Class]]` names. | |
+ var className = toString.call(a); | |
+ if (className != toString.call(b)) return false; | |
+ switch (className) { | |
+ // Strings, numbers, dates, and booleans are compared by value. | |
+ case '[object String]': | |
+ // Primitives and their corresponding object wrappers are equivalent; … | |
+ // equivalent to `new String("5")`. | |
+ return a == String(b); | |
+ case '[object Number]': | |
+ // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is p… | |
+ // other numeric values. | |
+ return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); | |
+ case '[object Date]': | |
+ case '[object Boolean]': | |
+ // Coerce dates and booleans to numeric primitive values. Dates are co… | |
+ // millisecond representations. Note that invalid dates with milliseco… | |
+ // of `NaN` are not equivalent. | |
+ return +a == +b; | |
+ // RegExps are compared by their source patterns and flags. | |
+ case '[object RegExp]': | |
+ return a.source == b.source && | |
+ a.global == b.global && | |
+ a.multiline == b.multiline && | |
+ a.ignoreCase == b.ignoreCase; | |
+ } | |
+ if (typeof a != 'object' || typeof b != 'object') return false; | |
+ // Assume equality for cyclic structures. The algorithm for detecting cycl… | |
+ // structures is adapted from ES 5.1 section 15.12.3, abstract operation `… | |
+ var length = aStack.length; | |
+ while (length--) { | |
+ // Linear search. Performance is inversely proportional to the number of | |
+ // unique nested structures. | |
+ if (aStack[length] == a) return bStack[length] == b; | |
+ } | |
+ // Add the first object to the stack of traversed objects. | |
+ aStack.push(a); | |
+ bStack.push(b); | |
+ var size = 0, result = true; | |
+ // Recursively compare objects and arrays. | |
+ if (className == '[object Array]') { | |
+ // Compare array lengths to determine if a deep comparison is necessary. | |
+ size = a.length; | |
+ result = size == b.length; | |
+ if (result) { | |
+ // Deep compare the contents, ignoring non-numeric properties. | |
+ while (size--) { | |
+ if (!(result = eq(a[size], b[size], aStack, bStack))) break; | |
+ } | |
+ } | |
+ } else { | |
+ // Objects with different constructors are not equivalent, but `Object`s | |
+ // from different frames are. | |
+ var aCtor = a.constructor, bCtor = b.constructor; | |
+ if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor)… | |
+ _.isFunction(bCtor) && (bCtor instanceof bCtor)… | |
+ return false; | |
+ } | |
+ // Deep compare objects. | |
+ for (var key in a) { | |
+ if (_.has(a, key)) { | |
+ // Count the expected number of properties. | |
+ size++; | |
+ // Deep compare each member. | |
+ if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack)))… | |
+ } | |
+ } | |
+ // Ensure that both objects contain the same number of properties. | |
+ if (result) { | |
+ for (key in b) { | |
+ if (_.has(b, key) && !(size--)) break; | |
+ } | |
+ result = !size; | |
+ } | |
+ } | |
+ // Remove the first object from the stack of traversed objects. | |
+ aStack.pop(); | |
+ bStack.pop(); | |
+ return result; | |
+ }; | |
+ | |
// Perform a deep comparison to check if two objects are equal. | |
_.isEqual = function(a, b) { | |
- // Check object identity. | |
- if (a === b) return true; | |
- // Different types? | |
- var atype = typeof(a), btype = typeof(b); | |
- if (atype != btype) return false; | |
- // Basic equality test (watch out for coercions). | |
- if (a == b) return true; | |
- // One is falsy and the other truthy. | |
- if ((!a && b) || (a && !b)) return false; | |
- // Unwrap any wrapped objects. | |
- if (a._chain) a = a._wrapped; | |
- if (b._chain) b = b._wrapped; | |
- // One of them implements an isEqual()? | |
- if (a.isEqual) return a.isEqual(b); | |
- // Check dates' integer values. | |
- if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime(); | |
- // Both are NaN? | |
- if (_.isNaN(a) && _.isNaN(b)) return false; | |
- // Compare regular expressions. | |
- if (_.isRegExp(a) && _.isRegExp(b)) | |
- return a.source === b.source && | |
- a.global === b.global && | |
- a.ignoreCase === b.ignoreCase && | |
- a.multiline === b.multiline; | |
- // If a is not an object by this point, we can't handle it. | |
- if (atype !== 'object') return false; | |
- // Check for different array lengths before comparing contents. | |
- if (a.length && (a.length !== b.length)) return false; | |
- // Nothing else worked, deep compare the contents. | |
- var aKeys = _.keys(a), bKeys = _.keys(b); | |
- // Different object sizes? | |
- if (aKeys.length != bKeys.length) return false; | |
- // Recursive comparison of contents. | |
- for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return f… | |
- return true; | |
+ return eq(a, b, [], []); | |
}; | |
- // Is a given array or object empty? | |
+ // Is a given array, string, or object empty? | |
+ // An "empty" object has no enumerable own-properties. | |
_.isEmpty = function(obj) { | |
+ if (obj == null) return true; | |
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; | |
- for (var key in obj) if (hasOwnProperty.call(obj, key)) return false; | |
+ for (var key in obj) if (_.has(obj, key)) return false; | |
return true; | |
}; | |
// Is a given value a DOM element? | |
_.isElement = function(obj) { | |
- return !!(obj && obj.nodeType == 1); | |
+ return !!(obj && obj.nodeType === 1); | |
}; | |
// Is a given value an array? | |
// Delegates to ECMA5's native Array.isArray | |
_.isArray = nativeIsArray || function(obj) { | |
- return toString.call(obj) === '[object Array]'; | |
+ return toString.call(obj) == '[object Array]'; | |
}; | |
- // Is a given variable an arguments object? | |
- _.isArguments = function(obj) { | |
- return !!(obj && hasOwnProperty.call(obj, 'callee')); | |
+ // Is a given variable an object? | |
+ _.isObject = function(obj) { | |
+ return obj === Object(obj); | |
}; | |
- // Is a given value a function? | |
- _.isFunction = function(obj) { | |
- return !!(obj && obj.constructor && obj.call && obj.apply); | |
- }; | |
+ // Add some isType methods: isArguments, isFunction, isString, isNumber, isD… | |
+ each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], functi… | |
+ _['is' + name] = function(obj) { | |
+ return toString.call(obj) == '[object ' + name + ']'; | |
+ }; | |
+ }); | |
- // Is a given value a string? | |
- _.isString = function(obj) { | |
- return !!(obj === '' || (obj && obj.charCodeAt && obj.substr)); | |
- }; | |
+ // Define a fallback version of the method in browsers (ahem, IE), where | |
+ // there isn't any inspectable "Arguments" type. | |
+ if (!_.isArguments(arguments)) { | |
+ _.isArguments = function(obj) { | |
+ return !!(obj && _.has(obj, 'callee')); | |
+ }; | |
+ } | |
+ | |
+ // Optimize `isFunction` if appropriate. | |
+ if (typeof (/./) !== 'function') { | |
+ _.isFunction = function(obj) { | |
+ return typeof obj === 'function'; | |
+ }; | |
+ } | |
- // Is a given value a number? | |
- _.isNumber = function(obj) { | |
- return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed)); | |
+ // Is a given object a finite number? | |
+ _.isFinite = function(obj) { | |
+ return isFinite(obj) && !isNaN(parseFloat(obj)); | |
}; | |
- // Is the given value `NaN`? `NaN` happens to be the only value in JavaScript | |
- // that does not equal itself. | |
+ // Is the given value `NaN`? (NaN is the only number which does not equal it… | |
_.isNaN = function(obj) { | |
- return obj !== obj; | |
+ return _.isNumber(obj) && obj != +obj; | |
}; | |
// Is a given value a boolean? | |
_.isBoolean = function(obj) { | |
- return obj === true || obj === false; | |
- }; | |
- | |
- // Is a given value a date? | |
- _.isDate = function(obj) { | |
- return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear); | |
- }; | |
- | |
- // Is the given value a regular expression? | |
- _.isRegExp = function(obj) { | |
- return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase… | |
+ return obj === true || obj === false || toString.call(obj) == '[object Boo… | |
}; | |
// Is a given value equal to null? | |
t@@ -681,6 +989,12 @@ | |
return obj === void 0; | |
}; | |
+ // Shortcut function for checking if an object has a given property directly | |
+ // on itself (in other words, not on a prototype). | |
+ _.has = function(obj, key) { | |
+ return hasOwnProperty.call(obj, key); | |
+ }; | |
+ | |
// Utility Functions | |
// ----------------- | |
t@@ -697,15 +1011,67 @@ | |
}; | |
// Run a function **n** times. | |
- _.times = function (n, iterator, context) { | |
- for (var i = 0; i < n; i++) iterator.call(context, i); | |
+ _.times = function(n, iterator, context) { | |
+ var accum = Array(n); | |
+ for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); | |
+ return accum; | |
+ }; | |
+ | |
+ // Return a random integer between min and max (inclusive). | |
+ _.random = function(min, max) { | |
+ if (max == null) { | |
+ max = min; | |
+ min = 0; | |
+ } | |
+ return min + Math.floor(Math.random() * (max - min + 1)); | |
+ }; | |
+ | |
+ // List of HTML entities for escaping. | |
+ var entityMap = { | |
+ escape: { | |
+ '&': '&', | |
+ '<': '<', | |
+ '>': '>', | |
+ '"': '"', | |
+ "'": ''', | |
+ '/': '/' | |
+ } | |
+ }; | |
+ entityMap.unescape = _.invert(entityMap.escape); | |
+ | |
+ // Regexes containing the keys and values listed immediately above. | |
+ var entityRegexes = { | |
+ escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), | |
+ unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') | |
+ }; | |
+ | |
+ // Functions for escaping and unescaping strings to/from HTML interpolation. | |
+ _.each(['escape', 'unescape'], function(method) { | |
+ _[method] = function(string) { | |
+ if (string == null) return ''; | |
+ return ('' + string).replace(entityRegexes[method], function(match) { | |
+ return entityMap[method][match]; | |
+ }); | |
+ }; | |
+ }); | |
+ | |
+ // If the value of the named property is a function then invoke it; | |
+ // otherwise, return it. | |
+ _.result = function(object, property) { | |
+ if (object == null) return null; | |
+ var value = object[property]; | |
+ return _.isFunction(value) ? value.call(object) : value; | |
}; | |
- // Add your own custom functions to the Underscore object, ensuring that | |
- // they're correctly added to the OOP wrapper as well. | |
+ // Add your own custom functions to the Underscore object. | |
_.mixin = function(obj) { | |
each(_.functions(obj), function(name){ | |
- addToWrapper(name, _[name] = obj[name]); | |
+ var func = _[name] = obj[name]; | |
+ _.prototype[name] = function() { | |
+ var args = [this._wrapped]; | |
+ push.apply(args, arguments); | |
+ return result.call(this, func.apply(_, args)); | |
+ }; | |
}); | |
}; | |
t@@ -713,7 +1079,7 @@ | |
// Useful for temporary DOM ids. | |
var idCounter = 0; | |
_.uniqueId = function(prefix) { | |
- var id = idCounter++; | |
+ var id = ++idCounter + ''; | |
return prefix ? prefix + id : id; | |
}; | |
t@@ -721,56 +1087,103 @@ | |
// following template settings to use alternative delimiters. | |
_.templateSettings = { | |
evaluate : /<%([\s\S]+?)%>/g, | |
- interpolate : /<%=([\s\S]+?)%>/g | |
+ interpolate : /<%=([\s\S]+?)%>/g, | |
+ escape : /<%-([\s\S]+?)%>/g | |
+ }; | |
+ | |
+ // When customizing `templateSettings`, if you don't want to define an | |
+ // interpolation, evaluation or escaping regex, we need one that is | |
+ // guaranteed not to match. | |
+ var noMatch = /(.)^/; | |
+ | |
+ // Certain characters need to be escaped so that they can be put into a | |
+ // string literal. | |
+ var escapes = { | |
+ "'": "'", | |
+ '\\': '\\', | |
+ '\r': 'r', | |
+ '\n': 'n', | |
+ '\t': 't', | |
+ '\u2028': 'u2028', | |
+ '\u2029': 'u2029' | |
}; | |
+ var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; | |
+ | |
// JavaScript micro-templating, similar to John Resig's implementation. | |
// Underscore templating handles arbitrary delimiters, preserves whitespace, | |
// and correctly escapes quotes within interpolated code. | |
- _.template = function(str, data) { | |
- var c = _.templateSettings; | |
- var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + | |
- 'with(obj||{}){__p.push(\'' + | |
- str.replace(/\\/g, '\\\\') | |
- .replace(/'/g, "\\'") | |
- .replace(c.interpolate, function(match, code) { | |
- return "'," + code.replace(/\\'/g, "'") + ",'"; | |
- }) | |
- .replace(c.evaluate || null, function(match, code) { | |
- return "');" + code.replace(/\\'/g, "'") | |
- .replace(/[\r\n\t]/g, ' ') + "__p.push('"; | |
- }) | |
- .replace(/\r/g, '\\r') | |
- .replace(/\n/g, '\\n') | |
- .replace(/\t/g, '\\t') | |
- + "');}return __p.join('');"; | |
- var func = new Function('obj', tmpl); | |
- return data ? func(data) : func; | |
- }; | |
- | |
- // The OOP Wrapper | |
- // --------------- | |
+ _.template = function(text, data, settings) { | |
+ var render; | |
+ settings = _.defaults({}, settings, _.templateSettings); | |
+ | |
+ // Combine delimiters into one regular expression via alternation. | |
+ var matcher = new RegExp([ | |
+ (settings.escape || noMatch).source, | |
+ (settings.interpolate || noMatch).source, | |
+ (settings.evaluate || noMatch).source | |
+ ].join('|') + '|$', 'g'); | |
+ | |
+ // Compile the template source, escaping string literals appropriately. | |
+ var index = 0; | |
+ var source = "__p+='"; | |
+ text.replace(matcher, function(match, escape, interpolate, evaluate, offse… | |
+ source += text.slice(index, offset) | |
+ .replace(escaper, function(match) { return '\\' + escapes[match]; }); | |
+ | |
+ if (escape) { | |
+ source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; | |
+ } | |
+ if (interpolate) { | |
+ source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; | |
+ } | |
+ if (evaluate) { | |
+ source += "';\n" + evaluate + "\n__p+='"; | |
+ } | |
+ index = offset + match.length; | |
+ return match; | |
+ }); | |
+ source += "';\n"; | |
+ // If a variable is not specified, place data values in local scope. | |
+ if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; | |
+ | |
+ source = "var __t,__p='',__j=Array.prototype.join," + | |
+ "print=function(){__p+=__j.call(arguments,'');};\n" + | |
+ source + "return __p;\n"; | |
+ | |
+ try { | |
+ render = new Function(settings.variable || 'obj', '_', source); | |
+ } catch (e) { | |
+ e.source = source; | |
+ throw e; | |
+ } | |
+ | |
+ if (data) return render(data, _); | |
+ var template = function(data) { | |
+ return render.call(this, data, _); | |
+ }; | |
+ | |
+ // Provide the compiled function source as a convenience for precompilatio… | |
+ template.source = 'function(' + (settings.variable || 'obj') + '){\n' + so… | |
+ | |
+ return template; | |
+ }; | |
+ | |
+ // Add a "chain" function, which will delegate to the wrapper. | |
+ _.chain = function(obj) { | |
+ return _(obj).chain(); | |
+ }; | |
+ | |
+ // OOP | |
+ // --------------- | |
// If Underscore is called as a function, it returns a wrapped object that | |
// can be used OO-style. This wrapper holds altered versions of all the | |
// underscore functions. Wrapped objects may be chained. | |
- var wrapper = function(obj) { this._wrapped = obj; }; | |
- | |
- // Expose `wrapper.prototype` as `_.prototype` | |
- _.prototype = wrapper.prototype; | |
// Helper function to continue chaining intermediate results. | |
- var result = function(obj, chain) { | |
- return chain ? _(obj).chain() : obj; | |
- }; | |
- | |
- // A method to easily add functions to the OOP wrapper. | |
- var addToWrapper = function(name, func) { | |
- wrapper.prototype[name] = function() { | |
- var args = slice.call(arguments); | |
- unshift.call(args, this._wrapped); | |
- return result(func.apply(_, args), this._chain); | |
- }; | |
+ var result = function(obj) { | |
+ return this._chain ? _(obj).chain() : obj; | |
}; | |
// Add all of the Underscore functions to the wrapper object. | |
t@@ -779,29 +1192,35 @@ | |
// Add all mutator Array functions to the wrapper. | |
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], funct… | |
var method = ArrayProto[name]; | |
- wrapper.prototype[name] = function() { | |
- method.apply(this._wrapped, arguments); | |
- return result(this._wrapped, this._chain); | |
+ _.prototype[name] = function() { | |
+ var obj = this._wrapped; | |
+ method.apply(obj, arguments); | |
+ if ((name == 'shift' || name == 'splice') && obj.length === 0) delete ob… | |
+ return result.call(this, obj); | |
}; | |
}); | |
// Add all accessor Array functions to the wrapper. | |
each(['concat', 'join', 'slice'], function(name) { | |
var method = ArrayProto[name]; | |
- wrapper.prototype[name] = function() { | |
- return result(method.apply(this._wrapped, arguments), this._chain); | |
+ _.prototype[name] = function() { | |
+ return result.call(this, method.apply(this._wrapped, arguments)); | |
}; | |
}); | |
- // Start chaining a wrapped Underscore object. | |
- wrapper.prototype.chain = function() { | |
- this._chain = true; | |
- return this; | |
- }; | |
+ _.extend(_.prototype, { | |
- // Extracts the result from a wrapped and chained object. | |
- wrapper.prototype.value = function() { | |
- return this._wrapped; | |
- }; | |
+ // Start chaining a wrapped Underscore object. | |
+ chain: function() { | |
+ this._chain = true; | |
+ return this; | |
+ }, | |
+ | |
+ // Extracts the result from a wrapped and chained object. | |
+ value: function() { | |
+ return this._wrapped; | |
+ } | |
+ | |
+ }); | |
-})(); | |
+}).call(this); | |
diff --git a/doc/html/cfd.html b/doc/html/cfd.html | |
t@@ -0,0 +1,600 @@ | |
+ | |
+ | |
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | |
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
+ | |
+ | |
+<html xmlns="http://www.w3.org/1999/xhtml"> | |
+ <head> | |
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |
+ | |
+ <title>Fluid simulation and particle-fluid interaction — sphere 1.00… | |
+ | |
+ <link rel="stylesheet" href="_static/default.css" type="text/css" /> | |
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" /> | |
+ <link rel="stylesheet" href="_static/breathe.css" type="text/css" /> | |
+ | |
+ <script type="text/javascript"> | |
+ var DOCUMENTATION_OPTIONS = { | |
+ URL_ROOT: '', | |
+ VERSION: '1.00-alpha', | |
+ COLLAPSE_INDEX: false, | |
+ FILE_SUFFIX: '.html', | |
+ HAS_SOURCE: true | |
+ }; | |
+ </script> | |
+ <script type="text/javascript" src="_static/jquery.js"></script> | |
+ <script type="text/javascript" src="_static/underscore.js"></script> | |
+ <script type="text/javascript" src="_static/doctools.js"></script> | |
+ <link rel="top" title="sphere 1.00-alpha documentation" href="index.html" … | |
+ <link rel="next" title="Python API" href="python_api.html" /> | |
+ <link rel="prev" title="Discrete element method" href="dem.html" /> | |
+ </head> | |
+ <body> | |
+ <div class="related"> | |
+ <h3>Navigation</h3> | |
+ <ul> | |
+ <li class="right" style="margin-right: 10px"> | |
+ <a href="genindex.html" title="General Index" | |
+ accesskey="I">index</a></li> | |
+ <li class="right" > | |
+ <a href="py-modindex.html" title="Python Module Index" | |
+ >modules</a> |</li> | |
+ <li class="right" > | |
+ <a href="python_api.html" title="Python API" | |
+ accesskey="N">next</a> |</li> | |
+ <li class="right" > | |
+ <a href="dem.html" title="Discrete element method" | |
+ accesskey="P">previous</a> |</li> | |
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> »</… | |
+ </ul> | |
+ </div> | |
+ | |
+ <div class="document"> | |
+ <div class="documentwrapper"> | |
+ <div class="bodywrapper"> | |
+ <div class="body"> | |
+ | |
+ <div class="section" id="fluid-simulation-and-particle-fluid-interaction"> | |
+<h1>Fluid simulation and particle-fluid interaction<a class="headerlink" href=… | |
+<p>A new and experimental addition to <em>sphere</em> is the ability to simula… | |
+of particles and a Newtonian fluid. The fluid is simulated using an Eulerian | |
+continuum approach, using a custom CUDA solver for GPU computation. This | |
+approach allows for fast simulations due to the limited need for GPU-CPU | |
+communications, as well as a flexible code base.</p> | |
+<p>The following sections will describe the theoretical background, as well as… | |
+solution procedure and the numerical implementation.</p> | |
+<div class="section" id="derivation-of-the-navier-stokes-equations-with-porosi… | |
+<h2>Derivation of the Navier Stokes equations with porosity<a class="headerlin… | |
+<p>Following the outline presented by <a class="reference external" href="http… | |
+continuity equation for an incompressible fluid material is given by:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/b588eea9cec4513a3be72255d8d3df214546bfe7.png" alt="\… | |
+</div><p>and the momentum equation:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/5b46624e0dc3d79b64f388898e2dff17d232656c.png" alt="\… | |
++ \rho (\boldsymbol{v} \cdot \nabla \boldsymbol{v}) | |
+= \nabla \cdot \boldsymbol{\sigma} | |
++ \rho \boldsymbol{f}"/></p> | |
+</div><p>Here, <img class="math" src="_images/math/d0b4b390a4806bb739c6b4adbdf… | |
+fluid density, <img class="math" src="_images/math/769bfdcb2a43bde2cd368d82a6f… | |
+<img class="math" src="_images/math/69b1fdf87f9a78aaef8057a34aea7a6c17dad726.p… | |
+Newtonian fluids, the Cauchy stress is given by:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/c9264cc703654b5651cb89a1c9f5e178b5d15cd0.png" alt="\… | |
+</div><p><img class="math" src="_images/math/36f73fc1312ee0349b3f3a0f3bd9eb550… | |
+tensor, and <img class="math" src="_images/math/023a7668d083165ce2aa65b49e876b… | |
+by:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/25586671e456c2d987cd75cefd64d9cee69f50b9.png" alt="\… | |
+\mu_f \nabla \boldsymbol{v} | |
++ \mu_f (\nabla \boldsymbol{v})^T"/></p> | |
+</div><p>By using the following vector identities:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/e4ca401f97df40e3568bc0624774cafa02d77cc1.png" alt="\… | |
+ | |
+\nabla \cdot (\nabla \boldsymbol{v}) = \nabla^2 \boldsymbol{v} | |
+ | |
+\nabla \cdot (\nabla \boldsymbol{v})^T | |
+= \nabla (\nabla \cdot \boldsymbol{v})"/></p> | |
+</div><p>the deviatoric component of the Cauchy stress tensor simplifies to the | |
+following, assuming that spatial variations in the viscosity can be neglected:… | |
+<div class="math"> | |
+<p><img src="_images/math/1053df745e2d73fc7ee27d39a01ad228c360072e.png" alt="=… | |
++ \mu_f \nabla^2 \boldsymbol{v}"/></p> | |
+</div><p>Since we are dealing with fluid flow in a porous medium, additional t… | |
+introduced to the equations for conservation of mass and momentum. In the | |
+following, the equations are derived for the first spatial component. The | |
+solution for the other components is trivial.</p> | |
+<p>The porosity value (in the saturated porous medium the volumetric fraction … | |
+the fluid phase) denoted <img class="math" src="_images/math/2c175f60eecef1de7… | |
+momentum equations. The continuity equation becomes:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/f8f0dc31c5c12bc6e3da6fc71519fa78ea04190b.png" alt="\… | |
++ \nabla \cdot (\phi \boldsymbol{v}) = 0"/></p> | |
+</div><p>For the <img class="math" src="_images/math/26eeb5258ca5099acf8fe96b2… | |
+with a body force <img class="math" src="_images/math/69b1fdf87f9a78aaef8057a3… | |
+<div class="math"> | |
+<p><img src="_images/math/900be94839e1ee03a8aa1134961912314905eb27.png" alt="\… | |
+= \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\sigma}) \right]_x | |
++ \phi f_x"/></p> | |
+</div><p>In the Eulerian formulation, an advection term is added, and the Cauc… | |
+tensor is represented as isotropic and deviatoric components individually:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/32e2ba09618e2d303d91d673a25ef66e29e94750.png" alt="\… | |
++ \boldsymbol{v} \cdot \nabla (\phi v_x) | |
+= \frac{1}{\rho} \left[ \nabla \cdot (-\phi p \boldsymbol{I}) | |
++ \phi \boldsymbol{\tau}) \right]_x | |
++ \phi f_x"/></p> | |
+</div><p>Using vector identities to rewrite the advection term, and expanding … | |
+stress tensor term:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/dcf5762fe2b4b81adb93ee084951f42a2f1eadbc.png" alt="\… | |
++ \nabla \cdot (\phi v_x \boldsymbol{v}) | |
+- \phi v_x (\nabla \cdot \boldsymbol{v}) | |
+= \frac{1}{\rho} \left[ -\nabla \phi p \right]_x | |
++ \frac{1}{\rho} \left[ -\phi \nabla p \right]_x | |
++ \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
++ \phi f_x"/></p> | |
+</div><p>Spatial variations in the porosity are neglected,</p> | |
+<div class="math"> | |
+<p><img src="_images/math/c42a32017c99646f19bb5807728595d4526c3b30.png" alt="\… | |
+</div><p>and the pressure is attributed to the fluid phase alone (model B in Z… | |
+2007 and Zhou et al. 2010). The divergence of fluid velocities is defined to be | |
+zero:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/44fafcf5a158459730d0dd7c293b93cdcf62f0a4.png" alt="\… | |
+</div><p>With these assumptions, the momentum equation simplifies to:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/6e62843666dcc0fe9a45a1c69c532c82a95a9451.png" alt="\… | |
++ \nabla \cdot (\phi v_x \boldsymbol{v}) | |
+= -\frac{1}{\rho} \frac{\partial p}{\partial x} | |
++ \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
++ \phi f_x"/></p> | |
+</div><p>The remaining part of the advection term is for the <img class="math"… | |
+found as:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/967116fa65cb7eb073c2f71309b21af6aca77a09.png" alt="\… | |
+\left[ | |
+ \frac{\partial}{\partial x}, | |
+ \frac{\partial}{\partial y}, | |
+ \frac{\partial}{\partial z} | |
+\right] | |
+\left[ | |
+ \begin{array}{c} | |
+ \phi v_x v_x\\ | |
+ \phi v_x v_y\\ | |
+ \phi v_x v_z\\ | |
+ \end{array} | |
+\right] | |
+= | |
+\frac{\partial (\phi v_x v_x)}{\partial x} + | |
+\frac{\partial (\phi v_x v_y)}{\partial y} + | |
+\frac{\partial (\phi v_x v_z)}{\partial z}"/></p> | |
+</div><p>The deviatoric stress tensor is in this case symmetrical, i.e. <img c… | |
+= \tau_{ji}"/>, and is found by:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/9d3cd4785fd2621936d97aa60cb5005a7e95b5b7.png" alt="\… | |
+= \frac{1}{\rho} | |
+\left[ | |
+ \left[ | |
+ \frac{\partial}{\partial x}, | |
+ \frac{\partial}{\partial y}, | |
+ \frac{\partial}{\partial z} | |
+ \right] | |
+ \phi | |
+ \left[ | |
+ \begin{matrix} | |
+ \tau_{xx} & \tau_{xy} & \tau_{xz}\\ | |
+ \tau_{yx} & \tau_{yy} & \tau_{yz}\\ | |
+ \tau_{zx} & \tau_{zy} & \tau_{zz}\\ | |
+ \end{matrix} | |
+ \right] | |
+\right]_x | |
+ | |
+= \frac{1}{\rho} | |
+\left[ | |
+ \begin{array}{c} | |
+ \frac{\partial (\phi \tau_{xx})}{\partial x} | |
+ + \frac{\partial (\phi \tau_{xy})}{\partial y} | |
+ + \frac{\partial (\phi \tau_{xz})}{\partial z}\\ | |
+ \frac{\partial (\phi \tau_{yx})}{\partial x} | |
+ + \frac{\partial (\phi \tau_{yy})}{\partial y} | |
+ + \frac{\partial (\phi \tau_{yz})}{\partial z}\\ | |
+ \frac{\partial (\phi \tau_{zx})}{\partial x} | |
+ + \frac{\partial (\phi \tau_{zy})}{\partial y} | |
+ + \frac{\partial (\phi \tau_{zz})}{\partial z}\\ | |
+ \end{array} | |
+\right]_x | |
+= \frac{1}{\rho} | |
+\left( | |
+ \frac{\partial (\phi \tau_{xx})}{\partial x} | |
+ + \frac{\partial (\phi \tau_{xy})}{\partial y} | |
+ + \frac{\partial (\phi \tau_{xz})}{\partial z} | |
+\right)"/></p> | |
+</div><p>In a linear viscous fluid, the stress and strain rate | |
+(<img class="math" src="_images/math/7727554a4dfa1572695dec332d6f6ee255815c96.… | |
+viscosity parameter <img class="math" src="_images/math/6160e3f2050b4cd56b336f… | |
+<div class="math"> | |
+<p><img src="_images/math/5fb7b1a39363304fc1fb9172ecd46e2f39274384.png" alt="\… | |
+= \mu_f \left( | |
+\frac{\partial v_i}{\partial x_j} + \frac{\partial v_j}{\partial x_i} | |
+\right)"/></p> | |
+</div><p>With this relationship, the deviatoric stress tensor components can be | |
+calculated as:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/f99450c7a8851772907f28ffd58be080f18894ae.png" alt="\… | |
+\tau_{yy} = 2 \mu_f \frac{\partial v_y}{\partial y} \qquad | |
+\tau_{zz} = 2 \mu_f \frac{\partial v_z}{\partial z} | |
+ | |
+\tau_{xy} = \mu_f \left( | |
+\frac{\partial v_x}{\partial y} + \frac{\partial v_y}{\partial x} \right) | |
+ | |
+\tau_{xz} = \mu_f \left( | |
+\frac{\partial v_x}{\partial z} + \frac{\partial v_z}{\partial x} \right) | |
+ | |
+\tau_{yz} = \mu_f \left( | |
+\frac{\partial v_y}{\partial z} + \frac{\partial v_z}{\partial y} \right)"/></… | |
+</div><p>where <img class="math" src="_images/math/6160e3f2050b4cd56b336ff5523… | |
+fluid rheology assumes identical bulk and shear viscosities. The derivation of | |
+the equations for the other spatial components is trivial.</p> | |
+</div> | |
+<div class="section" id="porosity-estimation"> | |
+<h2>Porosity estimation<a class="headerlink" href="#porosity-estimation" title… | |
+<p>The solid volume in each fluid cell is determined by the ratio of the | |
+a cell-centered spherical cell volume (<img class="math" src="_images/math/24e… | |
+particle volumes (<img class="math" src="_images/math/3c93d2ecf99a3c5ee161c98a… | |
+<img class="math" src="_images/math/74958bc18d017328dd0fac99816d2184296bfd3d.p… | |
+the fluid cell width. The nearby particles are characterized by position | |
+<img class="math" src="_images/math/d1e970327c74ae0d8249c6283d2e3134d042f6ff.p… | |
+as:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/7fe490dcbc6c9eb978f05b3334d2a5856dc66149.png" alt="d… | |
+</div><p>The common volume of the two intersecting spheres is zero if the volu… | |
+intersecting, lens shaped if they are intersecting, and spherical if the | |
+particle is fully contained by the spherical cell volume:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/6d9a9e0ef32ea9506bb55413b682112d0f3308a6.png" alt="V… | |
+\begin{cases} | |
+ 0 & \textit{if } R_i + r_j \leq d_{ij} \\ | |
+ \frac{1}{12d_{ij}} \left[ \pi (R_i + r_j - d_{ij})^2 | |
+ (d_{ij}^2 + 2d_{ij}r_j - 3r_j^2 + 2d_{ij} R_i + 6r_j R_i - 3R_i^2) | |
+ \right] & \textit{if } R_i - r_j < d_{ij} < R_i + r_j \\ | |
+ \frac{4}{3} \pi r^3_j & \textit{if } d_{ij} \leq R_i - r_j | |
+\end{cases}"/></p> | |
+</div><p>Using this method, the cell porosity values are continuous through ti… | |
+particles enter and exit the cell volume. The rate of porosity change | |
+(<img class="math" src="_images/math/38a6d1fc56073806115322bcd179578f05c88c02.… | |
+by considering the previous and current porosity.</p> | |
+</div> | |
+<div class="section" id="particle-fluid-interaction"> | |
+<h2>Particle-fluid interaction<a class="headerlink" href="#particle-fluid-inte… | |
+<p>The momentum exchange of the granular and fluid phases follows the procedure | |
+outlined by Gidaspow 1992 and Shamy and Zhegal 2005. The fluid and particle | |
+interaction is based on the concept of drag, where the magnitude is based on | |
+semi-empirical relationships. The drag force scales linearly with the relative | |
+difference in velocity between the fluid and particle phase. On the base of | |
+Newton’s third law, the resulting drag force is applied with opposite si… | |
+the particle and fluid.</p> | |
+<p>For fluid cells with porosities (<img class="math" src="_images/math/2c175f… | |
+force is based on the Ergun (1952) equation:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/baa542966f410097c2b4894388e41db6aa3cd996.png" alt="\… | |
+150 \frac{\mu_f (1-\phi)^2}{\phi\bar{d}^2} | |
++ 1.75 \frac{(1-\phi)\rho_f | |
+ ||\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p||}{\bar{d}} | |
+\right) | |
+(\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p)"/></p> | |
+</div><p>here, <img class="math" src="_images/math/46a84b389d8f594cd16c800ba42… | |
+<img class="math" src="_images/math/cbe3f817ae974accdf9d422c0df8f6c90721037c.p… | |
+<img class="math" src="_images/math/3b432b0e58dea7c6cc143bf67184fc19b8cd4336.p… | |
+particles in contact with the previously mentioned cell-centered sphere for | |
+porosity estimation contribute to the average particle velocity and diameter in | |
+the fluid cell.</p> | |
+<p>If the porosity is greater than 0.8, the cell-averaged drag force | |
+(<img class="math" src="_images/math/a6c493a071c2c31758bc2bc9adc2beb10d7acfd0.… | |
+which considers the fluid flow situation:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/7181906596d0aaac14cbde44ec57dd7e5866c81b.png" alt="\… | |
+\frac{3}{4} | |
+\frac{C_d (1-\phi) \phi^{-2.65} \mu_f \rho_f | |
+||\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p||}{\bar{d}} | |
+\right) | |
+(\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p)"/></p> | |
+</div><p>The drag coefficient <img class="math" src="_images/math/47bc1263e44f… | |
+Reynolds number <img class="math" src="_images/math/16ef95610462b9c2c03cba4c27… | |
+<div class="math"> | |
+<p><img src="_images/math/49a90589ae1e8a839b1ae6afb3dd1d162b6a094c.png" alt="C… | |
+\begin{cases} | |
+\frac{24}{Re} (1+0.15 (Re)^{0.687} & \textit{if } Re < 1,000 \\ | |
+0.44 & \textit{if } Re \geq 1,000 | |
+\end{cases}"/></p> | |
+</div><p>where the Reynold’s number is found by:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/8dbae01b96ab8dd591a50213f55caa3ac5958631.png" alt="R… | |
+||\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p||"/></p> | |
+</div><p>The interaction force is applied to the fluid with negative sign as a | |
+contribution to the body force <img class="math" src="_images/math/69b1fdf87f9… | |
+force applied particles in the fluid cell is:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/ac40b59145892a5a3c0141c5f35023ad85972855.png" alt="\… | |
+</div><p>where <img class="math" src="_images/math/257ee6442468d7529f036a8a81e… | |
+interaction force could be expanded to include the force induced by the fluid | |
+pressure gradient:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/0fdafcde7733804669f8df4846e5bfdb36c7afa7.png" alt="\… | |
+-\nabla p + | |
+\frac{\bar{\boldsymbol{f}}_d}{1-\phi} | |
+\right) V_p"/></p> | |
+</div></div> | |
+<div class="section" id="fluid-dynamics-solution-procedure-by-operator-splitti… | |
+<h2>Fluid dynamics solution procedure by operator splitting<a class="headerlin… | |
+<p>The partial differential terms in the previously described equations are fo… | |
+using finite central differences. Modifying the operator splitting methodology | |
+presented by Langtangen et al. (2002), the predicted velocity | |
+<img class="math" src="_images/math/90c8bfc206db2d9f4d0dd102507c9646a70755db.p… | |
+<img class="math" src="_images/math/a1ffc0a012620941fe660cedabff822ce7162eca.p… | |
+<div class="math"> | |
+<p><img src="_images/math/7ee03c7bfc1e46255c9d2d47d9b733a068c9ec2b.png" alt="\… | |
++ \nabla \cdot (\phi v_x \boldsymbol{v}) | |
+= - \frac{1}{\rho} \frac{\Delta p}{\Delta x} | |
++ \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
++ \phi f_x | |
+ | |
+\Downarrow | |
+ | |
+\phi \frac{\Delta v_x}{\Delta t} | |
++ v_x \frac{\Delta \phi}{\Delta t} | |
++ \nabla \cdot (\phi v_x \boldsymbol{v}) | |
+= - \frac{1}{\rho} \frac{\Delta p}{\Delta x} | |
++ \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
++ \phi f_x"/></p> | |
+</div><p>We want to isolate <img class="math" src="_images/math/b5e8dba2403c07… | |
+the new velocity.</p> | |
+<div class="math"> | |
+<p><img src="_images/math/038474380a078000f31889a32a1dcf79ac38c223.png" alt="\… | |
+= - \frac{1}{\rho} \frac{\Delta p}{\Delta x} | |
++ \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
++ \phi f_x | |
+- v_x \frac{\Delta \phi}{\Delta t} | |
+- \nabla \cdot (\phi v_x \boldsymbol{v}) | |
+ | |
+\Delta v_x | |
+= - \frac{1}{\rho} \frac{\Delta p}{\Delta x} \frac{\Delta t}{\phi} | |
++ \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
+ \frac{\Delta t}{\phi} | |
++ \Delta t f_x | |
+- v_x \frac{\Delta \phi}{\phi} | |
+- \nabla \cdot (\phi v_x \boldsymbol{v}) \frac{\Delta t}{\phi}"/></p> | |
+</div><p>The term <img class="math" src="_images/math/fdb63b9e51abe6bbb16acfb5… | |
+in the range <img class="math" src="_images/math/402b1a3c25643899cdefbe2e62d33… | |
+values in the solution procedure (Langtangen et al. 2002). A value of 0 | |
+corresponds to <a class="reference external" href="https://en.wikipedia.org/wi… | |
+in <a class="reference external" href="http://www.ams.org/journals/mcom/1968-2… | |
+<div class="math"> | |
+<p><img src="_images/math/dbcdbe7c53fa70f8517907ca1b3c440b28512dfc.png" alt="v… | |
+ | |
+v_x^* = v_x^t | |
+- \frac{\beta}{\rho} \frac{\Delta p^t}{\Delta x} \frac{\Delta t}{\phi^t} | |
++ \frac{1}{\rho} \left[ \nabla \cdot (\phi^t \boldsymbol{\tau}^t) \right]_x | |
+ \frac{\Delta t}{\phi} | |
++ \Delta t f_x | |
+- v^t_x \frac{\Delta \phi}{\phi^t} | |
+- \nabla \cdot (\phi^t v_x^t \boldsymbol{v}^t) \frac{\Delta t}{\phi^t}"/></p> | |
+</div><p>Here, <img class="math" src="_images/math/1eb29f9de3753a59530941141fc… | |
+(<img class="math" src="_images/math/f04823ba99f1452d779894f7a0745da23b55fbef.… | |
+<img class="math" src="_images/math/79c874e814b2550448c61e2627dc072653b68197.p… | |
+equation:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/c305e13251e69e8ce59b2908f84afbbd085b5103.png" alt="\… | |
+\boldsymbol{v}^{t+\Delta t}) = 0"/></p> | |
+</div><p>The divergence of a scalar and vector can be <a class="reference exte… | |
+<div class="math"> | |
+<p><img src="_images/math/41babe6999c89406f22db1ce5ed5e05ce939ed49.png" alt="\… | |
+\boldsymbol{v}^{t+\Delta t} \cdot \nabla \phi^t | |
++ \frac{\Delta \phi^t}{\Delta t} = 0"/></p> | |
+</div><p>The predicted velocity is corrected using the new pressure (Langtange… | |
+2002):</p> | |
+<div class="math"> | |
+<p><img src="_images/math/e3de9b8fce0b12c8459947bd22c727e379512fcc.png" alt="\… | |
+- \frac{\Delta t}{\rho} \nabla \epsilon | |
+\quad \text{where} \quad | |
+\epsilon = p^{t+\Delta t} - \beta p^t"/></p> | |
+</div><p>The above formulation of the future velocity is put into the continui… | |
+equation:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/25961440701892ff5417a2474f7f52bb353e5d98.png" alt="\… | |
+\phi^t \nabla \cdot | |
+\left( \boldsymbol{v}^* - \frac{\Delta t}{\rho} \nabla \epsilon \right) | |
++ | |
+\left( \boldsymbol{v}^* - \frac{\Delta t}{\rho} \nabla \epsilon \right) | |
+\cdot \nabla \phi^t + \frac{\Delta \phi^t}{\Delta t} = 0"/></p> | |
+</div><div class="math"> | |
+<p><img src="_images/math/1c7a111b8952b4162797cce2cbd426d74083b834.png" alt="\… | |
+\phi^t \nabla \cdot | |
+\boldsymbol{v}^* - \frac{\Delta t}{\rho} \phi^t \nabla^2 \epsilon | |
++ \nabla \phi^t \cdot \boldsymbol{v}^* | |
+- \nabla \phi^t \cdot \nabla \epsilon \frac{\Delta t}{\rho} | |
++ \frac{\Delta \phi^t}{\Delta t} = 0"/></p> | |
+</div><div class="math"> | |
+<p><img src="_images/math/8a2e0d0a28c9ebec91bd12def7b3b36dd9e92da7.png" alt="\… | |
+\frac{\Delta t}{\rho} \phi^t \nabla^2 \epsilon | |
+= \phi^t \nabla \cdot \boldsymbol{v}^* | |
++ \nabla \phi^t \cdot \boldsymbol{v}^* | |
+- \nabla \phi^t \cdot \nabla \epsilon \frac{\Delta t}{\rho} | |
++ \frac{\Delta \phi^t}{\Delta t}"/></p> | |
+</div><p>The pressure difference in time becomes a <a class="reference externa… | |
+<div class="math"> | |
+<p><img src="_images/math/7572a9bb14a95da7f7b42fd4b5bec33c42800556.png" alt="\… | |
+\nabla^2 \epsilon | |
+= \frac{\nabla \cdot \boldsymbol{v}^* \rho}{\Delta t} | |
++ \frac{\nabla \phi^t \cdot \boldsymbol{v}^* \rho}{\Delta t \phi^t} | |
+- \frac{\nabla \phi^t \cdot \nabla \epsilon}{\phi^t} | |
++ \frac{\Delta \phi^t \rho}{\Delta t^2 \phi^t}"/></p> | |
+</div><p>The right hand side of the above equation is termed the <em>forcing f… | |
+<img class="math" src="_images/math/bb2c93730dbb48558bb3c4738c956c4e8f816437.p… | |
+<div class="math"> | |
+<p><img src="_images/math/28d83ccd32811c0455355169ed8f6978d8d24dbe.png" alt="f… | |
+= \frac{\nabla \cdot \boldsymbol{v}^* \rho}{\Delta t} | |
++ \frac{\nabla \phi^t \cdot \boldsymbol{v}^* \rho}{\Delta t \phi^t} | |
++ \frac{\Delta \phi^t \rho}{\Delta t^2 \phi^t} | |
+ | |
+f_2 = | |
+\frac{\nabla \phi^t \cdot \nabla \epsilon}{\phi^t}"/></p> | |
+</div><p>During the <a class="reference external" href="http://www.rsmas.miami… | |
+while <img class="math" src="_images/math/e31584cdce0b50ee39c63081564f6e2ec5a7… | |
+during the first iteration, while <img class="math" src="_images/math/e31584cd… | |
+of the forcing function is found as:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/c4f23b63471bb0807346dc89db91e3a684cfa236.png" alt="f… | |
+</div><p>Using second-order finite difference approximations of the Laplace op… | |
+second-order partial derivatives, the differential equations become a system of | |
+equations that is solved using <a class="reference external" href="https://en.… | |
+number of unknowns is <img class="math" src="_images/math/954afa1fc1c88d82f51a… | |
+<p>The discrete Laplacian (approximation of the Laplace operator) can be obtai… | |
+by a finite-difference seven-point stencil in a three-dimensional, cubic | |
+grid with cell spacing <img class="math" src="_images/math/3d41adff4943d60236f… | |
+face neighbors:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/6e1feb8f41fb33ffbf60f657c5e095c2bb780e4d.png" alt="\… | |
+\frac{\epsilon_{i_x-1,i_y,i_z} - 2 \epsilon_{i_x,i_y,i_z} | |
++ \epsilon_{i_x+1,i_y,i_z}}{\Delta x^2} | |
++ \frac{\epsilon_{i_x,i_y-1,i_z} - 2 \epsilon_{i_x,i_y,i_z} | |
++ \epsilon_{i_x,i_y+1,i_z}}{\Delta y^2} | |
+ | |
++ \frac{\epsilon_{i_x,i_y,i_z-1} - 2 \epsilon_{i_x,i_y,i_z} | |
++ \epsilon_{i_x,i_y,i_z+1}}{\Delta z^2} | |
+\approx f_{i_x,i_y,i_z}"/></p> | |
+</div><p>Within a Jacobi iteration, the value of the unknowns (<img class="mat… | |
+used to find an updated solution estimate (<img class="math" src="_images/math… | |
+The solution for the updated value takes the form:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/5dfeda3ba68db3f629c37ad0b4cc75095c2d6570.png" alt="\… | |
+= \frac{-\Delta x^2 \Delta y^2 \Delta z^2 f_{i_x,i_y,i_z} | |
++ \Delta y^2 \Delta z^2 (\epsilon^n_{i_x-1,i_y,i_z} + | |
+ \epsilon^n_{i_x+1,i_y,i_z}) | |
++ \Delta x^2 \Delta z^2 (\epsilon^n_{i_x,i_y-1,i_z} + | |
+ \epsilon^n_{i_x,i_y+1,i_z}) | |
++ \Delta x^2 \Delta y^2 (\epsilon^n_{i_x,i_y,i_z-1} + | |
+ \epsilon^n_{i_x,i_y,i_z+1})} | |
+ {2 (\Delta x^2 \Delta y^2 | |
+ + \Delta x^2 \Delta z^2 | |
+ + \Delta y^2 \Delta z^2) }"/></p> | |
+</div><p>The difference between the current and updated value is termed the <e… | |
+residual</em>:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/c6e0b5935589b4f8948c91faa0de425bf2791db4.png" alt="r… | |
+- \epsilon^n_{i_x,i_y,i_z})^2}{(\epsilon^{n+1}_{i_x,i_y,i_z})^2}"/></p> | |
+</div><p>Note that the <img class="math" src="_images/math/eaf4418fbe935c15a60… | |
+of the residual.</p> | |
+<p>The updated values are at the end of the iteration stored as the current va… | |
+and the maximal value of the normalized residual is found. If this value is | |
+larger than a tolerance criteria, the procedure is repeated. The iterative | |
+procedure is ended if the number of iterations exceeds a defined limit.</p> | |
+<p>After the values of <img class="math" src="_images/math/eaf4418fbe935c15a60… | |
+pressures and velocities:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/c15067d0b2458f2c3bfd62743d7309f7e48213f9.png" alt="\… | |
+</div><div class="math"> | |
+<p><img src="_images/math/b55687e1799df2d682b5ba0c207ca16fa9c014fe.png" alt="\… | |
+\bar{\boldsymbol{v}}^* - \frac{\Delta t}{\rho} \nabla \epsilon"/></p> | |
+</div></div> | |
+<div class="section" id="boundary-conditions"> | |
+<h2>Boundary conditions<a class="headerlink" href="#boundary-conditions" title… | |
+<p>The lateral boundaries are periodic. This cannot be changed in the current | |
+version of <tt class="docutils literal"><span class="pre">sphere</span></tt>. … | |
+parallel lateral (<img class="math" src="_images/math/26eeb5258ca5099acf8fe96b… | |
+leaving through one side reappears on the opposite side.</p> | |
+<p>The top and bottom boundary conditions of the fluid grid can be either: | |
+prescribed pressure (Dirichlet), or prescribed velocity (Neumann). The | |
+(horizontal) velocities parallel to the boundaries are free to attain other | |
+values (free slip). The Dirichlet boundary condition is enforced by keeping the | |
+value of <img class="math" src="_images/math/eaf4418fbe935c15a606516d8f55dc380… | |
+<div class="math"> | |
+<p><img src="_images/math/01be15303939c78ee40a848a717482d046a9ad48.png" alt="\… | |
+= | |
+\epsilon^{n}_{i_x,i_y,i_z = 1 \vee n_z}"/></p> | |
+</div><p>The Neumann boundary condition of no flow across the boundary is enfo… | |
+setting the gradient of <img class="math" src="_images/math/eaf4418fbe935c15a6… | |
+e.g.:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/bdc15139139f2cc22f5656d0456d7b432a72c4e8.png" alt="\… | |
+</div></div> | |
+<div class="section" id="numerical-implementation"> | |
+<h2>Numerical implementation<a class="headerlink" href="#numerical-implementat… | |
+<p>Ghost nodes</p> | |
+<p>—</p> | |
+</div> | |
+</div> | |
+ | |
+ | |
+ </div> | |
+ </div> | |
+ </div> | |
+ <div class="sphinxsidebar"> | |
+ <div class="sphinxsidebarwrapper"> | |
+ <h3><a href="index.html">Table Of Contents</a></h3> | |
+ <ul> | |
+<li><a class="reference internal" href="#">Fluid simulation and particle-fluid… | |
+<li><a class="reference internal" href="#derivation-of-the-navier-stokes-equat… | |
+<li><a class="reference internal" href="#porosity-estimation">Porosity estimat… | |
+<li><a class="reference internal" href="#particle-fluid-interaction">Particle-… | |
+<li><a class="reference internal" href="#fluid-dynamics-solution-procedure-by-… | |
+<li><a class="reference internal" href="#boundary-conditions">Boundary conditi… | |
+<li><a class="reference internal" href="#numerical-implementation">Numerical i… | |
+</ul> | |
+</li> | |
+</ul> | |
+ | |
+ <h4>Previous topic</h4> | |
+ <p class="topless"><a href="dem.html" | |
+ title="previous chapter">Discrete element method</a></… | |
+ <h4>Next topic</h4> | |
+ <p class="topless"><a href="python_api.html" | |
+ title="next chapter">Python API</a></p> | |
+ <h3>This Page</h3> | |
+ <ul class="this-page-menu"> | |
+ <li><a href="_sources/cfd.txt" | |
+ rel="nofollow">Show Source</a></li> | |
+ </ul> | |
+<div id="searchbox" style="display: none"> | |
+ <h3>Quick search</h3> | |
+ <form class="search" action="search.html" method="get"> | |
+ <input type="text" name="q" /> | |
+ <input type="submit" value="Go" /> | |
+ <input type="hidden" name="check_keywords" value="yes" /> | |
+ <input type="hidden" name="area" value="default" /> | |
+ </form> | |
+ <p class="searchtip" style="font-size: 90%"> | |
+ Enter search terms or a module, class or function name. | |
+ </p> | |
+</div> | |
+<script type="text/javascript">$('#searchbox').show(0);</script> | |
+ </div> | |
+ </div> | |
+ <div class="clearer"></div> | |
+ </div> | |
+ <div class="related"> | |
+ <h3>Navigation</h3> | |
+ <ul> | |
+ <li class="right" style="margin-right: 10px"> | |
+ <a href="genindex.html" title="General Index" | |
+ >index</a></li> | |
+ <li class="right" > | |
+ <a href="py-modindex.html" title="Python Module Index" | |
+ >modules</a> |</li> | |
+ <li class="right" > | |
+ <a href="python_api.html" title="Python API" | |
+ >next</a> |</li> | |
+ <li class="right" > | |
+ <a href="dem.html" title="Discrete element method" | |
+ >previous</a> |</li> | |
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> »</… | |
+ </ul> | |
+ </div> | |
+ <div class="footer"> | |
+ © Copyright 2014, Anders Damsgaard. | |
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.3. | |
+ </div> | |
+ </body> | |
+</html> | |
+\ No newline at end of file | |
diff --git a/doc/html/dem.html b/doc/html/dem.html | |
t@@ -8,7 +8,7 @@ | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |
- <title>Discrete element method — sphere 0.35 documentation</title> | |
+ <title>Discrete element method — sphere 1.00-alpha documentation</ti… | |
<link rel="stylesheet" href="_static/default.css" type="text/css" /> | |
<link rel="stylesheet" href="_static/pygments.css" type="text/css" /> | |
t@@ -17,7 +17,7 @@ | |
<script type="text/javascript"> | |
var DOCUMENTATION_OPTIONS = { | |
URL_ROOT: '', | |
- VERSION: '0.35', | |
+ VERSION: '1.00-alpha', | |
COLLAPSE_INDEX: false, | |
FILE_SUFFIX: '.html', | |
HAS_SOURCE: true | |
t@@ -26,8 +26,8 @@ | |
<script type="text/javascript" src="_static/jquery.js"></script> | |
<script type="text/javascript" src="_static/underscore.js"></script> | |
<script type="text/javascript" src="_static/doctools.js"></script> | |
- <link rel="top" title="sphere 0.35 documentation" href="index.html" /> | |
- <link rel="next" title="Python API" href="python_api.html" /> | |
+ <link rel="top" title="sphere 1.00-alpha documentation" href="index.html" … | |
+ <link rel="next" title="Fluid simulation and particle-fluid interaction" h… | |
<link rel="prev" title="Introduction" href="introduction.html" /> | |
</head> | |
<body> | |
t@@ -41,12 +41,12 @@ | |
<a href="py-modindex.html" title="Python Module Index" | |
>modules</a> |</li> | |
<li class="right" > | |
- <a href="python_api.html" title="Python API" | |
+ <a href="cfd.html" title="Fluid simulation and particle-fluid intera… | |
accesskey="N">next</a> |</li> | |
<li class="right" > | |
<a href="introduction.html" title="Introduction" | |
accesskey="P">previous</a> |</li> | |
- <li><a href="index.html">sphere 0.35 documentation</a> »</li> | |
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> »</… | |
</ul> | |
</div> | |
t@@ -57,17 +57,168 @@ | |
<div class="section" id="discrete-element-method"> | |
<h1>Discrete element method<a class="headerlink" href="#discrete-element-metho… | |
-<p>The discrete element method (or distinct element method) was initially | |
-formulated by Cundall and Strack (1979). It simulates the physical behavior and | |
-interaction of discrete, unbreakable particles, with their own mass and inerti… | |
-under the influence of e.g. gravity and boundary conditions such as moving | |
-walls. By discretizing time into small time steps, explicit integration of | |
-Newton’s second law of motion is used to predict the new position and ki… | |
-values for each particle from the previous sums of forces. This Lagrangian | |
-approach is ideal for simulating discontinuous materials, such as granular | |
-matter. | |
-The complexity of the computations is kept low by representing the particles as | |
-spheres, which keeps contact-searching algorithms simple.</p> | |
+<p>Granular material is a very common form of matter, both in nature and indus… | |
+It can be defined as material consisting of interacting, discrete particles. | |
+Common granular materials include gravels, sands and soils, ice bergs, | |
+asteroids, powders, seeds, and other foods. Over 75% of the raw materials that | |
+pass through industry are granular. This wide occurrence has driven the desire | |
+to understand the fundamental mechanics of the material.</p> | |
+<p>Contrary to other common materials such as gases, liquids and solids, a gen… | |
+mathematical formulation of it’s behavior hasn’t yet been found. G… | |
+material can, however, display states that somewhat resemble gases, fluids and | |
+solids.</p> | |
+<p>The <a class="reference external" href="https://en.wikipedia.org/wiki/Discr… | |
+method that can be used to | |
+simulate the interaction of particles. Originally derived from | |
+<a class="reference external" href="https://en.wikipedia.org/wiki/Molecular_dy… | |
+it simulates particles as separate entities, and calculates their positions, | |
+velocities, and accelerations through time. See Cundall and Strack (1979) and | |
+<a class="reference external" href="http://anders-dc.github.io/2013/10/16/the-… | |
+general introduction to the DEM. The following sections will highlight the | |
+DEM implementation in <tt class="docutils literal"><span class="pre">sphere</s… | |
+Damsgaard et al. 2013. In the used notation, a bold symbol denotes a | |
+three-dimensional vector, and a dot denotes that the entity is a temporal | |
+derivative.</p> | |
+<div class="section" id="contact-search"> | |
+<h2>Contact search<a class="headerlink" href="#contact-search" title="Permalin… | |
+<p>Homogeneous cubic grid.</p> | |
+<div class="math"> | |
+<p><img src="_images/math/b2cd17a83bb89dc6b4ff954979dba3b2fbc8e043.png" alt="\… | |
+</div><p>where <img class="math" src="_images/math/b55ca7a0aa88ab7d58f4fc03531… | |
+positional vector of a particle, and <img class="math" src="_images/math/34857… | |
+of two particles. Negative values of <img class="math" src="_images/math/dd84c… | |
+are overlapping.</p> | |
+</div> | |
+<div class="section" id="contact-interaction"> | |
+<h2>Contact interaction<a class="headerlink" href="#contact-interaction" title… | |
+<p>Now that the inter-particle contacts have been identified and characterized… | |
+their overlap, the resulting forces from the interaction can be resolved. The | |
+interaction is decomposed into normal and tangential components, relative to t… | |
+contact interface orientation. The normal vector to the contact interface is | |
+found by:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/8bf1a4afd4cdf42bf3f7b27c9d9c99377a5d1898.png" alt="\… | |
+\frac{\boldsymbol{x}^i - \boldsymbol{x}^j} | |
+{||\boldsymbol{x}^i - \boldsymbol{x}^j||}"/></p> | |
+</div><p>The contact velocity <img class="math" src="_images/math/0dd32d7f3a00… | |
+<div class="math"> | |
+<p><img src="_images/math/767762b2cd832672331c1421813b919298d7e3b8.png" alt="\… | |
+(\boldsymbol{x}^i - \boldsymbol{x}^j) | |
++ (r^i + \frac{\delta_n^{ij}}{2}) | |
+ (\boldsymbol{n}^{ij} \times \boldsymbol{\omega}^{i}) | |
++ (r^j + \frac{\delta_n^{ij}}{2}) | |
+ (\boldsymbol{n}^{ij} \times \boldsymbol{\omega}^{j})"/></p> | |
+</div><p>The contact velocity is decomposed into normal and tangential compone… | |
+relative to the contact interface. The normal component is:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/c5671cc4a4d93628081c7bf7c0fd2cc6ff6c1cd3.png" alt="\… | |
+-(\dot{\boldsymbol{\delta}}^{ij} \cdot \boldsymbol{n}^{ij})"/></p> | |
+</div><p>and the tangential velocity component is found as:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/249d76d0a5d3773b138ce498e5df46c870f402b5.png" alt="\… | |
+\dot{\boldsymbol{\delta}}^{ij} | |
+- \boldsymbol{n}^{ij} | |
+ (\boldsymbol{n}^{ij} \cdot \dot{\boldsymbol{\delta}}^{ij})"/></p> | |
+</div><p>where <img class="math" src="_images/math/6922b3e4505e825344dc326ade5… | |
+particle. The total tangential displacement on the contact plane is found | |
+incrementally:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/213a272621ce146fc9f90ce519bc3dc1ac3337e8.png" alt="\… | |
+\int_0^{t_c} | |
+\dot{\boldsymbol{\delta}}^{ij}_t \Delta t"/></p> | |
+</div><p>where <img class="math" src="_images/math/dfc85915a4cda974f3bd5c3c52b… | |
+computational time step length. The tangential contact interface displacement … | |
+set to zero when a contact pair no longer overlaps. At each time step, the val… | |
+of <img class="math" src="_images/math/bf7f4ceb6fce455ca0347cd7b704321b92e04f8… | |
+interface:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/9c2a28916b33bc70a170729d2f237eabae96c0c0.png" alt="\… | |
+- (\boldsymbol{n} | |
+ (\boldsymbol{n} \cdot \boldsymbol{\delta}_{t,\text{uncorrected}}^{ij})"/></p> | |
+</div><p>With all the geometrical and kinetic components determined, the resul… | |
+of the particle interaction can be determined using a contact model. <tt class… | |
+features only one contact model in the normal direction to the contact; the | |
+linear-elastic-viscous (<em>Hookean</em> with viscous damping, or <em>Kelvin-V… | |
+contact model. The resulting force in the normal direction of the contact | |
+interface on particle <img class="math" src="_images/math/34857b3ba74ce5cd8607… | |
+<div class="math"> | |
+<p><img src="_images/math/30d5d726338d5b3ac9d9b0003c564594fd6176ce.png" alt="\… | |
+-k_n \delta_n^{ij} -\gamma_n \dot{\delta_n}^{ij} | |
+\right) \boldsymbol{n}^{ij}"/></p> | |
+</div><p>The parameter <img class="math" src="_images/math/fd26dda3f4f507f3fdf… | |
+contact interface, and <img class="math" src="_images/math/55f6b5380d462f954b6… | |
+viscosity, also in the normal direction. The loss of energy in this interaction | |
+due to the viscous component is for particle <img class="math" src="_images/ma… | |
+<div class="math"> | |
+<p><img src="_images/math/68e0c54203bc81b45ebe5dbf7d89fc61aa794c1e.png" alt="\… | |
+</div><p>The tangential force is determined by either a viscous-frictional con… | |
+or a elastic-viscous-frictional contact model. The former contact model is very | |
+computationally efficient, but somewhat inaccurate relative to the mechanics of | |
+real materials. The latter contact model is therefore the default, even though | |
+it results in longer computational times. The tangential force in the | |
+visco-frictional contact model:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/2e9b3638163af9fc450acac1aba9911e57a5c691.png" alt="\… | |
+</div><p><img class="math" src="_images/math/55f6b5380d462f954b65d7a2cd05e02ff… | |
+direction. The tangential displacement along the contact interface | |
+(<img class="math" src="_images/math/bf7f4ceb6fce455ca0347cd7b704321b92e04f8c.… | |
+model. The tangential force in the more realistic elastic-viscous-frictional | |
+contact model:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/18950ecc92ecc7397337b5f39323453647e6434a.png" alt="\… | |
+-k_t \boldsymbol{\delta}_t^{ij} -\gamma_t \dot{\boldsymbol{\delta}_t}^{ij}"/><… | |
+</div><p>The parameter <img class="math" src="_images/math/fd26dda3f4f507f3fdf… | |
+direction of the contact interface. Note that the tangential force is only | |
+found if the tangential displacement (<img class="math" src="_images/math/2ab5… | |
+velocity (<img class="math" src="_images/math/c51284f727b4cb7dac214d3517421141… | |
+zero. Otherwise it is defined as being <img class="math" src="_images/math/d5b… | |
+<p>For both types of contact model, the tangential force is limited by the Cou… | |
+criterion of static and dynamic friction:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/1d24d160d60604202abbae47aba869e96fb450aa.png" alt="|… | |
+\begin{cases} | |
+\mu_s ||\boldsymbol{f}^{ij}_n|| & | |
+ \text{if} \quad ||\boldsymbol{f}_t^{ij}|| = 0 \\ | |
+\mu_d ||\boldsymbol{f}^{ij}_n|| & | |
+ \text{if} \quad ||\boldsymbol{f}_t^{ij}|| > 0 | |
+\end{cases}"/></p> | |
+</div><p>If the elastic-viscous-frictional contact model is used and the Coulo… | |
+reached, the tangential displacement along the contact interface is limited to | |
+this value:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/ac73c7c8c596c32306e6ea203ebd039dd3538f06.png" alt="\… | |
+\frac{1}{k_t} \left( | |
+\mu_d ||\boldsymbol{f}_n^{ij}|| | |
+\frac{\boldsymbol{f}^{ij}_t}{||\boldsymbol{f}^{ij}_t||} | |
++ \gamma_t \dot{\boldsymbol{\delta}}_t^{ij} \right)"/></p> | |
+</div><p>If the tangential force reaches the Coulomb limit, the energy lost du… | |
+frictional dissipation is calculated as:</p> | |
+<div class="math"> | |
+<p><img src="_images/math/9d0d9139c35e749d7b4698592c91045d086d8b99.png" alt="\… | |
+\dot{\boldsymbol{\delta}}_t^{ij} \Delta t||}{\Delta t}"/></p> | |
+</div><p>The loss of energy by viscous dissipation in the tangential direction… | |
+found.</p> | |
+</div> | |
+<div class="section" id="temporal-integration"> | |
+<h2>Temporal integration<a class="headerlink" href="#temporal-integration" tit… | |
+<p>In the DEM, the time is discretized into small steps (<img class="math" src… | |
+step, the entire network of contacts is resolved, and the resulting forces and | |
+torques for each particle are found. With these values at hand, the new | |
+linear and rotational accelerations can be found using | |
+<a class="reference external" href="https://en.wikipedia.org/wiki/Newton%27s_l… | |
+of the motion of solid bodies. If a particle with mass <img class="math" src="… | |
+experiences a sum of forces denoted <img class="math" src="_images/math/5ed374… | |
+(<img class="math" src="_images/math/486938d1819df5972cb80ef2d101517b20412366.… | |
+<div class="math"> | |
+<p><img src="_images/math/c78863bcf0ada3d17f98c21c00ca8b115f6b61de.png" alt="\… | |
+</div><p>The new velocity and position is found by integrating the above equat… | |
+with regards to time. The simplest integration scheme in this regard is the | |
+<a class="reference external" href="https://en.wikipedia.org/wiki/Euler_method… | |
+<div class="math"> | |
+<p><img src="_images/math/fca2f0caf945233a44e5c1c1479f13d636edbf42.png" alt="\… | |
+</div><div class="math"> | |
+<p><img src="_images/math/93ddc61f81c056650ee9c02c3e62ed84e4a5327f.png" alt="\… | |
+</div></div> | |
</div> | |
t@@ -76,12 +227,22 @@ spheres, which keeps contact-searching algorithms simple.… | |
</div> | |
<div class="sphinxsidebar"> | |
<div class="sphinxsidebarwrapper"> | |
+ <h3><a href="index.html">Table Of Contents</a></h3> | |
+ <ul> | |
+<li><a class="reference internal" href="#">Discrete element method</a><ul> | |
+<li><a class="reference internal" href="#contact-search">Contact search</a></l… | |
+<li><a class="reference internal" href="#contact-interaction">Contact interact… | |
+<li><a class="reference internal" href="#temporal-integration">Temporal integr… | |
+</ul> | |
+</li> | |
+</ul> | |
+ | |
<h4>Previous topic</h4> | |
<p class="topless"><a href="introduction.html" | |
title="previous chapter">Introduction</a></p> | |
<h4>Next topic</h4> | |
- <p class="topless"><a href="python_api.html" | |
- title="next chapter">Python API</a></p> | |
+ <p class="topless"><a href="cfd.html" | |
+ title="next chapter">Fluid simulation and particle-flu… | |
<h3>This Page</h3> | |
<ul class="this-page-menu"> | |
<li><a href="_sources/dem.txt" | |
t@@ -114,16 +275,16 @@ spheres, which keeps contact-searching algorithms simple… | |
<a href="py-modindex.html" title="Python Module Index" | |
>modules</a> |</li> | |
<li class="right" > | |
- <a href="python_api.html" title="Python API" | |
+ <a href="cfd.html" title="Fluid simulation and particle-fluid intera… | |
>next</a> |</li> | |
<li class="right" > | |
<a href="introduction.html" title="Introduction" | |
>previous</a> |</li> | |
- <li><a href="index.html">sphere 0.35 documentation</a> »</li> | |
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> »</… | |
</ul> | |
</div> | |
<div class="footer"> | |
- © Copyright 2012, Anders Damsgaard. | |
+ © Copyright 2014, Anders Damsgaard. | |
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.3. | |
</div> | |
</body> | |
diff --git a/doc/html/genindex.html b/doc/html/genindex.html | |
t@@ -10,7 +10,7 @@ | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |
- <title>Index — sphere 0.35 documentation</title> | |
+ <title>Index — sphere 1.00-alpha documentation</title> | |
<link rel="stylesheet" href="_static/default.css" type="text/css" /> | |
<link rel="stylesheet" href="_static/pygments.css" type="text/css" /> | |
t@@ -19,7 +19,7 @@ | |
<script type="text/javascript"> | |
var DOCUMENTATION_OPTIONS = { | |
URL_ROOT: '', | |
- VERSION: '0.35', | |
+ VERSION: '1.00-alpha', | |
COLLAPSE_INDEX: false, | |
FILE_SUFFIX: '.html', | |
HAS_SOURCE: true | |
t@@ -28,7 +28,7 @@ | |
<script type="text/javascript" src="_static/jquery.js"></script> | |
<script type="text/javascript" src="_static/underscore.js"></script> | |
<script type="text/javascript" src="_static/doctools.js"></script> | |
- <link rel="top" title="sphere 0.35 documentation" href="index.html" /> | |
+ <link rel="top" title="sphere 1.00-alpha documentation" href="index.html" … | |
</head> | |
<body> | |
<div class="related"> | |
t@@ -40,7 +40,7 @@ | |
<li class="right" > | |
<a href="py-modindex.html" title="Python Module Index" | |
>modules</a> |</li> | |
- <li><a href="index.html">sphere 0.35 documentation</a> »</li> | |
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> »</… | |
</ul> | |
</div> | |
t@@ -75,7 +75,21 @@ | |
<table style="width: 100%" class="indextable genindextable"><tr> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.adjustUpperWall">adjustUpperWa… | |
+ <dt><a href="python_api.html#sphere.sim.acceleration">acceleration() (sphere… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.addParticle">addParticle() (sphere.s… | |
+ </dt> | |
+ | |
+ </dl></td> | |
+ <td style="width: 33%" valign="top"><dl> | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.adjustUpperWall">adjustUpperWall() (… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.adjustWall">adjustWall() (sphere.sim… | |
</dt> | |
</dl></td> | |
t@@ -85,17 +99,17 @@ | |
<table style="width: 100%" class="indextable genindextable"><tr> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.bond">bond() (sphere.Spherebin… | |
+ <dt><a href="python_api.html#sphere.sim.bond">bond() (sphere.sim method)</a> | |
</dt> | |
- <dt><a href="python_api.html#sphere.Spherebin.bondsRose">bondsRose() (sphere… | |
+ <dt><a href="python_api.html#sphere.sim.bondsRose">bondsRose() (sphere.sim m… | |
</dt> | |
</dl></td> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.bulkPorosity">bulkPorosity() (… | |
+ <dt><a href="python_api.html#sphere.sim.bulkPorosity">bulkPorosity() (sphere… | |
</dt> | |
</dl></td> | |
t@@ -108,22 +122,32 @@ | |
<dt><a href="python_api.html#sphere.cleanup">cleanup() (in module sphere)</a> | |
</dt> | |
+ <dd><dl> | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.cleanup">(sphere.sim method)</a> | |
+ </dt> | |
+ | |
+ </dl></dd> | |
- <dt><a href="python_api.html#sphere.Spherebin.consolidate">consolidate() (sp… | |
+ <dt><a href="python_api.html#sphere.sim.consolidate">consolidate() (sphere.s… | |
</dt> | |
- <dt><a href="python_api.html#sphere.convert">convert() (in module sphere)</a> | |
+ <dt><a href="python_api.html#sphere.sim.contactModel">contactModel() (sphere… | |
</dt> | |
</dl></td> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.createBondPair">createBondPair… | |
+ <dt><a href="python_api.html#sphere.convert">convert() (in module sphere)</a> | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.createBondPair">createBondPair() (sp… | |
</dt> | |
- <dt><a href="python_api.html#sphere.Spherebin.currentDevs">currentDevs() (sp… | |
+ <dt><a href="python_api.html#sphere.sim.currentNormalStress">currentNormalSt… | |
</dt> | |
</dl></td> | |
t@@ -133,7 +157,17 @@ | |
<table style="width: 100%" class="indextable genindextable"><tr> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.defaultParams">defaultParams()… | |
+ <dt><a href="python_api.html#sphere.sim.defaultParams">defaultParams() (sphe… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.defineWorldBoundaries">defineWorldBo… | |
+ </dt> | |
+ | |
+ </dl></td> | |
+ <td style="width: 33%" valign="top"><dl> | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.disableFluidPressureModulation">disa… | |
</dt> | |
</dl></td> | |
t@@ -143,7 +177,7 @@ | |
<table style="width: 100%" class="indextable genindextable"><tr> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.energy">energy() (sphere.Spher… | |
+ <dt><a href="python_api.html#sphere.sim.energy">energy() (sphere.sim method)… | |
</dt> | |
</dl></td> | |
t@@ -153,13 +187,13 @@ | |
<table style="width: 100%" class="indextable genindextable"><tr> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.forcechains">forcechains() (sp… | |
+ <dt><a href="python_api.html#sphere.sim.forcechains">forcechains() (sphere.s… | |
</dt> | |
</dl></td> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.forcechainsRose">forcechainsRo… | |
+ <dt><a href="python_api.html#sphere.sim.forcechainsRose">forcechainsRose() (… | |
</dt> | |
</dl></td> | |
t@@ -169,13 +203,13 @@ | |
<table style="width: 100%" class="indextable genindextable"><tr> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.generateBimodalRadii">generate… | |
+ <dt><a href="python_api.html#sphere.sim.generateBimodalRadii">generateBimoda… | |
</dt> | |
</dl></td> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.generateRadii">generateRadii()… | |
+ <dt><a href="python_api.html#sphere.sim.generateRadii">generateRadii() (sphe… | |
</dt> | |
</dl></td> | |
t@@ -185,29 +219,33 @@ | |
<table style="width: 100%" class="indextable genindextable"><tr> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.initGrid">initGrid() (sphere.S… | |
+ <dt><a href="python_api.html#sphere.sim.initFluid">initFluid() (sphere.sim m… | |
</dt> | |
- <dt><a href="python_api.html#sphere.Spherebin.initGridAndWorldsize">initGrid… | |
+ <dt><a href="python_api.html#sphere.sim.initGrid">initGrid() (sphere.sim met… | |
</dt> | |
- <dt><a href="python_api.html#sphere.Spherebin.initGridPos">initGridPos() (sp… | |
+ <dt><a href="python_api.html#sphere.sim.initGridAndWorldsize">initGridAndWor… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.initGridPos">initGridPos() (sphere.s… | |
</dt> | |
</dl></td> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.initRandomGridPos">initRandomG… | |
+ <dt><a href="python_api.html#sphere.sim.initRandomGridPos">initRandomGridPos… | |
</dt> | |
- <dt><a href="python_api.html#sphere.Spherebin.initRandomPos">initRandomPos()… | |
+ <dt><a href="python_api.html#sphere.sim.initRandomPos">initRandomPos() (sphe… | |
</dt> | |
- <dt><a href="python_api.html#sphere.Spherebin.initTemporal">initTemporal() (… | |
+ <dt><a href="python_api.html#sphere.sim.initTemporal">initTemporal() (sphere… | |
</dt> | |
</dl></td> | |
t@@ -217,13 +255,49 @@ | |
<table style="width: 100%" class="indextable genindextable"><tr> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.porosities">porosities() (sphe… | |
+ <dt><a href="python_api.html#sphere.sim.periodicBoundariesX">periodicBoundar… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.periodicBoundariesXY">periodicBounda… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.plotConvergence">plotConvergence() (… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.plotFluidDiffAdvPresZ">plotFluidDiff… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.plotFluidPressuresY">plotFluidPressu… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.plotFluidPressuresZ">plotFluidPressu… | |
</dt> | |
</dl></td> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.porosity">porosity() (sphere.S… | |
+ <dt><a href="python_api.html#sphere.sim.plotFluidVelocitiesY">plotFluidVeloc… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.plotFluidVelocitiesZ">plotFluidVeloc… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.plotPrescribedFluidPressures">plotPr… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.porosities">porosities() (sphere.sim… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.porosity">porosity() (sphere.sim met… | |
</dt> | |
</dl></td> | |
t@@ -233,22 +307,38 @@ | |
<table style="width: 100%" class="indextable genindextable"><tr> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.random2bonds">random2bonds() (… | |
+ <dt><a href="python_api.html#sphere.sim.randomBondPairs">randomBondPairs() (… | |
</dt> | |
- <dt><a href="python_api.html#sphere.Spherebin.readbin">readbin() (sphere.Sph… | |
+ <dt><a href="python_api.html#sphere.sim.readbin">readbin() (sphere.sim metho… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.readfirst">readfirst() (sphere.sim m… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.readlast">readlast() (sphere.sim met… | |
</dt> | |
</dl></td> | |
<td style="width: 33%" valign="top"><dl> | |
+ <dt><a href="python_api.html#sphere.sim.readsecond">readsecond() (sphere.sim… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.readstep">readstep() (sphere.sim met… | |
+ </dt> | |
+ | |
+ | |
<dt><a href="python_api.html#sphere.render">render() (in module sphere)</a> | |
</dt> | |
<dd><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.render">(sphere.Spherebin meth… | |
+ <dt><a href="python_api.html#sphere.sim.render">(sphere.sim method)</a> | |
</dt> | |
</dl></dd> | |
t@@ -258,7 +348,7 @@ | |
<dd><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.run">(sphere.Spherebin method)… | |
+ <dt><a href="python_api.html#sphere.sim.run">(sphere.sim method)</a> | |
</dt> | |
</dl></dd> | |
t@@ -269,35 +359,65 @@ | |
<table style="width: 100%" class="indextable genindextable"><tr> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.shear">shear() (sphere.Sphereb… | |
+ <dt><a href="python_api.html#sphere.sim.setBeta">setBeta() (sphere.sim metho… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.setFluidPressureModulation">setFluid… | |
</dt> | |
- <dt><a href="python_api.html#sphere.Spherebin.sheardisp">sheardisp() (sphere… | |
+ <dt><a href="python_api.html#sphere.sim.setGamma">setGamma() (sphere.sim met… | |
</dt> | |
- <dt><a href="python_api.html#sphere.Spherebin.shearstrain">shearstrain() (sp… | |
+ <dt><a href="python_api.html#sphere.sim.setMaxIterations">setMaxIterations()… | |
</dt> | |
- <dt><a href="python_api.html#sphere.Spherebin.shearvel">shearvel() (sphere.S… | |
+ <dt><a href="python_api.html#sphere.sim.setTheta">setTheta() (sphere.sim met… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.setTolerance">setTolerance() (sphere… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.shear">shear() (sphere.sim method)</… | |
</dt> | |
</dl></td> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#module-sphere">sphere (module)</a> | |
+ <dt><a href="python_api.html#sphere.sim.sheardisp">sheardisp() (sphere.sim m… | |
</dt> | |
- <dt><a href="python_api.html#sphere.Spherebin">Spherebin (class in sphere)</… | |
+ <dt><a href="python_api.html#sphere.sim.shearStrain">shearStrain() (sphere.s… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.shearVel">shearVel() (sphere.sim met… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim">sim (class in sphere)</a> | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#module-sphere">sphere (module)</a> | |
</dt> | |
<dt><a href="python_api.html#sphere.status">status() (in module sphere)</a> | |
</dt> | |
+ <dd><dl> | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.status">(sphere.sim method)</a> | |
+ </dt> | |
+ | |
+ </dl></dd> | |
</dl></td> | |
</tr></table> | |
t@@ -305,7 +425,7 @@ | |
<table style="width: 100%" class="indextable genindextable"><tr> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.thinsection_x1x3">thinsection_… | |
+ <dt><a href="python_api.html#sphere.sim.thinsection_x1x3">thinsection_x1x3()… | |
</dt> | |
t@@ -313,7 +433,7 @@ | |
</dt> | |
- <dt><a href="python_api.html#sphere.Spherebin.torqueScript">torqueScript() (… | |
+ <dt><a href="python_api.html#sphere.sim.torqueScript">torqueScript() (sphere… | |
</dt> | |
</dl></td> | |
t@@ -323,7 +443,11 @@ | |
</dt> | |
- <dt><a href="python_api.html#sphere.torqueScriptSerial3">torqueScriptSerial3… | |
+ <dt><a href="python_api.html#sphere.sim.totalMomentum">totalMomentum() (sphe… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.triaxial">triaxial() (sphere.sim met… | |
</dt> | |
</dl></td> | |
t@@ -333,7 +457,7 @@ | |
<table style="width: 100%" class="indextable genindextable"><tr> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.uniaxialStrainRate">uniaxialSt… | |
+ <dt><a href="python_api.html#sphere.sim.uniaxialStrainRate">uniaxialStrainRa… | |
</dt> | |
</dl></td> | |
t@@ -347,9 +471,19 @@ | |
</dt> | |
+ <dt><a href="python_api.html#sphere.vector_norm">vector_norm() (in module sp… | |
+ </dt> | |
+ | |
+ | |
<dt><a href="python_api.html#sphere.video">video() (in module sphere)</a> | |
</dt> | |
+ <dd><dl> | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.video">(sphere.sim method)</a> | |
+ </dt> | |
+ | |
+ </dl></dd> | |
</dl></td> | |
<td style="width: 33%" valign="top"><dl> | |
t@@ -357,7 +491,7 @@ | |
</dt> | |
- <dt><a href="python_api.html#sphere.Spherebin.voidRatio">voidRatio() (sphere… | |
+ <dt><a href="python_api.html#sphere.sim.voidRatio">voidRatio() (sphere.sim m… | |
</dt> | |
</dl></td> | |
t@@ -367,7 +501,21 @@ | |
<table style="width: 100%" class="indextable genindextable"><tr> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.writebin">writebin() (sphere.S… | |
+ <dt><a href="python_api.html#sphere.sim.writebin">writebin() (sphere.sim met… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.writeFluidVTK">writeFluidVTK() (sphe… | |
+ </dt> | |
+ | |
+ </dl></td> | |
+ <td style="width: 33%" valign="top"><dl> | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.writeVTK">writeVTK() (sphere.sim met… | |
+ </dt> | |
+ | |
+ | |
+ <dt><a href="python_api.html#sphere.sim.writeVTKall">writeVTKall() (sphere.s… | |
</dt> | |
</dl></td> | |
t@@ -377,7 +525,7 @@ | |
<table style="width: 100%" class="indextable genindextable"><tr> | |
<td style="width: 33%" valign="top"><dl> | |
- <dt><a href="python_api.html#sphere.Spherebin.zeroKinematics">zeroKinematics… | |
+ <dt><a href="python_api.html#sphere.sim.zeroKinematics">zeroKinematics() (sp… | |
</dt> | |
</dl></td> | |
t@@ -419,11 +567,11 @@ | |
<li class="right" > | |
<a href="py-modindex.html" title="Python Module Index" | |
>modules</a> |</li> | |
- <li><a href="index.html">sphere 0.35 documentation</a> »</li> | |
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> »</… | |
</ul> | |
</div> | |
<div class="footer"> | |
- © Copyright 2012, Anders Damsgaard. | |
+ © Copyright 2014, Anders Damsgaard. | |
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.3. | |
</div> | |
</body> | |
diff --git a/doc/html/index.html b/doc/html/index.html | |
t@@ -8,7 +8,7 @@ | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |
- <title>Welcome to sphere’s documentation! — sphere 0.35 documentat… | |
+ <title>The sphere documentation — sphere 1.00-alpha documentation</t… | |
<link rel="stylesheet" href="_static/default.css" type="text/css" /> | |
<link rel="stylesheet" href="_static/pygments.css" type="text/css" /> | |
t@@ -17,7 +17,7 @@ | |
<script type="text/javascript"> | |
var DOCUMENTATION_OPTIONS = { | |
URL_ROOT: '', | |
- VERSION: '0.35', | |
+ VERSION: '1.00-alpha', | |
COLLAPSE_INDEX: false, | |
FILE_SUFFIX: '.html', | |
HAS_SOURCE: true | |
t@@ -26,7 +26,7 @@ | |
<script type="text/javascript" src="_static/jquery.js"></script> | |
<script type="text/javascript" src="_static/underscore.js"></script> | |
<script type="text/javascript" src="_static/doctools.js"></script> | |
- <link rel="top" title="sphere 0.35 documentation" href="#" /> | |
+ <link rel="top" title="sphere 1.00-alpha documentation" href="#" /> | |
<link rel="next" title="Introduction" href="introduction.html" /> | |
</head> | |
<body> | |
t@@ -42,7 +42,7 @@ | |
<li class="right" > | |
<a href="introduction.html" title="Introduction" | |
accesskey="N">next</a> |</li> | |
- <li><a href="#">sphere 0.35 documentation</a> »</li> | |
+ <li><a href="#">sphere 1.00-alpha documentation</a> »</li> | |
</ul> | |
</div> | |
t@@ -51,44 +51,60 @@ | |
<div class="bodywrapper"> | |
<div class="body"> | |
- <div class="section" id="welcome-to-sphere-s-documentation"> | |
-<h1>Welcome to sphere’s documentation!<a class="headerlink" href="#welco… | |
-<p>This is the official documentation for the <em>sphere</em> discrete element… | |
-software. It presents the theory behind the discrete element method (DEM), the | |
-structure of the software source code, and the Python API for handling | |
-simulation setup and data analysis.</p> | |
-<p><em>sphere</em> is developed by Anders Damsgaard Christensen under supervis… | |
-Lunbek Egholm and Jan A. Piotrowski, all of the department of Geoscience, Aarh… | |
-University, Denmark. This document is a work in progress, and is still in an | |
-early state.</p> | |
-<p>Contact: Anders Damsgaard Christensen, <a class="reference external" href="… | |
+ <div class="section" id="the-sphere-documentation"> | |
+<h1>The sphere documentation<a class="headerlink" href="#the-sphere-documentat… | |
+<p>This is the official documentation for the <tt class="docutils literal"><sp… | |
+software. This document aims at guiding the installation process, documenting | |
+the usage, and explaining the relevant theory.</p> | |
+<p><tt class="docutils literal"><span class="pre">sphere</span></tt> is develo… | |
+supervision of David Lundbek Egholm and Jan A. Piotrowski, all of the Departme… | |
+of Geoscience, Aarhus University, Denmark. The author welcomes interested third | |
+party developers. This document is a work in progress.</p> | |
+<p>Contact: Anders Damsgaard, <a class="reference external" href="http://cs.au… | |
<a class="reference external" href="mailto:anders.damsgaard%40… | |
-<p>Contents:</p> | |
+<div class="section" id="contents"> | |
+<h2>Contents<a class="headerlink" href="#contents" title="Permalink to this he… | |
<div class="toctree-wrapper compound"> | |
<ul> | |
<li class="toctree-l1"><a class="reference internal" href="introduction.html">… | |
<li class="toctree-l2"><a class="reference internal" href="introduction.html#r… | |
-<li class="toctree-l2"><a class="reference internal" href="introduction.html#b… | |
+<li class="toctree-l2"><a class="reference internal" href="introduction.html#o… | |
+<li class="toctree-l2"><a class="reference internal" href="introduction.html#b… | |
+<li class="toctree-l2"><a class="reference internal" href="introduction.html#u… | |
<li class="toctree-l2"><a class="reference internal" href="introduction.html#w… | |
</ul> | |
</li> | |
-<li class="toctree-l1"><a class="reference internal" href="dem.html">Discrete … | |
-<li class="toctree-l1"><a class="reference internal" href="python_api.html">Py… | |
-<li class="toctree-l1"><a class="reference internal" href="sphere_internals.ht… | |
-<li class="toctree-l2"><a class="reference internal" href="sphere_internals.ht… | |
-<li class="toctree-l2"><a class="reference internal" href="sphere_internals.ht… | |
+<li class="toctree-l1"><a class="reference internal" href="dem.html">Discrete … | |
+<li class="toctree-l2"><a class="reference internal" href="dem.html#contact-se… | |
+<li class="toctree-l2"><a class="reference internal" href="dem.html#contact-in… | |
+<li class="toctree-l2"><a class="reference internal" href="dem.html#temporal-i… | |
+</ul> | |
+</li> | |
+<li class="toctree-l1"><a class="reference internal" href="cfd.html">Fluid sim… | |
+<li class="toctree-l2"><a class="reference internal" href="cfd.html#derivation… | |
+<li class="toctree-l2"><a class="reference internal" href="cfd.html#porosity-e… | |
+<li class="toctree-l2"><a class="reference internal" href="cfd.html#particle-f… | |
+<li class="toctree-l2"><a class="reference internal" href="cfd.html#fluid-dyna… | |
+<li class="toctree-l2"><a class="reference internal" href="cfd.html#boundary-c… | |
+<li class="toctree-l2"><a class="reference internal" href="cfd.html#numerical-… | |
+</ul> | |
+</li> | |
+<li class="toctree-l1"><a class="reference internal" href="python_api.html">Py… | |
+<li class="toctree-l2"><a class="reference internal" href="python_api.html#sam… | |
+<li class="toctree-l2"><a class="reference internal" href="python_api.html#mod… | |
</ul> | |
</li> | |
</ul> | |
</div> | |
</div> | |
<div class="section" id="indices-and-tables"> | |
-<h1>Indices and tables<a class="headerlink" href="#indices-and-tables" title="… | |
+<h2>Indices and tables<a class="headerlink" href="#indices-and-tables" title="… | |
<ul class="simple"> | |
<li><a class="reference internal" href="genindex.html"><em>Index</em></a></li> | |
<li><a class="reference internal" href="search.html"><em>Search Page</em></a><… | |
</ul> | |
</div> | |
+</div> | |
</div> | |
t@@ -98,11 +114,14 @@ early state.</p> | |
<div class="sphinxsidebarwrapper"> | |
<h3><a href="#">Table Of Contents</a></h3> | |
<ul> | |
-<li><a class="reference internal" href="#">Welcome to sphere’s documenta… | |
+<li><a class="reference internal" href="#">The sphere documentation</a><ul> | |
+<li><a class="reference internal" href="#contents">Contents</a><ul> | |
</ul> | |
</li> | |
<li><a class="reference internal" href="#indices-and-tables">Indices and table… | |
</ul> | |
+</li> | |
+</ul> | |
<h4>Next topic</h4> | |
<p class="topless"><a href="introduction.html" | |
t@@ -141,11 +160,11 @@ early state.</p> | |
<li class="right" > | |
<a href="introduction.html" title="Introduction" | |
>next</a> |</li> | |
- <li><a href="#">sphere 0.35 documentation</a> »</li> | |
+ <li><a href="#">sphere 1.00-alpha documentation</a> »</li> | |
</ul> | |
</div> | |
<div class="footer"> | |
- © Copyright 2012, Anders Damsgaard. | |
+ © Copyright 2014, Anders Damsgaard. | |
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.3. | |
</div> | |
</body> | |
diff --git a/doc/html/introduction.html b/doc/html/introduction.html | |
t@@ -8,7 +8,7 @@ | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |
- <title>Introduction — sphere 0.35 documentation</title> | |
+ <title>Introduction — sphere 1.00-alpha documentation</title> | |
<link rel="stylesheet" href="_static/default.css" type="text/css" /> | |
<link rel="stylesheet" href="_static/pygments.css" type="text/css" /> | |
t@@ -17,7 +17,7 @@ | |
<script type="text/javascript"> | |
var DOCUMENTATION_OPTIONS = { | |
URL_ROOT: '', | |
- VERSION: '0.35', | |
+ VERSION: '1.00-alpha', | |
COLLAPSE_INDEX: false, | |
FILE_SUFFIX: '.html', | |
HAS_SOURCE: true | |
t@@ -26,9 +26,9 @@ | |
<script type="text/javascript" src="_static/jquery.js"></script> | |
<script type="text/javascript" src="_static/underscore.js"></script> | |
<script type="text/javascript" src="_static/doctools.js"></script> | |
- <link rel="top" title="sphere 0.35 documentation" href="index.html" /> | |
+ <link rel="top" title="sphere 1.00-alpha documentation" href="index.html" … | |
<link rel="next" title="Discrete element method" href="dem.html" /> | |
- <link rel="prev" title="Welcome to sphere’s documentation!" href="index.… | |
+ <link rel="prev" title="The sphere documentation" href="index.html" /> | |
</head> | |
<body> | |
<div class="related"> | |
t@@ -44,9 +44,9 @@ | |
<a href="dem.html" title="Discrete element method" | |
accesskey="N">next</a> |</li> | |
<li class="right" > | |
- <a href="index.html" title="Welcome to sphere’s documentation!" | |
+ <a href="index.html" title="The sphere documentation" | |
accesskey="P">previous</a> |</li> | |
- <li><a href="index.html">sphere 0.35 documentation</a> »</li> | |
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> »</… | |
</ul> | |
</div> | |
t@@ -57,56 +57,75 @@ | |
<div class="section" id="introduction"> | |
<h1>Introduction<a class="headerlink" href="#introduction" title="Permalink to… | |
-<p>The <em>sphere</em>-software is used for three-dimensional discrete element… | |
+<p>The <tt class="docutils literal"><span class="pre">sphere</span></tt>-softw… | |
(DEM) particle simulations. The source code is written in C++, CUDA C and | |
Python, and is compiled by the user. The main computations are performed on the | |
graphics processing unit (GPU) using NVIDIA’s general purpose parallel c… | |
architecture, CUDA. Simulation setup and data analysis is performed with the | |
included Python API.</p> | |
-<p>The ultimate aim of the <em>sphere</em> software is to simulate soft-bedded… | |
+<p>The ultimate aim of the <tt class="docutils literal"><span class="pre">sphe… | |
conditions, while retaining the flexibility to perform simulations of granular | |
material in other environments.</p> | |
<p>The purpose of this documentation is to provide the user with a walk-throug… | |
the installation, work-flow, data-analysis and visualization methods of | |
-<em>sphere</em>. In addition, the <em>sphere</em> internals are exposed to pro… | |
+<tt class="docutils literal"><span class="pre">sphere</span></tt>. In addition… | |
understanding of the discrete element method numerical routines taking place.<… | |
<div class="admonition note"> | |
<p class="first admonition-title">Note</p> | |
-<p class="last">Command examples in this document starting with the symbol <tt… | |
+<p class="last">Command examples in this document starting with the symbol <tt… | |
+meant to be executed in the shell of the operational system, and <tt class="do… | |
+means execution in Python. <a class="reference external" href="http://ipython.… | |
+interactive Python shell.</p> | |
</div> | |
<p>All numerical values in this document, the source code, and the configurati… | |
files are typeset with strict respect to the SI unit system.</p> | |
<div class="section" id="requirements"> | |
<h2>Requirements<a class="headerlink" href="#requirements" title="Permalink to… | |
-<dl class="docutils"> | |
-<dt>The build requirements are:</dt> | |
-<dd><ul class="first last simple"> | |
+<p>The build requirements are:</p> | |
+<blockquote> | |
+<div><ul class="simple"> | |
<li>A Nvidia CUDA-supported version of Linux or Mac OS X (see the <a class="re… | |
release notes</a> for more information)</li> | |
<li><a class="reference external" href="https://www.gnu.org/software/make/">GN… | |
-<li><a class="reference external" href="http://www.cmake.org">CMake</a></li> | |
+<li><a class="reference external" href="http://www.cmake.org">CMake</a>, versi… | |
<li>The <a class="reference external" href="http://gcc.gnu.org/">GNU Compiler … | |
-<li>The <a class="reference external" href="https://developer.nvidia.com/cuda-… | |
+<li>The <a class="reference external" href="https://developer.nvidia.com/cuda-… | |
+version 5.0 or newer</li> | |
</ul> | |
-</dd> | |
-<dt>The runtime requirements are:</dt> | |
-<dd><ul class="first last simple"> | |
+</div></blockquote> | |
+<p>In Debian GNU/Linux, these dependencies can be installed by running:</p> | |
+<div class="highlight-python"><pre>$ sudo apt-get install build-essential cmak… | |
+</div> | |
+<p>Unfortunately, the Nvidia Toolkit is shipped under a non-free license. In o… | |
+to install it in Debian GNU/Linux, add <tt class="docutils literal"><span clas… | |
+<tt class="docutils literal"><span class="pre">/etc/apt/sources.list</span></t… | |
+<p>The runtime requirements are:</p> | |
+<blockquote> | |
+<div><ul class="simple"> | |
<li>A <a class="reference external" href="http://www.nvidia.com/object/cuda_gp… | |
compute capability 1.1 or greater.</li> | |
<li>A Nvidia CUDA-enabled GPU and device driver</li> | |
</ul> | |
-</dd> | |
-<dt>Optional tools, required for simulation setup and data processing:</dt> | |
-<dd><ul class="first last simple"> | |
-<li><a class="reference external" href="http://www.python.org/getit/releases/2… | |
+</div></blockquote> | |
+<p>Optional tools, required for simulation setup and data processing:</p> | |
+<blockquote> | |
+<div><ul class="simple"> | |
+<li><a class="reference external" href="http://www.python.org/">Python</a></li> | |
<li><a class="reference external" href="http://numpy.scipy.org">Numpy</a></li> | |
<li><a class="reference external" href="http://matplotlib.org">Matplotlib</a><… | |
+<li><a class="reference external" href="http://www.vtk.org">Python bindings fo… | |
<li><a class="reference external" href="http://www.imagemagick.org/script/inde… | |
-<li><a class="reference external" href="http://ffmpeg.org/">ffmpeg</a></li> | |
+<li><a class="reference external" href="http://ffmpeg.org/">ffmpeg</a>. Soon t… | |
</ul> | |
-</dd> | |
-<dt>Optional tools, required for building the documentation:</dt> | |
-<dd><ul class="first last simple"> | |
+</div></blockquote> | |
+<p>In Debian GNU/Linux, these dependencies can be installed by running:</p> | |
+<div class="highlight-python"><pre>$ sudo apt-get install python python-numpy … | |
+ imagemagick libav-tools</pre> | |
+</div> | |
+<p><tt class="docutils literal"><span class="pre">sphere</span></tt> is distri… | |
+following tools are required for building the documentation:</p> | |
+<blockquote> | |
+<div><ul class="simple"> | |
<li><a class="reference external" href="http://sphinx-doc.org">Sphinx</a><ul> | |
<li><a class="reference external" href="http://packages.python.org/sphinxcontr… | |
</ul> | |
t@@ -114,22 +133,65 @@ compute capability 1.1 or greater.</li> | |
<li><a class="reference external" href="http://www.stack.nl/~dimitri/doxygen/"… | |
<li><a class="reference external" href="http://michaeljones.github.com/breathe… | |
<li><a class="reference external" href="http://www.nongnu.org/dvipng/">dvipng<… | |
+<li><a class="reference external" href="http://www.tug.org/texlive/">TeX Live<… | |
</ul> | |
-</dd> | |
-</dl> | |
+</div></blockquote> | |
+<p>In Debian GNU/Linux, these dependencies can be installed by running:</p> | |
+<div class="highlight-python"><pre>$ sudo apt-get install python-sphinx python… | |
+ python-sphinxcontrib-programoutput texlive-full | |
+$ sudo pip install breathe</pre> | |
+</div> | |
<p><a class="reference external" href="http://git-scm.com">Git</a> is used as … | |
-platform, and the source code is maintained at <a class="reference external" h… | |
+platform, and the source code is maintained at <a class="reference external" h… | |
Public License, v.3</a>.</p> | |
+<div class="admonition note"> | |
+<p class="first admonition-title">Note</p> | |
+<p>All Debian GNU/Linux runtime, optional, and documentation dependencies | |
+mentioned above can be installed by executing the following command from the | |
+<tt class="docutils literal"><span class="pre">doc/</span></tt> folder:</p> | |
+<div class="last highlight-python"><pre>$ make install-debian-pkgs</pre> | |
+</div> | |
+</div> | |
+</div> | |
+<div class="section" id="obtaining-sphere"> | |
+<h2>Obtaining sphere<a class="headerlink" href="#obtaining-sphere" title="Perm… | |
+<p>The best way to keep up to date with subsequent updates, bugfixes and | |
+development, is to use the Git version control system. To obtain a local | |
+copy, execute:</p> | |
+<div class="highlight-python"><pre>$ git clone [email protected]:anders-dc/sphere… | |
+</div> | |
</div> | |
<div class="section" id="building-sphere"> | |
-<h2>Building <em>sphere</em><a class="headerlink" href="#building-sphere" titl… | |
-<p>All instructions required for building <em>sphere</em> are provided in a nu… | |
-<tt class="docutils literal"><span class="pre">Makefile</span></tt>‘s. T… | |
-root directory, and invoke CMake and GNU Make:</p> | |
+<h2>Building <tt class="docutils literal"><span class="pre">sphere</span></tt>… | |
+<p><tt class="docutils literal"><span class="pre">sphere</span></tt> is built … | |
+and <tt class="docutils literal"><span class="pre">nvcc</span></tt> from the N… | |
+<p>If you plan to run <tt class="docutils literal"><span class="pre">sphere</s… | |
+from the root directory:</p> | |
+<div class="highlight-python"><pre>$ cmake . && make</pre> | |
+</div> | |
+<p>If you instead plan to execute it on a Fermi GPU, change <tt class="docutil… | |
+<span class="pre">1)</span></tt> to <tt class="docutils literal"><span class="… | |
+<p>In some cases the CMake FindCUDA module will have troubles locating the | |
+CUDA samples directory, and will complain about <tt class="docutils literal"><… | |
+found.</p> | |
+<p>In that case, modify the <tt class="docutils literal"><span class="pre">CUD… | |
+<tt class="docutils literal"><span class="pre">src/CMakeLists.txt</span></tt> … | |
+<tt class="docutils literal"><span class="pre">cmake</span> <span class="pre">… | |
+sample subdirectory <tt class="docutils literal"><span class="pre">common/inc/… | |
+directory, and run <tt class="docutils literal"><span class="pre">cmake</span>… | |
+sphere cannot be distributed with this file.</p> | |
+<p>After a successfull installation, the <tt class="docutils literal"><span cl… | |
+in the root folder. To make sure that all components are working correctly, | |
+execute:</p> | |
+<div class="highlight-python"><pre>$ make test</pre> | |
+</div> | |
+<p>All instructions required for building <tt class="docutils literal"><span c… | |
+<tt class="docutils literal"><span class="pre">Makefile</span></tt>‘s. T… | |
+the root directory, and invoke CMake and GNU Make:</p> | |
<div class="highlight-python"><pre>$ cmake . && make</pre> | |
</div> | |
<p>If successfull, the Makefiles will create the required data folders, object | |
-files, as well as the <em>sphere</em> executable in the root folder. Issue the | |
+files, as well as the <tt class="docutils literal"><span class="pre">sphere</s… | |
following commands to check the executable:</p> | |
<div class="highlight-python"><pre>$ ./sphere --version</pre> | |
</div> | |
t@@ -152,8 +214,9 @@ following commands to check the executable:</p> | |
<div class="highlight-python"><pre>$ make test</pre> | |
</div> | |
<p>The documentation can be read in the <a class="reference external" href="ht… | |
-the <tt class="docutils literal"><span class="pre">doc/sphinx/</span></tt> fol… | |
-following commands:</p> | |
+the <tt class="docutils literal"><span class="pre">doc/sphinx/</span></tt> fol… | |
+<tt class="docutils literal"><span class="pre">doc/html</span></tt> and <tt cl… | |
+<p>Optionally, the documentation can be built using the following commands:</p> | |
<div class="highlight-python"><pre>$ cd doc/sphinx | |
$ make html | |
$ make latexpdf</pre> | |
t@@ -162,19 +225,26 @@ $ make latexpdf</pre> | |
<div class="highlight-python"><pre>$ make help</pre> | |
</div> | |
</div> | |
+<div class="section" id="updating-sphere"> | |
+<h2>Updating sphere<a class="headerlink" href="#updating-sphere" title="Permal… | |
+<p>To update your local version, type the following commands in the <tt class=… | |
+directory:</p> | |
+<div class="highlight-python"><pre>$ git pull && cmake . && ma… | |
+</div> | |
+</div> | |
<div class="section" id="work-flow"> | |
<h2>Work flow<a class="headerlink" href="#work-flow" title="Permalink to this … | |
-<p>After compiling the <em>sphere</em> binary, the procedure of a creating and… | |
-simulation is typically arranged in the following order:</p> | |
+<p>After compiling the <tt class="docutils literal"><span class="pre">sphere</… | |
+a simulation is typically arranged in the following order:</p> | |
<blockquote> | |
<div><ul class="simple"> | |
<li>Setup of particle assemblage, physical properties and conditions using the | |
Python API.</li> | |
-<li>Execution of <em>sphere</em> software, which simulates the particle behavi… | |
+<li>Execution of <tt class="docutils literal"><span class="pre">sphere</span><… | |
function of time, as a result of the conditions initially specified in the | |
input file.</li> | |
-<li>Inspection, analysis, interpretation and visualization of <em>sphere</em> … | |
-Python, and/or scene rendering using the built-in ray tracer.</li> | |
+<li>Inspection, analysis, interpretation and visualization of <tt class="docut… | |
+in Python, and/or scene rendering using the built-in ray tracer.</li> | |
</ul> | |
</div></blockquote> | |
</div> | |
t@@ -190,7 +260,9 @@ Python, and/or scene rendering using the built-in ray trac… | |
<ul> | |
<li><a class="reference internal" href="#">Introduction</a><ul> | |
<li><a class="reference internal" href="#requirements">Requirements</a></li> | |
-<li><a class="reference internal" href="#building-sphere">Building <em>sphere<… | |
+<li><a class="reference internal" href="#obtaining-sphere">Obtaining sphere</a… | |
+<li><a class="reference internal" href="#building-sphere">Building <tt class="… | |
+<li><a class="reference internal" href="#updating-sphere">Updating sphere</a><… | |
<li><a class="reference internal" href="#work-flow">Work flow</a></li> | |
</ul> | |
</li> | |
t@@ -198,7 +270,7 @@ Python, and/or scene rendering using the built-in ray trac… | |
<h4>Previous topic</h4> | |
<p class="topless"><a href="index.html" | |
- title="previous chapter">Welcome to sphere’s doc… | |
+ title="previous chapter">The sphere documentation</a><… | |
<h4>Next topic</h4> | |
<p class="topless"><a href="dem.html" | |
title="next chapter">Discrete element method</a></p> | |
t@@ -237,13 +309,13 @@ Python, and/or scene rendering using the built-in ray tr… | |
<a href="dem.html" title="Discrete element method" | |
>next</a> |</li> | |
<li class="right" > | |
- <a href="index.html" title="Welcome to sphere’s documentation!" | |
+ <a href="index.html" title="The sphere documentation" | |
>previous</a> |</li> | |
- <li><a href="index.html">sphere 0.35 documentation</a> »</li> | |
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> »</… | |
</ul> | |
</div> | |
<div class="footer"> | |
- © Copyright 2012, Anders Damsgaard. | |
+ © Copyright 2014, Anders Damsgaard. | |
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.3. | |
</div> | |
</body> | |
diff --git a/doc/html/objects.inv b/doc/html/objects.inv | |
Binary files differ. | |
diff --git a/doc/html/py-modindex.html b/doc/html/py-modindex.html | |
t@@ -8,7 +8,7 @@ | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |
- <title>Python Module Index — sphere 0.35 documentation</title> | |
+ <title>Python Module Index — sphere 1.00-alpha documentation</title> | |
<link rel="stylesheet" href="_static/default.css" type="text/css" /> | |
<link rel="stylesheet" href="_static/pygments.css" type="text/css" /> | |
t@@ -17,7 +17,7 @@ | |
<script type="text/javascript"> | |
var DOCUMENTATION_OPTIONS = { | |
URL_ROOT: '', | |
- VERSION: '0.35', | |
+ VERSION: '1.00-alpha', | |
COLLAPSE_INDEX: false, | |
FILE_SUFFIX: '.html', | |
HAS_SOURCE: true | |
t@@ -26,7 +26,7 @@ | |
<script type="text/javascript" src="_static/jquery.js"></script> | |
<script type="text/javascript" src="_static/underscore.js"></script> | |
<script type="text/javascript" src="_static/doctools.js"></script> | |
- <link rel="top" title="sphere 0.35 documentation" href="index.html" /> | |
+ <link rel="top" title="sphere 1.00-alpha documentation" href="index.html" … | |
<script type="text/javascript"> | |
t@@ -45,7 +45,7 @@ | |
<li class="right" > | |
<a href="#" title="Python Module Index" | |
>modules</a> |</li> | |
- <li><a href="index.html">sphere 0.35 documentation</a> »</li> | |
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> »</… | |
</ul> | |
</div> | |
t@@ -104,11 +104,11 @@ | |
<li class="right" > | |
<a href="#" title="Python Module Index" | |
>modules</a> |</li> | |
- <li><a href="index.html">sphere 0.35 documentation</a> »</li> | |
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> »</… | |
</ul> | |
</div> | |
<div class="footer"> | |
- © Copyright 2012, Anders Damsgaard. | |
+ © Copyright 2014, Anders Damsgaard. | |
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.3. | |
</div> | |
</body> | |
diff --git a/doc/html/python_api.html b/doc/html/python_api.html | |
t@@ -8,7 +8,7 @@ | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |
- <title>Python API — sphere 0.35 documentation</title> | |
+ <title>Python API — sphere 1.00-alpha documentation</title> | |
<link rel="stylesheet" href="_static/default.css" type="text/css" /> | |
<link rel="stylesheet" href="_static/pygments.css" type="text/css" /> | |
t@@ -17,7 +17,7 @@ | |
<script type="text/javascript"> | |
var DOCUMENTATION_OPTIONS = { | |
URL_ROOT: '', | |
- VERSION: '0.35', | |
+ VERSION: '1.00-alpha', | |
COLLAPSE_INDEX: false, | |
FILE_SUFFIX: '.html', | |
HAS_SOURCE: true | |
t@@ -26,9 +26,8 @@ | |
<script type="text/javascript" src="_static/jquery.js"></script> | |
<script type="text/javascript" src="_static/underscore.js"></script> | |
<script type="text/javascript" src="_static/doctools.js"></script> | |
- <link rel="top" title="sphere 0.35 documentation" href="index.html" /> | |
- <link rel="next" title="sphere internals" href="sphere_internals.html" /> | |
- <link rel="prev" title="Discrete element method" href="dem.html" /> | |
+ <link rel="top" title="sphere 1.00-alpha documentation" href="index.html" … | |
+ <link rel="prev" title="Fluid simulation and particle-fluid interaction" h… | |
</head> | |
<body> | |
<div class="related"> | |
t@@ -41,12 +40,9 @@ | |
<a href="py-modindex.html" title="Python Module Index" | |
>modules</a> |</li> | |
<li class="right" > | |
- <a href="sphere_internals.html" title="sphere internals" | |
- accesskey="N">next</a> |</li> | |
- <li class="right" > | |
- <a href="dem.html" title="Discrete element method" | |
+ <a href="cfd.html" title="Fluid simulation and particle-fluid intera… | |
accesskey="P">previous</a> |</li> | |
- <li><a href="index.html">sphere 0.35 documentation</a> »</li> | |
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> »</… | |
</ul> | |
</div> | |
t@@ -55,352 +51,1838 @@ | |
<div class="bodywrapper"> | |
<div class="body"> | |
- <div class="section" id="module-sphere"> | |
-<span id="python-api"></span><h1>Python API<a class="headerlink" href="#module… | |
+ <div class="section" id="python-api"> | |
+<h1>Python API<a class="headerlink" href="#python-api" title="Permalink to thi… | |
+<p>The Python module <tt class="docutils literal"><span class="pre">sphere</sp… | |
+application. It is recommended to use this module for simulation setup, | |
+simulation execution, and analysis of the simulation output data.</p> | |
+<p>In order to use the API, the file <tt class="docutils literal"><span class=… | |
+directory as the Python files.</p> | |
+<div class="section" id="sample-usage"> | |
+<h2>Sample usage<a class="headerlink" href="#sample-usage" title="Permalink to… | |
+<p>Below is a simple, annotated example of how to setup, execute, and post-pro… | |
+a <tt class="docutils literal"><span class="pre">sphere</span></tt> simulation… | |
+<tt class="docutils literal"><span class="pre">collision.py</span></tt>.</p> | |
+<div class="highlight-python"><table class="highlighttable"><tr><td class="lin… | |
+ 2 | |
+ 3 | |
+ 4 | |
+ 5 | |
+ 6 | |
+ 7 | |
+ 8 | |
+ 9 | |
+10 | |
+11 | |
+12 | |
+13 | |
+14 | |
+15 | |
+16 | |
+17 | |
+18 | |
+19 | |
+20 | |
+21 | |
+22 | |
+23 | |
+24 | |
+25 | |
+26 | |
+27 | |
+28 | |
+29 | |
+30 | |
+31 | |
+32 | |
+33 | |
+34 | |
+35 | |
+36 | |
+37 | |
+38 | |
+39 | |
+40 | |
+41 | |
+42 | |
+43 | |
+44 | |
+45 | |
+46 | |
+47 | |
+48 | |
+49 | |
+50 | |
+51 | |
+52 | |
+53 | |
+54 | |
+55 | |
+56 | |
+57 | |
+58 | |
+59</pre></div></td><td class="code"><div class="highlight"><pre><span class="c… | |
+<span class="sd">"""</span> | |
+<span class="sd">Example of two particles colliding.</span> | |
+<span class="sd">Place script in sphere/python/ folder, and invoke with `pytho… | |
+<span class="sd">"""</span> | |
+ | |
+<span class="c"># Import the sphere module for setting up, running, and analyz… | |
+<span class="c"># experiment. We also need the numpy module when setting array… | |
+<span class="c"># object.</span> | |
+<span class="kn">import</span> <span class="nn">sphere</span> | |
+<span class="kn">import</span> <span class="nn">numpy</span> | |
+ | |
+ | |
+<span class="c">### SIMULATION SETUP</span> | |
+ | |
+<span class="c"># Create a sphere object with two preallocated particles and a… | |
+<span class="n">SB</span> <span class="o">=</span> <span class="n">sphere</spa… | |
+ | |
+<span class="n">SB</span><span class="o">.</span><span class="n">radius</span>… | |
+ | |
+<span class="c"># Define the positions of the two particles</span> | |
+<span class="n">SB</span><span class="o">.</span><span class="n">x</span><span… | |
+<span class="n">SB</span><span class="o">.</span><span class="n">x</span><span… | |
+ | |
+<span class="c"># The default velocity is [0,0,0]. Slam particle 1 into partic… | |
+<span class="c"># a positive x velocity for particle 1.</span> | |
+<span class="n">SB</span><span class="o">.</span><span class="n">vel</span><sp… | |
+ | |
+<span class="c"># Set the world limits and the particle sorting grid. The part… | |
+<span class="c"># within the world limits for the entire simulation, otherwise… | |
+<span class="n">SB</span><span class="o">.</span><span class="n">initGridAndWo… | |
+ | |
+<span class="c"># Define the temporal parameters, e.g. the total time (total) … | |
+<span class="c"># output interval (file_dt), both in seconds</span> | |
+<span class="n">SB</span><span class="o">.</span><span class="n">initTemporal<… | |
+ | |
+<span class="c"># Using a 'dry' run, the sphere main program will disp… | |
+<span class="c"># sphere will end after displaying these values.</span> | |
+<span class="n">SB</span><span class="o">.</span><span class="n">run</span><sp… | |
+ | |
+ | |
+<span class="c">### RUNNING THE SIMULATION</span> | |
+ | |
+<span class="c"># Start the simulation on the GPU from the sphere program</spa… | |
+<span class="n">SB</span><span class="o">.</span><span class="n">run</span><sp… | |
+ | |
+ | |
+<span class="c">### ANALYSIS OF SIMULATION RESULTS</span> | |
+ | |
+<span class="c"># Plot the system energy through time, image saved as collisio… | |
+<span class="n">sphere</span><span class="o">.</span><span class="n">visualize… | |
+ | |
+<span class="c"># Render the particles using the built-in raytracer</span> | |
+<span class="n">SB</span><span class="o">.</span><span class="n">render</span>… | |
+ | |
+<span class="c"># Alternative visualization using ParaView. See the documentat… | |
+<span class="c"># ``sim.writeVTKall()`` for more information about displaying … | |
+<span class="c"># particles in ParaView.</span> | |
+<span class="n">SB</span><span class="o">.</span><span class="n">writeVTKall</… | |
+</pre></div> | |
+</td></tr></table></div> | |
+<p>The full documentation of the <tt class="docutils literal"><span class="pre… | |
+</div> | |
+<div class="section" id="module-sphere"> | |
+<span id="the-sphere-module"></span><h2>The <tt class="docutils literal"><span… | |
+<dl class="function"> | |
+<dt id="sphere.V_sphere"> | |
+<tt class="descclassname">sphere.</tt><tt class="descname">V_sphere</tt><big>(… | |
+<dd><p>Calculates the volume of a sphere with radius r</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Returns:</th><td class="fie… | |
+</tr> | |
+<tr class="field-even field"><th class="field-name">Return type:</th><td class… | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="function"> | |
+<dt id="sphere.cleanup"> | |
+<tt class="descclassname">sphere.</tt><tt class="descname">cleanup</tt><big>(<… | |
+<dd><p>Removes the input/output files and images belonging to the object simul… | |
+ID from the <tt class="docutils literal"><span class="pre">input/</span></tt>,… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="function"> | |
+<dt id="sphere.convert"> | |
+<tt class="descclassname">sphere.</tt><tt class="descname">convert</tt><big>(<… | |
+<dd><p>Converts all PPM images in img_out to graphics_format using Imagemagick… | |
+PPM images are subsequently removed.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>graphics_format</strong> (<em>str</em>) – Convert the images… | |
+<li><strong>folder</strong> (<em>str</em>) – The folder containing the P… | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="function"> | |
+<dt id="sphere.render"> | |
+<tt class="descclassname">sphere.</tt><tt class="descname">render</tt><big>(</… | |
+<dd><p>Render target binary using the <tt class="docutils literal"><span class… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>method</strong> (<em>str</em>) – The color visualization met… | |
+Possible values are: ‘normal’: color all particles with the same | |
+color, ‘pres’: color by pressure, ‘vel’: color by tran… | |
+velocity, ‘angvel’: color by rotational velocity, ‘xdispR… | |
+total displacement along the x-axis, ‘angpos’: color by angular | |
+position.</li> | |
+<li><strong>max_val</strong> (<em>float</em>) – The maximum value of the… | |
+<li><strong>lower_cutoff</strong> (<em>float</em>) – Do not render parti… | |
+value, of the field selected by <tt class="docutils literal"><span class="pre"… | |
+<li><strong>graphics_format</strong> (<em>str</em>) – Convert the PPM im… | |
+tracer to this image format using Imagemagick</li> | |
+<li><strong>verbose</strong> (<em>bool</em>) – Show verbose information … | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="function"> | |
+<dt id="sphere.run"> | |
+<tt class="descclassname">sphere.</tt><tt class="descname">run</tt><big>(</big… | |
+<dd><p>Execute <tt class="docutils literal"><span class="pre">sphere</span></t… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>binary</strong> (<em>str</em>) – Input file for <tt class="d… | |
+<li><strong>verbose</strong> (<em>bool</em>) – Show <tt class="docutils … | |
+<li><strong>hideinputfile</strong> (<em>bool</em>) – Hide the input file… | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
<dl class="class"> | |
-<dt id="sphere.Spherebin"> | |
-<em class="property">class </em><tt class="descclassname">sphere.</tt><tt clas… | |
-<dd><p>Class containing all data SPHERE data.</p> | |
+<dt id="sphere.sim"> | |
+<em class="property">class </em><tt class="descclassname">sphere.</tt><tt clas… | |
+<dd><p>Class containing all <tt class="docutils literal"><span class="pre">sph… | |
<p>Contains functions for reading and writing binaries, as well as simulation | |
-setup and data analysis.</p> | |
+setup and data analysis. Most arrays are initialized to default values.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>np</strong> (<em>int</em>) – The number of particles to allo… | |
+<li><strong>nd</strong> (<em>int</em>) – The number of spatial dimension… | |
+1D simulations currently are not possible.</li> | |
+<li><strong>nw</strong> (<em>int</em>) – The number of dynamic walls (de… | |
+<li><strong>sid</strong> (<em>str</em>) – The simulation id (default = &… | |
+will be written with this base name.</li> | |
+<li><strong>fluid</strong> (<em>bool</em>) – Setup fluid simulation (def… | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+<dl class="method"> | |
+<dt id="sphere.sim.acceleration"> | |
+<tt class="descname">acceleration</tt><big>(</big><em>idx=-1</em><big>)</big><… | |
+<dd><p>Returns the acceleration of one or more particles, selected by their | |
+index. If the index is equal to -1 (default value), all accelerations | |
+are returned.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+<tr class="field-even field"><th class="field-name">Returns:</th><td class="fi… | |
+</tr> | |
+<tr class="field-odd field"><th class="field-name">Return type:</th><td class=… | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.addParticle"> | |
+<tt class="descname">addParticle</tt><big>(</big><em>x</em>, <em>radius</em>, … | |
+<dd><p>Add a single particle to the simulation object. The only required | |
+parameters are the position (x) and the radius (radius).</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>x</strong> (<em>numpy.array</em>) – A vector pointing to the… | |
+<li><strong>radius</strong> (<em>float</em>) – The particle radius</li> | |
+<li><strong>vel</strong> (<em>numpy.array</em>) – The particle linear ve… | |
+<li><strong>fixvel</strong> (<em>float</em>) – Fix horizontal linear vel… | |
+<li><strong>angpos</strong> (<em>numpy.array</em>) – The particle angula… | |
+<li><strong>angvel</strong> (<em>numpy.array</em>) – The particle angula… | |
+<li><strong>torque</strong> (<em>numpy.array</em>) – The particle torque… | |
+<li><strong>es_dot</strong> (<em>float</em>) – The particle shear energy… | |
+<li><strong>es</strong> (<em>float</em>) – The particle shear energy los… | |
+<li><strong>ev_dot</strong> (<em>float</em>) – The particle viscous ener… | |
+<li><strong>ev</strong> (<em>float</em>) – The particle viscous energy l… | |
+<li><strong>p</strong> (<em>float</em>) – The particle pressure (default… | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.adjustUpperWall"> | |
+<tt class="descname">adjustUpperWall</tt><big>(</big><em>z_adjust=1.1</em><big… | |
+<dd><p>Included for legacy purposes, calls <a class="reference internal" href=… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+allow for wall movement.</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.adjustWall"> | |
+<tt class="descname">adjustWall</tt><big>(</big><em>idx</em>, <em>adjust=1.1</… | |
+<dd><p>Adjust grid and dynamic wall to max. particle position</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Param :</th><td class="fiel… | |
+left wall, 2 = +x, right wall, 3 = -y, front wall, 4 = +y, back | |
+wall.</td> | |
+</tr> | |
+<tr class="field-even field"><th class="field-name">Parameters:</th><td class=… | |
+allow for wall movement.</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.bond"> | |
+<tt class="descname">bond</tt><big>(</big><em>i</em>, <em>j</em><big>)</big><a… | |
+<dd><p>Create a bond between particles with index i and j</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>i</strong> (<em>int</em>) – Index of first particle in bond<… | |
+<li><strong>j</strong> (<em>int</em>) – Index of second particle in bond… | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.adjustUpperWall"> | |
-<tt class="descname">adjustUpperWall</tt><big>(</big><em>z_adjust=1.1</em><big… | |
-<dd><p>Adjust grid and dynamic upper wall to max. particle height</p> | |
+<dt id="sphere.sim.bondsRose"> | |
+<tt class="descname">bondsRose</tt><big>(</big><em>graphics_format='pdf'</em><… | |
+<dd><p>Visualize the trend and plunge angles of the bond pairs in a rose plot. | |
+The plot is saved in the current folder as | |
+‘bonds-<simulation id>-rose.<graphics_format>’.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.bond"> | |
-<tt class="descname">bond</tt><big>(</big><em>i</em>, <em>j</em><big>)</big><a… | |
-<dd><p>Create a bond between particles i and j</p> | |
+<dt id="sphere.sim.bulkPorosity"> | |
+<tt class="descname">bulkPorosity</tt><big>(</big><big>)</big><a class="header… | |
+<dd><p>Calculates the bulk porosity</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Returns:</th><td class="fie… | |
+</tr> | |
+<tr class="field-even field"><th class="field-name">Return type:</th><td class… | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.bondsRose"> | |
-<tt class="descname">bondsRose</tt><big>(</big><em>imgformat='pdf'</em><big>)<… | |
-<dd><p>Visualize strike- and dip angles of the bond pairs in a rose plot.</p> | |
+<dt id="sphere.sim.cleanup"> | |
+<tt class="descname">cleanup</tt><big>(</big><big>)</big><a class="headerlink"… | |
+<dd><p>Removes the input/output files and images belonging to the object | |
+simulation ID from the <tt class="docutils literal"><span class="pre">input/</… | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.bulkPorosity"> | |
-<tt class="descname">bulkPorosity</tt><big>(</big><big>)</big><a class="header… | |
-<dd><p>Calculate and return the bulk porosity</p> | |
+<dt id="sphere.sim.consolidate"> | |
+<tt class="descname">consolidate</tt><big>(</big><em>normal_stress=10000.0</em… | |
+<dd><p>Setup consolidation experiment. Specify the upper wall normal stress in | |
+Pascal, default value is 10 kPa.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.consolidate"> | |
-<tt class="descname">consolidate</tt><big>(</big><em>deviatoric_stress=10000.0… | |
-<dd><p>Setup consolidation experiment. Specify the upper wall | |
-deviatoric stress in Pascal, default value is 10 kPa.</p> | |
+<dt id="sphere.sim.contactModel"> | |
+<tt class="descname">contactModel</tt><big>(</big><em>contactmodel</em><big>)<… | |
+<dd><p>Define which contact model to use for the tangential component of | |
+particle-particle interactions. The elastic-viscous-frictional contact | |
+model (2) is considered to be the most realistic contact model, while | |
+the viscous-frictional contact model is significantly faster.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+(visco-frictional = 1, elasto-visco-frictional = 2)</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.createBondPair"> | |
-<tt class="descname">createBondPair</tt><big>(</big><em>i</em>, <em>j</em>, <e… | |
+<dt id="sphere.sim.createBondPair"> | |
+<tt class="descname">createBondPair</tt><big>(</big><em>i</em>, <em>j</em>, <e… | |
<dd><p>Bond particles i and j. Particle j is moved adjacent to particle i, | |
-and oriented randomly. | |
-@param spacing (float) The inter-particle distance prescribed. Positive | |
-values result in a inter-particle distance, negative equal an overlap. | |
-The value is relative to the sum of the two radii.</p> | |
+and oriented randomly.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>i</strong> (<em>int</em>) – Index of first particle in bond<… | |
+<li><strong>j</strong> (<em>int</em>) – Index of second particle in bond… | |
+<li><strong>spacing</strong> (<em>float</em>) – The inter-particle dista… | |
+values result in a inter-particle distance, negative equal an | |
+overlap. The value is relative to the sum of the two radii.</li> | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.currentDevs"> | |
-<tt class="descname">currentDevs</tt><big>(</big><big>)</big><a class="headerl… | |
-<dd><p>Return current magnitude of the deviatoric normal stress</p> | |
+<dt id="sphere.sim.currentNormalStress"> | |
+<tt class="descname">currentNormalStress</tt><big>(</big><big>)</big><a class=… | |
+<dd><p>Calculates the current magnitude of the top wall normal stress.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Returns:</th><td class="fie… | |
+</tr> | |
+<tr class="field-even field"><th class="field-name">Return type:</th><td class… | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.defaultParams"> | |
-<tt class="descname">defaultParams</tt><big>(</big><em>mu_s=0.4</em>, <em>mu_d… | |
-<dd><p>Initialize particle parameters to default values. | |
-Radii must be set prior to calling this function.</p> | |
+<dt id="sphere.sim.defaultParams"> | |
+<tt class="descname">defaultParams</tt><big>(</big><em>mu_s=0.4</em>, <em>mu_d… | |
+<dd><p>Initialize particle parameters to default values.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>mu_s</strong> (<em>float</em>) – The coefficient of static f… | |
+<li><strong>mu_d</strong> (<em>float</em>) – The coefficient of dynamic … | |
+<li><strong>rho</strong> (<em>float</em>) – The density of the particle … | |
+<li><strong>k_n</strong> (<em>float</em>) – The normal stiffness of the … | |
+<li><strong>k_t</strong> (<em>float</em>) – The tangential stiffness of … | |
+<li><strong>k_r</strong> (<em>float</em>) – The rolling stiffness of the… | |
+not used</em></li> | |
+<li><strong>gamma_n</strong> (<em>float</em>) – Particle-particle contac… | |
+<li><strong>gamma_t</strong> (<em>float</em>) – Particle-particle contac… | |
+<li><strong>gamma_r</strong> (<em>float</em>) – Particle-particle contac… | |
+not used</em></li> | |
+<li><strong>gamma_wn</strong> (<em>float</em>) – Wall-particle contact n… | |
+<li><strong>gamma_wt</strong> (<em>float</em>) – Wall-particle contact t… | |
+<li><strong>capillaryCohesion</strong> (<em>int</em>) – Enable particle-… | |
+interaction model (0 = no (default), 1 = yes)</li> | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.energy"> | |
-<tt class="descname">energy</tt><big>(</big><em>method</em><big>)</big><a clas… | |
-<dd><p>Calculate the sum of the energy components of all particles.</p> | |
+<dt id="sphere.sim.defineWorldBoundaries"> | |
+<tt class="descname">defineWorldBoundaries</tt><big>(</big><em>L, origo=[0.0, … | |
+<dd><p>Set the boundaries of the world. Particles will only be able to interact | |
+within this domain. With dynamic walls, allow space for expansions. | |
+<em>Important</em>: The particle radii have to be set beforehand. The world | |
+edges act as static walls.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>L</strong> (<em>numpy.array</em>) – The upper boundary of th… | |
+<li><strong>origo</strong> (<em>numpy.array</em>) – The lower boundary o… | |
+won’t work. Default = [0.0, 0.0, 0.0].</li> | |
+<li><strong>dx</strong> (<em>float</em>) – The cell width in any directi… | |
+(-1), the cell width is calculated to fit the largest particle.</li> | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.forcechains"> | |
-<tt class="descname">forcechains</tt><big>(</big><em>lc=200.0</em>, <em>uc=650… | |
+<dt id="sphere.sim.disableFluidPressureModulation"> | |
+<tt class="descname">disableFluidPressureModulation</tt><big>(</big><big>)</bi… | |
+<dd><p>Set the parameters for the sine wave modulating the fluid pressures | |
+at the top boundary to zero.</p> | |
+<p>See also: <a class="reference internal" href="#sphere.sim.setFluidPressureM… | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.energy"> | |
+<tt class="descname">energy</tt><big>(</big><em>method</em><big>)</big><a clas… | |
+<dd><p>Calculates the sum of the energy components of all particles.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+for potential energy [J], ‘kin’ for kinetic energy [J], ‘rot… | |
+rotational energy [J], ‘shear’ for energy lost by friction, | |
+‘shearrate’ for the rate of frictional energy loss [W], ‘vis… | |
+viscous losses normal to the contact [J], ‘visc_n_rate’ for the ra… | |
+of viscous losses normal to the contact [W], and finally ‘bondpot’ | |
+for the potential energy stored in bonds [J]</td> | |
+</tr> | |
+<tr class="field-even field"><th class="field-name">Returns:</th><td class="fi… | |
+</tr> | |
+<tr class="field-odd field"><th class="field-name">Return type:</th><td class=… | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.forcechains"> | |
+<tt class="descname">forcechains</tt><big>(</big><em>lc=200.0</em>, <em>uc=650… | |
<dd><p>Visualizes the force chains in the system from the magnitude of the | |
-normal contact forces, and produces an image of them. | |
-@param lc: Lower cutoff of contact forces. Contacts below are not | |
-visualized (float) | |
-@param uc: Upper cutoff of contact forces. Contacts above are | |
-visualized with this value (float) | |
-@param outformat: Format of output image. Possible values are | |
-‘interactive’, ‘png’, ‘epslatex’, ‘e… | |
-@param disp: Display forcechains in ‘2d’ or ‘3d’ | |
-Warning: Will segfault if no contacts are found.</p> | |
+normal contact forces, and produces an image of them. Warning: Will | |
+segfault if no contacts are found.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>lc</strong> (<em>float</em>) – Lower cutoff of contact force… | |
+visualized</li> | |
+<li><strong>uc</strong> (<em>float</em>) – Upper cutoff of contact force… | |
+visualized with this value</li> | |
+<li><strong>outformat</strong> (<em>str</em>) – Format of output image. … | |
+‘interactive’, ‘png’, ‘epslatex’, ‘e… | |
+<li><strong>disp</strong> (<em>str</em>) – Display forcechains in ‘… | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.forcechainsRose"> | |
-<tt class="descname">forcechainsRose</tt><big>(</big><em>lower_limit=0.25</em>… | |
-<dd><p>Visualize strike- and dip angles of the strongest force chains in a | |
-rose plot.</p> | |
+<dt id="sphere.sim.forcechainsRose"> | |
+<tt class="descname">forcechainsRose</tt><big>(</big><em>lower_limit=0.25</em>… | |
+<dd><p>Visualize trend and plunge angles of the strongest force chains in a | |
+rose plot. The plots are saved in the current folder with the name | |
+‘fc-<simulation id>-rose.pdf’.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>lower_limit</strong> (<em>float</em>) – Do not visualize for… | |
+contact force magnitude, in ]0;1[</li> | |
+<li><strong>graphics_format</strong> (<em>str</em>) – Save the plot in t… | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.generateBimodalRadii"> | |
-<tt class="descname">generateBimodalRadii</tt><big>(</big><em>r_small=0.005</e… | |
-<dd><p>Draw radii from two sizes | |
-@param r_small: Radii of small population (float), in ]0;r_large[ | |
-@param r_large: Radii of large population (float), in ]r_small;inf[ | |
-@param ratio: Approximate volumetric ratio between the two | |
-populations (large/small).</p> | |
+<dt id="sphere.sim.generateBimodalRadii"> | |
+<tt class="descname">generateBimodalRadii</tt><big>(</big><em>r_small=0.005</e… | |
+<dd><p>Draw random radii from two distinct sizes.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>r_small</strong> (<em>float</em>) – Radii of small populatio… | |
+<li><strong>r_large</strong> (<em>float</em>) – Radii of large populatio… | |
+<li><strong>ratio</strong> (<em>float</em>) – Approximate volumetric rat… | |
+populations (large/small).</li> | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+<p>See also: <a class="reference internal" href="#sphere.sim.generateRadii" ti… | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.generateRadii"> | |
-<tt class="descname">generateRadii</tt><big>(</big><em>psd='logn'</em>, <em>ra… | |
-<dd><p>Draw random particle radii from the selected probability distribution. | |
-Specify mean radius and variance. The variance should be kept at a | |
-very low value.</p> | |
+<dt id="sphere.sim.generateRadii"> | |
+<tt class="descname">generateRadii</tt><big>(</big><em>psd='logn'</em>, <em>ra… | |
+<dd><p>Draw random particle radii from a selected probability distribution. | |
+The larger the variance of radii is, the slower the computations will | |
+run. The reason is two-fold: The smallest particle dictates the time | |
+step length, where smaller particles cause shorter time steps. At the | |
+same time, the largest particle determines the sorting cell size, where | |
+larger particles cause larger cells. Larger cells are likely to contain | |
+more particles, causing more contact checks.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>psd</strong> (<em>str</em>) – The particle side distribution… | |
+<tt class="docutils literal"><span class="pre">logn</span></tt>, which is a lo… | |
+for approximating well-sorted, coarse sediments. The other possible | |
+value is <tt class="docutils literal"><span class="pre">uni</span></tt>, which… | |
+<tt class="docutils literal"><span class="pre">radius_mean-radius_variance</sp… | |
+<li><strong>radius_mean</strong> (<em>float</em>) – The mean radius [m] … | |
+<li><strong>radius_variance</strong> (<em>float</em>) – The variance in … | |
+[m].</li> | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+<p>See also: <a class="reference internal" href="#sphere.sim.generateBimodalRa… | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.initGrid"> | |
-<tt class="descname">initGrid</tt><big>(</big><big>)</big><a class="headerlink… | |
+<dt id="sphere.sim.initFluid"> | |
+<tt class="descname">initFluid</tt><big>(</big><em>mu=0.00089</em><big>)</big>… | |
+<dd><p>Initialize the fluid arrays and the fluid viscosity. The default value | |
+of <tt class="docutils literal"><span class="pre">mu</span></tt> equals the dy… | |
+The value for water at 0 degrees Celcius is 17.87e-4 kg/(m*s).</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.initGrid"> | |
+<tt class="descname">initGrid</tt><big>(</big><em>dx=-1</em><big>)</big><a cla… | |
<dd><p>Initialize grid suitable for the particle positions set previously. | |
The margin parameter adjusts the distance (in no. of max. radii) | |
-from the particle boundaries.</p> | |
+from the particle boundaries. | |
+<em>Important</em>: The particle radii have to be set beforehand if the cell | |
+width isn’t specified by <cite>dx</cite>. | |
+:param dx: The cell width in any direction. If the default value is used</p> | |
+<blockquote> | |
+<div>(-1), the cell width is calculated to fit the largest particle.</div></bl… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.initGridAndWorldsize"> | |
-<tt class="descname">initGridAndWorldsize</tt><big>(</big><em>g=array([ 0.</em… | |
+<dt id="sphere.sim.initGridAndWorldsize"> | |
+<tt class="descname">initGridAndWorldsize</tt><big>(</big><em>margin=2.0</em><… | |
<dd><p>Initialize grid suitable for the particle positions set previously. | |
The margin parameter adjusts the distance (in no. of max. radii) | |
-from the particle boundaries.</p> | |
+from the particle boundaries. If the upper wall is dynamic, it is placed | |
+at the top boundary of the world.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.initGridPos"> | |
-<tt class="descname">initGridPos</tt><big>(</big><em>g=array([ 0.</em>, <em>0.… | |
+<dt id="sphere.sim.initGridPos"> | |
+<tt class="descname">initGridPos</tt><big>(</big><em>gridnum=array([12</em>, <… | |
<dd><p>Initialize particle positions in loose, cubic configuration. | |
-Radii must be set beforehand. | |
-xynum is the number of rows in both x- and y- directions.</p> | |
+<tt class="docutils literal"><span class="pre">gridnum</span></tt> is the numb… | |
+<em>Important</em>: The particle radii and the boundary conditions (periodic or | |
+not) for the x and y boundaries have to be set beforehand.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.initRandomGridPos"> | |
-<tt class="descname">initRandomGridPos</tt><big>(</big><em>g=array([ 0.</em>, … | |
-<dd><p>Initialize particle positions in loose, cubic configuration. | |
-Radii must be set beforehand. | |
-xynum is the number of rows in both x- and y- directions.</p> | |
+<dt id="sphere.sim.initRandomGridPos"> | |
+<tt class="descname">initRandomGridPos</tt><big>(</big><em>gridnum=array([12</… | |
+<dd><p>Initialize particle positions in loose, cubic configuration with some | |
+variance. <tt class="docutils literal"><span class="pre">gridnum</span></tt> i… | |
+directions. <em>Important</em>: The particle radii and the boundary conditions | |
+(periodic or not) for the x and y boundaries have to be set beforehand. | |
+The world size and grid height (in the z direction) is readjusted to fit | |
+the particle positions.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.initRandomPos"> | |
-<tt class="descname">initRandomPos</tt><big>(</big><em>g=array([ 0.</em>, <em>… | |
-<dd><p>Initialize particle positions in loose, cubic configuration. | |
-Radii must be set beforehand. | |
-xynum is the number of rows in both x- and y- directions.</p> | |
+<dt id="sphere.sim.initRandomPos"> | |
+<tt class="descname">initRandomPos</tt><big>(</big><em>gridnum=array([12</em>,… | |
+<dd><p>Initialize particle positions in completely random configuration. Radii | |
+<em>must</em> be set beforehand. If the x and y boundaries are set as periodic, | |
+the particle centers will be placed all the way to the edge. On regular, | |
+non-periodic boundaries, the particles are restrained at the edges to | |
+make space for their radii within the bounding box.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>gridnum</strong> (<em>numpy.array</em>) – The number of sort… | |
+(default = [12, 12, 36])</li> | |
+<li><strong>dx</strong> (<em>float</em>) – The cell width in any directi… | |
+(-1), the cell width is calculated to fit the largest particle.</li> | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.initTemporal"> | |
-<tt class="descname">initTemporal</tt><big>(</big><em>total</em>, <em>current=… | |
-<dd><p>Set temporal parameters for the simulation. | |
-Particle radii and physical parameters need to be set | |
-prior to these.</p> | |
+<dt id="sphere.sim.initTemporal"> | |
+<tt class="descname">initTemporal</tt><big>(</big><em>total</em>, <em>current=… | |
+<dd><p>Set temporal parameters for the simulation. <em>Important</em>: Particl… | |
+physical parameters, and the optional fluid grid need to be set prior to | |
+these if the computational time step (dt) isn’t set explicitly. If the | |
+parameter <cite>dt</cite> is the default value (-1), the function will estimat… | |
+best time step length. The value of the computational time step for the | |
+DEM is checked for stability in the CFD solution if fluid simulation is | |
+included.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>total</strong> (<em>float</em>) – The time at which to end t… | |
+<li><strong>current</strong> – The current time [s] (default = 0.0 s)</l… | |
+<li><strong>file_dt</strong> – The interval between output files [s] (de… | |
+<li><strong>dt</strong> – The computational time step length [s]</li> | |
+</ul> | |
+</td> | |
+</tr> | |
+<tr class="field-even field"><th class="field-name">Step_count :</th><td class… | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.porosities"> | |
-<tt class="descname">porosities</tt><big>(</big><em>outformat='pdf'</em>, <em>… | |
-<dd><p>Plot porosities with depth</p> | |
+<dt id="sphere.sim.periodicBoundariesX"> | |
+<tt class="descname">periodicBoundariesX</tt><big>(</big><big>)</big><a class=… | |
+<dd><p>Set the x boundary conditions to be periodic.</p> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.porosity"> | |
-<tt class="descname">porosity</tt><big>(</big><em>slices=10</em>, <em>verbose=… | |
-<dd><p>Calculate the porosity as a function of depth, by averaging values | |
-in horizontal slabs. | |
-Returns porosity values and depth</p> | |
+<dt id="sphere.sim.periodicBoundariesXY"> | |
+<tt class="descname">periodicBoundariesXY</tt><big>(</big><big>)</big><a class… | |
+<dd><p>Set the x and y boundary conditions to be periodic.</p> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.random2bonds"> | |
-<tt class="descname">random2bonds</tt><big>(</big><em>ratio=0.3</em>, <em>spac… | |
-<dd><p>Bond an amount of particles in two-particle clusters | |
-@param ratio: The amount of particles to bond, values in ]0.0;1.0] (float) | |
-@param spacing: The distance relative to the sum of radii between bonded | |
-particles, neg. values denote an overlap. Values in ]0.0,inf[ (float). | |
-The particles should be initialized beforehand. | |
-Note: The actual number of bonds is likely to be somewhat smaller than | |
-specified, due to the random selection algorithm.</p> | |
+<dt id="sphere.sim.plotConvergence"> | |
+<tt class="descname">plotConvergence</tt><big>(</big><em>graphics_format='png'… | |
+<dd><p>Plot the convergence evolution in the CFD solver. The plot is saved | |
+in the output folder with the file name | |
+‘<simulation id>-conv.<graphics_format>’.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.readbin"> | |
-<tt class="descname">readbin</tt><big>(</big><em>targetbin</em>, <em>verbose=T… | |
-<dd><p>Reads a target SPHERE binary file</p> | |
+<dt id="sphere.sim.plotFluidDiffAdvPresZ"> | |
+<tt class="descname">plotFluidDiffAdvPresZ</tt><big>(</big><em>graphics_format… | |
+<dd><p>Compare contributions to the velocity from diffusion and advection, | |
+assuming the flow is 1D along the z-axis, phi = 1, and dphi = 0. This | |
+solution is analog to the predicted velocity and not constrained by the | |
+conservation of mass. The plot is saved in the output folder with the | |
+name format ‘<simulation id>-diff_adv-t=<current time>s-mu=&… | |
+viscosity>Pa-s.<graphics_format>’.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.render"> | |
-<tt class="descname">render</tt><big>(</big><em>method='pres'</em>, <em>max_va… | |
-<dd><p>Render all output files that belong to the simulation, determined by si… | |
+<dt id="sphere.sim.plotFluidPressuresY"> | |
+<tt class="descname">plotFluidPressuresY</tt><big>(</big><em>y=-1</em>, <em>gr… | |
+<dd><p>Plot fluid pressures in a plane normal to the second axis. | |
+The plot is saved in the current folder with the format | |
+‘p_f-<simulation id>-y<y value>.<graphics_format>̵… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>y</strong> (<em>int</em>) – Plot pressures in fluid cells wi… | |
+this value is -1, the center y position is used.</li> | |
+<li><strong>graphics_format</strong> (<em>str</em>) – Save the plot in t… | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+<p>See also: <a class="reference internal" href="#sphere.sim.writeFluidVTK" ti… | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.run"> | |
-<tt class="descname">run</tt><big>(</big><em>verbose=True</em>, <em>hideinputf… | |
-<dd><p>Execute sphere with target project</p> | |
+<dt id="sphere.sim.plotFluidPressuresZ"> | |
+<tt class="descname">plotFluidPressuresZ</tt><big>(</big><em>z=-1</em>, <em>gr… | |
+<dd><p>Plot fluid pressures in a plane normal to the third axis. | |
+The plot is saved in the current folder with the format | |
+‘p_f-<simulation id>-z<z value>.<graphics_format>̵… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>z</strong> (<em>int</em>) – Plot pressures in fluid cells wi… | |
+this value is -1, the center z position is used.</li> | |
+<li><strong>graphics_format</strong> (<em>str</em>) – Save the plot in t… | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+<p>See also: <a class="reference internal" href="#sphere.sim.writeFluidVTK" ti… | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.shear"> | |
-<tt class="descname">shear</tt><big>(</big><em>shear_strain_rate=1</em>, <em>p… | |
-<dd><p>Setup shear experiment. Specify the upper wall | |
-deviatoric stress in Pascal, default value is 10 kPa. | |
-The shear strain rate is the shear length divided by the | |
-initial height per second.</p> | |
+<dt id="sphere.sim.plotFluidVelocitiesY"> | |
+<tt class="descname">plotFluidVelocitiesY</tt><big>(</big><em>y=-1</em>, <em>g… | |
+<dd><p>Plot fluid velocities in a plane normal to the second axis. | |
+The plot is saved in the current folder with the format | |
+‘v_f-<simulation id>-z<z value>.<graphics_format>̵… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>y</strong> (<em>int</em>) – Plot velocities in fluid cells w… | |
+this value is -1, the center y position is used.</li> | |
+<li><strong>graphics_format</strong> (<em>str</em>) – Save the plot in t… | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+<p>See also: <a class="reference internal" href="#sphere.sim.writeFluidVTK" ti… | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.sheardisp"> | |
-<tt class="descname">sheardisp</tt><big>(</big><em>outformat='pdf'</em>, <em>z… | |
-<dd><p>Show particle x-displacement vs. the z-pos</p> | |
+<dt id="sphere.sim.plotFluidVelocitiesZ"> | |
+<tt class="descname">plotFluidVelocitiesZ</tt><big>(</big><em>z=-1</em>, <em>g… | |
+<dd><p>Plot fluid velocities in a plane normal to the third axis. | |
+The plot is saved in the current folder with the format | |
+‘v_f-<simulation id>-z<z value>.<graphics_format>̵… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>z</strong> (<em>int</em>) – Plot velocities in fluid cells w… | |
+this value is -1, the center z position is used.</li> | |
+<li><strong>graphics_format</strong> (<em>str</em>) – Save the plot in t… | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+<p>See also: <a class="reference internal" href="#sphere.sim.writeFluidVTK" ti… | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.shearstrain"> | |
-<tt class="descname">shearstrain</tt><big>(</big><big>)</big><a class="headerl… | |
-<dd><p>Calculates and returns the current shear strain (gamma) value of the ex… | |
+<dt id="sphere.sim.plotPrescribedFluidPressures"> | |
+<tt class="descname">plotPrescribedFluidPressures</tt><big>(</big><em>graphics… | |
+<dd><p>Plot the prescribed fluid pressures through time that may be | |
+modulated through the class parameters p_mod_A, p_mod_f, and p_mod_phi. | |
+The plot is saved in the output folder with the file name | |
+‘<simulation id>-pres.<graphics_format>’.</p> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.shearvel"> | |
-<tt class="descname">shearvel</tt><big>(</big><big>)</big><a class="headerlink… | |
-<dd><p>Calculates and returns the shear velocity (gamma_dot) of the experiment… | |
+<dt id="sphere.sim.porosities"> | |
+<tt class="descname">porosities</tt><big>(</big><em>graphics_format='pdf'</em>… | |
+<dd><p>Plot the averaged porosities with depth. The plot is saved in the format | |
+‘<simulation id>-porosity.<graphics_format>’.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>graphics_format</strong> (<em>str</em>) – Save the plot in t… | |
+<li><strong>zslices</strong> (<em>int</em>) – The number of points along… | |
+the porosity in</li> | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.thinsection_x1x3"> | |
-<tt class="descname">thinsection_x1x3</tt><big>(</big><em>x2='center'</em>, <e… | |
-<dd><p>Produce a 2D image of particles on a x1,x3 plane, intersecting the seco… | |
-Output is saved as ‘<sid>-ts-x1x3.txt’ in the current folder… | |
-<p>An upper limit to the pressure color bar range can be set by the cbmax para… | |
+<dt id="sphere.sim.porosity"> | |
+<tt class="descname">porosity</tt><big>(</big><em>slices=10</em>, <em>verbose=… | |
+<dd><p>Calculates the porosity as a function of depth, by averaging values in | |
+horizontal slabs. Returns porosity values and their corresponding depth. | |
+The values are calculated using the external <tt class="docutils literal"><spa… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>slices</strong> (<em>int</em>) – The number of vertical slab… | |
+<li><strong>verbose</strong> (<em>bool</em>) – Show the file name of the… | |
+disk</li> | |
+</ul> | |
+</td> | |
+</tr> | |
+<tr class="field-even field"><th class="field-name">Returns:</th><td class="fi… | |
+</td> | |
+</tr> | |
+<tr class="field-odd field"><th class="field-name">Return type:</th><td class=… | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.randomBondPairs"> | |
+<tt class="descname">randomBondPairs</tt><big>(</big><em>ratio=0.3</em>, <em>s… | |
+<dd><p>Bond an amount of particles in two-particle clusters. The particles | |
+should be initialized beforehand. Note: The actual number of bonds is | |
+likely to be somewhat smaller than specified, due to the random | |
+selection algorithm.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>ratio</strong> (<em>float</em>) – The amount of particles to… | |
+<li><strong>spacing</strong> (<em>float</em>) – The distance relative to… | |
+particles, neg. values denote an overlap. Values in ]0.0,inf[.</li> | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.readbin"> | |
+<tt class="descname">readbin</tt><big>(</big><em>targetbin</em>, <em>verbose=T… | |
+<dd><p>Reads a target <tt class="docutils literal"><span class="pre">sphere</s… | |
+<p>See also <a class="reference internal" href="#sphere.sim.writebin" title="s… | |
+<a class="reference internal" href="#sphere.sim.readsecond" title="sphere.sim.… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>targetbin</strong> (<em>str</em>) – The path to the binary <… | |
+<li><strong>verbose</strong> (<em>bool</em>) – Show diagnostic informati… | |
+<li><strong>bonds</strong> (<em>bool</em>) – The input file contains bon… | |
+This parameter should be true for all recent <tt class="docutils literal"><spa… | |
+<li><strong>devsmod</strong> (<em>bool</em>) – The input file contains i… | |
+stresses at the top wall (default = True). This parameter should be | |
+true for all recent <tt class="docutils literal"><span class="pre">sphere</spa… | |
+<li><strong>esysparticle</strong> (<em>bool</em>) – Stop reading the fil… | |
+which is useful for reading output files from other DEM programs. | |
+(default = False)</li> | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.readfirst"> | |
+<tt class="descname">readfirst</tt><big>(</big><em>verbose=True</em><big>)</bi… | |
+<dd><p>Read the first output file from the <tt class="docutils literal"><span … | |
+to the object simulation id (<tt class="docutils literal"><span class="pre">se… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+</tbody> | |
+</table> | |
+<p>See also <a class="reference internal" href="#sphere.sim.readbin" title="sp… | |
+<a class="reference internal" href="#sphere.sim.readstep" title="sphere.sim.re… | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.readlast"> | |
+<tt class="descname">readlast</tt><big>(</big><em>verbose=True</em><big>)</big… | |
+<dd><p>Read the last output file from the <tt class="docutils literal"><span c… | |
+to the object simulation id (<tt class="docutils literal"><span class="pre">se… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+</tbody> | |
+</table> | |
+<p>See also <a class="reference internal" href="#sphere.sim.readbin" title="sp… | |
+<a class="reference internal" href="#sphere.sim.readstep" title="sphere.sim.re… | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.readsecond"> | |
+<tt class="descname">readsecond</tt><big>(</big><em>verbose=True</em><big>)</b… | |
+<dd><p>Read the second output file from the <tt class="docutils literal"><span… | |
+corresponding to the object simulation id (<tt class="docutils literal"><span … | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+</tbody> | |
+</table> | |
+<p>See also <a class="reference internal" href="#sphere.sim.readbin" title="sp… | |
+and <a class="reference internal" href="#sphere.sim.readstep" title="sphere.si… | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.readstep"> | |
+<tt class="descname">readstep</tt><big>(</big><em>step</em>, <em>verbose=True<… | |
+<dd><p>Read a output file from the <tt class="docutils literal"><span class="p… | |
+to the object simulation id (<tt class="docutils literal"><span class="pre">se… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>step</strong> (<em>int</em>) – The output file number to rea… | |
+<li><strong>verbose</strong> (<em>bool</em>) – Display diagnostic inform… | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+<p>See also <a class="reference internal" href="#sphere.sim.readbin" title="sp… | |
+and <a class="reference internal" href="#sphere.sim.readsecond" title="sphere.… | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.render"> | |
+<tt class="descname">render</tt><big>(</big><em>method='pres'</em>, <em>max_va… | |
+<dd><p>Using the built-in ray tracer, render all output files that belong to | |
+the simulation, determined by the simulation id (<tt class="docutils literal">… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>method</strong> (<em>str</em>) – The color visualization met… | |
+Possible values are: ‘normal’: color all particles with the same | |
+color, ‘pres’: color by pressure, ‘vel’: color by tran… | |
+velocity, ‘angvel’: color by rotational velocity, ‘xdispR… | |
+total displacement along the x-axis, ‘angpos’: color by angular | |
+position.</li> | |
+<li><strong>max_val</strong> (<em>float</em>) – The maximum value of the… | |
+<li><strong>lower_cutoff</strong> (<em>float</em>) – Do not render parti… | |
+value, of the field selected by <tt class="docutils literal"><span class="pre"… | |
+<li><strong>graphics_format</strong> (<em>str</em>) – Convert the PPM im… | |
+tracer to this image format using Imagemagick</li> | |
+<li><strong>verbose</strong> (<em>bool</em>) – Show verbose information … | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.run"> | |
+<tt class="descname">run</tt><big>(</big><em>verbose=True</em>, <em>hideinputf… | |
+<dd><p>Start <tt class="docutils literal"><span class="pre">sphere</span></tt>… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>verbose</strong> (<em>bool</em>) – Show <tt class="docutils … | |
+<li><strong>hideinputfile</strong> (<em>bool</em>) – Hide the file name … | |
+<li><strong>dry</strong> (<em>bool</em>) – Perform a dry run. Important … | |
+the <tt class="docutils literal"><span class="pre">sphere</span></tt> program,… | |
+<li><strong>valgrind</strong> (<em>bool</em>) – Run the program with <tt… | |
+memory leaks in the host code. This causes a significant increase in | |
+computational time.</li> | |
+<li><strong>cudamemcheck</strong> (<em>bool</em>) – Run the program with… | |
+check for device memory leaks and errors. This causes a significant | |
+increase in computational time.</li> | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.setBeta"> | |
+<tt class="descname">setBeta</tt><big>(</big><em>beta</em><big>)</big><a class… | |
+<dd><p>Beta is a fluid solver parameter, used in velocity prediction and | |
+pressure iteration 1.0: Use old pressures for fluid velocity prediction | |
+(see Langtangen et al. 2002) 0.0: Do not use old pressures for fluid | |
+velocity prediction (Chorin’s original projection method, see Chorin | |
+(1968) and “Projection method (fluid dynamics)” page on Wikipedia.… | |
+best results precision and performance-wise are obtained by using a beta | |
+of 0 and a low tolerance criteria value.</p> | |
+<p>The default and recommended value is 0.0.</p> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.setFluidPressureModulation"> | |
+<tt class="descname">setFluidPressureModulation</tt><big>(</big><em>A</em>, <e… | |
+<dd><p>Set the parameters for the sine wave modulating the fluid pressures | |
+at the top boundary. Note that a cos-wave is obtained with phi=pi/2.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>A</strong> (<em>float</em>) – Fluctuation amplitude [Pa]</li> | |
+<li><strong>f</strong> (<em>float</em>) – Fluctuation frequency [Hz]</li> | |
+<li><strong>phi</strong> (<em>float</em>) – Fluctuation phase shift (def… | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+<p>See also: <a class="reference internal" href="#sphere.sim.disableFluidPress… | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.setGamma"> | |
+<tt class="descname">setGamma</tt><big>(</big><em>gamma</em><big>)</big><a cla… | |
+<dd><p>Gamma is a fluid solver parameter, used for smoothing the pressure | |
+values. The epsilon (pressure) values are smoothed by including the | |
+average epsilon value of the six closest (face) neighbor cells. This | |
+parameter should be in the range [0.0;1.0[. The higher the value, the | |
+more averaging is introduced. A value of 0.0 disables all averaging.</p> | |
+<p>The default and recommended value is 0.5.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.setMaxIterations"> | |
+<tt class="descname">setMaxIterations</tt><big>(</big><em>maxiter</em><big>)</… | |
+<dd><p>A fluid solver parameter, the value of the maxiter parameter denotes the | |
+maximal allowed number of fluid solver iterations before ending the | |
+fluid solver loop prematurely. The residual values are at that point not | |
+fulfilling the tolerance criteria. The parameter is included to avoid | |
+infinite hangs.</p> | |
+<p>The default and recommended value is 1e4.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+solver</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.setTheta"> | |
+<tt class="descname">setTheta</tt><big>(</big><em>theta</em><big>)</big><a cla… | |
+<dd><p>Theta is a fluid solver under-relaxation parameter, used in solution of | |
+Poisson equation. The value should be within the range ]0.0;1.0]. At a | |
+value of 1.0, the new estimate of epsilon values is used exclusively. At | |
+lower values, a linear interpolation between new and old values is used. | |
+The solution typically converges faster with a value of 1.0, but | |
+instabilities may be avoided with lower values.</p> | |
+<p>The default and recommended value is 1.0.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.setTolerance"> | |
+<tt class="descname">setTolerance</tt><big>(</big><em>tolerance</em><big>)</bi… | |
+<dd><p>A fluid solver parameter, the value of the tolerance parameter denotes | |
+the required value of the maximum normalized residual for the fluid | |
+solver.</p> | |
+<p>The default and recommended value is 1.0e-8.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+residual</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.shear"> | |
+<tt class="descname">shear</tt><big>(</big><em>shear_strain_rate=1.0</em><big>… | |
+<dd><p>Setup shear experiment. The shear strain rate is the shear velocity | |
+divided by the initial height per second. The shear movement is along | |
+the positive x axis. The function zeroes the tangential wall viscosity | |
+(gamma_wt) and the wall friction coefficients (mu_ws, mu_wn).</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.shearStrain"> | |
+<tt class="descname">shearStrain</tt><big>(</big><big>)</big><a class="headerl… | |
+<dd><p>Calculates and returns the current shear strain (gamma) value of the | |
+experiment. The shear strain is found by determining the total x-axis | |
+displacement of the upper, fixed particles.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Returns:</th><td class="fie… | |
+</tr> | |
+<tr class="field-even field"><th class="field-name">Return type:</th><td class… | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.shearVel"> | |
+<tt class="descname">shearVel</tt><big>(</big><big>)</big><a class="headerlink… | |
+<dd><p>Calculates and returns the shear velocity (gamma_dot) of the | |
+experiment. The shear velocity is the x-axis velocity value of the | |
+upper particles.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Returns:</th><td class="fie… | |
+</tr> | |
+<tr class="field-even field"><th class="field-name">Return type:</th><td class… | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.sheardisp"> | |
+<tt class="descname">sheardisp</tt><big>(</big><em>graphics_format='pdf'</em>,… | |
+<dd><p>Plot the particle x-axis displacement against the original vertical | |
+particle position. The plot is saved in the current directory with the | |
+file name ‘<simulation id>-sheardisp.<graphics_format>’… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.status"> | |
+<tt class="descname">status</tt><big>(</big><big>)</big><a class="headerlink" … | |
+<dd><p>Returns the current simulation status by using the simulation id | |
+(<tt class="docutils literal"><span class="pre">sid</span></tt>) as an identif… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Returns:</th><td class="fie… | |
+</tr> | |
+<tr class="field-even field"><th class="field-name">Return type:</th><td class… | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.thinsection_x1x3"> | |
+<tt class="descname">thinsection_x1x3</tt><big>(</big><em>x2='center'</em>, <e… | |
+<dd><p>Produce a 2D image of particles on a x1,x3 plane, intersecting the | |
+second axis at x2. Output is saved as ‘<sid>-ts-x1x3.txt’ in… | |
+current folder.</p> | |
+<p>An upper limit to the pressure color bar range can be set by the | |
+cbmax parameter.</p> | |
<dl class="docutils"> | |
<dt>The data can be plotted in gnuplot with:</dt> | |
<dd>gnuplot> set size ratio -1 | |
gnuplot> set palette defined (0 “blue”, 0.5 “gray”,… | |
-gnuplot> plot ‘<sid>-ts-x1x3.txt’ with circles palette fs… | |
+gnuplot> plot ‘<sid>-ts-x1x3.txt’ with circles palette fs… | |
</dl> | |
+<p>This function also saves a plot of the inter-particle slip angles.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>x2</strong> (<em>foat</em>) – The position along the second … | |
+<li><strong>graphics_format</strong> (<em>str</em>) – Save the slip angl… | |
+<li><strong>cbmax</strong> (<em>float</em>) – The maximal value of the p… | |
+<li><strong>arrowscale</strong> (<em>float</em>) – Scale the rotational … | |
+<li><strong>velarrowscale</strong> (<em>float</em>) – Scale the translat… | |
+<li><strong>slipscale</strong> (<em>float</em>) – Scale the slip arrows … | |
+<li><strong>verbose</strong> (<em>bool</em>) – Show function output duri… | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.torqueScript"> | |
-<tt class="descname">torqueScript</tt><big>(</big><em>email='adc@geo.au.dk… | |
-<dd><p>Create job script for the Torque queue manager for the binary</p> | |
+<dt id="sphere.sim.torqueScript"> | |
+<tt class="descname">torqueScript</tt><big>(</big><em>email='adc@geo.au.dk… | |
+<dd><p>Creates a job script for the Torque queue manager for the simulation | |
+object.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>email</strong> (<em>str</em>) – The e-mail address that Torq… | |
+<li><strong>email_alerts</strong> (<em>str</em>) – The type of Torque me… | |
+address. The character ‘b’ causes a mail to be sent when the | |
+execution begins. The character ‘e’ causes a mail to be sent when | |
+the execution ends normally. The character ‘a’ causes a mail to be | |
+sent if the execution ends abnormally. The characters can be written | |
+in any order.</li> | |
+<li><strong>walltime</strong> (<em>str</em>) – The maximal allowed time … | |
+‘HH:MM:SS’.</li> | |
+<li><strong>queue</strong> (<em>str</em>) – The Torque queue to schedule… | |
+<li><strong>cudapath</strong> (<em>str</em>) – The path of the CUDA libr… | |
+nodes</li> | |
+<li><strong>spheredir</strong> (<em>str</em>) – The path to the root dir… | |
+cluster</li> | |
+<li><strong>use_workdir</strong> (<em>bool</em>) – Use a different worki… | |
+folder</li> | |
+<li><strong>workdir</strong> (<em>str</em>) – The working directory duri… | |
+<cite>use_workdir=True</cite></li> | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.uniaxialStrainRate"> | |
-<tt class="descname">uniaxialStrainRate</tt><big>(</big><em>wvel=-0.001</em>, … | |
-<dd><p>Setup consolidation experiment. Specify the upper wall | |
-velocity in m/s, default value is -0.001 m/s (i.e. downwards).</p> | |
+<dt id="sphere.sim.totalMomentum"> | |
+<tt class="descname">totalMomentum</tt><big>(</big><big>)</big><a class="heade… | |
+<dd><p>Returns the sum of particle momentums.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Returns:</th><td class="fie… | |
+</tr> | |
+<tr class="field-even field"><th class="field-name">Return type:</th><td class… | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.voidRatio"> | |
-<tt class="descname">voidRatio</tt><big>(</big><big>)</big><a class="headerlin… | |
-<dd><p>Returns the current void ratio</p> | |
+<dt id="sphere.sim.triaxial"> | |
+<tt class="descname">triaxial</tt><big>(</big><em>wvel=-0.001</em>, <em>normal… | |
+<dd><p>Setup triaxial experiment. The upper wall is moved at a fixed velocity | |
+in m/s, default values is -0.001 m/s (i.e. downwards). The side walls | |
+are exerting a defined normal stress.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>wvel</strong> (<em>float</em>) – Upper wall velocity. Negati… | |
+moves downwards.</li> | |
+<li><strong>normal_stress</strong> (<em>float</em>) – The normal stress … | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.writebin"> | |
-<tt class="descname">writebin</tt><big>(</big><em>folder='../input/'</em>, <em… | |
-<dd><p>Writes to a target SPHERE binary file</p> | |
+<dt id="sphere.sim.uniaxialStrainRate"> | |
+<tt class="descname">uniaxialStrainRate</tt><big>(</big><em>wvel=-0.001</em><b… | |
+<dd><p>Setup consolidation experiment. Specify the upper wall velocity in m/s, | |
+default value is -0.001 m/s (i.e. downwards).</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+moves downwards.</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="method"> | |
-<dt id="sphere.Spherebin.zeroKinematics"> | |
-<tt class="descname">zeroKinematics</tt><big>(</big><big>)</big><a class="head… | |
-<dd><p>Zero kinematics of particles</p> | |
+<dt id="sphere.sim.video"> | |
+<tt class="descname">video</tt><big>(</big><em>out_folder='./'</em>, <em>video… | |
+<dd><p>Uses ffmpeg to combine images to animation. All images should be | |
+rendered beforehand using func:<cite>render()</cite>.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>out_folder</strong> (<em>str</em>) – The output folder for t… | |
+<li><strong>video_format</strong> (<em>str</em>) – The format of the out… | |
+<li><strong>graphics_folder</strong> (<em>str</em>) – The folder contain… | |
+<li><strong>graphics_format</strong> (<em>str</em>) – The format of the … | |
+<li><strong>fps</strong> (<em>int</em>) – The number of frames per secon… | |
+<li><strong>qscale</strong> (<em>float</em>) – The output video quality,… | |
+<li><strong>bitrate</strong> (<em>int</em>) – The bitrate to use in the … | |
+<li><strong>verbose</strong> (<em>bool</em>) – Show ffmpeg output</li> | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
+<dl class="method"> | |
+<dt id="sphere.sim.voidRatio"> | |
+<tt class="descname">voidRatio</tt><big>(</big><big>)</big><a class="headerlin… | |
+<dd><p>Calculates the current void ratio</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Returns:</th><td class="fie… | |
+</tr> | |
+<tr class="field-even field"><th class="field-name">Return type:</th><td class… | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
-<dl class="function"> | |
-<dt id="sphere.V_sphere"> | |
-<tt class="descclassname">sphere.</tt><tt class="descname">V_sphere</tt><big>(… | |
-<dd><p>Returns the volume of a sphere with radius r</p> | |
+<dl class="method"> | |
+<dt id="sphere.sim.writeFluidVTK"> | |
+<tt class="descname">writeFluidVTK</tt><big>(</big><em>folder='../output/'</em… | |
+<dd><p>Writes a VTK file for the fluid grid to the <tt class="docutils literal… | |
+default. The file name will be in the format <tt class="docutils literal"><spa… | |
+The vti files can be used for visualizing the fluid in ParaView.</p> | |
+<p>The fluid grid is visualized by opening the vti files, and pressing | |
+“Apply” to import all fluid field properties. To visualize the sca… | |
+fields, such as the pressure, the porosity, the porosity change or the | |
+velocity magnitude, choose “Surface” or “Surface With Edges&… | |
+“Representation”. Choose the desired property as the “Colori… | |
+It may be desirable to show the color bar by pressing the “Show” b… | |
+and “Rescale” to fit the color range limits to the current file. T… | |
+coordinate system can be displayed by checking the “Show Axis” fie… | |
+All adjustments by default require the “Apply” button to be pressed | |
+before regenerating the view.</p> | |
+<p>The fluid vector fields (e.g. the fluid velocity) can be visualizing by | |
+e.g. arrows. To do this, select the fluid data in the “Pipeline | |
+Browser”. Press “Glyph” from the “Common” toolba… | |
+“Filters” mennu, and press “Glyph” from the “Com… | |
+that “Arrow” is selected as the “Glyph type”, and R… | |
+“Vectors” value. Adjust the “Maximum Number of Points”… | |
+big as the number of fluid cells in the grid. Press “Apply” to vis… | |
+the arrows.</p> | |
+<p>If several data files are generated for the same simulation (e.g. using | |
+the <a class="reference internal" href="#sphere.sim.writeVTKall" title="sphere… | |
+visualization through time by using the ParaView controls.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>folder</strong> (<em>str</em>) – The folder where to place t… | |
+(default = ‘../output/’)</li> | |
+<li><strong>verbose</strong> (<em>bool</em>) – Show diagnostic informati… | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
-<dl class="function"> | |
-<dt id="sphere.cleanup"> | |
-<tt class="descclassname">sphere.</tt><tt class="descname">cleanup</tt><big>(<… | |
-<dd><p>Remove input/output files and images from simulation</p> | |
+<dl class="method"> | |
+<dt id="sphere.sim.writeVTK"> | |
+<tt class="descname">writeVTK</tt><big>(</big><em>folder='../output/'</em>, <e… | |
+<dd><p>Writes a VTK file with particle information to the <tt class="docutils … | |
+by default. The file name will be in the format <tt class="docutils literal"><… | |
+The vtu files can be used to visualize the particles in ParaView.</p> | |
+<p>After opening the vtu files, the particle fields will show up in the | |
+“Properties” list. Press “Apply” to import all fields … | |
+session. The particles are visualized by selecting the imported data in | |
+the “Pipeline Browser”. Afterwards, click the “Glyph” … | |
+“Common” toolbar, or go to the “Filters” menu, and pre… | |
+the “Common” list. Choose “Sphere” as the “Glyph… | |
+1.0, choose “scalar” as the “Scale Mode”. Check the &#… | |
+set the “Set Scale Factor” to 1.0. The field “Maximum Number… | |
+may be increased if the number of particles exceed the default value. | |
+Finally press “Apply”, and the particles will appear in the main w… | |
+<p>The sphere resolution may be adjusted (“Theta resolution”, R… | |
+resolution”) to increase the quality and the computational requirements | |
+of the rendering. All adjustments by default require the “Apply” b… | |
+to be pressed before regenerating the view.</p> | |
+<p>If several vtu files are generated for the same simulation (e.g. using | |
+the :func:<tt class="docutils literal"><span class="pre">writeVTKall()</span><… | |
+visualization through time by using the ParaView controls.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>folder</strong> (<em>str</em>) – The folder where to place t… | |
+(default = ‘../output/’)</li> | |
+<li><strong>verbose</strong> (<em>bool</em>) – Show diagnostic informati… | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
-<dl class="function"> | |
-<dt id="sphere.convert"> | |
-<tt class="descclassname">sphere.</tt><tt class="descname">convert</tt><big>(<… | |
-<dd><p>Converts all PPM images in img_out to graphicsformat, using ImageMagick… | |
+<dl class="method"> | |
+<dt id="sphere.sim.writeVTKall"> | |
+<tt class="descname">writeVTKall</tt><big>(</big><em>verbose=True</em><big>)</… | |
+<dd><p>Writes a VTK file for each simulation output file with particle | |
+information and the fluid grid to the <tt class="docutils literal"><span class… | |
+The file name will be in the format <tt class="docutils literal"><span class="… | |
+<tt class="docutils literal"><span class="pre">fluid-<self.sid>.vti</spa… | |
+particles, and the vti files for visualizing the fluid in ParaView.</p> | |
+<p>After opening the vtu files, the particle fields will show up in the | |
+“Properties” list. Press “Apply” to import all fields … | |
+session. The particles are visualized by selecting the imported data in | |
+the “Pipeline Browser”. Afterwards, click the “Glyph” … | |
+“Common” toolbar, or go to the “Filters” menu, and pre… | |
+the “Common” list. Choose “Sphere” as the “Glyph… | |
+1.0, choose “scalar” as the “Scale Mode”. Check the &#… | |
+set the “Set Scale Factor” to 1.0. The field “Maximum Number… | |
+may be increased if the number of particles exceed the default value. | |
+Finally press “Apply”, and the particles will appear in the main w… | |
+<p>The sphere resolution may be adjusted (“Theta resolution”, R… | |
+resolution”) to increase the quality and the computational requirements | |
+of the rendering.</p> | |
+<p>The fluid grid is visualized by opening the vti files, and pressing | |
+“Apply” to import all fluid field properties. To visualize the sca… | |
+fields, such as the pressure, the porosity, the porosity change or the | |
+velocity magnitude, choose “Surface” or “Surface With Edges&… | |
+“Representation”. Choose the desired property as the “Colori… | |
+It may be desirable to show the color bar by pressing the “Show” b… | |
+and “Rescale” to fit the color range limits to the current file. T… | |
+coordinate system can be displayed by checking the “Show Axis” fie… | |
+All adjustments by default require the “Apply” button to be pressed | |
+before regenerating the view.</p> | |
+<p>The fluid vector fields (e.g. the fluid velocity) can be visualizing by | |
+e.g. arrows. To do this, select the fluid data in the “Pipeline | |
+Browser”. Press “Glyph” from the “Common” toolba… | |
+“Filters” mennu, and press “Glyph” from the “Com… | |
+that “Arrow” is selected as the “Glyph type”, and R… | |
+“Vectors” value. Adjust the “Maximum Number of Points”… | |
+big as the number of fluid cells in the grid. Press “Apply” to vis… | |
+the arrows.</p> | |
+<p>If several data files are generated for the same simulation (e.g. using | |
+the <a class="reference internal" href="#sphere.sim.writeVTKall" title="sphere… | |
+visualization through time by using the ParaView controls.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
-<dl class="function"> | |
-<dt id="sphere.render"> | |
-<tt class="descclassname">sphere.</tt><tt class="descname">render</tt><big>(</… | |
-<dd><p>Render target binary using the sphere raytracer.</p> | |
+<dl class="method"> | |
+<dt id="sphere.sim.writebin"> | |
+<tt class="descname">writebin</tt><big>(</big><em>folder='../input/'</em>, <em… | |
+<dd><p>Writes a <tt class="docutils literal"><span class="pre">sphere</span></… | |
+The file name will be in the format <tt class="docutils literal"><span class="… | |
+<p>See also <a class="reference internal" href="#sphere.sim.readbin" title="sp… | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>folder</strong> (<em>str</em>) – The folder where to place t… | |
+<li><strong>verbose</strong> (<em>bool</em>) – Show diagnostic informati… | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+</dd></dl> | |
+ | |
+<dl class="method"> | |
+<dt id="sphere.sim.zeroKinematics"> | |
+<tt class="descname">zeroKinematics</tt><big>(</big><big>)</big><a class="head… | |
+<dd><p>Zero all kinematic parameters of the particles. This function is useful | |
+when output from one simulation is reused in another simulation.</p> | |
</dd></dl> | |
-<dl class="function"> | |
-<dt id="sphere.run"> | |
-<tt class="descclassname">sphere.</tt><tt class="descname">run</tt><big>(</big… | |
-<dd><p>Execute sphere with target binary as input</p> | |
</dd></dl> | |
<dl class="function"> | |
<dt id="sphere.status"> | |
<tt class="descclassname">sphere.</tt><tt class="descname">status</tt><big>(</… | |
-<dd><p>Check the status.dat file for the target project, | |
-and return the last file numer.</p> | |
+<dd><p>Check the status.dat file for the target project, and return the last o… | |
+file number.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+</tr> | |
+<tr class="field-even field"><th class="field-name">Returns:</th><td class="fi… | |
+</tr> | |
+<tr class="field-odd field"><th class="field-name">Return type:</th><td class=… | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="function"> | |
<dt id="sphere.thinsectionVideo"> | |
<tt class="descclassname">sphere.</tt><tt class="descname">thinsectionVideo</t… | |
-<dd><p>Use ffmpeg to combine thin section images to animation. | |
-This function will start off by rendering the images.</p> | |
+<dd><p>Uses ffmpeg to combine thin section images to an animation. This functi… | |
+will implicity render the thin section images beforehand.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>project</strong> (<em>str</em>) – The simulation id of the p… | |
+<li><strong>out_folder</strong> (<em>str</em>) – The output folder for t… | |
+<li><strong>video_format</strong> (<em>str</em>) – The format of the out… | |
+<li><strong>fps</strong> (<em>int</em>) – The number of frames per secon… | |
+<li><strong>qscale</strong> (<em>float</em>) – The output video quality,… | |
+<li><strong>bitrate</strong> (<em>int</em>) – The bitrate to use in the … | |
+<li><strong>verbose</strong> (<em>bool</em>) – Show ffmpeg output</li> | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="function"> | |
<dt id="sphere.torqueScriptParallel3"> | |
-<tt class="descclassname">sphere.</tt><tt class="descname">torqueScriptParalle… | |
+<tt class="descclassname">sphere.</tt><tt class="descname">torqueScriptParalle… | |
<dd><p>Create job script for the Torque queue manager for three binaries, | |
-executed in parallel. | |
-Returns the filename of the script</p> | |
+executed in parallel, ideally on three GPUs.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>email</strong> (<em>str</em>) – The e-mail address that Torq… | |
+<li><strong>email_alerts</strong> (<em>str</em>) – The type of Torque me… | |
+address. The character ‘b’ causes a mail to be sent when the | |
+execution begins. The character ‘e’ causes a mail to be sent when | |
+the execution ends normally. The character ‘a’ causes a mail to be | |
+sent if the execution ends abnormally. The characters can be written | |
+in any order.</li> | |
+<li><strong>walltime</strong> (<em>str</em>) – The maximal allowed time … | |
+‘HH:MM:SS’.</li> | |
+<li><strong>queue</strong> (<em>str</em>) – The Torque queue to schedule… | |
+<li><strong>cudapath</strong> (<em>str</em>) – The path of the CUDA libr… | |
+<li><strong>spheredir</strong> (<em>str</em>) – The path to the root dir… | |
+<li><strong>use_workdir</strong> (<em>bool</em>) – Use a different worki… | |
+<li><strong>workdir</strong> (<em>str</em>) – The working directory duri… | |
+<cite>use_workdir=True</cite></li> | |
+</ul> | |
+</td> | |
+</tr> | |
+<tr class="field-even field"><th class="field-name">Returns:</th><td class="fi… | |
+</td> | |
+</tr> | |
+<tr class="field-odd field"><th class="field-name">Return type:</th><td class=… | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
+<p>See also <tt class="xref py py-func docutils literal"><span class="pre">tor… | |
</dd></dl> | |
<dl class="function"> | |
-<dt id="sphere.torqueScriptSerial3"> | |
-<tt class="descclassname">sphere.</tt><tt class="descname">torqueScriptSerial3… | |
-<dd><p>Create job script for the Torque queue manager for three binaries</p> | |
+<dt id="sphere.vector_norm"> | |
+<tt class="descclassname">sphere.</tt><tt class="descname">vector_norm</tt><bi… | |
+<dd><p>Returns a 1D vector of normalized values. The input array should have | |
+one row per particle, and three rows; one per Euclidean axis.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Returns:</th><td class="fie… | |
+</tr> | |
+<tr class="field-even field"><th class="field-name">Return type:</th><td class… | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="function"> | |
<dt id="sphere.video"> | |
<tt class="descclassname">sphere.</tt><tt class="descname">video</tt><big>(</b… | |
-<dd><p>Use ffmpeg to combine images to animation. All images should be rendere… | |
+<dd><p>Uses ffmpeg to combine images to animation. All images should be | |
+rendered beforehand using func:<cite>render()</cite>.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>project</strong> (<em>str</em>) – The simulation id of the p… | |
+<li><strong>out_folder</strong> (<em>str</em>) – The output folder for t… | |
+<li><strong>video_format</strong> (<em>str</em>) – The format of the out… | |
+<li><strong>graphics_folder</strong> (<em>str</em>) – The folder contain… | |
+<li><strong>graphics_format</strong> (<em>str</em>) – The format of the … | |
+<li><strong>fps</strong> (<em>int</em>) – The number of frames per secon… | |
+<li><strong>qscale</strong> (<em>float</em>) – The output video quality,… | |
+<li><strong>bitrate</strong> (<em>int</em>) – The bitrate to use in the … | |
+<li><strong>verbose</strong> (<em>bool</em>) – Show ffmpeg output</li> | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
<dl class="function"> | |
<dt id="sphere.visualize"> | |
<tt class="descclassname">sphere.</tt><tt class="descname">visualize</tt><big>… | |
-<dd><p>Visualize output from the target project, | |
-where the temporal progress is of interest.</p> | |
+<dd><p>Visualize output from the target project, where the temporal progress i… | |
+interest. The output will be saved in the current folder with a name | |
+combining the simulation id of the project, and the visualization method.</p> | |
+<table class="docutils field-list" frame="void" rules="none"> | |
+<col class="field-name" /> | |
+<col class="field-body" /> | |
+<tbody valign="top"> | |
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="… | |
+<li><strong>project</strong> (<em>str</em>) – The simulation id of the p… | |
+<li><strong>method</strong> (<em>str</em>) – The type of plot to render.… | |
+‘walls’, ‘triaxial’ and ‘shear’</li> | |
+<li><strong>savefig</strong> (<em>bool</em>) – Save the image instead of… | |
+<li><strong>outformat</strong> – The output format of the plot data. Thi… | |
+format, or in text (‘txt’).</li> | |
+</ul> | |
+</td> | |
+</tr> | |
+</tbody> | |
+</table> | |
</dd></dl> | |
</div> | |
+</div> | |
</div> | |
t@@ -408,12 +1890,18 @@ where the temporal progress is of interest.</p> | |
</div> | |
<div class="sphinxsidebar"> | |
<div class="sphinxsidebarwrapper"> | |
+ <h3><a href="index.html">Table Of Contents</a></h3> | |
+ <ul> | |
+<li><a class="reference internal" href="#">Python API</a><ul> | |
+<li><a class="reference internal" href="#sample-usage">Sample usage</a></li> | |
+<li><a class="reference internal" href="#module-sphere">The <tt class="docutil… | |
+</ul> | |
+</li> | |
+</ul> | |
+ | |
<h4>Previous topic</h4> | |
- <p class="topless"><a href="dem.html" | |
- title="previous chapter">Discrete element method</a></… | |
- <h4>Next topic</h4> | |
- <p class="topless"><a href="sphere_internals.html" | |
- title="next chapter">sphere internals</a></p> | |
+ <p class="topless"><a href="cfd.html" | |
+ title="previous chapter">Fluid simulation and particle… | |
<h3>This Page</h3> | |
<ul class="this-page-menu"> | |
<li><a href="_sources/python_api.txt" | |
t@@ -446,16 +1934,13 @@ where the temporal progress is of interest.</p> | |
<a href="py-modindex.html" title="Python Module Index" | |
>modules</a> |</li> | |
<li class="right" > | |
- <a href="sphere_internals.html" title="sphere internals" | |
- >next</a> |</li> | |
- <li class="right" > | |
- <a href="dem.html" title="Discrete element method" | |
+ <a href="cfd.html" title="Fluid simulation and particle-fluid intera… | |
>previous</a> |</li> | |
- <li><a href="index.html">sphere 0.35 documentation</a> »</li> | |
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> »</… | |
</ul> | |
</div> | |
<div class="footer"> | |
- © Copyright 2012, Anders Damsgaard. | |
+ © Copyright 2014, Anders Damsgaard. | |
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.3. | |
</div> | |
</body> | |
diff --git a/doc/html/search.html b/doc/html/search.html | |
t@@ -8,7 +8,7 @@ | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |
- <title>Search — sphere 0.35 documentation</title> | |
+ <title>Search — sphere 1.00-alpha documentation</title> | |
<link rel="stylesheet" href="_static/default.css" type="text/css" /> | |
<link rel="stylesheet" href="_static/pygments.css" type="text/css" /> | |
t@@ -17,7 +17,7 @@ | |
<script type="text/javascript"> | |
var DOCUMENTATION_OPTIONS = { | |
URL_ROOT: '', | |
- VERSION: '0.35', | |
+ VERSION: '1.00-alpha', | |
COLLAPSE_INDEX: false, | |
FILE_SUFFIX: '.html', | |
HAS_SOURCE: true | |
t@@ -27,10 +27,12 @@ | |
<script type="text/javascript" src="_static/underscore.js"></script> | |
<script type="text/javascript" src="_static/doctools.js"></script> | |
<script type="text/javascript" src="_static/searchtools.js"></script> | |
- <link rel="top" title="sphere 0.35 documentation" href="index.html" /> | |
+ <link rel="top" title="sphere 1.00-alpha documentation" href="index.html" … | |
<script type="text/javascript"> | |
jQuery(function() { Search.loadIndex("searchindex.js"); }); | |
</script> | |
+ | |
+ <script type="text/javascript" id="searchindexloader"></script> | |
</head> | |
t@@ -44,7 +46,7 @@ | |
<li class="right" > | |
<a href="py-modindex.html" title="Python Module Index" | |
>modules</a> |</li> | |
- <li><a href="index.html">sphere 0.35 documentation</a> »</li> | |
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> »</… | |
</ul> | |
</div> | |
t@@ -95,11 +97,11 @@ | |
<li class="right" > | |
<a href="py-modindex.html" title="Python Module Index" | |
>modules</a> |</li> | |
- <li><a href="index.html">sphere 0.35 documentation</a> »</li> | |
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> »</… | |
</ul> | |
</div> | |
<div class="footer"> | |
- © Copyright 2012, Anders Damsgaard. | |
+ © Copyright 2014, Anders Damsgaard. | |
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.3. | |
</div> | |
</body> | |
diff --git a/doc/html/searchindex.js b/doc/html/searchindex.js | |
t@@ -1 +1 @@ | |
-Search.setIndex({objects:{"":{sphere:[4,0,1,""]},sphere:{status:[4,2,1,""],tor… | |
-\ No newline at end of file | |
+Search.setIndex({objects:{"":{sphere:[5,0,1,""]},sphere:{status:[5,1,1,""],con… | |
+\ No newline at end of file | |
diff --git a/doc/html/sphere_internals.html b/doc/html/sphere_internals.html | |
t@@ -8,7 +8,7 @@ | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |
- <title>sphere internals — sphere 0.35 documentation</title> | |
+ <title>sphere internals — sphere 1.00-alpha documentation</title> | |
<link rel="stylesheet" href="_static/default.css" type="text/css" /> | |
<link rel="stylesheet" href="_static/pygments.css" type="text/css" /> | |
t@@ -17,7 +17,7 @@ | |
<script type="text/javascript"> | |
var DOCUMENTATION_OPTIONS = { | |
URL_ROOT: '', | |
- VERSION: '0.35', | |
+ VERSION: '1.00-alpha', | |
COLLAPSE_INDEX: false, | |
FILE_SUFFIX: '.html', | |
HAS_SOURCE: true | |
t@@ -26,8 +26,7 @@ | |
<script type="text/javascript" src="_static/jquery.js"></script> | |
<script type="text/javascript" src="_static/underscore.js"></script> | |
<script type="text/javascript" src="_static/doctools.js"></script> | |
- <link rel="top" title="sphere 0.35 documentation" href="index.html" /> | |
- <link rel="prev" title="Python API" href="python_api.html" /> | |
+ <link rel="top" title="sphere 1.00-alpha documentation" href="index.html" … | |
</head> | |
<body> | |
<div class="related"> | |
t@@ -39,10 +38,7 @@ | |
<li class="right" > | |
<a href="py-modindex.html" title="Python Module Index" | |
>modules</a> |</li> | |
- <li class="right" > | |
- <a href="python_api.html" title="Python API" | |
- accesskey="P">previous</a> |</li> | |
- <li><a href="index.html">sphere 0.35 documentation</a> »</li> | |
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> »</… | |
</ul> | |
</div> | |
t@@ -241,14 +237,14 @@ An important note is that the texttt{C} examples of the … | |
<div class="section" id="c-reference"> | |
<h2>C++ reference<a class="headerlink" href="#c-reference" title="Permalink to… | |
<div class="line-block"> | |
-<div class="line"><em>class</em> <strong>DEM</strong></div> | |
+<div class="line" id=""><span id="project0classDEM"></span><em>class</em> <str… | |
</div> | |
<blockquote> | |
<div><p></p> | |
<p></p> | |
<em>Public Functions</em><blockquote> | |
-<div><p id=""><span id="project0classDEM_1a3cd6355aefc172c712f1c91f4ffc36f2"><… | |
-<div class="line"> <strong>DEM</strong>(std::string inputbin, const int verbos… | |
+<div><p id=""><span id="project0classDEM_1a59ab83e9cbdb7bbeb3f43564fbb294b4"><… | |
+<div class="line"> <strong>DEM</strong>(std::string inputbin, const int verbos… | |
</div> | |
</p> | |
<blockquote> | |
t@@ -279,24 +275,32 @@ An important note is that the texttt{C} examples of the … | |
<div><p></p> | |
<p></p> | |
</div></blockquote> | |
-<p id=""><span id="project0classDEM_1a101aafa4f3197948fafbb1500e58f0f5"></span… | |
-<div class="line">void <strong>checkValues</strong>(void)</div> | |
+<p id=""><span id="project0classDEM_1a2fabfdb491bd95fc41b78b14d7098b76"></span… | |
+<div class="line">void <strong>diagnostics</strong>()</div> | |
</div> | |
</p> | |
<blockquote> | |
<div><p></p> | |
<p></p> | |
</div></blockquote> | |
-<p id=""><span id="project0classDEM_1ae2a1e6696686617f9cbe05b1a264212d"></span… | |
-<div class="line">void <strong>reportValues</strong>(void)</div> | |
+<p id=""><span id="project0classDEM_1ac8ee9469bd6efd769a87e9537be1ad9c"></span… | |
+<div class="line">void <strong>checkValues</strong>()</div> | |
</div> | |
</p> | |
<blockquote> | |
<div><p></p> | |
<p></p> | |
</div></blockquote> | |
-<p id=""><span id="project0classDEM_1a870787f68a947706aac1960e5cc1eda8"></span… | |
-<div class="line">void <strong>startTime</strong>(void)</div> | |
+<p id=""><span id="project0classDEM_1a3f6f5bdff5193be0deeff3599559e33a"></span… | |
+<div class="line">void <strong>reportValues</strong>()</div> | |
+</div> | |
+</p> | |
+<blockquote> | |
+<div><p></p> | |
+<p></p> | |
+</div></blockquote> | |
+<p id=""><span id="project0classDEM_1a4d8d968838244cd44b0961bdc621f874"></span… | |
+<div class="line">void <strong>startTime</strong>()</div> | |
</div> | |
</p> | |
<blockquote> | |
t@@ -327,16 +331,16 @@ An important note is that the texttt{C} examples of the … | |
<div><p></p> | |
<p></p> | |
</div></blockquote> | |
-<p id=""><span id="project0classDEM_1ad686ac121d845e804515bcf7209b9f71"></span… | |
-<div class="line">Float3 <strong>minPos</strong>(void)</div> | |
+<p id=""><span id="project0classDEM_1a5e3a8e75216d4014819019b365b718cb"></span… | |
+<div class="line">Float3 <strong>minPos</strong>()</div> | |
</div> | |
</p> | |
<blockquote> | |
<div><p></p> | |
<p></p> | |
</div></blockquote> | |
-<p id=""><span id="project0classDEM_1ab6c76ee881de3d710fe1fdcb3ca46dfc"></span… | |
-<div class="line">Float3 <strong>maxPos</strong>(void)</div> | |
+<p id=""><span id="project0classDEM_1a55e5de749a916600bb4e0680ecafcd0f"></span… | |
+<div class="line">Float3 <strong>maxPos</strong>()</div> | |
</div> | |
</p> | |
<blockquote> | |
t@@ -359,6 +363,54 @@ An important note is that the texttt{C} examples of the N… | |
<div><p></p> | |
<p></p> | |
</div></blockquote> | |
+<p id=""><span id="project0classDEM_1a4f398ac462ce442eff0b649286c03bcb"></span… | |
+<div class="line">void <strong>printNSarray</strong>(FILE * stream, Float * ar… | |
+</div> | |
+</p> | |
+<blockquote> | |
+<div><p></p> | |
+<p></p> | |
+</div></blockquote> | |
+<p id=""><span id="project0classDEM_1a5495c54f09b7a9d157352daf7de77500"></span… | |
+<div class="line">void <strong>printNSarray</strong>(FILE * stream, Float * ar… | |
+</div> | |
+</p> | |
+<blockquote> | |
+<div><p></p> | |
+<p></p> | |
+</div></blockquote> | |
+<p id=""><span id="project0classDEM_1ae7b0770eb976dbc262e76f414444f78d"></span… | |
+<div class="line">void <strong>printNSarray</strong>(FILE * stream, Float3 * a… | |
+</div> | |
+</p> | |
+<blockquote> | |
+<div><p></p> | |
+<p></p> | |
+</div></blockquote> | |
+<p id=""><span id="project0classDEM_1add65316e75fc181c73ab636dff42187a"></span… | |
+<div class="line">void <strong>printNSarray</strong>(FILE * stream, Float3 * a… | |
+</div> | |
+</p> | |
+<blockquote> | |
+<div><p></p> | |
+<p></p> | |
+</div></blockquote> | |
+<p id=""><span id="project0classDEM_1a777844d118f6d317b17466ec0760bbff"></span… | |
+<div class="line">void <strong>writeNSarray</strong>(Float * array, const char… | |
+</div> | |
+</p> | |
+<blockquote> | |
+<div><p></p> | |
+<p></p> | |
+</div></blockquote> | |
+<p id=""><span id="project0classDEM_1a9eb88295a2de9273d24605e960e1b6f0"></span… | |
+<div class="line">void <strong>writeNSarray</strong>(Float3 * array, const cha… | |
+</div> | |
+</p> | |
+<blockquote> | |
+<div><p></p> | |
+<p></p> | |
+</div></blockquote> | |
</div></blockquote> | |
</div></blockquote> | |
</div> | |
t@@ -379,9 +431,6 @@ An important note is that the texttt{C} examples of the NV… | |
</li> | |
</ul> | |
- <h4>Previous topic</h4> | |
- <p class="topless"><a href="python_api.html" | |
- title="previous chapter">Python API</a></p> | |
<h3>This Page</h3> | |
<ul class="this-page-menu"> | |
<li><a href="_sources/sphere_internals.txt" | |
t@@ -413,14 +462,11 @@ An important note is that the texttt{C} examples of the … | |
<li class="right" > | |
<a href="py-modindex.html" title="Python Module Index" | |
>modules</a> |</li> | |
- <li class="right" > | |
- <a href="python_api.html" title="Python API" | |
- >previous</a> |</li> | |
- <li><a href="index.html">sphere 0.35 documentation</a> »</li> | |
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> »</… | |
</ul> | |
</div> | |
<div class="footer"> | |
- © Copyright 2012, Anders Damsgaard. | |
+ © Copyright 2014, Anders Damsgaard. | |
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.3. | |
</div> | |
</body> | |
diff --git a/doc/pdf/sphere.pdf b/doc/pdf/sphere.pdf | |
Binary files differ. | |
diff --git a/doc/sphinx/cfd.rst b/doc/sphinx/cfd.rst | |
t@@ -0,0 +1,578 @@ | |
+Fluid simulation and particle-fluid interaction | |
+=============================================== | |
+A new and experimental addition to *sphere* is the ability to simulate a mixtu… | |
+of particles and a Newtonian fluid. The fluid is simulated using an Eulerian | |
+continuum approach, using a custom CUDA solver for GPU computation. This | |
+approach allows for fast simulations due to the limited need for GPU-CPU | |
+communications, as well as a flexible code base. | |
+ | |
+The following sections will describe the theoretical background, as well as the | |
+solution procedure and the numerical implementation. | |
+ | |
+Derivation of the Navier Stokes equations with porosity | |
+------------------------------------------------------- | |
+Following the outline presented by `Limache and Idelsohn (2006)`_, the | |
+continuity equation for an incompressible fluid material is given by: | |
+ | |
+.. math:: | |
+ \nabla \cdot \boldsymbol{v} = 0 | |
+ | |
+and the momentum equation: | |
+ | |
+.. math:: | |
+ \rho \frac{\partial \boldsymbol{v}}{\partial t} | |
+ + \rho (\boldsymbol{v} \cdot \nabla \boldsymbol{v}) | |
+ = \nabla \cdot \boldsymbol{\sigma} | |
+ + \rho \boldsymbol{f} | |
+ | |
+Here, :math:`\boldsymbol{v}` is the fluid velocity, :math:`\rho` is the | |
+fluid density, :math:`\boldsymbol{\sigma}` is the `Cauchy stress tensor`_, and | |
+:math:`\boldsymbol{f}` is a body force (e.g. gravity). For incompressible | |
+Newtonian fluids, the Cauchy stress is given by: | |
+ | |
+.. math:: | |
+ \boldsymbol{\sigma} = -p \boldsymbol{I} + \boldsymbol{\tau} | |
+ | |
+:math:`p` is the fluid pressure, :math:`\boldsymbol{I}` is the identity | |
+tensor, and :math:`\boldsymbol{\tau}` is the deviatoric stress tensor, given | |
+by: | |
+ | |
+.. math:: | |
+ \boldsymbol{\tau} = | |
+ \mu_f \nabla \boldsymbol{v} | |
+ + \mu_f (\nabla \boldsymbol{v})^T | |
+ | |
+By using the following vector identities: | |
+ | |
+.. math:: | |
+ \nabla \cdot (p \boldsymbol{I}) = \nabla p | |
+ | |
+ \nabla \cdot (\nabla \boldsymbol{v}) = \nabla^2 \boldsymbol{v} | |
+ | |
+ \nabla \cdot (\nabla \boldsymbol{v})^T | |
+ = \nabla (\nabla \cdot \boldsymbol{v}) | |
+ | |
+the deviatoric component of the Cauchy stress tensor simplifies to the | |
+following, assuming that spatial variations in the viscosity can be neglected: | |
+ | |
+.. math:: | |
+ = -\nabla p | |
+ + \mu_f \nabla^2 \boldsymbol{v} | |
+ | |
+Since we are dealing with fluid flow in a porous medium, additional terms are | |
+introduced to the equations for conservation of mass and momentum. In the | |
+following, the equations are derived for the first spatial component. The | |
+solution for the other components is trivial. | |
+ | |
+The porosity value (in the saturated porous medium the volumetric fraction of | |
+the fluid phase) denoted :math:`\phi` is incorporated in the continuity and | |
+momentum equations. The continuity equation becomes: | |
+ | |
+.. math:: | |
+ \frac{\partial \phi}{\partial t} | |
+ + \nabla \cdot (\phi \boldsymbol{v}) = 0 | |
+ | |
+For the :math:`x` component, the Lagrangian formulation of the momentum equati… | |
+with a body force :math:`\boldsymbol{f}` becomes: | |
+ | |
+.. math:: | |
+ \frac{D (\phi v_x)}{D t} | |
+ = \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\sigma}) \right]_x | |
+ + \phi f_x | |
+ | |
+In the Eulerian formulation, an advection term is added, and the Cauchy stress | |
+tensor is represented as isotropic and deviatoric components individually: | |
+ | |
+.. math:: | |
+ \frac{\partial (\phi v_x)}{\partial t} | |
+ + \boldsymbol{v} \cdot \nabla (\phi v_x) | |
+ = \frac{1}{\rho} \left[ \nabla \cdot (-\phi p \boldsymbol{I}) | |
+ + \phi \boldsymbol{\tau}) \right]_x | |
+ + \phi f_x | |
+ | |
+Using vector identities to rewrite the advection term, and expanding the fluid | |
+stress tensor term: | |
+ | |
+.. math:: | |
+ \frac{\partial (\phi v_x)}{\partial t} | |
+ + \nabla \cdot (\phi v_x \boldsymbol{v}) | |
+ - \phi v_x (\nabla \cdot \boldsymbol{v}) | |
+ = \frac{1}{\rho} \left[ -\nabla \phi p \right]_x | |
+ + \frac{1}{\rho} \left[ -\phi \nabla p \right]_x | |
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
+ + \phi f_x | |
+ | |
+Spatial variations in the porosity are neglected, | |
+ | |
+.. math:: | |
+ \nabla \phi := 0 | |
+ | |
+and the pressure is attributed to the fluid phase alone (model B in Zhu et al. | |
+2007 and Zhou et al. 2010). The divergence of fluid velocities is defined to be | |
+zero: | |
+ | |
+.. math:: | |
+ \nabla \cdot \boldsymbol{v} := 0 | |
+ | |
+With these assumptions, the momentum equation simplifies to: | |
+ | |
+.. math:: | |
+ \frac{\partial (\phi v_x)}{\partial t} | |
+ + \nabla \cdot (\phi v_x \boldsymbol{v}) | |
+ = -\frac{1}{\rho} \frac{\partial p}{\partial x} | |
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
+ + \phi f_x | |
+ | |
+The remaining part of the advection term is for the :math:`x` component | |
+found as: | |
+ | |
+.. math:: | |
+ \nabla \cdot (\phi v_x \boldsymbol{v}) = | |
+ \left[ | |
+ \frac{\partial}{\partial x}, | |
+ \frac{\partial}{\partial y}, | |
+ \frac{\partial}{\partial z} | |
+ \right] | |
+ \left[ | |
+ \begin{array}{c} | |
+ \phi v_x v_x\\ | |
+ \phi v_x v_y\\ | |
+ \phi v_x v_z\\ | |
+ \end{array} | |
+ \right] | |
+ = | |
+ \frac{\partial (\phi v_x v_x)}{\partial x} + | |
+ \frac{\partial (\phi v_x v_y)}{\partial y} + | |
+ \frac{\partial (\phi v_x v_z)}{\partial z} | |
+ | |
+The deviatoric stress tensor is in this case symmetrical, i.e. :math:`\tau_{ij} | |
+= \tau_{ji}`, and is found by: | |
+ | |
+.. math:: | |
+ \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
+ = \frac{1}{\rho} | |
+ \left[ | |
+ \left[ | |
+ \frac{\partial}{\partial x}, | |
+ \frac{\partial}{\partial y}, | |
+ \frac{\partial}{\partial z} | |
+ \right] | |
+ \phi | |
+ \left[ | |
+ \begin{matrix} | |
+ \tau_{xx} & \tau_{xy} & \tau_{xz}\\ | |
+ \tau_{yx} & \tau_{yy} & \tau_{yz}\\ | |
+ \tau_{zx} & \tau_{zy} & \tau_{zz}\\ | |
+ \end{matrix} | |
+ \right] | |
+ \right]_x | |
+ | |
+ = \frac{1}{\rho} | |
+ \left[ | |
+ \begin{array}{c} | |
+ \frac{\partial (\phi \tau_{xx})}{\partial x} | |
+ + \frac{\partial (\phi \tau_{xy})}{\partial y} | |
+ + \frac{\partial (\phi \tau_{xz})}{\partial z}\\ | |
+ \frac{\partial (\phi \tau_{yx})}{\partial x} | |
+ + \frac{\partial (\phi \tau_{yy})}{\partial y} | |
+ + \frac{\partial (\phi \tau_{yz})}{\partial z}\\ | |
+ \frac{\partial (\phi \tau_{zx})}{\partial x} | |
+ + \frac{\partial (\phi \tau_{zy})}{\partial y} | |
+ + \frac{\partial (\phi \tau_{zz})}{\partial z}\\ | |
+ \end{array} | |
+ \right]_x | |
+ = \frac{1}{\rho} | |
+ \left( | |
+ \frac{\partial (\phi \tau_{xx})}{\partial x} | |
+ + \frac{\partial (\phi \tau_{xy})}{\partial y} | |
+ + \frac{\partial (\phi \tau_{xz})}{\partial z} | |
+ \right) | |
+ | |
+In a linear viscous fluid, the stress and strain rate | |
+(:math:`\dot{\boldsymbol{\epsilon}}`) is linearly dependent, scaled by the | |
+viscosity parameter :math:`\mu_f`: | |
+ | |
+.. math:: | |
+ \tau_{ij} = 2 \mu_f \dot{\epsilon}_{ij} | |
+ = \mu_f \left( | |
+ \frac{\partial v_i}{\partial x_j} + \frac{\partial v_j}{\partial x_i} | |
+ \right) | |
+ | |
+With this relationship, the deviatoric stress tensor components can be | |
+calculated as: | |
+ | |
+.. math:: | |
+ \tau_{xx} = 2 \mu_f \frac{\partial v_x}{\partial x} \qquad | |
+ \tau_{yy} = 2 \mu_f \frac{\partial v_y}{\partial y} \qquad | |
+ \tau_{zz} = 2 \mu_f \frac{\partial v_z}{\partial z} | |
+ | |
+ \tau_{xy} = \mu_f \left( | |
+ \frac{\partial v_x}{\partial y} + \frac{\partial v_y}{\partial x} \right) | |
+ | |
+ \tau_{xz} = \mu_f \left( | |
+ \frac{\partial v_x}{\partial z} + \frac{\partial v_z}{\partial x} \right) | |
+ | |
+ \tau_{yz} = \mu_f \left( | |
+ \frac{\partial v_y}{\partial z} + \frac{\partial v_z}{\partial y} \right) | |
+ | |
+where :math:`\mu_f` is the dynamic viscosity. The above formulation of the | |
+fluid rheology assumes identical bulk and shear viscosities. The derivation of | |
+the equations for the other spatial components is trivial. | |
+ | |
+Porosity estimation | |
+------------------- | |
+The solid volume in each fluid cell is determined by the ratio of the | |
+a cell-centered spherical cell volume (:math:`V_c`) and the sum of intersecting | |
+particle volumes (:math:`V_s`). The spherical cell volume has a center at | |
+:math:`\boldsymbol{x}_i`, and a radius of :math:`R_i`, which is equal to half | |
+the fluid cell width. The nearby particles are characterized by position | |
+:math:`\boldsymbol{x}_j` and radius :math:`r_j`. The center distance is defined | |
+as: | |
+ | |
+.. math:: | |
+ d_{ij} = ||\boldsymbol{x}_i - \boldsymbol{x}_j|| | |
+ | |
+The common volume of the two intersecting spheres is zero if the volumes aren't | |
+intersecting, lens shaped if they are intersecting, and spherical if the | |
+particle is fully contained by the spherical cell volume: | |
+ | |
+.. math:: | |
+ V^s_{i} = \sum_j | |
+ \begin{cases} | |
+ 0 & \textit{if } R_i + r_j \leq d_{ij} \\ | |
+ \frac{1}{12d_{ij}} \left[ \pi (R_i + r_j - d_{ij})^2 | |
+ (d_{ij}^2 + 2d_{ij}r_j - 3r_j^2 + 2d_{ij} R_i + 6r_j R_i - 3R_i^2) | |
+ \right] & \textit{if } R_i - r_j < d_{ij} < R_i + r_j \\ | |
+ \frac{4}{3} \pi r^3_j & \textit{if } d_{ij} \leq R_i - r_j | |
+ \end{cases} | |
+ | |
+Using this method, the cell porosity values are continuous through time as | |
+particles enter and exit the cell volume. The rate of porosity change | |
+(:math:`d\phi/dt`) is estimated by the backwards Euler method | |
+by considering the previous and current porosity. | |
+ | |
+Particle-fluid interaction | |
+-------------------------- | |
+The momentum exchange of the granular and fluid phases follows the procedure | |
+outlined by Gidaspow 1992 and Shamy and Zhegal 2005. The fluid and particle | |
+interaction is based on the concept of drag, where the magnitude is based on | |
+semi-empirical relationships. The drag force scales linearly with the relative | |
+difference in velocity between the fluid and particle phase. On the base of | |
+Newton's third law, the resulting drag force is applied with opposite signs to | |
+the particle and fluid. | |
+ | |
+For fluid cells with porosities (:math:`\phi`) less or equal to 0.8, the drag | |
+force is based on the Ergun (1952) equation: | |
+ | |
+.. math:: | |
+ \bar{\boldsymbol{f}}_d = \left( | |
+ 150 \frac{\mu_f (1-\phi)^2}{\phi\bar{d}^2} | |
+ + 1.75 \frac{(1-\phi)\rho_f | |
+ ||\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p||}{\bar{d}} | |
+ \right) | |
+ (\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p) | |
+ | |
+here, :math:`\bar{d}` denotes the average particle diameter in the cell, | |
+:math:`\boldsymbol{v}_f` is the fluid flow velocity, and | |
+:math:`\bar{\boldsymbol{v}}_p` is the average particle velocity in the cell. A… | |
+particles in contact with the previously mentioned cell-centered sphere for | |
+porosity estimation contribute to the average particle velocity and diameter in | |
+the fluid cell. | |
+ | |
+If the porosity is greater than 0.8, the cell-averaged drag force | |
+(:math:`\bar{\boldsymbol{f}}_d` is found from the Wen and Yu (1966) equation, | |
+which considers the fluid flow situation: | |
+ | |
+.. math:: | |
+ \bar{\boldsymbol{f}}_d = \left( | |
+ \frac{3}{4} | |
+ \frac{C_d (1-\phi) \phi^{-2.65} \mu_f \rho_f | |
+ ||\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p||}{\bar{d}} | |
+ \right) | |
+ (\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p) | |
+ | |
+The drag coefficient :math:`C_d` is evaluated depending on the magnitude of the | |
+Reynolds number :math:`Re`: | |
+ | |
+.. math:: | |
+ C_d = | |
+ \begin{cases} | |
+ \frac{24}{Re} (1+0.15 (Re)^{0.687} & \textit{if } Re < 1,000 \\ | |
+ 0.44 & \textit{if } Re \geq 1,000 | |
+ \end{cases} | |
+ | |
+where the Reynold's number is found by: | |
+ | |
+.. math:: | |
+ Re = \frac{\phi\rho_f\bar{d}}{\mu_f} | |
+ ||\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p|| | |
+ | |
+The interaction force is applied to the fluid with negative sign as a | |
+contribution to the body force :math:`\boldsymbol{f}`. The fluid interaction | |
+force applied particles in the fluid cell is: | |
+ | |
+.. math:: | |
+ \boldsymbol{f}_i = \frac{\bar{\boldsymbol{f}}_d V_p}{1-\phi} | |
+ | |
+where :math:`V_p` denotes the particle volume. Optionally, the above | |
+interaction force could be expanded to include the force induced by the fluid | |
+pressure gradient: | |
+ | |
+.. math:: | |
+ \boldsymbol{f}_i = \left( | |
+ -\nabla p + | |
+ \frac{\bar{\boldsymbol{f}}_d}{1-\phi} | |
+ \right) V_p | |
+ | |
+ | |
+Fluid dynamics solution procedure by operator splitting | |
+------------------------------------------------------- | |
+The partial differential terms in the previously described equations are found | |
+using finite central differences. Modifying the operator splitting methodology | |
+presented by Langtangen et al. (2002), the predicted velocity | |
+:math:`\boldsymbol{v}^*` after a finite time step | |
+:math:`\Delta t` is found by explicit integration of the momentum equation. | |
+ | |
+.. math:: | |
+ \frac{\Delta (\phi v_x)}{\Delta t} | |
+ + \nabla \cdot (\phi v_x \boldsymbol{v}) | |
+ = - \frac{1}{\rho} \frac{\Delta p}{\Delta x} | |
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
+ + \phi f_x | |
+ | |
+ \Downarrow | |
+ | |
+ \phi \frac{\Delta v_x}{\Delta t} | |
+ + v_x \frac{\Delta \phi}{\Delta t} | |
+ + \nabla \cdot (\phi v_x \boldsymbol{v}) | |
+ = - \frac{1}{\rho} \frac{\Delta p}{\Delta x} | |
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
+ + \phi f_x | |
+ | |
+We want to isolate :math:`\Delta v_x` in the above equation in order to project | |
+the new velocity. | |
+ | |
+.. math:: | |
+ \phi \frac{\Delta v_x}{\Delta t} | |
+ = - \frac{1}{\rho} \frac{\Delta p}{\Delta x} | |
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
+ + \phi f_x | |
+ - v_x \frac{\Delta \phi}{\Delta t} | |
+ - \nabla \cdot (\phi v_x \boldsymbol{v}) | |
+ | |
+ \Delta v_x | |
+ = - \frac{1}{\rho} \frac{\Delta p}{\Delta x} \frac{\Delta t}{\phi} | |
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x | |
+ \frac{\Delta t}{\phi} | |
+ + \Delta t f_x | |
+ - v_x \frac{\Delta \phi}{\phi} | |
+ - \nabla \cdot (\phi v_x \boldsymbol{v}) \frac{\Delta t}{\phi} | |
+ | |
+The term :math:`\beta` is introduced as an adjustable, dimensionless parameter | |
+in the range :math:`[0;1]`, and determines the importance of the old pressure | |
+values in the solution procedure (Langtangen et al. 2002). A value of 0 | |
+corresponds to `Chorin's projection method`_ originally described | |
+in `Chorin (1968)`_. | |
+ | |
+.. math:: | |
+ v_x^* = v_x^t + \Delta v_x | |
+ | |
+ v_x^* = v_x^t | |
+ - \frac{\beta}{\rho} \frac{\Delta p^t}{\Delta x} \frac{\Delta t}{\phi^t} | |
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi^t \boldsymbol{\tau}^t) \right]_x | |
+ \frac{\Delta t}{\phi} | |
+ + \Delta t f_x | |
+ - v^t_x \frac{\Delta \phi}{\phi^t} | |
+ - \nabla \cdot (\phi^t v_x^t \boldsymbol{v}^t) \frac{\Delta t}{\phi^t} | |
+ | |
+Here, :math:`\Delta x` denotes the cell spacing. The velocity found | |
+(:math:`v_x^*`) is only a prediction of the fluid velocity at time | |
+:math:`t+\Delta t`, since the estimate isn't constrained by the continuity | |
+equation: | |
+ | |
+.. math:: | |
+ \frac{\Delta \phi^t}{\Delta t} + \nabla \cdot (\phi^t | |
+ \boldsymbol{v}^{t+\Delta t}) = 0 | |
+ | |
+The divergence of a scalar and vector can be `split`_: | |
+ | |
+.. math:: | |
+ \phi^t \nabla \cdot \boldsymbol{v}^{t+\Delta t} + | |
+ \boldsymbol{v}^{t+\Delta t} \cdot \nabla \phi^t | |
+ + \frac{\Delta \phi^t}{\Delta t} = 0 | |
+ | |
+The predicted velocity is corrected using the new pressure (Langtangen et al. | |
+2002): | |
+ | |
+.. math:: | |
+ \boldsymbol{v}^{t+\Delta t} = \boldsymbol{v}^* | |
+ - \frac{\Delta t}{\rho} \nabla \epsilon | |
+ \quad \text{where} \quad | |
+ \epsilon = p^{t+\Delta t} - \beta p^t | |
+ | |
+The above formulation of the future velocity is put into the continuity | |
+equation: | |
+ | |
+.. math:: | |
+ \Rightarrow | |
+ \phi^t \nabla \cdot | |
+ \left( \boldsymbol{v}^* - \frac{\Delta t}{\rho} \nabla \epsilon \right) | |
+ + | |
+ \left( \boldsymbol{v}^* - \frac{\Delta t}{\rho} \nabla \epsilon \right) | |
+ \cdot \nabla \phi^t + \frac{\Delta \phi^t}{\Delta t} = 0 | |
+ | |
+.. math:: | |
+ \Rightarrow | |
+ \phi^t \nabla \cdot | |
+ \boldsymbol{v}^* - \frac{\Delta t}{\rho} \phi^t \nabla^2 \epsilon | |
+ + \nabla \phi^t \cdot \boldsymbol{v}^* | |
+ - \nabla \phi^t \cdot \nabla \epsilon \frac{\Delta t}{\rho} | |
+ + \frac{\Delta \phi^t}{\Delta t} = 0 | |
+ | |
+.. math:: | |
+ \Rightarrow | |
+ \frac{\Delta t}{\rho} \phi^t \nabla^2 \epsilon | |
+ = \phi^t \nabla \cdot \boldsymbol{v}^* | |
+ + \nabla \phi^t \cdot \boldsymbol{v}^* | |
+ - \nabla \phi^t \cdot \nabla \epsilon \frac{\Delta t}{\rho} | |
+ + \frac{\Delta \phi^t}{\Delta t} | |
+ | |
+The pressure difference in time becomes a `Poisson equation`_ with added terms: | |
+ | |
+.. math:: | |
+ \Rightarrow | |
+ \nabla^2 \epsilon | |
+ = \frac{\nabla \cdot \boldsymbol{v}^* \rho}{\Delta t} | |
+ + \frac{\nabla \phi^t \cdot \boldsymbol{v}^* \rho}{\Delta t \phi^t} | |
+ - \frac{\nabla \phi^t \cdot \nabla \epsilon}{\phi^t} | |
+ + \frac{\Delta \phi^t \rho}{\Delta t^2 \phi^t} | |
+ | |
+The right hand side of the above equation is termed the *forcing function* | |
+:math:`f`, which is decomposed into two terms, :math:`f_1` and :math:`f_2`: | |
+ | |
+.. math:: | |
+ f_1 | |
+ = \frac{\nabla \cdot \boldsymbol{v}^* \rho}{\Delta t} | |
+ + \frac{\nabla \phi^t \cdot \boldsymbol{v}^* \rho}{\Delta t \phi^t} | |
+ + \frac{\Delta \phi^t \rho}{\Delta t^2 \phi^t} | |
+ | |
+ f_2 = | |
+ \frac{\nabla \phi^t \cdot \nabla \epsilon}{\phi^t} | |
+ | |
+ | |
+During the `Jacobi iterative solution procedure`_ :math:`f_1` remains constant, | |
+while :math:`f_2` changes value. For this reason, :math:`f_1` is found only | |
+during the first iteration, while :math:`f_2` is updated every time. The value | |
+of the forcing function is found as: | |
+ | |
+.. math:: | |
+ f = f_1 - f_2 | |
+ | |
+Using second-order finite difference approximations of the Laplace operator | |
+second-order partial derivatives, the differential equations become a system of | |
+equations that is solved using `iteratively`_ using Jacobi updates. The total | |
+number of unknowns is :math:`(n_x - 1)(n_y - 1)(n_z - 1)`. | |
+ | |
+The discrete Laplacian (approximation of the Laplace operator) can be obtained | |
+by a finite-difference seven-point stencil in a three-dimensional, cubic | |
+grid with cell spacing :math:`\Delta x, \Delta y, \Delta z`, considering the s… | |
+face neighbors: | |
+ | |
+.. math:: | |
+ \nabla^2 \epsilon_{i_x,i_y,i_z} \approx | |
+ \frac{\epsilon_{i_x-1,i_y,i_z} - 2 \epsilon_{i_x,i_y,i_z} | |
+ + \epsilon_{i_x+1,i_y,i_z}}{\Delta x^2} | |
+ + \frac{\epsilon_{i_x,i_y-1,i_z} - 2 \epsilon_{i_x,i_y,i_z} | |
+ + \epsilon_{i_x,i_y+1,i_z}}{\Delta y^2} | |
+ | |
+ + \frac{\epsilon_{i_x,i_y,i_z-1} - 2 \epsilon_{i_x,i_y,i_z} | |
+ + \epsilon_{i_x,i_y,i_z+1}}{\Delta z^2} | |
+ \approx f_{i_x,i_y,i_z} | |
+ | |
+Within a Jacobi iteration, the value of the unknowns (:math:`\epsilon^n`) is | |
+used to find an updated solution estimate (:math:`\epsilon^{n+1}`). | |
+The solution for the updated value takes the form: | |
+ | |
+.. math:: | |
+ \epsilon^{n+1}_{i_x,i_y,i_z} | |
+ = \frac{-\Delta x^2 \Delta y^2 \Delta z^2 f_{i_x,i_y,i_z} | |
+ + \Delta y^2 \Delta z^2 (\epsilon^n_{i_x-1,i_y,i_z} + | |
+ \epsilon^n_{i_x+1,i_y,i_z}) | |
+ + \Delta x^2 \Delta z^2 (\epsilon^n_{i_x,i_y-1,i_z} + | |
+ \epsilon^n_{i_x,i_y+1,i_z}) | |
+ + \Delta x^2 \Delta y^2 (\epsilon^n_{i_x,i_y,i_z-1} + | |
+ \epsilon^n_{i_x,i_y,i_z+1})} | |
+ {2 (\Delta x^2 \Delta y^2 | |
+ + \Delta x^2 \Delta z^2 | |
+ + \Delta y^2 \Delta z^2) } | |
+ | |
+The difference between the current and updated value is termed the *normalized | |
+residual*: | |
+ | |
+.. math:: | |
+ r_{i_x,i_y,i_z} = \frac{(\epsilon^{n+1}_{i_x,i_y,i_z} | |
+ - \epsilon^n_{i_x,i_y,i_z})^2}{(\epsilon^{n+1}_{i_x,i_y,i_z})^2} | |
+ | |
+Note that the :math:`\epsilon` values cannot be 0 due to the above normalizati… | |
+of the residual. | |
+ | |
+The updated values are at the end of the iteration stored as the current value… | |
+and the maximal value of the normalized residual is found. If this value is | |
+larger than a tolerance criteria, the procedure is repeated. The iterative | |
+procedure is ended if the number of iterations exceeds a defined limit. | |
+ | |
+After the values of :math:`\epsilon` are found, they are used to find the new | |
+pressures and velocities: | |
+ | |
+.. math:: | |
+ \bar{p}^{t+\Delta t} = \beta \bar{p}^t + \epsilon | |
+ | |
+.. math:: | |
+ \bar{\boldsymbol{v}}^{t+\Delta t} = | |
+ \bar{\boldsymbol{v}}^* - \frac{\Delta t}{\rho} \nabla \epsilon | |
+ | |
+ | |
+Boundary conditions | |
+------------------- | |
+The lateral boundaries are periodic. This cannot be changed in the current | |
+version of ``sphere``. This means that the fluid properties at the paired, | |
+parallel lateral (:math:`x` and :math:`y`) boundaries are identical. A flow | |
+leaving through one side reappears on the opposite side. | |
+ | |
+The top and bottom boundary conditions of the fluid grid can be either: | |
+prescribed pressure (Dirichlet), or prescribed velocity (Neumann). The | |
+(horizontal) velocities parallel to the boundaries are free to attain other | |
+values (free slip). The Dirichlet boundary condition is enforced by keeping the | |
+value of :math:`\epsilon` constant at the boundaries, e.g.: | |
+ | |
+.. math:: | |
+ \epsilon^{n+1}_{i_x,i_y,i_z = 1 \vee n_z} | |
+ = | |
+ \epsilon^{n}_{i_x,i_y,i_z = 1 \vee n_z} | |
+ | |
+The Neumann boundary condition of no flow across the boundary is enforced by | |
+setting the gradient of :math:`\epsilon` perpendicular to the boundary to zero, | |
+e.g.: | |
+ | |
+.. math:: | |
+ \nabla_z \epsilon^{n+1}_{i_x,i_y,i_z = 1 \vee n_z} = 0 | |
+ | |
+ | |
+Numerical implementation | |
+------------------------ | |
+Ghost nodes | |
+ | |
+--- | |
+ | |
+ | |
+ | |
+ | |
+.. _Limache and Idelsohn (2006): http://www.cimec.org.ar/ojs/index.php/mc/arti… | |
+.. _Cauchy stress tensor: https://en.wikipedia.org/wiki/Cauchy_stress_tensor | |
+.. _`Chorin's projection method`: https://en.wikipedia.org/wiki/Projection_met… | |
+.. _`Chorin (1968)`: http://www.ams.org/journals/mcom/1968-22-104/S0025-5718-1… | |
+.. _split: http://www.wolframalpha.com/input/?i=div(p+v) | |
+.. _Poisson equation: https://en.wikipedia.org/wiki/Poisson's_equation | |
+.. _`Jacobi iterative solution procedure`: http://www.rsmas.miami.edu/personal… | |
+.. _iteratively: https://en.wikipedia.org/wiki/Relaxation_(iterative_method) | |
+ | |
diff --git a/doc/sphinx/conf.py b/doc/sphinx/conf.py | |
t@@ -50,16 +50,16 @@ master_doc = 'index' | |
# General information about the project. | |
project = u'sphere' | |
-copyright = u'2012, Anders Damsgaard' | |
+copyright = u'2014, Anders Damsgaard' | |
# The version info for the project you're documenting, acts as replacement for | |
# |version| and |release|, also used in various other places throughout the | |
# built documents. | |
# | |
# The short X.Y version. | |
-version = '0.35' | |
+version = '1.00' | |
# The full version, including alpha/beta/rc tags. | |
-release = '0.35' | |
+release = '1.00-alpha' | |
# The language for content autogenerated by Sphinx. Refer to documentation | |
# for a list of supported languages. | |
diff --git a/doc/sphinx/dem.rst b/doc/sphinx/dem.rst | |
t@@ -1,16 +1,217 @@ | |
Discrete element method | |
======================= | |
-The discrete element method (or distinct element method) was initially | |
-formulated by Cundall and Strack (1979). It simulates the physical behavior and | |
-interaction of discrete, unbreakable particles, with their own mass and inerti… | |
-under the influence of e.g. gravity and boundary conditions such as moving | |
-walls. By discretizing time into small time steps, explicit integration of | |
-Newton's second law of motion is used to predict the new position and kinematic | |
-values for each particle from the previous sums of forces. This Lagrangian | |
-approach is ideal for simulating discontinuous materials, such as granular | |
-matter. | |
-The complexity of the computations is kept low by representing the particles as | |
-spheres, which keeps contact-searching algorithms simple. | |
+Granular material is a very common form of matter, both in nature and industry. | |
+It can be defined as material consisting of interacting, discrete particles. | |
+Common granular materials include gravels, sands and soils, ice bergs, | |
+asteroids, powders, seeds, and other foods. Over 75% of the raw materials that | |
+pass through industry are granular. This wide occurrence has driven the desire | |
+to understand the fundamental mechanics of the material. | |
+Contrary to other common materials such as gases, liquids and solids, a general | |
+mathematical formulation of it's behavior hasn't yet been found. Granular | |
+material can, however, display states that somewhat resemble gases, fluids and | |
+solids. | |
+.. The discrete element method (or distinct element method) was initially | |
+ formulated by Cundall and Strack (1979). It simulates the physical behavio… | |
+ interaction of discrete, unbreakable particles, with their own mass and in… | |
+ under the influence of e.g. gravity and boundary conditions such as moving | |
+ walls. By discretizing time into small time steps, explicit integration of | |
+ Newton's second law of motion is used to predict the new position and kine… | |
+ values for each particle from the previous sums of forces. This Lagrangian | |
+ approach is ideal for simulating discontinuous materials, such as granular | |
+ matter. | |
+ The complexity of the computations is kept low by representing the particl… | |
+ spheres, which keeps contact-searching algorithms simple. | |
+ | |
+The `Discrete Element Method | |
+<https://en.wikipedia.org/wiki/Discrete_element_method>`_ (DEM) is a numerical | |
+method that can be used to | |
+simulate the interaction of particles. Originally derived from | |
+`Molecular Dynamics <https://en.wikipedia.org/wiki/Molecular_dynamics>`_, | |
+it simulates particles as separate entities, and calculates their positions, | |
+velocities, and accelerations through time. See Cundall and Strack (1979) and | |
+`this blog post | |
+<http://anders-dc.github.io/2013/10/16/the-discrete-element-method/>`_ for | |
+general introduction to the DEM. The following sections will highlight the | |
+DEM implementation in ``sphere``. Some of the details are also described in | |
+Damsgaard et al. 2013. In the used notation, a bold symbol denotes a | |
+three-dimensional vector, and a dot denotes that the entity is a temporal | |
+derivative. | |
+ | |
+Contact search | |
+-------------- | |
+Homogeneous cubic grid. | |
+ | |
+.. math:: | |
+ \delta_n^{ij} = ||\boldsymbol{x}^i - \boldsymbol{x}^j|| - (r^i + r^j) | |
+ | |
+where :math:`r` is the particle radius, and :math:`\boldsymbol{x}` denotes the | |
+positional vector of a particle, and :math:`i` and :math:`j` denote the indexes | |
+of two particles. Negative values of :math:`\delta_n` denote that the particles | |
+are overlapping. | |
+ | |
+ | |
+Contact interaction | |
+------------------- | |
+Now that the inter-particle contacts have been identified and characterized by | |
+their overlap, the resulting forces from the interaction can be resolved. The | |
+interaction is decomposed into normal and tangential components, relative to t… | |
+contact interface orientation. The normal vector to the contact interface is | |
+found by: | |
+ | |
+.. math:: | |
+ \boldsymbol{n}^{ij} = | |
+ \frac{\boldsymbol{x}^i - \boldsymbol{x}^j} | |
+ {||\boldsymbol{x}^i - \boldsymbol{x}^j||} | |
+ | |
+The contact velocity :math:`\dot{\boldsymbol{\delta}}` is found by: | |
+ | |
+.. math:: | |
+ \dot{\boldsymbol{\delta}}^{ij} = | |
+ (\boldsymbol{x}^i - \boldsymbol{x}^j) | |
+ + (r^i + \frac{\delta_n^{ij}}{2}) | |
+ (\boldsymbol{n}^{ij} \times \boldsymbol{\omega}^{i}) | |
+ + (r^j + \frac{\delta_n^{ij}}{2}) | |
+ (\boldsymbol{n}^{ij} \times \boldsymbol{\omega}^{j}) | |
+ | |
+The contact velocity is decomposed into normal and tangential components, | |
+relative to the contact interface. The normal component is: | |
+ | |
+.. math:: | |
+ \dot{\delta}^{ij}_n = | |
+ -(\dot{\boldsymbol{\delta}}^{ij} \cdot \boldsymbol{n}^{ij}) | |
+ | |
+and the tangential velocity component is found as: | |
+ | |
+.. math:: | |
+ \dot{\boldsymbol{\delta}}^{ij}_t = | |
+ \dot{\boldsymbol{\delta}}^{ij} | |
+ - \boldsymbol{n}^{ij} | |
+ (\boldsymbol{n}^{ij} \cdot \dot{\boldsymbol{\delta}}^{ij}) | |
+ | |
+where :math:`\boldsymbol{\omega}` is the rotational velocity vector of a | |
+particle. The total tangential displacement on the contact plane is found | |
+incrementally: | |
+ | |
+.. math:: | |
+ \boldsymbol{\delta}_{t,\text{uncorrected}}^{ij} = | |
+ \int_0^{t_c} | |
+ \dot{\boldsymbol{\delta}}^{ij}_t \Delta t | |
+ | |
+where :math:`t_c` is the duration of the contact and :math:`\Delta t` is the | |
+computational time step length. The tangential contact interface displacement … | |
+set to zero when a contact pair no longer overlaps. At each time step, the val… | |
+of :math:`\boldsymbol{\delta}_t` is corrected for rotation of the contact | |
+interface: | |
+ | |
+.. math:: | |
+ \boldsymbol{\delta}_t^{ij} = \boldsymbol{\delta}_{t,\text{uncorrected}}^{ij} | |
+ - (\boldsymbol{n} | |
+ (\boldsymbol{n} \cdot \boldsymbol{\delta}_{t,\text{uncorrected}}^{ij}) | |
+ | |
+With all the geometrical and kinetic components determined, the resulting forc… | |
+of the particle interaction can be determined using a contact model. ``sphere`` | |
+features only one contact model in the normal direction to the contact; the | |
+linear-elastic-viscous (*Hookean* with viscous damping, or *Kelvin-Voigt*) | |
+contact model. The resulting force in the normal direction of the contact | |
+interface on particle :math:`i` is: | |
+ | |
+.. math:: | |
+ \boldsymbol{f}_n^{ij} = \left( | |
+ -k_n \delta_n^{ij} -\gamma_n \dot{\delta_n}^{ij} | |
+ \right) \boldsymbol{n}^{ij} | |
+ | |
+The parameter :math:`k_n` is the defined `spring coefficient | |
+<https://en.wikipedia.org/wiki/Hooke's_law>`_ in the normal direction of the | |
+contact interface, and :math:`\gamma_n` is the defined contact interface | |
+viscosity, also in the normal direction. The loss of energy in this interaction | |
+due to the viscous component is for particle :math:`i` calculated as: | |
+ | |
+.. math:: | |
+ \dot{e}^i_v = \gamma_n (\dot{\delta}^{ij}_n)^2 | |
+ | |
+The tangential force is determined by either a viscous-frictional contact mode… | |
+or a elastic-viscous-frictional contact model. The former contact model is very | |
+computationally efficient, but somewhat inaccurate relative to the mechanics of | |
+real materials. The latter contact model is therefore the default, even though | |
+it results in longer computational times. The tangential force in the | |
+visco-frictional contact model: | |
+ | |
+.. math:: | |
+ \boldsymbol{f}_t^{ij} = -\gamma_t \dot{\boldsymbol{\delta}_t}^{ij} | |
+ | |
+:math:`\gamma_n` is the defined contact interface viscosity in the tangential | |
+direction. The tangential displacement along the contact interface | |
+(:math:`\boldsymbol{\delta}_t`) is not calculated and stored for this contact | |
+model. The tangential force in the more realistic elastic-viscous-frictional | |
+contact model: | |
+ | |
+.. math:: | |
+ \boldsymbol{f}_t^{ij} = | |
+ -k_t \boldsymbol{\delta}_t^{ij} -\gamma_t \dot{\boldsymbol{\delta}_t}^{ij} | |
+ | |
+The parameter :math:`k_n` is the defined spring coefficient in the tangential | |
+direction of the contact interface. Note that the tangential force is only | |
+found if the tangential displacement (:math:`\delta_t`) or the tangential | |
+velocity (:math:`\dot{\delta}_t`) is non-zero, in order to avoid division by | |
+zero. Otherwise it is defined as being :math:`[0,0,0]`. | |
+ | |
+For both types of contact model, the tangential force is limited by the Coulomb | |
+criterion of static and dynamic friction: | |
+ | |
+.. math:: | |
+ ||\boldsymbol{f}^{ij}_t|| \leq | |
+ \begin{cases} | |
+ \mu_s ||\boldsymbol{f}^{ij}_n|| & | |
+ \text{if} \quad ||\boldsymbol{f}_t^{ij}|| = 0 \\ | |
+ \mu_d ||\boldsymbol{f}^{ij}_n|| & | |
+ \text{if} \quad ||\boldsymbol{f}_t^{ij}|| > 0 | |
+ \end{cases} | |
+ | |
+If the elastic-viscous-frictional contact model is used and the Coulomb limit … | |
+reached, the tangential displacement along the contact interface is limited to | |
+this value: | |
+ | |
+.. math:: | |
+ \boldsymbol{\delta}_t^{ij} = | |
+ \frac{1}{k_t} \left( | |
+ \mu_d ||\boldsymbol{f}_n^{ij}|| | |
+ \frac{\boldsymbol{f}^{ij}_t}{||\boldsymbol{f}^{ij}_t||} | |
+ + \gamma_t \dot{\boldsymbol{\delta}}_t^{ij} \right) | |
+ | |
+If the tangential force reaches the Coulomb limit, the energy lost due to | |
+frictional dissipation is calculated as: | |
+ | |
+.. math:: | |
+ \dot{e}^i_s = \frac{||\boldsymbol{f}^{ij}_t | |
+ \dot{\boldsymbol{\delta}}_t^{ij} \Delta t||}{\Delta t} | |
+ | |
+The loss of energy by viscous dissipation in the tangential direction is not | |
+found. | |
+ | |
+ | |
+Temporal integration | |
+-------------------- | |
+In the DEM, the time is discretized into small steps (:math:`\Delta t`). For e… | |
+step, the entire network of contacts is resolved, and the resulting forces and | |
+torques for each particle are found. With these values at hand, the new | |
+linear and rotational accelerations can be found using | |
+`Newton's second law <https://en.wikipedia.org/wiki/Newton%27s_laws_of_motion>… | |
+of the motion of solid bodies. If a particle with mass :math:`m` at a point in… | |
+experiences a sum of forces denoted :math:`\boldsymbol{F}`, the resultant acce… | |
+(:math:`\boldsymbol{a}`) can be found by rearranging Newton's second law: | |
+ | |
+.. math:: | |
+ \boldsymbol{F} = m \boldsymbol{a} \Rightarrow \boldsymbol{a} = \frac{\bolds… | |
+ | |
+The new velocity and position is found by integrating the above equation | |
+with regards to time. The simplest integration scheme in this regard is the | |
+`Euler method <https://en.wikipedia.org/wiki/Euler_method>`_: | |
+ | |
+.. math:: | |
+ \boldsymbol{v} = \boldsymbol{v}_{old} + \boldsymbol{a} \Delta t | |
+ | |
+.. math:: | |
+ \boldsymbol{p} = \boldsymbol{p}_{old} + \boldsymbol{v} \Delta t | |
diff --git a/doc/sphinx/index.rst b/doc/sphinx/index.rst | |
t@@ -3,36 +3,36 @@ | |
You can adapt this file completely to your liking, but it should at least | |
contain the root `toctree` directive. | |
-Welcome to sphere's documentation! | |
-================================== | |
-This is the official documentation for the *sphere* discrete element modelling | |
-software. It presents the theory behind the discrete element method (DEM), the | |
-structure of the software source code, and the Python API for handling | |
-simulation setup and data analysis. | |
- | |
-*sphere* is developed by Anders Damsgaard Christensen under supervision of Dav… | |
-Lunbek Egholm and Jan A. Piotrowski, all of the department of Geoscience, Aarh… | |
-University, Denmark. This document is a work in progress, and is still in an | |
-early state. | |
- | |
-Contact: Anders Damsgaard Christensen, `<http://cs.au.dk/~adc>`_, | |
+The sphere documentation | |
+======================== | |
+This is the official documentation for the ``sphere`` discrete element modelli… | |
+software. This document aims at guiding the installation process, documenting | |
+the usage, and explaining the relevant theory. | |
+ | |
+``sphere`` is developed by Anders Damsgaard as part as his Ph.D. project, under | |
+supervision of David Lundbek Egholm and Jan A. Piotrowski, all of the Departme… | |
+of Geoscience, Aarhus University, Denmark. The author welcomes interested third | |
+party developers. This document is a work in progress. | |
+ | |
+Contact: Anders Damsgaard, `<http://cs.au.dk/~adc>`_, | |
`<[email protected]>`_ | |
-Contents: | |
- | |
+Contents | |
+-------- | |
.. toctree:: | |
:maxdepth: 2 | |
introduction | |
dem | |
+ cfd | |
python_api | |
- sphere_internals | |
+ .. sphere_internals | |
Indices and tables | |
-================== | |
+------------------ | |
.. * :ref:`modindex` | |
diff --git a/doc/sphinx/introduction.rst b/doc/sphinx/introduction.rst | |
t@@ -1,49 +1,75 @@ | |
Introduction | |
============ | |
-The *sphere*-software is used for three-dimensional discrete element method | |
+ | |
+The ``sphere``-software is used for three-dimensional discrete element method | |
(DEM) particle simulations. The source code is written in C++, CUDA C and | |
Python, and is compiled by the user. The main computations are performed on the | |
graphics processing unit (GPU) using NVIDIA's general purpose parallel computi… | |
architecture, CUDA. Simulation setup and data analysis is performed with the | |
included Python API. | |
-The ultimate aim of the *sphere* software is to simulate soft-bedded subglacial | |
+The ultimate aim of the ``sphere`` software is to simulate soft-bedded subglac… | |
conditions, while retaining the flexibility to perform simulations of granular | |
material in other environments. | |
The purpose of this documentation is to provide the user with a walk-through of | |
the installation, work-flow, data-analysis and visualization methods of | |
-*sphere*. In addition, the *sphere* internals are exposed to provide a way of | |
+``sphere``. In addition, the ``sphere`` internals are exposed to provide a way… | |
understanding of the discrete element method numerical routines taking place. | |
-.. note:: Command examples in this document starting with the symbol ``$`` are… | |
+.. note:: Command examples in this document starting with the symbol ``$`` are | |
+ meant to be executed in the shell of the operational system, and ``>>>`` | |
+ means execution in Python. `IPython <http://ipython.org>`_ is an excellent, | |
+ interactive Python shell. | |
All numerical values in this document, the source code, and the configuration | |
files are typeset with strict respect to the SI unit system. | |
+ | |
Requirements | |
------------ | |
+ | |
The build requirements are: | |
+ | |
* A Nvidia CUDA-supported version of Linux or Mac OS X (see the `CUDA toolkit | |
release notes <http://docs.nvidia.com/cuda/cuda-toolkit-release-notes/inde… | |
* `GNU Make <https://www.gnu.org/software/make/>`_ | |
- * `CMake <http://www.cmake.org>`_ | |
+ * `CMake <http://www.cmake.org>`_, version 2.8 or newer | |
* The `GNU Compiler Collection <http://gcc.gnu.org/>`_ (GCC) | |
- * The `Nvidia CUDA toolkit and SDK <https://developer.nvidia.com/cuda-downlo… | |
+ * The `Nvidia CUDA toolkit <https://developer.nvidia.com/cuda-downloads>`_, | |
+ version 5.0 or newer | |
+ | |
+In Debian GNU/Linux, these dependencies can be installed by running:: | |
+ | |
+ $ sudo apt-get install build-essential cmake nvidia-cuda-toolkit | |
+ | |
+Unfortunately, the Nvidia Toolkit is shipped under a non-free license. In order | |
+to install it in Debian GNU/Linux, add ``non-free`` archives to your | |
+``/etc/apt/sources.list``. | |
The runtime requirements are: | |
+ | |
* A `CUDA-enabled GPU <http://www.nvidia.com/object/cuda_gpus.html>`_ with | |
compute capability 1.1 or greater. | |
* A Nvidia CUDA-enabled GPU and device driver | |
Optional tools, required for simulation setup and data processing: | |
- * `Python 2.7 <http://www.python.org/getit/releases/2.7/>`_ | |
+ | |
+ * `Python <http://www.python.org/>`_ | |
* `Numpy <http://numpy.scipy.org>`_ | |
* `Matplotlib <http://matplotlib.org>`_ | |
+ * `Python bindings for VTK <http://www.vtk.org>`_ | |
* `Imagemagick <http://www.imagemagick.org/script/index.php>`_ | |
- * `ffmpeg <http://ffmpeg.org/>`_ | |
+ * `ffmpeg <http://ffmpeg.org/>`_. Soon to be replaced by avconv! | |
+ | |
+In Debian GNU/Linux, these dependencies can be installed by running:: | |
+ | |
+ $ sudo apt-get install python python-numpy python-matplotlib python-vtk \ | |
+ imagemagick libav-tools | |
+ | |
+``sphere`` is distributed with a HTML and PDF build of the documentation. The | |
+following tools are required for building the documentation: | |
-Optional tools, required for building the documentation: | |
* `Sphinx <http://sphinx-doc.org>`_ | |
* `sphinxcontrib-programoutput <http://packages.python.org/sphinxcontrib-p… | |
t@@ -51,23 +77,75 @@ Optional tools, required for building the documentation: | |
* `Doxygen <http://www.stack.nl/~dimitri/doxygen/>`_ | |
* `Breathe <http://michaeljones.github.com/breathe/>`_ | |
* `dvipng <http://www.nongnu.org/dvipng/>`_ | |
+ * `TeX Live <http://www.tug.org/texlive/>`_, including ``pdflatex`` | |
+ | |
+In Debian GNU/Linux, these dependencies can be installed by running:: | |
+ | |
+ $ sudo apt-get install python-sphinx python-pip doxygen dvipng \ | |
+ python-sphinxcontrib-programoutput texlive-full | |
+ $ sudo pip install breathe | |
`Git <http://git-scm.com>`_ is used as the distributed version control system | |
platform, and the source code is maintained at `Github | |
-<https://github.com/anders-dc/sphere/>`_. *sphere* is licensed under the `GNU | |
+<https://github.com/anders-dc/sphere/>`_. ``sphere`` is licensed under the `GNU | |
Public License, v.3 <https://www.gnu.org/licenses/gpl.html>`_. | |
+.. note:: All Debian GNU/Linux runtime, optional, and documentation dependenci… | |
+ mentioned above can be installed by executing the following command from the | |
+ ``doc/`` folder:: | |
+ | |
+ $ make install-debian-pkgs | |
+ | |
+ | |
+Obtaining sphere | |
+---------------- | |
+ | |
+The best way to keep up to date with subsequent updates, bugfixes and | |
+development, is to use the Git version control system. To obtain a local | |
+copy, execute:: | |
+ | |
+ $ git clone [email protected]:anders-dc/sphere.git | |
-Building *sphere* | |
------------------ | |
-All instructions required for building *sphere* are provided in a number of | |
-``Makefile``'s. To generate the main *sphere* command-line executable, go to t… | |
-root directory, and invoke CMake and GNU Make:: | |
+ | |
+Building ``sphere`` | |
+------------------- | |
+ | |
+``sphere`` is built using ``cmake``, the platform-specific C/C++ compilers, | |
+and ``nvcc`` from the Nvidia CUDA toolkit. | |
+ | |
+If you plan to run ``sphere`` on a Kepler GPU, execute the following commands | |
+from the root directory:: | |
+ | |
+ $ cmake . && make | |
+ | |
+If you instead plan to execute it on a Fermi GPU, change ``set(GPU_GENERATION | |
+1)`` to ``set(GPU_GENERATION 0`` in ``CMakeLists.txt``. | |
+ | |
+In some cases the CMake FindCUDA module will have troubles locating the | |
+CUDA samples directory, and will complain about ``helper_math.h`` not being | |
+found. | |
+ | |
+In that case, modify the ``CUDA_SDK_ROOT_DIR`` variable in | |
+``src/CMakeLists.txt`` to the path where you installed the CUDA samples, and r… | |
+``cmake . && make`` again. Alternatively, copy ``helper_math.h`` from the CUDA | |
+sample subdirectory ``common/inc/helper_math.h`` into the sphere ``src/`` | |
+directory, and run ``cmake`` and ``make`` again. Due to license restrictions, | |
+sphere cannot be distributed with this file. | |
+ | |
+After a successfull installation, the ``sphere`` executable will be located | |
+in the root folder. To make sure that all components are working correctly, | |
+execute:: | |
+ | |
+ $ make test | |
+ | |
+All instructions required for building ``sphere`` are provided in a number of | |
+``Makefile``'s. To generate the main ``sphere`` command-line executable, go to | |
+the root directory, and invoke CMake and GNU Make:: | |
$ cmake . && make | |
If successfull, the Makefiles will create the required data folders, object | |
-files, as well as the *sphere* executable in the root folder. Issue the | |
+files, as well as the ``sphere`` executable in the root folder. Issue the | |
following commands to check the executable:: | |
$ ./sphere --version | |
t@@ -82,8 +160,10 @@ The build can be verified by running a number of automated… | |
The documentation can be read in the `reStructuredText | |
<http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html>`_-format … | |
-the ``doc/sphinx/`` folder, or build into e.g. HTML or PDF format with the | |
-following commands:: | |
+the ``doc/sphinx/`` folder, or in the HTML or PDF formats in the folders | |
+``doc/html`` and ``doc/pdf``. | |
+ | |
+Optionally, the documentation can be built using the following commands:: | |
$ cd doc/sphinx | |
$ make html | |
t@@ -94,15 +174,26 @@ To see all available output formats, execute:: | |
$ make help | |
+Updating sphere | |
+--------------- | |
+ | |
+To update your local version, type the following commands in the ``sphere`` ro… | |
+directory:: | |
+ | |
+ $ git pull && cmake . && make | |
+ | |
+ | |
Work flow | |
--------- | |
-After compiling the *sphere* binary, the procedure of a creating and handling a | |
-simulation is typically arranged in the following order: | |
+ | |
+After compiling the ``sphere`` binary, the procedure of a creating and handling | |
+a simulation is typically arranged in the following order: | |
+ | |
* Setup of particle assemblage, physical properties and conditions using the | |
Python API. | |
- * Execution of *sphere* software, which simulates the particle behavior as a | |
+ * Execution of ``sphere`` software, which simulates the particle behavior as… | |
function of time, as a result of the conditions initially specified in the | |
input file. | |
- * Inspection, analysis, interpretation and visualization of *sphere* output … | |
- Python, and/or scene rendering using the built-in ray tracer. | |
+ * Inspection, analysis, interpretation and visualization of ``sphere`` output | |
+ in Python, and/or scene rendering using the built-in ray tracer. | |
diff --git a/doc/sphinx/python_api.rst b/doc/sphinx/python_api.rst | |
t@@ -1,5 +1,27 @@ | |
Python API | |
========== | |
+The Python module ``sphere`` is intended as the main interface to the ``sphere… | |
+application. It is recommended to use this module for simulation setup, | |
+simulation execution, and analysis of the simulation output data. | |
+ | |
+In order to use the API, the file ``sphere.py`` must be placed in the same | |
+directory as the Python files. | |
+ | |
+Sample usage | |
+------------ | |
+Below is a simple, annotated example of how to setup, execute, and post-process | |
+a ``sphere`` simulation. The example is also found in the ``python/`` folder … | |
+``collision.py``. | |
+ | |
+.. literalinclude:: ../../python/collision.py | |
+ :language: python | |
+ :linenos: | |
+ | |
+The full documentation of the ``sphere`` Python API can be found below. | |
+ | |
+ | |
+The ``sphere`` module | |
+--------------------- | |
.. automodule:: sphere | |
:members: | |
diff --git a/doc/sphinx/sphere_internals.rst b/doc/sphinx/sphere_internals.rst | |
t@@ -207,8 +207,6 @@ An important note is that the \texttt{C} examples of the N… | |
*sphere* is supplied with several Makefiles, which automate the compilation pr… | |
- | |
- | |
C++ reference | |
------------- | |
.. doxygenclass:: DEM | |
diff --git a/python/collapse.py b/python/collapse.py | |
t@@ -1,7 +1,7 @@ | |
#!/usr/bin/env python | |
# Import sphere functionality | |
-from sphere import * | |
+import sphere | |
### EXPERIMENT SETUP ### | |
initialization = True | |
t@@ -18,7 +18,7 @@ sim_id = "collapse" | |
### INITIALIZATION ### | |
# New class | |
-init = Spherebin(np = np, nd = 3, nw = 0, sid = sim_id + "-init") | |
+init = sphere.sim(np = np, nd = 3, nw = 0, sid = sim_id + "-init") | |
# Save radii | |
init.generateRadii(radius_mean = 0.1) | |
t@@ -35,8 +35,6 @@ init.initRandomGridPos(gridnum = numpy.array([hcells, hcells… | |
init.initTemporal(total = 10.0) | |
if (initialization == True): | |
- # Write input file for sphere | |
- init.writebin() | |
# Run sphere | |
init.run(dry = True) | |
t@@ -49,7 +47,7 @@ if (initialization == True): | |
### COLLAPSE ### | |
# New class | |
-coll = Spherebin(np = init.np, nw = init.nw, sid = sim_id) | |
+coll = sphere.sim(np = init.np, nw = init.nw, sid = sim_id) | |
# Read last output file of initialization step | |
lastf = status(sim_id + "-init") | |
t@@ -68,8 +66,6 @@ coll.adjustUpperWall() | |
coll.initTemporal(total = 5.0, file_dt = 0.10) | |
if (collapse == True): | |
- # Write input file for sphere | |
- coll.writebin() | |
# Run sphere | |
coll.run(dry = True) | |
diff --git a/python/collision.py b/python/collision.py | |
t@@ -1,7 +1,7 @@ | |
#!/usr/bin/env python | |
""" | |
Example of two particles colliding. | |
-Place script in sphere/python folder, and invoke with `python collision.py` | |
+Place script in sphere/python/ folder, and invoke with `python collision.py` | |
""" | |
# Import the sphere module for setting up, running, and analyzing the | |
t@@ -14,7 +14,7 @@ import numpy | |
### SIMULATION SETUP | |
# Create a sphere object with two preallocated particles and a simulation ID | |
-SB = sphere.Spherebin(np = 2, sid = 'collision') | |
+SB = sphere.sim(np = 2, sid = 'collision') | |
SB.radius[:] = 0.3 # set radii to 0.3 m | |
t@@ -26,20 +26,14 @@ SB.x[1, :] = numpy.array([11.0, 5.0, 5.0]) # particle 2 … | |
# a positive x velocity for particle 1. | |
SB.vel[0, 0] = 1.0 | |
-# let's disable gravity in this simulation | |
-GRAVITY = numpy.array([0.0, 0.0, 0.0]) | |
- | |
# Set the world limits and the particle sorting grid. The particles need to st… | |
# within the world limits for the entire simulation, otherwise it will stop! | |
-SB.initGridAndWorldsize(g = GRAVITY, margin = 5.0) | |
+SB.initGridAndWorldsize(margin = 5.0) | |
# Define the temporal parameters, e.g. the total time (total) and the file | |
# output interval (file_dt), both in seconds | |
SB.initTemporal(total = 2.0, file_dt = 0.1) | |
-# Save the simulation as a input file for sphere | |
-SB.writebin() | |
- | |
# Using a 'dry' run, the sphere main program will display important parameters. | |
# sphere will end after displaying these values. | |
SB.run(dry = True) | |
t@@ -58,3 +52,8 @@ sphere.visualize(SB.sid, method = 'energy') | |
# Render the particles using the built-in raytracer | |
SB.render() | |
+ | |
+# Alternative visualization using ParaView. See the documentation of | |
+# ``sim.writeVTKall()`` for more information about displaying the | |
+# particles in ParaView. | |
+SB.writeVTKall() | |
diff --git a/python/darcy.py b/python/darcy.py | |
t@@ -1,87 +0,0 @@ | |
-#!/usr/bin/env python | |
- | |
-# Import sphere functionality | |
-from sphere import * | |
-import sys | |
- | |
-### EXPERIMENT SETUP ### | |
-initialization = True | |
-consolidation = True | |
-#shearing = True | |
-rendering = False | |
-plots = True | |
- | |
- | |
- | |
-# Number of particles | |
-#np = 2e2 | |
-np = 1e4 | |
- | |
-# Common simulation id | |
-sim_id = "darcy" | |
- | |
-# Deviatoric stress [Pa] | |
-#devs = 10e3 | |
-devslist = [10.0e3] | |
- | |
-### INITIALIZATION ### | |
- | |
-# New class | |
-init = Spherebin(np = np, nd = 3, nw = 0, sid = sim_id + "-init") | |
- | |
-# Save radii | |
-init.generateRadii(radius_mean = 0.05) | |
- | |
-# Use default params | |
-init.defaultParams(mu_s = 0.4, mu_d = 0.4, nu = 8.9e-4) | |
- | |
-# Initialize positions in random grid (also sets world size) | |
-#init.initRandomGridPos(gridnum = numpy.array([9, 9, 1000]), periodic = 1, con… | |
-#init.initRandomGridPos(gridnum = numpy.array([10, 10, 1000]), periodic = 1, c… | |
-#init.initRandomGridPos(gridnum = numpy.array([32, 32, 1000]), periodic = 1, c… | |
-init.initRandomGridPos(gridnum = numpy.array([32, 32, 1000]), periodic = 1, co… | |
- | |
-# Bond ~30% of the particles | |
-#init.random2bonds(spacing=0.1) | |
- | |
-# Set duration of simulation | |
-init.initTemporal(total = 1.0) | |
-#init.initTemporal(total = 0.01) | |
-init.time_file_dt[0] = 0.05 | |
-#init.time_file_dt[0] = init.time_dt[0]*0.99 | |
-#init.time_total[0] = init.time_dt[0]*2.0 | |
-#init.initTemporal(total = 0.5) | |
-#init.time_dt[0] = 1.0e-5; | |
- | |
-init.f_rho[2,2,4] = 1.1 | |
-#init.f_rho[6,6,10] = 1.1 | |
-#init.f_rho[:,:,-1] = 1.0001 | |
- | |
-if (initialization == True): | |
- | |
- # Write input file for sphere | |
- init.writebin() | |
- | |
- # Run sphere | |
- init.run(dry=True) | |
- init.run(darcyflow=True) | |
- | |
- | |
- if (plots == True): | |
- # Make a graph of energies | |
- visualize(init.sid, "energy", savefig=True, outformat='png') | |
- | |
- if (rendering == True): | |
- # Render images with raytracer | |
- init.render(method = "pres", max_val = 2.0*devs, verbose = False) | |
- | |
- project = init.sid | |
- lastfile = status(init.sid) | |
- sb = Spherebin() | |
- for i in range(lastfile+1): | |
- fn = "../output/{0}.output{1:0=5}.bin".format(project, i) | |
- sb.sid = project + ".output{:0=5}".format(i) | |
- sb.readbin(fn, verbose = False) | |
- for y in range(0,sb.num[1]): | |
- sb.plotFluidDensities(y = y) | |
- sb.plotFluidVelocities(y = y) | |
diff --git a/python/ns_cons_of_mass.py b/python/ns_cons_of_mass.py | |
t@@ -0,0 +1,203 @@ | |
+#!/usr/bin/env python | |
+ | |
+# Import sphere functionality | |
+import sphere | |
+from sphere import visualize, status | |
+import sys | |
+import numpy | |
+import pylab | |
+ | |
+figformat = 'pdf' | |
+ | |
+# Number of particles | |
+np = 1e4 | |
+#np = 50 | |
+ | |
+# Common simulation id | |
+sim_id = "ns" | |
+ | |
+# New class | |
+init = sphere.sim(np = np, nd = 3, nw = 0, sid = sim_id + "-init") | |
+ | |
+ | |
+# Save radii | |
+init.generateRadii(radius_mean = 0.05, histogram=False) | |
+ | |
+ | |
+# Use default params | |
+init.defaultParams(mu_s = 0.4, mu_d = 0.4, nu = 8.9e-4) | |
+ | |
+# Initialize positions in random grid (also sets world size) | |
+#init.initRandomGridPos(gridnum = numpy.array([80, 80, 1000]), periodic = 1, c… | |
+init.initRandomGridPos(gridnum = numpy.array([40, 40, 1000]), periodic = 1, co… | |
+#init.initRandomGridPos(gridnum = numpy.array([6, 6, 1000]), periodic = 1, con… | |
+ | |
+# Set duration of simulation | |
+#init.initTemporal(total = 2.5) | |
+#init.time_file_dt[0] = 0.01 | |
+ | |
+#init.initTemporal(total = 0.01) | |
+init.initTemporal(total = 0.002) | |
+init.time_file_dt[0] = 0.001 | |
+ | |
+#init.initTemporal(1) | |
+#init.time_file_dt[0] = init.time_dt[0]*0.9 | |
+#init.time_total[0] = init.time_file_dt[0]*10.5 | |
+ | |
+ | |
+ | |
+# Small pertubation | |
+#init.p_f[init.num[0]/2,init.num[1]/2,init.num[2]/2] = 2.0 | |
+#init.p_f[:,:,init.num[2]-1] = 1.0 | |
+#init.p_f[:,:,0] = 2.0 | |
+ | |
+#init.g[2] = -10.0 | |
+init.g[2] = 0.0 # uncomment to disable gravity | |
+ | |
+# Run sphere | |
+init.run(dry=True) | |
+init.run(cfd=True) | |
+ | |
+init.writeVTKall() | |
+ | |
+#if (plots == True): | |
+ # Make a graph of energies | |
+ #visualize(init.sid, "energy", savefig=True, outformat='png') | |
+ | |
+#if (rendering == True): | |
+ # Render images with raytracer | |
+ #init.render(method = "pres", max_val = 2.0*devs, verbose = False) | |
+ | |
+project = init.sid | |
+lastfile = status(init.sid) | |
+sb = sphere.sim() | |
+time = numpy.zeros(lastfile+1) | |
+sum_op_f = numpy.zeros(lastfile+1) | |
+for i in range(lastfile+1): | |
+ fn = "../output/{0}.output{1:0=5}.bin".format(project, i) | |
+ sb.sid = project + ".output{:0=5}".format(i) | |
+ sb.readbin(fn, verbose = False) | |
+ #for y in range(0,sb.num[1]): | |
+ #sb.plotFluidDensities(y = y) | |
+ #sb.plotFluidVelocities(y = y) | |
+ | |
+ time[i] = sb.time_current[0] | |
+ sum_op_f[i] = sb.p_f.sum() | |
+ | |
+#stack = numpy.vstack((time,sum_op_f)) | |
+#numpy.savetxt("sum_op_f", stack) | |
+ | |
+# Set figure parameters | |
+fig_width_pt = 246.0 # Get this from LaTeX using \showthe\columnwidth | |
+#fig_width_pt = 50.0 # Get this from LaTeX using \showthe\columnwidth | |
+inches_per_pt = 1.0/72.27 # Convert pt to inch | |
+golden_mean = (pylab.sqrt(5)-1.0)/2.0 # Aesthetic ratio | |
+fig_width = fig_width_pt*inches_per_pt # width in inches | |
+fig_height = fig_width*golden_mean # height in inches | |
+fig_size = [fig_width,fig_height] | |
+params = {'backend': 'ps', | |
+ 'axes.labelsize': 10, | |
+ 'text.fontsize': 10, | |
+ 'legend.fontsize': 10, | |
+ 'xtick.labelsize': 8, | |
+ 'ytick.labelsize': 8, | |
+ 'text.usetex': | |
+ True, | |
+ 'figure.figsize': | |
+ fig_size} | |
+pylab.rcParams.update(params) | |
+ | |
+# generate data | |
+#x = pylab.arange(-2*pylab.pi,2*pylab.pi,0.01) | |
+#y1 = pylab.sin(x) | |
+#y2 = pylab.cos(x) | |
+ | |
+ | |
+y = init.num[1]/2 | |
+ | |
+t = 0 | |
+fn = "../output/{0}.output{1:0=5}.bin".format(project, t) | |
+sb.sid = project + ".output{:0=5}".format(i) | |
+sb.readbin(fn, verbose = False) | |
+pylab.figure(1) | |
+pylab.clf() | |
+pylab.axes([0.125,0.2,0.95-0.125,0.95-0.2]) | |
+pylab.imshow(sb.p_f[:,y,:].T, origin='lower', interpolation='nearest', | |
+ cmap=pylab.cm.Blues) | |
+#imgplt.set_interpolation('nearest') | |
+#pylab.set_interpolation('bicubic') | |
+#imgplt.set_cmap('hot') | |
+pylab.xlabel('$i_x$') | |
+pylab.ylabel('$i_z$') | |
+pylab.title('$t = {}$ s'.format(t*sb.time_file_dt[0])) | |
+#cb = pylab.colorbar(orientation = 'horizontal', shrink=0.8, pad=0.23) | |
+cb = pylab.colorbar(orientation = 'horizontal', shrink=0.8, pad=0.23, ticks=[s… | |
+cb.ax.set_xticklabels([1.0, sb.p_f[:,y,:].max()]) | |
+cb.set_label('H [m]') | |
+ | |
+pylab.savefig('ns_cons_of_mass1.' + figformat) | |
+pylab.clf() | |
+ | |
+t = 1 | |
+fn = "../output/{0}.output{1:0=5}.bin".format(project, t) | |
+sb.sid = project + ".output{:0=5}".format(i) | |
+sb.readbin(fn, verbose = False) | |
+pylab.figure(1) | |
+pylab.clf() | |
+pylab.axes([0.125,0.2,0.95-0.125,0.95-0.2]) | |
+pylab.imshow(sb.p_f[:,y,:].T, origin='lower', interpolation='nearest', | |
+ cmap=pylab.cm.Blues) | |
+#pylab.set_interpolation('bicubic') | |
+pylab.xlabel('$i_x$') | |
+pylab.ylabel('$i_z$') | |
+pylab.title('$t = {}$ s'.format(t*sb.time_file_dt[0])) | |
+cb = pylab.colorbar(orientation = 'horizontal', shrink=0.8, pad=0.23, ticks=[s… | |
+#cb.ax.set_xticklabels([1.0, sb.p_f[:,y,:].max()]) | |
+cb.ax.set_xticklabels([sb.p_f[:,y,:].min(), sb.p_f[:,y,:].max()]) | |
+cb.set_label('H [m]') | |
+#pylab.tight_layout() | |
+pylab.savefig('ns_cons_of_mass2.' + figformat) | |
+pylab.clf() | |
+ | |
+t = init.status() | |
+fn = "../output/{0}.output{1:0=5}.bin".format(project, t) | |
+sb.sid = project + ".output{:0=5}".format(i) | |
+sb.readbin(fn, verbose = False) | |
+pylab.figure(1) | |
+pylab.clf() | |
+pylab.axes([0.125,0.2,0.95-0.125,0.95-0.2]) | |
+pylab.imshow(sb.p_f[:,y,:].T, origin='lower', interpolation='nearest', | |
+ cmap=pylab.cm.Blues) | |
+#pylab.set_interpolation('bicubic') | |
+pylab.xlabel('$i_x$') | |
+pylab.ylabel('$i_z$') | |
+pylab.title('$t = {}$ s'.format(t*sb.time_file_dt[0])) | |
+cb = pylab.colorbar(orientation = 'horizontal', shrink=0.8, pad=0.23, ticks=[s… | |
+#cb.ax.set_xticklabels([1.0, sb.p_f[:,y,:].max()]) | |
+cb.ax.set_xticklabels([sb.p_f[:,y,:].min(), sb.p_f[:,y,:].max()]) | |
+cb.set_label('H [m]') | |
+#pylab.tight_layout() | |
+pylab.savefig('ns_cons_of_mass3.' + figformat) | |
+pylab.clf() | |
+ | |
+#pylab.axes([0.125,0.2,0.95-0.125,0.95-0.2]) | |
+pylab.axes([0.20,0.2,0.95-0.20,0.95-0.2]) | |
+pylab.plot(time, sum_op_f, '-k') | |
+pylab.xlabel('$t$ [s]') | |
+pylab.ylabel('$\sum H_i$ [m]') | |
+pylab.xlim([0,time.max()]) | |
+pylab.ylim([0,sum_op_f.max()*1.1]) | |
+#pylab.legend() | |
+#pylab.tight_layout() | |
+pylab.grid() | |
+pylab.savefig('ns_cons_of_mass4.' + figformat) | |
+pylab.clf() | |
+ | |
+#pylab.tight_layout(h_pad=6.0) | |
+#pylab.savefig('ns_cons_of_mass.eps') | |
+ | |
+#fig = matplotlib.pyplot.figure(figsize=(10,5),dpi=300) | |
+#ax = matplotlib.pyplot.subplot2grid((1, 1), (0, 0)) | |
+#ax.plot(time, sum_op_f) | |
+#fig.savefig("ns_cons_of_mass.png") | |
+#fig.clf() | |
diff --git a/python/ns_cons_of_mass_run.sh b/python/ns_cons_of_mass_run.sh | |
t@@ -0,0 +1,4 @@ | |
+#!/bin/sh | |
+rm ../output/*.vti; rm ../output/*.vtu; \ | |
+ cd ~/code/sphere-cfd && cmake . && make -j2 &&\ | |
+ cd ~/code/sphere-cfd/python && ipython -i ns_cons_of_mass.py | |
diff --git a/python/segregation.py b/python/segregation.py | |
t@@ -1,7 +1,7 @@ | |
#!/usr/bin/env python | |
# Import sphere functionality | |
-from sphere import * | |
+import sphere | |
### EXPERIMENT SETUP ### | |
initialization = True | |
t@@ -24,13 +24,13 @@ devslist = [120e3] | |
### INITIALIZATION ### | |
# New class | |
-init = Spherebin(np = np, nd = 3, nw = 0, sid = sim_id + "-init") | |
+init = sphere.sim(np = np, nd = 3, nw = 0, sid = sim_id + "-init") | |
# Save radii | |
init.generateRadii(radius_mean = 0.08) | |
# Use default params | |
-init.defaultParams(gamma_n = 0.0, mu_s = 0.4, mu_d = 0.4) | |
+init.defaultParams(gamma_n = 100.0, mu_s = 0.4, mu_d = 0.4) | |
# Initialize positions in random grid (also sets world size) | |
hcells = np**(1.0/3.0)*0.6 | |
t@@ -47,8 +47,6 @@ init.radius[I] = init.radius[I] * 0.5 | |
init.initTemporal(total = 10.0) | |
if (initialization == True): | |
- # Write input file for sphere | |
- init.writebin() | |
# Run sphere | |
init.run(dry=True) | |
t@@ -67,10 +65,10 @@ if (initialization == True): | |
for devs in devslist: | |
# New class | |
- cons = Spherebin(np = init.np, nw = 1, sid = sim_id + "-cons-devs{}".format(… | |
+ cons = sphere.sim(np = init.np, nw = 1, sid = sim_id + "-cons-devs{}".format… | |
# Read last output file of initialization step | |
- lastf = status(sim_id + "-init") | |
+ lastf = sphere.status(sim_id + "-init") | |
cons.readbin("../output/" + sim_id + "-init.output{:0=5}.bin".format(lastf),… | |
# Setup consolidation experiment | |
t@@ -88,8 +86,6 @@ for devs in devslist: | |
if (consolidation == True): | |
- # Write input file for sphere | |
- cons.writebin() | |
# Run sphere | |
cons.run(dry=True) # show values, don't run | |
t@@ -108,10 +104,10 @@ for devs in devslist: | |
### SHEARING ### | |
# New class | |
- shear = Spherebin(np = cons.np, nw = cons.nw, sid = sim_id + "-shear-devs{}"… | |
+ shear = sphere.sim(np = cons.np, nw = cons.nw, sid = sim_id + "-shear-devs{}… | |
# Read last output file of initialization step | |
- lastf = status(sim_id + "-cons-devs{}".format(devs)) | |
+ lastf = sphere.status(sim_id + "-cons-devs{}".format(devs)) | |
shear.readbin("../output/" + sim_id + "-cons-devs{}.output{:0=5}.bin".format… | |
# Setup shear experiment | |
t@@ -178,8 +174,6 @@ for devs in devslist: | |
shear.initTemporal(total = 400.0) | |
if (shearing == True): | |
- # Write input file for sphere | |
- shear.writebin() | |
# Run sphere | |
shear.run(dry=True) | |
diff --git a/python/shear-test.py b/python/shear-test.py | |
t@@ -8,7 +8,7 @@ initialization = True | |
consolidation = True | |
shearing = True | |
rendering = True | |
-plots = True | |
+plots = True | |
# Number of particles | |
np = 1e4 | |
t@@ -29,7 +29,7 @@ init = Spherebin(np = np, nd = 3, nw = 0, sid = sim_id + "-i… | |
init.generateRadii(radius_mean = 0.02) | |
# Use default params | |
-init.defaultParams(gamma_n = 0.0, mu_s = 0.3, mu_d = 0.3) | |
+init.defaultParams(gamma_n = 100.0, mu_s = 0.6, mu_d = 0.6) | |
# Initialize positions in random grid (also sets world size) | |
hcells = np**(1.0/3.0) | |
t@@ -39,92 +39,100 @@ init.initRandomGridPos(gridnum = numpy.array([hcells, hce… | |
init.initTemporal(total = 5.0) | |
if (initialization == True): | |
- # Write input file for sphere | |
- init.writebin() | |
- # Run sphere | |
- init.run() | |
+ # Run sphere | |
+ init.run(dry = True) | |
+ init.run() | |
+ | |
+ if (plots == True): | |
+ # Make a graph of energies | |
+ visualize(init.sid, "energy", savefig=True, outformat='png') | |
- if (plots == True): | |
- # Make a graph of energies | |
- visualize(init.sid, "energy", savefig=True, outformat='png') | |
+ init.writeVTKall() | |
- if (rendering == True): | |
- # Render images with raytracer | |
- init.render(method = "angvel", max_val = 0.3, verbose = False) | |
+ if (rendering == True): | |
+ # Render images with raytracer | |
+ init.render(method = "angvel", max_val = 0.3, verbose = False) | |
-### CONSOLIDATION ### | |
+# For each normal stress, consolidate and subsequently shear the material | |
for devs in devslist: | |
- # New class | |
- cons = Spherebin(np = init.np, nw = 1, sid = sim_id + "-cons-devs{}".format(… | |
- # Read last output file of initialization step | |
- lastf = status(sim_id + "-init") | |
- cons.readbin("../output/" + sim_id + "-init.output{:0=5}.bin".format(lastf),… | |
+ ### CONSOLIDATION ### | |
- # Setup consolidation experiment | |
- cons.consolidate(deviatoric_stress = devs, periodic = init.periodic) | |
+ # New class | |
+ cons = Spherebin(np = init.np, nw = 1, sid = sim_id + "-cons-devs{}".forma… | |
+ # Read last output file of initialization step | |
+ lastf = status(sim_id + "-init") | |
+ cons.readbin("../output/" + sim_id + "-init.output{:0=5}.bin".format(lastf… | |
- # Set duration of simulation | |
- cons.initTemporal(total = 1.5) | |
- #cons.initTemporal(total = 0.0019, file_dt = 0.00009) | |
- #cons.initTemporal(total = 0.0019, file_dt = 1e-6) | |
- #cons.initTemporal(total = 0.19, file_dt = 0.019) | |
+ # Setup consolidation experiment | |
+ cons.consolidate(deviatoric_stress = devs, periodic = init.periodic) | |
- cons.w_m[0] *= 0.001 | |
+ # Set duration of simulation | |
+ cons.initTemporal(total = 1.5) | |
+ #cons.initTemporal(total = 0.0019, file_dt = 0.00009) | |
+ #cons.initTemporal(total = 0.0019, file_dt = 1e-6) | |
+ #cons.initTemporal(total = 0.19, file_dt = 0.019) | |
+ """ | |
+ cons.w_m[0] *= 0.001 | |
+ cons.mu_s[0] = 0.0 | |
+ cons.mu_d[0] = 0.0 | |
+ cons.gamma_wn[0] = 1e4 | |
+ cons.gamma_wt[0] = 1e4 | |
+ cons.contactmodel[0] = 1 | |
+ """ | |
- if (consolidation == True): | |
- # Write input file for sphere | |
- cons.writebin() | |
+ if (consolidation == True): | |
- # Run sphere | |
- cons.run(dry=True) # show values, don't run | |
- cons.run() # run | |
+ # Run sphere | |
+ cons.run(dry = True) # show values, don't run | |
+ cons.run() # run | |
- if (plots == True): | |
- # Make a graph of energies | |
- visualize(cons.sid, "energy", savefig=True, outformat='png') | |
- visualize(cons.sid, "walls", savefig=True, outformat='png') | |
+ if (plots == True): | |
+ # Make a graph of energies | |
+ visualize(cons.sid, "energy", savefig=True, outformat='png') | |
+ visualize(cons.sid, "walls", savefig=True, outformat='png') | |
- if (rendering == True): | |
- # Render images with raytracer | |
- cons.render(method = "pres", max_val = 2.0*devs, verbose = False) | |
+ cons.writeVTKall() | |
+ if (rendering == True): | |
+ # Render images with raytracer | |
+ cons.render(method = "pres", max_val = 2.0*devs, verbose = False) | |
-### SHEARING ### | |
- # New class | |
- shear = Spherebin(np = cons.np, nw = cons.nw, sid = sim_id + "-shear-devs{}"… | |
+ ### SHEARING ### | |
- # Read last output file of initialization step | |
- lastf = status(sim_id + "-cons-devs{}".format(devs)) | |
- shear.readbin("../output/" + sim_id + "-cons-devs{}.output{:0=5}.bin".format… | |
+ # New class | |
+ shear = Spherebin(np = cons.np, nw = cons.nw, sid = sim_id + "-shear-devs{… | |
- # Setup shear experiment | |
- shear.shear(shear_strain_rate = 0.05, periodic = init.periodic) | |
+ # Read last output file of initialization step | |
+ lastf = status(sim_id + "-cons-devs{}".format(devs)) | |
+ shear.readbin("../output/" + sim_id + "-cons-devs{}.output{:0=5}.bin".form… | |
- # Set duration of simulation | |
- shear.initTemporal(total = 20.0) | |
+ # Setup shear experiment | |
+ shear.shear(shear_strain_rate = 0.05, periodic = init.periodic) | |
- if (shearing == True): | |
- # Write input file for sphere | |
- shear.writebin() | |
+ # Set duration of simulation | |
+ shear.initTemporal(total = 20.0) | |
- # Run sphere | |
- shear.run(dry=True) | |
- shear.run() | |
+ if (shearing == True): | |
- if (plots == True): | |
- # Make a graph of energies | |
- visualize(shear.sid, "energy", savefig=True, outformat='png') | |
- visualize(shear.sid, "shear", savefig=True, outformat='png') | |
+ # Run sphere | |
+ shear.run(dry = True) | |
+ shear.run() | |
- if (rendering == True): | |
- # Render images with raytracer | |
- shear.render(method = "pres", max_val = 2.0*devs, verbose = False) | |
+ if (plots == True): | |
+ # Make a graph of energies | |
+ visualize(shear.sid, "energy", savefig=True, outformat='png') | |
+ visualize(shear.sid, "shear", savefig=True, outformat='png') | |
+ | |
+ shear.writeVTKall() | |
+ if (rendering == True): | |
+ # Render images with raytracer | |
+ shear.render(method = "pres", max_val = 2.0*devs, verbose = False) | |
diff --git a/python/sphere.py b/python/sphere.py | |
t@@ -1,8 +1,8 @@ | |
#!/usr/bin/env python2.7 | |
import math | |
import numpy | |
-import matplotlib as mpl | |
-mpl.use('Agg') | |
+import matplotlib | |
+matplotlib.use('Agg') | |
import matplotlib.pyplot as plt | |
from matplotlib.font_manager import FontProperties | |
import subprocess | |
t@@ -10,92 +10,191 @@ import vtk | |
numpy.seterr(all='warn', over='raise') | |
+# Sphere version number. This field should correspond to the value in | |
+# `../src/constants.h`. | |
+VERSION=1.0 | |
-class Spherebin: | |
- """ | |
- Class containing all data SPHERE data. | |
+class sim: | |
+ ''' | |
+ Class containing all ``sphere`` data. | |
Contains functions for reading and writing binaries, as well as simulation | |
- setup and data analysis. | |
- """ | |
- | |
- def __init__(self, np = 1, nd = 3, nw = 1, sid = 'unnamed'): | |
- """ | |
- Constructor - initializes arrays | |
- | |
- :param np: the number of particles to allocate memory for | |
- :param nd: the number of spatial dimensions | |
- :param nw: the number of dynamic walls | |
- :param sid: the simulation id | |
- :type np: int | |
- :type nd: int | |
- :type nw: int | |
- :type sid: string | |
- | |
- """ | |
- | |
- self.nd = numpy.ones(1, dtype=numpy.int32) * nd | |
- self.np = numpy.ones(1, dtype=numpy.uint32) * np | |
+ setup and data analysis. Most arrays are initialized to default values. | |
+ | |
+ :param np: The number of particles to allocate memory for (default = 1) | |
+ :type np: int | |
+ :param nd: The number of spatial dimensions (default = 3). Note that 2D and | |
+ 1D simulations currently are not possible. | |
+ :type nd: int | |
+ :param nw: The number of dynamic walls (default = 1) | |
+ :type nw: int | |
+ :param sid: The simulation id (default = 'unnamed'). The simulation files | |
+ will be written with this base name. | |
+ :type sid: str | |
+ :param fluid: Setup fluid simulation (default = False) | |
+ :type fluid: bool | |
+ ''' | |
+ | |
+ def __init__(self, sid = 'unnamed', np = 0, nd = 3, nw = 0, fluid = False): | |
+ | |
+ # Sphere version number | |
+ self.version = numpy.ones(1, dtype=numpy.float64)*VERSION | |
+ | |
+ # The number of spatial dimensions. Values other that 3 do not work | |
+ self.nd = numpy.ones(1, dtype=numpy.int32)*nd | |
+ | |
+ # The number of particles | |
+ self.np = numpy.ones(1, dtype=numpy.uint32)*np | |
+ | |
+ # The simulation id (text string) | |
self.sid = sid | |
- # Time parameters | |
+ ## Time parameters | |
+ # Computational time step length [s] | |
self.time_dt = numpy.zeros(1, dtype=numpy.float64) | |
+ | |
+ # Current time [s] | |
self.time_current = numpy.zeros(1, dtype=numpy.float64) | |
+ | |
+ # Total time [s] | |
self.time_total = numpy.zeros(1, dtype=numpy.float64) | |
+ | |
+ # File output interval [s] | |
self.time_file_dt = numpy.zeros(1, dtype=numpy.float64) | |
+ | |
+ # The number of files written | |
self.time_step_count = numpy.zeros(1, dtype=numpy.uint32) | |
- # World dimensions and grid data | |
+ ## World dimensions and grid data | |
+ # The Euclidean coordinate to the origo of the sorting grid | |
self.origo = numpy.zeros(self.nd, dtype=numpy.float64) | |
+ | |
+ # The sorting grid size (x,y,z) | |
self.L = numpy.zeros(self.nd, dtype=numpy.float64) | |
+ | |
+ # The number of sorting cells in each dimension | |
self.num = numpy.zeros(self.nd, dtype=numpy.uint32) | |
+ | |
+ # Whether to treat the lateral boundaries as periodic (1) or not (0) | |
self.periodic = numpy.zeros(1, dtype=numpy.uint32) | |
- # Particle data | |
+ ## Particle data | |
+ # Particle position vectors [m] | |
self.x = numpy.zeros((self.np, self.nd), dtype=numpy.float64) | |
+ | |
+ # Particle radii [m] | |
self.radius = numpy.ones(self.np, dtype=numpy.float64) | |
+ | |
+ # The sums of x and y movement [m] | |
self.xysum = numpy.zeros((self.np, 2), dtype=numpy.float64) | |
+ | |
+ # The linear velocities [m/s] | |
self.vel = numpy.zeros((self.np, self.nd), dtype=numpy.float64) | |
+ | |
+ # Fix the particle horizontal velocities? 0: No, 1: Yes | |
self.fixvel = numpy.zeros(self.np, dtype=numpy.float64) | |
+ | |
+ # The linear force vectors [N] | |
self.force = numpy.zeros((self.np, self.nd), dtype=numpy.float64) | |
+ | |
+ # The angular position vectors [rad] | |
self.angpos = numpy.zeros((self.np, self.nd), dtype=numpy.float64) | |
+ | |
+ # The angular velocity vectors [rad/s] | |
self.angvel = numpy.zeros((self.np, self.nd), dtype=numpy.float64) | |
+ | |
+ # The torque vectors [N*m] | |
self.torque = numpy.zeros((self.np, self.nd), dtype=numpy.float64) | |
+ # The shear friction energy dissipation rates [W] | |
self.es_dot = numpy.zeros(self.np, dtype=numpy.float64) | |
+ | |
+ # The total shear energy dissipations [J] | |
self.es = numpy.zeros(self.np, dtype=numpy.float64) | |
+ | |
+ # The viscous energy dissipation rates [W] | |
self.ev_dot = numpy.zeros(self.np, dtype=numpy.float64) | |
+ | |
+ # The total viscois energy dissipation [J] | |
self.ev = numpy.zeros(self.np, dtype=numpy.float64) | |
+ | |
+ # The total particle pressures [Pa] | |
self.p = numpy.zeros(self.np, dtype=numpy.float64) | |
+ # The gravitational acceleration vector [N*m/s] | |
self.g = numpy.array([0.0, 0.0, 0.0], dtype=numpy.float64) | |
+ | |
+ # The Hookean coefficient for elastic stiffness normal to the contacts | |
+ # [N/m] | |
self.k_n = numpy.ones(1, dtype=numpy.float64) * 1.16e9 | |
+ | |
+ # The Hookean coefficient for elastic stiffness tangential to the | |
+ # contacts [N/m] | |
self.k_t = numpy.ones(1, dtype=numpy.float64) * 1.16e9 | |
+ | |
+ # The Hookean coefficient for elastic stiffness opposite of contact | |
+ # rotations. UNUSED | |
self.k_r = numpy.zeros(1, dtype=numpy.float64) | |
+ | |
+ # The viscosity normal to the contact [N/(m/s)] | |
self.gamma_n = numpy.zeros(1, dtype=numpy.float64) | |
+ | |
+ # The viscosity tangential to the contact [N/(m/s)] | |
self.gamma_t = numpy.zeros(1, dtype=numpy.float64) | |
+ | |
+ # The viscosity to contact rotation [N/(m/s)] | |
self.gamma_r = numpy.zeros(1, dtype=numpy.float64) | |
- self.mu_s = numpy.ones(1, dtype=numpy.float64) | |
- self.mu_d = numpy.ones(1, dtype=numpy.float64) | |
+ | |
+ # The coefficient of static friction on the contact [-] | |
+ self.mu_s = numpy.ones(1, dtype=numpy.float64) * 0.5 | |
+ | |
+ # The coefficient of dynamic friction on the contact [-] | |
+ self.mu_d = numpy.ones(1, dtype=numpy.float64) * 0.5 | |
+ | |
+ # The coefficient of rotational friction on the contact [-] | |
self.mu_r = numpy.zeros(1, dtype=numpy.float64) | |
+ | |
+ # The viscosity normal to the walls [N/(m/s)] | |
self.gamma_wn = numpy.ones(1, dtype=numpy.float64) * 1.0e3 | |
- self.gamma_wt = numpy.ones(1, dtype=numpy.float64) * 1.0e3 | |
- self.mu_ws = numpy.ones(1, dtype=numpy.float64) | |
- self.mu_wd = numpy.ones(1, dtype=numpy.float64) | |
+ | |
+ # The viscosity tangential to the walls [N/(m/s)] | |
+ self.gamma_wt = numpy.zeros(1, dtype=numpy.float64) | |
+ | |
+ # The coeffient of static friction of the walls [-] | |
+ self.mu_ws = numpy.ones(1, dtype=numpy.float64) * 0.5 | |
+ | |
+ # The coeffient of dynamic friction of the walls [-] | |
+ self.mu_wd = numpy.ones(1, dtype=numpy.float64) * 0.5 | |
+ | |
+ # The particle density [kg/(m^3)] | |
self.rho = numpy.ones(1, dtype=numpy.float64) * 2600.0 | |
- self.contactmodel = numpy.ones(1, dtype=numpy.uint32) * 2 # contact… | |
+ | |
+ # The contact model to use | |
+ # 1: Normal: elasto-viscous, tangential: visco-frictional | |
+ # 2: Normal: elasto-viscous, tangential: elasto-visco-frictional | |
+ self.contactmodel = numpy.ones(1, dtype=numpy.uint32) * 2 # lin-visc-el | |
+ | |
+ # Capillary bond prefactor | |
self.kappa = numpy.zeros(1, dtype=numpy.float64) | |
+ | |
+ # Capillary bond debonding distance [m] | |
self.db = numpy.zeros(1, dtype=numpy.float64) | |
+ | |
+ # Capillary bond liquid volume [m^3] | |
self.V_b = numpy.zeros(1, dtype=numpy.float64) | |
- # Wall data | |
- # nw: Number of dynamic walls | |
+ ## Wall data | |
+ # Number of dynamic walls | |
# nw = 1: Uniaxial | |
# nw = 2: Biaxial | |
# nw = 5: Triaxial | |
self.nw = numpy.ones(1, dtype=numpy.uint32) * nw | |
+ | |
+ # Wall modes | |
+ # 0: Fixed, 1: Normal stress condition, 2: Normal velocity condition | |
self.wmode = numpy.zeros(self.nw, dtype=numpy.int32) | |
+ # Wall normals | |
self.w_n = numpy.zeros((self.nw, self.nd), dtype=numpy.float64) | |
if (self.nw >= 1): | |
self.w_n[0,2] = -1.0 | |
t@@ -108,111 +207,375 @@ class Spherebin: | |
if (self.nw >= 5): | |
self.w_n[4,1] = 1.0 | |
+ # Wall positions on the axes that are parallel to the wall normal [m] | |
self.w_x = numpy.ones(self.nw, dtype=numpy.float64) | |
+ | |
+ # Wall masses [kg] | |
self.w_m = numpy.zeros(self.nw, dtype=numpy.float64) | |
+ | |
+ # Wall velocities on the axes that are parallel to the wall normal [m/… | |
self.w_vel = numpy.zeros(self.nw, dtype=numpy.float64) | |
+ | |
+ # Wall forces on the axes that are parallel to the wall normal [m/s] | |
self.w_force = numpy.zeros(self.nw, dtype=numpy.float64) | |
+ | |
+ # Wall stress on the axes that are parallel to the wall normal [Pa] | |
self.w_devs = numpy.zeros(self.nw, dtype=numpy.float64) | |
+ | |
+ # Wall stress modulation amplitude [Pa] | |
self.w_devs_A = numpy.zeros(1, dtype=numpy.float64) | |
+ | |
+ # Wall stress modulation frequency [Hz] | |
self.w_devs_f = numpy.zeros(1, dtype=numpy.float64) | |
+ ## Bond parameters | |
+ # Radius multiplier to the parallel-bond radii | |
self.lambda_bar = numpy.ones(1, dtype=numpy.float64) | |
+ | |
+ # Number of bonds | |
self.nb0 = numpy.zeros(1, dtype=numpy.uint32) | |
+ | |
+ # Bond tensile strength [Pa] | |
self.sigma_b = numpy.ones(1, dtype=numpy.uint32) * numpy.infty | |
+ | |
+ # Bond shear strength [Pa] | |
self.tau_b = numpy.ones(1, dtype=numpy.uint32) * numpy.infty | |
+ | |
+ # Bond pairs | |
self.bonds = numpy.zeros((self.nb0, 2), dtype=numpy.uint32) | |
+ | |
+ # Parallel bond movement | |
self.bonds_delta_n = numpy.zeros(self.nb0, dtype=numpy.float64) | |
+ | |
+ # Shear bond movement | |
self.bonds_delta_t = numpy.zeros((self.nb0, self.nd), | |
dtype=numpy.float64) | |
+ | |
+ # Twisting bond movement | |
self.bonds_omega_n = numpy.zeros(self.nb0, dtype=numpy.float64) | |
+ | |
+ # Bending bond movement | |
self.bonds_omega_t = numpy.zeros((self.nb0, self.nd), | |
dtype=numpy.float64) | |
- self.nu = numpy.zeros(1, dtype=numpy.float64) | |
- self.f_v = numpy.zeros( | |
- (self.num[0], self.num[1], self.num[2], self.nd), | |
- dtype=numpy.float64) | |
- self.f_rho = numpy.zeros((self.num[0], self.num[1], self.num[2]), | |
- dtype=numpy.float64) | |
- self.f_phi = numpy.zeros((self.num[0], self.num[1], self.num[2]), | |
- dtype=numpy.float64) | |
- self.f_K = numpy.zeros((self.num[0], self.num[1], self.num[2]), | |
- dtype=numpy.float64) | |
+ ## Fluid parameters | |
+ | |
+ # Simulate fluid? True: Yes, False: no | |
+ self.fluid = fluid | |
+ | |
+ if (self.fluid == True): | |
+ | |
+ # Fluid dynamic viscosity [N/(m/s)] | |
+ self.mu = numpy.zeros(1, dtype=numpy.float64) | |
+ | |
+ # Fluid velocities [m/s] | |
+ self.v_f = numpy.zeros( | |
+ (self.num[0], self.num[1], self.num[2], self.nd), | |
+ dtype=numpy.float64) | |
+ | |
+ # Fluid pressures [Pa] | |
+ self.p_f = numpy.zeros((self.num[0], self.num[1], self.num[2]), | |
+ dtype=numpy.float64) | |
+ | |
+ # Fluid cell porosities [-] | |
+ self.phi = numpy.zeros((self.num[0], self.num[1], self.num[2]), | |
+ dtype=numpy.float64) | |
+ | |
+ # Fluid cell porosity change [1/s] | |
+ self.dphi = numpy.zeros((self.num[0], self.num[1], self.num[2]), | |
+ dtype=numpy.float64) | |
+ | |
+ # Fluid density [kg/(m^3)] | |
+ self.rho_f = numpy.ones(1, dtype=numpy.float64) * 1.0e3 | |
+ | |
+ # Pressure modulation at the top boundary | |
+ self.p_mod_A = numpy.zeros(1, dtype=numpy.float64) # Amplitude [P… | |
+ self.p_mod_f = numpy.zeros(1, dtype=numpy.float64) # Frequency [H… | |
+ self.p_mod_phi = numpy.zeros(1, dtype=numpy.float64) # Shift [rad] | |
+ | |
+ # Boundary conditions at the top and bottom of the fluid grid | |
+ # 0: Dirichlet, 1: Neumann | |
+ self.bc_bot = numpy.zeros(1, dtype=numpy.int32) | |
+ self.bc_top = numpy.zeros(1, dtype=numpy.int32) | |
+ # Free slip boundaries? 1: yes | |
+ self.free_slip_bot = numpy.ones(1, dtype=numpy.int32) | |
+ self.free_slip_top = numpy.ones(1, dtype=numpy.int32) | |
+ | |
+ | |
+ ## Solver parameters | |
+ | |
+ # Smoothing parameter, should be in the range [0.0;1.0[. | |
+ # 0.0 = no smoothing. | |
+ self.gamma = numpy.array(0.0) | |
+ | |
+ # Under-relaxation parameter, should be in the range ]0.0;1.0]. | |
+ # 1.0 = no under-relaxation | |
+ self.theta = numpy.array(1.0) | |
+ | |
+ # Velocity projection parameter, should be in the range [0.0;1.0] | |
+ self.beta = numpy.array(0.0) | |
+ | |
+ # Tolerance criteria for the normalized max. residual | |
+ self.tolerance = numpy.array(1.0e-8) | |
+ | |
+ # The maximum number of iterations to perform per time step | |
+ self.maxiter = numpy.array(1e4) | |
+ | |
def __cmp__(self, other): | |
- """ Called when to Spherebin objects are compared. | |
- Returns 0 if the values are identical """ | |
- if ( (\ | |
- self.nd == other.nd and\ | |
- self.np == other.np and\ | |
- self.time_dt == other.time_dt and\ | |
- self.time_current == other.time_current and\ | |
- self.time_total == other.time_total and\ | |
- self.time_file_dt == other.time_file_dt and\ | |
- self.time_step_count == other.time_step_count and\ | |
- (self.origo == other.origo).all() and\ | |
- (self.L == other.L).all() and\ | |
- (self.num == other.num).all() and\ | |
- self.periodic == other.periodic and\ | |
- (self.x == other.x).all() and\ | |
- (self.radius == other.radius).all() and\ | |
- (self.xysum == other.xysum).all() and\ | |
- (self.vel == other.vel).all() and\ | |
- (self.fixvel == other.fixvel).all() and\ | |
- (self.force == other.force).all() and\ | |
- (self.angpos == other.angpos).all() and\ | |
- (self.angvel == other.angvel).all() and\ | |
- (self.torque == other.torque).all() and\ | |
- (self.es_dot == other.es_dot).all() and\ | |
- (self.es == other.es).all() and\ | |
- (self.ev_dot == other.ev_dot).all() and\ | |
- (self.ev == other.ev).all() and\ | |
- (self.p == other.p).all() and\ | |
- (self.g == other.g).all() and\ | |
- self.k_n == other.k_n and\ | |
- self.k_t == other.k_t and\ | |
- self.k_r == other.k_r and\ | |
- self.gamma_n == other.gamma_n and\ | |
- self.gamma_t == other.gamma_t and\ | |
- self.gamma_r == other.gamma_r and\ | |
- self.mu_s == other.mu_s and\ | |
- self.mu_d == other.mu_d and\ | |
- self.mu_r == other.mu_r and\ | |
- self.rho == other.rho and\ | |
- self.contactmodel == other.contactmodel and\ | |
- self.kappa == other.kappa and\ | |
- self.db == other.db and\ | |
- self.V_b == other.V_b and\ | |
- self.nw == other.nw and\ | |
- (self.wmode == other.wmode).all() and\ | |
- (self.w_n == other.w_n).all() and\ | |
- (self.w_x == other.w_x).all() and\ | |
- (self.w_m == other.w_m).all() and\ | |
- (self.w_vel == other.w_vel).all() and\ | |
- (self.w_force == other.w_force).all() and\ | |
- (self.w_devs == other.w_devs).all() and\ | |
- self.w_devs_A == other.w_devs_A and\ | |
- self.w_devs_f == other.w_devs_f and\ | |
- self.gamma_wn == other.gamma_wn and\ | |
- self.gamma_wt == other.gamma_wt and\ | |
- self.lambda_bar == other.lambda_bar and\ | |
- self.nb0 == other.nb0 and\ | |
- self.sigma_b == other.sigma_b and\ | |
- self.tau_b == other.tau_b and\ | |
- self.bonds == other.bonds and\ | |
- self.bonds_delta_n == other.bonds_delta_n and\ | |
- self.bonds_delta_t == other.bonds_delta_t and\ | |
- self.bonds_omega_n == other.bonds_omega_n and\ | |
- self.bonds_omega_t == other.bonds_omega_t and\ | |
- self.nu == other.nu and\ | |
- (self.f_v == other.f_v).all() and\ | |
- (self.f_rho == other.f_rho).all() and\ | |
- (self.f_K == other.f_K).all() and\ | |
- (self.f_phi == other.f_phi).all()\ | |
- ).all() == True): | |
- return 0 # All equal | |
- else: | |
+ ''' | |
+ Called when to sim objects are compared. Returns 0 if the values | |
+ are identical. | |
+ TODO: Replace print(#) with print("field name") | |
+ ''' | |
+ if (self.version != other.version): | |
+ print(1) | |
return 1 | |
+ elif (self.nd != other.nd): | |
+ print(2) | |
+ return 2 | |
+ elif (self.np != other.np): | |
+ print(4) | |
+ return 4 | |
+ elif (self.time_dt != other.time_dt): | |
+ print(5) | |
+ return 5 | |
+ elif (self.time_current != other.time_current): | |
+ print(6) | |
+ return 6 | |
+ elif (self.time_total != other.time_total): | |
+ print(7) | |
+ return 7 | |
+ elif (self.time_file_dt != other.time_file_dt): | |
+ print(8) | |
+ return 8 | |
+ elif (self.time_step_count != other.time_step_count): | |
+ print(9) | |
+ return 9 | |
+ elif ((self.origo != other.origo).any()): | |
+ print(10) | |
+ return 10 | |
+ elif ((self.L != other.L).any()): | |
+ print(11) | |
+ return 11 | |
+ elif ((self.num != other.num).any()): | |
+ print(12) | |
+ return 12 | |
+ elif (self.periodic != other.periodic): | |
+ print(13) | |
+ return 13 | |
+ elif ((self.x != other.x).any()): | |
+ print(14) | |
+ return 14 | |
+ elif ((self.radius != other.radius).any()): | |
+ print(15) | |
+ return 15 | |
+ elif ((self.xysum != other.xysum).any()): | |
+ print(16) | |
+ return 16 | |
+ elif ((self.vel != other.vel).any()): | |
+ print(17) | |
+ return 17 | |
+ elif ((self.fixvel != other.fixvel).any()): | |
+ print(18) | |
+ return 18 | |
+ elif ((self.force != other.force).any()): | |
+ print(19) | |
+ return 19 | |
+ elif ((self.angpos != other.angpos).any()): | |
+ print(20) | |
+ return 20 | |
+ elif ((self.angvel != other.angvel).any()): | |
+ print(21) | |
+ return 21 | |
+ elif ((self.torque != other.torque).any()): | |
+ print(22) | |
+ return 22 | |
+ elif ((self.es_dot != other.es_dot).any()): | |
+ print(23) | |
+ return 23 | |
+ elif ((self.es != other.es).any()): | |
+ print(24) | |
+ return 24 | |
+ elif ((self.ev_dot != other.ev_dot).any()): | |
+ print(25) | |
+ return 25 | |
+ elif ((self.ev != other.ev).any()): | |
+ print(26) | |
+ return 26 | |
+ elif ((self.p != other.p).any()): | |
+ print(27) | |
+ return 27 | |
+ elif ((self.g != other.g).any()): | |
+ print(28) | |
+ return 28 | |
+ elif (self.k_n != other.k_n): | |
+ print(29) | |
+ return 29 | |
+ elif (self.k_t != other.k_t): | |
+ print(30) | |
+ return 30 | |
+ elif (self.k_r != other.k_r): | |
+ print(31) | |
+ return 31 | |
+ elif (self.gamma_n != other.gamma_n): | |
+ print(32) | |
+ return 32 | |
+ elif (self.gamma_t != other.gamma_t): | |
+ print(33) | |
+ return 33 | |
+ elif (self.gamma_r != other.gamma_r): | |
+ print(34) | |
+ return 34 | |
+ elif (self.mu_s != other.mu_s): | |
+ print(35) | |
+ return 35 | |
+ elif (self.mu_d != other.mu_d): | |
+ print(36) | |
+ return 36 | |
+ elif (self.mu_r != other.mu_r): | |
+ print(37) | |
+ return 37 | |
+ elif (self.rho != other.rho): | |
+ print(38) | |
+ return 38 | |
+ elif (self.contactmodel != other.contactmodel): | |
+ print(39) | |
+ return 39 | |
+ elif (self.kappa != other.kappa): | |
+ print(40) | |
+ return 40 | |
+ elif (self.db != other.db): | |
+ print(41) | |
+ return 41 | |
+ elif (self.V_b != other.V_b): | |
+ print(42) | |
+ return 42 | |
+ elif (self.nw != other.nw): | |
+ print(43) | |
+ return 43 | |
+ elif ((self.wmode != other.wmode).any()): | |
+ print(44) | |
+ return 44 | |
+ elif ((self.w_n != other.w_n).any()): | |
+ print(45) | |
+ return 45 | |
+ elif ((self.w_x != other.w_x).any()): | |
+ print(46) | |
+ return 46 | |
+ elif ((self.w_m != other.w_m).any()): | |
+ print(47) | |
+ return 47 | |
+ elif ((self.w_vel != other.w_vel).any()): | |
+ print(48) | |
+ return 48 | |
+ elif ((self.w_force != other.w_force).any()): | |
+ print(49) | |
+ return 49 | |
+ elif ((self.w_devs != other.w_devs).any()): | |
+ print(50) | |
+ return 50 | |
+ elif (self.w_devs_A != other.w_devs_A): | |
+ print(51) | |
+ return 51 | |
+ elif (self.w_devs_f != other.w_devs_f): | |
+ print(52) | |
+ return 52 | |
+ elif (self.gamma_wn != other.gamma_wn): | |
+ print(53) | |
+ return 53 | |
+ elif (self.gamma_wt != other.gamma_wt): | |
+ print(54) | |
+ return 54 | |
+ elif (self.lambda_bar != other.lambda_bar): | |
+ print(55) | |
+ return 55 | |
+ elif (self.nb0 != other.nb0): | |
+ print(56) | |
+ return 56 | |
+ elif (self.sigma_b != other.sigma_b): | |
+ print(57) | |
+ return 57 | |
+ elif (self.tau_b != other.tau_b): | |
+ print(58) | |
+ return 58 | |
+ elif (self.bonds != other.bonds): | |
+ print(59) | |
+ return 59 | |
+ elif (self.bonds_delta_n != other.bonds_delta_n): | |
+ print(60) | |
+ return 60 | |
+ elif (self.bonds_delta_t != other.bonds_delta_t): | |
+ print(61) | |
+ return 61 | |
+ elif (self.bonds_omega_n != other.bonds_omega_n): | |
+ print(62) | |
+ return 62 | |
+ elif (self.bonds_omega_t != other.bonds_omega_t): | |
+ print(63) | |
+ return 63 | |
+ elif (self.fluid != other.fluid): | |
+ print(64) | |
+ return 64 | |
+ | |
+ if (self.fluid == True): | |
+ if (self.mu != other.mu): | |
+ print(65) | |
+ return 65 | |
+ elif ((self.v_f != other.v_f).any()): | |
+ print(66) | |
+ return 66 | |
+ elif ((self.p_f != other.p_f).any()): | |
+ print(67) | |
+ return 67 | |
+ #elif ((self.phi != other.phi).any()): | |
+ #print(68) | |
+ #return 68 | |
+ elif ((self.dphi != other.dphi).any()): | |
+ print(69) | |
+ return 69 | |
+ elif (self.rho_f != other.rho_f): | |
+ print(70) | |
+ return 70 | |
+ elif (self.p_mod_A != other.p_mod_A): | |
+ print(71) | |
+ return 71 | |
+ elif (self.p_mod_f != other.p_mod_f): | |
+ print(72) | |
+ return 72 | |
+ elif (self.p_mod_phi != other.p_mod_phi): | |
+ print(73) | |
+ return 73 | |
+ elif (self.bc_bot != other.bc_bot): | |
+ print(74) | |
+ return 74 | |
+ elif (self.bc_top != other.bc_top): | |
+ print(75) | |
+ return 75 | |
+ elif (self.free_slip_bot != other.free_slip_bot): | |
+ print(76) | |
+ return 76 | |
+ elif (self.free_slip_top != other.free_slip_top): | |
+ print(77) | |
+ return 77 | |
+ elif (self.gamma != other.gamma): | |
+ print(78) | |
+ return 78 | |
+ elif (self.theta != other.theta): | |
+ print(79) | |
+ return 79 | |
+ elif (self.beta != other.beta): | |
+ print(80) | |
+ return 80 | |
+ elif (self.tolerance != other.tolerance): | |
+ print(81) | |
+ return 81 | |
+ elif (self.maxiter != other.maxiter): | |
+ print(82) | |
+ return 82 | |
+ | |
+ # All equal | |
+ return 0 | |
def addParticle(self, | |
x, | |
t@@ -229,9 +592,34 @@ class Spherebin: | |
ev_dot = numpy.zeros(1), | |
ev = numpy.zeros(1), | |
p = numpy.zeros(1)): | |
- ''' Add a single particle to the simulation object. The only required | |
- parameters are the position (x), a length-three array, and the | |
- radius (radius), a length-one array. | |
+ ''' | |
+ Add a single particle to the simulation object. The only required | |
+ parameters are the position (x) and the radius (radius). | |
+ | |
+ :param x: A vector pointing to the particle center coordinate. | |
+ :type x: numpy.array | |
+ :param radius: The particle radius | |
+ :type radius: float | |
+ :param vel: The particle linear velocity (default = [0,0,0]) | |
+ :type vel: numpy.array | |
+ :param fixvel: Fix horizontal linear velocity (0: No, 1: Yes, default=… | |
+ :type fixvel: float | |
+ :param angpos: The particle angular position (default = [0,0,0]) | |
+ :type angpos: numpy.array | |
+ :param angvel: The particle angular velocity (default = [0,0,0]) | |
+ :type angvel: numpy.array | |
+ :param torque: The particle torque (default = [0,0,0]) | |
+ :type torque: numpy.array | |
+ :param es_dot: The particle shear energy loss rate (default = 0) | |
+ :type es_dot: float | |
+ :param es: The particle shear energy loss (default = 0) | |
+ :type es: float | |
+ :param ev_dot: The particle viscous energy rate loss (default = 0) | |
+ :type ev_dot: float | |
+ :param ev: The particle viscous energy loss (default = 0) | |
+ :type ev: float | |
+ :param p: The particle pressure (default = 0) | |
+ :type p: float | |
''' | |
self.np = self.np + 1 | |
t@@ -249,11 +637,32 @@ class Spherebin: | |
self.es = numpy.append(self.es, es) | |
self.ev_dot = numpy.append(self.ev_dot, ev_dot) | |
self.ev = numpy.append(self.ev, ev) | |
- self.p = numpy.append(self.p, p) | |
+ self.p = numpy.append(self.p, p) | |
def readbin(self, targetbin, verbose = True, bonds = True, devsmod = True, | |
- fluid = True, esysparticle = False): | |
- 'Reads a target SPHERE binary file' | |
+ esysparticle = False): | |
+ ''' | |
+ Reads a target ``sphere`` binary file. | |
+ | |
+ See also :func:`writebin()`, :func:`readfirst()`, :func:`readlast()`, | |
+ :func:`readsecond`, and :func:`readstep`. | |
+ | |
+ :param targetbin: The path to the binary ``sphere`` file | |
+ :type targetbin: str | |
+ :param verbose: Show diagnostic information (default = True) | |
+ :type verbose: bool | |
+ :param bonds: The input file contains bond information (default = True… | |
+ This parameter should be true for all recent ``sphere`` versions. | |
+ :type bonds: bool | |
+ :param devsmod: The input file contains information about modulating | |
+ stresses at the top wall (default = True). This parameter should be | |
+ true for all recent ``sphere`` versions. | |
+ :type devsmod: bool | |
+ :param esysparticle: Stop reading the file after reading the kinematic… | |
+ which is useful for reading output files from other DEM programs. | |
+ (default = False) | |
+ :type esysparticle: bool | |
+ ''' | |
fh = None | |
try : | |
t@@ -261,16 +670,24 @@ class Spherebin: | |
print("Input file: {0}".format(targetbin)) | |
fh = open(targetbin, "rb") | |
+ # Read the file version | |
+ self.version = numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
+ | |
# Read the number of dimensions and particles | |
self.nd = numpy.fromfile(fh, dtype=numpy.int32, count=1) | |
self.np = numpy.fromfile(fh, dtype=numpy.uint32, count=1) | |
# Read the time variables | |
- self.time_dt = numpy.fromfile(fh, dtype=numpy.float64, cou… | |
- self.time_current = numpy.fromfile(fh, dtype=numpy.float64, cou… | |
- self.time_total = numpy.fromfile(fh, dtype=numpy.float64, cou… | |
- self.time_file_dt = numpy.fromfile(fh, dtype=numpy.float64, cou… | |
- self.time_step_count = numpy.fromfile(fh, dtype=numpy.uint32, coun… | |
+ self.time_dt = \ | |
+ numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
+ self.time_current =\ | |
+ numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
+ self.time_total =\ | |
+ numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
+ self.time_file_dt =\ | |
+ numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
+ self.time_step_count =\ | |
+ numpy.fromfile(fh, dtype=numpy.uint32, count=1) | |
# Allocate array memory for particles | |
self.x = numpy.empty((self.np, self.nd), dtype=numpy.float64) | |
t@@ -285,40 +702,49 @@ class Spherebin: | |
self.p = numpy.empty(self.np, dtype=numpy.float64) | |
# Read remaining data from binary | |
- self.origo = numpy.fromfile(fh, dtype=numpy.float64, count=self… | |
- self.L = numpy.fromfile(fh, dtype=numpy.float64, count=self… | |
- self.num = numpy.fromfile(fh, dtype=numpy.uint32, count=self.… | |
+ self.origo = numpy.fromfile(fh, dtype=numpy.float64, count=self.nd) | |
+ self.L = numpy.fromfile(fh, dtype=numpy.float64, count=self.nd) | |
+ self.num = numpy.fromfile(fh, dtype=numpy.uint32, count=self.nd) | |
self.periodic = numpy.fromfile(fh, dtype=numpy.int32, count=1) | |
# Per-particle vectors | |
for i in range(self.np): | |
- self.x[i,:] = numpy.fromfile(fh, dtype=numpy.float64, count… | |
- self.radius[i] = numpy.fromfile(fh, dtype=numpy.float64, count… | |
+ self.x[i,:] =\ | |
+ numpy.fromfile(fh, dtype=numpy.float64, count=self.nd) | |
+ self.radius[i] =\ | |
+ numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
- self.xysum = numpy.fromfile(fh, dtype=numpy.float64, count=self.np… | |
+ self.xysum = numpy.fromfile(fh, dtype=numpy.float64,\ | |
+ count=self.np*2).reshape(self.np,2) | |
for i in range(self.np): | |
- self.vel[i,:] = numpy.fromfile(fh, dtype=numpy.float64, count… | |
- self.fixvel[i] = numpy.fromfile(fh, dtype=numpy.float64, count… | |
+ self.vel[i,:] =\ | |
+ numpy.fromfile(fh, dtype=numpy.float64, count=self.nd) | |
+ self.fixvel[i] =\ | |
+ numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
- self.force = numpy.fromfile(fh, dtype=numpy.float64, count=self.np… | |
+ self.force = numpy.fromfile(fh, dtype=numpy.float64,\ | |
+ count=self.np*self.nd).reshape(self.np, self.nd) | |
- self.angpos = numpy.fromfile(fh, dtype=numpy.float64, count=self.n… | |
- self.angvel = numpy.fromfile(fh, dtype=numpy.float64, count=self.n… | |
- self.torque = numpy.fromfile(fh, dtype=numpy.float64, count=self.n… | |
+ self.angpos = numpy.fromfile(fh, dtype=numpy.float64,\ | |
+ count=self.np*self.nd).reshape(self.np, self.nd) | |
+ self.angvel = numpy.fromfile(fh, dtype=numpy.float64,\ | |
+ count=self.np*self.nd).reshape(self.np, self.nd) | |
+ self.torque = numpy.fromfile(fh, dtype=numpy.float64,\ | |
+ count=self.np*self.nd).reshape(self.np, self.nd) | |
if (esysparticle == True): | |
return | |
# Per-particle single-value parameters | |
- self.es_dot = numpy.fromfile(fh, dtype=numpy.float64, count=self.… | |
- self.es = numpy.fromfile(fh, dtype=numpy.float64, count=self.… | |
- self.ev_dot = numpy.fromfile(fh, dtype=numpy.float64, count=self.… | |
- self.ev = numpy.fromfile(fh, dtype=numpy.float64, count=self.… | |
- self.p = numpy.fromfile(fh, dtype=numpy.float64, count=self.… | |
+ self.es_dot = numpy.fromfile(fh, dtype=numpy.float64, count=self.n… | |
+ self.es = numpy.fromfile(fh, dtype=numpy.float64, count=self.n… | |
+ self.ev_dot = numpy.fromfile(fh, dtype=numpy.float64, count=self.n… | |
+ self.ev = numpy.fromfile(fh, dtype=numpy.float64, count=self.n… | |
+ self.p = numpy.fromfile(fh, dtype=numpy.float64, count=self.n… | |
# Constant, global physical parameters | |
- self.g = numpy.fromfile(fh, dtype=numpy.float64, count=… | |
+ self.g = numpy.fromfile(fh, dtype=numpy.float64, count=self.n… | |
self.k_n = numpy.fromfile(fh, dtype=numpy.float64, count=… | |
self.k_t = numpy.fromfile(fh, dtype=numpy.float64, count=… | |
self.k_r = numpy.fromfile(fh, dtype=numpy.float64, count=… | |
t@@ -341,7 +767,8 @@ class Spherebin: | |
# Wall data | |
self.nw = numpy.fromfile(fh, dtype=numpy.uint32, count=1) | |
self.wmode = numpy.empty(self.nw, dtype=numpy.int32) | |
- self.w_n = numpy.empty(self.nw*self.nd, dtype=numpy.float64).r… | |
+ self.w_n = numpy.empty(self.nw*self.nd, dtype=numpy.float64)\ | |
+ .reshape(self.nw,self.nd) | |
self.w_x = numpy.empty(self.nw, dtype=numpy.float64) | |
self.w_m = numpy.empty(self.nw, dtype=numpy.float64) | |
self.w_vel = numpy.empty(self.nw, dtype=numpy.float64) | |
t@@ -350,20 +777,23 @@ class Spherebin: | |
self.wmode = numpy.fromfile(fh, dtype=numpy.int32, count=self.nw) | |
for i in range(self.nw): | |
- self.w_n[i,:] = numpy.fromfile(fh, dtype=numpy.float64, count=… | |
+ self.w_n[i,:] =\ | |
+ numpy.fromfile(fh, dtype=numpy.float64, count=self.nd) | |
self.w_x[i] = numpy.fromfile(fh, dtype=numpy.float64, count=… | |
for i in range(self.nw): | |
- self.w_m[i] = numpy.fromfile(fh, dtype=numpy.float64, coun… | |
- self.w_vel[i] = numpy.fromfile(fh, dtype=numpy.float64, coun… | |
- self.w_force[i] = numpy.fromfile(fh, dtype=numpy.float64, coun… | |
- self.w_devs[i] = numpy.fromfile(fh, dtype=numpy.float64, coun… | |
+ self.w_m[i] = numpy.fromfile(fh, dtype=numpy.float64, count=… | |
+ self.w_vel[i] = numpy.fromfile(fh, dtype=numpy.float64, count=… | |
+ self.w_force[i] =\ | |
+ numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
+ self.w_devs[i]= numpy.fromfile(fh, dtype=numpy.float64, count=… | |
if (devsmod == True): | |
self.w_devs_A = numpy.fromfile(fh, dtype=numpy.float64, count=… | |
self.w_devs_f = numpy.fromfile(fh, dtype=numpy.float64, count=… | |
if (bonds == True): | |
# Inter-particle bonds | |
- self.lambda_bar = numpy.fromfile(fh, dtype=numpy.float64, coun… | |
+ self.lambda_bar =\ | |
+ numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
self.nb0 = numpy.fromfile(fh, dtype=numpy.uint32, count=1) | |
self.sigma_b = numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
self.tau_b = numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
t@@ -384,38 +814,86 @@ class Spherebin: | |
else: | |
self.nb0 = numpy.zeros(1, dtype=numpy.uint32) | |
- if (fluid == True): | |
- self.nu = numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
- if (self.nu[0] > 0.0): | |
- self.f_v = numpy.empty( | |
- (self.num[0], self.num[1], self.num[2], self.nd), | |
- dtype=numpy.float64) | |
- self.f_rho = numpy.empty((self.num[0],self.num[1],self.num… | |
- self.f_phi = numpy.empty((self.num[0],self.num[1],self.num… | |
- self.f_K = numpy.empty((self.num[0],self.num[1],self.num[2… | |
- for z in range(self.num[2]): | |
- for y in range(self.num[1]): | |
- for x in range(self.num[0]): | |
- self.f_v[x,y,z,0] = \ | |
- numpy.fromfile(fh, dtype=numpy.float64, count=… | |
- self.f_v[x,y,z,1] = \ | |
- numpy.fromfile(fh, dtype=numpy.float64, count=… | |
- self.f_v[x,y,z,2] = \ | |
- numpy.fromfile(fh, dtype=numpy.float64, count=… | |
- self.f_rho[x,y,z] = \ | |
- numpy.fromfile(fh, dtype=numpy.float64, count=… | |
- self.f_phi[x,y,z] = \ | |
- numpy.fromfile(fh, dtype=numpy.float64, count=… | |
- self.f_K[x,y,z] = \ | |
- numpy.fromfile(fh, dtype=numpy.float64, count=… | |
+ if (self.fluid == True): | |
+ self.mu = numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
+ | |
+ self.v_f = numpy.empty( | |
+ (self.num[0], self.num[1], self.num[2], self.nd), | |
+ dtype=numpy.float64) | |
+ self.p_f = \ | |
+ numpy.empty((self.num[0],self.num[1],self.num[2]), | |
+ dtype=numpy.float64) | |
+ self.phi = \ | |
+ numpy.empty((self.num[0],self.num[1],self.num[2]), | |
+ dtype=numpy.float64) | |
+ self.dphi = \ | |
+ numpy.empty((self.num[0],self.num[1],self.num[2]), | |
+ dtype=numpy.float64) | |
+ | |
+ for z in range(self.num[2]): | |
+ for y in range(self.num[1]): | |
+ for x in range(self.num[0]): | |
+ self.v_f[x,y,z,0] = \ | |
+ numpy.fromfile(fh, dtype=numpy.float64,\ | |
+ count=1) | |
+ self.v_f[x,y,z,1] = \ | |
+ numpy.fromfile(fh, dtype=numpy.float64,\ | |
+ count=1) | |
+ self.v_f[x,y,z,2] = \ | |
+ numpy.fromfile(fh, dtype=numpy.float64,\ | |
+ count=1) | |
+ self.p_f[x,y,z] = \ | |
+ numpy.fromfile(fh, dtype=numpy.float64,\ | |
+ count=1) | |
+ self.phi[x,y,z] = \ | |
+ numpy.fromfile(fh, dtype=numpy.float64,\ | |
+ count=1) | |
+ self.dphi[x,y,z] = \ | |
+ numpy.fromfile(fh, dtype=numpy.float64,\ | |
+ count=1) | |
+ | |
+ if (self.version >= 0.36): | |
+ self.rho_f =\ | |
+ numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
+ self.p_mod_A =\ | |
+ numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
+ self.p_mod_f =\ | |
+ numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
+ self.p_mod_phi =\ | |
+ numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
+ | |
+ self.bc_bot =\ | |
+ numpy.fromfile(fh, dtype=numpy.int32, count=1) | |
+ self.bc_top =\ | |
+ numpy.fromfile(fh, dtype=numpy.int32, count=1) | |
+ self.free_slip_bot =\ | |
+ numpy.fromfile(fh, dtype=numpy.int32, count=1) | |
+ self.free_slip_top =\ | |
+ numpy.fromfile(fh, dtype=numpy.int32, count=1) | |
+ | |
+ self.gamma = numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
+ self.theta = numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
+ self.beta = numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
+ self.tolerance =\ | |
+ numpy.fromfile(fh, dtype=numpy.float64, count=1) | |
+ self.maxiter = numpy.fromfile(fh, dtype=numpy.uint32, count=1) | |
finally: | |
if fh is not None: | |
fh.close() | |
def writebin(self, folder = "../input/", verbose = True): | |
- 'Writes to a target SPHERE binary file' | |
+ ''' | |
+ Writes a ``sphere`` binary file to the ``../input/`` folder by default. | |
+ The file name will be in the format ``<self.sid>.bin``. | |
+ See also :func:`readbin()`. | |
+ | |
+ :param folder: The folder where to place the output binary file | |
+ :type folder: str | |
+ :param verbose: Show diagnostic information (default = True) | |
+ :type verbose: bool | |
+ ''' | |
fh = None | |
try : | |
targetbin = folder + "/" + self.sid + ".bin" | |
t@@ -424,6 +902,9 @@ class Spherebin: | |
fh = open(targetbin, "wb") | |
+ # Write the current version number | |
+ fh.write(self.version.astype(numpy.float64)) | |
+ | |
# Write the number of dimensions and particles | |
fh.write(self.nd.astype(numpy.int32)) | |
fh.write(self.np.astype(numpy.uint32)) | |
t@@ -512,38 +993,146 @@ class Spherebin: | |
fh.write(self.bonds_omega_n.astype(numpy.float64)) | |
fh.write(self.bonds_omega_t.astype(numpy.float64)) | |
- fh.write(self.nu.astype(numpy.float64)) | |
- if (self.nu[0] > 0.0): | |
+ if (self.fluid == True): | |
+ fh.write(self.mu.astype(numpy.float64)) | |
for z in range(self.num[2]): | |
for y in range(self.num[1]): | |
for x in range(self.num[0]): | |
- fh.write(self.f_v[x,y,z,0].astype(numpy.float64)) | |
- fh.write(self.f_v[x,y,z,1].astype(numpy.float64)) | |
- fh.write(self.f_v[x,y,z,2].astype(numpy.float64)) | |
- fh.write(self.f_rho[x,y,z].astype(numpy.float64)) | |
- fh.write(self.f_phi[x,y,z].astype(numpy.float64)) | |
- fh.write(self.f_K[x,y,z].astype(numpy.float64)) | |
+ fh.write(self.v_f[x,y,z,0].astype(numpy.float64)) | |
+ fh.write(self.v_f[x,y,z,1].astype(numpy.float64)) | |
+ fh.write(self.v_f[x,y,z,2].astype(numpy.float64)) | |
+ fh.write(self.p_f[x,y,z].astype(numpy.float64)) | |
+ fh.write(self.phi[x,y,z].astype(numpy.float64)) | |
+ fh.write(self.dphi[x,y,z].astype(numpy.float64)) | |
+ | |
+ fh.write(self.rho_f.astype(numpy.float64)) | |
+ fh.write(self.p_mod_A.astype(numpy.float64)) | |
+ fh.write(self.p_mod_f.astype(numpy.float64)) | |
+ fh.write(self.p_mod_phi.astype(numpy.float64)) | |
+ | |
+ fh.write(self.bc_bot.astype(numpy.int32)) | |
+ fh.write(self.bc_top.astype(numpy.int32)) | |
+ fh.write(self.free_slip_bot.astype(numpy.int32)) | |
+ fh.write(self.free_slip_top.astype(numpy.int32)) | |
+ | |
+ fh.write(self.gamma.astype(numpy.float64)) | |
+ fh.write(self.theta.astype(numpy.float64)) | |
+ fh.write(self.beta.astype(numpy.float64)) | |
+ fh.write(self.tolerance.astype(numpy.float64)) | |
+ fh.write(self.maxiter.astype(numpy.uint32)) | |
finally: | |
if fh is not None: | |
fh.close() | |
- def writeVTKall(self): | |
- 'Writes all output binaries from the simulation to VTK files' | |
+ def writeVTKall(self, verbose = True): | |
+ ''' | |
+ Writes a VTK file for each simulation output file with particle | |
+ information and the fluid grid to the ``../output/`` folder by default. | |
+ The file name will be in the format ``<self.sid>.vtu`` and | |
+ ``fluid-<self.sid>.vti``. The vtu files can be used to visualize the | |
+ particles, and the vti files for visualizing the fluid in ParaView. | |
+ | |
+ After opening the vtu files, the particle fields will show up in the | |
+ "Properties" list. Press "Apply" to import all fields into the ParaView | |
+ session. The particles are visualized by selecting the imported data in | |
+ the "Pipeline Browser". Afterwards, click the "Glyph" button in the | |
+ "Common" toolbar, or go to the "Filters" menu, and press "Glyph" from | |
+ the "Common" list. Choose "Sphere" as the "Glyph Type", set "Radius" to | |
+ 1.0, choose "scalar" as the "Scale Mode". Check the "Edit" checkbox, a… | |
+ set the "Set Scale Factor" to 1.0. The field "Maximum Number of Points" | |
+ may be increased if the number of particles exceed the default value. | |
+ Finally press "Apply", and the particles will appear in the main windo… | |
+ | |
+ The sphere resolution may be adjusted ("Theta resolution", "Phi | |
+ resolution") to increase the quality and the computational requirements | |
+ of the rendering. | |
+ | |
+ The fluid grid is visualized by opening the vti files, and pressing | |
+ "Apply" to import all fluid field properties. To visualize the scalar | |
+ fields, such as the pressure, the porosity, the porosity change or the | |
+ velocity magnitude, choose "Surface" or "Surface With Edges" as the | |
+ "Representation". Choose the desired property as the "Coloring" field. | |
+ It may be desirable to show the color bar by pressing the "Show" butto… | |
+ and "Rescale" to fit the color range limits to the current file. The | |
+ coordinate system can be displayed by checking the "Show Axis" field. | |
+ All adjustments by default require the "Apply" button to be pressed | |
+ before regenerating the view. | |
+ | |
+ The fluid vector fields (e.g. the fluid velocity) can be visualizing by | |
+ e.g. arrows. To do this, select the fluid data in the "Pipeline | |
+ Browser". Press "Glyph" from the "Common" toolbar, or go to the | |
+ "Filters" mennu, and press "Glyph" from the "Common" list. Make sure | |
+ that "Arrow" is selected as the "Glyph type", and "Velocity" as the | |
+ "Vectors" value. Adjust the "Maximum Number of Points" to be at least … | |
+ big as the number of fluid cells in the grid. Press "Apply" to visuali… | |
+ the arrows. | |
+ | |
+ If several data files are generated for the same simulation (e.g. using | |
+ the :func:`writeVTKall()` function), it is able to step the | |
+ visualization through time by using the ParaView controls. | |
+ | |
+ :param verbose: Show diagnostic information (default = True) | |
+ :type verbose: bool | |
+ ''' | |
lastfile = status(self.sid) | |
- sb = Spherebin() | |
+ sb = sim(fluid = self.fluid) | |
for i in range(lastfile+1): | |
fn = "../output/{0}.output{1:0=5}.bin".format(self.sid, i) | |
sb.sid = self.sid + ".{:0=5}".format(i) | |
sb.readbin(fn, verbose = False) | |
- sb.writeVTK() | |
- if (sb.nu > 0.0): | |
- sb.writeFluidVTK() | |
- | |
+ if (self.np[0] > 0): | |
+ if (i == 0): | |
+ sb.writeVTK(verbose=verbose) | |
+ elif (i == lastfile): | |
+ if (verbose == True): | |
+ print("\tto") | |
+ sb.writeVTK(verbose=verbose) | |
+ else: | |
+ sb.writeVTK(verbose=False) | |
+ if (self.fluid == True): | |
+ if (i == 0): | |
+ sb.writeFluidVTK(verbose=verbose) | |
+ elif (i == lastfile): | |
+ if (verbose == True): | |
+ print("\tto") | |
+ sb.writeFluidVTK(verbose=verbose) | |
+ else: | |
+ sb.writeFluidVTK(verbose=False) | |
def writeVTK(self, folder = '../output/', verbose = True): | |
- 'Writes to a target VTK file' | |
+ ''' | |
+ Writes a VTK file with particle information to the ``../output/`` fold… | |
+ by default. The file name will be in the format ``<self.sid>.vtu``. | |
+ The vtu files can be used to visualize the particles in ParaView. | |
+ | |
+ After opening the vtu files, the particle fields will show up in the | |
+ "Properties" list. Press "Apply" to import all fields into the ParaView | |
+ session. The particles are visualized by selecting the imported data in | |
+ the "Pipeline Browser". Afterwards, click the "Glyph" button in the | |
+ "Common" toolbar, or go to the "Filters" menu, and press "Glyph" from | |
+ the "Common" list. Choose "Sphere" as the "Glyph Type", set "Radius" to | |
+ 1.0, choose "scalar" as the "Scale Mode". Check the "Edit" checkbox, a… | |
+ set the "Set Scale Factor" to 1.0. The field "Maximum Number of Points" | |
+ may be increased if the number of particles exceed the default value. | |
+ Finally press "Apply", and the particles will appear in the main windo… | |
+ | |
+ The sphere resolution may be adjusted ("Theta resolution", "Phi | |
+ resolution") to increase the quality and the computational requirements | |
+ of the rendering. All adjustments by default require the "Apply" button | |
+ to be pressed before regenerating the view. | |
+ | |
+ If several vtu files are generated for the same simulation (e.g. using | |
+ the :func:``writeVTKall()`` function), it is able to step the | |
+ visualization through time by using the ParaView controls. | |
+ | |
+ :param folder: The folder where to place the output binary file (defau… | |
+ (default = '../output/') | |
+ :type folder: str | |
+ :param verbose: Show diagnostic information (default = True) | |
+ :type verbose: bool | |
+ ''' | |
fh = None | |
try : | |
t@@ -557,16 +1146,20 @@ class Spherebin: | |
# http://www.vtk.org/VTK/img/file-formats.pdf | |
fh.write('<?xml version="1.0"?>\n') # XML header | |
- fh.write('<VTKFile type="UnstructuredGrid" version="0.1" byte_orde… | |
+ fh.write('<VTKFile type="UnstructuredGrid" version="0.1" ' | |
+ + 'byte_order="LittleEndian">\n') # VTK header | |
fh.write(' <UnstructuredGrid>\n') | |
- fh.write(' <Piece NumberOfPoints="{}" NumberOfCells="0">\n'.for… | |
+ fh.write(' <Piece NumberOfPoints="{}" '.format(self.np[0]) | |
+ + 'NumberOfCells="0">\n') | |
# Coordinates for each point (positions) | |
fh.write(' <Points>\n') | |
- fh.write(' <DataArray name="Position" type="Float32" Number… | |
+ fh.write(' <DataArray name="Position" type="Float32" ' | |
+ + 'NumberOfComponents="3" format="ascii">\n') | |
fh.write(' ') | |
for i in range(self.np): | |
- fh.write('{} {} {} '.format(self.x[i,0], self.x[i,1], self.x[i… | |
+ fh.write('{} {} {} '.format(\ | |
+ self.x[i,0], self.x[i,1], self.x[i,2])) | |
fh.write('\n') | |
fh.write(' </DataArray>\n') | |
fh.write(' </Points>\n') | |
t@@ -575,7 +1168,8 @@ class Spherebin: | |
fh.write(' <PointData Scalars="Radius" Vectors="vector">\n') | |
# Radii | |
- fh.write(' <DataArray type="Float32" Name="Radius" format="… | |
+ fh.write(' <DataArray type="Float32" Name="Radius" ' | |
+ + 'format="ascii">\n') | |
fh.write(' ') | |
for i in range(self.np): | |
fh.write('{} '.format(self.radius[i])) | |
t@@ -583,7 +1177,8 @@ class Spherebin: | |
fh.write(' </DataArray>\n') | |
# xysum.x | |
- fh.write(' <DataArray type="Float32" Name="Xdisplacement" f… | |
+ fh.write(' <DataArray type="Float32" Name="Xdisplacement" ' | |
+ + 'format="ascii">\n') | |
fh.write(' ') | |
for i in range(self.np): | |
fh.write('{} '.format(self.xysum[i,0])) | |
t@@ -591,7 +1186,8 @@ class Spherebin: | |
fh.write(' </DataArray>\n') | |
# xysum.y | |
- fh.write(' <DataArray type="Float32" Name="Ydisplacement" f… | |
+ fh.write(' <DataArray type="Float32" Name="Ydisplacement" ' | |
+ + 'format="ascii">\n') | |
fh.write(' ') | |
for i in range(self.np): | |
fh.write('{} '.format(self.xysum[i,1])) | |
t@@ -599,15 +1195,18 @@ class Spherebin: | |
fh.write(' </DataArray>\n') | |
# Velocity | |
- fh.write(' <DataArray type="Float32" Name="Velocity" Number… | |
+ fh.write(' <DataArray type="Float32" Name="Velocity" ' | |
+ + 'NumberOfComponents="3" format="ascii">\n') | |
fh.write(' ') | |
for i in range(self.np): | |
- fh.write('{} {} {} '.format(self.vel[i,0], self.vel[i,1], self… | |
+ fh.write('{} {} {} '.format(\ | |
+ self.vel[i,0], self.vel[i,1], self.vel[i,2])) | |
fh.write('\n') | |
fh.write(' </DataArray>\n') | |
# fixvel | |
- fh.write(' <DataArray type="Float32" Name="FixedVel" format… | |
+ fh.write(' <DataArray type="Float32" Name="FixedVel" ' | |
+ + 'format="ascii">\n') | |
fh.write(' ') | |
for i in range(self.np): | |
fh.write('{} '.format(self.fixvel[i])) | |
t@@ -615,39 +1214,48 @@ class Spherebin: | |
fh.write(' </DataArray>\n') | |
# Force | |
- fh.write(' <DataArray type="Float32" Name="Force" NumberOfC… | |
+ fh.write(' <DataArray type="Float32" Name="Force" ' | |
+ + 'NumberOfComponents="3" format="ascii">\n') | |
fh.write(' ') | |
for i in range(self.np): | |
- fh.write('{} {} {} '.format(self.force[i,0], self.force[i,1], … | |
+ fh.write('{} {} {} '.format(\ | |
+ self.force[i,0], self.force[i,1], self.force[i,2])) | |
fh.write('\n') | |
fh.write(' </DataArray>\n') | |
# Angular Position | |
- fh.write(' <DataArray type="Float32" Name="AngularPosition"… | |
+ fh.write(' <DataArray type="Float32" Name="AngularPosition"… | |
+ + 'NumberOfComponents="3" format="ascii">\n') | |
fh.write(' ') | |
for i in range(self.np): | |
- fh.write('{} {} {} '.format(self.angpos[i,0], self.angpos[i,1]… | |
+ fh.write('{} {} {} '.format(\ | |
+ self.angpos[i,0], self.angpos[i,1], self.angpos[i,2])) | |
fh.write('\n') | |
fh.write(' </DataArray>\n') | |
# Angular Velocity | |
- fh.write(' <DataArray type="Float32" Name="AngularVelocity"… | |
+ fh.write(' <DataArray type="Float32" Name="AngularVelocity"… | |
+ + 'NumberOfComponents="3" format="ascii">\n') | |
fh.write(' ') | |
for i in range(self.np): | |
- fh.write('{} {} {} '.format(self.angvel[i,0], self.angvel[i,1]… | |
+ fh.write('{} {} {} '.format(\ | |
+ self.angvel[i,0], self.angvel[i,1], self.angvel[i,2])) | |
fh.write('\n') | |
fh.write(' </DataArray>\n') | |
# Torque | |
- fh.write(' <DataArray type="Float32" Name="Torque" NumberOf… | |
+ fh.write(' <DataArray type="Float32" Name="Torque" ' | |
+ + 'NumberOfComponents="3" format="ascii">\n') | |
fh.write(' ') | |
for i in range(self.np): | |
- fh.write('{} {} {} '.format(self.torque[i,0], self.torque[i,1]… | |
+ fh.write('{} {} {} '.format(\ | |
+ self.torque[i,0], self.torque[i,1], self.torque[i,2])) | |
fh.write('\n') | |
fh.write(' </DataArray>\n') | |
# Shear energy rate | |
- fh.write(' <DataArray type="Float32" Name="ShearEnergyRate"… | |
+ fh.write(' <DataArray type="Float32" Name="ShearEnergyRate"… | |
+ + 'format="ascii">\n') | |
fh.write(' ') | |
for i in range(self.np): | |
fh.write('{} '.format(self.es_dot[i])) | |
t@@ -655,7 +1263,8 @@ class Spherebin: | |
fh.write(' </DataArray>\n') | |
# Shear energy | |
- fh.write(' <DataArray type="Float32" Name="ShearEnergy" for… | |
+ fh.write(' <DataArray type="Float32" Name="ShearEnergy" ' | |
+ + 'format="ascii">\n') | |
fh.write(' ') | |
for i in range(self.np): | |
fh.write('{} '.format(self.es[i])) | |
t@@ -663,7 +1272,8 @@ class Spherebin: | |
fh.write(' </DataArray>\n') | |
# Viscous energy rate | |
- fh.write(' <DataArray type="Float32" Name="ViscousEnergyRat… | |
+ fh.write(' <DataArray type="Float32" ' | |
+ + 'Name="ViscousEnergyRate" format="ascii">\n') | |
fh.write(' ') | |
for i in range(self.np): | |
fh.write('{} '.format(self.ev_dot[i])) | |
t@@ -671,7 +1281,8 @@ class Spherebin: | |
fh.write(' </DataArray>\n') | |
# Shear energy | |
- fh.write(' <DataArray type="Float32" Name="ViscousEnergy" f… | |
+ fh.write(' <DataArray type="Float32" Name="ViscousEnergy" ' | |
+ + 'format="ascii">\n') | |
fh.write(' ') | |
for i in range(self.np): | |
fh.write('{} '.format(self.ev[i])) | |
t@@ -679,7 +1290,8 @@ class Spherebin: | |
fh.write(' </DataArray>\n') | |
# Pressure | |
- fh.write(' <DataArray type="Float32" Name="Pressure" format… | |
+ fh.write(' <DataArray type="Float32" Name="Pressure" ' | |
+ + 'format="ascii">\n') | |
fh.write(' ') | |
for i in range(self.np): | |
fh.write('{} '.format(self.p[i])) | |
t@@ -689,11 +1301,14 @@ class Spherebin: | |
fh.write(' </PointData>\n') | |
fh.write(' <Cells>\n') | |
- fh.write(' <DataArray type="Int32" Name="connectivity" form… | |
+ fh.write(' <DataArray type="Int32" Name="connectivity" ' | |
+ + 'format="ascii">\n') | |
fh.write(' </DataArray>\n') | |
- fh.write(' <DataArray type="Int32" Name="offsets" format="a… | |
+ fh.write(' <DataArray type="Int32" Name="offsets" ' | |
+ + 'format="ascii">\n') | |
fh.write(' </DataArray>\n') | |
- fh.write(' <DataArray type="UInt8" Name="types" format="asc… | |
+ fh.write(' <DataArray type="UInt8" Name="types" ' | |
+ + 'format="ascii">\n') | |
fh.write(' </DataArray>\n') | |
fh.write(' </Cells>\n') | |
fh.write(' </Piece>\n') | |
t@@ -705,7 +1320,41 @@ class Spherebin: | |
fh.close() | |
def writeFluidVTK(self, folder = '../output/', verbose = True): | |
- 'Writes fluid data to a target VTK file' | |
+ ''' | |
+ Writes a VTK file for the fluid grid to the ``../output/`` folder by | |
+ default. The file name will be in the format ``fluid-<self.sid>.vti``. | |
+ The vti files can be used for visualizing the fluid in ParaView. | |
+ | |
+ The fluid grid is visualized by opening the vti files, and pressing | |
+ "Apply" to import all fluid field properties. To visualize the scalar | |
+ fields, such as the pressure, the porosity, the porosity change or the | |
+ velocity magnitude, choose "Surface" or "Surface With Edges" as the | |
+ "Representation". Choose the desired property as the "Coloring" field. | |
+ It may be desirable to show the color bar by pressing the "Show" butto… | |
+ and "Rescale" to fit the color range limits to the current file. The | |
+ coordinate system can be displayed by checking the "Show Axis" field. | |
+ All adjustments by default require the "Apply" button to be pressed | |
+ before regenerating the view. | |
+ | |
+ The fluid vector fields (e.g. the fluid velocity) can be visualizing by | |
+ e.g. arrows. To do this, select the fluid data in the "Pipeline | |
+ Browser". Press "Glyph" from the "Common" toolbar, or go to the | |
+ "Filters" mennu, and press "Glyph" from the "Common" list. Make sure | |
+ that "Arrow" is selected as the "Glyph type", and "Velocity" as the | |
+ "Vectors" value. Adjust the "Maximum Number of Points" to be at least … | |
+ big as the number of fluid cells in the grid. Press "Apply" to visuali… | |
+ the arrows. | |
+ | |
+ If several data files are generated for the same simulation (e.g. using | |
+ the :func:`writeVTKall()` function), it is able to step the | |
+ visualization through time by using the ParaView controls. | |
+ | |
+ :param folder: The folder where to place the output binary file (defau… | |
+ (default = '../output/') | |
+ :type folder: str | |
+ :param verbose: Show diagnostic information (default = True) | |
+ :type verbose: bool | |
+ ''' | |
filename = folder + '/fluid-' + self.sid + '.vti' # image grid | |
t@@ -730,32 +1379,31 @@ class Spherebin: | |
# array of scalars: porosities | |
poros = vtk.vtkDoubleArray() | |
- poros.SetName("Porosity change") | |
+ poros.SetName("Porosity") | |
poros.SetNumberOfComponents(1) | |
poros.SetNumberOfTuples(grid.GetNumberOfPoints()) | |
- # array of scalars: hydraulic conductivities | |
- cond = vtk.vtkDoubleArray() | |
- cond.SetName("Conductivity") | |
- cond.SetNumberOfComponents(1) | |
- cond.SetNumberOfTuples(grid.GetNumberOfPoints()) | |
- | |
+ # array of scalars: porosity change | |
+ dporos = vtk.vtkDoubleArray() | |
+ dporos.SetName("Porosity change") | |
+ dporos.SetNumberOfComponents(1) | |
+ dporos.SetNumberOfTuples(grid.GetNumberOfPoints()) | |
# insert values | |
for z in range(self.num[2]): | |
for y in range(self.num[1]): | |
for x in range(self.num[0]): | |
idx = x + self.num[0]*y + self.num[0]*self.num[1]*z; | |
- pres.SetValue(idx, self.f_rho[x,y,z]) | |
- vel.SetTuple(idx, self.f_v[x,y,z,:]) | |
- poros.SetValue(idx, self.f_phi[x,y,z]) | |
- cond.SetValue(idx, self.f_K[x,y,z]) | |
+ pres.SetValue(idx, self.p_f[x,y,z]) | |
+ vel.SetTuple(idx, self.v_f[x,y,z,:]) | |
+ poros.SetValue(idx, self.phi[x,y,z]) | |
+ dporos.SetValue(idx, self.dphi[x,y,z]) | |
# add pres array to grid | |
grid.GetPointData().AddArray(pres) | |
grid.GetPointData().AddArray(vel) | |
grid.GetPointData().AddArray(poros) | |
- grid.GetPointData().AddArray(cond) | |
+ grid.GetPointData().AddArray(dporos) | |
# write VTK XML image data file | |
writer = vtk.vtkXMLImageDataWriter() | |
t@@ -767,21 +1415,61 @@ class Spherebin: | |
def readfirst(self, verbose=True): | |
- ''' Read first output file of self.sid ''' | |
+ ''' | |
+ Read the first output file from the ``../output/`` folder, correspondi… | |
+ to the object simulation id (``self.sid``). | |
+ | |
+ :param verbose: Display diagnostic information (default = True) | |
+ :type verbose: bool | |
+ | |
+ See also :func:`readbin()`, :func:`readlast()`, :func:`readsecond`, and | |
+ :func:`readstep`. | |
+ ''' | |
+ | |
fn = "../output/{0}.output00000.bin".format(self.sid) | |
self.readbin(fn, verbose) | |
def readsecond(self, verbose=True): | |
- ''' Read second output file of self.sid ''' | |
+ ''' | |
+ Read the second output file from the ``../output/`` folder, | |
+ corresponding to the object simulation id (``self.sid``). | |
+ | |
+ :param verbose: Display diagnostic information (default = True) | |
+ :type verbose: bool | |
+ | |
+ See also :func:`readbin()`, :func:`readfirst()`, :func:`readlast()`, | |
+ and :func:`readstep`. | |
+ ''' | |
fn = "../output/{0}.output00001.bin".format(self.sid) | |
self.readbin(fn, verbose) | |
def readstep(self, step, verbose=True): | |
- ''' Read output binary from time step 'step' from output/ folder. ''' | |
+ ''' | |
+ Read a output file from the ``../output/`` folder, corresponding | |
+ to the object simulation id (``self.sid``). | |
+ | |
+ :param step: The output file number to read, starting from 0. | |
+ :type step: int | |
+ :param verbose: Display diagnostic information (default = True) | |
+ :type verbose: bool | |
+ | |
+ See also :func:`readbin()`, :func:`readfirst()`, :func:`readlast()`, | |
+ and :func:`readsecond`. | |
+ ''' | |
fn = "../output/{0}.output{1:0=5}.bin".format(self.sid, step) | |
+ self.readbin(fn, verbose) | |
def readlast(self, verbose=True): | |
- ''' Read last output binary of self.sid from output/ folder. ''' | |
+ ''' | |
+ Read the last output file from the ``../output/`` folder, corresponding | |
+ to the object simulation id (``self.sid``). | |
+ | |
+ :param verbose: Display diagnostic information (default = True) | |
+ :type verbose: bool | |
+ | |
+ See also :func:`readbin()`, :func:`readfirst()`, :func:`readsecond`, a… | |
+ :func:`readstep`. | |
+ ''' | |
lastfile = status(self.sid) | |
fn = "../output/{0}.output{1:0=5}.bin".format(self.sid, lastfile) | |
self.readbin(fn, verbose) | |
t@@ -790,13 +1478,33 @@ class Spherebin: | |
radius_mean = 440e-6, | |
radius_variance = 8.8e-9, | |
histogram = True): | |
- """ Draw random particle radii from the selected probability distribut… | |
- Specify mean radius and variance. The variance should be kept at a | |
- very low value. | |
- """ | |
+ ''' | |
+ Draw random particle radii from a selected probability distribution. | |
+ The larger the variance of radii is, the slower the computations will | |
+ run. The reason is two-fold: The smallest particle dictates the time | |
+ step length, where smaller particles cause shorter time steps. At the | |
+ same time, the largest particle determines the sorting cell size, where | |
+ larger particles cause larger cells. Larger cells are likely to contain | |
+ more particles, causing more contact checks. | |
+ | |
+ :param psd: The particle side distribution. One possible value is | |
+ ``logn``, which is a log-normal probability distribution, suitable | |
+ for approximating well-sorted, coarse sediments. The other possible | |
+ value is ``uni``, which is a uniform distribution from | |
+ ``radius_mean-radius_variance`` to ``radius_mean+radius_variance``. | |
+ :type psd: str | |
+ :param radius_mean: The mean radius [m] (default = 440e-6 m) | |
+ :type radius_mean: float | |
+ :param radius_variance: The variance in the probability distribution | |
+ [m]. | |
+ :type radius_variance: float | |
+ | |
+ See also: :func:`generateBimodalRadii()`. | |
+ ''' | |
if psd == 'logn': # Log-normal probability distribution | |
- mu = math.log((radius_mean**2)/math.sqrt(radius_variance+radius_me… | |
+ mu = math.log(\ | |
+ (radius_mean**2)/math.sqrt(radius_variance+radius_mean**2)) | |
sigma = math.sqrt(math.log(radius_variance/(radius_mean**2)+1)) | |
self.radius = numpy.random.lognormal(mu, sigma, self.np) | |
if psd == 'uni': # Uniform distribution | |
t@@ -807,8 +1515,10 @@ class Spherebin: | |
# Show radii as histogram | |
if (histogram == True): | |
fig = plt.figure(figsize=(15,10), dpi=300) | |
- figtitle = 'Particle size distribution, {0} particles'.format(self… | |
- fig.text(0.5,0.95,figtitle,horizontalalignment='center',fontproper… | |
+ figtitle = 'Particle size distribution, {0} particles'.format(\ | |
+ self.np[0]) | |
+ fig.text(0.5,0.95,figtitle,horizontalalignment='center',\ | |
+ fontproperties=FontProperties(size=18)) | |
bins = 20 | |
# Create histogram | |
t@@ -818,7 +1528,7 @@ class Spherebin: | |
plt.xlabel('Radii [m]') | |
plt.ylabel('Count') | |
plt.axis('tight') | |
- fig.savefig('psd.png') | |
+ fig.savefig(self.sid + '-psd.png') | |
fig.clf() | |
def generateBimodalRadii(self, | |
t@@ -826,12 +1536,19 @@ class Spherebin: | |
r_large = 0.05, | |
ratio = 0.2, | |
verbose = True): | |
- """ Draw radii from two sizes | |
- @param r_small: Radii of small population (float), in ]0;r_large[ | |
- @param r_large: Radii of large population (float), in ]r_small;inf[ | |
- @param ratio: Approximate volumetric ratio between the two | |
+ ''' | |
+ Draw random radii from two distinct sizes. | |
+ | |
+ :param r_small: Radii of small population [m], in ]0;r_large[ | |
+ :type r_small: float | |
+ :param r_large: Radii of large population [m], in ]r_small;inf[ | |
+ :type r_large: float | |
+ :param ratio: Approximate volumetric ratio between the two | |
populations (large/small). | |
- """ | |
+ :type ratio: float | |
+ | |
+ See also: :func:`generateRadii()`. | |
+ ''' | |
if (r_small >= r_large): | |
raise Exception("r_large should be larger than r_small") | |
t@@ -850,34 +1567,64 @@ class Spherebin: | |
raise Exception("Volumetric ratio seems wrong") | |
if (verbose == True): | |
- print("generateBimodalRadii created " + str(nlarge) + " large part… | |
+ print("generateBimodalRadii created " + str(nlarge) | |
+ + " large particles, and " + str(self.np[0] - nlarge) | |
+ + " small") | |
+ def contactModel(self, contactmodel): | |
+ ''' | |
+ Define which contact model to use for the tangential component of | |
+ particle-particle interactions. The elastic-viscous-frictional contact | |
+ model (2) is considered to be the most realistic contact model, while | |
+ the viscous-frictional contact model is significantly faster. | |
+ | |
+ :param contactmodel: The type of tangential contact model to use | |
+ (visco-frictional = 1, elasto-visco-frictional = 2) | |
+ :type contactmodel: int | |
+ ''' | |
+ self.contactmodel[0] = contactmodel | |
- def initRandomPos(self, g = numpy.array([0.0, 0.0, -9.80665]), | |
- gridnum = numpy.array([12, 12, 36]), | |
- periodic = 1, | |
- contactmodel = 2): | |
- """ Initialize particle positions in loose, cubic configuration. | |
- Radii must be set beforehand. | |
- xynum is the number of rows in both x- and y- directions. | |
- """ | |
- self.g = g | |
- self.periodic[0] = periodic | |
+ def periodicBoundariesXY(self): | |
+ ''' | |
+ Set the x and y boundary conditions to be periodic. | |
+ ''' | |
+ self.periodic[0] = 1 | |
+ | |
+ def periodicBoundariesX(self): | |
+ ''' | |
+ Set the x boundary conditions to be periodic. | |
+ ''' | |
+ self.periodic[0] = 2 | |
+ | |
+ | |
+ def initRandomPos(self, gridnum = numpy.array([12, 12, 36])): | |
+ ''' | |
+ Initialize particle positions in completely random configuration. Radii | |
+ *must* be set beforehand. If the x and y boundaries are set as periodi… | |
+ the particle centers will be placed all the way to the edge. On regula… | |
+ non-periodic boundaries, the particles are restrained at the edges to | |
+ make space for their radii within the bounding box. | |
+ | |
+ :param gridnum: The number of sorting cells in each spatial direction | |
+ (default = [12, 12, 36]) | |
+ :type gridnum: numpy.array | |
+ :param dx: The cell width in any direction. If the default value is us… | |
+ (-1), the cell width is calculated to fit the largest particle. | |
+ :type dx: float | |
+ ''' | |
# Calculate cells in grid | |
self.num = gridnum | |
+ # Cell configuration | |
+ if (dx > 0.0): | |
+ cellsize = dx | |
+ else: | |
+ cellsize = 2.1 * numpy.amax(self.radius) | |
+ | |
# World size | |
- r_max = numpy.amax(self.radius) | |
- cellsize = 2 * r_max | |
self.L = self.num * cellsize | |
- # Init fluid arrays | |
- self.f_v = numpy.zeros((self.num[0],self.num[1],self.num[2],self.nd), … | |
- self.f_rho = numpy.ones((self.num[0],self.num[1],self.num[2]), dtype=n… | |
- self.f_phi = numpy.zeros((self.num[0],self.num[1],self.num[2],self.nd)… | |
- self.f_K = numpy.zeros((self.num[0],self.num[1],self.num[2],self.nd), … | |
- | |
# Particle positions randomly distributed without overlap | |
for i in range(self.np): | |
t@@ -898,54 +1645,97 @@ class Spherebin: | |
- (self.radius[i] + self.radius[j]) | |
if (delta_len < 0.0): | |
overlaps = True | |
- print("\rFinding non-overlapping particle positions, {0} % complet… | |
+ print("\rFinding non-overlapping particle positions, " | |
+ + "{0} % complete".format(numpy.ceil(i/self.np[0]*100))) | |
# Print newline | |
print() | |
- self.contactmodel[0] = contactmodel | |
- | |
- def initGrid(self): | |
- """ Initialize grid suitable for the particle positions set previously. | |
- The margin parameter adjusts the distance (in no. of max. radii) | |
- from the particle boundaries. | |
- """ | |
+ def defineWorldBoundaries(self, L, origo=[0.0, 0.0, 0.0], dx=-1): | |
+ ''' | |
+ Set the boundaries of the world. Particles will only be able to intera… | |
+ within this domain. With dynamic walls, allow space for expansions. | |
+ *Important*: The particle radii have to be set beforehand. The world | |
+ edges act as static walls. | |
+ | |
+ :param L: The upper boundary of the domain [m] | |
+ :type L: numpy.array | |
+ :param origo: The lower boundary of the domain [m]. Negative values | |
+ won't work. Default = [0.0, 0.0, 0.0]. | |
+ :type origo: numpy.array | |
+ :param dx: The cell width in any direction. If the default value is us… | |
+ (-1), the cell width is calculated to fit the largest particle. | |
+ :type dx: float | |
+ ''' | |
# Cell configuration | |
- cellsize_min = 2.1 * numpy.amax(self.radius) | |
+ if (dx > 0.0): | |
+ cellsize_min = dx | |
+ else: | |
+ cellsize_min = 2.1 * numpy.amax(self.radius) | |
+ | |
+ # Lower boundary of the sorting grid | |
+ self.origo[:] = origo[:] | |
+ | |
+ # Upper boundary of the sorting grid | |
+ self.L[:] = L[:] | |
+ | |
+ # Adjust the number of sorting cells along each axis to fit the largest | |
+ # particle size and the world size | |
self.num[0] = numpy.ceil((self.L[0]-self.origo[0])/cellsize_min) | |
self.num[1] = numpy.ceil((self.L[1]-self.origo[1])/cellsize_min) | |
self.num[2] = numpy.ceil((self.L[2]-self.origo[2])/cellsize_min) | |
+ #if (self.num.any() < 4): | |
if (self.num[0] < 4 or self.num[1] < 4 or self.num[2] < 4): | |
- print("Error: The grid must be at least 3 cells in each direction") | |
- print(" Grid: x={}, y={}, z={}".format(self.num[0], self.num[1], s… | |
+ raise Exception("Error: The grid must be at least 3 cells in each " | |
+ + "direction\nGrid: x={}, y={}, z={}\n".format(\ | |
+ self.num[0], self.num[1], self.num[2]) | |
+ + "Please increase the world size.") | |
- # Init fluid arrays | |
- self.f_v = numpy.zeros((self.num[0],self.num[1],self.num[2],self.nd), … | |
- self.f_rho = numpy.ones((self.num[0],self.num[1],self.num[2]), dtype=n… | |
- self.f_phi = numpy.zeros((self.num[0],self.num[1],self.num[2],self.nd)… | |
- self.f_K = numpy.zeros((self.num[0],self.num[1],self.num[2],self.nd), … | |
+ def initGrid(self, dx=-1): | |
+ ''' | |
+ Initialize grid suitable for the particle positions set previously. | |
+ The margin parameter adjusts the distance (in no. of max. radii) | |
+ from the particle boundaries. | |
+ *Important*: The particle radii have to be set beforehand if the cell | |
+ width isn't specified by `dx`. | |
+ :param dx: The cell width in any direction. If the default value is us… | |
+ (-1), the cell width is calculated to fit the largest particle. | |
+ :type dx: float | |
+ ''' | |
+ | |
+ # Cell configuration | |
+ if (dx > 0.0): | |
+ cellsize_min = dx | |
+ else: | |
+ cellsize_min = 2.1 * numpy.amax(self.radius) | |
+ self.num[0] = numpy.ceil((self.L[0]-self.origo[0])/cellsize_min) | |
+ self.num[1] = numpy.ceil((self.L[1]-self.origo[1])/cellsize_min) | |
+ self.num[2] = numpy.ceil((self.L[2]-self.origo[2])/cellsize_min) | |
+ | |
+ if (self.num[0] < 4 or self.num[1] < 4 or self.num[2] < 4): | |
+ raise Exception("Error: The grid must be at least 3 cells in each " | |
+ + "direction\nGrid: x={}, y={}, z={}".format(\ | |
+ self.num[0], self.num[1], self.num[2])) | |
# Put upper wall at top boundary | |
if (self.nw > 0): | |
self.w_x[0] = self.L[0] | |
- # Generate grid based on particle positions | |
- def initGridAndWorldsize(self, g = numpy.array([0.0, 0.0, -9.80665]), | |
- margin = 2.0, | |
- periodic = 1, | |
- contactmodel = 2): | |
- """ Initialize grid suitable for the particle positions set previously. | |
- The margin parameter adjusts the distance (in no. of max. radii) | |
- from the particle boundaries. | |
- """ | |
+ def initGridAndWorldsize(self, margin = 2.0): | |
+ ''' | |
+ Initialize grid suitable for the particle positions set previously. | |
+ The margin parameter adjusts the distance (in no. of max. radii) | |
+ from the particle boundaries. If the upper wall is dynamic, it is plac… | |
+ at the top boundary of the world. | |
- self.g = g | |
- self.periodic[0] = periodic | |
+ :param margin: Distance to world boundary in no. of max. particle radii | |
+ :type margin: float | |
+ ''' | |
# Cell configuration | |
r_max = numpy.amax(self.radius) | |
t@@ -966,38 +1756,27 @@ class Spherebin: | |
self.num[2] = numpy.ceil((self.L[2]-self.origo[2])/cellsize_min) | |
if (self.num[0] < 4 or self.num[1] < 4 or self.num[2] < 4): | |
- print("Error: The grid must be at least 3 cells in each direction") | |
- print(self.num) | |
- | |
- # Init fluid arrays | |
- self.f_v = numpy.zeros((self.num[0],self.num[1],self.num[2],self.nd), … | |
- self.f_rho = numpy.ones((self.num[0],self.num[1],self.num[2]), dtype=n… | |
- self.f_phi = numpy.zeros((self.num[0],self.num[1],self.num[2],self.nd)… | |
- self.f_K = numpy.zeros((self.num[0],self.num[1],self.num[2],self.nd), … | |
- | |
- | |
- self.contactmodel[0] = contactmodel | |
+ raise Exception("Error: The grid must be at least 3 cells in each " | |
+ + "direction, num = " + str(self.num)) | |
# Put upper wall at top boundary | |
if (self.nw > 0): | |
self.w_x[0] = self.L[0] | |
- # Initialize particle positions to regular, grid-like, non-overlapping con… | |
- def initGridPos(self, g = numpy.array([0.0, 0.0, -9.80665]), | |
- gridnum = numpy.array([12, 12, 36]), | |
- periodic = 1, | |
- contactmodel = 2): | |
- """ Initialize particle positions in loose, cubic configuration. | |
- Radii must be set beforehand. | |
- xynum is the number of rows in both x- and y- directions. | |
- """ | |
+ def initGridPos(self, gridnum = numpy.array([12, 12, 36])): | |
+ ''' | |
+ Initialize particle positions in loose, cubic configuration. | |
+ ``gridnum`` is the number of cells in the x, y and z directions. | |
+ *Important*: The particle radii and the boundary conditions (periodic … | |
+ not) for the x and y boundaries have to be set beforehand. | |
- self.g = g | |
- self.periodic[0] = periodic | |
+ :param gridnum: The number of particles in x, y and z directions | |
+ :type gridnum: numpy.array | |
+ ''' | |
# Calculate cells in grid | |
- self.num = gridnum | |
+ self.num = numpy.asarray(gridnum) | |
# World size | |
r_max = numpy.amax(self.radius) | |
t@@ -1025,7 +1804,6 @@ class Spherebin: | |
# Particle positions randomly distributed without overlap | |
for i in range(self.np): | |
- | |
# Find position in 3d mesh from linear index | |
gridpos[0] = (i % (self.num[0])) | |
gridpos[1] = numpy.floor(i/(self.num[0])) % (self.num[0]) | |
t@@ -1035,44 +1813,40 @@ class Spherebin: | |
for d in range(self.nd): | |
self.x[i,d] = gridpos[d] * cellsize + 0.5*cellsize | |
- if (self.periodic[0] == 1): # Allow pushing every 2.nd level out o… | |
+ # Allow pushing every 2.nd level out of lateral boundaries | |
+ if (self.periodic[0] == 1): | |
# Offset every second level | |
if (gridpos[2] % 2): | |
self.x[i,0] += 0.5*cellsize | |
self.x[i,1] += 0.5*cellsize | |
- # Init fluid arrays | |
- self.f_v = numpy.zeros((self.num[0],self.num[1],self.num[2],self.nd), … | |
- self.f_rho = numpy.ones((self.num[0],self.num[1],self.num[2]), dtype=n… | |
- self.f_phi = numpy.zeros((self.num[0],self.num[1],self.num[2],self.nd)… | |
- self.f_K = numpy.zeros((self.num[0],self.num[1],self.num[2],self.nd), … | |
- | |
- self.contactmodel[0] = contactmodel | |
- | |
# Readjust grid to correct size | |
if (self.periodic[0] == 1): | |
self.num[0] += 1 | |
self.num[1] += 1 | |
- def initRandomGridPos(self, g = numpy.array([0.0, 0.0, -9.80665]), | |
- gridnum = numpy.array([12, 12, 32]), | |
- periodic = 1, | |
- contactmodel = 2): | |
- """ Initialize particle positions in loose, cubic configuration. | |
- Radii must be set beforehand. | |
- xynum is the number of rows in both x- and y- directions. | |
- """ | |
- | |
- self.g = g | |
- self.periodic[0] = periodic | |
+ def initRandomGridPos(self, gridnum = numpy.array([12, 12, 32])): | |
+ ''' | |
+ Initialize particle positions in loose, cubic configuration with some | |
+ variance. ``gridnum`` is the number of cells in the x, y and z | |
+ directions. *Important*: The particle radii and the boundary conditio… | |
+ (periodic or not) for the x and y boundaries have to be set beforehand. | |
+ The world size and grid height (in the z direction) is readjusted to f… | |
+ the particle positions. | |
+ | |
+ :param gridnum: The number of particles in x, y and z directions | |
+ :type gridnum: numpy.array | |
+ ''' | |
# Calculate cells in grid | |
- coarsegrid = numpy.floor(gridnum/2) | |
+ coarsegrid = numpy.floor(numpy.asarray(gridnum)/2) | |
# World size | |
r_max = numpy.amax(self.radius) | |
- cellsize = 2.1 * r_max * 2 # Cells in grid 2*size to make space for ra… | |
+ | |
+ # Cells in grid 2*size to make space for random offset | |
+ cellsize = 2.1 * r_max * 2 | |
# Check whether there are enough grid cells | |
if (((coarsegrid[0]-1)*(coarsegrid[1]-1)*(coarsegrid[2]-1)) < self.np): | |
t@@ -1089,38 +1863,38 @@ class Spherebin: | |
gridpos[1] = numpy.floor(i/(coarsegrid[0])) % (coarsegrid[0]) | |
gridpos[2] = numpy.floor(i/((coarsegrid[0])*(coarsegrid[1]))) | |
- # Place particles in grid structure, and randomly adjust the posit… | |
- # within the oversized cells (uniform distribution) | |
+ # Place particles in grid structure, and randomly adjust the | |
+ # positions within the oversized cells (uniform distribution) | |
for d in range(self.nd): | |
r = self.radius[i]*1.05 | |
self.x[i,d] = gridpos[d] * cellsize \ | |
+ ((cellsize-r) - r) * numpy.random.random_sample() + r | |
- self.contactmodel[0] = contactmodel | |
- | |
# Calculate new grid with cell size equal to max. particle diameter | |
x_max = numpy.max(self.x[:,0] + self.radius) | |
y_max = numpy.max(self.x[:,1] + self.radius) | |
z_max = numpy.max(self.x[:,2] + self.radius) | |
+ | |
# Adjust size of world | |
self.num[0] = numpy.ceil(x_max/cellsize) | |
self.num[1] = numpy.ceil(y_max/cellsize) | |
self.num[2] = numpy.ceil(z_max/cellsize) | |
self.L = self.num * cellsize | |
- # Init fluid arrays | |
- self.f_v = numpy.zeros((self.num[0],self.num[1],self.num[2],self.nd), … | |
- self.f_rho = numpy.ones((self.num[0],self.num[1],self.num[2]), dtype=n… | |
- self.f_phi = numpy.zeros((self.num[0],self.num[1],self.num[2],self.nd)… | |
- self.f_K = numpy.zeros((self.num[0],self.num[1],self.num[2],self.nd), … | |
- | |
def createBondPair(self, i, j, spacing=-0.1): | |
- """ Bond particles i and j. Particle j is moved adjacent to particle i, | |
+ ''' | |
+ Bond particles i and j. Particle j is moved adjacent to particle i, | |
and oriented randomly. | |
- @param spacing (float) The inter-particle distance prescribed. Positive | |
- values result in a inter-particle distance, negative equal an overlap. | |
- The value is relative to the sum of the two radii. | |
- """ | |
+ | |
+ :param i: Index of first particle in bond | |
+ :type i: int | |
+ :param j: Index of second particle in bond | |
+ :type j: int | |
+ :param spacing: The inter-particle distance prescribed. Positive | |
+ values result in a inter-particle distance, negative equal an | |
+ overlap. The value is relative to the sum of the two radii. | |
+ :type spacing: float | |
+ ''' | |
x_i = self.x[i] | |
r_i = self.radius[i] | |
t@@ -1164,26 +1938,36 @@ class Spherebin: | |
raise Exception("Error, something went wrong in createBondPair") | |
- def random2bonds(self, ratio=0.3, spacing=-0.1): | |
- """ Bond an amount of particles in two-particle clusters | |
- @param ratio: The amount of particles to bond, values in ]0.0;1.0] (fl… | |
- @param spacing: The distance relative to the sum of radii between bond… | |
- particles, neg. values denote an overlap. Values in ]0.0,inf[ (float). | |
- The particles should be initialized beforehand. | |
- Note: The actual number of bonds is likely to be somewhat smaller than | |
- specified, due to the random selection algorithm. | |
- """ | |
+ def randomBondPairs(self, ratio=0.3, spacing=-0.1): | |
+ ''' | |
+ Bond an amount of particles in two-particle clusters. The particles | |
+ should be initialized beforehand. Note: The actual number of bonds is | |
+ likely to be somewhat smaller than specified, due to the random | |
+ selection algorithm. | |
+ | |
+ :param ratio: The amount of particles to bond, values in ]0.0;1.0] | |
+ :type ratio: float | |
+ :param spacing: The distance relative to the sum of radii between bond… | |
+ particles, neg. values denote an overlap. Values in ]0.0,inf[. | |
+ :type spacing: float | |
+ ''' | |
- bondparticles = numpy.unique(numpy.random.random_integers(0, high=self… | |
+ bondparticles = numpy.unique(\ | |
+ numpy.random.random_integers(0, high=self.np-1,\ | |
+ size=int(self.np*ratio))) | |
if (bondparticles.size % 2 > 0): | |
bondparticles = bondparticles[:-1].copy() | |
- bondparticles = bondparticles.reshape(int(bondparticles.size/2), 2).co… | |
+ bondparticles =\ | |
+ bondparticles.reshape(int(bondparticles.size/2), 2).copy() | |
for n in numpy.arange(bondparticles.shape[0]): | |
self.createBondPair(bondparticles[n,0], bondparticles[n,1], spacin… | |
def zeroKinematics(self): | |
- 'Zero kinematics of particles' | |
+ ''' | |
+ Zero all kinematic parameters of the particles. This function is useful | |
+ when output from one simulation is reused in another simulation. | |
+ ''' | |
self.vel = numpy.zeros(self.np*self.nd, dtype=numpy.float64)\ | |
.reshape(self.np, self.nd) | |
t@@ -1197,12 +1981,19 @@ class Spherebin: | |
.reshape(self.np, 2) | |
def adjustUpperWall(self, z_adjust = 1.1): | |
- 'Included for legacy purposes, calls adjustWall with idx=0' | |
+ ''' | |
+ Included for legacy purposes, calls :func:`adjustWall()` with ``idx=0`… | |
+ | |
+ :param z_adjust: Increase the world and grid size by this amount to | |
+ allow for wall movement. | |
+ :type z_adjust: float | |
+ ''' | |
# Initialize upper wall | |
self.nw = numpy.ones(1) | |
self.wmode = numpy.zeros(1) # fixed BC | |
- self.w_n = numpy.zeros(self.nw*self.nd, dtype=numpy.float64).reshape(s… | |
+ self.w_n = numpy.zeros(self.nw*self.nd, dtype=numpy.float64).reshape(\ | |
+ self.nw,self.nd) | |
self.w_n[0,2] = -1.0 | |
self.w_vel = numpy.zeros(1) | |
self.w_force = numpy.zeros(1) | |
t@@ -1213,7 +2004,17 @@ class Spherebin: | |
self.adjustWall(idx=0, adjust = z_adjust) | |
def adjustWall(self, idx, adjust = 1.1): | |
- 'Adjust grid and dynamic wall to max. particle position' | |
+ ''' | |
+ Adjust grid and dynamic wall to max. particle position | |
+ | |
+ :param: idx: The wall to adjust. 0 = +z, upper wall (default), 1 = -x, | |
+ left wall, 2 = +x, right wall, 3 = -y, front wall, 4 = +y, back | |
+ wall. | |
+ :type idx: int | |
+ :param z_adjust: Increase the world and grid size by this amount to | |
+ allow for wall movement. | |
+ :type z_adjust: float | |
+ ''' | |
if (idx == 0): | |
dim = 2 | |
t@@ -1237,14 +2038,22 @@ class Spherebin: | |
self.w_x[idx] = numpy.array([xmax]) | |
else: | |
self.w_x[idx] = numpy.array([xmin]) | |
- self.w_m[idx] = numpy.array([self.rho[0] * self.np * math.pi * (cellsi… | |
+ self.w_m[idx] = numpy.array([self.rho[0]*self.np*math.pi \ | |
+ *(cellsize/2.0)**3]) | |
- def consolidate(self, deviatoric_stress = 10e3, | |
- periodic = 1): | |
- """ Setup consolidation experiment. Specify the upper wall | |
- deviatoric stress in Pascal, default value is 10 kPa. | |
- """ | |
+ def consolidate(self, normal_stress = 10e3): | |
+ ''' | |
+ Setup consolidation experiment. Specify the upper wall normal stress in | |
+ Pascal, default value is 10 kPa. | |
+ | |
+ :param normal_stress: The normal stress to apply from the upper wall | |
+ :type normal_stress: float | |
+ ''' | |
+ | |
+ if (normal_stress <= 0.0): | |
+ raise Exception('consolidate() error: The normal stress should be ' | |
+ 'a positive value, but is ' + str(normal_stress) + ' Pa') | |
# Zero the kinematics of all particles | |
self.zeroKinematics() | |
t@@ -1254,13 +2063,17 @@ class Spherebin: | |
# Set the top wall BC to a value of deviatoric stress | |
self.wmode = numpy.array([1]) | |
- self.w_devs = numpy.ones(1) * deviatoric_stress | |
+ self.w_devs = numpy.ones(1) * normal_stress | |
+ | |
+ def uniaxialStrainRate(self, wvel = -0.001): | |
+ ''' | |
+ Setup consolidation experiment. Specify the upper wall velocity in m/s, | |
+ default value is -0.001 m/s (i.e. downwards). | |
- def uniaxialStrainRate(self, wvel = -0.001, | |
- periodic = 1): | |
- """ Setup consolidation experiment. Specify the upper wall | |
- velocity in m/s, default value is -0.001 m/s (i.e. downwards). | |
- """ | |
+ :param wvel: Upper wall velocity. Negative values mean that the wall | |
+ moves downwards. | |
+ :type wvel: float | |
+ ''' | |
# zero kinematics | |
self.zeroKinematics() | |
t@@ -1270,20 +2083,27 @@ class Spherebin: | |
self.wmode = numpy.array([2]) # strain rate BC | |
self.w_vel = numpy.array([wvel]) | |
- def triaxial(self, wvel = -0.001, deviatoric_stress = 10.0e3): | |
- """ Setup triaxial experiment. The upper wall is moved at a fixed | |
- velocity in m/s, default values is -0.001 m/s (i.e. downwards). | |
- The side walls are exerting a deviatoric stress | |
- """ | |
+ def triaxial(self, wvel = -0.001, normal_stress = 10.0e3): | |
+ ''' | |
+ Setup triaxial experiment. The upper wall is moved at a fixed velocity | |
+ in m/s, default values is -0.001 m/s (i.e. downwards). The side walls | |
+ are exerting a defined normal stress. | |
+ | |
+ :param wvel: Upper wall velocity. Negative values mean that the wall | |
+ moves downwards. | |
+ :type wvel: float | |
+ :param normal_stress: The normal stress to apply from the upper wall. | |
+ :type normal_stress: float | |
+ ''' | |
# zero kinematics | |
self.zeroKinematics() | |
# Initialize walls | |
self.nw[0] = 5 # five dynamic walls | |
- self.wmode = numpy.array([2,1,1,1,1]) # define BCs (vel, stress, stre… | |
+ self.wmode = numpy.array([2,1,1,1,1]) # BCs (vel, stress, stress, ...) | |
self.w_vel = numpy.array([1,0,0,0,0]) * wvel | |
- self.w_devs = numpy.array([0,1,1,1,1]) * deviatoric_stress | |
+ self.w_devs = numpy.array([0,1,1,1,1]) * normal_stress | |
self.w_n = numpy.array(([0,0,-1], [-1,0,0], [1,0,0], [0,-1,0], [0,1,0]… | |
dtype=numpy.float64) | |
self.w_x = numpy.zeros(5) | |
t@@ -1292,16 +2112,17 @@ class Spherebin: | |
for i in range(5): | |
self.adjustWall(idx=i) | |
- | |
- def shear(self, | |
- shear_strain_rate = 1, | |
- periodic = 1): | |
- """ Setup shear experiment. Specify the upper wall | |
- deviatoric stress in Pascal, default value is 10 kPa. | |
- The shear strain rate is the shear length divided by the | |
- initial height per second. | |
- """ | |
+ def shear(self, shear_strain_rate = 1.0): | |
+ ''' | |
+ Setup shear experiment. The shear strain rate is the shear velocity | |
+ divided by the initial height per second. The shear movement is along | |
+ the positive x axis. The function zeroes the tangential wall viscosity | |
+ (gamma_wt) and the wall friction coefficients (mu_ws, mu_wn). | |
+ | |
+ :param shear_strain_rate: The shear strain rate to use. | |
+ :type shear_strain_rate: float | |
+ ''' | |
# Find lowest and heighest point | |
z_min = numpy.min(self.x[:,2] - self.radius) | |
t@@ -1317,14 +2138,9 @@ class Spherebin: | |
# zero kinematics | |
self.zeroKinematics() | |
- # set the thickness of the horizons of fixed particles | |
- #fixheight = 2*cellsize | |
- #fixheight = cellsize | |
- | |
# Fix horizontal velocity to 0.0 of lowermost particles | |
d_max_below = numpy.max(self.radius[numpy.nonzero(self.x[:,2] < | |
(z_max-z_min)*0.3)])*2.0 | |
- #I = numpy.nonzero(self.x[:,2] < (z_min + fixheight)) | |
I = numpy.nonzero(self.x[:,2] < (z_min + d_max_below)) | |
self.fixvel[I] = 1 | |
self.angvel[I,0] = 0.0 | |
t@@ -1336,7 +2152,6 @@ class Spherebin: | |
# Fix horizontal velocity to specific value of uppermost particles | |
d_max_top = numpy.max(self.radius[numpy.nonzero(self.x[:,2] > | |
(z_max-z_min)*0.7)])*2.0 | |
- #I = numpy.nonzero(self.x[:,2] > (z_max - fixheight)) | |
I = numpy.nonzero(self.x[:,2] > (z_max - d_max_top)) | |
self.fixvel[I] = 1 | |
self.angvel[I,0] = 0.0 | |
t@@ -1345,9 +2160,7 @@ class Spherebin: | |
self.vel[I,0] = (z_max-z_min)*shear_strain_rate | |
self.vel[I,1] = 0.0 # y-dim | |
- | |
- # Set wall viscosities to zero | |
- self.gamma_wn[0] = 0.0 | |
+ # Set wall tangential viscosity to zero | |
self.gamma_wt[0] = 0.0 | |
# Set wall friction coefficients to zero | |
t@@ -1357,18 +2170,79 @@ class Spherebin: | |
def initTemporal(self, total, | |
current = 0.0, | |
file_dt = 0.05, | |
- step_count = 0): | |
- """ Set temporal parameters for the simulation. | |
- Particle radii and physical parameters need to be set | |
- prior to these. | |
- """ | |
+ step_count = 0, | |
+ dt = -1): | |
+ ''' | |
+ Set temporal parameters for the simulation. *Important*: Particle radi… | |
+ physical parameters, and the optional fluid grid need to be set prior … | |
+ these if the computational time step (dt) isn't set explicitly. If the | |
+ parameter `dt` is the default value (-1), the function will estimate t… | |
+ best time step length. The value of the computational time step for the | |
+ DEM is checked for stability in the CFD solution if fluid simulation is | |
+ included. | |
+ | |
+ :param total: The time at which to end the simulation [s] | |
+ :type total: float | |
+ :param current: The current time [s] (default = 0.0 s) | |
+ :type total: float | |
+ :param file_dt: The interval between output files [s] (default = 0.05 … | |
+ :type total: float | |
+ :step_count: The number of the first output file (default = 0) | |
+ :type step_count: int | |
+ :param dt: The computational time step length [s] | |
+ :type total: float | |
+ ''' | |
- r_min = numpy.amin(self.radius) | |
# Computational time step (O'Sullivan et al, 2003) | |
- #self.time_dt[0] = 0.17 * math.sqrt((4.0/3.0 * math.pi * r_min**3 * se… | |
+ #self.time_dt[0] = 0.17 * \ | |
+ #math.sqrt((4.0/3.0 * math.pi * r_min**3 * self.rho[0]) \ | |
+ #/ numpy.amax([self.k_n[:], self.k_t[:]]) ) | |
# Computational time step (Zhang and Campbell, 1992) | |
- self.time_dt[0] = 0.075 * math.sqrt((V_sphere(r_min) * self.rho[0]) / … | |
+ if dt > 0: | |
+ self.time_dt[0] = dt | |
+ if (self.np[0] > 0): | |
+ print("Warning: Manually specifying the time step length when " | |
+ + "simulating particles may produce instabilities.") | |
+ else: | |
+ r_min = numpy.amin(self.radius) | |
+ self.time_dt[0] = 0.075 *\ | |
+ math.sqrt((V_sphere(r_min) * self.rho[0]) \ | |
+ / numpy.amax([self.k_n[:], self.k_t[:]]) ) | |
+ | |
+ | |
+ # Check numerical stability of the fluid phase, by criteria derived by | |
+ # von Neumann stability analysis of the diffusion and advection terms | |
+ if (self.fluid == True): | |
+ | |
+ # Cell spacing | |
+ dx = numpy.amin((\ | |
+ self.L[0]/self.num[0],\ | |
+ self.L[1]/self.num[1],\ | |
+ self.L[2]/self.num[2])) | |
+ | |
+ # Diffusion term | |
+ if (self.mu[0]*self.time_dt[0]/(dx*dx) > 0.5): | |
+ raise Exception("Error: The time step is too large to ensure " | |
+ + "stability in the diffusive term of the fluid " | |
+ + "momentum equation.") | |
+ | |
+ # Normalized velocities | |
+ v_norm = numpy.empty(self.num[0]*self.num[1]*self.num[2]) | |
+ idx = 0 | |
+ for x in numpy.arange(self.num[0]): | |
+ for y in numpy.arange(self.num[1]): | |
+ for z in numpy.arange(self.num[2]): | |
+ v_norm[idx] = numpy.sqrt(self.v_f[x,y,z,:].dot(\ | |
+ self.v_f[x,y,z,:])) | |
+ idx = idx + 1 | |
+ | |
+ # Advection term. This term has to be reevaluated during the | |
+ # computations, as the fluid velocity changes. | |
+ if (numpy.amax(v_norm)*self.time_dt[0]/dx > 1.0): | |
+ raise Exception("Error: The time step is too large to ensure " | |
+ + "stability in the advective term of the fluid " | |
+ + "momentum equation.") | |
# Time at start | |
self.time_current[0] = current | |
t@@ -1376,36 +2250,70 @@ class Spherebin: | |
self.time_file_dt[0] = file_dt | |
self.time_step_count[0] = 0 | |
- def initFluid(self, nu = 8.9e-4): | |
- """ Initialize the fluid arrays and the fluid viscosity """ | |
- self.f_rho = numpy.ones((self.num[0], self.num[1], self.num[2]), | |
+ def initFluid(self, mu = 8.9e-4): | |
+ ''' | |
+ Initialize the fluid arrays and the fluid viscosity. The default value | |
+ of ``mu`` equals the dynamic viscosity of water at 25 degrees Celcius. | |
+ The value for water at 0 degrees Celcius is 17.87e-4 kg/(m*s). | |
+ | |
+ :param mu: The fluid dynamic viscosity [kg/(m*s)] | |
+ :type mu: float | |
+ ''' | |
+ self.mu[0] = mu | |
+ self.p_f = numpy.ones((self.num[0], self.num[1], self.num[2]), | |
dtype=numpy.float64) | |
- self.f_v = numpy.zeros((self.num[0], self.num[1], self.num[2], self.nd… | |
+ self.v_f = numpy.zeros((self.num[0], self.num[1], self.num[2], self.nd… | |
dtype=numpy.float64) | |
- self.f_phi = numpy.ones((self.num[0], self.num[1], self.num[2]), | |
+ self.phi = numpy.ones((self.num[0], self.num[1], self.num[2]), | |
dtype=numpy.float64) | |
- self.f_K = numpy.ones((self.num[0], self.num[1], self.num[2]), | |
+ self.dphi = numpy.zeros((self.num[0], self.num[1], self.num[2]), | |
dtype=numpy.float64) | |
- | |
def defaultParams(self, | |
- mu_s = 0.4, | |
+ mu_s = 0.4, | |
mu_d = 0.4, | |
mu_r = 0.0, | |
rho = 2600, | |
k_n = 1.16e9, | |
k_t = 1.16e9, | |
k_r = 0, | |
- gamma_n = 0, | |
- gamma_t = 0, | |
- gamma_r = 0, | |
- gamma_wn = 1e4, | |
- gamma_wt = 1e4, | |
- capillaryCohesion = 0, | |
- nu = 0.0): | |
- """ Initialize particle parameters to default values. | |
- Radii must be set prior to calling this function. | |
- """ | |
+ gamma_n = 0.0, | |
+ gamma_t = 0.0, | |
+ gamma_r = 0.0, | |
+ gamma_wn = 1.0e4, | |
+ gamma_wt = 1.0e4, | |
+ capillaryCohesion = 0): | |
+ ''' | |
+ Initialize particle parameters to default values. | |
+ | |
+ :param mu_s: The coefficient of static friction between particles [-] | |
+ :type mu_s: float | |
+ :param mu_d: The coefficient of dynamic friction between particles [-] | |
+ :type mu_d: float | |
+ :param rho: The density of the particle material [kg/(m^3)] | |
+ :type rho: float | |
+ :param k_n: The normal stiffness of the particles [N/m] | |
+ :type k_n: float | |
+ :param k_t: The tangential stiffness of the particles [N/m] | |
+ :type k_t: float | |
+ :param k_r: The rolling stiffness of the particles [N/rad] *Parameter | |
+ not used* | |
+ :type k_r: float | |
+ :param gamma_n: Particle-particle contact normal viscosity [Ns/m] | |
+ :type gamma_n: float | |
+ :param gamma_t: Particle-particle contact tangential viscosity [Ns/m] | |
+ :type gamma_t: float | |
+ :param gamma_r: Particle-particle contact rolling viscosity *Parameter | |
+ not used* | |
+ :type gamma_r: float | |
+ :param gamma_wn: Wall-particle contact normal viscosity [Ns/m] | |
+ :type gamma_wn: float | |
+ :param gamma_wt: Wall-particle contact tangential viscosity [Ns/m] | |
+ :type gamma_wt: float | |
+ :param capillaryCohesion: Enable particle-particle capillary cohesion | |
+ interaction model (0 = no (default), 1 = yes) | |
+ :type capillaryCohesion: int | |
+ ''' | |
# Particle material density, kg/m^3 | |
self.rho = numpy.ones(1, dtype=numpy.float64) * rho | |
t@@ -1425,7 +2333,8 @@ class Spherebin: | |
# Contact normal viscosity. Critical damping: 2*sqrt(m*k_n). | |
# Normal force component elastic if nu = 0.0. | |
#self.gamma_n = numpy.ones(self.np, dtype=numpy.float64) \ | |
- # * nu_frac * 2.0 * math.sqrt(4.0/3.0 * math.pi * num… | |
+ # * nu_frac * 2.0 * math.sqrt(4.0/3.0 * math.pi \ | |
+ # * numpy.amin(self.radius)**3 \ | |
# * self.rho[0] * self.k_n[0]) | |
self.gamma_n = numpy.ones(1, dtype=numpy.float64) * gamma_n | |
t@@ -1436,15 +2345,18 @@ class Spherebin: | |
self.gamma_r = numpy.ones(1, dtype=numpy.float64) * gamma_r | |
# Contact static shear friction coefficient | |
- #self.mu_s = numpy.ones(1, dtype=numpy.float64) * numpy.tan(numpy.radi… | |
+ #self.mu_s = numpy.ones(1, dtype=numpy.float64) * \ | |
+ #numpy.tan(numpy.radians(ang_s)) | |
self.mu_s = numpy.ones(1, dtype=numpy.float64) * mu_s | |
# Contact dynamic shear friction coefficient | |
- #self.mu_d = numpy.ones(1, dtype=numpy.float64) * numpy.tan(numpy.radi… | |
+ #self.mu_d = numpy.ones(1, dtype=numpy.float64) * \ | |
+ #numpy.tan(numpy.radians(ang_d)) | |
self.mu_d = numpy.ones(1, dtype=numpy.float64) * mu_d | |
# Contact rolling friction coefficient | |
- #self.mu_r = numpy.ones(1, dtype=numpy.float64) * numpy.tan(numpy.radi… | |
+ #self.mu_r = numpy.ones(1, dtype=numpy.float64) * \ | |
+ #numpy.tan(numpy.radians(ang_r)) | |
self.mu_r = numpy.ones(1, dtype=numpy.float64) * mu_r | |
# Wall viscosities | |
t@@ -1460,7 +2372,8 @@ class Spherebin: | |
# Wettability, 0=perfect | |
theta = 0.0; | |
if (capillaryCohesion == 1): | |
- self.kappa[0] = 2.0 * math.pi * gamma_t * numpy.cos(theta) # Pref… | |
+ # Prefactor | |
+ self.kappa[0] = 2.0 * math.pi * gamma_t * numpy.cos(theta) | |
self.V_b[0] = 1e-12 # Liquid volume at bond | |
else : | |
self.kappa[0] = 0.0; # Zero capillary force | |
t@@ -1469,12 +2382,16 @@ class Spherebin: | |
# Debonding distance | |
self.db[0] = (1.0 + theta/2.0) * self.V_b**(1.0/3.0) | |
- # Fluid dynamic viscosity | |
- self.nu[0] = nu | |
- | |
def bond(self, i, j): | |
- """ Create a bond between particles i and j """ | |
+ ''' | |
+ Create a bond between particles with index i and j | |
+ | |
+ :param i: Index of first particle in bond | |
+ :type i: int | |
+ :param j: Index of second particle in bond | |
+ :type j: int | |
+ ''' | |
self.lambda_bar[0] = 1.0 # Radius multiplier to parallel-bond radii | |
t@@ -1490,9 +2407,11 @@ class Spherebin: | |
self.bonds_delta_n = numpy.append(self.bonds_delta_n, [0.0]) | |
if (hasattr(self, 'bonds_delta_t') == False): | |
- self.bonds_delta_t = numpy.array([[0.0, 0.0, 0.0]], dtype=numpy.ui… | |
+ self.bonds_delta_t = numpy.array([[0.0, 0.0, 0.0]],\ | |
+ dtype=numpy.uint32) | |
else : | |
- self.bonds_delta_t = numpy.vstack((self.bonds_delta_t, [0.0, 0.0, … | |
+ self.bonds_delta_t = numpy.vstack((self.bonds_delta_t,\ | |
+ [0.0, 0.0, 0.0])) | |
if (hasattr(self, 'bonds_omega_n') == False): | |
self.bonds_omega_n = numpy.array([0.0], dtype=numpy.uint32) | |
t@@ -1501,38 +2420,59 @@ class Spherebin: | |
self.bonds_omega_n = numpy.append(self.bonds_omega_n, [0.0]) | |
if (hasattr(self, 'bonds_omega_t') == False): | |
- self.bonds_omega_t = numpy.array([[0.0, 0.0, 0.0]], dtype=numpy.ui… | |
+ self.bonds_omega_t = numpy.array([[0.0, 0.0, 0.0]],\ | |
+ dtype=numpy.uint32) | |
else : | |
- self.bonds_omega_t = numpy.vstack((self.bonds_omega_t, [0.0, 0.0, … | |
+ self.bonds_omega_t = numpy.vstack((self.bonds_omega_t,\ | |
+ [0.0, 0.0, 0.0])) | |
# Increment the number of bonds with one | |
self.nb0 += 1 | |
- def currentDevs(self): | |
- ''' Return current magnitude of the deviatoric normal stress ''' | |
- return w_devs[0] + w_devs_A * numpy.sin(2.0 * numpy.pi * self.time_cur… | |
+ def currentNormalStress(self): | |
+ ''' | |
+ Calculates the current magnitude of the top wall normal stress. | |
+ | |
+ :returns: The current top wall normal stress in Pascal | |
+ :return type: float | |
+ ''' | |
+ return w_devs[0] + w_devs_A*numpy.sin(2.0*numpy.pi*self.time_current) | |
def energy(self, method): | |
- """ Calculate the sum of the energy components of all particles. | |
- """ | |
+ ''' | |
+ Calculates the sum of the energy components of all particles. | |
+ | |
+ :param method: The type of energy to return. Possible values are 'pot' | |
+ for potential energy [J], 'kin' for kinetic energy [J], 'rot' for | |
+ rotational energy [J], 'shear' for energy lost by friction, | |
+ 'shearrate' for the rate of frictional energy loss [W], 'visc_n' f… | |
+ viscous losses normal to the contact [J], 'visc_n_rate' for the ra… | |
+ of viscous losses normal to the contact [W], and finally 'bondpot' | |
+ for the potential energy stored in bonds [J] | |
+ :type method: str | |
+ :returns: The value of the selected energy type | |
+ :return type: float | |
+ ''' | |
if method == 'pot': | |
- m = numpy.ones(self.np) * 4.0/3.0 * math.pi * self.radius**3 * sel… | |
- return numpy.sum(m * math.sqrt(numpy.dot(self.g,self.g)) * self.x[… | |
+ m = numpy.ones(self.np)*4.0/3.0*math.pi*self.radius**3*self.rho | |
+ return numpy.sum(m*math.sqrt(numpy.dot(self.g,self.g))*self.x[:,2]) | |
elif method == 'kin': | |
- m = numpy.ones(self.np) * 4.0/3.0 * math.pi * self.radius**3 * sel… | |
+ m = numpy.ones(self.np)*4.0/3.0*math.pi*self.radius**3*self.rho | |
esum = 0.0 | |
for i in range(self.np): | |
- esum += 0.5 * m[i] * math.sqrt(numpy.dot(self.vel[i,:],self.ve… | |
+ esum += 0.5*m[i]*math.sqrt(\ | |
+ numpy.dot(self.vel[i,:],self.vel[i,:]))**2 | |
return esum | |
elif method == 'rot': | |
- m = numpy.ones(self.np) * 4.0/3.0 * math.pi * self.radius**3 * sel… | |
+ m = numpy.ones(self.np)*4.0/3.0*math.pi*self.radius**3*self.rho | |
esum = 0.0 | |
for i in range(self.np): | |
- esum += 0.5 * 2.0/5.0 * m[i] * self.radius[i]**2 \ | |
- * math.sqrt(numpy.dot(self.angvel[i,:],self.angvel[i,:… | |
+ esum += 0.5*2.0/5.0*m[i]*self.radius[i]**2 \ | |
+ *math.sqrt(\ | |
+ numpy.dot(self.angvel[i,:],self.angvel[i,:]))**2 | |
return esum | |
elif method == 'shear': | |
t@@ -1549,20 +2489,33 @@ class Spherebin: | |
elif method == 'bondpot': | |
if (self.nb0 > 0): | |
- R_bar = self.lambda_bar * numpy.minimum(self.radius[self.bonds… | |
- A = numpy.pi * R_bar**2 | |
- I = 0.25 * numpy.pi * R_bar**4 | |
+ R_bar = self.lambda_bar*numpy.minimum(\ | |
+ self.radius[self.bonds[:,0]],\ | |
+ self.radius[self.bonds[:,1]]) | |
+ A = numpy.pi*R_bar**2 | |
+ I = 0.25*numpy.pi*R_bar**4 | |
J = I*2.0 | |
- bondpot_fn = numpy.sum(0.5 * A * self.k_n * numpy.abs(self.bon… | |
- bondpot_ft = numpy.sum(0.5 * A * self.k_t * numpy.linalg.norm(… | |
- bondpot_tn = numpy.sum(0.5 * J * self.k_t * numpy.abs(self.bon… | |
- bondpot_tt = numpy.sum(0.5 * I * self.k_n * numpy.linalg.norm(… | |
+ bondpot_fn = numpy.sum(\ | |
+ 0.5*A*self.k_n*numpy.abs(self.bonds_delta_n)**2) | |
+ bondpot_ft = numpy.sum(\ | |
+ 0.5*A*self.k_t*numpy.linalg.norm(self.bonds_delta_t)**… | |
+ bondpot_tn = numpy.sum(\ | |
+ 0.5*J*self.k_t*numpy.abs(self.bonds_omega_n)**2) | |
+ bondpot_tt = numpy.sum(\ | |
+ 0.5*I*self.k_n*numpy.linalg.norm(self.bonds_omega_t)**… | |
return bondpot_fn + bondpot_ft + bondpot_tn + bondpot_tt | |
else : | |
return 0.0 | |
+ else: | |
+ raise Exception('Unknownw energy() method "' + method + '"') | |
def voidRatio(self): | |
- 'Returns the current void ratio' | |
+ ''' | |
+ Calculates the current void ratio | |
+ | |
+ :returns: The void ratio, in [0:1] | |
+ :return type: float | |
+ ''' | |
# Find the bulk volume | |
V_t = (self.L[0] - self.origo[0]) \ | |
t@@ -1577,7 +2530,12 @@ class Spherebin: | |
return e | |
def bulkPorosity(self): | |
- """ Calculate and return the bulk porosity """ | |
+ ''' | |
+ Calculates the bulk porosity | |
+ | |
+ :returns: The bulk porosity, in [0:1] | |
+ :return type: float | |
+ ''' | |
if (self.nw == 0): | |
V_total = self.L[0] * self.L[1] * self.L[2] | |
t@@ -1593,10 +2551,19 @@ class Spherebin: | |
def porosity(self, | |
slices = 10, | |
verbose = False): | |
- """ Calculate the porosity as a function of depth, by averaging values | |
- in horizontal slabs. | |
- Returns porosity values and depth | |
- """ | |
+ ''' | |
+ Calculates the porosity as a function of depth, by averaging values in | |
+ horizontal slabs. Returns porosity values and their corresponding dept… | |
+ The values are calculated using the external ``porosity`` program. | |
+ | |
+ :param slices: The number of vertical slabs to find porosities in. | |
+ :type slices: int | |
+ :param verbose: Show the file name of the temporary file written to | |
+ disk | |
+ :type verbose: bool | |
+ :returns: A 2d array of depths and their averaged porosities | |
+ :return type: numpy.array | |
+ ''' | |
# Write data as binary | |
self.writebin(verbose=False) | |
t@@ -1627,8 +2594,28 @@ class Spherebin: | |
return numpy.array(porosity), numpy.array(depth) | |
def run(self, verbose=True, hideinputfile=False, dry=False, valgrind=False, | |
- cudamemcheck=False, darcyflow=False): | |
- 'Execute sphere with target project' | |
+ cudamemcheck=False): | |
+ ''' | |
+ Start ``sphere`` calculations on the ``sim`` object | |
+ | |
+ :param verbose: Show ``sphere`` output | |
+ :type verbose: bool | |
+ :param hideinputfile: Hide the file name of the ``sphere`` input file | |
+ :type hideinputfile: bool | |
+ :param dry: Perform a dry run. Important parameter values are shown by | |
+ the ``sphere`` program, and it exits afterwards. | |
+ :type dry: bool | |
+ :param valgrind: Run the program with ``valgrind`` in order to check | |
+ memory leaks in the host code. This causes a significant increase … | |
+ computational time. | |
+ :type valgrind: bool | |
+ :param cudamemcheck: Run the program with ``cudamemcheck`` in order to | |
+ check for device memory leaks and errors. This causes a significant | |
+ increase in computational time. | |
+ :type cudamemcheck: bool | |
+ ''' | |
+ | |
+ self.writebin(verbose=False) | |
quiet = "" | |
stdout = "" | |
t@@ -1645,24 +2632,66 @@ class Spherebin: | |
if (valgrind == True): | |
valgrindbin = "valgrind -q " | |
if (cudamemcheck == True): | |
- cudamemchk = "cuda-memcheck " | |
- if (darcyflow == True): | |
+ cudamemchk = "cuda-memcheck --leak-check full " | |
+ if (self.fluid == True): | |
binary = "porousflow" | |
- status = subprocess.call("cd ..; " + valgrindbin + cudamemchk + "./" +… | |
+ cmd = "cd ..; " + valgrindbin + cudamemchk + "./" + binary + " " \ | |
+ + quiet + dryarg + "input/" + self.sid + ".bin " + stdout | |
+ #print(cmd) | |
+ status = subprocess.call(cmd, shell=True) | |
if (status != 0): | |
print("Warning: the sphere run returned with status " + str(status… | |
+ def cleanup(self): | |
+ ''' | |
+ Removes the input/output files and images belonging to the object | |
+ simulation ID from the ``input/``, ``output/`` and ``img_out/`` folder… | |
+ ''' | |
+ cleanup(self) | |
+ | |
def torqueScript(self, | |
- email="[email protected]", | |
- email_alerts="ae", | |
- walltime="24:00:00", | |
- queue="qfermi", | |
- cudapath="/com/cuda/4.0.17/cuda", | |
- spheredir="/home/adc/code/sphere", | |
- workdir="/scratch"): | |
- 'Create job script for the Torque queue manager for the binary' | |
+ email='[email protected]', | |
+ email_alerts='ae', | |
+ walltime='24:00:00', | |
+ queue='qfermi', | |
+ cudapath='/com/cuda/4.0.17/cuda', | |
+ spheredir='/home/adc/code/sphere', | |
+ use_workdir=False, | |
+ workdir='/scratch'): | |
+ ''' | |
+ Creates a job script for the Torque queue manager for the simulation | |
+ object. | |
+ | |
+ :param email: The e-mail address that Torque messages should be sent to | |
+ :type email: str | |
+ :param email_alerts: The type of Torque messages to send to the e-mail | |
+ address. The character 'b' causes a mail to be sent when the | |
+ execution begins. The character 'e' causes a mail to be sent when | |
+ the execution ends normally. The character 'a' causes a mail to be | |
+ sent if the execution ends abnormally. The characters can be writt… | |
+ in any order. | |
+ :type email_alerts: str | |
+ :param walltime: The maximal allowed time for the job, in the format | |
+ 'HH:MM:SS'. | |
+ :type walltime: str | |
+ :param queue: The Torque queue to schedule the job for | |
+ :type queue: str | |
+ :param cudapath: The path of the CUDA library on the cluster compute | |
+ nodes | |
+ :type cudapath: str | |
+ :param spheredir: The path to the root directory of sphere on the | |
+ cluster | |
+ :type spheredir: str | |
+ :param use_workdir: Use a different working directory than the sphere | |
+ folder | |
+ :type use_workdir: bool | |
+ :param workdir: The working directory during the calculations, if | |
+ `use_workdir=True` | |
+ :type workdir: str | |
+ | |
+ ''' | |
filename = self.sid + ".sh" | |
fh = None | |
t@@ -1678,17 +2707,22 @@ class Spherebin: | |
fh.write('#PBS -m ' + email_alerts + '\n') | |
fh.write('CUDAPATH=' + cudapath + '\n') | |
fh.write('export PATH=$CUDAPATH/bin:$PATH\n') | |
- fh.write('export LD_LIBRARY_PATH=$CUDAPATH/lib64:$CUDAPATH/lib:$LD… | |
+ fh.write('export LD_LIBRARY_PATH=$CUDAPATH/lib64' | |
+ + ':$CUDAPATH/lib:$LD_LIBRARY_PATH\n') | |
fh.write('echo "`whoami`@`hostname`"\n') | |
fh.write('echo "Start at `date`"\n') | |
fh.write('ORIGDIR=' + spheredir + '\n') | |
- fh.write('WORKDIR=' + workdir + "/$PBS_JOBID\n") | |
- fh.write('cp -r $ORIGDIR/* $WORKDIR\n') | |
- fh.write('cd $WORKDIR\n') | |
+ if (use_workdir == True): | |
+ fh.write('WORKDIR=' + workdir + "/$PBS_JOBID\n") | |
+ fh.write('cp -r $ORIGDIR/* $WORKDIR\n') | |
+ fh.write('cd $WORKDIR\n') | |
+ else: | |
+ fh.write('cd ' + spheredir + '\n') | |
fh.write('cmake . && make\n') | |
fh.write('./sphere input/' + self.sid + '.bin > /dev/null &\n') | |
fh.write('wait\n') | |
- fh.write('cp $WORKDIR/output/* $ORIGDIR/output/\n') | |
+ if (use_workdir == True): | |
+ fh.write('cp $WORKDIR/output/* $ORIGDIR/output/\n') | |
fh.write('echo "End at `date`"\n') | |
finally : | |
t@@ -1699,9 +2733,30 @@ class Spherebin: | |
method = "pres", | |
max_val = 1e3, | |
lower_cutoff = 0.0, | |
- graphicsformat = "png", | |
+ graphics_format = "png", | |
verbose=True): | |
- 'Render all output files that belong to the simulation, determined by … | |
+ ''' | |
+ Using the built-in ray tracer, render all output files that belong to | |
+ the simulation, determined by the simulation id (``sid``). | |
+ | |
+ :param method: The color visualization method to use for the particles. | |
+ Possible values are: 'normal': color all particles with the same | |
+ color, 'pres': color by pressure, 'vel': color by translational | |
+ velocity, 'angvel': color by rotational velocity, 'xdisp': color by | |
+ total displacement along the x-axis, 'angpos': color by angular | |
+ position. | |
+ :type method: str | |
+ :param max_val: The maximum value of the color bar | |
+ :type max_val: float | |
+ :param lower_cutoff: Do not render particles with a value below this | |
+ value, of the field selected by ``method`` | |
+ :type lower_cutoff: float | |
+ :param graphics_format: Convert the PPM images generated by the ray | |
+ tracer to this image format using Imagemagick | |
+ :type graphics_format: str | |
+ :param verbose: Show verbose information during ray tracing | |
+ :type verbose: bool | |
+ ''' | |
print("Rendering {} images with the raytracer".format(self.sid)) | |
t@@ -1711,18 +2766,18 @@ class Spherebin: | |
# Render images using sphere raytracer | |
if (method == "normal"): | |
- subprocess.call("cd ..; for F in `ls output/" + self.sid + "*.bin`… | |
- + " --render $F; done" \ | |
- , shell=True) | |
+ subprocess.call("cd ..; for F in `ls output/" + self.sid | |
+ + "*.bin`; do ./sphere " + quiet | |
+ + " --render $F; done", shell=True) | |
else : | |
- subprocess.call("cd ..; for F in `ls output/" + self.sid + "*.bin`… | |
- + " --method " + method + " {}".format(max_val) \ | |
- + " -l {}".format(lower_cutoff) \ | |
- + " --render $F; done" \ | |
- , shell=True) | |
+ subprocess.call("cd ..; for F in `ls output/" + self.sid | |
+ + "*.bin`; do ./sphere " + quiet | |
+ + " --method " + method + " {}".format(max_val) | |
+ + " -l {}".format(lower_cutoff) | |
+ + " --render $F; done", shell=True) | |
# Convert images to compressed format | |
- convert() | |
+ convert(graphics_format=graphics_format) | |
def video(self, | |
out_folder = "./", | |
t@@ -1733,13 +2788,40 @@ class Spherebin: | |
qscale = 1, | |
bitrate = 1800, | |
verbose = False): | |
- 'Use ffmpeg to combine images to animation. All images should be rende… | |
+ ''' | |
+ Uses ffmpeg to combine images to animation. All images should be | |
+ rendered beforehand using func:`render()`. | |
+ | |
+ :param out_folder: The output folder for the video file | |
+ :type out_folder: str | |
+ :param video_format: The format of the output video | |
+ :type video_format: str | |
+ :param graphics_folder: The folder containing the rendered images | |
+ :type graphics_folder: str | |
+ :param graphics_format: The format of the rendered images | |
+ :type graphics_format: str | |
+ :param fps: The number of frames per second to use in the video | |
+ :type fps: int | |
+ :param qscale: The output video quality, in ]0;1] | |
+ :type qscale: float | |
+ :param bitrate: The bitrate to use in the output video | |
+ :type bitrate: int | |
+ :param verbose: Show ffmpeg output | |
+ :type verbose: bool | |
+ ''' | |
video(self.sid, out_folder, video_format, graphics_folder, \ | |
graphics_format, fps, qscale, bitrate, verbose) | |
- def shearvel(self): | |
- 'Calculates and returns the shear velocity (gamma_dot) of the experime… | |
+ def shearVel(self): | |
+ ''' | |
+ Calculates and returns the shear velocity (gamma_dot) of the | |
+ experiment. The shear velocity is the x-axis velocity value of the | |
+ upper particles. | |
+ | |
+ :returns: The shear velocity applied by the upper, fixed particles [m/… | |
+ :return type: float | |
+ ''' | |
# Find the fixed particles | |
fixvel = numpy.nonzero(self.fixvel > 0.0) | |
t@@ -1747,29 +2829,42 @@ class Spherebin: | |
# The shear velocity is the x-axis velocity value of the upper particl… | |
return self.vel[fixvel,0].max() | |
- def shearstrain(self): | |
- 'Calculates and returns the current shear strain (gamma) value of the … | |
+ def shearStrain(self): | |
+ ''' | |
+ Calculates and returns the current shear strain (gamma) value of the | |
+ experiment. The shear strain is found by determining the total x-axis | |
+ displacement of the upper, fixed particles. | |
+ | |
+ :returns: The total shear strain [-] | |
+ :return type: float | |
+ ''' | |
# Current height | |
w_x0 = self.w_x[0] | |
# Displacement of the upper, fixed particles in the shear direction | |
- xdisp = self.time_current[0] * self.shearvel() | |
+ xdisp = self.time_current[0] * self.shearVel() | |
# Return shear strain | |
return xdisp/w_x0 | |
def forcechains(self, lc=200.0, uc=650.0, outformat='png', disp='2d'): | |
- ''' Visualizes the force chains in the system from the magnitude of the | |
- normal contact forces, and produces an image of them. | |
- @param lc: Lower cutoff of contact forces. Contacts below are not | |
- visualized (float) | |
- @param uc: Upper cutoff of contact forces. Contacts above are | |
- visualized with this value (float) | |
- @param outformat: Format of output image. Possible values are | |
- 'interactive', 'png', 'epslatex', 'epslatex-color' | |
- @param disp: Display forcechains in '2d' or '3d' | |
- Warning: Will segfault if no contacts are found. | |
+ ''' | |
+ Visualizes the force chains in the system from the magnitude of the | |
+ normal contact forces, and produces an image of them. Warning: Will | |
+ segfault if no contacts are found. | |
+ | |
+ :param lc: Lower cutoff of contact forces. Contacts below are not | |
+ visualized | |
+ :type lc: float | |
+ :param uc: Upper cutoff of contact forces. Contacts above are | |
+ visualized with this value | |
+ :type uc: float | |
+ :param outformat: Format of output image. Possible values are | |
+ 'interactive', 'png', 'epslatex', 'epslatex-color' | |
+ :type outformat: str | |
+ :param disp: Display forcechains in '2d' or '3d' | |
+ :type disp: str | |
''' | |
self.writebin() | |
t@@ -1778,17 +2873,28 @@ class Spherebin: | |
if (disp == '2d'): | |
nd = '-2d ' | |
- subprocess.call("cd .. && ./forcechains " + nd + "-f " + outformat + "… | |
+ subprocess.call("cd .. && ./forcechains " + nd + "-f " + outformat \ | |
+ + " -lc " + str(lc) + " -uc " + str(uc) + " input/" + self.sid… | |
+ + ".bin > python/tmp.gp", shell=True) | |
subprocess.call("gnuplot tmp.gp && rm tmp.bin && rm tmp.gp", shell=Tru… | |
- def forcechainsRose(self, lower_limit=0.25): | |
- ''' Visualize strike- and dip angles of the strongest force chains in a | |
- rose plot. | |
+ def forcechainsRose(self, lower_limit=0.25, graphics_format='pdf'): | |
+ ''' | |
+ Visualize trend and plunge angles of the strongest force chains in a | |
+ rose plot. The plots are saved in the current folder with the name | |
+ 'fc-<simulation id>-rose.pdf'. | |
+ | |
+ :param lower_limit: Do not visualize force chains below this relative | |
+ contact force magnitude, in ]0;1[ | |
+ :type lower_limit: float | |
+ :param graphics_format: Save the plot in this format | |
+ :type graphics_format: str | |
''' | |
self.writebin(verbose=False) | |
- subprocess.call("cd .. && ./forcechains -f txt input/" + self.sid + ".… | |
+ subprocess.call("cd .. && ./forcechains -f txt input/" + self.sid \ | |
+ + ".bin > python/fc-tmp.txt", shell=True) | |
# data will have the shape (numcontacts, 7) | |
data = numpy.loadtxt("fc-tmp.txt", skiprows=1) | |
t@@ -1802,7 +2908,8 @@ class Spherebin: | |
# find the indexes of these contacts | |
I = numpy.nonzero(data[:,6] > f_n_lim) | |
- # loop through these contacts and find the strike and dip of the conta… | |
+ # loop through these contacts and find the strike and dip of the | |
+ # contacts | |
strikelist = [] # strike direction of the normal vector, [0:360[ | |
diplist = [] # dip of the normal vector, [0:90] | |
for i in I[0]: | |
t@@ -1838,18 +2945,26 @@ class Spherebin: | |
plt.figure(figsize=[4,4]) | |
- ax = plt.subplot(111, polar=True, axisbg="w") | |
+ ax = plt.subplot(111, polar=True, axisbg='w') | |
ax.scatter(strikelist, diplist, c='k', marker='+') | |
ax.set_rmax(90) | |
ax.set_rticks([]) | |
- plt.savefig("fc-" + self.sid + "-rose.pdf", transparent=True) | |
+ plt.savefig('fc-' + self.sid + '-rose.' + graphics_format,\ | |
+ transparent=True) | |
+ | |
+ subprocess.call('rm fc-tmp.txt', shell=True) | |
- subprocess.call("rm fc-tmp.txt", shell=True) | |
+ def bondsRose(self, graphics_format='pdf'): | |
+ ''' | |
+ Visualize the trend and plunge angles of the bond pairs in a rose plot. | |
+ The plot is saved in the current folder as | |
+ 'bonds-<simulation id>-rose.<graphics_format>'. | |
- def bondsRose(self, imgformat = "pdf"): | |
- ''' Visualize strike- and dip angles of the bond pairs in a rose plot. | |
+ :param graphics_format: Save the plot in this format | |
+ :type graphics_format: str | |
''' | |
- # loop through these contacts and find the strike and dip of the conta… | |
+ # loop through these contacts and find the strike and dip of the | |
+ # contacts | |
strikelist = [] # strike direction of the normal vector, [0:360[ | |
diplist = [] # dip of the normal vector, [0:90] | |
for n in numpy.arange(self.nb0): | |
t@@ -1886,20 +3001,44 @@ class Spherebin: | |
else : | |
strikelist.append(2.0*numpy.pi - math.acos(dx/dhoriz)) | |
- | |
plt.figure(figsize=[4,4]) | |
- ax = plt.subplot(111, polar=True, axisbg="w") | |
+ ax = plt.subplot(111, polar=True, axisbg='w') | |
ax.scatter(strikelist, diplist, c='k', marker='+') | |
ax.set_rmax(90) | |
ax.set_rticks([]) | |
- plt.savefig("bonds-" + self.sid + "-rose." + imgformat, transparent=Tr… | |
+ plt.savefig('bonds-' + self.sid + '-rose.' + graphics_format,\ | |
+ transparent=True) | |
def status(self): | |
- ''' Show the current simulation status ''' | |
+ ''' | |
+ Returns the current simulation status by using the simulation id | |
+ (``sid``) as an identifier. | |
+ | |
+ :returns: The number of the last output file written | |
+ :return type: int | |
+ ''' | |
return status(self.sid) | |
- def sheardisp(self, outformat='pdf', zslices=32): | |
- ''' Show particle x-displacement vs. the z-pos ''' | |
+ | |
+ def totalMomentum(self): | |
+ ''' | |
+ Returns the sum of particle momentums. | |
+ | |
+ :returns: The sum of particle momentums (m*v) [N*s] | |
+ :return type: float | |
+ ''' | |
+ v_norm = vector_norm(self.vel) | |
+ return numpy.sum(V_sphere(self.radius))*self.rho*v_norm | |
+ | |
+ def sheardisp(self, graphics_format='pdf', zslices=32): | |
+ ''' | |
+ Plot the particle x-axis displacement against the original vertical | |
+ particle position. The plot is saved in the current directory with the | |
+ file name '<simulation id>-sheardisp.<graphics_format>'. | |
+ | |
+ :param graphics_format: Save the plot in this format | |
+ :type graphics_format: str | |
+ ''' | |
# Bin data and error bars for alternative visualization | |
h_total = numpy.max(self.x[:,2]) - numpy.min(self.x[:,2]) | |
t@@ -1934,40 +3073,69 @@ class Spherebin: | |
c='black', linestyle='-', linewidth=1.4) | |
ax.set_xlabel("Horizontal particle displacement, [m]") | |
ax.set_ylabel("Vertical position, [m]") | |
- plt.savefig(self.sid + '-sheardisp.' + outformat, transparent=True) | |
- | |
- def porosities(self, outformat='pdf', zslices=16): | |
- ''' Plot porosities with depth ''' | |
+ plt.savefig(self.sid + '-sheardisp.' + graphics_format, | |
+ transparent=True) | |
+ def porosities(self, graphics_format='pdf', zslices=16): | |
+ ''' | |
+ Plot the averaged porosities with depth. The plot is saved in the form… | |
+ '<simulation id>-porosity.<graphics_format>'. | |
+ | |
+ :param graphics_format: Save the plot in this format | |
+ :type graphics_format: str | |
+ :param zslices: The number of points along the vertical axis to sample | |
+ the porosity in | |
+ :type zslices: int | |
+ ''' | |
porosity, depth = self.porosity(zslices) | |
plt.figure(figsize=[4, 4]) | |
ax = plt.subplot(111) | |
ax.plot(porosity, depth, | |
c='black', linestyle='-', linewidth=1.4) | |
- ax.set_xlabel("Horizontally averaged porosity, [-]") | |
- ax.set_ylabel("Vertical position, [m]") | |
- plt.savefig(self.sid + '-porositiy.' + outformat, transparent=True) | |
- | |
- | |
+ ax.set_xlabel('Horizontally averaged porosity, [-]') | |
+ ax.set_ylabel('Vertical position, [m]') | |
+ plt.savefig(self.sid + '-porositiy.' + graphics_format, | |
+ transparent=True) | |
def thinsection_x1x3(self, | |
x2 = 'center', | |
- graphicsformat = 'png', | |
+ graphics_format = 'png', | |
cbmax = None, | |
arrowscale = 0.01, | |
velarrowscale = 1.0, | |
slipscale = 1.0, | |
verbose = False): | |
- ''' Produce a 2D image of particles on a x1,x3 plane, intersecting the… | |
- Output is saved as '<sid>-ts-x1x3.txt' in the current folder. | |
- | |
- An upper limit to the pressure color bar range can be set by the c… | |
- | |
- The data can be plotted in gnuplot with: | |
- gnuplot> set size ratio -1 | |
- gnuplot> set palette defined (0 "blue", 0.5 "gray", 1 "red") | |
- gnuplot> plot '<sid>-ts-x1x3.txt' with circles palette fs tran… | |
+ ''' | |
+ Produce a 2D image of particles on a x1,x3 plane, intersecting the | |
+ second axis at x2. Output is saved as '<sid>-ts-x1x3.txt' in the | |
+ current folder. | |
+ | |
+ An upper limit to the pressure color bar range can be set by the | |
+ cbmax parameter. | |
+ | |
+ The data can be plotted in gnuplot with: | |
+ gnuplot> set size ratio -1 | |
+ gnuplot> set palette defined (0 "blue", 0.5 "gray", 1 "red") | |
+ gnuplot> plot '<sid>-ts-x1x3.txt' with circles palette fs \ | |
+ transparent solid 0.4 noborder | |
+ | |
+ This function also saves a plot of the inter-particle slip angles. | |
+ | |
+ :param x2: The position along the second axis of the intersecting plane | |
+ :type x2: foat | |
+ :param graphics_format: Save the slip angle plot in this format | |
+ :type graphics_format: str | |
+ :param cbmax: The maximal value of the pressure color bar range | |
+ :type cbmax: float | |
+ :param arrowscale: Scale the rotational arrows by this value | |
+ :type arrowscale: float | |
+ :param velarrowscale: Scale the translational arrows by this value | |
+ :type velarrowscale: float | |
+ :param slipscale: Scale the slip arrows by this value | |
+ :type slipscale: float | |
+ :param verbose: Show function output during calculations | |
+ :type verbose: bool | |
''' | |
if (x2 == 'center') : | |
t@@ -2029,19 +3197,32 @@ class Spherebin: | |
# Save two arrows per particle | |
axlist.append(self.x[i,0]) # x starting point of arrow | |
axlist.append(self.x[i,0]) # x starting point of arrow | |
- aylist.append(self.x[i,2] + r_circ*0.5) # y starting point of … | |
- aylist.append(self.x[i,2] - r_circ*0.5) # y starting point of … | |
- daxlist.append(self.angvel[i,1]*arrowscale) # delta x for arro… | |
- daxlist.append(-self.angvel[i,1]*arrowscale) # delta x for arr… | |
+ | |
+ # y starting point of arrow | |
+ aylist.append(self.x[i,2] + r_circ*0.5) | |
+ | |
+ # y starting point of arrow | |
+ aylist.append(self.x[i,2] - r_circ*0.5) | |
+ | |
+ # delta x for arrow end point | |
+ daxlist.append(self.angvel[i,1]*arrowscale) | |
+ | |
+ # delta x for arrow end point | |
+ daxlist.append(-self.angvel[i,1]*arrowscale) | |
daylist.append(0.0) # delta y for arrow end point | |
daylist.append(0.0) # delta y for arrow end point | |
# Store linear velocity data | |
- dvxlist.append(self.vel[i,0]*velarrowscale) # delta x for arro… | |
- dvylist.append(self.vel[i,2]*velarrowscale) # delta y for arro… | |
+ | |
+ # delta x for arrow end point | |
+ dvxlist.append(self.vel[i,0]*velarrowscale) | |
+ | |
+ # delta y for arrow end point | |
+ dvylist.append(self.vel[i,2]*velarrowscale) | |
if (r_circ > self.radius[i]): | |
- raise Exception("Error, circle radius is larger than the p… | |
+ raise Exception("Error, circle radius is larger than the " | |
+ + "particle radius") | |
if (self.p[i] > pmax): | |
pmax = self.p[i] | |
t@@ -2076,7 +3257,8 @@ class Spherebin: | |
if fh is not None: | |
fh.close() | |
- # Save angular velocity data. The arrow lengths are normalized to max.… | |
+ # Save angular velocity data. The arrow lengths are normalized to max. | |
+ # radius | |
# Output format: x, y, deltax, deltay | |
# gnuplot> plot '-' using 1:2:3:4 with vectors head filled lt 2 | |
filename = '../gnuplot/data/' + self.sid + '-ts-x1x3-arrows.txt' | |
t@@ -2106,9 +3288,8 @@ class Spherebin: | |
if fh is not None: | |
fh.close() | |
- | |
- | |
- # Check whether there are slips between the particles intersecting the… | |
+ # Check whether there are slips between the particles intersecting the | |
+ # plane | |
sxlist = [] | |
sylist = [] | |
dsxlist = [] | |
t@@ -2117,7 +3298,8 @@ class Spherebin: | |
slipvellist = [] # velocity of the slip | |
for i in ilist: | |
- # Loop through other particles, and check whether they are in cont… | |
+ # Loop through other particles, and check whether they are in | |
+ # contact | |
for j in ilist: | |
#if (i < j): | |
if (i != j): | |
t@@ -2145,7 +3327,8 @@ class Spherebin: | |
angvel_i = self.angvel[i,:] | |
angvel_j = self.angvel[j,:] | |
- # Determine the tangential contact surface velocity in… | |
+ # Determine the tangential contact surface velocity in | |
+ # the x,z plane | |
dot_delta = (vel_i - vel_j) \ | |
+ r_i * numpy.cross(n_ij, angvel_i) \ | |
+ r_j * numpy.cross(n_ij, angvel_j) | |
t@@ -2164,9 +3347,12 @@ class Spherebin: | |
sylist.append(cpos[2]) | |
dsxlist.append(dot_delta_t[0] * slipscale) | |
dsylist.append(dot_delta_t[2] * slipscale) | |
- #anglelist.append(math.degrees(math.atan(dot_delta… | |
- anglelist.append(math.atan(dot_delta_t[2]/dot_delt… | |
- slipvellist.append(numpy.sqrt(dot_delta_t.dot(dot_… | |
+ #anglelist.append(math.degrees(\ | |
+ #math.atan(dot_delta_t[2]/dot_delta_t[0]))) | |
+ anglelist.append(\ | |
+ math.atan(dot_delta_t[2]/dot_delta_t[0])) | |
+ slipvellist.append(\ | |
+ numpy.sqrt(dot_delta_t.dot(dot_delta_t))) | |
# Write slip lines to text file | |
t@@ -2184,38 +3370,56 @@ class Spherebin: | |
# Plot thinsection with gnuplot script | |
gamma = self.shearstrain() | |
- subprocess.call("""cd ../gnuplot/scripts && gnuplot -e "sid='{}'; gamm… | |
- self.sid, self.shearstrain(), self.origo[0], self.L[0], self.o… | |
+ subprocess.call('''cd ../gnuplot/scripts && gnuplot -e "sid='{}'; ''' \ | |
+ + '''gamma='{:.4}'; xmin='{}'; xmax='{}'; ymin='{}'; ''' \ | |
+ + '''ymax='{}'" plotts.gp'''.format(\ | |
+ self.sid, self.shearstrain(), self.origo[0], self.L[0], \ | |
+ self.origo[2], self.L[2]), shell=True) | |
# Find all particles who have a slip velocity higher than slipvel | |
slipvellimit = 0.01 | |
slipvels = numpy.nonzero(numpy.array(slipvellist) > slipvellimit) | |
- | |
# Bin slip angle data for histogram | |
binno = 36/2 | |
- hist_ang, bins_ang = numpy.histogram(numpy.array(anglelist)[slipvels],… | |
+ hist_ang, bins_ang = numpy.histogram(numpy.array(anglelist)[slipvels],\ | |
+ bins=binno, density=False) | |
center_ang = (bins_ang[:-1] + bins_ang[1:]) / 2.0 | |
center_ang_mirr = numpy.concatenate((center_ang, center_ang + math.pi)) | |
hist_ang_mirr = numpy.tile(hist_ang, 2) | |
# Write slip angles to text file | |
- #numpy.savetxt(self.sid + '-ts-x1x3-slipangles.txt', zip(center_ang, h… | |
+ #numpy.savetxt(self.sid + '-ts-x1x3-slipangles.txt', zip(center_ang,\ | |
+ #hist_ang), fmt="%f\t%f") | |
fig = plt.figure() | |
ax = fig.add_subplot(111, polar=True) | |
ax.bar(center_ang_mirr, hist_ang_mirr, width=30.0/180.0) | |
- fig.savefig('../img_out/' + self.sid + '-ts-x1x3-slipangles.png') | |
+ fig.savefig('../img_out/' + self.sid + '-ts-x1x3-slipangles.' + | |
+ graphics_format) | |
fig.clf() | |
- def plotFluidDensitiesY(self, y = -1, imgformat = 'png'): | |
+ def plotFluidPressuresY(self, y = -1, graphics_format = 'png'): | |
+ ''' | |
+ Plot fluid pressures in a plane normal to the second axis. | |
+ The plot is saved in the current folder with the format | |
+ 'p_f-<simulation id>-y<y value>.<graphics_format>'. | |
+ | |
+ :param y: Plot pressures in fluid cells with these y axis values. If | |
+ this value is -1, the center y position is used. | |
+ :type y: int | |
+ :param graphics_format: Save the plot in this format | |
+ :type graphics_format: str | |
+ | |
+ See also: :func:`writeFluidVTK()` and :func:`plotFluidPressuresZ()` | |
+ ''' | |
if (y == -1): | |
y = self.num[1]/2 | |
plt.figure(figsize=[8,8]) | |
- plt.title("Fluid densities") | |
+ plt.title('Fluid pressures') | |
imgplt = plt.imshow(self.f_rho[:,y,:].T, origin='lower') | |
imgplt.set_interpolation('nearest') | |
#imgplt.set_interpolation('bicubic') | |
t@@ -2223,16 +3427,29 @@ class Spherebin: | |
plt.xlabel('$x_1$') | |
plt.ylabel('$x_3$') | |
plt.colorbar() | |
- plt.savefig('f_rho-' + self.sid + \ | |
- '-y' + str(y) + '.' + imgformat, transparent=False) | |
+ plt.savefig('p_f-' + self.sid + \ | |
+ '-y' + str(y) + '.' + graphics_format, transparent=False) | |
+ | |
+ def plotFluidPressuresZ(self, z = -1, graphics_format = 'png'): | |
+ ''' | |
+ Plot fluid pressures in a plane normal to the third axis. | |
+ The plot is saved in the current folder with the format | |
+ 'p_f-<simulation id>-z<z value>.<graphics_format>'. | |
- def plotFluidDensitiesZ(self, z = -1, imgformat = 'png'): | |
+ :param z: Plot pressures in fluid cells with these z axis values. If | |
+ this value is -1, the center z position is used. | |
+ :type z: int | |
+ :param graphics_format: Save the plot in this format | |
+ :type graphics_format: str | |
+ | |
+ See also: :func:`writeFluidVTK()` and :func:`plotFluidPressuresY()` | |
+ ''' | |
if (z == -1): | |
z = self.num[2]/2 | |
plt.figure(figsize=[8,8]) | |
- plt.title("Fluid densities") | |
+ plt.title('Fluid pressures') | |
imgplt = plt.imshow(self.f_rho[:,:,z].T, origin='lower') | |
imgplt.set_interpolation('nearest') | |
#imgplt.set_interpolation('bicubic') | |
t@@ -2240,15 +3457,28 @@ class Spherebin: | |
plt.xlabel('$x_1$') | |
plt.ylabel('$x_2$') | |
plt.colorbar() | |
- plt.savefig('f_rho-' + self.sid + \ | |
- '-z' + str(z) + '.' + imgformat, transparent=False) | |
+ plt.savefig('p_f-' + self.sid + \ | |
+ '-z' + str(z) + '.' + graphics_format, transparent=False) | |
+ | |
+ def plotFluidVelocitiesY(self, y = -1, graphics_format = 'png'): | |
+ ''' | |
+ Plot fluid velocities in a plane normal to the second axis. | |
+ The plot is saved in the current folder with the format | |
+ 'v_f-<simulation id>-z<z value>.<graphics_format>'. | |
- def plotFluidVelocitiesY(self, y = -1, imgformat = 'png'): | |
+ :param y: Plot velocities in fluid cells with these y axis values. If | |
+ this value is -1, the center y position is used. | |
+ :type y: int | |
+ :param graphics_format: Save the plot in this format | |
+ :type graphics_format: str | |
+ | |
+ See also: :func:`writeFluidVTK()` and :func:`plotFluidVelocitiesZ()` | |
+ ''' | |
if (y == -1): | |
y = self.num[1]/2 | |
- plt.title("Fluid velocities") | |
+ plt.title('Fluid velocities') | |
plt.figure(figsize=[8,8]) | |
plt.subplot(131) | |
t@@ -2281,10 +3511,23 @@ class Spherebin: | |
plt.ylabel('$x_3$') | |
plt.colorbar(orientation = 'horizontal') | |
- plt.savefig('f_v-' + self.sid + \ | |
- '-y' + str(y) + '.' + imgformat, transparent=False) | |
+ plt.savefig('v_f-' + self.sid + \ | |
+ '-y' + str(y) + '.' + graphics_format, transparent=False) | |
- def plotFluidVelocitiesZ(self, z = -1, imgformat = 'png'): | |
+ def plotFluidVelocitiesZ(self, z = -1, graphics_format = 'png'): | |
+ ''' | |
+ Plot fluid velocities in a plane normal to the third axis. | |
+ The plot is saved in the current folder with the format | |
+ 'v_f-<simulation id>-z<z value>.<graphics_format>'. | |
+ | |
+ :param z: Plot velocities in fluid cells with these z axis values. If | |
+ this value is -1, the center z position is used. | |
+ :type z: int | |
+ :param graphics_format: Save the plot in this format | |
+ :type graphics_format: str | |
+ | |
+ See also: :func:`writeFluidVTK()` and :func:`plotFluidVelocitiesY()` | |
+ ''' | |
if (z == -1): | |
z = self.num[2]/2 | |
t@@ -2322,95 +3565,352 @@ class Spherebin: | |
plt.ylabel('$x_2$') | |
plt.colorbar(orientation = 'horizontal') | |
- plt.savefig('f_v-' + self.sid + \ | |
- '-z' + str(z) + '.' + imgformat, transparent=False) | |
+ plt.savefig('v_f-' + self.sid + \ | |
+ '-z' + str(z) + '.' + graphics_format, transparent=False) | |
- def plotFluidPorositiesY(self, iteration = -1, y = -1, outformat='png'): | |
- ''' Plot the porosity values from the simulation. If iteration is -1 | |
- (default value), the last output file will be shown. If the y value is | |
- -1, the center x,z plane will be rendered ''' | |
+ def plotFluidDiffAdvPresZ(self, graphics_format = 'png'): | |
+ ''' | |
+ Compare contributions to the velocity from diffusion and advection, | |
+ assuming the flow is 1D along the z-axis, phi = 1, and dphi = 0. This | |
+ solution is analog to the predicted velocity and not constrained by the | |
+ conservation of mass. The plot is saved in the output folder with the | |
+ name format '<simulation id>-diff_adv-t=<current time>s-mu=<dynamic | |
+ viscosity>Pa-s.<graphics_format>'. | |
+ | |
+ :param graphics_format: Save the plot in this format | |
+ :type graphics_format: str | |
+ ''' | |
- phi = numpy.loadtxt('../output/{}.d_phi.output{:0=5}.bin'.format(self.… | |
+ # The v_z values are read from self.v_f[0,0,:,2] | |
+ dz = self.L[2]/self.num[2] | |
+ rho = self.rho_f | |
- if (y == -1): | |
- y = self.num[2]/2 | |
+ # Central difference gradients | |
+ dvz_dz = (self.v_f[0,0,1:,2] - self.v_f[0,0,:-1,2])/(2.0*dz) | |
+ dvzvz_dz = (self.v_f[0,0,1:,2]**2 - self.v_f[0,0,:-1,2]**2)/(2.0*dz) | |
- plt.figure(figsize=[8,8]) | |
- imgplt = plt.imshow(phi[:,y,:].T, origin='lower') | |
- imgplt.set_interpolation('nearest') | |
- #imgplt.set_interpolation('bicubic') | |
- #imgplt.set_cmap('hot') | |
- plt.xlabel('$i_x$') | |
+ # Diffusive contribution to velocity change | |
+ dvz_diff = 2.0*self.mu/rho*dvz_dz*self.time_dt | |
+ | |
+ # Advective contribution to velocity change | |
+ dvz_adv = dvzvz_dz*self.time_dt | |
+ | |
+ # Pressure gradient | |
+ dp_dz = (self.p_f[0,0,1:] - self.p_f[0,0,:-1])/(2.0*dz) | |
+ | |
+ cellno = numpy.arange(1, self.num[2]) | |
+ | |
+ fig = plt.figure() | |
+ titlesize=12 | |
+ | |
+ plt.subplot(1,3,1) | |
+ plt.title('Pressure', fontsize=titlesize) | |
plt.ylabel('$i_z$') | |
- plt.colorbar() | |
- plt.savefig(self.sid + '-porosities.output{1:0=5}'.format(iteration)\ | |
- + '-y' + str(y) + '.' + imgformat, transparent=False) | |
+ plt.xlabel('$p_z$') | |
+ plt.plot(self.p_f[0,0,:], numpy.arange(self.num[2])) | |
+ plt.grid() | |
+ | |
+ plt.subplot(1,3,2) | |
+ plt.title('Pressure gradient', fontsize=titlesize) | |
+ plt.ylabel('$i_z$') | |
+ plt.xlabel('$\Delta p_z$') | |
+ plt.plot(dp_dz, cellno) | |
+ plt.grid() | |
+ | |
+ plt.subplot(1,3,3) | |
+ plt.title('Velocity prediction terms', fontsize=titlesize) | |
+ plt.ylabel('$i_z$') | |
+ plt.xlabel('$\Delta v_z$') | |
+ plt.plot(dvz_diff, cellno, label='Diffusion') | |
+ plt.plot(dvz_adv, cellno, label='Advection') | |
+ plt.plot(dvz_diff+dvz_adv, cellno, '--', label='Sum') | |
+ leg = plt.legend(loc='best', prop={'size':8}) | |
+ leg.get_frame().set_alpha(0.5) | |
+ plt.grid() | |
+ | |
+ plt.tight_layout() | |
+ plt.savefig('../output/{}-diff_adv-t={:.2e}s-mu={:.2e}Pa-s.{}'.format(\ | |
+ self.sid, self.time_current[0], self.mu[0], graphics_format)) | |
+ plt.clf() | |
+ plt.close(fig) | |
+ | |
+ def plotConvergence(self, graphics_format='png'): | |
+ ''' | |
+ Plot the convergence evolution in the CFD solver. The plot is saved | |
+ in the output folder with the file name | |
+ '<simulation id>-conv.<graphics_format>'. | |
+ | |
+ :param graphics_format: Save the plot in this format | |
+ :type graphics_format: str | |
+ ''' | |
+ fig = plt.figure() | |
+ conv = numpy.loadtxt('../output/' + self.sid + '-conv.log') | |
+ | |
+ plt.title('Convergence evolution in CFD solver in "' + self.sid + '"') | |
+ plt.xlabel('Time step') | |
+ plt.ylabel('Jacobi iterations') | |
+ plt.plot(conv[:,0], conv[:,1]) | |
+ plt.grid() | |
+ plt.savefig('../output/' + self.sid + '-conv.' + graphics_format) | |
+ plt.clf() | |
+ plt.close(fig) | |
+ | |
+ def setFluidPressureModulation(self, A, f, phi=0.0): | |
+ ''' | |
+ Set the parameters for the sine wave modulating the fluid pressures | |
+ at the top boundary. Note that a cos-wave is obtained with phi=pi/2. | |
+ :param A: Fluctuation amplitude [Pa] | |
+ :type A: float | |
+ :param f: Fluctuation frequency [Hz] | |
+ :type f: float | |
+ :param phi: Fluctuation phase shift (default=0.0) | |
+ :type phi: float | |
+ See also: :func:`disableFluidPressureModulation()` | |
+ ''' | |
+ self.p_mod_A[0] = A | |
+ self.p_mod_f[0] = f | |
+ self.p_mod_phi[0] = phi | |
-def convert(graphicsformat = "png", | |
- folder = "../img_out"): | |
- 'Converts all PPM images in img_out to graphicsformat, using ImageMagick' | |
+ def disableFluidPressureModulation(self): | |
+ ''' | |
+ Set the parameters for the sine wave modulating the fluid pressures | |
+ at the top boundary to zero. | |
+ | |
+ See also: :func:`setFluidPressureModulation()` | |
+ ''' | |
+ self.setFluidPressureModulation(A = 0.0, f = 0.0) | |
- #quiet = " > /dev/null" | |
- quiet = "" | |
+ def plotPrescribedFluidPressures(self, graphics_format='png'): | |
+ ''' | |
+ Plot the prescribed fluid pressures through time that may be | |
+ modulated through the class parameters p_mod_A, p_mod_f, and p_mod_phi. | |
+ The plot is saved in the output folder with the file name | |
+ '<simulation id>-pres.<graphics_format>'. | |
+ ''' | |
+ | |
+ fig = plt.figure() | |
+ conv = numpy.loadtxt('../output/' + self.sid + '-conv.log') | |
+ | |
+ plt.title('Prescribed fluid pressures at the top in "' + self.sid + '"… | |
+ plt.xlabel('Time [s]') | |
+ plt.ylabel('Pressure [Pa]') | |
+ t = numpy.linspace(0,self.time_total,\ | |
+ self.time_total/self.time_file_dt) | |
+ p = self.p_f[0,0,-1] + \ | |
+ self.p_mod_A * \ | |
+ numpy.sin(2.0*numpy.pi*self.p_mod_f*t + self.p_mod_phi) | |
+ plt.plot(t, p, '.-') | |
+ plt.grid() | |
+ plt.savefig('../output/' + self.sid + '-pres.' + graphics_format) | |
+ plt.clf() | |
+ plt.close(fig) | |
+ | |
+ def acceleration(self, idx=-1): | |
+ ''' | |
+ Returns the acceleration of one or more particles, selected by their | |
+ index. If the index is equal to -1 (default value), all accelerations | |
+ are returned. | |
+ | |
+ :param idx: Index or index range of particles | |
+ :type idx: int, list or numpy.array | |
+ :returns: n-by-3 matrix of acceleration(s) | |
+ :return type: numpy.array | |
+ ''' | |
+ if idx == -1: | |
+ idx = range(self.np) | |
+ return self.force[idx,:]/(V_sphere(self.radius[idx])*self.rho[0]) + \ | |
+ self.g | |
+ | |
+ def setGamma(self, gamma): | |
+ ''' | |
+ Gamma is a fluid solver parameter, used for smoothing the pressure | |
+ values. The epsilon (pressure) values are smoothed by including the | |
+ average epsilon value of the six closest (face) neighbor cells. This | |
+ parameter should be in the range [0.0;1.0[. The higher the value, the | |
+ more averaging is introduced. A value of 0.0 disables all averaging. | |
+ | |
+ The default and recommended value is 0.5. | |
+ | |
+ :param theta: The smoothing parameter value | |
+ :type theta: float | |
+ ''' | |
+ self.gamma[0] = gamma | |
+ | |
+ def setTheta(self, theta): | |
+ ''' | |
+ Theta is a fluid solver under-relaxation parameter, used in solution of | |
+ Poisson equation. The value should be within the range ]0.0;1.0]. At a | |
+ value of 1.0, the new estimate of epsilon values is used exclusively. … | |
+ lower values, a linear interpolation between new and old values is use… | |
+ The solution typically converges faster with a value of 1.0, but | |
+ instabilities may be avoided with lower values. | |
+ | |
+ The default and recommended value is 1.0. | |
+ | |
+ :param theta: The under-relaxation parameter value | |
+ :type theta: float | |
+ ''' | |
+ self.theta[0] = theta | |
+ | |
+ | |
+ def setBeta(self, beta): | |
+ ''' | |
+ Beta is a fluid solver parameter, used in velocity prediction and | |
+ pressure iteration 1.0: Use old pressures for fluid velocity prediction | |
+ (see Langtangen et al. 2002) 0.0: Do not use old pressures for fluid | |
+ velocity prediction (Chorin's original projection method, see Chorin | |
+ (1968) and "Projection method (fluid dynamics)" page on Wikipedia. The | |
+ best results precision and performance-wise are obtained by using a be… | |
+ of 0 and a low tolerance criteria value. | |
+ | |
+ The default and recommended value is 0.0. | |
+ ''' | |
+ self.beta[0] = beta | |
+ | |
+ def setTolerance(self, tolerance): | |
+ ''' | |
+ A fluid solver parameter, the value of the tolerance parameter denotes | |
+ the required value of the maximum normalized residual for the fluid | |
+ solver. | |
+ | |
+ The default and recommended value is 1.0e-8. | |
+ | |
+ :param tolerance: The tolerance criteria for the maximal normalized | |
+ residual | |
+ :type tolerance: float | |
+ ''' | |
+ self.tolerance[0] = tolerance | |
+ | |
+ def setMaxIterations(self, maxiter): | |
+ ''' | |
+ A fluid solver parameter, the value of the maxiter parameter denotes t… | |
+ maximal allowed number of fluid solver iterations before ending the | |
+ fluid solver loop prematurely. The residual values are at that point n… | |
+ fulfilling the tolerance criteria. The parameter is included to avoid | |
+ infinite hangs. | |
+ | |
+ The default and recommended value is 1e4. | |
+ | |
+ :param maxiter: The maximum number of Jacobi iterations in the fluid | |
+ solver | |
+ :type maxiter: int | |
+ ''' | |
+ self.maxiter[0] = maxiter | |
+ | |
+ | |
+def convert(graphics_format = 'png', folder = '../img_out'): | |
+ ''' | |
+ Converts all PPM images in img_out to graphics_format using Imagemagick. A… | |
+ PPM images are subsequently removed. | |
+ | |
+ :param graphics_format: Convert the images to this format | |
+ :type graphics_format: str | |
+ :param folder: The folder containing the PPM images to convert | |
+ :type folder: str | |
+ ''' | |
+ | |
+ #quiet = ' > /dev/null' | |
+ quiet = '' | |
# Convert images | |
- subprocess.call("for F in " + folder \ | |
- + "/*.ppm ; do BASE=`basename $F .ppm`; convert $F " \ | |
- + folder + "/$BASE." + graphicsformat + " " \ | |
- + quiet + " ; done", shell=True) | |
+ subprocess.call('for F in ' + folder \ | |
+ + '/*.ppm ; do BASE=`basename $F .ppm`; convert $F ' \ | |
+ + folder + '/$BASE.' + graphics_format + ' ' \ | |
+ + quiet + ' ; done', shell=True) | |
# Remove PPM files | |
- subprocess.call("rm " + folder + "/*.ppm", shell=True) | |
- | |
+ subprocess.call('rm ' + folder + '/*.ppm', shell=True) | |
def render(binary, | |
- method = "pres", | |
+ method = 'pres', | |
max_val = 1e3, | |
lower_cutoff = 0.0, | |
- graphicsformat = "png", | |
+ graphics_format = 'png', | |
verbose=True): | |
- 'Render target binary using the sphere raytracer.' | |
- | |
- quiet = "" | |
+ ''' | |
+ Render target binary using the ``sphere`` raytracer. | |
+ | |
+ :param method: The color visualization method to use for the particles. | |
+ Possible values are: 'normal': color all particles with the same | |
+ color, 'pres': color by pressure, 'vel': color by translational | |
+ velocity, 'angvel': color by rotational velocity, 'xdisp': color by | |
+ total displacement along the x-axis, 'angpos': color by angular | |
+ position. | |
+ :type method: str | |
+ :param max_val: The maximum value of the color bar | |
+ :type max_val: float | |
+ :param lower_cutoff: Do not render particles with a value below this | |
+ value, of the field selected by ``method`` | |
+ :type lower_cutoff: float | |
+ :param graphics_format: Convert the PPM images generated by the ray | |
+ tracer to this image format using Imagemagick | |
+ :type graphics_format: str | |
+ :param verbose: Show verbose information during ray tracing | |
+ :type verbose: bool | |
+ ''' | |
+ quiet = '' | |
if (verbose == False): | |
- quiet = "-q" | |
+ quiet = '-q' | |
# Render images using sphere raytracer | |
- if (method == "normal"): | |
- subprocess.call("cd .. ; ./sphere " + quiet + \ | |
- " --render " + binary, shell=True) | |
+ if (method == 'normal'): | |
+ subprocess.call('cd .. ; ./sphere ' + quiet + \ | |
+ ' --render ' + binary, shell=True) | |
else : | |
- subprocess.call("cd .. ; ./sphere " + quiet + \ | |
- " --method " + method + " {}".format(max_val) + \ | |
- " -l {}".format(lower_cutoff) + \ | |
- " --render " + binary, shell=True) | |
+ subprocess.call('cd .. ; ./sphere ' + quiet + \ | |
+ ' --method ' + method + ' {}'.format(max_val) + \ | |
+ ' -l {}'.format(lower_cutoff) + \ | |
+ ' --render ' + binary, shell=True) | |
# Convert images to compressed format | |
- convert() | |
- | |
+ convert(graphics_format) | |
def video(project, | |
- out_folder = "./", | |
- video_format = "mp4", | |
- graphics_folder = "../img_out/", | |
- graphics_format = "png", | |
+ out_folder = './', | |
+ video_format = 'mp4', | |
+ graphics_folder = '../img_out/', | |
+ graphics_format = 'png', | |
fps = 25, | |
qscale = 1, | |
bitrate = 1800, | |
verbose = False): | |
- 'Use ffmpeg to combine images to animation. All images should be rendered … | |
- | |
- # Possible loglevels: quiet, panic, fatal, error, warning, info, verbose, … | |
- loglevel = "info" # verbose = True | |
+ ''' | |
+ Uses ffmpeg to combine images to animation. All images should be | |
+ rendered beforehand using func:`render()`. | |
+ | |
+ :param project: The simulation id of the project to render | |
+ :type project: str | |
+ :param out_folder: The output folder for the video file | |
+ :type out_folder: str | |
+ :param video_format: The format of the output video | |
+ :type video_format: str | |
+ :param graphics_folder: The folder containing the rendered images | |
+ :type graphics_folder: str | |
+ :param graphics_format: The format of the rendered images | |
+ :type graphics_format: str | |
+ :param fps: The number of frames per second to use in the video | |
+ :type fps: int | |
+ :param qscale: The output video quality, in ]0;1] | |
+ :type qscale: float | |
+ :param bitrate: The bitrate to use in the output video | |
+ :type bitrate: int | |
+ :param verbose: Show ffmpeg output | |
+ :type verbose: bool | |
+ ''' | |
+ # Possible loglevels: | |
+ # quiet, panic, fatal, error, warning, info, verbose, debug | |
+ loglevel = 'info' # verbose = True | |
if (verbose == False): | |
- loglevel = "error" | |
+ loglevel = 'error' | |
subprocess.call(\ | |
- "ffmpeg -qscale {0} -r {1} -b {2} -y ".format(qscale, fps, bitrate… | |
- + "-loglevel " + loglevel + " " \ | |
- + "-i " + graphics_folder + project + ".output%05d." + graphics_fo… | |
- + out_folder + "/" + project + "." + video_format, shell=True) | |
+ 'ffmpeg -qscale {0} -r {1} -b {2} -y '.format(\ | |
+ qscale, fps, bitrate) \ | |
+ + '-loglevel ' + loglevel + ' ' \ | |
+ + '-i ' + graphics_folder + project + '.output%05d.' \ | |
+ + graphics_format + ' ' \ | |
+ + out_folder + '/' + project + '.' + video_format, shell=True) | |
def thinsectionVideo(project, | |
out_folder = "./", | |
t@@ -2419,13 +3919,32 @@ def thinsectionVideo(project, | |
qscale = 1, | |
bitrate = 1800, | |
verbose = False): | |
+ ''' | |
+ Uses ffmpeg to combine thin section images to an animation. This function | |
+ will implicity render the thin section images beforehand. | |
+ | |
+ :param project: The simulation id of the project to render | |
+ :type project: str | |
+ :param out_folder: The output folder for the video file | |
+ :type out_folder: str | |
+ :param video_format: The format of the output video | |
+ :type video_format: str | |
+ :param fps: The number of frames per second to use in the video | |
+ :type fps: int | |
+ :param qscale: The output video quality, in ]0;1] | |
+ :type qscale: float | |
+ :param bitrate: The bitrate to use in the output video | |
+ :type bitrate: int | |
+ :param verbose: Show ffmpeg output | |
+ :type verbose: bool | |
+ ''' | |
''' Use ffmpeg to combine thin section images to animation. | |
This function will start off by rendering the images. | |
''' | |
# Render thin section images (png) | |
lastfile = status(project) | |
- sb = Spherebin() | |
+ sb = sim(fluid = self.fluid) | |
for i in range(lastfile+1): | |
fn = "../output/{0}.output{1:0=5}.bin".format(project, i) | |
sb.sid = project + ".output{:0=5}".format(i) | |
t@@ -2433,13 +3952,15 @@ def thinsectionVideo(project, | |
sb.thinsection_x1x3(cbmax = sb.w_devs[0]*4.0) | |
# Combine images to animation | |
- # Possible loglevels: quiet, panic, fatal, error, warning, info, verbose, … | |
+ # Possible loglevels: | |
+ # quiet, panic, fatal, error, warning, info, verbose, debug | |
loglevel = "info" # verbose = True | |
if (verbose == False): | |
loglevel = "error" | |
subprocess.call(\ | |
- "ffmpeg -qscale {0} -r {1} -b {2} -y ".format(qscale, fps, bitrate… | |
+ "ffmpeg -qscale {0} -r {1} -b {2} -y ".format(\ | |
+ qscale, fps, bitrate) \ | |
+ "-loglevel " + loglevel + " " \ | |
+ "-i ../img_out/" + project + ".output%05d-ts-x1x3.png " \ | |
+ "-vf 'crop=((in_w/2)*2):((in_h/2)*2)' " \ | |
t@@ -2447,18 +3968,27 @@ def thinsectionVideo(project, | |
shell=True) | |
def visualize(project, method = 'energy', savefig = True, outformat = 'png'): | |
- """ Visualize output from the target project, | |
- where the temporal progress is of interest. | |
- """ | |
+ ''' | |
+ Visualize output from the target project, where the temporal progress is of | |
+ interest. The output will be saved in the current folder with a name | |
+ combining the simulation id of the project, and the visualization method. | |
+ | |
+ :param project: The simulation id of the project to render | |
+ :type project: str | |
+ :param method: The type of plot to render. Possible values are 'energy', | |
+ 'walls', 'triaxial' and 'shear' | |
+ :type method: str | |
+ :param savefig: Save the image instead of showing it on screen | |
+ :type savefig: bool | |
+ :param outformat: The output format of the plot data. This can be an image | |
+ format, or in text ('txt'). | |
+ ''' | |
lastfile = status(project) | |
### Plotting | |
if (outformat != 'txt'): | |
fig = plt.figure(figsize=(15,10),dpi=300) | |
- #figtitle = "{0}, simulation {1}".format(method, project) | |
- #fig.text(0.5,0.95,figtitle,horizontalalignment='center',fontpropertie… | |
- | |
if method == 'energy': | |
t@@ -2474,7 +4004,7 @@ def visualize(project, method = 'energy', savefig = True… | |
Esum = numpy.zeros(lastfile+1) | |
# Read energy values from project binaries | |
- sb = Spherebin() | |
+ sb = sim(fluid = self.fluid) | |
for i in range(lastfile+1): | |
fn = "../output/{0}.output{1:0=5}.bin".format(project, i) | |
sb.readbin(fn, verbose = False) | |
t@@ -2563,7 +4093,8 @@ def visualize(project, method = 'energy', savefig = True… | |
ax10.plot(t, Epot, '+-g') | |
ax10.plot(t, Ekin, '+-b') | |
ax10.plot(t, Erot, '+-r') | |
- ax10.legend(('$\sum E_{pot}$','$\sum E_{kin}$','$\sum E_{rot}$'), … | |
+ ax10.legend(('$\sum E_{pot}$','$\sum E_{kin}$','$\sum E_{rot}$'),\ | |
+ 'upper right', shadow=True) | |
ax10.grid() | |
fig.tight_layout() | |
t@@ -2571,17 +4102,21 @@ def visualize(project, method = 'energy', savefig = Tr… | |
elif method == 'walls': | |
# Read energy values from project binaries | |
- sb = Spherebin() | |
+ sb = sim(fluid = self.fluid) | |
for i in range(lastfile+1): | |
fn = "../output/{0}.output{1:0=5}.bin".format(project, i) | |
sb.readbin(fn, verbose = False) | |
# Allocate arrays on first run | |
if (i == 0): | |
- wforce = numpy.zeros((lastfile+1)*sb.nw[0], dtype=numpy.float6… | |
- wvel = numpy.zeros((lastfile+1)*sb.nw[0], dtype=numpy.float6… | |
- wpos = numpy.zeros((lastfile+1)*sb.nw[0], dtype=numpy.float6… | |
- wdevs = numpy.zeros((lastfile+1)*sb.nw[0], dtype=numpy.float6… | |
+ wforce = numpy.zeros((lastfile+1)*sb.nw[0],\ | |
+ dtype=numpy.float64).reshape((lastfile+1), sb.nw[0]) | |
+ wvel = numpy.zeros((lastfile+1)*sb.nw[0],\ | |
+ dtype=numpy.float64).reshape((lastfile+1), sb.nw[0]) | |
+ wpos = numpy.zeros((lastfile+1)*sb.nw[0],\ | |
+ dtype=numpy.float64).reshape((lastfile+1), sb.nw[0]) | |
+ wdevs = numpy.zeros((lastfile+1)*sb.nw[0],\ | |
+ dtype=numpy.float64).reshape((lastfile+1), sb.nw[0]) | |
maxpos = numpy.zeros((lastfile+1), dtype=numpy.float64) | |
logstress = numpy.zeros((lastfile+1), dtype=numpy.float64) | |
voidratio = numpy.zeros((lastfile+1), dtype=numpy.float64) | |
t@@ -2639,12 +4174,13 @@ def visualize(project, method = 'energy', savefig = Tr… | |
elif method == 'triaxial': | |
# Read energy values from project binaries | |
- sb = Spherebin() | |
+ sb = sim(fluid = self.fluid) | |
for i in range(lastfile+1): | |
fn = "../output/{0}.output{1:0=5}.bin".format(project, i) | |
sb.readbin(fn, verbose = False) | |
- vol = (sb.w_x[0]-sb.origo[2]) * (sb.w_x[1]-sb.w_x[2]) * (sb.w_x[3]… | |
+ vol = (sb.w_x[0]-sb.origo[2]) * (sb.w_x[1]-sb.w_x[2]) \ | |
+ * (sb.w_x[3] - sb.w_x[4]) | |
# Allocate arrays on first run | |
if (i == 0): | |
t@@ -2692,7 +4228,7 @@ def visualize(project, method = 'energy', savefig = True… | |
elif method == 'shear': | |
- sb = Spherebin() | |
+ sb = sim(fluid = self.fluid) | |
# Read stress values from project binaries | |
for i in range(lastfile+1): | |
fn = "../output/{0}.output{1:0=5}.bin".format(project, i) | |
t@@ -2700,13 +4236,24 @@ def visualize(project, method = 'energy', savefig = Tr… | |
# First iteration: Allocate arrays and find constant values | |
if (i == 0): | |
- xdisp = numpy.zeros(lastfile+1, dtype=numpy.float64) # Sh… | |
- sigma_eff = numpy.zeros(lastfile+1, dtype=numpy.float64) # No… | |
- sigma_def = numpy.zeros(lastfile+1, dtype=numpy.float64) # No… | |
- tau = numpy.zeros(lastfile+1, dtype=numpy.float64) # Sh… | |
- dilation = numpy.zeros(lastfile+1, dtype=numpy.float64) # Up… | |
+ # Shear displacement | |
+ xdisp = numpy.zeros(lastfile+1, dtype=numpy.float64) | |
+ | |
+ # Normal stress | |
+ sigma_eff = numpy.zeros(lastfile+1, dtype=numpy.float64) | |
+ | |
+ # Normal stress | |
+ sigma_def = numpy.zeros(lastfile+1, dtype=numpy.float64) | |
+ | |
+ # Shear stress | |
+ tau = numpy.zeros(lastfile+1, dtype=numpy.float64) | |
+ | |
+ # Upper wall position | |
+ dilation = numpy.zeros(lastfile+1, dtype=numpy.float64) | |
+ | |
+ # Upper wall position | |
tau_u = 0.0 # Peak shear stress | |
- tau_u_shearstrain = 0.0 # Shear strain value of peak shear str… | |
+ tau_u_shearstrain = 0.0 # Shear strain value of peak sh. stress | |
fixvel = numpy.nonzero(sb.fixvel > 0.0) | |
#fixvel_upper = numpy.nonzero(sb.vel[fixvel,0] > 0.0) | |
t@@ -2723,8 +4270,8 @@ def visualize(project, method = 'energy', savefig = True… | |
xdisp[i] = xdisp[i-1] + sb.time_file_dt[0] * shearvel | |
sigma_eff[i] = sb.w_force[0] / A | |
sigma_def[i] = sb.w_devs[0] | |
- dilation[i] = sb.w_x[0] - w_x0 # dilation in meters | |
- #dilation[i] = (sb.w_x[0] - w_x0)/w_x0 * 100.0 # dilation in per… | |
+ dilation[i] = sb.w_x[0] - w_x0 # dilation in meters | |
+ #dilation[i] = (sb.w_x[0] - w_x0)/w_x0 * 100.0 # dilation in perce… | |
# Test if this was the max. shear stress | |
if (tau[i] > tau_u): | |
t@@ -2734,7 +4281,8 @@ def visualize(project, method = 'energy', savefig = True… | |
# Plot stresses | |
if (outformat != 'txt'): | |
- shearinfo = "$\\tau_u$ = {:.3} Pa at $\gamma$ = {:.3}".format(tau_… | |
+ shearinfo = "$\\tau_u$ = {:.3} Pa at $\gamma$ = {:.3}".format(\ | |
+ tau_u, tau_u_shearstrain) | |
fig.text(0.5, 0.03, shearinfo, horizontalalignment='center', | |
fontproperties=FontProperties(size=14)) | |
ax1 = plt.subplot2grid((1, 2), (0, 0)) | |
t@@ -2762,7 +4310,8 @@ def visualize(project, method = 'energy', savefig = True… | |
fh = open(filename, "w") | |
L = sb.L[2] - sb.origo[2] # Initial height | |
for i in range(lastfile+1): | |
- # format: shear distance [mm], sigma [kPa], tau [kPa], Dil… | |
+ # format: shear distance [mm], sigma [kPa], tau [kPa], | |
+ # Dilation [%] | |
fh.write("{0}\t{1}\t{2}\t{3}\n".format(xdisp[i], | |
sigma_eff[i]/1000.0, | |
tau[i]/1000.0, | |
t@@ -2785,27 +4334,67 @@ def visualize(project, method = 'energy', savefig = Tr… | |
plt.show() | |
def run(binary, verbose=True, hideinputfile=False): | |
- 'Execute sphere with target binary as input' | |
+ ''' | |
+ Execute ``sphere`` with target binary file as input. | |
+ | |
+ :param binary: Input file for ``sphere`` | |
+ :type binary: str | |
+ :param verbose: Show ``sphere`` output | |
+ :type verbose: bool | |
+ :param hideinputfile: Hide the input file | |
+ :type hideinputfile: bool | |
+ ''' | |
- quiet = "" | |
- stdout = "" | |
+ quiet = '' | |
+ stdout = '' | |
if (verbose == False): | |
- quiet = "-q" | |
+ quiet = '-q' | |
if (hideinputfile == True): | |
- stdout = " > /dev/null" | |
- subprocess.call("cd ..; ./sphere " + quiet + " " + binary + " " + stdout, … | |
+ stdout = ' > /dev/null' | |
+ subprocess.call('cd ..; ./sphere ' + quiet + ' ' + binary + ' ' + stdout, \ | |
+ shell=True) | |
def torqueScriptParallel3(obj1, obj2, obj3, | |
- email="[email protected]", | |
- email_alerts="ae", | |
- walltime="24:00:00", | |
- queue="qfermi", | |
- cudapath="/com/cuda/4.0.17/cuda", | |
- spheredir="/home/adc/code/sphere", | |
- workdir="/scratch"): | |
- ''' Create job script for the Torque queue manager for three binaries, | |
- executed in parallel. | |
- Returns the filename of the script | |
+ email='[email protected]', | |
+ email_alerts='ae', | |
+ walltime='24:00:00', | |
+ queue='qfermi', | |
+ cudapath='/com/cuda/4.0.17/cuda', | |
+ spheredir='/home/adc/code/sphere', | |
+ use_workdir=False, | |
+ workdir='/scratch'): | |
+ ''' | |
+ Create job script for the Torque queue manager for three binaries, | |
+ executed in parallel, ideally on three GPUs. | |
+ | |
+ :param email: The e-mail address that Torque messages should be sent to | |
+ :type email: str | |
+ :param email_alerts: The type of Torque messages to send to the e-mail | |
+ address. The character 'b' causes a mail to be sent when the | |
+ execution begins. The character 'e' causes a mail to be sent when | |
+ the execution ends normally. The character 'a' causes a mail to be | |
+ sent if the execution ends abnormally. The characters can be written | |
+ in any order. | |
+ :type email_alerts: str | |
+ :param walltime: The maximal allowed time for the job, in the format | |
+ 'HH:MM:SS'. | |
+ :type walltime: str | |
+ :param queue: The Torque queue to schedule the job for | |
+ :type queue: str | |
+ :param cudapath: The path of the CUDA library on the cluster compute nodes | |
+ :type cudapath: str | |
+ :param spheredir: The path to the root directory of sphere on the cluster | |
+ :type spheredir: str | |
+ :param use_workdir: Use a different working directory than the sphere fold… | |
+ :type use_workdir: bool | |
+ :param workdir: The working directory during the calculations, if | |
+ `use_workdir=True` | |
+ :type workdir: str | |
+ | |
+ :returns: The filename of the script | |
+ :return type: str | |
+ | |
+ See also :func:`torqueScript()` | |
''' | |
filename = obj1.sid + '_' + obj2.sid + '_' + obj3.sid + '.sh' | |
t@@ -2823,19 +4412,24 @@ def torqueScriptParallel3(obj1, obj2, obj3, | |
fh.write('#PBS -m ' + email_alerts + '\n') | |
fh.write('CUDAPATH=' + cudapath + '\n') | |
fh.write('export PATH=$CUDAPATH/bin:$PATH\n') | |
- fh.write('export LD_LIBRARY_PATH=$CUDAPATH/lib64:$CUDAPATH/lib:$LD_LIB… | |
+ fh.write('export LD_LIBRARY_PATH=$CUDAPATH/lib64') | |
+ fh.write(':$CUDAPATH/lib:$LD_LIBRARY_PATH\n') | |
fh.write('echo "`whoami`@`hostname`"\n') | |
fh.write('echo "Start at `date`"\n') | |
- fh.write('ORIGDIR=' + spheredir + '\n') | |
- fh.write('WORKDIR=' + workdir + "/$PBS_JOBID\n") | |
- fh.write('cp -r $ORIGDIR/* $WORKDIR\n') | |
- fh.write('cd $WORKDIR\n') | |
+ if (use_workdir == True): | |
+ fh.write('ORIGDIR=' + spheredir + '\n') | |
+ fh.write('WORKDIR=' + workdir + "/$PBS_JOBID\n") | |
+ fh.write('cp -r $ORIGDIR/* $WORKDIR\n') | |
+ fh.write('cd $WORKDIR\n') | |
+ else: | |
+ fh.write('cd ' + spheredir + '\n') | |
fh.write('cmake . && make\n') | |
fh.write('./sphere input/' + obj1.sid + '.bin > /dev/null &\n') | |
fh.write('./sphere input/' + obj2.sid + '.bin > /dev/null &\n') | |
fh.write('./sphere input/' + obj3.sid + '.bin > /dev/null &\n') | |
fh.write('wait\n') | |
- fh.write('cp $WORKDIR/output/* $ORIGDIR/output/\n') | |
+ if (use_workdir == True): | |
+ fh.write('cp $WORKDIR/output/* $ORIGDIR/output/\n') | |
fh.write('echo "End at `date`"\n') | |
return filename | |
t@@ -2843,78 +4437,68 @@ def torqueScriptParallel3(obj1, obj2, obj3, | |
if fh is not None: | |
fh.close() | |
- | |
-def torqueScriptSerial3(obj1, obj2, obj3, | |
- email="[email protected]", | |
- email_alerts="ae", | |
- walltime="24:00:00", | |
- queue="qfermi", | |
- cudapath="/com/cuda/4.0.17/cuda", | |
- spheredir="/home/adc/code/sphere", | |
- workdir="/scratch"): | |
- '''Create job script for the Torque queue manager for three binaries | |
- ''' | |
- | |
- filename = self.sid + ".sh" | |
- fh = None | |
- try : | |
- fh = open(filename, "w") | |
- | |
- fh.write('#!/bin/sh\n') | |
- fh.write('#PBS -N ' + obj1.sid + '_' + obj2.sid + '_' + obj3.sid + '\n… | |
- fh.write('#PBS -l nodes=1:ppn=1\n') | |
- fh.write('#PBS -l walltime=' + walltime + '\n') | |
- fh.write('#PBS -q ' + queue + '\n') | |
- fh.write('#PBS -M ' + email + '\n') | |
- fh.write('#PBS -m ' + email_alerts + '\n') | |
- fh.write('CUDAPATH=' + cudapath + '\n') | |
- fh.write('export PATH=$CUDAPATH/bin:$PATH\n') | |
- fh.write('export LD_LIBRARY_PATH=$CUDAPATH/lib64:$CUDAPATH/lib:$LD_LIB… | |
- fh.write('echo "`whoami`@`hostname`"\n') | |
- fh.write('echo "Start at `date`"\n') | |
- fh.write('ORIGDIR=' + spheredir + '\n') | |
- fh.write('WORKDIR=' + workdir + "/$PBS_JOBID\n") | |
- fh.write('cp -r $ORIGDIR/* $WORKDIR\n') | |
- fh.write('cd $WORKDIR\n') | |
- fh.write('cmake . && make\n') | |
- fh.write('./sphere input/' + obj1.sid + '.bin > /dev/null\n') | |
- fh.write('./sphere input/' + obj2.sid + '.bin > /dev/null\n') | |
- fh.write('./sphere input/' + obj3.sid + '.bin > /dev/null\n') | |
- fh.write('cp $WORKDIR/output/* $ORIGDIR/output/\n') | |
- fh.write('echo "End at `date`"\n') | |
- | |
- finally : | |
- if fh is not None: | |
- fh.close() | |
- | |
- | |
- | |
def status(project): | |
- """ Check the status.dat file for the target project, | |
- and return the last file numer. | |
- """ | |
+ ''' | |
+ Check the status.dat file for the target project, and return the last outp… | |
+ file number. | |
+ | |
+ :param project: The simulation id of the target project | |
+ :type project: str | |
+ | |
+ :returns: The last output file written in the simulation calculations | |
+ :return type: int | |
+ ''' | |
fh = None | |
try : | |
filepath = "../output/{0}.status.dat".format(project) | |
- #print(filepath) | |
fh = open(filepath) | |
data = fh.read() | |
- #print(data) | |
return int(data.split()[2]) # Return last file number | |
finally : | |
if fh is not None: | |
fh.close() | |
-def cleanup(spherebin): | |
- 'Remove input/output files and images from simulation' | |
- subprocess.call("rm -f ../input/" + spherebin.sid + ".bin", shell=True) | |
- subprocess.call("rm -f ../output/" + spherebin.sid + ".*.bin", shell=True) | |
- subprocess.call("rm -f ../img_out/" + spherebin.sid + ".*", shell=True) | |
+def cleanup(sim): | |
+ ''' | |
+ Removes the input/output files and images belonging to the object simulati… | |
+ ID from the ``input/``, ``output/`` and ``img_out/`` folders. | |
+ | |
+ :param spherebin: A sim object | |
+ :type spherebin: sim | |
+ ''' | |
+ subprocess.call("rm -f ../input/" + sim.sid + ".bin", shell=True) | |
+ subprocess.call("rm -f ../output/" + sim.sid + ".*.bin", shell=True) | |
+ subprocess.call("rm -f ../img_out/" + sim.sid + ".*", shell=True) | |
+ subprocess.call("rm -f ../output/" + sim.sid + ".status.dat", shell=True) | |
+ subprocess.call("rm -f ../output/" + sim.sid + ".*.vtu", shell=True) | |
+ subprocess.call("rm -f ../output/fluid-" + sim.sid + ".*.vti", shell=True) | |
+ subprocess.call("rm -f ../output/" + sim.sid + "-conv.png", shell=True) | |
+ subprocess.call("rm -f ../output/" + sim.sid + "-conv.log", shell=True) | |
+ | |
+ | |
+def vector_norm(ndvector): | |
+ ''' | |
+ Returns a 1D vector of normalized values. The input array should have | |
+ one row per particle, and three rows; one per Euclidean axis. | |
+ | |
+ :returns: A value of the velocity magnutude per particle | |
+ :return type: numpy.array | |
+ ''' | |
+ | |
+ # Normalized velocities | |
+ v_norm = numpy.empty(ndvector.shape[0]) | |
+ for i in range(ndvector.shape[0]): | |
+ v_norm[i] = numpy.sqrt(ndvector[i,:].dot(ndvector[i,:])) | |
+ return v_norm | |
def V_sphere(r): | |
- """ Returns the volume of a sphere with radius r | |
- """ | |
+ ''' | |
+ Calculates the volume of a sphere with radius r | |
+ | |
+ :returns: The sphere volume [m^3] | |
+ :return type: float | |
+ ''' | |
return 4.0/3.0 * math.pi * r**3.0 | |
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 | |
diff --git a/python/uniaxial.py b/python/uniaxial.py | |
t@@ -1,7 +1,7 @@ | |
#!/usr/bin/env python | |
# Import sphere functionality | |
-from sphere import * | |
+import sphere | |
### EXPERIMENT SETUP ### | |
initialization = False | |
t@@ -18,7 +18,7 @@ sim_id = "uniaxial-test" | |
### INITIALIZATION ### | |
# New class | |
-init = Spherebin(np = np, nd = 3, nw = 0, sid = sim_id + "-init") | |
+init = sphere.sim(np = np, nd = 3, nw = 0, sid = sim_id + "-init") | |
# Save radii | |
init.generateRadii(radius_mean = 0.05) | |
t@@ -33,8 +33,6 @@ init.initRandomGridPos(gridnum = numpy.array([12, 12, 1000])… | |
init.initTemporal(total = 5.0) | |
if (initialization == True): | |
- # Write input file for sphere | |
- init.writebin() | |
# Run sphere | |
init.run() | |
t@@ -51,10 +49,10 @@ if (initialization == True): | |
### CONSOLIDATION ### | |
# New class | |
-cons = Spherebin(np = np, nw = 1, sid = sim_id + "-cons") | |
+cons = sphere.sim(np = np, nw = 1, sid = sim_id + "-cons") | |
# Read last output file of initialization step | |
-lastf = status(sim_id + "-init") | |
+lastf = sphere.status(sim_id + "-init") | |
cons.readbin("../output/" + sim_id + "-init.output{:0=5}.bin".format(lastf), v… | |
# Setup consolidation experiment | |
t@@ -71,8 +69,6 @@ cons.w_m[0] *= 0.001 | |
if (consolidation == True): | |
- # Write input file for sphere | |
- cons.writebin() | |
# Run sphere | |
cons.run(dry=True) # show values, don't run | |
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt | |
t@@ -22,13 +22,16 @@ ENDIF (GPU_GENERATION EQUAL 1) | |
# Rule to build executable program | |
CUDA_ADD_EXECUTABLE(../sphere | |
- main.cpp file_io.cpp sphere.cpp device.cu utility.cu utility.cpp darcy.cpp) | |
+ main.cpp file_io.cpp sphere.cpp device.cu utility.cu utility.cpp | |
+ navierstokes.cpp) | |
CUDA_ADD_EXECUTABLE(../porosity | |
- porosity.cpp file_io.cpp sphere.cpp device.cu utility.cu utility.cpp darcy… | |
+ porosity.cpp file_io.cpp sphere.cpp device.cu utility.cu utility.cpp | |
+ navierstokes.cpp) | |
CUDA_ADD_EXECUTABLE(../forcechains | |
- forcechains.cpp file_io.cpp sphere.cpp device.cu utility.cu utility.cpp da… | |
+ forcechains.cpp file_io.cpp sphere.cpp device.cu utility.cu utility.cpp | |
+ navierstokes.cpp) | |
CUDA_ADD_EXECUTABLE(../porousflow | |
- porousflow.cpp darcy.cpp file_io.cpp sphere.cpp device.cu utility.cpp util… | |
+ porousflow.cpp navierstokes.cpp file_io.cpp sphere.cpp device.cu utility.c… | |
#ADD_EXECUTABLE(unittests boost-unit-tests.cpp sphere.cpp) | |
#TARGET_LINK_LIBRARIES(unittests | |
diff --git a/src/constants.h b/src/constants.h | |
t@@ -17,7 +17,7 @@ const Float PI = 3.14159265358979; | |
const unsigned int ND = 3; | |
// Define source code version | |
-const Float VERS = 0.35; | |
+const double VERSION = 1.00; | |
// Max. number of contacts per particle | |
//const int NC = 16; | |
diff --git a/src/contactmodels.cuh b/src/contactmodels.cuh | |
t@@ -14,14 +14,16 @@ __device__ Float contactLinear_wall(Float3* F, Float3* T, … | |
Float3 n, Float delta, Float wvel) | |
{ | |
// Fetch particle velocities from global memory | |
- Float4 linvel_tmp = dev_vel_sorted[idx_a]; | |
+ Float4 vel_tmp = dev_vel_sorted[idx_a]; | |
Float4 angvel_tmp = dev_angvel_sorted[idx_a]; | |
// Convert velocities to three-component vectors | |
- Float3 linvel = MAKE_FLOAT3(linvel_tmp.x, | |
- linvel_tmp.y, | |
- linvel_tmp.z); | |
- Float3 angvel = MAKE_FLOAT3(angvel_tmp.x, | |
+ Float3 vel_linear = MAKE_FLOAT3( | |
+ vel_tmp.x, | |
+ vel_tmp.y, | |
+ vel_tmp.z); | |
+ Float3 angvel = MAKE_FLOAT3( | |
+ angvel_tmp.x, | |
angvel_tmp.y, | |
angvel_tmp.z); | |
t@@ -30,42 +32,58 @@ __device__ Float contactLinear_wall(Float3* F, Float3* T, … | |
// Contact velocity is the sum of the linear and | |
// rotational components | |
- Float3 vel = linvel + radius_a * cross(n, angvel) + wvel; | |
+ //Float3 vel = linvel + radius_a * cross(n, angvel) + wvel; | |
+ Float3 vel = vel_linear + (radius_a + delta/2.0) * cross(n, angvel) + wvel; | |
// Normal component of the contact velocity | |
- Float vel_n = dot(vel, n); | |
+ //Float vel_n = dot(vel, n); | |
+ Float vel_n = -dot(vel, n); | |
// The tangential velocity is the contact velocity | |
// with the normal component subtracted | |
- Float3 vel_t = vel - n * (dot(vel, n)); | |
- Float vel_t_length = length(vel_t); | |
+ //Float3 vel_t = vel - n * (dot(vel, n)); | |
+ const Float3 vel_t = vel - n * (dot(n, vel)); | |
+ const Float vel_t_length = length(vel_t); | |
// Calculate elastic normal component | |
//Float3 f_n = -devC_params.k_n * delta * n; | |
// Normal force component: Elastic - viscous damping | |
- Float3 f_n = (-devC_params.k_n * delta - devC_params.gamma_wn * vel_n) * n; | |
+ //Float3 f_n = (-devC_params.k_n * delta - devC_params.gamma_wn * vel_n) *… | |
+ Float3 f_n = (-devC_params.k_n * delta + devC_params.gamma_wn * vel_n) * n; | |
+ | |
+ // Print data for contact model validation | |
+ /*printf("f_n_elast = %f\tgamma_wn = %f\tf_n_visc = %f\n", | |
+ devC_params.k_n*delta, | |
+ devC_params.gamma_wn, | |
+ devC_params.gamma_wn*vel_n);*/ | |
+ | |
+ // Store the energy lost by viscous damping. See derivation in | |
+ // contactLinear() | |
+ *ev_dot += devC_params.gamma_wn * vel_n * vel_n; | |
// Make sure the viscous damping doesn't exceed the elastic component, | |
- // i.e. the damping factor doesn't exceed the critical damping, 2*sqrt(m*k… | |
- if (dot(f_n, n) < 0.0f) | |
- f_n = MAKE_FLOAT3(0.0f, 0.0f, 0.0f); | |
+ // i.e. the damping factor doesn't exceed the critical damping: | |
+ // 2*sqrt(m*k_n) | |
+ if (dot(f_n, n) < 0.0) | |
+ f_n = MAKE_FLOAT3(0.0, 0.0, 0.0); | |
- Float f_n_length = length(f_n); // Save length for later use | |
+ const Float f_n_length = length(f_n); // Save length for later use | |
// Initialize vectors | |
- Float3 f_t = MAKE_FLOAT3(0.0f, 0.0f, 0.0f); | |
- Float3 T_res = MAKE_FLOAT3(0.0f, 0.0f, 0.0f); | |
+ Float3 f_t = MAKE_FLOAT3(0.0, 0.0, 0.0); | |
// Check that the tangential velocity is high enough to avoid | |
// divide by zero (producing a NaN) | |
- if (vel_t_length > 0.f) { | |
+ if (vel_t_length > 0.0 && devC_params.gamma_wt > 0.0) { | |
- Float f_t_visc = devC_params.gamma_wt * vel_t_length; // Tangential f… | |
+ // Tangential force by viscous model | |
+ const Float3 f_t_visc = devC_params.gamma_wt * vel_t; | |
+ const Float f_t_visc_length = length(f_t_visc); | |
// Determine max. friction | |
Float f_t_limit; | |
- if (vel_t_length > 0.001f) { // Dynamic | |
+ if (vel_t_length > 0.0) { // Dynamic | |
f_t_limit = devC_params.mu_wd * f_n_length; | |
} else { // Static | |
f_t_limit = devC_params.mu_ws * f_n_length; | |
t@@ -73,14 +91,15 @@ __device__ Float contactLinear_wall(Float3* F, Float3* T, … | |
// If the shear force component exceeds the friction, | |
// the particle slips and energy is dissipated | |
- if (f_t_visc < f_t_limit) { | |
- f_t = -1.0f * f_t_visc * vel_t/vel_t_length; | |
+ if (f_t_visc_length < f_t_limit) { | |
+ f_t = -1.0*f_t_visc; | |
} else { // Dynamic friction, friction failure | |
- f_t = -1.0f * f_t_limit * vel_t/vel_t_length; | |
+ f_t = -f_t_limit * vel_t/vel_t_length; | |
// Shear energy production rate [W] | |
//*es_dot += -dot(vel_t, f_t); | |
+ *es_dot += length(length(f_t) * vel_t * devC_dt) / devC_dt; | |
} | |
} | |
t@@ -98,7 +117,7 @@ __device__ Float contactLinear_wall(Float3* F, Float3* T, F… | |
*F += f_n + f_t; | |
// Total torque from wall | |
- *T += -radius_a * cross(n, f_t) + T_res; | |
+ *T += cross(-(radius_a + delta*0.5)*n, f_t); | |
// Pressure excerted onto particle from this contact | |
*p += f_n_length / (4.0f * PI * radius_a*radius_a); | |
t@@ -246,6 +265,7 @@ __device__ void contactLinearViscous(Float3* F, Float3* T, | |
// Add force components from this collision to total force for particle | |
*F += f_n + f_t + f_c; | |
//*T += -(radius_a + delta_ab/2.0f) * cross(n_ab, f_t) + T_res; | |
+ *T += -(radius_a + delta_ab/2.0f) * cross(n_ab, f_t); | |
// Pressure excerted onto the particle from this contact | |
*p += f_n_length / (4.0f * PI * radius_a*radius_a); | |
t@@ -436,10 +456,12 @@ __device__ void contactLinear(Float3* F, Float3* T, | |
//delta_t = -1.0/devC_params.k_t * devC_params.mu_d * t + | |
//+ devC_params.gamma_t * vel_t; | |
- // In the sliding friction case, the tangential spring is adjusted… | |
- // a length consistent with Coulombs (dynamic) condition (Luding 2… | |
+ // In the sliding friction case, the tangential spring is adjusted | |
+ // to a length consistent with Coulombs (dynamic) condition (Luding | |
+ // 2008) | |
delta_t = -1.0/devC_params.k_t | |
- * (devC_params.mu_d * length(f_n-f_c) * t + devC_params.gamma_… | |
+ * (devC_params.mu_d * length(f_n-f_c) * t | |
+ + devC_params.gamma_t * vel_t); | |
// Shear friction heat production rate: | |
// The energy lost from the tangential spring is dissipated as heat | |
diff --git a/src/contactsearch.cuh b/src/contactsearch.cuh | |
t@@ -399,7 +399,6 @@ __global__ void interact(unsigned int* dev_gridParticleInd… | |
Float4 x_a = dev_x_sorted[idx_a]; | |
Float radius_a = x_a.w; | |
- | |
// Fetch world dimensions in constant memory read | |
Float3 origo = MAKE_FLOAT3(devC_grid.origo[0], | |
devC_grid.origo[1], | |
t@@ -529,16 +528,17 @@ __global__ void interact(unsigned int* dev_gridParticleI… | |
} | |
} else { | |
__syncthreads(); | |
- // Remove this contact (there is no particle with inde… | |
+ // Remove this contact (there is no particle with | |
+ // index=np) | |
dev_contacts[mempos] = devC_np; | |
// Zero sum of shear displacement in this position | |
- dev_delta_t[mempos] = MAKE_FLOAT4(0.0f, 0.0f, 0.0f, 0.… | |
+ dev_delta_t[mempos] = MAKE_FLOAT4(0.0, 0.0, 0.0, 0.0); | |
} | |
} else { // if dev_contacts[mempos] == devC_np | |
__syncthreads(); | |
// Zero sum of shear displacement in this position | |
- dev_delta_t[mempos] = MAKE_FLOAT4(0.0f, 0.0f, 0.0f, 0.0f); | |
+ dev_delta_t[mempos] = MAKE_FLOAT4(0.0, 0.0, 0.0, 0.0); | |
} | |
} // Contact loop end | |
t@@ -551,9 +551,12 @@ __global__ void interact(unsigned int* dev_gridParticleIn… | |
int3 targetPos; | |
// Calculate address in grid from position | |
- gridPos.x = floor((x_a.x - devC_grid.origo[0]) / (devC_grid.L[0]/d… | |
- gridPos.y = floor((x_a.y - devC_grid.origo[1]) / (devC_grid.L[1]/d… | |
- gridPos.z = floor((x_a.z - devC_grid.origo[2]) / (devC_grid.L[2]/d… | |
+ gridPos.x = floor((x_a.x - devC_grid.origo[0]) | |
+ / (devC_grid.L[0]/devC_grid.num[0])); | |
+ gridPos.y = floor((x_a.y - devC_grid.origo[1]) | |
+ / (devC_grid.L[1]/devC_grid.num[1])); | |
+ gridPos.z = floor((x_a.z - devC_grid.origo[2]) | |
+ / (devC_grid.L[2]/devC_grid.num[2])); | |
// Find overlaps between particle no. idx and all particles | |
// from its own cell + 26 neighbor cells. | |
t@@ -662,7 +665,6 @@ __global__ void interact(unsigned int* dev_gridParticleInd… | |
} | |
} | |
- | |
// Hold threads for coalesced write | |
__syncthreads(); | |
diff --git a/src/darcy.cpp b/src/darcy.cpp | |
t@@ -1,838 +0,0 @@ | |
-#include <iostream> | |
-#include <cstdio> | |
-#include <cstdlib> | |
-#include <string> | |
-#include <vector> | |
- | |
-#include "typedefs.h" | |
-#include "datatypes.h" | |
-#include "constants.h" | |
-#include "sphere.h" | |
-#include "utility.h" | |
- | |
-// Enable line below to make x and y boundaries periodic | |
-#define PERIODIC_XY | |
- | |
-// Initialize memory | |
-void DEM::initDarcyMem(const Float cellsizemultiplier) | |
-{ | |
- // Number of cells | |
- d.nx = floor(grid.num[0]*cellsizemultiplier); | |
- d.ny = floor(grid.num[1]*cellsizemultiplier); | |
- d.nz = floor(grid.num[2]*cellsizemultiplier); | |
- | |
- //unsigned int ncells = d.nx*d.ny*d.nz; // without ghost nodes | |
- unsigned int ncells = (d.nx+2)*(d.ny+2)*(d.nz+2); // with ghost nodes | |
- | |
- d.H = new Float[ncells]; // hydraulic pressure matrix | |
- d.H_new = new Float[ncells]; // hydraulic pressure matrix | |
- d.V = new Float3[ncells]; // Cell hydraulic velocity | |
- d.dH = new Float3[ncells]; // Cell gradient in hydraulic pressures | |
- d.K = new Float[ncells]; // hydraulic conductivity matrix | |
- d.T = new Float3[ncells]; // hydraulic transmissivity matrix | |
- d.Ss = new Float[ncells]; // hydraulic storativity matrix | |
- d.W = new Float[ncells]; // hydraulic recharge | |
- d.phi = new Float[ncells]; // cell porosity | |
- d.dphi = new Float[ncells]; // cell porosity change | |
-} | |
- | |
-// Free memory | |
-void DEM::freeDarcyMem() | |
-{ | |
- delete[] d.H; | |
- delete[] d.H_new; | |
- delete[] d.V; | |
- delete[] d.dH; | |
- delete[] d.K; | |
- delete[] d.T; | |
- delete[] d.Ss; | |
- delete[] d.W; | |
- delete[] d.phi; | |
- delete[] d.dphi; | |
-} | |
- | |
-// 3D index to 1D index | |
-unsigned int DEM::idx( | |
- const int x, | |
- const int y, | |
- const int z) | |
-{ | |
- // without ghost nodes | |
- //return x + d.nx*y + d.nx*d.ny*z; | |
- | |
- // with ghost nodes | |
- // the ghost nodes are placed at x,y,z = -1 and WIDTH | |
- return (x+1) + (d.nx+2)*(y+1) + (d.nx+2)*(d.ny+2)*(z+1); | |
-} | |
- | |
-// Set initial values | |
-void DEM::initDarcyVals() | |
-{ | |
- // Hydraulic permeability [m^2] | |
- const Float k = 1.0e-10; | |
- | |
- // Density of the fluid [kg/m^3] | |
- const Float rho = 1000.0; | |
- | |
- int ix, iy, iz, cellidx; | |
- | |
- // Set values for all cells, including ghost nodes | |
- for (ix=-1; ix<=d.nx; ++ix) { | |
- for (iy=-1; iy<=d.ny; ++iy) { | |
- for (iz=-1; iz<=d.nz; ++iz) { | |
- | |
- cellidx = idx(ix,iy,iz); | |
- | |
- // Hydraulic storativity [-] | |
- d.Ss[cellidx] = 1.0; | |
- //d.Ss[cellidx] = 8.0e-3; | |
- | |
- // Hydraulic recharge [s^-1] | |
- d.W[cellidx] = 0.0; | |
- } | |
- } | |
- } | |
- | |
- // Extract water from all cells in center | |
- /*ix = d.nx/2; iy = d.ny/2; | |
- Float cellvolume = d.dx*d.dy*d.dz; | |
- for (iz=0; iz<d.nz; ++iz) { | |
- //d.W[idx(ix,iy,iz)] = -1.0e-4/cellvolume; | |
- d.W[idx(ix,iy,iz)] = -2.0e-3; | |
- }*/ | |
-} | |
- | |
- | |
-// Copy values from cell with index 'read' to cell with index 'write' | |
-void DEM::copyDarcyVals(unsigned int read, unsigned int write) | |
-{ | |
- d.H[write] = d.H[read]; | |
- d.H_new[write] = d.H_new[read]; | |
- d.V[write] = MAKE_FLOAT3(d.V[read].x, d.V[read].y, d.V[read].z); | |
- d.dH[write] = MAKE_FLOAT3(d.dH[read].x, d.dH[read].y, d.dH[read].z); | |
- d.K[write] = d.K[read]; | |
- d.T[write] = MAKE_FLOAT3(d.T[read].x, d.T[read].y, d.T[read].z); | |
- d.Ss[write] = d.Ss[read]; | |
- d.W[write] = d.W[read]; | |
- d.phi[write] = d.phi[read]; | |
- d.dphi[write] = d.dphi[read]; | |
-} | |
- | |
-// Update ghost nodes from their parent cell values | |
-// The edge (diagonal) cells are not written since they are not read | |
-void DEM::setDarcyGhostNodes() | |
-{ | |
- int ix, iy, iz; | |
- | |
- // The x-normal plane | |
- for (iy=0; iy<d.ny; ++iy) { | |
- for (iz=0; iz<d.nz; ++iz) { | |
- | |
- // Ghost nodes at x=-1 | |
- copyDarcyVals( | |
- idx(d.nx-1,iy,iz), // Read from this cell | |
- idx(-1,iy,iz)); // Copy to this cell | |
- | |
- // Ghost nodes at x=d.nx | |
- copyDarcyVals( | |
- idx(0,iy,iz), | |
- idx(d.nx,iy,iz)); | |
- } | |
- } | |
- | |
- // The y-normal plane | |
- for (ix=0; ix<d.nx; ++ix) { | |
- for (iz=0; iz<d.nz; ++iz) { | |
- | |
- // Ghost nodes at y=-1 | |
- copyDarcyVals( | |
- idx(ix,d.ny-1,iz), | |
- idx(ix,-1,iz)); | |
- | |
- // Ghost nodes at y=d.ny | |
- copyDarcyVals( | |
- idx(ix,0,iz), | |
- idx(ix,d.ny,iz)); | |
- } | |
- } | |
- | |
- // The z-normal plane | |
- for (ix=0; ix<d.nx; ++ix) { | |
- for (iy=0; iy<d.ny; ++iy) { | |
- | |
- // Ghost nodes at z=-1 | |
- copyDarcyVals( | |
- idx(ix,iy,d.nz-1), | |
- idx(ix,iy,-1)); | |
- | |
- // Ghost nodes at z=d.nz | |
- copyDarcyVals( | |
- idx(ix,iy,0), | |
- idx(ix,iy,d.nz)); | |
- } | |
- } | |
-} | |
- | |
-// Find cell transmissivities from hydraulic conductivities and cell dimensions | |
-void DEM::findDarcyTransmissivities() | |
-{ | |
- // Find porosities from cell particle content | |
- findPorosities(); | |
- | |
- // Density of the fluid [kg/m^3] | |
- const Float rho = 1000.0; | |
- | |
- // Representative grain radius | |
- Float r_bar2 = meanRadius()*2.0 * 1.0e-6; | |
- // Grain size factor for Kozeny-Carman relationship | |
- Float d_factor = r_bar2*r_bar2/180.0; | |
- | |
- int ix, iy, iz, cellidx; | |
- Float K, k; | |
- for (ix=0; ix<d.nx; ++ix) { | |
- for (iy=0; iy<d.ny; ++iy) { | |
- for (iz=0; iz<d.nz; ++iz) { | |
- | |
- cellidx = idx(ix,iy,iz); | |
- | |
- // Read cell porosity | |
- Float phi = d.phi[cellidx]; | |
- | |
- // Calculate permeability from the Kozeny-Carman relationship | |
- // Nelson 1994 eq. 1c | |
- // Boek 2012 eq. 16 | |
- //k = a*phi*phi*phi/(1.0 - phi*phi); | |
- // Schwartz and Zhang 2003 | |
- k = phi*phi*phi/((1.0-phi)*(1.0-phi)) * d_factor; | |
- | |
- // Save hydraulic conductivity [m/s] | |
- //K = d.K[cellidx]; | |
- K = k*rho*-params.g[2]/params.nu; | |
- d.K[cellidx] = K; | |
- | |
- // Hydraulic transmissivity [m2/s] | |
- Float3 T = {K*d.dx, K*d.dy, K*d.dz}; | |
- d.T[cellidx] = T; | |
- } | |
- } | |
- } | |
-} | |
- | |
-// Set the gradient to 0.0 in all dimensions at the boundaries | |
-// Unused | |
-void DEM::setDarcyBCNeumannZero() | |
-{ | |
- Float3 z3 = MAKE_FLOAT3(0.0, 0.0, 0.0); | |
- int ix, iy, iz; | |
- int nx = d.nx-1; | |
- int ny = d.ny-1; | |
- int nz = d.nz-1; | |
- | |
- // I don't care that the values at four edges are written twice | |
- | |
- // x-y plane at z=0 and z=d.dz-1 | |
- for (ix=0; ix<d.nx; ++ix) { | |
- for (iy=0; iy<d.ny; ++iy) { | |
- d.dH[idx(ix,iy, 0)] = z3; | |
- d.dH[idx(ix,iy,nz)] = z3; | |
- } | |
- } | |
- | |
- // x-z plane at y=0 and y=d.dy-1 | |
- for (ix=0; ix<d.nx; ++ix) { | |
- for (iz=0; iz<d.nz; ++iz) { | |
- d.dH[idx(ix, 0,iz)] = z3; | |
- d.dH[idx(ix,ny,iz)] = z3; | |
- } | |
- } | |
- | |
- // y-z plane at x=0 and x=d.nx-1 | |
- for (iy=0; iy<d.ny; ++iy) { | |
- for (iz=0; iz<d.nz; ++iz) { | |
- d.dH[idx( 0,iy,iz)] = z3; | |
- d.dH[idx(nx,iy,iz)] = z3; | |
- } | |
- } | |
-} | |
- | |
- | |
-// Find the spatial gradient in pressures per cell | |
-void DEM::findDarcyGradients() | |
-{ | |
- // Cell sizes squared | |
- //const Float dx2 = d.dx*d.dx; | |
- //const Float dx2 = d.dx*d.dx; | |
- //const Float dy2 = d.dy*d.dy; | |
- const Float dx2 = 2.0*d.dx; | |
- const Float dy2 = 2.0*d.dy; | |
- const Float dz2 = 2.0*d.dz; | |
- | |
- //Float H; | |
- int ix, iy, iz, cellidx; | |
- | |
- // Without ghost-nodes | |
- /*for (ix=1; ix<d.nx-1; ++ix) { | |
- for (iy=1; iy<d.ny-1; ++iy) { | |
- for (iz=1; iz<d.nz-1; ++iz) {*/ | |
- | |
- // With ghost-nodes | |
- for (ix=0; ix<d.nx; ++ix) { | |
- for (iy=0; iy<d.ny; ++iy) { | |
- //for (iz=1; iz<d.nz-1; ++iz) { | |
- for (iz=0; iz<d.nz; ++iz) { | |
- | |
- cellidx = idx(ix,iy,iz); | |
- | |
- //H = d.H[cellidx]; // cell hydraulic pressure | |
- | |
- // First order central differences | |
- // x-boundary | |
- d.dH[cellidx].x | |
- = (d.H[idx(ix+1,iy,iz)] - d.H[idx(ix-1,iy,iz)])/dx2; | |
- | |
- // y-boundary | |
- d.dH[cellidx].y | |
- = (d.H[idx(ix,iy+1,iz)] - d.H[idx(ix,iy-1,iz)])/dy2; | |
- | |
- // z-boundary | |
- d.dH[cellidx].z | |
- = (d.H[idx(ix,iy,iz+1)] - d.H[idx(ix,iy,iz-1)])/dz2; | |
- | |
- /* | |
- // Periodic x boundaries | |
- if (ix == 0) { | |
- d.dH[cellidx].x = (d.H[idx(ix+1,iy,iz)] | |
- - 2.0*H + d.H[idx(d.nx-1,iy,iz)])/dx2; | |
- } else if (ix == d.nx-1) { | |
- d.dH[cellidx].x = (d.H[idx(0,iy,iz)] | |
- - 2.0*H + d.H[idx(ix-1,iy,iz)])/dx2; | |
- } else { | |
- d.dH[cellidx].x = (d.H[idx(ix+1,iy,iz)] | |
- - 2.0*H + d.H[idx(ix-1,iy,iz)])/dx2; | |
- } | |
- | |
- // Periodic y boundaries | |
- if (iy == 0) { | |
- d.dH[cellidx].y = (d.H[idx(ix,iy+1,iz)] | |
- - 2.0*H + d.H[idx(ix,d.ny-1,iz)])/dy2; | |
- } else if (iy == d.ny-1) { | |
- d.dH[cellidx].y = (d.H[idx(ix,0,iz)] | |
- - 2.0*H + d.H[idx(ix,iy-1,iz)])/dy2; | |
- } else { | |
- d.dH[cellidx].y = (d.H[idx(ix,iy+1,iz)] | |
- - 2.0*H + d.H[idx(ix,iy-1,iz)])/dy2; | |
- }*/ | |
- | |
- } | |
- } | |
- } | |
-} | |
- | |
-// Arithmetic mean of two numbers | |
-inline Float amean(Float a, Float b) { | |
- return (a+b)*0.5; | |
-} | |
- | |
-// Harmonic mean of two numbers | |
-inline Float hmean(Float a, Float b) { | |
- return (2.0*a*b)/(a+b); | |
-} | |
- | |
-// Perform an explicit step. | |
-void DEM::explDarcyStep() | |
-{ | |
- | |
- // Find transmissivities from cell particle content | |
- findDarcyTransmissivities(); | |
- | |
- // Check the time step length | |
- checkDarcyTimestep(); | |
- | |
- // Cell dims squared | |
- const Float dxdx = d.dx*d.dx; | |
- const Float dydy = d.dy*d.dy; | |
- const Float dzdz = d.dz*d.dz; | |
- const Float dxdydz = d.dx*d.dy*d.dz; | |
- | |
- //setDarcyBCNeumannZero(); | |
- | |
- // Update ghost node values from their parent cell values | |
- setDarcyGhostNodes(); | |
- | |
- // Explicit 3D finite difference scheme | |
- // new = old + production*timestep + gradient*timestep | |
- int ix, iy, iz, cellidx; | |
- Float K, H, deltaH; | |
- Float Tx, Ty, Tz, S; | |
- //Float Tx_n, Tx_p, Ty_n, Ty_p, Tz_n, Tz_p; | |
- Float gradx_n, gradx_p, grady_n, grady_p, gradz_n, gradz_p; | |
- for (ix=0; ix<d.nx; ++ix) { | |
- for (iy=0; iy<d.ny; ++iy) { | |
- for (iz=0; iz<d.nz; ++iz) { | |
- | |
- // Cell linear index | |
- cellidx = idx(ix,iy,iz); | |
- | |
- // If x,y,z boundaries are fixed values: Enforce Dirichlet BC | |
- /*if (ix == 0 || iy == 0 || iz == 0 || | |
- ix == d.nx-1 || iy == d.ny-1 || iz == d.nz-1) { | |
- d.H_new[cellidx] = d.H[cellidx];*/ | |
- | |
- // If z boundaries are fixed val, x and y are periodic: | |
- /*if (iz == 0 || iz == d.nz-1) { | |
- d.H_new[cellidx] = d.H[cellidx]; | |
- | |
- } else {*/ | |
- | |
- // Cell hydraulic transmissivities | |
- const Float3 T = d.T[cellidx]; | |
- | |
- // Cell hydraulic head | |
- H = d.H[cellidx]; | |
- | |
- // Harmonic mean of transmissivity | |
- // (in neg. and pos. direction along axis from cell) | |
- // with periodic x and y boundaries | |
- // without ghost nodes | |
- /* | |
- if (ix == 0) | |
- gradx_n = hmean(Tx, d.T[idx(d.nx-1,iy,iz)].x) | |
- * (d.H[idx(d.nx-1,iy,iz)] - H)/dx2; | |
- else | |
- gradx_n = hmean(Tx, d.T[idx(ix-1,iy,iz)].x) | |
- * (d.H[idx(ix-1,iy,iz)] - H)/dx2; | |
- | |
- if (ix == d.nx-1) | |
- gradx_p = hmean(Tx, d.T[idx(0,iy,iz)].x) | |
- * (d.H[idx(0,iy,iz)] - H)/dx2; | |
- else | |
- gradx_p = hmean(Tx, d.T[idx(ix+1,iy,iz)].x) | |
- * (d.H[idx(ix+1,iy,iz)] - H)/dx2; | |
- | |
- if (iy == 0) | |
- grady_n = hmean(Ty, d.T[idx(ix,d.ny-1,iz)].y) | |
- * (d.H[idx(ix,d.ny-1,iz)] - H)/dy2; | |
- else | |
- grady_n = hmean(Ty, d.T[idx(ix,iy-1,iz)].y) | |
- * (d.H[idx(ix,iy-1,iz)] - H)/dy2; | |
- | |
- if (iy == d.ny-1) | |
- grady_p = hmean(Ty, d.T[idx(ix,0,iz)].y) | |
- * (d.H[idx(ix,0,iz)] - H)/dy2; | |
- else | |
- grady_p = hmean(Ty, d.T[idx(ix,iy+1,iz)].y) | |
- * (d.H[idx(ix,iy+1,iz)] - H)/dy2; | |
- */ | |
- | |
- gradx_n = hmean(T.x, d.T[idx(ix-1,iy,iz)].x) | |
- * (d.H[idx(ix-1,iy,iz)] - H)/dxdx; | |
- gradx_p = hmean(T.x, d.T[idx(ix+1,iy,iz)].x) | |
- * (d.H[idx(ix+1,iy,iz)] - H)/dxdx; | |
- | |
- grady_n = hmean(T.y, d.T[idx(ix,iy-1,iz)].y) | |
- * (d.H[idx(ix,iy-1,iz)] - H)/dydy; | |
- grady_p = hmean(T.y, d.T[idx(ix,iy+1,iz)].y) | |
- * (d.H[idx(ix,iy+1,iz)] - H)/dydy; | |
- | |
- // Neumann (no-flow) boundary condition at +z and -z boundaries | |
- // enforced by a gradient value of 0.0 | |
- if (iz == 0) | |
- gradz_n = 0.0; | |
- else | |
- gradz_n = hmean(T.z, d.T[idx(ix,iy,iz-1)].z) | |
- * (d.H[idx(ix,iy,iz-1)] - H)/dzdz; | |
- if (iz == d.nz-1) | |
- gradz_p = 0.0; | |
- else | |
- gradz_p = hmean(T.z, d.T[idx(ix,iy,iz+1)].z) | |
- * (d.H[idx(ix,iy,iz+1)] - H)/dzdz; | |
- | |
- /*std::cerr << ix << ',' << iy << ',' << iz << '\t' | |
- << H << '\t' << Tx << ',' << Ty << ',' << Tz << '\t' | |
- << gradx_n << ',' << gradx_p << '\t' | |
- << grady_n << ',' << grady_p << '\t' | |
- << gradz_n << ',' << gradz_p << std::endl;*/ | |
- | |
- // Cell hydraulic storativity | |
- S = d.Ss[cellidx]*dxdydz; | |
- | |
- // Laplacian operator | |
- deltaH = time.dt/S * | |
- ( gradx_n + gradx_p | |
- + grady_n + grady_p | |
- + gradz_n + gradz_p | |
- + d.W[cellidx] - d.dphi[cellidx]/time.dt); | |
- | |
- // Calculate new hydraulic pressure in cell | |
- d.H_new[cellidx] = H + deltaH; | |
- //} | |
- } | |
- } | |
- } | |
- | |
- // Swap d.H and d.H_new | |
- Float* tmp = d.H; | |
- d.H = d.H_new; | |
- d.H_new = tmp; | |
- | |
- // Find macroscopic cell fluid velocities | |
- findDarcyVelocities(); | |
-} | |
- | |
-// Print array values to file stream (stdout, stderr, other file) | |
-void DEM::printDarcyArray(FILE* stream, Float* arr) | |
-{ | |
- int x, y, z; | |
- for (z=0; z<d.nz; z++) { | |
- for (y=0; y<d.ny; y++) { | |
- for (x=0; x<d.nx; x++) { | |
- fprintf(stream, "%f\t", arr[idx(x,y,z)]); | |
- } | |
- fprintf(stream, "\n"); | |
- } | |
- fprintf(stream, "\n"); | |
- } | |
-} | |
- | |
-// Overload printDarcyArray to add optional description | |
-void DEM::printDarcyArray(FILE* stream, Float* arr, std::string desc) | |
-{ | |
- std::cout << "\n" << desc << ":\n"; | |
- printDarcyArray(stream, arr); | |
-} | |
- | |
-// Print array values to file stream (stdout, stderr, other file) | |
-void DEM::printDarcyArray(FILE* stream, Float3* arr) | |
-{ | |
- int x, y, z; | |
- for (z=0; z<d.nz; z++) { | |
- for (y=0; y<d.ny; y++) { | |
- for (x=0; x<d.nx; x++) { | |
- fprintf(stream, "%f,%f,%f\t", | |
- arr[idx(x,y,z)].x, | |
- arr[idx(x,y,z)].y, | |
- arr[idx(x,y,z)].z); | |
- } | |
- fprintf(stream, "\n"); | |
- } | |
- fprintf(stream, "\n"); | |
- } | |
-} | |
- | |
-// Overload printDarcyArray to add optional description | |
-void DEM::printDarcyArray(FILE* stream, Float3* arr, std::string desc) | |
-{ | |
- std::cout << "\n" << desc << ":\n"; | |
- printDarcyArray(stream, arr); | |
-} | |
- | |
-// Find cell velocity | |
-void DEM::findDarcyVelocities() | |
-{ | |
- // Flux [m/s]: q = -k/nu * dH | |
- // Pore velocity [m/s]: v = q/n | |
- Float3 q, v, dH; | |
- | |
- // Dynamic viscosity | |
- Float nu = params.nu; | |
- | |
- // Porosity [-]: n | |
- | |
- // Find cell gradients | |
- findDarcyGradients(); | |
- | |
- int ix, iy, iz, cellidx; | |
- for (ix=0; ix<d.nx; ++ix) { | |
- for (iy=0; iy<d.ny; ++iy) { | |
- for (iz=0; iz<d.nz; ++iz) { | |
- | |
- cellidx = idx(ix,iy,iz); | |
- dH = d.dH[cellidx]; | |
- | |
- // Approximate cell porosity | |
- Float phi = d.phi[cellidx]; | |
- | |
- // Calculate flux | |
- // The sign might need to be reversed, depending on the | |
- // grid orientation | |
- q.x = -d.K[cellidx]/nu * dH.x; | |
- q.y = -d.K[cellidx]/nu * dH.y; | |
- q.z = -d.K[cellidx]/nu * dH.z; | |
- | |
- // Calculate velocity | |
- v.x = q.x/phi; | |
- v.y = q.y/phi; | |
- v.z = q.z/phi; | |
- d.V[cellidx] = v; | |
- } | |
- } | |
- } | |
-} | |
- | |
-// Return the lower corner coordinates of a cell | |
-inline Float3 DEM::cellMinBoundaryDarcy( | |
- const int x, | |
- const int y, | |
- const int z) | |
-{ | |
- const Float3 x_min = {x*d.dx, y*d.dy, z*d.dz}; | |
- return x_min; | |
-} | |
- | |
-// Return the upper corner coordinates of a cell | |
-inline Float3 DEM::cellMaxBoundaryDarcy( | |
- const int x, | |
- const int y, | |
- const int z) | |
-{ | |
- const Float3 x_max = {(x+1)*d.dx, (y+1)*d.dy, (z+1)*d.dz}; | |
- return x_max; | |
-} | |
- | |
-// Return the volume of a cell | |
-inline Float DEM::cellVolumeDarcy() | |
-{ | |
- const Float cell_volume = d.dx*d.dy*d.dz; | |
- return cell_volume; | |
-} | |
- | |
-// Find the porosity of a target cell | |
-Float DEM::cellPorosity( | |
- const int x, | |
- const int y, | |
- const int z) | |
-{ | |
- const Float3 x_min = cellMinBoundaryDarcy(x,y,z); | |
- const Float3 x_max = cellMaxBoundaryDarcy(x,y,z); | |
- Float cell_volume = cellVolumeDarcy(); | |
- Float void_volume = cell_volume; | |
- | |
- unsigned int i; | |
- Float4 xr; | |
- for (i=0; i<np; ++i) { | |
- | |
- // Read the position and radius | |
- xr = k.x[i]; | |
- | |
- if (xr.x >= x_min.x && xr.y >= x_min.y && xr.z >= x_min.z | |
- && xr.x < x_max.x && xr.y < x_max.y && xr.z < x_max.z) { | |
- void_volume -= 4.0/3.0*M_PI*xr.w*xr.w*xr.w; | |
- } | |
- } | |
- | |
- // Return the porosity, which should always be ]0.0;1.0[ | |
- Float phi = fmin(0.99, fmax(0.01, void_volume/cell_volume)); | |
- return phi; | |
-} | |
- | |
-// Calculate the porosity for each cell | |
-void DEM::findPorosities() | |
-{ | |
- int ix, iy, iz, cellidx; | |
- Float phi, phi_0; | |
- for (ix=0; ix<d.nx; ++ix) { | |
- for (iy=0; iy<d.ny; ++iy) { | |
- for (iz=0; iz<d.nz; ++iz) { | |
- phi_0 = d.phi[idx(ix,iy,iz)]; | |
- phi = cellPorosity(ix,iy,iz); | |
- d.phi[idx(ix,iy,iz)] = phi; | |
- d.dphi[idx(ix,iy,iz)] = phi - phi_0; | |
- } | |
- } | |
- } | |
-} | |
- | |
-// Returns the mean particle radius | |
-Float DEM::meanRadius() | |
-{ | |
- unsigned int i; | |
- Float r_sum; | |
- for (i=0; i<np; ++i) | |
- r_sum += k.x[i].w; | |
- return r_sum/((Float)np); | |
-} | |
- | |
-// Find particles with centres inside a spatial interval | |
-// NOTE: This function is untested and unused | |
-std::vector<unsigned int> DEM::particlesInCell( | |
- const Float3 min, const Float3 max) | |
-{ | |
- // Particles radii inside cell will be stored in this vector | |
- std::vector<unsigned int> pidx; | |
- | |
- unsigned int i; | |
- Float4 x; | |
- for (i=0; i<np; ++i) { | |
- | |
- // Read the position | |
- x = k.x[i]; | |
- | |
- if (x.x >= min.x && x.y >= min.y && x.z >= min.z | |
- && x.x < max.x && x.y < max.y && x.z < max.z) { | |
- pidx.push_back(i); | |
- } | |
- } | |
-} | |
- | |
-// Add fluid drag to the particles inside each cell | |
-void DEM::fluidDragDarcy() | |
-{ | |
- /*unsigned int ix, iy, iz, cellidx; | |
- for (ix=0; ix<d_nx; ++ix) { | |
- for (iy=0; iy<d_ny; ++iy) { | |
- for (iz=0; iz<d_nz; ++iz) { | |
- | |
- | |
- } | |
- } | |
- }*/ | |
-} | |
- | |
-// Get maximum value in 3d array with ghost nodes | |
-Float DEM::getTmax() | |
-{ | |
- Float max = -1.0e13; // initialize with a small number | |
- int ix,iy,iz; | |
- Float3 val; | |
- for (ix=0; ix<d.nx; ++ix) { | |
- for (iy=0; iy<d.ny; ++iy) { | |
- for (iz=0; iz<d.nz; ++iz) { | |
- val = d.T[idx(ix,iy,iz)]; | |
- if (val.x > max) | |
- max = val.x; | |
- if (val.y > max) | |
- max = val.y; | |
- if (val.z > max) | |
- max = val.z; | |
- } | |
- } | |
- } | |
- return max; | |
-} | |
-// Get maximum value in 1d array with ghost nodes | |
-Float DEM::getSsmin() | |
-{ | |
- Float min = 1.0e13; // initialize with a small number | |
- int ix,iy,iz; | |
- Float val; | |
- for (ix=0; ix<d.nx; ++ix) { | |
- for (iy=0; iy<d.ny; ++iy) { | |
- for (iz=0; iz<d.nz; ++iz) { | |
- val = d.Ss[idx(ix,iy,iz)]; | |
- if (val < min) | |
- min = val; | |
- } | |
- } | |
- } | |
- return min; | |
-} | |
- | |
- | |
-// Check whether the time step length is sufficient for keeping the explicit | |
-// time stepping method stable | |
-void DEM::checkDarcyTimestep() | |
-{ | |
- Float T_max = getTmax(); | |
- Float S_min = getSsmin()*d.dx*d.dy*d.dz; | |
- | |
- // Use numerical criterion from Sonnenborg & Henriksen 2005 | |
- Float value = T_max/S_min | |
- * (time.dt/(d.dx*d.dx) + time.dt/(d.dy*d.dy) + time.dt/(d.dz*d.dz)); | |
- | |
- if (value > 0.5) { | |
- std::cerr << "Error! The explicit Darcy solution will be unstable.\n" | |
- << "This happens due to a combination of the following:\n" | |
- << " - The transmissivity T (i.e. hydraulic conductivity, K (" | |
- << T_max/(d.dx) | |
- << ") is too large (" << T_max << ")\n" | |
- << " - The storativity S is too small" | |
- << " (" << S_min << ")\n" | |
- << " - The time step is too large" | |
- << " (" << time.dt << ")\n" | |
- << " - The cell dimensions are too small\n" | |
- << " Reason: (" << value << " > 0.5)" | |
- << std::endl; | |
- exit(1); | |
- } | |
-} | |
- | |
-// Initialize darcy arrays, their values, and check the time step length | |
-void DEM::initDarcy() | |
-{ | |
- if (params.nu <= 0.0) { | |
- std::cerr << "Error in initDarcy. The dymamic viscosity (params.nu), " | |
- << "should be larger than 0.0, but is " << params.nu << std::endl; | |
- exit(1); | |
- } | |
- | |
- // Cell size | |
- d.dx = grid.L[0]/d.nx; | |
- d.dy = grid.L[1]/d.ny; | |
- d.dz = grid.L[2]/d.nz; | |
- | |
- if (verbose == 1) { | |
- std::cout << " - Fluid grid dimensions: " | |
- << d.nx << "*" | |
- << d.ny << "*" | |
- << d.nz << std::endl; | |
- std::cout << " - Fluid grid cell size: " | |
- << d.dx << "*" | |
- << d.dy << "*" | |
- << d.dz << std::endl; | |
- } | |
- | |
- //initDarcyMem(); // done in readbin | |
- initDarcyVals(); | |
- findDarcyTransmissivities(); | |
- | |
- // set dphi values to zero | |
- for (int ix=0; ix<d.nx; ++ix) { | |
- for (int iy=0; iy<d.ny; ++iy) { | |
- for (int iz=0; iz<d.nz; ++iz) { | |
- d.dphi[idx(ix,iy,iz)] = 0.0; | |
- } | |
- } | |
- } | |
- | |
- checkDarcyTimestep(); | |
-} | |
- | |
-// Write values in scalar field to file | |
-void DEM::writeDarcyArray(Float* array, const char* filename) | |
-{ | |
- FILE* file; | |
- if ((file = fopen(filename,"w"))) { | |
- printDarcyArray(file, array); | |
- fclose(file); | |
- } else { | |
- fprintf(stderr, "Error, could not open %s.\n", filename); | |
- } | |
-} | |
- | |
-// Write values in vector field to file | |
-void DEM::writeDarcyArray(Float3* array, const char* filename) | |
-{ | |
- FILE* file; | |
- if ((file = fopen(filename,"w"))) { | |
- printDarcyArray(file, array); | |
- fclose(file); | |
- } else { | |
- fprintf(stderr, "Error, could not open %s.\n", filename); | |
- } | |
-} | |
- | |
- | |
-// Print final heads and free memory | |
-void DEM::endDarcy() | |
-{ | |
- writeDarcyArray(d.phi, "d_phi.txt"); | |
- writeDarcyArray(d.K, "d_K.txt"); | |
- | |
- //printDarcyArray(stdout, d.K, "d.K"); | |
- //printDarcyArray(stdout, d.H, "d.H"); | |
- //printDarcyArray(stdout, d.H_new, "d.H_new"); | |
- //printDarcyArray(stdout, d.V, "d.V"); | |
- freeDarcyMem(); | |
-} | |
- | |
-// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 | |
diff --git a/src/darcy.cuh b/src/darcy.cuh | |
t@@ -1,783 +0,0 @@ | |
-// darcy.cu | |
-// CUDA implementation of Darcy flow | |
- | |
-// Enable line below to perform Darcy flow computations on the GPU, disable for | |
-// Enable GPU computation | |
-#define DARCY_GPU | |
- | |
-#include <iostream> | |
-#include <cuda.h> | |
-//#include <cutil_math.h> | |
-#include <helper_math.h> | |
- | |
-#include "vector_arithmetic.h" // for arbitrary prec. vectors | |
-#include "sphere.h" | |
-#include "datatypes.h" | |
-#include "utility.cuh" | |
-#include "utility.h" | |
-#include "constants.cuh" | |
-#include "debug.h" | |
- | |
-// Initialize memory | |
-void DEM::initDarcyMemDev(void) | |
-{ | |
- // number of cells | |
- //unsigned int ncells = d.nx*d.ny*d.nz; // without ghost nodes | |
- unsigned int ncells = (d.nx+2)*(d.ny+2)*(d.nz+2); // with ghost nodes | |
- unsigned int memSizeF = sizeof(Float) * ncells; | |
- | |
- cudaMalloc((void**)&dev_d_H, memSizeF); // hydraulic pressure | |
- cudaMalloc((void**)&dev_d_H_new, memSizeF); // new pressure matrix | |
- cudaMalloc((void**)&dev_d_V, memSizeF*3); // cell hydraulic velocity | |
- cudaMalloc((void**)&dev_d_dH, memSizeF*3); // hydraulic pressure gradient | |
- cudaMalloc((void**)&dev_d_K, memSizeF); // hydraulic conductivity | |
- cudaMalloc((void**)&dev_d_T, memSizeF*3); // hydraulic transmissivity | |
- cudaMalloc((void**)&dev_d_Ss, memSizeF); // hydraulic storativi | |
- cudaMalloc((void**)&dev_d_W, memSizeF); // hydraulic recharge | |
- cudaMalloc((void**)&dev_d_phi, memSizeF); // cell porosity | |
- cudaMalloc((void**)&dev_d_dphi, memSizeF); // cell porosity change | |
- | |
- checkForCudaErrors("End of initDarcyMemDev"); | |
-} | |
- | |
-// Free memory | |
-void DEM::freeDarcyMemDev() | |
-{ | |
- cudaFree(dev_d_H); | |
- cudaFree(dev_d_H_new); | |
- cudaFree(dev_d_V); | |
- cudaFree(dev_d_dH); | |
- cudaFree(dev_d_K); | |
- cudaFree(dev_d_T); | |
- cudaFree(dev_d_Ss); | |
- cudaFree(dev_d_W); | |
- cudaFree(dev_d_phi); | |
- cudaFree(dev_d_dphi); | |
-} | |
- | |
-// Transfer to device | |
-void DEM::transferDarcyToGlobalDeviceMemory(int statusmsg) | |
-{ | |
- checkForCudaErrors("Before attempting cudaMemcpy in " | |
- "transferDarcyToGlobalDeviceMemory"); | |
- | |
- //if (verbose == 1 && statusmsg == 1) | |
- //std::cout << " Transfering fluid data to the device: "; | |
- | |
- // number of cells | |
- //unsigned int ncells = d.nx*d.ny*d.nz; // without ghost nodes | |
- unsigned int ncells = (d.nx+2)*(d.ny+2)*(d.nz+2); // with ghost nodes | |
- unsigned int memSizeF = sizeof(Float) * ncells; | |
- | |
- // Kinematic particle values | |
- cudaMemcpy(dev_d_H, d.H, memSizeF, cudaMemcpyHostToDevice); | |
- checkForCudaErrors("transferDarcyToGlobalDeviceMemory after first cudaMemc… | |
- cudaMemcpy(dev_d_H_new, d.H_new, memSizeF, cudaMemcpyHostToDevice); | |
- cudaMemcpy(dev_d_V, d.V, memSizeF*3, cudaMemcpyHostToDevice); | |
- cudaMemcpy(dev_d_dH, d.dH, memSizeF*3, cudaMemcpyHostToDevice); | |
- cudaMemcpy(dev_d_K, d.K, memSizeF, cudaMemcpyHostToDevice); | |
- cudaMemcpy(dev_d_T, d.T, memSizeF*3, cudaMemcpyHostToDevice); | |
- cudaMemcpy(dev_d_Ss, d.Ss, memSizeF, cudaMemcpyHostToDevice); | |
- cudaMemcpy(dev_d_W, d.W, memSizeF, cudaMemcpyHostToDevice); | |
- cudaMemcpy(dev_d_phi, d.phi, memSizeF, cudaMemcpyHostToDevice); | |
- cudaMemcpy(dev_d_dphi, d.dphi, memSizeF, cudaMemcpyHostToDevice); | |
- | |
- checkForCudaErrors("End of transferDarcyToGlobalDeviceMemory"); | |
- //if (verbose == 1 && statusmsg == 1) | |
- //std::cout << "Done" << std::endl; | |
-} | |
- | |
-// Transfer from device | |
-void DEM::transferDarcyFromGlobalDeviceMemory(int statusmsg) | |
-{ | |
- if (verbose == 1 && statusmsg == 1) | |
- std::cout << " Transfering darcy data from the device: "; | |
- | |
- // number of cells | |
- //unsigned int ncells = d.nx*d.ny*d.nz; // without ghost nodes | |
- unsigned int ncells = (d.nx+2)*(d.ny+2)*(d.nz+2); // with ghost nodes | |
- unsigned int memSizeF = sizeof(Float) * ncells; | |
- | |
- // Kinematic particle values | |
- cudaMemcpy(d.H, dev_d_H, memSizeF, cudaMemcpyDeviceToHost); | |
- cudaMemcpy(d.H_new, dev_d_H_new, memSizeF, cudaMemcpyDeviceToHost); | |
- cudaMemcpy(d.V, dev_d_V, memSizeF*3, cudaMemcpyDeviceToHost); | |
- cudaMemcpy(d.dH, dev_d_dH, memSizeF*3, cudaMemcpyDeviceToHost); | |
- cudaMemcpy(d.K, dev_d_K, memSizeF, cudaMemcpyDeviceToHost); | |
- cudaMemcpy(d.T, dev_d_T, memSizeF*3, cudaMemcpyDeviceToHost); | |
- cudaMemcpy(d.Ss, dev_d_Ss, memSizeF, cudaMemcpyDeviceToHost); | |
- cudaMemcpy(d.W, dev_d_W, memSizeF, cudaMemcpyDeviceToHost); | |
- cudaMemcpy(d.phi, dev_d_phi, memSizeF, cudaMemcpyDeviceToHost); | |
- cudaMemcpy(d.dphi, dev_d_dphi, memSizeF, cudaMemcpyDeviceToHost); | |
- | |
- checkForCudaErrors("End of transferDarcyFromGlobalDeviceMemory"); | |
- if (verbose == 1 && statusmsg == 1) | |
- std::cout << "Done" << std::endl; | |
-} | |
- | |
-// Get linear index from 3D grid position | |
-__inline__ __device__ unsigned int idx( | |
- const int x, const int y, const int z) | |
-{ | |
- // without ghost nodes | |
- //return x + dev_grid.num[0]*y + dev_grid.num[0]*dev_grid.num[1]*z; | |
- | |
- // with ghost nodes | |
- // the ghost nodes are placed at x,y,z = -1 and WIDTH | |
- return (x+1) + (devC_grid.num[0]+2)*(y+1) + | |
- (devC_grid.num[0]+2)*(devC_grid.num[1]+2)*(z+1); | |
-} | |
- | |
-__device__ void copyDarcyValsDev( | |
- unsigned int read, unsigned int write, | |
- Float* dev_d_H, Float* dev_d_H_new, | |
- Float3* dev_d_V, Float3* dev_d_dH, | |
- Float* dev_d_K, Float3* dev_d_T, | |
- Float* dev_d_Ss, Float* dev_d_W, | |
- Float* dev_d_phi, | |
- Float* dev_d_dphi) | |
-{ | |
- // Coalesced read | |
- const Float H = dev_d_H[read]; | |
- const Float H_new = dev_d_H_new[read]; | |
- const Float3 V = dev_d_V[read]; | |
- const Float3 dH = dev_d_dH[read]; | |
- const Float K = dev_d_K[read]; | |
- const Float3 T = dev_d_T[read]; | |
- const Float Ss = dev_d_Ss[read]; | |
- const Float W = dev_d_W[read]; | |
- const Float phi = dev_d_phi[read]; | |
- const Float dphi = dev_d_dphi[read]; | |
- | |
- // Coalesced write | |
- __syncthreads(); | |
- dev_d_H[write] = H; | |
- dev_d_H_new[write] = H_new; | |
- dev_d_V[write] = V; | |
- dev_d_dH[write] = dH; | |
- dev_d_K[write] = K; | |
- dev_d_T[write] = T; | |
- dev_d_Ss[write] = Ss; | |
- dev_d_W[write] = W; | |
- dev_d_phi[write] = phi; | |
- dev_d_dphi[write] = dphi; | |
-} | |
- | |
-// Update ghost nodes from their parent cell values | |
-// The edge (diagonal) cells are not written since they are note read | |
-// Launch this kernel for all cells in the grid | |
-__global__ void setDarcyGhostNodesDev( | |
- Float* dev_d_H, Float* dev_d_H_new, | |
- Float3* dev_d_V, Float3* dev_d_dH, | |
- Float* dev_d_K, Float3* dev_d_T, | |
- Float* dev_d_Ss, Float* dev_d_W, | |
- Float* dev_d_phi, | |
- Float* dev_d_dphi) | |
-{ | |
- // 3D thread index | |
- const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
- const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
- const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
- | |
- // Grid dimensions | |
- const unsigned int nx = devC_grid.num[0]; | |
- const unsigned int ny = devC_grid.num[1]; | |
- const unsigned int nz = devC_grid.num[2]; | |
- | |
- // 1D thread index | |
- const unsigned int cellidx = idx(x,y,z); | |
- | |
- // 1D position of ghost node | |
- unsigned int writeidx; | |
- | |
- // check that we are not outside the fluid grid | |
- if (x < nx && y < ny && z < nz) { | |
- | |
- if (x == 0) { | |
- writeidx = idx(nx,y,z); | |
- copyDarcyValsDev(cellidx, writeidx, | |
- dev_d_H, dev_d_H_new, | |
- dev_d_V, dev_d_dH, | |
- dev_d_K, dev_d_T, | |
- dev_d_Ss, dev_d_W, | |
- dev_d_phi, | |
- dev_d_dphi); | |
- } | |
- if (x == nx-1) { | |
- writeidx = idx(-1,y,z); | |
- copyDarcyValsDev(cellidx, writeidx, | |
- dev_d_H, dev_d_H_new, | |
- dev_d_V, dev_d_dH, | |
- dev_d_K, dev_d_T, | |
- dev_d_Ss, dev_d_W, | |
- dev_d_phi, | |
- dev_d_dphi); | |
- } | |
- | |
- if (y == 0) { | |
- writeidx = idx(x,ny,z); | |
- copyDarcyValsDev(cellidx, writeidx, | |
- dev_d_H, dev_d_H_new, | |
- dev_d_V, dev_d_dH, | |
- dev_d_K, dev_d_T, | |
- dev_d_Ss, dev_d_W, | |
- dev_d_phi, | |
- dev_d_dphi); | |
- } | |
- if (y == ny-1) { | |
- writeidx = idx(x,-1,z); | |
- copyDarcyValsDev(cellidx, writeidx, | |
- dev_d_H, dev_d_H_new, | |
- dev_d_V, dev_d_dH, | |
- dev_d_K, dev_d_T, | |
- dev_d_Ss, dev_d_W, | |
- dev_d_phi, | |
- dev_d_dphi); | |
- } | |
- | |
- if (z == 0) { | |
- writeidx = idx(x,y,nz); | |
- copyDarcyValsDev(cellidx, writeidx, | |
- dev_d_H, dev_d_H_new, | |
- dev_d_V, dev_d_dH, | |
- dev_d_K, dev_d_T, | |
- dev_d_Ss, dev_d_W, | |
- dev_d_phi, | |
- dev_d_dphi); | |
- } | |
- if (z == nz-1) { | |
- writeidx = idx(x,y,-1); | |
- copyDarcyValsDev(cellidx, writeidx, | |
- dev_d_H, dev_d_H_new, | |
- dev_d_V, dev_d_dH, | |
- dev_d_K, dev_d_T, | |
- dev_d_Ss, dev_d_W, | |
- dev_d_phi, | |
- dev_d_dphi); | |
- } | |
- } | |
-} | |
- | |
-// Find the porosity in each cell on the base of a cubic grid, binning particl… | |
-// into the cells containing their centers. This approximation causes | |
-// non-continuous porosities through time. | |
-__global__ void findPorositiesCubicDev( | |
- unsigned int* dev_cellStart, | |
- unsigned int* dev_cellEnd, | |
- Float4* dev_x_sorted, | |
- Float* dev_d_phi, | |
- Float* dev_d_dphi) | |
-{ | |
- // 3D thread index | |
- const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
- const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
- const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
- | |
- // Grid dimensions | |
- const unsigned int nx = devC_grid.num[0]; | |
- const unsigned int ny = devC_grid.num[1]; | |
- const unsigned int nz = devC_grid.num[2]; | |
- | |
- // Cell dimensions | |
- const Float dx = devC_grid.L[0]/nx; | |
- const Float dy = devC_grid.L[1]/ny; | |
- const Float dz = devC_grid.L[2]/nz; | |
- const Float cell_volume = dx*dy*dz; | |
- | |
- Float void_volume = cell_volume; | |
- Float4 xr; // particle pos. and radius | |
- | |
- // check that we are not outside the fluid grid | |
- if (x < nx && y < ny && z < nz) { | |
- | |
- // Calculate linear cell ID | |
- const unsigned int cellID = x + y*devC_grid.num[0] | |
- + (devC_grid.num[0] * devC_grid.num[1])*z; | |
- | |
- // Lowest particle index in cell | |
- const unsigned int startIdx = dev_cellStart[cellID]; | |
- | |
- // Read old porosity | |
- __syncthreads(); | |
- Float phi_0 = dev_d_phi[idx(x,y,z)]; | |
- | |
- Float phi = 0.99; | |
- | |
- // Make sure cell is not empty | |
- if (startIdx != 0xffffffff) { | |
- | |
- // Highest particle index in cell | |
- const unsigned int endIdx = dev_cellEnd[cellID]; | |
- | |
- // Iterate over cell particles | |
- for (unsigned int i = startIdx; i<endIdx; ++i) { | |
- | |
- // Read particle position and radius | |
- __syncthreads(); | |
- xr = dev_x_sorted[i]; | |
- | |
- // Subtract particle volume from void volume | |
- void_volume -= 4.0/3.0*M_PI*xr.w*xr.w*xr.w; | |
- } | |
- | |
- // Make sure that the porosity is in the interval ]0.0;1.0[ | |
- phi = fmin(0.99, fmax(0.01, void_volume/cell_volume)); | |
- } | |
- | |
- // Save porosity and porosity change | |
- __syncthreads(); | |
- dev_d_phi[idx(x,y,z)] = phi; | |
- dev_d_dphi[idx(x,y,z)] = phi - phi_0; | |
- } | |
-} | |
- | |
- | |
-// Find the porosity in each cell on the base of a sphere, centered at the cell | |
-// center. This approximation is continuous through time and generally | |
-// preferable to findPorositiesCubicDev, although it's slower. | |
-__global__ void findPorositiesSphericalDev( | |
- unsigned int* dev_cellStart, | |
- unsigned int* dev_cellEnd, | |
- Float4* dev_x_sorted, | |
- Float* dev_d_phi, | |
- Float* dev_d_dphi, | |
- unsigned int iteration) | |
-{ | |
- // 3D thread index | |
- const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
- const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
- const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
- | |
- // Grid dimensions | |
- const unsigned int nx = devC_grid.num[0]; | |
- const unsigned int ny = devC_grid.num[1]; | |
- const unsigned int nz = devC_grid.num[2]; | |
- | |
- // Cell dimensions | |
- const Float dx = devC_grid.L[0]/nx; | |
- const Float dy = devC_grid.L[1]/ny; | |
- const Float dz = devC_grid.L[2]/nz; | |
- | |
- // Cell sphere radius | |
- const Float R = fmin(dx, fmin(dy,dz)) * 0.5; | |
- const Float cell_volume = 4.0/3.0*M_PI*R*R*R; | |
- | |
- Float void_volume = cell_volume; | |
- Float4 xr; // particle pos. and radius | |
- | |
- // check that we are not outside the fluid grid | |
- if (x < nx && y < ny && z < nz) { | |
- | |
- // Cell sphere center position | |
- const Float3 X = MAKE_FLOAT3( | |
- x*dx + 0.5*dx, | |
- y*dy + 0.5*dy, | |
- z*dz + 0.5*dz); | |
- | |
- Float d, r; | |
- Float phi = 0.99; | |
- | |
- // Read old porosity | |
- __syncthreads(); | |
- Float phi_0 = dev_d_phi[idx(x,y,z)]; | |
- | |
- // The cell 3d index | |
- const int3 gridPos = make_int3((int)x,(int)y,(int)z); | |
- | |
- // The neighbor cell 3d index | |
- int3 targetCell; | |
- | |
- // The distance modifier for particles across periodic boundaries | |
- Float3 dist, distmod; | |
- | |
- // Iterate over 27 neighbor cells | |
- for (int z_dim=-1; z_dim<2; ++z_dim) { // z-axis | |
- for (int y_dim=-1; y_dim<2; ++y_dim) { // y-axis | |
- for (int x_dim=-1; x_dim<2; ++x_dim) { // x-axis | |
- | |
- // Index of neighbor cell this iteration is looking at | |
- targetCell = gridPos + make_int3(x_dim, y_dim, z_dim); | |
- | |
- // Get distance modifier for interparticle | |
- // vector, if it crosses a periodic boundary | |
- // DISTMOD IS NEVER NOT 0,0,0 !!!!!! | |
- distmod = MAKE_FLOAT3(0.0, 0.0, 0.0); | |
- if (findDistMod(&targetCell, &distmod) != -1) { | |
- | |
- // Calculate linear cell ID | |
- const unsigned int cellID = | |
- targetCell.x + targetCell.y * devC_grid.num[0] | |
- + (devC_grid.num[0] * devC_grid.num[1]) | |
- * targetCell.z; | |
- | |
- // Lowest particle index in cell | |
- const unsigned int startIdx = dev_cellStart[cellID]; | |
- | |
- // Make sure cell is not empty | |
- if (startIdx != 0xffffffff) { | |
- | |
- // Highest particle index in cell | |
- const unsigned int endIdx = dev_cellEnd[cellID]; | |
- | |
- // Iterate over cell particles | |
- for (unsigned int i = startIdx; i<endIdx; ++i) { | |
- | |
- // Read particle position and radius | |
- __syncthreads(); | |
- xr = dev_x_sorted[i]; | |
- r = xr.w; | |
- | |
- // Find center distance | |
- dist = MAKE_FLOAT3( | |
- X.x - xr.x, | |
- X.y - xr.y, | |
- X.z - xr.z); | |
- dist += distmod; | |
- d = length(dist); | |
- | |
- // Lens shaped intersection | |
- if ((R - r) < d && d < (R + r)) { | |
- void_volume -= | |
- 1.0/(12.0*d) * ( | |
- M_PI*(R + r - d)*(R + r - d) | |
- *(d*d + 2.0*d*r - 3.0*r*r | |
- + 2.0*d*R + 6.0*r*R | |
- - 3.0*R*R) ); | |
- } | |
- | |
- // Particle fully contained in cell sphere | |
- if (d <= R - r) { | |
- void_volume -= 4.0/3.0*M_PI*r*r*r; | |
- } | |
- } | |
- } | |
- } | |
- } | |
- } | |
- } | |
- | |
- // Make sure that the porosity is in the interval ]0.0;1.0[ | |
- phi = fmin(0.9, fmax(0.1, void_volume/cell_volume)); | |
- //phi = void_volume/cell_volume; | |
- | |
- Float dphi = phi - phi_0; | |
- if (iteration == 0) { | |
- // Do not use the initial CPU porosity estimates | |
- dphi = 0.0; | |
- } | |
- | |
- // Save porosity and porosity change | |
- __syncthreads(); | |
- dev_d_phi[idx(x,y,z)] = phi; | |
- dev_d_dphi[idx(x,y,z)] = dphi; | |
- } | |
-} | |
- | |
- | |
-// Find cell transmissivities from hydraulic conductivities and cell dimensions | |
-// Make sure to compute the porosities (d_phi) beforehand | |
-// d_factor: Grain size factor for Kozeny-Carman relationship | |
-__global__ void findDarcyTransmissivitiesDev( | |
- Float* dev_d_K, | |
- Float3* dev_d_T, | |
- Float* dev_d_phi, | |
- Float d_factor) | |
-{ | |
- // 3D thread index | |
- const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
- const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
- const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
- | |
- // Grid dimensions | |
- const unsigned int nx = devC_grid.num[0]; | |
- const unsigned int ny = devC_grid.num[1]; | |
- const unsigned int nz = devC_grid.num[2]; | |
- | |
- // Grid sizes | |
- const Float dx = devC_grid.L[0]/nx; | |
- const Float dy = devC_grid.L[1]/ny; | |
- const Float dz = devC_grid.L[2]/nz; | |
- | |
- // Density of the fluid [kg/m^3] | |
- const Float rho = 1000.0; | |
- | |
- // Check that we are not outside the fluid grid | |
- if (x < nx && y < ny && z < nz) { | |
- | |
- // 1D thread index | |
- const unsigned int cellidx = idx(x,y,z); | |
- | |
- __syncthreads(); | |
- | |
- // Read the cell porosity [-] | |
- const Float phi = dev_d_phi[cellidx]; | |
- | |
- // Calculate permeability from the Kozeny-Carman relationship | |
- // Nelson 1994 eq. 1c | |
- // Boek 2012 eq. 16 | |
- //k = a*phi*phi*phi/(1.0 - phi*phi); | |
- // Schwartz and Zhang 2003 | |
- Float k = phi*phi*phi/((1.0-phi)*(1.0-phi)) * d_factor; | |
- | |
- // Save hydraulic conductivity [m/s] | |
- //const Float K = k*rho*-devC_params.g[2]/devC_params.nu; | |
- const Float K = k*rho*-devC_params.g[2]/devC_params.nu; | |
- //const Float K = 0.5; | |
- //const Float K = 1.0e-2; | |
- | |
- // Hydraulic transmissivity [m2/s] | |
- Float3 T = {K*dx, K*dy, K*dz}; | |
- | |
- // Save values. Note! The K values are unused | |
- __syncthreads(); | |
- dev_d_K[cellidx] = K; | |
- dev_d_T[cellidx] = T; | |
- | |
- } | |
-} | |
- | |
-// Find the spatial gradient in e.g.pressures per cell | |
-// using first order central differences | |
-__global__ void findDarcyGradientsDev( | |
- Float* dev_scalarfield, // in | |
- Float3* dev_vectorfield) // out | |
-{ | |
- // 3D thread index | |
- const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
- const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
- const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
- | |
- // Grid dimensions | |
- const unsigned int nx = devC_grid.num[0]; | |
- const unsigned int ny = devC_grid.num[1]; | |
- const unsigned int nz = devC_grid.num[2]; | |
- | |
- // Grid sizes | |
- const Float dx = devC_grid.L[0]/nx; | |
- const Float dy = devC_grid.L[1]/ny; | |
- const Float dz = devC_grid.L[2]/nz; | |
- | |
- // 1D thread index | |
- const unsigned int cellidx = idx(x,y,z); | |
- | |
- // Check that we are not outside the fluid grid | |
- Float3 gradient; | |
- Float xp, xn, yp, yn, zp, zn; | |
- if (x < nx && y < ny && z < nz) { | |
- | |
- // Read 6 neighbor cells | |
- __syncthreads(); | |
- xp = dev_scalarfield[idx(x+1,y,z)]; | |
- xn = dev_scalarfield[idx(x-1,y,z)]; | |
- yp = dev_scalarfield[idx(x,y+1,z)]; | |
- yn = dev_scalarfield[idx(x,y-1,z)]; | |
- zp = dev_scalarfield[idx(x,y,z+1)]; | |
- zn = dev_scalarfield[idx(x,y,z-1)]; | |
- | |
- // Calculate central-difference gradients | |
- // x | |
- gradient.x = (xp - xn)/(2.0*dx); | |
- | |
- // y | |
- gradient.y = (yp - yn)/(2.0*dy); | |
- | |
- // z | |
- gradient.z = (zp - zn)/(2.0*dz); | |
- | |
- // Write gradient | |
- __syncthreads(); | |
- dev_vectorfield[cellidx] = gradient; | |
- } | |
-} | |
- | |
-// Arithmetic mean of two numbers | |
-__device__ Float ameanDev(Float a, Float b) { | |
- return (a+b)*0.5; | |
-} | |
- | |
-// Harmonic mean of two numbers | |
-__device__ Float hmeanDev(Float a, Float b) { | |
- return (2.0*a*b)/(a+b); | |
-} | |
- | |
-// Perform an explicit step. | |
-__global__ void explDarcyStepDev( | |
- Float* dev_d_H, | |
- Float* dev_d_H_new, | |
- Float3* dev_d_T, | |
- Float* dev_d_Ss, | |
- Float* dev_d_W, | |
- Float* dev_d_dphi) | |
-{ | |
- // 3D thread index | |
- const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
- const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
- const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
- | |
- // Grid dimensions | |
- const unsigned int nx = devC_grid.num[0]; | |
- const unsigned int ny = devC_grid.num[1]; | |
- const unsigned int nz = devC_grid.num[2]; | |
- | |
- // Cell sizes | |
- const Float dx = devC_grid.L[0]/nx; | |
- const Float dy = devC_grid.L[1]/ny; | |
- const Float dz = devC_grid.L[2]/nz; | |
- | |
- // 1D thread index | |
- const unsigned int cellidx = idx(x,y,z); | |
- | |
- // Check that we are not outside the fluid grid | |
- if (x < nx && y < ny && z < nz) { | |
- | |
- // Explicit 3D finite difference scheme | |
- // new = old + production*timestep + gradient*timestep | |
- | |
- // Enforce Dirichlet BC | |
- /*if (x == 0 || y == 0 || z == 0 || | |
- x == nx-1 || y == ny-1 || z == nz-1) { | |
- __syncthreads(); | |
- dev_d_H_new[cellidx] = dev_d_H[cellidx]; | |
- } else {*/ | |
- | |
- // Read cell and the six neighbor cell hydraulic transmissivities | |
- __syncthreads(); | |
- const Float3 T = dev_d_T[cellidx]; | |
- const Float3 T_xn = dev_d_T[idx(x-1,y,z)]; | |
- const Float3 T_xp = dev_d_T[idx(x+1,y,z)]; | |
- const Float3 T_yn = dev_d_T[idx(x,y-1,z)]; | |
- const Float3 T_yp = dev_d_T[idx(x,y+1,z)]; | |
- const Float3 T_zn = dev_d_T[idx(x,y,z-1)]; | |
- const Float3 T_zp = dev_d_T[idx(x,y,z+1)]; | |
- | |
- // Read the cell hydraulic specific storativity | |
- const Float Ss = dev_d_Ss[cellidx]; | |
- | |
- // Read the cell hydraulic recharge | |
- const Float Q = dev_d_W[cellidx]; | |
- | |
- // Read the cell porosity change | |
- const Float dphi = dev_d_dphi[cellidx]; | |
- | |
- // Read the cell and the six neighbor cell hydraulic pressures | |
- const Float H = dev_d_H[cellidx]; | |
- const Float H_xn = dev_d_H[idx(x-1,y,z)]; | |
- const Float H_xp = dev_d_H[idx(x+1,y,z)]; | |
- const Float H_yn = dev_d_H[idx(x,y-1,z)]; | |
- const Float H_yp = dev_d_H[idx(x,y+1,z)]; | |
- const Float H_zn = dev_d_H[idx(x,y,z-1)]; | |
- const Float H_zp = dev_d_H[idx(x,y,z+1)]; | |
- | |
- // Calculate the gradients in the pressure between | |
- // the cell and it's six neighbors | |
- const Float TdH_xn = hmeanDev(T.x, T_xn.x) * (H_xn - H)/(dx*dx); | |
- const Float TdH_xp = hmeanDev(T.x, T_xp.x) * (H_xp - H)/(dx*dx); | |
- const Float TdH_yn = hmeanDev(T.y, T_yn.y) * (H_yn - H)/(dy*dy); | |
- const Float TdH_yp = hmeanDev(T.y, T_yp.y) * (H_yp - H)/(dy*dy); | |
- Float TdH_zn = hmeanDev(T.z, T_zn.z) * (H_zn - H)/(dz*dz); | |
- Float TdH_zp = hmeanDev(T.z, T_zp.z) * (H_zp - H)/(dz*dz); | |
- | |
- // Mean cell dimension | |
- const Float dx_bar = (dx+dy+dz)/3.0; | |
- | |
- // Neumann (no-flow) boundary condition at +z and -z boundaries | |
- // enforced by a gradient value of 0.0 | |
- if (z == 0) | |
- TdH_zn = 0.0; | |
- if (z == nz-1) | |
- TdH_zp = 0.0; | |
- | |
- // Determine the Laplacian operator | |
- const Float laplacianH = | |
- TdH_xn + TdH_xp | |
- + TdH_yn + TdH_yp | |
- + TdH_zn + TdH_zp; | |
- | |
- const Float porevol = -dx_bar*dphi/devC_dt; | |
- | |
- // The change in hydraulic pressure | |
- const Float deltaH = devC_dt/(Ss*dx*dy*dz)*(laplacianH + Q + porevol); | |
- | |
- /*printf("%d,%d,%d: H = %f, deltaH = %f,\tlaplacianH = %f," | |
- "dphi = %f,\tpore = %f, " | |
- "TdH_xn = %f, TdH_xp = %f, " | |
- "TdH_yn = %f, TdH_yp = %f, " | |
- "TdH_zn = %f, TdH_zp = %f\n" | |
- "\tT_xn = %f,\tT_xp = %f,\t" | |
- "T_yn = %f,\tT_yp = %f,\t" | |
- "T_zn = %f,\tT_zp = %f\n", | |
- x,y,z, H, deltaH, laplacianH, dphi, porevol, | |
- TdH_xn, TdH_xp, | |
- TdH_yn, TdH_yp, | |
- TdH_zn, TdH_zp, | |
- T_xn.x, T_xp.x, | |
- T_yn.y, T_yp.y, | |
- T_zn.z, T_zp.z);*/ | |
- | |
- // The pressure should never be negative | |
- const Float H_new = fmax(0.0, H + deltaH); | |
- | |
- // Write the new hydraulic pressure in cell | |
- __syncthreads(); | |
- dev_d_H_new[cellidx] = H_new; | |
- //} | |
- } | |
-} | |
- | |
-// Find cell velocity | |
-__global__ void findDarcyVelocitiesDev( | |
- Float* dev_d_H, | |
- Float3* dev_d_dH, | |
- Float3* dev_d_V, | |
- Float* dev_d_phi, | |
- Float* dev_d_K) | |
-{ | |
- // 3D thread index | |
- const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
- const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
- const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
- | |
- // 1D thread index | |
- const unsigned int cellidx = idx(x,y,z); | |
- | |
- // Check that we are not outside the fluid grid | |
- if (x < devC_grid.num[0] && y < devC_grid.num[1] && z < devC_grid.num[2]) { | |
- | |
- // Flux [m/s]: q = -k/nu * dH | |
- // Pore velocity [m/s]: v = q/n | |
- | |
- // Dynamic viscosity | |
- Float nu = devC_params.nu; | |
- | |
- __syncthreads(); | |
- const Float3 dH = dev_d_dH[cellidx]; | |
- const Float K = dev_d_K[cellidx]; | |
- const Float phi = dev_d_phi[cellidx]; | |
- | |
- // Calculate flux | |
- // The sign might need to be reversed, depending on the | |
- // grid orientation | |
- Float3 q = MAKE_FLOAT3( | |
- -K/nu * dH.x, | |
- -K/nu * dH.y, | |
- -K/nu * dH.z); | |
- | |
- // Calculate velocity | |
- Float3 v = MAKE_FLOAT3( | |
- v.x = q.x/phi, | |
- v.y = q.y/phi, | |
- v.z = q.z/phi); | |
- | |
- // Save velocity | |
- __syncthreads(); | |
- dev_d_V[cellidx] = v; | |
- } | |
-} | |
- | |
-// Print final heads and free memory | |
-void DEM::endDarcyDev() | |
-{ | |
- freeDarcyMemDev(); | |
-} | |
- | |
-// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 | |
diff --git a/src/datatypes.h b/src/datatypes.h | |
t@@ -4,7 +4,6 @@ | |
#include <math.h> | |
#include "vector_functions.h" | |
-//#include "vector_arithmetic.h" | |
#include "typedefs.h" | |
#include "constants.h" | |
t@@ -19,13 +18,13 @@ struct Kinematics { | |
Float2 *xysum; // Horizontal distance traveled | |
Float4 *vel; // Translational velocities + fixvels (w) | |
Float4 *acc; // Translational accelerations | |
- Float4 *force; // Sums of forces | |
+ Float4 *force; // Sums of forces | |
Float4 *angpos; // Angular positions | |
Float4 *angvel; // Angular velocities | |
Float4 *angacc; // Angular accelerations | |
Float4 *torque; // Sums of torques | |
unsigned int *contacts; // List of contacts per particle | |
- Float4 *distmod; // Distance modifiers for contacts across periodic… | |
+ Float4 *distmod; // Distance modifiers across periodic boundaries | |
Float4 *delta_t; // Accumulated shear distance of contacts | |
uint2 *bonds; // Particle bond pairs | |
Float4 *bonds_delta; // Particle bond displacement | |
t@@ -34,102 +33,118 @@ struct Kinematics { | |
// Structure containing individual particle energies | |
struct Energies { | |
- Float *es_dot; // Frictional dissipation rates | |
- Float *es; // Frictional dissipations | |
- Float *ev_dot; // Viscous dissipation rates | |
- Float *ev; // Viscous dissipations | |
- Float *p; // Pressures | |
+ Float *es_dot; // Frictional dissipation rates | |
+ Float *es; // Frictional dissipations | |
+ Float *ev_dot; // Viscous dissipation rates | |
+ Float *ev; // Viscous dissipations | |
+ Float *p; // Pressures | |
}; | |
// Structure containing grid parameters | |
struct Grid { | |
- Float origo[ND]; // World coordinate system origo | |
- Float L[ND]; // World dimensions | |
- unsigned int num[ND]; // Neighbor-search cells along each axis | |
- int periodic; // Behavior of boundaries at 1st and 2nd worl… | |
+ Float origo[ND]; // World coordinate system origo | |
+ Float L[ND]; // World dimensions | |
+ unsigned int num[ND]; // Neighbor-search cells along each axis | |
+ int periodic; // Behavior of boundaries at 1st and 2nd world edge | |
}; | |
struct Sorting { | |
- Float4 *x_sorted; // Positions + radii (w) (sorted) | |
- Float4 *vel_sorted; // Translational velocities + fixvels (w) (so… | |
- Float4 *angvel_sorted; // Angular velocities (sorted) | |
- unsigned int *gridParticleCellID; // Hash key (cell index) from position i… | |
+ Float4 *x_sorted; // Positions + radii (w) (sorted) | |
+ Float4 *vel_sorted; // Velocities + fixvels (w) (sorted) | |
+ Float4 *angvel_sorted; // Angular velocities (sorted) | |
+ unsigned int *gridParticleCellID; // Hash key (cell idx) in grid | |
unsigned int *gridParticleIndex; // Original indexes of particles | |
- unsigned int *cellStart; // First index of sorted idx'es in cel… | |
- unsigned int *cellEnd; // Last index of sorted idx'es in cells | |
+ unsigned int *cellStart; // First index of sorted idx'es in cells | |
+ unsigned int *cellEnd; // Last index of sorted idx'es in cells | |
}; | |
// Structure containing time parameters | |
struct Time { | |
- Float dt; // Computational time step length | |
- double current; // Current time | |
- double total; // Total time (at the end of experiment) | |
- Float file_dt; // Time between output files | |
- unsigned int step_count; // Number of steps taken at current time | |
+ Float dt; // Computational time step length | |
+ double current; // Current time | |
+ double total; // Total time (at the end of experiment) | |
+ Float file_dt; // Time between output files | |
+ unsigned int step_count; // Number of output files written | |
}; | |
// Structure containing constant, global physical parameters | |
struct Params { | |
- Float g[ND]; // Gravitational acceleration | |
- Float k_n; // Normal stiffness | |
- Float k_t; // Tangential stiffness | |
- Float k_r; // Rotational stiffness | |
- Float gamma_n; // Normal viscosity | |
- Float gamma_t; // Tangential viscosity | |
- Float gamma_r; // Rotational viscosity | |
- Float mu_s; // Static friction coefficient | |
- Float mu_d; // Dynamic friction coefficient | |
- Float mu_r; // Rotational friction coefficient | |
- Float gamma_wn; // Wall normal viscosity | |
- Float gamma_wt; // Wall tangential viscosity | |
- Float mu_ws; // Wall static friction coefficient | |
- Float mu_wd; // Wall dynamic friction coefficient | |
- Float rho; // Material density | |
+ Float g[ND]; // Gravitational acceleration | |
+ Float k_n; // Normal stiffness | |
+ Float k_t; // Tangential stiffness | |
+ Float k_r; // Rotational stiffness | |
+ Float gamma_n; // Normal viscosity | |
+ Float gamma_t; // Tangential viscosity | |
+ Float gamma_r; // Rotational viscosity | |
+ Float mu_s; // Static friction coefficient | |
+ Float mu_d; // Dynamic friction coefficient | |
+ Float mu_r; // Rotational friction coefficient | |
+ Float gamma_wn; // Wall normal viscosity | |
+ Float gamma_wt; // Wall tangential viscosity | |
+ Float mu_ws; // Wall static friction coefficient | |
+ Float mu_wd; // Wall dynamic friction coefficient | |
+ Float rho; // Material density | |
unsigned int contactmodel; // Inter-particle contact model | |
- Float kappa; // Capillary bond prefactor | |
- Float db; // Capillary bond debonding distance | |
- Float V_b; // Volume of fluid in capillary bond | |
+ Float kappa; // Capillary bond prefactor | |
+ Float db; // Capillary bond debonding distance | |
+ Float V_b; // Volume of fluid in capillary bond | |
Float lambda_bar; // Radius multiplier to parallel-bond radii | |
unsigned int nb0; // Number of inter-particle bonds at t=0 | |
Float sigma_b; // Bond tensile strength | |
Float tau_b; // Bond shear strength | |
- Float devs_A; // Amplitude of modulations in deviatoric normal str… | |
- Float devs_f; // Frequency of modulations in deviatoric normal str… | |
- Float nu; // Fluid dynamic viscosity | |
+ Float devs_A; // Amplitude of modulations in normal stress | |
+ Float devs_f; // Frequency of modulations in normal stress | |
+ Float mu; // Fluid dynamic viscosity | |
+ Float rho_f; // Fluid density | |
}; | |
// Structure containing wall parameters | |
struct Walls { | |
- unsigned int nw; // Number of walls (<= MAXWALLS) | |
- int wmode[MAXWALLS]; // Wall modes | |
- Float4* nx; // Wall normal and position | |
- Float4* mvfd; // Wall mass, velocity, force and dev. stress | |
+ unsigned int nw; // Number of walls (<= MAXWALLS) | |
+ int wmode[MAXWALLS]; // Wall modes | |
+ Float4* nx; // Wall normal and position | |
+ Float4* mvfd; // Wall mass, velocity, force and dev. stress | |
}; | |
// Structures containing fluid parameters | |
-struct Darcy { | |
- int nx, ny, nz; // Number of cells in each dim | |
- Float dx, dy, dz; // Cell length in each dim | |
- Float* H; // Cell hydraulic heads | |
- Float* H_new; // Cell hydraulic heads | |
- Float3* V; // Cell fluid velocity | |
- Float3* dH; // Cell spatial gradient in heads | |
- Float* K; // Cell hydraulic conductivities (anisotropic) | |
- Float3* T; // Cell hydraulic transmissivity | |
- Float* Ss; // Cell hydraulic storativity | |
- Float* W; // Cell hydraulic recharge | |
- Float* phi; // Cell porosity | |
- Float* dphi; // Cell porosity change | |
+struct NavierStokes { | |
+ int nx, ny, nz; // Number of cells in each dim | |
+ Float dx, dy, dz; // Cell length in each dim | |
+ Float* p; // Cell hydraulic pressures | |
+ Float3* v; // Cell fluid velocity | |
+ Float* v_x; // Fluid velocity in staggered grid | |
+ Float* v_y; // Fluid velocity in staggered grid | |
+ Float* v_z; // Fluid velocity in staggered grid | |
+ Float3* v_p; // Predicted fluid velocity | |
+ Float* v_p_x; // Predicted fluid velocity in staggered grid | |
+ Float* v_p_y; // Predicted fluid velocity in staggered grid | |
+ Float* v_p_z; // Predicted fluid velocity in staggered grid | |
+ Float* phi; // Cell porosity | |
+ Float* dphi; // Cell porosity change | |
+ Float* norm; // Normalized residual of epsilon updates | |
+ Float* epsilon; // Iterative solution parameter | |
+ Float* epsilon_new; // Updated value of iterative solution parameter | |
+ Float p_mod_A; // Pressure modulation amplitude at top | |
+ Float p_mod_f; // Pressure modulation frequency at top | |
+ Float p_mod_phi; // Pressure modulation phase at top | |
+ int bc_bot; // 0: Dirichlet, 1: Neumann | |
+ int bc_top; // 0: Dirichlet, 1: Neumann | |
+ int free_slip_bot; // 0: no, 1: yes | |
+ int free_slip_top; // 0: no, 1: yes | |
+ Float gamma; // Solver parameter: Smoothing | |
+ Float theta; // Solver parameter: Under-relaxation | |
+ Float beta; // Solver parameter: Solution method | |
+ Float tolerance; // Solver parameter: Max residual tolerance | |
+ unsigned int maxiter; // Solver parameter: Max iterations to perform | |
}; | |
- | |
// Image structure | |
struct rgba { | |
- unsigned char r; // Red | |
- unsigned char g; // Green | |
- unsigned char b; // Blue | |
- unsigned char a; // Alpha | |
+ unsigned char r; // Red | |
+ unsigned char g; // Green | |
+ unsigned char b; // Blue | |
+ unsigned char a; // Alpha | |
}; | |
#endif | |
diff --git a/src/debug.h b/src/debug.h | |
t@@ -11,4 +11,27 @@ | |
// 1: Yes | |
#define CONTACTINFO 0 | |
+// The number of fluid solver iterations to perform between checking the norm. | |
+// residual value | |
+const unsigned int nijacnorm = 10; | |
+ | |
+// Write max. residual during the latest solution loop to logfile | |
+// 'max_res_norm.dat' | |
+// 0: False, 1: True | |
+const int write_res_log = 0; | |
+ | |
+// Report epsilon values during Jacobi iterations to stdout | |
+// 0: False, 1: True | |
+const int report_epsilon = 0; | |
+const int report_even_more_epsilon = 0; | |
+ | |
+// Report the number of iterations it took before convergence to logfile | |
+// 'output/<sid>-conv.dat' | |
+// 0: False, 1: True | |
+const int write_conv_log = 1; | |
+ | |
+// The interval between iteration number reporting in 'output/<sid>-conv.log' | |
+const int conv_log_interval = 10; | |
+//const int conv_log_interval = 1; | |
+ | |
#endif | |
diff --git a/src/device.cu b/src/device.cu | |
t@@ -1,9 +1,9 @@ | |
// device.cu -- GPU specific operations utilizing the CUDA API. | |
#include <iostream> | |
+#include <fstream> | |
#include <string> | |
#include <cstdio> | |
#include <cuda.h> | |
-//#include <cutil_math.h> | |
#include <helper_math.h> | |
#include "vector_arithmetic.h" // for arbitrary prec. vectors | |
t@@ -13,7 +13,6 @@ | |
#include "sphere.h" | |
#include "datatypes.h" | |
-#include "utility.cuh" | |
#include "utility.h" | |
#include "constants.cuh" | |
#include "debug.h" | |
t@@ -24,13 +23,10 @@ | |
#include "contactsearch.cuh" | |
#include "integration.cuh" | |
#include "raytracer.cuh" | |
-#include "latticeboltzmann.cuh" | |
-#include "darcy.cuh" | |
- | |
+#include "navierstokes.cuh" | |
// Wrapper function for initializing the CUDA components. | |
// Called from main.cpp | |
-//extern "C" | |
__host__ void DEM::initializeGPU(void) | |
{ | |
using std::cout; // stdout | |
t@@ -144,7 +140,8 @@ __global__ void checkConstantValues(int* dev_equal, | |
dev_params->V_b != devC_params.V_b || | |
dev_params->lambda_bar != devC_params.lambda_bar || | |
dev_params->nb0 != devC_params.nb0 || | |
- dev_params->nu != devC_params.nu) | |
+ dev_params->mu != devC_params.mu || | |
+ dev_params->rho_f != devC_params.rho_f) | |
*dev_equal = 2; // Not ok | |
} | |
t@@ -303,21 +300,9 @@ __host__ void DEM::allocateGlobalDeviceMemory(void) | |
cudaMalloc((void**)&dev_walls_nx, sizeof(Float4)*walls.nw); | |
cudaMalloc((void**)&dev_walls_mvfd, sizeof(Float4)*walls.nw); | |
cudaMalloc((void**)&dev_walls_force_pp, sizeof(Float)*walls.nw*np); | |
- cudaMalloc((void**)&dev_walls_vel0, sizeof(Float)*walls.nw); | |
+ cudaMalloc((void**)&dev_walls_acc, sizeof(Float)*walls.nw); | |
// dev_walls_force_partial allocated later | |
- // Fluid arrays | |
-#ifdef LBM_GPU | |
- if (params.nu > 0.0 && darcy == 0) { | |
- cudaMalloc((void**)&dev_f, | |
- sizeof(Float)*grid.num[0]*grid.num[1]*grid.num[2]*19); | |
- cudaMalloc((void**)&dev_f_new, | |
- sizeof(Float)*grid.num[0]*grid.num[1]*grid.num[2]*19); | |
- cudaMalloc((void**)&dev_v_rho, | |
- sizeof(Float4)*grid.num[0]*grid.num[1]*grid.num[2]); | |
- } | |
-#endif | |
- | |
checkForCudaErrors("End of allocateGlobalDeviceMemory"); | |
if (verbose == 1) | |
std::cout << "Done" << std::endl; | |
t@@ -367,24 +352,14 @@ __host__ void DEM::freeGlobalDeviceMemory() | |
cudaFree(dev_walls_mvfd); | |
cudaFree(dev_walls_force_partial); | |
cudaFree(dev_walls_force_pp); | |
- cudaFree(dev_walls_vel0); | |
+ cudaFree(dev_walls_acc); | |
// Fluid arrays | |
-#ifdef LBM_GPU | |
- if (params.nu > 0.0 && darcy == 0) { | |
- cudaFree(dev_f); | |
- cudaFree(dev_f_new); | |
- cudaFree(dev_v_rho); | |
+ if (navierstokes == 1) { | |
+ freeNSmemDev(); | |
} | |
-#endif | |
-#ifdef DARCY_GPU | |
- if (params.nu > 0.0 && darcy == 1) { | |
- freeDarcyMemDev(); | |
- } | |
-#endif | |
- | |
- checkForCudaErrors("During cudaFree calls"); | |
+ //checkForCudaErrors("During cudaFree calls"); | |
if (verbose == 1) | |
std::cout << "Done" << std::endl; | |
t@@ -458,28 +433,13 @@ __host__ void DEM::transferToGlobalDeviceMemory(int stat… | |
sizeof(Float4)*walls.nw, cudaMemcpyHostToDevice); | |
cudaMemcpy( dev_walls_mvfd, walls.mvfd, | |
sizeof(Float4)*walls.nw, cudaMemcpyHostToDevice); | |
- for (int i = 0; i<walls.nw; ++i) { | |
- cudaMemcpy( &dev_walls_vel0[i], &walls.mvfd[i].y, | |
- sizeof(Float), cudaMemcpyHostToDevice); | |
- } | |
// Fluid arrays | |
- if (params.nu > 0.0) { | |
- if (darcy == 0) { | |
-#ifdef LBM_GPU | |
- cudaMemcpy( dev_f, f, | |
- sizeof(Float)*grid.num[0]*grid.num[1]*grid.num[2]*19, | |
- cudaMemcpyHostToDevice); | |
- cudaMemcpy( dev_v_rho, v_rho, | |
- sizeof(Float4)*grid.num[0]*grid.num[1]*grid.num[2], | |
- cudaMemcpyHostToDevice); | |
-#endif | |
- } else if (darcy == 1) { | |
- transferDarcyToGlobalDeviceMemory(1); | |
- } else { | |
- std::cerr << "Error: Darcy value not understood (" | |
- << darcy << ")" << std::endl; | |
- } | |
+ if (navierstokes == 1) { | |
+ transferNStoGlobalDeviceMemory(1); | |
+ } else if (navierstokes != 0) { | |
+ std::cerr << "Error: navierstokes value not understood (" | |
+ << navierstokes << ")" << std::endl; | |
} | |
checkForCudaErrors("End of transferToGlobalDeviceMemory"); | |
t@@ -551,24 +511,11 @@ __host__ void DEM::transferFromGlobalDeviceMemory() | |
sizeof(Float4)*walls.nw, cudaMemcpyDeviceToHost); | |
// Fluid arrays | |
- if (params.nu > 0.0) { | |
- if (darcy == 0) { | |
-#ifdef LBM_GPU | |
- cudaMemcpy( f, dev_f, | |
- sizeof(Float)*grid.num[0]*grid.num[1]*grid.num[2]*19, | |
- cudaMemcpyDeviceToHost); | |
- cudaMemcpy(v_rho, dev_v_rho, | |
- sizeof(Float4)*grid.num[0]*grid.num[1]*grid.num[2], | |
- cudaMemcpyDeviceToHost); | |
-#endif | |
- } else { | |
-#ifdef DARCY_GPU | |
- transferDarcyFromGlobalDeviceMemory(0); | |
-#endif | |
- } | |
+ if (navierstokes == 1) { | |
+ transferNSfromGlobalDeviceMemory(0); | |
} | |
- checkForCudaErrors("End of transferFromGlobalDeviceMemory"); | |
+ //checkForCudaErrors("End of transferFromGlobalDeviceMemory"); | |
} | |
t@@ -590,8 +537,10 @@ __host__ void DEM::startTime() | |
// Write initial data to output/<sid>.output00000.bin | |
writebin(("output/" + sid + ".output00000.bin").c_str()); | |
- // Model world variables | |
- float tic, toc, filetimeclock, time_spent, dev_time_spent; | |
+ // Time variables | |
+ clock_t tic, toc; | |
+ double filetimeclock, time_spent; | |
+ float dev_time_spent; | |
// Start CPU clock | |
tic = clock(); | |
t@@ -614,7 +563,7 @@ __host__ void DEM::startTime() | |
iDivUp(grid.num[0], dimBlockFluid.x), | |
iDivUp(grid.num[1], dimBlockFluid.y), | |
iDivUp(grid.num[2], dimBlockFluid.z)); | |
- if (dimGridFluid.z > 64 && params.nu > 0.0) { | |
+ if (dimGridFluid.z > 64 && navierstokes == 1) { | |
cerr << "Error: dimGridFluid.z > 64" << endl; | |
exit(1); | |
} | |
t@@ -636,7 +585,7 @@ __host__ void DEM::startTime() | |
<< dimBlock.x << "*" << dimBlock.y << "*" << dimBlock.z << "\n" | |
<< " - Shared memory required per block: " << smemSize << " bytes" | |
<< endl; | |
- if (params.nu > 0.0 && darcy == 0) { | |
+ if (navierstokes == 1) { | |
cout << " - Blocks per fluid grid: " | |
<< dimGridFluid.x << "*" << dimGridFluid.y << "*" << | |
dimGridFluid.z << "\n" | |
t@@ -661,27 +610,6 @@ __host__ void DEM::startTime() | |
time.step_count); | |
fclose(fp); | |
- // Initialize fluid distribution array | |
- Float d_factor; | |
- if (params.nu > 0.0) { | |
- if (darcy == 0) { | |
-#ifdef LBM_GPU | |
- initFluid<<< dimGridFluid, dimBlockFluid >>>(dev_v_rho, dev_f); | |
- cudaThreadSynchronize(); | |
-#endif | |
- } else if (darcy == 1) { | |
-#ifdef DARCY_GPU | |
- // Representative grain radius | |
- const Float r_bar2 = meanRadius()*2.0; | |
- // Grain size factor for Kozeny-Carman relationship | |
- d_factor = r_bar2*r_bar2/180.0 * 1.0e-6; | |
-#endif | |
- } else { | |
- std::cerr << "Error, darcy value (" << darcy | |
- << ") not understood." << std::endl; | |
- } | |
- } | |
- | |
if (verbose == 1) { | |
cout << "\n Entering the main calculation time loop...\n\n" | |
<< " IMPORTANT: Do not close this terminal, doing so will \n" | |
t@@ -712,17 +640,34 @@ __host__ void DEM::startTime() | |
double t_integrateWalls = 0.0; | |
double t_findPorositiesDev = 0.0; | |
- double t_findDarcyTransmissivitiesDev = 0.0; | |
- double t_setDarcyGhostNodesDev = 0.0; | |
- double t_explDarcyStepDev = 0.0; | |
- double t_findDarcyGradientsDev = 0.0; | |
- double t_findDarcyVelocitiesDev = 0.0; | |
+ double t_findNSstressTensor = 0.0; | |
+ double t_findNSdivphiviv = 0.0; | |
+ double t_findNSdivphitau = 0.0; | |
+ double t_findPredNSvelocities = 0.0; | |
+ double t_setNSepsilon = 0.0; | |
+ double t_setNSdirichlet = 0.0; | |
+ double t_setNSghostNodesDev = 0.0; | |
+ double t_findNSforcing = 0.0; | |
+ double t_jacobiIterationNS = 0.0; | |
+ double t_updateNSvelocityPressure = 0.0; | |
if (PROFILING == 1) { | |
cudaEventCreate(&kernel_tic); | |
cudaEventCreate(&kernel_toc); | |
} | |
+ // The model start time is saved for profiling performance | |
+ double t_start = time.current; | |
+ double t_ratio; // ration between time flow in model vs. reality | |
+ | |
+ // Write a log file of the number of iterations it took before | |
+ // convergence in the fluid solver | |
+ std::ofstream convlog; | |
+ if (write_conv_log == 1) { | |
+ std::string f = "output/" + sid + "-conv.log"; | |
+ convlog.open(f.c_str()); | |
+ } | |
+ | |
if (verbose == 1) | |
cout << " Current simulation time: " << time.current << " s."; | |
t@@ -738,383 +683,775 @@ __host__ void DEM::startTime() | |
// Routine check for errors | |
checkForCudaErrors("Start of main while loop"); | |
+ if (np > 0) { | |
- // For each particle: | |
- // Compute hash key (cell index) from position | |
- // in the fine, uniform and homogenous grid. | |
- if (PROFILING == 1) | |
- startTimer(&kernel_tic); | |
- calcParticleCellID<<<dimGrid, dimBlock>>>(dev_gridParticleCellID, | |
- dev_gridParticleIndex, | |
- dev_x); | |
- | |
- // Synchronization point | |
- cudaThreadSynchronize(); | |
- if (PROFILING == 1) | |
- stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
- &t_calcParticleCellID); | |
- checkForCudaErrors("Post calcParticleCellID"); | |
- | |
- | |
- // Sort particle (key, particle ID) pairs by hash key with Thrust radix | |
- // sort | |
- if (PROFILING == 1) | |
- startTimer(&kernel_tic); | |
- thrust::sort_by_key(thrust::device_ptr<uint>(dev_gridParticleCellID), | |
- thrust::device_ptr<uint>(dev_gridParticleCellID + np), | |
- thrust::device_ptr<uint>(dev_gridParticleIndex)); | |
- cudaThreadSynchronize(); // Needed? Does thrust synchronize implicitly? | |
- if (PROFILING == 1) | |
- stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, &t_thrustsort… | |
- checkForCudaErrors("Post thrust::sort_by_key"); | |
- | |
- | |
- // Zero cell array values by setting cellStart to its highest possible | |
- // value, specified with pointer value 0xffffffff, which for a 32 bit | |
- // unsigned int | |
- // is 4294967295. | |
- cudaMemset(dev_cellStart, 0xffffffff, | |
- grid.num[0]*grid.num[1]*grid.num[2]*sizeof(unsigned int)); | |
- cudaThreadSynchronize(); | |
- checkForCudaErrors("Post cudaMemset"); | |
- | |
- // Use sorted order to reorder particle arrays (position, velocities, | |
- // radii) to ensure coherent memory access. Save ordered configurations | |
- // in new arrays (*_sorted). | |
- if (PROFILING == 1) | |
- startTimer(&kernel_tic); | |
- reorderArrays<<<dimGrid, dimBlock, smemSize>>>(dev_cellStart, | |
- dev_cellEnd, | |
- dev_gridParticleCellID, | |
- dev_gridParticleIndex, | |
- dev_x, dev_vel, | |
- dev_angvel, | |
- dev_x_sorted, | |
- dev_vel_sorted, | |
- dev_angvel_sorted); | |
- | |
- // Synchronization point | |
- cudaThreadSynchronize(); | |
- if (PROFILING == 1) | |
- stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, &t_reorderArr… | |
- checkForCudaErrors("Post reorderArrays", iter); | |
- | |
- // The contact search in topology() is only necessary for determining | |
- // the accumulated shear distance needed in the linear elastic | |
- // and nonlinear contact force model | |
- if (params.contactmodel == 2 || params.contactmodel == 3) { | |
- // For each particle: Search contacts in neighbor cells | |
+ // For each particle: | |
+ // Compute hash key (cell index) from position | |
+ // in the fine, uniform and homogenous grid. | |
if (PROFILING == 1) | |
startTimer(&kernel_tic); | |
- topology<<<dimGrid, dimBlock>>>(dev_cellStart, | |
- dev_cellEnd, | |
- dev_gridParticleIndex, | |
- dev_x_sorted, | |
- dev_contacts, | |
- dev_distmod); | |
- | |
+ calcParticleCellID<<<dimGrid, dimBlock>>>(dev_gridParticleCellID, | |
+ dev_gridParticleIndex, | |
+ dev_x); | |
// Synchronization point | |
cudaThreadSynchronize(); | |
if (PROFILING == 1) | |
- stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, &t_topolo… | |
- checkForCudaErrors("Post topology: One or more particles moved out… | |
- } | |
+ stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
+ &t_calcParticleCellID); | |
+ checkForCudaErrorsIter("Post calcParticleCellID", iter); | |
- // For each particle: Process collisions and compute resulting forces. | |
- //cudaPrintfInit(); | |
- if (PROFILING == 1) | |
- startTimer(&kernel_tic); | |
- interact<<<dimGrid, dimBlock>>>(dev_gridParticleIndex, | |
- dev_cellStart, | |
- dev_cellEnd, | |
- dev_x, | |
- dev_x_sorted, | |
- dev_vel_sorted, | |
- dev_angvel_sorted, | |
- dev_vel, | |
- dev_angvel, | |
- dev_force, | |
- dev_torque, | |
- dev_es_dot, | |
- dev_ev_dot, | |
- dev_es, | |
- dev_ev, | |
- dev_p, | |
- dev_walls_nx, | |
- dev_walls_mvfd, | |
- dev_walls_force_pp, | |
- dev_contacts, | |
- dev_distmod, | |
- dev_delta_t); | |
- | |
- | |
- // Synchronization point | |
- cudaThreadSynchronize(); | |
- //cudaPrintfDisplay(stdout, true); | |
- if (PROFILING == 1) | |
- stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, &t_interact); | |
- checkForCudaErrors("Post interact - often caused if particles move out… | |
- | |
- // Process particle pairs | |
- if (params.nb0 > 0) { | |
+ // Sort particle (key, particle ID) pairs by hash key with Thrust | |
+ // radix sort | |
if (PROFILING == 1) | |
startTimer(&kernel_tic); | |
- bondsLinear<<<dimGridBonds, dimBlock>>>( | |
- dev_bonds, | |
- dev_bonds_delta, | |
- dev_bonds_omega, | |
- dev_x, | |
- dev_vel, | |
+ thrust::sort_by_key( | |
+ thrust::device_ptr<uint>(dev_gridParticleCellID), | |
+ thrust::device_ptr<uint>(dev_gridParticleCellID + np), | |
+ thrust::device_ptr<uint>(dev_gridParticleIndex)); | |
+ cudaThreadSynchronize(); // Maybe Thrust synchronizes implicitly? | |
+ if (PROFILING == 1) | |
+ stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
+ &t_thrustsort); | |
+ checkForCudaErrorsIter("Post thrust::sort_by_key", iter); | |
+ | |
+ | |
+ // Zero cell array values by setting cellStart to its highest | |
+ // possible value, specified with pointer value 0xffffffff, which | |
+ // for a 32 bit unsigned int is 4294967295. | |
+ cudaMemset(dev_cellStart, 0xffffffff, | |
+ grid.num[0]*grid.num[1]*grid.num[2]*sizeof(unsigned int)); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrorsIter("Post cudaMemset", iter); | |
+ | |
+ // Use sorted order to reorder particle arrays (position, | |
+ // velocities, radii) to ensure coherent memory access. Save order… | |
+ // configurations in new arrays (*_sorted). | |
+ if (PROFILING == 1) | |
+ startTimer(&kernel_tic); | |
+ reorderArrays<<<dimGrid, dimBlock, smemSize>>>(dev_cellStart, | |
+ dev_cellEnd, | |
+ dev_gridParticleCellID, | |
+ dev_gridParticleIndex, | |
+ dev_x, dev_vel, | |
dev_angvel, | |
- dev_force, | |
- dev_torque); | |
+ dev_x_sorted, | |
+ dev_vel_sorted, | |
+ dev_angvel_sorted); | |
+ | |
// Synchronization point | |
cudaThreadSynchronize(); | |
- //cudaPrintfDisplay(stdout, true); | |
if (PROFILING == 1) | |
- stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, &t_bondsL… | |
- checkForCudaErrors("Post bondsLinear", iter); | |
- } | |
+ stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
+ &t_reorderArrays); | |
+ checkForCudaErrorsIter("Post reorderArrays", iter); | |
+ | |
+ // The contact search in topology() is only necessary for | |
+ // determining the accumulated shear distance needed in the linear | |
+ // elastic and nonlinear contact force model | |
+ if (params.contactmodel == 2 || params.contactmodel == 3) { | |
+ // For each particle: Search contacts in neighbor cells | |
+ if (PROFILING == 1) | |
+ startTimer(&kernel_tic); | |
+ topology<<<dimGrid, dimBlock>>>(dev_cellStart, | |
+ dev_cellEnd, | |
+ dev_gridParticleIndex, | |
+ dev_x_sorted, | |
+ dev_contacts, | |
+ dev_distmod); | |
+ | |
+ | |
+ // Synchronization point | |
+ cudaThreadSynchronize(); | |
+ if (PROFILING == 1) | |
+ stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
+ &t_topology); | |
+ checkForCudaErrorsIter( | |
+ "Post topology: One or more particles moved " | |
+ "outside the grid.\nThis could possibly be caused by a… | |
+ "numerical instability.\nIs the computational time ste… | |
+ " too large?", iter); | |
+ } | |
+ | |
- // Process fluid and particle interaction in each cell | |
- if (params.nu > 0.0 && darcy == 0 && grid.periodic == 1) { | |
-#ifdef LBM_GPU | |
+ // For each particle process collisions and compute resulting forc… | |
+ //cudaPrintfInit(); | |
if (PROFILING == 1) | |
startTimer(&kernel_tic); | |
- latticeBoltzmannD3Q19<<<dimGridFluid, dimBlockFluid>>> ( | |
- dev_f, | |
- dev_f_new, | |
- dev_v_rho, | |
+ interact<<<dimGrid, dimBlock>>>(dev_gridParticleIndex, | |
dev_cellStart, | |
dev_cellEnd, | |
+ dev_x, | |
dev_x_sorted, | |
dev_vel_sorted, | |
- dev_force, | |
- dev_gridParticleIndex); | |
+ dev_angvel_sorted, | |
+ dev_vel, | |
+ dev_angvel, | |
+ dev_force, | |
+ dev_torque, | |
+ dev_es_dot, | |
+ dev_ev_dot, | |
+ dev_es, | |
+ dev_ev, | |
+ dev_p, | |
+ dev_walls_nx, | |
+ dev_walls_mvfd, | |
+ dev_walls_force_pp, | |
+ dev_contacts, | |
+ dev_distmod, | |
+ dev_delta_t); | |
+ | |
+ // Synchronization point | |
cudaThreadSynchronize(); | |
+ //cudaPrintfDisplay(stdout, true); | |
if (PROFILING == 1) | |
stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
- &t_latticeBoltzmannD3Q19); | |
- checkForCudaErrors("Post latticeBoltzmannD3Q19", iter); | |
- | |
- // Flip flop | |
- swapFloatArrays(dev_f, dev_f_new); | |
-#else | |
- latticeBoltzmannD3Q19(f, f_new, v_rho, | |
- time.dt, grid, params); | |
- // Flip flop | |
- swapFloatArrays(f, f_new); | |
-#endif | |
- | |
+ &t_interact); | |
+ checkForCudaErrorsIter( | |
+ "Post interact - often caused if particles move " | |
+ "outside the grid", iter); | |
+ | |
+ // Process particle pairs | |
+ if (params.nb0 > 0) { | |
+ if (PROFILING == 1) | |
+ startTimer(&kernel_tic); | |
+ bondsLinear<<<dimGridBonds, dimBlock>>>( | |
+ dev_bonds, | |
+ dev_bonds_delta, | |
+ dev_bonds_omega, | |
+ dev_x, | |
+ dev_vel, | |
+ dev_angvel, | |
+ dev_force, | |
+ dev_torque); | |
+ // Synchronization point | |
+ cudaThreadSynchronize(); | |
+ //cudaPrintfDisplay(stdout, true); | |
+ if (PROFILING == 1) | |
+ stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
+ &t_bondsLinear); | |
+ checkForCudaErrorsIter("Post bondsLinear", iter); | |
+ } | |
} | |
- // Solve darcy flow through grid | |
- if (params.nu > 0.0 && darcy == 1) { | |
+ // Solve Navier Stokes flow through the grid | |
+ if (navierstokes == 1) { | |
-#ifdef DARCY_GPU | |
- | |
- checkForCudaErrors("Before findPorositiesDev", iter); | |
- // Find cell porosities | |
+ checkForCudaErrorsIter("Before findPorositiesDev", iter); | |
+ // Find cell porosities, average particle velocities, and average | |
+ // particle diameters. These are needed for predicting the fluid | |
+ // velocities | |
if (PROFILING == 1) | |
startTimer(&kernel_tic); | |
- /*findPorositiesCubicDev<<<dimGridFluid, dimBlockFluid>>>( | |
- dev_cellStart, | |
- dev_cellEnd, | |
- dev_x_sorted, | |
- dev_d_phi, | |
- dev_d_dphi);*/ | |
- findPorositiesSphericalDev<<<dimGridFluid, dimBlockFluid>>>( | |
- dev_cellStart, | |
- dev_cellEnd, | |
- dev_x_sorted, | |
- dev_d_phi, | |
- dev_d_dphi, | |
- iter); | |
+ findPorositiesVelocitiesDiametersSpherical | |
+ <<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_cellStart, | |
+ dev_cellEnd, | |
+ dev_x_sorted, | |
+ dev_vel_sorted, | |
+ dev_ns_phi, | |
+ dev_ns_dphi, | |
+ dev_ns_vp_avg, | |
+ dev_ns_d_avg, | |
+ iter, | |
+ np); | |
cudaThreadSynchronize(); | |
if (PROFILING == 1) | |
stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
&t_findPorositiesDev); | |
- checkForCudaErrors("Post findPorositiesDev", iter); | |
+ checkForCudaErrorsIter("Post findPorositiesDev", iter); | |
+ | |
+#ifdef CFDDEMCOUPLING | |
+ /*if (params.nu <= 0.0) { | |
+ std::cerr << "Error! The fluid needs a positive viscosity " | |
+ "value in order to simulate particle-fluid interaction." | |
+ << std::endl; | |
+ exit(1); | |
+ }*/ | |
+ | |
+ if (np > 0) { | |
+ // Determine the interaction force | |
+ findInteractionForce<<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_phi, | |
+ dev_ns_d_avg, | |
+ dev_ns_vp_avg, | |
+ dev_ns_v, | |
+ dev_ns_fi); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrorsIter("Post findInteractionForce", iter); | |
+ | |
+ // Apply interaction force to the particles | |
+ applyParticleInteractionForce<<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_fi, | |
+ dev_ns_phi, | |
+ dev_gridParticleIndex, | |
+ dev_cellStart, | |
+ dev_cellEnd, | |
+ dev_x_sorted, | |
+ dev_force); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrorsIter("Post applyParticleInteractionForce", | |
+ iter); | |
+ } | |
+#endif | |
+ | |
+ // Initial guess for the top epsilon values. These may be changed … | |
+ // setUpperPressureNS | |
+ Float pressure = ns.p[idx(0,0,ns.nz-1)]; | |
+ Float pressure_new = pressure; // Dirichlet | |
+ Float epsilon_value = pressure_new - ns.beta*pressure; | |
+ setNSepsilonTop<<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_epsilon, | |
+ dev_ns_epsilon_new, | |
+ epsilon_value); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrorsIter("Post setNSepsilonTop", iter); | |
+ | |
+ // Modulate the pressures at the upper boundary cells | |
+ if ((ns.p_mod_A > 1.0e-5 || ns.p_mod_A < -1.0e-5) && | |
+ ns.p_mod_f > 1.0e-7) { | |
+ Float new_pressure = ns.p[idx(0,0,ns.nz-1)] // original pressu… | |
+ + ns.p_mod_A*sin(2.0*M_PI*ns.p_mod_f*time.current | |
+ + ns.p_mod_phi); | |
+ setUpperPressureNS<<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_p, | |
+ dev_ns_epsilon, | |
+ dev_ns_epsilon_new, | |
+ ns.beta, | |
+ new_pressure); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrorsIter("Post setUpperPressureNS", iter); | |
+ | |
+ if (report_even_more_epsilon == 1) { | |
+ std::cout | |
+ << "\n@@@@@@ TIME STEP " << iter << " @@@@@@" | |
+ << "\n###### EPSILON AFTER setUpperPressureNS ######" | |
+ << std::endl; | |
+ transferNSepsilonFromGlobalDeviceMemory(); | |
+ printNSarray(stdout, ns.epsilon, "epsilon"); | |
+ } | |
+ } | |
- // Find resulting cell transmissivities | |
+ // Set the values of the ghost nodes in the grid | |
if (PROFILING == 1) | |
startTimer(&kernel_tic); | |
- findDarcyTransmissivitiesDev<<<dimGridFluid, dimBlockFluid>>>( | |
- dev_d_K, | |
- dev_d_T, | |
- dev_d_phi, | |
- d_factor); | |
+ /*setNSghostNodesDev<<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_p, | |
+ dev_ns_v, | |
+ dev_ns_v_p, | |
+ dev_ns_phi, | |
+ dev_ns_dphi, | |
+ dev_ns_epsilon);*/ | |
+ setNSghostNodes<Float><<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_p, ns.bc_bot, ns.bc_top); | |
+ | |
+ setNSghostNodes<Float3><<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_v, ns.bc_bot, ns.bc_top); | |
+ | |
+ setNSghostNodes<Float3><<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_v_p, ns.bc_bot, ns.bc_top); | |
+ | |
+ setNSghostNodes<Float><<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_phi, ns.bc_bot, ns.bc_top); | |
+ | |
+ setNSghostNodes<Float><<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_dphi, ns.bc_bot, ns.bc_top); | |
+ | |
cudaThreadSynchronize(); | |
if (PROFILING == 1) | |
stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
- &t_findDarcyTransmissivitiesDev); | |
- checkForCudaErrors("Post findDarcyTransmissivitiesDev", iter); | |
+ &t_setNSghostNodesDev); | |
+ checkForCudaErrorsIter("Post setNSghostNodesDev", iter); | |
+ /*std::cout << "\n###### EPSILON AFTER setNSghostNodesDev ######" | |
+ << std::endl; | |
+ transferNSepsilonFromGlobalDeviceMemory(); | |
+ printNSarray(stdout, ns.epsilon, "epsilon");*/ | |
+ | |
- // Perform explicit Darcy time step | |
+ // Find the fluid stress tensor, needed for predicting the fluid | |
+ // velocities | |
if (PROFILING == 1) | |
startTimer(&kernel_tic); | |
- setDarcyGhostNodesDev<<<dimGridFluid, dimBlockFluid>>>( | |
- dev_d_H, | |
- dev_d_H_new, | |
- dev_d_V, | |
- dev_d_dH, | |
- dev_d_K, | |
- dev_d_T, | |
- dev_d_Ss, | |
- dev_d_W, | |
- dev_d_phi, | |
- dev_d_dphi); | |
+ findNSstressTensor<<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_v, | |
+ dev_ns_tau); | |
cudaThreadSynchronize(); | |
if (PROFILING == 1) | |
stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
- &t_setDarcyGhostNodesDev); | |
- checkForCudaErrors("Post setDarcyGhostNodesDev", iter); | |
+ &t_findNSstressTensor); | |
+ checkForCudaErrorsIter("Post findNSstressTensor", iter); | |
+ | |
+ // Set stress tensor values in the ghost nodes | |
+ setNSghostNodes_tau<<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_tau, | |
+ ns.bc_bot, | |
+ ns.bc_top); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrorsIter("Post setNSghostNodes_tau", iter); | |
- // Perform explicit Darcy time step | |
+ // Find the divergence of phi*vi*v, needed for predicting the fluid | |
+ // velocities | |
if (PROFILING == 1) | |
startTimer(&kernel_tic); | |
- explDarcyStepDev<<<dimGridFluid, dimBlockFluid>>>( | |
- dev_d_H, | |
- dev_d_H_new, | |
- dev_d_T, | |
- dev_d_Ss, | |
- dev_d_W, | |
- dev_d_dphi); | |
+ findNSdivphiviv<<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_phi, | |
+ dev_ns_v, | |
+ dev_ns_div_phi_vi_v); | |
cudaThreadSynchronize(); | |
if (PROFILING == 1) | |
stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
- &t_explDarcyStepDev); | |
- checkForCudaErrors("Post explDarcyStepDev", iter); | |
+ &t_findNSdivphiviv); | |
+ checkForCudaErrorsIter("Post findNSdivphiviv", iter); | |
- // Flip flop | |
- Float* tmp = dev_d_H; | |
- dev_d_H = dev_d_H_new; | |
- dev_d_H_new = tmp; | |
- | |
- | |
- // Find the pressure gradients | |
+ // Find the divergence of phi*tau, needed for predicting the fluid | |
+ // velocities | |
if (PROFILING == 1) | |
startTimer(&kernel_tic); | |
- findDarcyGradientsDev<<<dimGridFluid, dimBlockFluid>>>( | |
- dev_d_H, dev_d_dH); | |
+ findNSdivphitau<<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_phi, | |
+ dev_ns_tau, | |
+ dev_ns_div_phi_tau); | |
cudaThreadSynchronize(); | |
if (PROFILING == 1) | |
stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
- &t_findDarcyGradientsDev); | |
- checkForCudaErrors("Post findDarcyGradientsDev", iter); | |
+ &t_findNSdivphitau); | |
+ checkForCudaErrorsIter("Post findNSdivphitau", iter); | |
- // Find the velocities caused by the pressure gradients | |
+ // Predict the fluid velocities on the base of the old pressure | |
+ // field and ignoring the incompressibility constraint | |
if (PROFILING == 1) | |
startTimer(&kernel_tic); | |
- findDarcyVelocitiesDev<<<dimGridFluid, dimBlockFluid>>>( | |
- dev_d_H, | |
- dev_d_dH, | |
- dev_d_V, | |
- dev_d_phi, | |
- dev_d_K); | |
+ findPredNSvelocities<<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_p, | |
+ dev_ns_v, | |
+ dev_ns_phi, | |
+ dev_ns_dphi, | |
+ dev_ns_div_phi_vi_v, | |
+ dev_ns_div_phi_tau, | |
+ ns.bc_bot, | |
+ ns.bc_top, | |
+ ns.beta, | |
+ dev_ns_fi, | |
+ dev_ns_v_p); | |
cudaThreadSynchronize(); | |
if (PROFILING == 1) | |
stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
- &t_findDarcyVelocitiesDev); | |
- checkForCudaErrors("Post findDarcyVelocitiesDev", iter); | |
- | |
-#else | |
- // Copy device data to host memory | |
- transferFromGlobalDeviceMemory(); | |
+ &t_findPredNSvelocities); | |
+ checkForCudaErrorsIter("Post findPredNSvelocities", iter); | |
- // Pause the CPU thread until all CUDA calls previously issued are… | |
+ setNSghostNodes<Float3><<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_v_p); | |
cudaThreadSynchronize(); | |
+ checkForCudaErrorsIter("Post setNSghostNodesFloat3(dev_ns_v_p)", | |
+ iter); | |
+ | |
+ // In the first iteration of the sphere program, we'll need to | |
+ // manually estimate the values of epsilon. In the subsequent | |
+ // iterations, the previous values are used. | |
+ if (iter == 0) { | |
+ | |
+ // Define the first estimate of the values of epsilon. | |
+ // The initial guess depends on the value of ns.beta. | |
+ Float pressure = ns.p[idx(2,2,2)]; | |
+ Float pressure_new = pressure; // Guess p_current = p_new | |
+ Float epsilon_value = pressure_new - ns.beta*pressure; | |
+ if (PROFILING == 1) | |
+ startTimer(&kernel_tic); | |
+ setNSepsilonInterior<<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_epsilon, epsilon_value); | |
+ cudaThreadSynchronize(); | |
+ | |
+ setNSnormZero<<<dimGridFluid, dimBlockFluid>>>(dev_ns_norm); | |
+ cudaThreadSynchronize(); | |
+ | |
+ if (PROFILING == 1) | |
+ stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
+ &t_setNSepsilon); | |
+ checkForCudaErrorsIter("Post setNSepsilonInterior", iter); | |
+ | |
+ if (report_even_more_epsilon == 1) { | |
+ std::cout | |
+ << "\n###### EPSILON AFTER setNSepsilonInterior ######" | |
+ << std::endl; | |
+ transferNSepsilonFromGlobalDeviceMemory(); | |
+ printNSarray(stdout, ns.epsilon, "epsilon"); | |
+ } | |
+ | |
+ // Set the epsilon values at the lower boundary | |
+ pressure = ns.p[idx(0,0,0)]; | |
+ pressure_new = pressure; // Guess p_current = p_new | |
+ epsilon_value = pressure_new - ns.beta*pressure; | |
+ if (PROFILING == 1) | |
+ startTimer(&kernel_tic); | |
+ setNSepsilonBottom<<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_epsilon, | |
+ dev_ns_epsilon_new, | |
+ epsilon_value); | |
+ cudaThreadSynchronize(); | |
+ if (PROFILING == 1) | |
+ stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
+ &t_setNSdirichlet); | |
+ checkForCudaErrorsIter("Post setNSepsilonBottom", iter); | |
+ | |
+ if (report_even_more_epsilon == 1) { | |
+ std::cout | |
+ << "\n###### EPSILON AFTER setNSepsilonBottom ######" | |
+ << std::endl; | |
+ transferNSepsilonFromGlobalDeviceMemory(); | |
+ printNSarray(stdout, ns.epsilon, "epsilon"); | |
+ } | |
+ | |
+ /*setNSghostNodes<Float><<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_epsilon); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrors("Post setNSghostNodesFloat(dev_ns_epsilon… | |
+ iter);*/ | |
+ setNSghostNodes<Float><<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_epsilon, | |
+ ns.bc_bot, ns.bc_top); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrorsIter("Post setNSghostNodesEpsilon(1)", | |
+ iter); | |
+ | |
+ if (report_even_more_epsilon == 1) { | |
+ std::cout << | |
+ "\n###### EPSILON AFTER setNSghostNodes(epsilon) #####… | |
+ << std::endl; | |
+ transferNSepsilonFromGlobalDeviceMemory(); | |
+ printNSarray(stdout, ns.epsilon, "epsilon"); | |
+ } | |
+ } | |
+ | |
+ // Solve the system of epsilon using a Jacobi iterative solver. | |
+ // The average normalized residual is initialized to a large value. | |
+ //double avg_norm_res; | |
+ double max_norm_res; | |
+ | |
+ // Write a log file of the normalized residuals during the Jacobi | |
+ // iterations | |
+ std::ofstream reslog; | |
+ if (write_res_log == 1) | |
+ reslog.open("max_res_norm.dat"); | |
+ | |
+ // transfer normalized residuals from GPU to CPU | |
+ if (report_epsilon == 1) { | |
+ std::cout << "\n###### BEFORE FIRST JACOBI ITERATION ######" | |
+ << "\n@@@@@@ TIME STEP " << iter << " @@@@@@" | |
+ << std::endl; | |
+ transferNSepsilonFromGlobalDeviceMemory(); | |
+ printNSarray(stdout, ns.epsilon, "epsilon"); | |
+ } | |
+ | |
+ for (unsigned int nijac = 0; nijac<ns.maxiter; ++nijac) { | |
+ | |
+ // Only grad(epsilon) changes during the Jacobi iterations. The | |
+ // remaining terms of the forcing function are only calculated | |
+ // during the first iteration. | |
+ if (PROFILING == 1) | |
+ startTimer(&kernel_tic); | |
+ findNSforcing<<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_epsilon, | |
+ dev_ns_f1, | |
+ dev_ns_f2, | |
+ dev_ns_f, | |
+ dev_ns_phi, | |
+ dev_ns_dphi, | |
+ dev_ns_v_p, | |
+ nijac); | |
+ cudaThreadSynchronize(); | |
+ if (PROFILING == 1) | |
+ stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
+ &t_findNSforcing); | |
+ checkForCudaErrorsIter("Post findNSforcing", iter); | |
+ /*setNSghostNodesForcing<<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_f1, | |
+ dev_ns_f2, | |
+ dev_ns_f, | |
+ nijac); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrors("Post setNSghostNodesForcing", iter);*/ | |
+ | |
+ setNSghostNodes<Float><<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_epsilon, | |
+ ns.bc_bot, ns.bc_top); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrorsIter("Post setNSghostNodesEpsilon(2)", | |
+ iter); | |
+ | |
+ if (report_epsilon == 1) { | |
+ std::cout << "\n###### JACOBI ITERATION " | |
+ << nijac << " after setNSghostNodes(epsilon,2) ######" | |
+ << std::endl; | |
+ transferNSepsilonFromGlobalDeviceMemory(); | |
+ printNSarray(stdout, ns.epsilon, "epsilon"); | |
+ } | |
+ | |
+ /*smoothing<Float><<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_epsilon, | |
+ ns.bc_bot, ns.bc_top); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrorsIter("Post smoothing", iter); | |
+ | |
+ if (report_epsilon == 1) { | |
+ std::cout << "\n###### JACOBI ITERATION " | |
+ << nijac << " after smoothing(epsilon) ######" | |
+ << std::endl; | |
+ transferNSepsilonFromGlobalDeviceMemory(); | |
+ printNSarray(stdout, ns.epsilon, "epsilon"); | |
+ } | |
+ | |
+ setNSghostNodes<Float><<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_epsilon, | |
+ ns.bc_bot, ns.bc_top); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrorsIter("Post setNSghostNodesEpsilon(3)", | |
+ iter); | |
+ */ | |
+ | |
+ /*if (report_epsilon == 1) { | |
+ std::cout << "\n###### JACOBI ITERATION " | |
+ << nijac | |
+ << " after setNSghostNodesEpsilon(epsilon,3) ######" | |
+ << std::endl; | |
+ transferNSepsilonFromGlobalDeviceMemory(); | |
+ printNSarray(stdout, ns.epsilon, "epsilon"); | |
+ }*/ | |
+ | |
+ // Store old values | |
+ /*copyValues<Float><<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_epsilon, | |
+ dev_ns_epsilon_old); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrorsIter("Post copyValues (epsilon->epsilon_old)… | |
+ iter);*/ | |
+ | |
+ // Perform a single Jacobi iteration | |
+ if (PROFILING == 1) | |
+ startTimer(&kernel_tic); | |
+ jacobiIterationNS<<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_epsilon, | |
+ dev_ns_epsilon_new, | |
+ dev_ns_norm, | |
+ dev_ns_f, | |
+ ns.bc_bot, | |
+ ns.bc_top, | |
+ ns.theta); | |
+ cudaThreadSynchronize(); | |
+ if (PROFILING == 1) | |
+ stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
+ &t_jacobiIterationNS); | |
+ checkForCudaErrorsIter("Post jacobiIterationNS", iter); | |
+ | |
+ // Flip flop: swap new and current array pointers | |
+ /*Float* tmp = dev_ns_epsilon; | |
+ dev_ns_epsilon = dev_ns_epsilon_new; | |
+ dev_ns_epsilon_new = tmp;*/ | |
+ | |
+ // Copy new values to current values | |
+ copyValues<Float><<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_epsilon_new, | |
+ dev_ns_epsilon); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrorsIter("Post copyValues (epsilon_new->epsilon)… | |
+ iter); | |
+ | |
+ /*findNormalizedResiduals<<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_epsilon_old, | |
+ dev_ns_epsilon, | |
+ dev_ns_norm, | |
+ ns.bc_bot, ns.bc_top); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrorsIter("Post findNormalizedResiduals", | |
+ iter);*/ | |
+ | |
+ if (report_epsilon == 1) { | |
+ std::cout << "\n###### JACOBI ITERATION " | |
+ << nijac << " after jacobiIterationNS ######" | |
+ << std::endl; | |
+ transferNSepsilonFromGlobalDeviceMemory(); | |
+ printNSarray(stdout, ns.epsilon, "epsilon"); | |
+ } | |
+ | |
+ if (nijac % nijacnorm == 0) { | |
+ | |
+ // Read the normalized residuals from the device | |
+ transferNSnormFromGlobalDeviceMemory(); | |
- // Perform a Darcy time step on the CPU | |
- explDarcyStep(); | |
+ // Write the normalized residuals to the terminal | |
+ //printNSarray(stdout, ns.norm, "norm"); | |
+ | |
+ // Find the maximum value of the normalized residuals | |
+ max_norm_res = maxNormResNS(); | |
+ | |
+ // Write the Jacobi iteration number and maximum value of | |
+ // the normalized residual to the log file | |
+ if (write_res_log == 1) | |
+ reslog << nijac << '\t' << max_norm_res << std::endl; | |
+ } | |
- // Transfer data from host to device memory | |
- transferToGlobalDeviceMemory(0); | |
+ if (max_norm_res < ns.tolerance) { | |
+ | |
+ if (write_conv_log == 1 && iter % conv_log_interval == 0) | |
+ convlog << iter << '\t' << nijac << std::endl; | |
+ | |
+ // Apply smoothing if requested | |
+ if (ns.gamma > 0.0) { | |
+ setNSghostNodes<Float><<<dimGridFluid, dimBlockFluid>>… | |
+ dev_ns_epsilon, | |
+ ns.bc_bot, ns.bc_top); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrorsIter("Post setNSghostNodesEpsilon(4)… | |
+ iter); | |
+ | |
+ smoothing<<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_epsilon, | |
+ ns.gamma, | |
+ ns.bc_bot, ns.bc_top); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrorsIter("Post smoothing", iter); | |
+ | |
+ setNSghostNodes<Float><<<dimGridFluid, dimBlockFluid>>… | |
+ dev_ns_epsilon, | |
+ ns.bc_bot, ns.bc_top); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrorsIter("Post setNSghostNodesEpsilon(4)… | |
+ iter); | |
+ } | |
+ | |
+ if (report_epsilon == 1) { | |
+ std::cout << "\n###### JACOBI ITERATION " | |
+ << nijac << " after smoothing ######" | |
+ << std::endl; | |
+ transferNSepsilonFromGlobalDeviceMemory(); | |
+ printNSarray(stdout, ns.epsilon, "epsilon"); | |
+ } | |
+ | |
+ break; // solution has converged, exit Jacobi iter. loop | |
+ } | |
+ | |
+ if (nijac >= ns.maxiter-1) { | |
+ | |
+ if (write_conv_log == 1) | |
+ convlog << iter << '\t' << nijac << std::endl; | |
+ | |
+ std::cerr << "\nIteration " << iter << ", time " | |
+ << iter*time.dt << " s: " | |
+ "Error, the epsilon solution in the fluid " | |
+ "calculations did not converge. Try increasing the " | |
+ "value of 'ns.maxiter' (" << ns.maxiter | |
+ << ") or increase 'ns.tolerance' (" | |
+ << ns.tolerance << ")." << std::endl; | |
+ } | |
+ //break; // end after Jacobi first iteration | |
+ } // end Jacobi iteration loop | |
- // Pause the CPU thread until all CUDA calls previously issued are… | |
+ if (write_res_log == 1) | |
+ reslog.close(); | |
+ | |
+ // Find the new pressures and velocities | |
+ if (PROFILING == 1) | |
+ startTimer(&kernel_tic); | |
+ updateNSvelocityPressure<<<dimGridFluid, dimBlockFluid>>>( | |
+ dev_ns_p, | |
+ dev_ns_v, | |
+ dev_ns_v_p, | |
+ dev_ns_epsilon, | |
+ ns.beta, | |
+ ns.bc_bot, | |
+ ns.bc_top); | |
cudaThreadSynchronize(); | |
-#endif | |
+ if (PROFILING == 1) | |
+ stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
+ &t_updateNSvelocityPressure); | |
+ checkForCudaErrorsIter("Post updateNSvelocityPressure", iter); | |
} | |
- // Update particle kinematics | |
- if (PROFILING == 1) | |
- startTimer(&kernel_tic); | |
- integrate<<<dimGrid, dimBlock>>>(dev_x_sorted, | |
- dev_vel_sorted, | |
- dev_angvel_sorted, | |
- dev_x, | |
- dev_vel, | |
- dev_angvel, | |
- dev_force, | |
- dev_torque, | |
- dev_angpos, | |
- dev_acc, | |
- dev_angacc, | |
- dev_vel0, | |
- dev_angvel0, | |
- dev_xysum, | |
- dev_gridParticleIndex); | |
- cudaThreadSynchronize(); | |
- checkForCudaErrors("Post integrate"); | |
- | |
- | |
- cudaThreadSynchronize(); | |
- if (PROFILING == 1) | |
- stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, &t_integrate); | |
- | |
- // Summation of forces on wall | |
- if (PROFILING == 1) | |
- startTimer(&kernel_tic); | |
- if (walls.nw > 0) { | |
- summation<<<dimGrid, dimBlock>>>(dev_walls_force_pp, | |
- dev_walls_force_partial); | |
- } | |
- // Synchronization point | |
- cudaThreadSynchronize(); | |
- if (PROFILING == 1) | |
- stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, &t_summation); | |
- checkForCudaErrors("Post wall force summation"); | |
- | |
- // Update wall kinematics | |
- if (PROFILING == 1) | |
- startTimer(&kernel_tic); | |
- if (walls.nw > 0) { | |
- integrateWalls<<< 1, walls.nw>>>( | |
- dev_walls_nx, | |
- dev_walls_mvfd, | |
- dev_walls_wmode, | |
- dev_walls_force_partial, | |
- dev_walls_vel0, | |
- blocksPerGrid, | |
- time.current); | |
- } | |
+ /*std::cout << "\n###### ITERATION " | |
+ << iter << " ######" << std::endl; | |
+ transferNSepsilonFromGlobalDeviceMemory(); | |
+ printNSarray(stdout, ns.epsilon, "epsilon");*/ | |
+ //transferNSepsilonNewFromGlobalDeviceMemory(); | |
+ //printNSarray(stdout, ns.epsilon_new, "epsilon_new"); | |
- // Synchronization point | |
- cudaThreadSynchronize(); | |
- if (PROFILING == 1) | |
- stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, &t_integrateW… | |
- checkForCudaErrors("Post integrateWalls"); | |
+ if (np > 0) { | |
+ // Update particle kinematics | |
+ if (PROFILING == 1) | |
+ startTimer(&kernel_tic); | |
+ integrate<<<dimGrid, dimBlock>>>(dev_x_sorted, | |
+ dev_vel_sorted, | |
+ dev_angvel_sorted, | |
+ dev_x, | |
+ dev_vel, | |
+ dev_angvel, | |
+ dev_force, | |
+ dev_torque, | |
+ dev_angpos, | |
+ dev_acc, | |
+ dev_angacc, | |
+ dev_vel0, | |
+ dev_angvel0, | |
+ dev_xysum, | |
+ dev_gridParticleIndex, | |
+ iter); | |
+ cudaThreadSynchronize(); | |
+ checkForCudaErrorsIter("Post integrate", iter); | |
+ if (PROFILING == 1) | |
+ stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
+ &t_integrate); | |
- /*for (int a=0; a<params.nb0; ++a) | |
- std::cout << "bond " << a << ":\n" | |
- << k.bonds_delta[a].x << ", " | |
- << k.bonds_delta[a].y << ", " | |
- << k.bonds_delta[a].z << ", " | |
- << k.bonds_delta[a].w << std::endl; | |
- break;*/ | |
+ // Summation of forces on wall | |
+ if (PROFILING == 1) | |
+ startTimer(&kernel_tic); | |
+ if (walls.nw > 0) { | |
+ summation<<<dimGrid, dimBlock>>>(dev_walls_force_pp, | |
+ dev_walls_force_partial); | |
+ } | |
+ cudaThreadSynchronize(); | |
+ if (PROFILING == 1) | |
+ stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
+ &t_summation); | |
+ checkForCudaErrorsIter("Post wall force summation", iter); | |
+ | |
+ // Update wall kinematics | |
+ if (PROFILING == 1) | |
+ startTimer(&kernel_tic); | |
+ if (walls.nw > 0) { | |
+ integrateWalls<<< 1, walls.nw>>>( | |
+ dev_walls_nx, | |
+ dev_walls_mvfd, | |
+ dev_walls_wmode, | |
+ dev_walls_force_partial, | |
+ dev_walls_acc, | |
+ blocksPerGrid, | |
+ time.current, | |
+ iter); | |
+ } | |
+ cudaThreadSynchronize(); | |
+ if (PROFILING == 1) | |
+ stopTimer(&kernel_tic, &kernel_toc, &kernel_elapsed, | |
+ &t_integrateWalls); | |
+ checkForCudaErrorsIter("Post integrateWalls", iter); | |
+ } | |
// Update timers and counters | |
- time.current += time.dt; | |
+ time.current = iter*time.dt; | |
filetimeclock += time.dt; | |
++iter; | |
// Report time to console | |
if (verbose == 1 && (iter % stdout_report == 0)) { | |
+ | |
+ toc = clock(); | |
+ time_spent = (toc - tic)/(CLOCKS_PER_SEC); // real time spent | |
+ | |
+ // Real time it takes to compute a second of model time | |
+ t_ratio = time_spent/(time.current - t_start); | |
+ | |
cout << "\r Current simulation time: " | |
<< time.current << "/" | |
- << time.total << " s. "; // << std::flush; | |
+ << time.total << " s. (" | |
+ << t_ratio << " s/s) "; // << std::flush; | |
} | |
t@@ -1122,28 +1459,40 @@ __host__ void DEM::startTime() | |
// between output files has been reached | |
if (filetimeclock >= time.file_dt) { | |
- // Pause the CPU thread until all CUDA calls previously issued are… | |
+ // Pause the CPU thread until all CUDA calls previously issued are | |
+ // completed | |
cudaThreadSynchronize(); | |
- checkForCudaErrors("Beginning of file output section"); | |
+ checkForCudaErrorsIter("Beginning of file output section", iter); | |
//// Copy device data to host memory | |
transferFromGlobalDeviceMemory(); | |
+ checkForCudaErrorsIter("After transferFromGlobalDeviceMemory()", | |
+ iter); | |
- // Pause the CPU thread until all CUDA calls previously issued are… | |
+ // Pause the CPU thread until all CUDA calls previously issued are | |
+ // completed | |
cudaThreadSynchronize(); | |
+ // Check the numerical stability of the NS solver | |
+ if (navierstokes == 1) | |
+ checkNSstability(); | |
+ | |
// Write binary output file | |
time.step_count += 1; | |
- sprintf(file,"output/%s.output%05d.bin", sid.c_str(), time.step_co… | |
- writebin(file); | |
- | |
- // Write Darcy arrays | |
- if (params.nu > 0.0 && darcy == 1) { | |
- sprintf(file,"output/%s.d_phi.output%05d.bin", sid.c_str(), ti… | |
- writeDarcyArray(d.phi, file); | |
- sprintf(file,"output/%s.d_K.output%05d.bin", sid.c_str(), time… | |
- writeDarcyArray(d.K, file); | |
- } | |
+ sprintf(file,"output/%s.output%05d.bin", sid.c_str(), | |
+ time.step_count); writebin(file); | |
+ | |
+ /*std::cout << "\n###### OUTPUT FILE " << time.step_count << " ###… | |
+ << std::endl; | |
+ transferNSepsilonFromGlobalDeviceMemory(); | |
+ printNSarray(stdout, ns.epsilon, "epsilon");*/ | |
+ | |
+ // Write fluid arrays | |
+ /*if (navierstokes == 1) { | |
+ sprintf(file,"output/%s.ns_phi.output%05d.bin", sid.c_str(), | |
+ time.step_count); | |
+ writeNSarray(ns.phi, file); | |
+ }*/ | |
if (CONTACTINFO == 1) { | |
// Write contact information to stdout | |
t@@ -1156,7 +1505,8 @@ __host__ void DEM::startTime() | |
cout << "- contacts:\n"; | |
for (int nc = 0; nc < NC; ++nc) | |
- cout << "[" << nc << "]=" << k.contacts[nc+NC*n] << '\… | |
+ cout << "[" << nc << "]=" << k.contacts[nc+NC*n] << | |
+ '\n'; | |
cout << "\n- delta_t:\n"; | |
for (int nc = 0; nc < NC; ++nc) | |
t@@ -1191,6 +1541,10 @@ __host__ void DEM::startTime() | |
//break; | |
} | |
+ if (write_conv_log == 1) | |
+ convlog.close(); | |
+ | |
+ | |
// Stop clock and display calculation time spent | |
toc = clock(); | |
cudaEventRecord(dev_toc, 0); | |
t@@ -1223,9 +1577,10 @@ __host__ void DEM::startTime() | |
double t_sum = t_calcParticleCellID + t_thrustsort + t_reorderArrays + | |
t_topology + t_interact + t_bondsLinear + t_latticeBoltzmannD3Q19 + | |
t_integrate + t_summation + t_integrateWalls + t_findPorositiesDev… | |
- t_findDarcyTransmissivitiesDev + t_setDarcyGhostNodesDev + | |
- t_explDarcyStepDev + t_findDarcyGradientsDev + | |
- t_findDarcyVelocitiesDev; | |
+ t_findNSstressTensor + | |
+ t_findNSdivphiviv + t_findNSdivphitau + t_findPredNSvelocities + | |
+ t_setNSepsilon + t_setNSdirichlet + t_setNSghostNodesDev + | |
+ t_findNSforcing + t_jacobiIterationNS + t_updateNSvelocityPressure; | |
cout << "\nKernel profiling statistics:\n" | |
<< " - calcParticleCellID:\t\t" << t_calcParticleCellID/1000.0 | |
t@@ -1240,61 +1595,58 @@ __host__ void DEM::startTime() | |
<< " - topology:\t\t\t" << t_topology/1000.0 << " s" | |
<< "\t(" << 100.0*t_topology/t_sum << " %)\n"; | |
} | |
- cout | |
- << " - interact:\t\t\t" << t_interact/1000.0 << " s" | |
+ cout << " - interact:\t\t\t" << t_interact/1000.0 << " s" | |
<< "\t(" << 100.0*t_interact/t_sum << " %)\n"; | |
if (params.nb0 > 0) { | |
- cout | |
- << " - bondsLinear:\t\t" << t_bondsLinear/1000.0 << " s" | |
+ cout << " - bondsLinear:\t\t" << t_bondsLinear/1000.0 << " s" | |
<< "\t(" << 100.0*t_bondsLinear/t_sum << " %)\n"; | |
} | |
- if (params.nu > 0.0 && darcy == 0) { | |
- cout | |
- << " - latticeBoltzmann:\t\t" << t_latticeBoltzmannD3Q19/1000.0 << | |
- " s" << "\t(" << 100.0*t_latticeBoltzmannD3Q19/t_sum << " %)\n"; | |
- } | |
- cout | |
- << " - integrate:\t\t\t" << t_integrate/1000.0 << " s" | |
+ cout << " - integrate:\t\t\t" << t_integrate/1000.0 << " s" | |
<< "\t(" << 100.0*t_integrate/t_sum << " %)\n" | |
<< " - summation:\t\t\t" << t_summation/1000.0 << " s" | |
<< "\t(" << 100.0*t_summation/t_sum << " %)\n" | |
<< " - integrateWalls:\t\t" << t_integrateWalls/1000.0 << " s" | |
<< "\t(" << 100.0*t_integrateWalls/t_sum << " %)\n"; | |
- if (params.nu > 0.0 && darcy == 1) { | |
- cout | |
- << " - findPorositiesDev:\t\t" << t_findPorositiesDev/1000.0 | |
+ if (navierstokes == 1) { | |
+ cout << " - findPorositiesDev:\t\t" << t_findPorositiesDev/1000.0 | |
<< " s" << "\t(" << 100.0*t_findPorositiesDev/t_sum << " %)\n" | |
- << " - findDarcyTransmis.Dev:\t" << | |
- t_findDarcyTransmissivitiesDev/1000.0 << " s" | |
- << "\t(" << 100.0*t_findDarcyTransmissivitiesDev/t_sum << " %)\n" | |
- << " - setDarcyGhostNodesDev:\t" << t_setDarcyGhostNodesDev/1000.0 | |
- << " s" << "\t(" << 100.0*t_setDarcyGhostNodesDev/t_sum << " %)\n" | |
- << " - explDarcyStepDev:\t\t" << t_explDarcyStepDev/1000.0 << " s" | |
- << "\t(" << 100.0*t_explDarcyStepDev/t_sum << " %)\n" | |
- << " - findDarcyGradientsDev:\t" << t_findDarcyGradientsDev/1000.0 | |
- << " s" | |
- << "\t(" << 100.0*t_findDarcyGradientsDev/t_sum << " %)\n" | |
- << " - findDarcyVelocitiesDev:\t" | |
- << t_findDarcyVelocitiesDev/1000.0 << " s" | |
- << "\t(" << 100.0*t_findDarcyVelocitiesDev/t_sum << " %)\n"; | |
+ << " - findNSstressTensor:\t\t" << t_findNSstressTensor/1000.0 | |
+ << " s" << "\t(" << 100.0*t_findNSstressTensor/t_sum << " %)\n" | |
+ << " - findNSdivphiviv:\t\t" << t_findNSdivphiviv/1000.0 | |
+ << " s" << "\t(" << 100.0*t_findNSdivphiviv/t_sum << " %)\n" | |
+ << " - findNSdivphitau:\t\t" << t_findNSdivphitau/1000.0 | |
+ << " s" << "\t(" << 100.0*t_findNSdivphitau/t_sum << " %)\n" | |
+ << " - findPredNSvelocities:\t" << t_findPredNSvelocities/1000.0 | |
+ << " s" << "\t(" << 100.0*t_findPredNSvelocities/t_sum << " %)\n" | |
+ << " - setNSepsilon:\t\t" << t_setNSepsilon/1000.0 | |
+ << " s" << "\t(" << 100.0*t_setNSepsilon/t_sum << " %)\n" | |
+ << " - setNSdirichlet:\t\t" << t_setNSdirichlet/1000.0 | |
+ << " s" << "\t(" << 100.0*t_setNSdirichlet/t_sum << " %)\n" | |
+ << " - setNSghostNodesDev:\t\t" << t_setNSghostNodesDev/1000.0 | |
+ << " s" << "\t(" << 100.0*t_setNSghostNodesDev/t_sum << " %)\n" | |
+ << " - findNSforcing:\t\t" << t_findNSforcing/1000.0 << " s" | |
+ << "\t(" << 100.0*t_findNSforcing/t_sum << " %)\n" | |
+ << " - jacobiIterationNS:\t\t" << t_jacobiIterationNS/1000.0 << "… | |
+ << "\t(" << 100.0*t_jacobiIterationNS/t_sum << " %)\n" | |
+ << " - updateNSvelocityPressure:\t" | |
+ << t_updateNSvelocityPressure/1000.0 << " s" | |
+ << "\t(" << 100.0*t_updateNSvelocityPressure/t_sum << " %)\n"; | |
} | |
} | |
- | |
// Free GPU device memory | |
freeGlobalDeviceMemory(); | |
+ checkForCudaErrorsIter("After freeGlobalDeviceMemory()", iter); | |
- // Contact info arrays | |
+ // Free contact info arrays | |
delete[] k.contacts; | |
delete[] k.distmod; | |
delete[] k.delta_t; | |
- if (darcy == 1 && params.nu > 0.0) { | |
-#ifdef DARCY_GPU | |
- endDarcyDev(); | |
-#endif | |
- endDarcy(); | |
+ if (navierstokes == 1) { | |
+ endNS(); | |
} | |
-} /* EOF */ | |
+ cudaDeviceReset(); | |
+} | |
// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 | |
diff --git a/src/file_io.cpp b/src/file_io.cpp | |
t@@ -34,7 +34,8 @@ void DEM::readbin(const char *target) | |
unsigned int i; | |
// Open input file | |
- // if target is string: std::ifstream ifs(target.c_str(), std::ios_base::b… | |
+ // if target is string: | |
+ // std::ifstream ifs(target.c_str(), std::ios_base::binary); | |
std::ifstream ifs(target, std::ios_base::binary); | |
if (!ifs) { | |
cerr << "Could not read input binary file '" | |
t@@ -42,12 +43,21 @@ void DEM::readbin(const char *target) | |
exit(1); | |
} | |
+ Float version; | |
+ ifs.read(as_bytes(version), sizeof(Float)); | |
+ if (version != VERSION) { | |
+ std::cerr << "Error: The input file '" << target << "' is written by " | |
+ "sphere version " << version << ", which is incompatible with this… | |
+ "version (" << VERSION << ")." << std::endl; | |
+ exit(1); | |
+ } | |
+ | |
ifs.read(as_bytes(nd), sizeof(nd)); | |
ifs.read(as_bytes(np), sizeof(np)); | |
if (nd != ND) { | |
- cerr << "Dimensionality mismatch between dataset and this SPHERE progr… | |
- << "The dataset is " << nd | |
+ cerr << "Dimensionality mismatch between dataset and this SPHERE " | |
+ "program.\nThe dataset is " << nd | |
<< "D, this SPHERE binary is " << ND << "D.\n" | |
<< "This execution is terminating." << endl; | |
exit(-1); // Return unsuccessful exit status | |
t@@ -66,8 +76,8 @@ void DEM::readbin(const char *target) | |
ifs.read(as_bytes(time.file_dt), sizeof(time.file_dt)); | |
ifs.read(as_bytes(time.step_count), sizeof(time.step_count)); | |
- // For spatial vectors an array of Float4 vectors is chosen for best fit w… | |
- // GPU memory handling. Vector variable structure: ( x, y, z, <empty>). | |
+ // For spatial vectors an array of Float4 vectors is chosen for best fit | |
+ // with GPU memory handling. Vector variable structure: ( x, y, z, <empty>… | |
// Indexing starts from 0. | |
// Allocate host arrays | |
t@@ -236,59 +246,50 @@ void DEM::readbin(const char *target) | |
ifs.read(as_bytes(k.bonds_omega[i].z), sizeof(Float)); | |
} | |
- // Read fluid parameters | |
- ifs.read(as_bytes(params.nu), sizeof(params.nu)); | |
unsigned int x, y, z; | |
if (verbose == 1) | |
cout << "Done\n"; | |
- if (params.nu > 0.0 && darcy == 0) { // Lattice-Boltzmann flow | |
- | |
- if (verbose == 1) | |
- cout << " - Reading LBM values:\t\t\t\t "; | |
- | |
- //f = new Float[grid.num[0]*grid.num[1]*grid.num[2]*19]; | |
- //f_new = new Float[grid.num[0]*grid.num[1]*grid.num[2]*19]; | |
- v_rho = new Float4[grid.num[0]*grid.num[1]*grid.num[2]]; | |
- | |
- for (z = 0; z<grid.num[2]; ++z) { | |
- for (y = 0; y<grid.num[1]; ++y) { | |
- for (x = 0; x<grid.num[0]; ++x) { | |
- i = x + grid.num[0]*y + grid.num[0]*grid.num[1]*z; | |
- ifs.read(as_bytes(v_rho[i].x), sizeof(Float)); | |
- ifs.read(as_bytes(v_rho[i].y), sizeof(Float)); | |
- ifs.read(as_bytes(v_rho[i].z), sizeof(Float)); | |
- ifs.read(as_bytes(v_rho[i].w), sizeof(Float)); | |
- } | |
- } | |
- } | |
- | |
- if (verbose == 1) | |
- cout << "Done" << std::endl; | |
+ if (navierstokes == 1) { // Navier Stokes flow | |
- } else if (params.nu > 0.0 && darcy == 1) { // Darcy flow | |
+ initNSmem(); | |
- const Float cellsizemultiplier = 1.0; | |
- initDarcyMem(cellsizemultiplier); | |
+ ifs.read(as_bytes(params.mu), sizeof(params.mu)); | |
if (verbose == 1) | |
- cout << " - Reading Darcy values:\t\t\t "; | |
+ cout << " - Reading fluid values:\t\t\t "; | |
for (z = 0; z<grid.num[2]; ++z) { | |
for (y = 0; y<grid.num[1]; ++y) { | |
for (x = 0; x<grid.num[0]; ++x) { | |
i = idx(x,y,z); | |
- ifs.read(as_bytes(d.V[i].x), sizeof(Float)); | |
- ifs.read(as_bytes(d.V[i].y), sizeof(Float)); | |
- ifs.read(as_bytes(d.V[i].z), sizeof(Float)); | |
- ifs.read(as_bytes(d.H[i]), sizeof(Float)); | |
- ifs.read(as_bytes(d.phi[i]), sizeof(Float)); | |
- ifs.read(as_bytes(d.K[i]), sizeof(Float)); | |
+ ifs.read(as_bytes(ns.v[i].x), sizeof(Float)); | |
+ ifs.read(as_bytes(ns.v[i].y), sizeof(Float)); | |
+ ifs.read(as_bytes(ns.v[i].z), sizeof(Float)); | |
+ ifs.read(as_bytes(ns.p[i]), sizeof(Float)); | |
+ ifs.read(as_bytes(ns.phi[i]), sizeof(Float)); | |
+ ifs.read(as_bytes(ns.dphi[i]), sizeof(Float)); | |
} | |
} | |
} | |
+ ifs.read(as_bytes(params.rho_f), sizeof(Float)); | |
+ ifs.read(as_bytes(ns.p_mod_A), sizeof(Float)); | |
+ ifs.read(as_bytes(ns.p_mod_f), sizeof(Float)); | |
+ ifs.read(as_bytes(ns.p_mod_phi), sizeof(Float)); | |
+ | |
+ ifs.read(as_bytes(ns.bc_bot), sizeof(int)); | |
+ ifs.read(as_bytes(ns.bc_top), sizeof(int)); | |
+ ifs.read(as_bytes(ns.free_slip_bot), sizeof(int)); | |
+ ifs.read(as_bytes(ns.free_slip_top), sizeof(int)); | |
+ | |
+ ifs.read(as_bytes(ns.gamma), sizeof(Float)); | |
+ ifs.read(as_bytes(ns.theta), sizeof(Float)); | |
+ ifs.read(as_bytes(ns.beta), sizeof(Float)); | |
+ ifs.read(as_bytes(ns.tolerance), sizeof(Float)); | |
+ ifs.read(as_bytes(ns.maxiter), sizeof(unsigned int)); | |
+ | |
if (verbose == 1) | |
cout << "Done" << std::endl; | |
} | |
t@@ -309,13 +310,16 @@ void DEM::writebin(const char *target) | |
std::ofstream ofs(target, std::ios_base::binary); | |
if (!ofs) { | |
std::cerr << "could create output binary file '" | |
- << target << std::endl; | |
- exit(1); // Return unsuccessful exit status | |
+ << target << "'" << std::endl; | |
+ exit(1); | |
} | |
// If double precision: Values can be written directly | |
if (sizeof(Float) == sizeof(double)) { | |
+ double version = VERSION; | |
+ ofs.write(as_bytes(version), sizeof(Float)); | |
+ | |
ofs.write(as_bytes(nd), sizeof(nd)); | |
ofs.write(as_bytes(np), sizeof(np)); | |
t@@ -449,36 +453,43 @@ void DEM::writebin(const char *target) | |
ofs.write(as_bytes(k.bonds_omega[i].z), sizeof(Float)); | |
} | |
- ofs.write(as_bytes(params.nu), sizeof(params.nu)); | |
- int x, y, z; | |
- if (params.nu > 0.0 && darcy == 0) { // Lattice Boltzmann flow | |
- for (z = 0; z<grid.num[2]; ++z) { | |
- for (y = 0; y<grid.num[1]; ++y) { | |
- for (x = 0; x<grid.num[0]; ++x) { | |
- i = x + grid.num[0]*y + grid.num[0]*grid.num[1]*z; | |
- ofs.write(as_bytes(v_rho[i].x), sizeof(Float)); | |
- ofs.write(as_bytes(v_rho[i].y), sizeof(Float)); | |
- ofs.write(as_bytes(v_rho[i].z), sizeof(Float)); | |
- ofs.write(as_bytes(v_rho[i].w), sizeof(Float)); | |
- } | |
- } | |
- } | |
- } else if (params.nu > 0.0 && darcy == 1) { // Darcy flow | |
- for (z=0; z<d.nz; z++) { | |
- for (y=0; y<d.ny; y++) { | |
- for (x=0; x<d.nx; x++) { | |
+ if (navierstokes == 1) { // Navier Stokes flow | |
+ | |
+ ofs.write(as_bytes(params.mu), sizeof(params.mu)); | |
+ | |
+ int x, y, z; | |
+ for (z=0; z<ns.nz; z++) { | |
+ for (y=0; y<ns.ny; y++) { | |
+ for (x=0; x<ns.nx; x++) { | |
i = idx(x,y,z); | |
- ofs.write(as_bytes(d.V[i].x), sizeof(Float)); | |
- ofs.write(as_bytes(d.V[i].y), sizeof(Float)); | |
- ofs.write(as_bytes(d.V[i].z), sizeof(Float)); | |
- ofs.write(as_bytes(d.H[i]), sizeof(Float)); | |
- ofs.write(as_bytes(d.phi[i]), sizeof(Float)); | |
- ofs.write(as_bytes(d.K[i]), sizeof(Float)); | |
+ ofs.write(as_bytes(ns.v[i].x), sizeof(Float)); | |
+ ofs.write(as_bytes(ns.v[i].y), sizeof(Float)); | |
+ ofs.write(as_bytes(ns.v[i].z), sizeof(Float)); | |
+ ofs.write(as_bytes(ns.p[i]), sizeof(Float)); | |
+ ofs.write(as_bytes(ns.phi[i]), sizeof(Float)); | |
+ ofs.write(as_bytes(ns.dphi[i]), sizeof(Float)); | |
} | |
} | |
} | |
+ | |
+ ofs.write(as_bytes(params.rho_f), sizeof(Float)); | |
+ ofs.write(as_bytes(ns.p_mod_A), sizeof(Float)); | |
+ ofs.write(as_bytes(ns.p_mod_f), sizeof(Float)); | |
+ ofs.write(as_bytes(ns.p_mod_phi), sizeof(Float)); | |
+ | |
+ ofs.write(as_bytes(ns.bc_bot), sizeof(int)); | |
+ ofs.write(as_bytes(ns.bc_top), sizeof(int)); | |
+ ofs.write(as_bytes(ns.free_slip_bot), sizeof(int)); | |
+ ofs.write(as_bytes(ns.free_slip_top), sizeof(int)); | |
+ | |
+ ofs.write(as_bytes(ns.gamma), sizeof(Float)); | |
+ ofs.write(as_bytes(ns.theta), sizeof(Float)); | |
+ ofs.write(as_bytes(ns.beta), sizeof(Float)); | |
+ ofs.write(as_bytes(ns.tolerance), sizeof(Float)); | |
+ ofs.write(as_bytes(ns.maxiter), sizeof(unsigned int)); | |
} | |
+ | |
// Close file if it is still open | |
if (ofs.is_open()) | |
ofs.close(); | |
diff --git a/src/forcechains.cpp b/src/forcechains.cpp | |
t@@ -1,14 +1,3 @@ | |
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
-/* SPHERE source code by Anders Damsgaard Christensen, 2010-12, */ | |
-/* a 3D Discrete Element Method algorithm with CUDA GPU acceleration. */ | |
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
- | |
-// Licence: GNU Public License (GPL) v. 3. See license.txt. | |
-// See doc/sphere-doc.pdf for full documentation. | |
-// Compile with GNU make by typing 'make' in the src/ directory. | |
-// SPHERE is called from the command line with './sphere_<architecture> projec… | |
- | |
- | |
// Including library files | |
#include <iostream> | |
#include <string> | |
t@@ -43,23 +32,28 @@ int main(const int argc, const char *argv[]) | |
// Display help if requested | |
if (argvi == "-h" || argvi == "--help") { | |
std::cout << argv[0] << ": sphere force chain visualizer\n" | |
- << "Usage: " << argv[0] << " [OPTION[S]]... [FILE1 ...] > outp… | |
- << "-h, --help\t\tPrint help\n" | |
- << "-V, --version\t\tPrint version information and exit\n" | |
- << "-v, --verbose\t\tDisplay in-/output file names\n" | |
- << "-lc <val>, --lower-cutoff <val>\t\tOnly show contacts wher… | |
- << "-uc <val>, --upper-cutoff <val>\t\tOnly show contacts wher… | |
- << "-f, --format\t\tOutput format to stdout, interactive defau… | |
- << "\t\t\tinteractive, png, epslatex, epslatex-color\n" | |
- << "-2d\t\t\twrite output as 2d coordinates (3d default)\n" | |
- << "The values below the cutoff are not visualized, the values… | |
+ << "Usage: " << argv[0] << " [OPTION[S]]... [FILE1 ...] > " | |
+ "outputfile\nOptions:\n" | |
+ "-h, --help\t\tPrint help\n" | |
+ "-V, --version\t\tPrint version information and exit\n" | |
+ "-v, --verbose\t\tDisplay in-/output file names\n" | |
+ "-lc <val>, --lower-cutoff <val>\t\tOnly show contacts where " | |
+ "the force value is greater\n" | |
+ "-uc <val>, --upper-cutoff <val>\t\tOnly show contacts where " | |
+ "the force value is greater\n" | |
+ "-f, --format\t\tOutput format to stdout, interactive default.… | |
+ "Possible values:\n" | |
+ "\t\t\tinteractive, png, epslatex, epslatex-color\n" | |
+ "-2d\t\t\twrite output as 2d coordinates (3d default)\n" | |
+ "The values below the cutoff are not visualized, the values " | |
+ "above are truncated to the upper limit\n" | |
<< std::endl; | |
return 0; // Exit with success | |
} | |
// Display version with fancy ASCII art | |
else if (argvi == "-V" || argvi == "--version") { | |
- std::cout << "Force chain calculator, sphere version " << VERS | |
+ std::cout << "Force chain calculator, sphere version " << VERSION | |
<< std::endl; | |
return 0; | |
} | |
t@@ -84,7 +78,8 @@ int main(const int argc, const char *argv[]) | |
nfiles++; | |
if (verbose == 1) | |
- std::cout << argv[0] << ": processing input file: " << argvi <… | |
+ std::cout << argv[0] << ": processing input file: " << argvi << | |
+ std::endl; | |
// Create DEM class, read data from input binary, check values | |
DEM dem(argvi, verbose, 0, 0, 0); | |
diff --git a/src/integration.cuh b/src/integration.cuh | |
t@@ -4,6 +4,11 @@ | |
// integration.cuh | |
// Functions responsible for temporal integration | |
+//// Choose temporal integration scheme. Uncomment only one! | |
+//#define EULER | |
+//#define TY2 | |
+#define TY3 | |
+ | |
// Second order integration scheme based on Taylor expansion of particle kinem… | |
// Kernel executed on device, and callable from host only. | |
__global__ void integrate(Float4* dev_x_sorted, Float4* dev_vel_sorted, // Inp… | |
t@@ -13,176 +18,217 @@ __global__ void integrate(Float4* dev_x_sorted, Float4* … | |
Float4* dev_acc, Float4* dev_angacc, | |
Float4* dev_vel0, Float4* dev_angvel0, | |
Float2* dev_xysum, | |
- unsigned int* dev_gridParticleIndex) // Input: Sorted-Unsorted key | |
+ unsigned int* dev_gridParticleIndex, // Input: Sorted-Unsorted key | |
+ unsigned int iter) | |
{ | |
unsigned int idx = threadIdx.x + blockIdx.x * blockDim.x; // Thread id | |
if (idx < devC_np) { // Condition prevents block size error | |
- // Copy data to temporary arrays to avoid any potential read-after-wri… | |
- // write-after-read, or write-after-write hazards. | |
+ // Copy data to temporary arrays to avoid any potential | |
+ // read-after-write, write-after-read, or write-after-write hazards. | |
+ __syncthreads(); | |
unsigned int orig_idx = dev_gridParticleIndex[idx]; | |
- Float4 force = dev_force[orig_idx]; | |
- Float4 torque = dev_torque[orig_idx]; | |
- Float4 angpos = dev_angpos[orig_idx]; | |
- Float4 acc = dev_acc[orig_idx]; | |
- Float4 angacc = dev_angacc[orig_idx]; | |
- Float4 vel0 = dev_vel0[orig_idx]; | |
- Float4 angvel0 = dev_angvel0[orig_idx]; | |
- Float4 x = dev_x_sorted[idx]; | |
- Float4 vel = dev_vel_sorted[idx]; | |
- Float4 angvel = dev_angvel_sorted[idx]; | |
- Float radius = x.w; | |
- | |
- Float2 xysum = MAKE_FLOAT2(0.0f, 0.0f); | |
+ const Float4 force = dev_force[orig_idx]; | |
+ const Float4 torque = dev_torque[orig_idx]; | |
+ const Float4 angpos = dev_angpos[orig_idx]; | |
+ const Float4 x = dev_x_sorted[idx]; | |
+ const Float4 vel = dev_vel_sorted[idx]; | |
+ const Float4 angvel = dev_angvel_sorted[idx]; | |
+ Float2 xysum = dev_xysum[orig_idx]; | |
+ | |
+ // Get old accelerations for three-term Taylor expansion. These values | |
+ // don't exist in the first time step | |
+#ifdef TY3 | |
+ Float4 acc0, angacc0; | |
+ if (iter == 0) { | |
+ acc0 = MAKE_FLOAT4(0.0, 0.0, 0.0, 0.0); | |
+ angacc0 = MAKE_FLOAT4(0.0, 0.0, 0.0, 0.0); | |
+ } else { | |
+ __syncthreads(); | |
+ acc0 = dev_acc[orig_idx]; | |
+ angacc0 = dev_angacc[orig_idx]; | |
+ } | |
+#endif | |
+ | |
+ const Float radius = x.w; | |
+ | |
+ // New values | |
+ Float4 x_new, vel_new, angpos_new, angvel_new; | |
// Coherent read from constant memory to registers | |
- Float dt = devC_dt; | |
- Float3 origo = MAKE_FLOAT3(devC_grid.origo[0], devC_grid.origo[1], dev… | |
- Float3 L = MAKE_FLOAT3(devC_grid.L[0], devC_grid.L[1], devC_grid.L… | |
- Float rho = devC_params.rho; | |
+ const Float dt = devC_dt; | |
+ const Float3 origo = MAKE_FLOAT3( | |
+ devC_grid.origo[0], | |
+ devC_grid.origo[1], | |
+ devC_grid.origo[2]); | |
+ const Float3 L = MAKE_FLOAT3( | |
+ devC_grid.L[0], | |
+ devC_grid.L[1], | |
+ devC_grid.L[2]); | |
// Particle mass | |
- Float m = 4.0/3.0 * PI * radius*radius*radius * rho; | |
+ Float m = 4.0/3.0 * PI * radius*radius*radius * devC_params.rho; | |
- // Update linear acceleration of particle | |
- acc.x = force.x / m; | |
- acc.y = force.y / m; | |
- acc.z = force.z / m; | |
+ // Find the acceleration by Newton's second law | |
+ Float4 acc = MAKE_FLOAT4(0.0, 0.0, 0.0, 0.0); | |
+ acc.x = force.x/m + devC_params.g[0]; | |
+ acc.y = force.y/m + devC_params.g[1]; | |
+ acc.z = force.z/m + devC_params.g[2]; | |
- // Update angular acceleration of particle | |
+ // Find the angular acceleration by Newton's second law | |
// (angacc = (total moment)/Intertia, intertia = 2/5*m*r^2) | |
+ Float4 angacc = MAKE_FLOAT4(0.0, 0.0, 0.0, 0.0); | |
angacc.x = torque.x * 1.0 / (2.0/5.0 * m * radius*radius); | |
angacc.y = torque.y * 1.0 / (2.0/5.0 * m * radius*radius); | |
angacc.z = torque.z * 1.0 / (2.0/5.0 * m * radius*radius); | |
- // Add gravity | |
- acc.x += devC_params.g[0]; | |
- acc.y += devC_params.g[1]; | |
- acc.z += devC_params.g[2]; | |
- | |
- // Check if particle has a fixed horizontal velocity | |
+ // Modify the acceleration if the particle is marked as having a fixed | |
+ // velocity. In that case, zero the horizontal acceleration and disable | |
+ // gravity to counteract segregation. Particles may move in the | |
+ // z-dimension, to allow for dilation. | |
if (vel.w > 0.0f) { | |
- // Zero horizontal acceleration and disable | |
- // gravity to counteract segregation. | |
- // Particles may move in the z-dimension, | |
- // to allow for dilation. | |
acc.x = 0.0; | |
acc.y = 0.0; | |
acc.z -= devC_params.g[2]; | |
// Zero the angular acceleration | |
- angacc = MAKE_FLOAT4(0.0f, 0.0f, 0.0f, 0.0f); | |
- } | |
+ angacc = MAKE_FLOAT4(0.0, 0.0, 0.0, 0.0); | |
+ } | |
+ | |
+ | |
+#ifdef EULER | |
+ // Forward Euler | |
+ // Truncation error O(dt^2) for positions and velocities | |
+ x_new.x = x.x + vel.x*dt; | |
+ x_new.y = x.y + vel.y*dt; | |
+ x_new.z = x.z + vel.z*dt; | |
+ x_new.w = x.w; // transfer radius | |
+ | |
+ vel_new.x = vel.x + acc.x*dt; | |
+ vel_new.y = vel.y + acc.y*dt; | |
+ vel_new.z = vel.z + acc.z*dt; | |
+ vel_new.w = vel.w; // transfer fixvel | |
+ | |
+ angpos_new.x = angpos.x + angvel.x*dt; | |
+ angpos_new.y = angpos.y + angvel.y*dt; | |
+ angpos_new.z = angpos.z + angvel.z*dt; | |
+ angpos_new.w = angpos.w; | |
+ | |
+ angvel_new.x = angvel.x + angacc.x*dt; | |
+ angvel_new.y = angvel.y + angacc.y*dt; | |
+ angvel_new.z = angvel.z + angacc.z*dt; | |
+ angvel_new.w = angvel.w; | |
+ | |
+ // Add horizontal-displacement for this time step to the sum of | |
+ // horizontal displacements | |
+ xysum.x += vel.x*dt; | |
+ xysum.y += vel.y*dt; | |
+#endif | |
+#ifdef TY2 | |
+ // Two-term Taylor expansion (TY2) | |
+ // Truncation error O(dt^3) for positions, O(dt^2) for velocities | |
+ x_new.x = x.x + vel.x*dt + 0.5*acc.x*dt*dt; | |
+ x_new.y = x.y + vel.y*dt + 0.5*acc.y*dt*dt; | |
+ x_new.z = x.z + vel.z*dt + 0.5*acc.z*dt*dt; | |
+ x_new.w = x.w; // transfer radius | |
+ | |
+ vel_new.x = vel.x + acc.x*dt; | |
+ vel_new.y = vel.y + acc.y*dt; | |
+ vel_new.z = vel.z + acc.z*dt; | |
+ vel_new.w = vel.w; // transfer fixvel | |
+ | |
+ angpos_new.x = angpos.x + angvel.x*dt + 0.5*angacc.x*dt*dt; | |
+ angpos_new.y = angpos.y + angvel.y*dt + 0.5*angacc.y*dt*dt; | |
+ angpos_new.z = angpos.z + angvel.z*dt + 0.5*angacc.z*dt*dt; | |
+ angpos_new.w = angpos.w; | |
+ | |
+ angvel_new.x = angvel.x + angacc.x*dt; | |
+ angvel_new.y = angvel.y + angacc.y*dt; | |
+ angvel_new.z = angvel.z + angacc.z*dt; | |
+ angvel_new.w = angvel.w; | |
+ | |
+ // Add horizontal-displacement for this time step to the sum of | |
+ // horizontal displacements | |
+ xysum.x += vel.x*dt + 0.5*acc.x*dt*dt; | |
+ xysum.y += vel.y*dt + 0.5*acc.y*dt*dt; | |
+#endif | |
+ | |
+#ifdef TY3 | |
+ // Three-term Taylor expansion (TY3) | |
+ // Truncation error O(dt^4) for positions, O(dt^3) for velocities | |
+ // Approximate acceleration change by backwards difference: | |
+ const Float3 dacc_dt = MAKE_FLOAT3( | |
+ (acc.x - acc0.x)/dt, | |
+ (acc.y - acc0.y)/dt, | |
+ (acc.z - acc0.z)/dt); | |
+ | |
+ const Float3 dangacc_dt = MAKE_FLOAT3( | |
+ (angacc.x - angacc0.x)/dt, | |
+ (angacc.y - angacc0.y)/dt, | |
+ (angacc.z - angacc0.z)/dt); | |
+ | |
+ x_new.x = x.x + vel.x*dt + 0.5*acc.x*dt*dt + 1.0/6.0*dacc_dt.x*dt*dt*d… | |
+ x_new.y = x.y + vel.y*dt + 0.5*acc.y*dt*dt + 1.0/6.0*dacc_dt.y*dt*dt*d… | |
+ x_new.z = x.z + vel.z*dt + 0.5*acc.z*dt*dt + 1.0/6.0*dacc_dt.z*dt*dt*d… | |
+ x_new.w = x.w; // transfer radius | |
+ | |
+ vel_new.x = vel.x + acc.x*dt + 0.5*dacc_dt.x*dt*dt; | |
+ vel_new.y = vel.y + acc.y*dt + 0.5*dacc_dt.y*dt*dt; | |
+ vel_new.z = vel.z + acc.z*dt + 0.5*dacc_dt.z*dt*dt; | |
+ vel_new.w = vel.w; // transfer fixvel | |
+ | |
+ angpos_new.x = angpos.x + angvel.x*dt + 0.5*angacc.x*dt*dt | |
+ + 1.0/6.0*dangacc_dt.x*dt*dt*dt; | |
+ angpos_new.y = angpos.y + angvel.y*dt + 0.5*angacc.y*dt*dt | |
+ + 1.0/6.0*dangacc_dt.y*dt*dt*dt; | |
+ angpos_new.z = angpos.z + angvel.z*dt + 0.5*angacc.z*dt*dt | |
+ + 1.0/6.0*dangacc_dt.z*dt*dt*dt; | |
+ angpos_new.w = angpos.w; | |
+ | |
+ angvel_new.x = angvel.x + angacc.x*dt + 0.5*dangacc_dt.x*dt*dt; | |
+ angvel_new.y = angvel.y + angacc.y*dt + 0.5*dangacc_dt.y*dt*dt; | |
+ angvel_new.z = angvel.z + angacc.z*dt + 0.5*dangacc_dt.z*dt*dt; | |
+ angvel_new.w = angvel.w; | |
+ | |
+ // Add horizontal-displacement for this time step to the sum of | |
+ // horizontal displacements | |
+ xysum.x += vel.x*dt + 0.5*acc.x*dt*dt + 1.0/6.0*dacc_dt.x*dt*dt*dt; | |
+ xysum.y += vel.y*dt + 0.5*acc.y*dt*dt + 1.0/6.0*dacc_dt.y*dt*dt*dt; | |
+#endif | |
- // Move particle across boundary if it is periodic | |
+ // Move particles outside the domain across periodic boundaries | |
if (devC_grid.periodic == 1) { | |
- if (x.x < origo.x) | |
- x.x += L.x; | |
- if (x.x > L.x) | |
- x.x -= L.x; | |
- if (x.y < origo.y) | |
- x.y += L.y; | |
- if (x.y > L.y) | |
- x.y -= L.y; | |
+ if (x_new.x < origo.x) | |
+ x_new.x += L.x; | |
+ if (x_new.x > L.x) | |
+ x_new.x -= L.x; | |
+ if (ND == 3) { | |
+ if (x_new.y < origo.y) | |
+ x_new.y += L.y; | |
+ if (x_new.y > L.y) | |
+ x_new.y -= L.y; | |
+ } | |
} else if (devC_grid.periodic == 2) { | |
- if (x.x < origo.x) | |
- x.x += L.x; | |
- if (x.x > L.x) | |
- x.x -= L.x; | |
+ if (x_new.x < origo.x) | |
+ x_new.x += L.x; | |
+ if (x_new.x > L.x) | |
+ x_new.x -= L.x; | |
} | |
- | |
- //// Half-step leapfrog Verlet integration scheme //// | |
- // Update half-step linear velocities | |
- vel0.x += acc.x * dt; | |
- vel0.y += acc.y * dt; | |
- vel0.z += acc.z * dt; | |
- | |
- // Update half-step angular velocities | |
- angvel0.x += angacc.x * dt; | |
- angvel0.y += angacc.y * dt; | |
- angvel0.z += angacc.z * dt; | |
- | |
- // Update positions | |
- x.x += vel0.x * dt; | |
- x.y += vel0.y * dt; | |
- x.z += vel0.z * dt; | |
- | |
- // Update angular positions | |
- angpos.x += angvel0.x * dt; | |
- angpos.y += angvel0.y * dt; | |
- angpos.z += angvel0.z * dt; | |
- | |
- // Update full-step linear velocity | |
- vel.x = vel0.x + 0.5 * acc.x * dt; | |
- vel.y = vel0.y + 0.5 * acc.y * dt; | |
- vel.z = vel0.z + 0.5 * acc.z * dt; | |
- | |
- // Update full-step angular velocity | |
- angvel.x = angvel0.x + 0.5 * angacc.x * dt; | |
- angvel.y = angvel0.y + 0.5 * angacc.y * dt; | |
- angvel.z = angvel0.z + 0.5 * angacc.z * dt; | |
- | |
- /* | |
- //// First-order Euler integration scheme /// | |
- // Update angular position | |
- angpos.x += angvel.x * dt; | |
- angpos.y += angvel.y * dt; | |
- angpos.z += angvel.z * dt; | |
- | |
- // Update position | |
- x.x += vel.x * dt; | |
- x.y += vel.y * dt; | |
- x.z += vel.z * dt; | |
- */ | |
- | |
- /* | |
- /// Second-order scheme based on Taylor expansion /// | |
- // Update angular position | |
- angpos.x += angvel.x * dt + angacc.x * dt*dt * 0.5; | |
- angpos.y += angvel.y * dt + angacc.y * dt*dt * 0.5; | |
- angpos.z += angvel.z * dt + angacc.z * dt*dt * 0.5; | |
- | |
- // Update position | |
- x.x += vel.x * dt + acc.x * dt*dt * 0.5; | |
- x.y += vel.y * dt + acc.y * dt*dt * 0.5; | |
- x.z += vel.z * dt + acc.z * dt*dt * 0.5; | |
- */ | |
- | |
- /* | |
- // Update angular velocity | |
- angvel.x += angacc.x * dt; | |
- angvel.y += angacc.y * dt; | |
- angvel.z += angacc.z * dt; | |
- | |
- // Update linear velocity | |
- vel.x += acc.x * dt; | |
- vel.y += acc.y * dt; | |
- vel.z += acc.z * dt; | |
- */ | |
- | |
- // Add x-displacement for this time step to | |
- // sum of x-displacements | |
- //x.w += vel.x * dt + (acc.x * dt*dt)/2.0f; | |
- xysum.x += vel.x * dt; | |
- xysum.y += vel.y * dt;// + (acc.y * dt*dt * 0.5f; | |
- | |
// Hold threads for coalesced write | |
__syncthreads(); | |
// Store data in global memory at original, pre-sort positions | |
- dev_xysum[orig_idx] += xysum; | |
+ dev_xysum[orig_idx] = xysum; | |
dev_acc[orig_idx] = acc; | |
dev_angacc[orig_idx] = angacc; | |
- dev_angvel[orig_idx] = angvel; | |
- dev_angvel0[orig_idx] = angvel0; | |
- dev_vel[orig_idx] = vel; | |
- dev_vel0[orig_idx] = vel0; | |
- dev_angpos[orig_idx] = angpos; | |
- dev_x[orig_idx] = x; | |
+ dev_angvel[orig_idx] = angvel_new; | |
+ dev_vel[orig_idx] = vel_new; | |
+ dev_angpos[orig_idx] = angpos_new; | |
+ dev_x[orig_idx] = x_new; | |
} | |
} // End of integrate(...) | |
t@@ -226,71 +272,95 @@ __global__ void integrateWalls( | |
Float4* dev_walls_mvfd, | |
int* dev_walls_wmode, | |
Float* dev_walls_force_partial, | |
- Float* dev_walls_vel0, | |
+ Float* dev_walls_acc, | |
unsigned int blocksPerGrid, | |
- Float t_current) | |
+ Float t_current, | |
+ unsigned int iter) | |
{ | |
unsigned int idx = threadIdx.x + blockIdx.x * blockDim.x; // Thread id | |
if (idx < devC_nw) { // Condition prevents block size error | |
- // Copy data to temporary arrays to avoid any potential read-after-wri… | |
- // write-after-read, or write-after-write hazards. | |
+ // Copy data to temporary arrays to avoid any potential | |
+ // read-after-write, write-after-read, or write-after-write hazards. | |
Float4 w_nx = dev_walls_nx[idx]; | |
Float4 w_mvfd = dev_walls_mvfd[idx]; | |
int wmode = dev_walls_wmode[idx]; // Wall BC, 0: fixed, 1: devs, 2: v… | |
- Float vel0 = dev_walls_vel0[idx]; | |
- Float acc; | |
- if (wmode == 0) // Wall fixed: do nothing | |
- return; | |
+ if (wmode != 0) { // wmode == 0: Wall fixed: do nothing | |
- // Find the final sum of forces on wall | |
- w_mvfd.z = 0.0; | |
- for (int i=0; i<blocksPerGrid; ++i) { | |
- w_mvfd.z += dev_walls_force_partial[i]; | |
- } | |
+#ifdef TY3 | |
+ Float acc0; | |
+ if (iter == 0) | |
+ acc0 = 0.0; | |
+ else | |
+ acc0 = dev_walls_acc[idx]; | |
+#endif | |
- Float dt = devC_dt; | |
+ // Find the final sum of forces on wall | |
+ w_mvfd.z = 0.0; | |
+ for (int i=0; i<blocksPerGrid; ++i) { | |
+ w_mvfd.z += dev_walls_force_partial[i]; | |
+ } | |
- // Normal load = Deviatoric stress times wall surface area, | |
- // directed downwards. | |
- Float sigma_0 = w_mvfd.w + devC_params.devs_A * sin(2.0 * 3.141596654 … | |
- Float N = -sigma_0*devC_grid.L[0]*devC_grid.L[1]; | |
+ const Float dt = devC_dt; | |
- // Calculate resulting acceleration of wall | |
- // (Wall mass is stored in x component of position Float4) | |
- acc = (w_mvfd.z + N)/w_mvfd.x; | |
+ // Normal load = Deviatoric stress times wall surface area, | |
+ // directed downwards. | |
+ const Float sigma_0 = w_mvfd.w | |
+ + devC_params.devs_A*sin(2.0*PI*devC_params.devs_f * t_current… | |
+ const Float N = -sigma_0*devC_grid.L[0]*devC_grid.L[1]; | |
- // If Wall BC is controlled by velocity, it should not change | |
- if (wmode == 2) { | |
- acc = 0.0; | |
- } | |
+ // Calculate resulting acceleration of wall | |
+ // (Wall mass is stored in x component of position Float4) | |
+ Float acc = (w_mvfd.z + N)/w_mvfd.x; | |
- //// Half-step leapfrog Verlet integration scheme //// | |
- | |
- // Update half-step velocity | |
- vel0 += acc * dt; | |
+ // If Wall BC is controlled by velocity, it should not change | |
+ if (wmode == 2) { | |
+ acc = 0.0; | |
+ } | |
- // Update position. Second-order scheme based on Taylor expansion | |
- //w_nx.w += w_mvfd.y * dt + (acc * dt*dt)/2.0; | |
+#ifdef EULER | |
+ // Forward Euler tempmoral integration. | |
- // Update position | |
- w_nx.w += vel0 * dt; | |
+ // Update position | |
+ w_nx.w += w_mvfd.y*dt; | |
- // Update position. First-order Euler integration scheme | |
- //w_nx.w += w_mvfd.y * dt; | |
+ // Update velocity | |
+ w_mvfd.y += acc*dt; | |
+#endif | |
- // Update linear velocity | |
- //w_mvfd.y += acc * dt; | |
- w_mvfd.y = vel0 + 0.5 * acc * dt; | |
+#ifdef TY2 | |
+ // Two-term Taylor expansion for tempmoral integration. | |
+ // The truncation error is O(dt^3) for positions and O(dt^2) for | |
+ // velocities. | |
- //cuPrintf("\nwall %d, wmode = %d, force = %f, acc = %f\n", idx, wmode… | |
+ // Update position | |
+ w_nx.w += w_mvfd.y*dt + 0.5*acc*dt*dt; | |
- // Store data in global memory | |
- dev_walls_nx[idx] = w_nx; | |
- dev_walls_mvfd[idx] = w_mvfd; | |
- dev_walls_vel0[idx] = vel0; | |
+ // Update velocity | |
+ w_mvfd.y += acc*dt; | |
+#endif | |
+ | |
+#ifdef TY3 | |
+ // Three-term Taylor expansion for tempmoral integration. | |
+ // The truncation error is O(dt^4) for positions and O(dt^3) for | |
+ // velocities. The acceleration change approximated by backwards | |
+ // central difference: | |
+ const Float dacc_dt = (acc - acc0)/dt; | |
+ | |
+ // Update position | |
+ w_nx.w += w_mvfd.y*dt + 0.5*acc*dt*dt + 1.0/6.0*dacc_dt*dt*dt*dt; | |
+ | |
+ // Update velocity | |
+ w_mvfd.y += acc*dt + 0.5*dacc_dt*dt*dt; | |
+#endif | |
+ | |
+ // Store data in global memory | |
+ dev_walls_nx[idx] = w_nx; | |
+ dev_walls_mvfd[idx] = w_mvfd; | |
+ dev_walls_acc[idx] = acc; | |
+ } | |
} | |
} // End of integrateWalls(...) | |
diff --git a/src/latticeboltzmann.cuh b/src/latticeboltzmann.cuh | |
t@@ -1,762 +0,0 @@ | |
-#ifndef LATTICEBOLTZMANN_CUH_ | |
-#define LATTICEBOLTZMANN_CUH_ | |
- | |
-#include "utility.h" | |
- | |
-// Enable line below to perform lattice Boltzmann computations on the | |
-// GPU, disable for CPU computation | |
-//#define LBM_GPU | |
- | |
-// latticeboltzmann.cuh | |
-// Functions for solving the Navier-Stokes equations using the Lattice-Boltzma… | |
-// method with D3Q19 stencils | |
- | |
-// Calculate linear cell index from position (x,y,z) | |
-// and fluid position vector (i). | |
-// From A. Monitzer 2013 | |
-#ifdef LBM_GPU | |
-__device__ | |
-#endif | |
-unsigned int grid2index( | |
- unsigned int x, unsigned int y, unsigned int z, | |
- unsigned int i, | |
- unsigned int nx, unsigned int ny, unsigned int nz) | |
-{ | |
- return x + ((y + z*ny)*nx) + (nx*ny*nz*i); | |
-} | |
- | |
- | |
-// Equilibrium distribution | |
-#ifdef LBM_GPU | |
-__device__ | |
-#endif | |
-Float feq(Float3 v, Float rho, Float3 e, Float w, Float dt, Float dx) | |
-{ | |
- // Monitzer 2010 | |
- //return w*rho * (1.0 - 3.0/2.0 * dot(v,v) + 3.0*dot(e,v) + | |
- //9.0/2.0*dot(e,v)*dot(e,v)); | |
- | |
- // Rinaldi 2012 | |
- //return w*rho * (1.0 + 3.0*dot(e,v) + 9.0/2.0*dot(e,v)*dot(e,v) | |
- //- 3.0/2.0*dot(v,v)); | |
- | |
- // Hecht 2010 | |
- //Float c2_s = 1.0/sqrt(3); // D3Q19 lattice speed of sound | |
- //c2_s *= c2_s; | |
- //return w*rho * (1.0 + dot(e,v)/c2_s | |
- //+ (dot(e,v)*dot(e,v))/(2.0*c2_s*c2_s) | |
- //- dot(v,v)*dot(v,v)/(2.0*c2_s)); | |
- | |
- // Chirila 2010 | |
- //Float c2 = 1.0*grid.num[0]/devC_dt; | |
- //Float c2 = 1.0/sqrt(3.0); | |
- //c2 *= c2; // Propagation speed on the lattice | |
- //return w*rho * (1.0 + 3.0*dot(e,v)/c2 | |
- //+ 9.0/2.0 * dot(e,v)*dot(e,v)/(c2*c2) | |
- //- 3.0/2.0 * dot(v,v)/c2); | |
- | |
- // Habich 2011 | |
- Float c2 = dx/dt * dx/dt; | |
- return rho*w | |
- * (1.0 + 3.0/c2*dot(e,v) | |
- + 9.0/(2.0*c2*c2) * dot(e,v)*dot(e,v) | |
- - 3.0/(2.0*c2) * dot(v,v)); | |
-} | |
- | |
-// Collision operator | |
-// Bhatnagar-Gross-Krook approximation (BGK), Thurey (2003). | |
-#ifdef LBM_GPU | |
-__device__ | |
-#endif | |
-Float bgk( | |
- Float dt, | |
- Float dx, | |
- Float f, | |
- Float tau, | |
- Float3 v, | |
- Float rho, | |
- Float3 e, | |
- Float w, | |
- Float3 extF) | |
-{ | |
- //Float feqval = feq(v, rho, e, w); | |
- //printf("feq(v, rho=%f, e, w=%f) = %f\n", rho, w, feqval); | |
- | |
- // Monitzer 2008 | |
- //return dt / tau * (f - feq(v, rho, e, w)) | |
- //- (1.0 - 1.0/(2.0*tau)) * 3.0/w * dot(extF, e); | |
- //return dt / tau * (f - feq(v, rho, e, w)) | |
- // + (2.0*tau - 1.0/(2.0*tau)) * 3.0/w * dot(extF, e); | |
- //return dt/tau * (f - feq(v, rho, e, w)) | |
- //+ (2.0*tau - 1.0/(2.0*tau)) * 3.0/w * dot(extF, e); | |
- | |
- // Monitzer 2010 | |
- //return dt/tau*(f - feq(v, rho, e, w)) | |
- //+ (2.0*tau - 1.0)/(2.0*tau) * 3.0/w * dot(extF, e); | |
- | |
- // Rinaldi 2012 | |
- //return 1.0/tau * (feq(v, rho, e, w) - f); | |
- | |
- // Habich 2011 | |
- return (f - feq(v, rho, e, w, dt, dx))/tau | |
- + (2.0*tau - 1.0)/(2.0*tau) * 3.0/w * dot(extF, e); | |
-} | |
- | |
-// Initialize the fluid distributions on the base of the densities provided | |
-#ifdef LBM_GPU | |
-__global__ void initFluid( | |
- Float4* dev_v_rho, | |
- Float* dev_f) | |
-#else | |
-void initFluid( | |
- Float4* dev_v_rho, | |
- Float* dev_f, | |
- unsigned int nx, | |
- unsigned int ny, | |
- unsigned int nz) | |
-#endif | |
- | |
-{ | |
-#ifdef LBM_GPU | |
- // 3D thread index | |
- const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
- const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
- const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
- | |
- // Grid dimensions | |
- const unsigned int nx = devC_grid.num[0]; | |
- const unsigned int ny = devC_grid.num[1]; | |
- const unsigned int nz = devC_grid.num[2]; | |
-#else | |
- for (unsigned int z = 0; z<nz; z++) { | |
- for (unsigned int y = 0; y<ny; y++) { | |
- for (unsigned int x = 0; x<nx; x++) { | |
-#endif | |
- | |
- // Check that we are not outside the fluid grid | |
- if (x < nx && y < ny && z < nz) { | |
- | |
- // 1D thread index | |
- const unsigned int tidx = x + nx*y + nx*ny*z; | |
- | |
- // Read velocity and density, zero velocity | |
-#ifdef LBM_GPU | |
- __syncthreads(); | |
-#endif | |
- Float4 v_rho = dev_v_rho[tidx]; | |
- v_rho = MAKE_FLOAT4(0.0, 0.0, 0.0, v_rho.w); | |
- | |
- // Set values to equilibrium distribution (f_i = w_i * rho_0) | |
-#ifdef LBM_GPU | |
- __syncthreads(); | |
-#endif | |
- dev_v_rho[tidx] = v_rho; | |
- dev_f[grid2index(x,y,z,0,nx,ny,nz)] = 1.0/3.0 * v_rho.w; | |
- dev_f[grid2index(x,y,z,1,nx,ny,nz)] = 1.0/18.0 * v_rho.w; | |
- dev_f[grid2index(x,y,z,2,nx,ny,nz)] = 1.0/18.0 * v_rho.w; | |
- dev_f[grid2index(x,y,z,3,nx,ny,nz)] = 1.0/18.0 * v_rho.w; | |
- dev_f[grid2index(x,y,z,4,nx,ny,nz)] = 1.0/18.0 * v_rho.w; | |
- dev_f[grid2index(x,y,z,5,nx,ny,nz)] = 1.0/18.0 * v_rho.w; | |
- dev_f[grid2index(x,y,z,6,nx,ny,nz)] = 1.0/18.0 * v_rho.w; | |
- dev_f[grid2index(x,y,z,7,nx,ny,nz)] = 1.0/36.0 * v_rho.w; | |
- dev_f[grid2index(x,y,z,8,nx,ny,nz)] = 1.0/36.0 * v_rho.w; | |
- dev_f[grid2index(x,y,z,9,nx,ny,nz)] = 1.0/36.0 * v_rho.w; | |
- dev_f[grid2index(x,y,z,10,nx,ny,nz)] = 1.0/36.0 * v_rho.w; | |
- dev_f[grid2index(x,y,z,11,nx,ny,nz)] = 1.0/36.0 * v_rho.w; | |
- dev_f[grid2index(x,y,z,12,nx,ny,nz)] = 1.0/36.0 * v_rho.w; | |
- dev_f[grid2index(x,y,z,13,nx,ny,nz)] = 1.0/36.0 * v_rho.w; | |
- dev_f[grid2index(x,y,z,14,nx,ny,nz)] = 1.0/36.0 * v_rho.w; | |
- dev_f[grid2index(x,y,z,15,nx,ny,nz)] = 1.0/36.0 * v_rho.w; | |
- dev_f[grid2index(x,y,z,16,nx,ny,nz)] = 1.0/36.0 * v_rho.w; | |
- dev_f[grid2index(x,y,z,17,nx,ny,nz)] = 1.0/36.0 * v_rho.w; | |
- dev_f[grid2index(x,y,z,18,nx,ny,nz)] = 1.0/36.0 * v_rho.w; | |
- } | |
-#ifndef LBM_GPU | |
- }}} | |
-#endif | |
-} | |
- | |
-// Combined streaming and collision step with particle coupling and optional | |
-// periodic boundaries. Derived from A. Monitzer 2013 | |
-#ifdef LBM_GPU | |
-__global__ void latticeBoltzmannD3Q19( | |
- Float* dev_f, | |
- Float* dev_f_new, | |
- Float4* dev_v_rho, // fluid velocities and densities | |
- unsigned int* dev_cellStart, // first particle in cells | |
- unsigned int* dev_cellEnd, // last particle in cells | |
- Float4* dev_x_sorted, // particle positions + radii | |
- Float4* dev_vel_sorted, // particle velocities + fixvel | |
- Float4* dev_force, | |
- unsigned int* dev_gridParticleIndex) | |
-#else | |
-void latticeBoltzmannD3Q19( | |
- Float* dev_f, | |
- Float* dev_f_new, | |
- Float4* dev_v_rho, // fluid velocities and densities | |
- Float devC_dt, | |
- Grid& grid, | |
- Params& params) | |
- | |
-#endif | |
-{ | |
-#ifdef LBM_GPU | |
- // 3D thread index | |
- const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
- const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
- const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
- | |
- // Grid dimensions | |
- const unsigned int nx = devC_grid.num[0]; | |
- const unsigned int ny = devC_grid.num[1]; | |
- const unsigned int nz = devC_grid.num[2]; | |
-#else | |
- // Grid dimensions | |
- const unsigned int nx = grid.num[0]; | |
- const unsigned int ny = grid.num[1]; | |
- const unsigned int nz = grid.num[2]; | |
- | |
- for (unsigned int z = 0; z<nz; z++) { | |
- for (unsigned int y = 0; y<ny; y++) { | |
- for (unsigned int x = 0; x<nx; x++) { | |
-#endif | |
- | |
- // Check that we are not outside the fluid grid | |
- if (x < nx && y < ny && z < nz) { | |
- | |
- // 1D thread index | |
- const unsigned int tidx = x + nx*y + nx*ny*z; | |
- //printf("(x,y,x) = (%d,%d,%d), tidx = %d\n", x, y, z, tidx); | |
- | |
- // Load the fluid distribution into local registers | |
-#ifdef LBM_GPU | |
- __syncthreads(); | |
-#endif | |
- Float f_0 = dev_f[grid2index(x,y,z,0,nx,ny,nz)]; | |
- Float f_1 = dev_f[grid2index(x,y,z,1,nx,ny,nz)]; | |
- Float f_2 = dev_f[grid2index(x,y,z,2,nx,ny,nz)]; | |
- Float f_3 = dev_f[grid2index(x,y,z,3,nx,ny,nz)]; | |
- Float f_4 = dev_f[grid2index(x,y,z,4,nx,ny,nz)]; | |
- Float f_5 = dev_f[grid2index(x,y,z,5,nx,ny,nz)]; | |
- Float f_6 = dev_f[grid2index(x,y,z,6,nx,ny,nz)]; | |
- Float f_7 = dev_f[grid2index(x,y,z,7,nx,ny,nz)]; | |
- Float f_8 = dev_f[grid2index(x,y,z,8,nx,ny,nz)]; | |
- Float f_9 = dev_f[grid2index(x,y,z,9,nx,ny,nz)]; | |
- Float f_10 = dev_f[grid2index(x,y,z,10,nx,ny,nz)]; | |
- Float f_11 = dev_f[grid2index(x,y,z,11,nx,ny,nz)]; | |
- Float f_12 = dev_f[grid2index(x,y,z,12,nx,ny,nz)]; | |
- Float f_13 = dev_f[grid2index(x,y,z,13,nx,ny,nz)]; | |
- Float f_14 = dev_f[grid2index(x,y,z,14,nx,ny,nz)]; | |
- Float f_15 = dev_f[grid2index(x,y,z,15,nx,ny,nz)]; | |
- Float f_16 = dev_f[grid2index(x,y,z,16,nx,ny,nz)]; | |
- Float f_17 = dev_f[grid2index(x,y,z,17,nx,ny,nz)]; | |
- Float f_18 = dev_f[grid2index(x,y,z,18,nx,ny,nz)]; | |
- | |
- // Directional vectors to each lattice-velocity in D3Q19 | |
- // Zero velocity: i = 0 | |
- // Faces: i = 1..6 | |
- // Edges: i = 7..18 | |
- const Float3 e_0 = MAKE_FLOAT3( 0.0, 0.0, 0.0); // zero vel. | |
- const Float3 e_1 = MAKE_FLOAT3( 1.0, 0.0, 0.0); // face: +x | |
- const Float3 e_2 = MAKE_FLOAT3(-1.0, 0.0, 0.0); // face: -x | |
- const Float3 e_3 = MAKE_FLOAT3( 0.0, 1.0, 0.0); // face: +y | |
- const Float3 e_4 = MAKE_FLOAT3( 0.0,-1.0, 0.0); // face: -y | |
- const Float3 e_5 = MAKE_FLOAT3( 0.0, 0.0, 1.0); // face: +z | |
- const Float3 e_6 = MAKE_FLOAT3( 0.0, 0.0,-1.0); // face: -z | |
- const Float3 e_7 = MAKE_FLOAT3( 1.0, 1.0, 0.0); // edge: +x,+y | |
- const Float3 e_8 = MAKE_FLOAT3(-1.0,-1.0, 0.0); // edge: -x,-y | |
- const Float3 e_9 = MAKE_FLOAT3(-1.0, 1.0, 0.0); // edge: -x,+y | |
- const Float3 e_10 = MAKE_FLOAT3( 1.0,-1.0, 0.0); // edge: +x,-y | |
- const Float3 e_11 = MAKE_FLOAT3( 1.0, 0.0, 1.0); // edge: +x,+z | |
- const Float3 e_12 = MAKE_FLOAT3(-1.0, 0.0,-1.0); // edge: -x,-z | |
- const Float3 e_13 = MAKE_FLOAT3( 0.0, 1.0, 1.0); // edge: +y,+z | |
- const Float3 e_14 = MAKE_FLOAT3( 0.0,-1.0,-1.0); // edge: -y,-z | |
- const Float3 e_15 = MAKE_FLOAT3(-1.0, 0.0, 1.0); // edge: -x,+z | |
- const Float3 e_16 = MAKE_FLOAT3( 1.0, 0.0,-1.0); // edge: +x,-z | |
- const Float3 e_17 = MAKE_FLOAT3( 0.0,-1.0, 1.0); // edge: -y,+z | |
- const Float3 e_18 = MAKE_FLOAT3( 0.0, 1.0,-1.0); // edge: +y,-z | |
- | |
- | |
- //// Calculate the cell's macroproperties | |
- | |
- // Fluid density (rho = sum(f_i)) | |
- const Float rho = f_0 + f_1 + f_2 + f_3 + f_4 + f_5 + f_6 + f_7 + f_8 + | |
- f_9 + f_10 + f_11 + f_12 + f_13 + f_14 + f_15 + f_16 + f_17 + f_18; | |
- | |
- // Fluid velocity (v = sum(f_i*e_i)/rho) | |
- const Float3 v = (f_0*e_0 + f_1*e_1 + f_2*e_2 + f_3*e_3 + f_4*e_4 + | |
- f_5*e_5 + f_6*e_6 + f_7*e_7 + f_8*e_8 + f_9*e_9 + f_10*e_10 + | |
- f_11*e_11 + f_12*e_12 + f_13*e_13 + f_14*e_14 + f_15*e_15 + | |
- f_16*e_16 + f_17*e_17 + f_18*e_18) / rho; | |
- | |
- //// Calculate the force transferred from the particles to the fluid | |
- /*Float3 f_particle; | |
- Float3 f_particles = MAKE_FLOAT3(0.0, 0.0, 0.0); | |
- Float4 x_particle4; // particle position + radius | |
- Float r_particle; // radius | |
- Float4 v_particle4; // particle velocity + fixvel | |
- Float3 v_particle; // particle velocity | |
- | |
- | |
- // Lowest particle index in cell | |
- unsigned int startIdx = dev_cellStart[tidx]; | |
- //unsigned int orig_idx; | |
- | |
- // Make sure cell is not empty | |
- if (startIdx != 0xffffffff) { | |
- | |
- // Highest particle index in cell + 1 | |
- unsigned int endIdx = dev_cellEnd[tidx]; | |
- | |
- // Iterate over cell particles | |
- for (unsigned int idx = startIdx; idx<endIdx; ++idx) { | |
- | |
- // Read particle radius and velocity | |
- __syncthreads(); | |
- x_particle4 = dev_x_sorted[idx]; | |
- v_particle4 = dev_vel_sorted[idx]; | |
- | |
- r_particle = x_particle4.w; | |
- v_particle = MAKE_FLOAT3( | |
- v_particle4.x, | |
- v_particle4.y, | |
- v_particle4.z); | |
- | |
- // Aerodynamic drag | |
- f_particle = (v - v_particle) * r_particle*r_particle; | |
- | |
- // Add the drag force to the sum of forces in the cell | |
- f_particles += f_particle; | |
- | |
- // The particle experiences the opposite drag force | |
- // Causes "unspecified launch failure" | |
- //orig_idx = dev_gridParticleIndex[idx]; | |
- //dev_force[orig_idx] = MAKE_FLOAT4( | |
- //-f_particle.x, | |
- //-f_particle.y, | |
- //-f_particle.z, | |
- //0.0); | |
- } | |
- } | |
- | |
- // Scale the particle force | |
- // 100: experimental value, depends on the grid size compared to the | |
- // particle size and the time step size | |
- f_particles *= 100.0 * rho * 6.0; | |
- */ | |
- | |
-#ifdef LBM_GPU | |
- Float dx = devC_grid.L[0]/devC_grid.num[0]; | |
- | |
- // Fluid constant (Wei et al. 2004), nu: kinematic viscosity [Pa*s] | |
- // nu = 1/6*(2*tau - 1) * dx * c | |
- //const Float tau = 0.5*(1.0 + 6.0*devC_params.nu); | |
- //const Float tau = 1.0/6.0*(2.0*devC_params.nu - 1.0) * dx*dx/devC_dt; | |
- const Float tau = (6.0*devC_params.nu * devC_dt/(dx*dx) + 1)/2.0; | |
- | |
- // Gravitational force (F = g * m) | |
- const Float3 f_gravity = MAKE_FLOAT3( | |
- devC_params.g[0]*dx*rho, | |
- devC_params.g[1] | |
- * ((Float)devC_grid.L[1]/devC_grid.num[1]) * rho, | |
- devC_params.g[2] | |
- * ((Float)devC_grid.L[2]/devC_grid.num[2]) * rho); | |
-#else | |
- Float dx = grid.L[0]/grid.num[0]; | |
- | |
- // Fluid constant (Wei et al. 2004), nu: kinematic viscosity [Pa*s] | |
- //const Float tau = 0.5*(1.0 + 6.0*params.nu); | |
- //const Float tau = 1.0/6.0*(2.0*params.nu - 1.0) * dx*dx/devC_dt; | |
- const Float tau = (6.0*params.nu * devC_dt/(dx*dx) + 1)/2.0; | |
- | |
- //if (tau <= 0.5) { | |
- //fprintf(stderr, "Error, tau <= 0.5\n"); | |
- //exit(1); | |
- //} | |
- | |
- // Gravitational force (F = g * m) | |
- const Float3 f_gravity = MAKE_FLOAT3( | |
- params.g[0]*dx*rho, | |
- params.g[1] * ((Float)grid.L[1]/grid.num[1]) * rho, | |
- params.g[2] * ((Float)grid.L[2]/grid.num[2]) * rho); | |
-#endif | |
- | |
- // The final external force | |
- //const Float3 f_ext = f_particles + f_gravity; | |
- const Float3 f_ext = f_gravity; | |
- //const Float3 f_ext = MAKE_FLOAT3(0.0, 0.0, 0.0); | |
- //printf("%d,%d,%d: f_ext = %f, %f, %f\n", x, y, z, | |
- //f_ext.x, f_ext.y, f_ext.z); | |
- | |
- //// Collide fluid | |
- // Weights corresponding to each e_i lattice-velocity in D3Q19, sum to… | |
- f_0 -= bgk(devC_dt, dx, f_0, tau, v, rho, e_0, 1.0/3.0, f_ext); | |
- f_1 -= bgk(devC_dt, dx, f_1, tau, v, rho, e_1, 1.0/18.0, f_ext); | |
- f_2 -= bgk(devC_dt, dx, f_2, tau, v, rho, e_2, 1.0/18.0, f_ext); | |
- f_3 -= bgk(devC_dt, dx, f_3, tau, v, rho, e_3, 1.0/18.0, f_ext); | |
- f_4 -= bgk(devC_dt, dx, f_4, tau, v, rho, e_4, 1.0/18.0, f_ext); | |
- f_5 -= bgk(devC_dt, dx, f_5, tau, v, rho, e_5, 1.0/18.0, f_ext); | |
- f_6 -= bgk(devC_dt, dx, f_6, tau, v, rho, e_6, 1.0/18.0, f_ext); | |
- f_7 -= bgk(devC_dt, dx, f_7, tau, v, rho, e_7, 1.0/36.0, f_ext); | |
- f_8 -= bgk(devC_dt, dx, f_8, tau, v, rho, e_8, 1.0/36.0, f_ext); | |
- f_9 -= bgk(devC_dt, dx, f_9, tau, v, rho, e_9, 1.0/36.0, f_ext); | |
- f_10 -= bgk(devC_dt, dx, f_10, tau, v, rho, e_10, 1.0/36.0, f_ext); | |
- f_11 -= bgk(devC_dt, dx, f_11, tau, v, rho, e_11, 1.0/36.0, f_ext); | |
- f_12 -= bgk(devC_dt, dx, f_12, tau, v, rho, e_12, 1.0/36.0, f_ext); | |
- f_13 -= bgk(devC_dt, dx, f_13, tau, v, rho, e_13, 1.0/36.0, f_ext); | |
- f_14 -= bgk(devC_dt, dx, f_14, tau, v, rho, e_14, 1.0/36.0, f_ext); | |
- f_15 -= bgk(devC_dt, dx, f_15, tau, v, rho, e_15, 1.0/36.0, f_ext); | |
- f_16 -= bgk(devC_dt, dx, f_16, tau, v, rho, e_16, 1.0/36.0, f_ext); | |
- f_17 -= bgk(devC_dt, dx, f_17, tau, v, rho, e_17, 1.0/36.0, f_ext); | |
- f_18 -= bgk(devC_dt, dx, f_18, tau, v, rho, e_18, 1.0/36.0, f_ext); | |
- //Float bgkval = bgk(devC_dt, f_1, tau, v, rho, e_1, 1.0/18.0, f_ext… | |
- //Float feqval = feq(v, rho, e_1, 1.0/18.0); | |
- //printf("%d,%d,%d: dt %f, f %f, feq %f, tau %f, v %fx%fx%f, rho %f, e… | |
- //x, y, z, devC_dt, f_1, feqval, tau, v.x, v.y, v.z, rho, | |
- //e_1.x, e_1.y, e_1.z, f_ext.x, f_ext.y, f_ext.z, bgkval); | |
- | |
- | |
- //// Stream fluid | |
- // Lower and upper boundaries: bounceback, sides: periodic | |
- | |
- | |
- // There may be a write conflict due to bounce backs | |
-#ifdef LBM_GPU | |
- __syncthreads(); | |
-#endif | |
- | |
- // Write fluid velocity and density to global memory | |
- dev_v_rho[tidx] = MAKE_FLOAT4(v.x, v.y, v.z, rho); | |
- //dev_v_rho[tidx] = MAKE_FLOAT4(x, y, z, rho); | |
- | |
- // Face 0 | |
- dev_f_new[grid2index(x,y,z,0,nx,ny,nz)] = fmax(0.0, f_0); | |
- | |
- //* | |
- | |
- // Face 1 (+x): Bounce back | |
- if (x < nx-1) | |
- dev_f_new[grid2index(x+1, y, z, 1,nx,ny,nz)] = fmax(0.0, f_1); | |
- else | |
- dev_f_new[grid2index( x, y, z, 2,nx,ny,nz)] = fmax(0.0, f_1); | |
- | |
- // Face 2 (-x): Bounce back | |
- if (x > 0) | |
- dev_f_new[grid2index(x-1, y, z, 2,nx,ny,nz)] = fmax(0.0, f_2); | |
- else | |
- dev_f_new[grid2index( x, y, z, 1,nx,ny,nz)] = fmax(0.0, f_2); | |
- | |
- // Face 3 (+y): Bounce back | |
- if (y < ny-1) | |
- dev_f_new[grid2index( x,y+1, z, 3,nx,ny,nz)] = fmax(0.0, f_3); | |
- else | |
- dev_f_new[grid2index( x, y, z, 4,nx,ny,nz)] = fmax(0.0, f_3); | |
- | |
- // Face 4 (-y): Bounce back | |
- if (y > 0) | |
- dev_f_new[grid2index( x,y-1, z, 4,nx,ny,nz)] = fmax(0.0, f_4); | |
- else | |
- dev_f_new[grid2index( x, y, z, 3,nx,ny,nz)] = fmax(0.0, f_4); | |
- | |
- // Face 5 (+z): Bounce back | |
- if (z < nz-1) | |
- dev_f_new[grid2index( x, y,z+1, 5,nx,ny,nz)] = fmax(0.0, f_5); | |
- else | |
- dev_f_new[grid2index( x, y, z, 6,nx,ny,nz)] = fmax(0.0, f_5); | |
- | |
- // Face 6 (-z): Bounce back | |
- if (z > 0) | |
- dev_f_new[grid2index( x, y,z-1, 6,nx,ny,nz)] = fmax(0.0, f_6); | |
- else | |
- dev_f_new[grid2index( x, y, z, 5,nx,ny,nz)] = fmax(0.0, f_6); | |
- | |
- // Edge 7 (+x,+y): Bounce back | |
- if (x < nx-1 && y < ny-1) | |
- dev_f_new[grid2index(x+1,y+1, z, 7,nx,ny,nz)] = fmax(0.0, f_7); | |
- else if (x < nx-1) | |
- dev_f_new[grid2index(x+1, y, z, 9,nx,ny,nz)] = fmax(0.0, f_7); | |
- else if (y < ny-1) | |
- dev_f_new[grid2index( x,y+1, z, 10,nx,ny,nz)] = fmax(0.0, f_7); | |
- else | |
- dev_f_new[grid2index( x, y, z, 8,nx,ny,nz)] = fmax(0.0, f_7); | |
- | |
- // Edge 8 (-x,-y): Bounce back | |
- if (x > 0 && y > 0) | |
- dev_f_new[grid2index(x-1,y-1, z, 8,nx,ny,nz)] = fmax(0.0, f_8); | |
- else if (x > 0) | |
- dev_f_new[grid2index(x-1, y, z, 9,nx,ny,nz)] = fmax(0.0, f_8); | |
- else if (y > 0) | |
- dev_f_new[grid2index( x,y-1, z, 10,nx,ny,nz)] = fmax(0.0, f_8); | |
- else | |
- dev_f_new[grid2index( x, y, z, 7,nx,ny,nz)] = fmax(0.0, f_8); | |
- | |
- // Edge 9 (-x,+y): Bounce back | |
- if (x > 0 && y < ny-1) | |
- dev_f_new[grid2index(x-1,y+1, z, 9,nx,ny,nz)] = fmax(0.0, f_9); | |
- else if (x > 0) | |
- dev_f_new[grid2index(x-1, y, z, 8,nx,ny,nz)] = fmax(0.0, f_9); | |
- else if (y < ny-1) | |
- dev_f_new[grid2index( x,y+1, z, 7,nx,ny,nz)] = fmax(0.0, f_9); | |
- else | |
- dev_f_new[grid2index( x, y, z, 10,nx,ny,nz)] = fmax(0.0, f_9); | |
- | |
- // Edge 10 (+x,-y): Bounce back | |
- if (x < nx-1 && y > 0) | |
- dev_f_new[grid2index(x+1,y-1, z, 10,nx,ny,nz)] = fmax(0.0, f_10); | |
- else if (x < nx-1) | |
- dev_f_new[grid2index(x+1, y, z, 8,nx,ny,nz)] = fmax(0.0, f_10); | |
- else if (y > 0) | |
- dev_f_new[grid2index( x,y-1, z, 7,nx,ny,nz)] = fmax(0.0, f_10); | |
- else | |
- dev_f_new[grid2index( x, y, z, 9,nx,ny,nz)] = fmax(0.0, f_10); | |
- | |
- // Edge 11 (+x,+z): Bounce back | |
- if (x < nx-1 && z < nz-1) | |
- dev_f_new[grid2index(x+1, y,z+1, 11,nx,ny,nz)] = fmax(0.0, f_11); | |
- else if (x < nx-1) | |
- dev_f_new[grid2index(x+1, y, z, 16,nx,ny,nz)] = fmax(0.0, f_11); | |
- else if (z < nz-1) | |
- dev_f_new[grid2index( x, y,z+1, 15,nx,ny,nz)] = fmax(0.0, f_11); | |
- else | |
- dev_f_new[grid2index( x, y, z, 12,nx,ny,nz)] = fmax(0.0, f_11); | |
- | |
- // Edge 12 (-x,-z): Bounce back | |
- if (x > 0 && z > 0) | |
- dev_f_new[grid2index(x-1, y,z-1, 12,nx,ny,nz)] = fmax(0.0, f_12); | |
- else if (x > 0) | |
- dev_f_new[grid2index(x-1, y, z, 15,nx,ny,nz)] = fmax(0.0, f_12); | |
- else if (z > 0) | |
- dev_f_new[grid2index( x, y,z-1, 16,nx,ny,nz)] = fmax(0.0, f_12); | |
- else | |
- dev_f_new[grid2index( x, y, z, 11,nx,ny,nz)] = fmax(0.0, f_12); | |
- | |
- // Edge 13 (+y,+z): Bounce back | |
- if (y < ny-1 && z < nz-1) | |
- dev_f_new[grid2index( x,y+1,z+1, 13,nx,ny,nz)] = fmax(0.0, f_13); | |
- else if (y < ny-1) | |
- dev_f_new[grid2index( x,y+1, z, 18,nx,ny,nz)] = fmax(0.0, f_13); | |
- else if (z < nz-1) | |
- dev_f_new[grid2index( x, y,z+1, 17,nx,ny,nz)] = fmax(0.0, f_13); | |
- else | |
- dev_f_new[grid2index( x, y, z, 14,nx,ny,nz)] = fmax(0.0, f_13); | |
- | |
- // Edge 14 (-y,-z): Bounce back | |
- if (y > 0 && z > 0) | |
- dev_f_new[grid2index( x,y-1,z-1, 14,nx,ny,nz)] = fmax(0.0, f_14); | |
- else if (y > 0) | |
- dev_f_new[grid2index( x,y-1, z, 17,nx,ny,nz)] = fmax(0.0, f_14); | |
- else if (z > 0) | |
- dev_f_new[grid2index( x, y,z-1, 18,nx,ny,nz)] = fmax(0.0, f_14); | |
- else | |
- dev_f_new[grid2index( x, y, z, 13,nx,ny,nz)] = fmax(0.0, f_14); | |
- | |
- // Edge 15 (-x,+z): Bounce back | |
- if (x > 0 && z < nz-1) | |
- dev_f_new[grid2index(x-1, y,z+1, 15,nx,ny,nz)] = fmax(0.0, f_15); | |
- else if (x > 0) | |
- dev_f_new[grid2index(x-1, y, z, 12,nx,ny,nz)] = fmax(0.0, f_15); | |
- else if (z < nz-1) | |
- dev_f_new[grid2index( x, y,z+1, 11,nx,ny,nz)] = fmax(0.0, f_15); | |
- else | |
- dev_f_new[grid2index( x, y, z, 16,nx,ny,nz)] = fmax(0.0, f_15); | |
- | |
- // Edge 16 (+x,-z) | |
- if (x < nx-1 && z > 0) | |
- dev_f_new[grid2index(x+1, y,z-1, 16,nx,ny,nz)] = fmax(0.0, f_16); | |
- else if (x < nx-1) | |
- dev_f_new[grid2index(x+1, y, z, 11,nx,ny,nz)] = fmax(0.0, f_16); | |
- else if (z > 0) | |
- dev_f_new[grid2index( x, y,z-1, 12,nx,ny,nz)] = fmax(0.0, f_16); | |
- else | |
- dev_f_new[grid2index( x, y, z, 15,nx,ny,nz)] = fmax(0.0, f_16); | |
- | |
- // Edge 17 (-y,+z) | |
- if (y > 0 && z < nz-1) | |
- dev_f_new[grid2index( x,y-1,z+1, 17,nx,ny,nz)] = fmax(0.0, f_17); | |
- else if (y > 0) | |
- dev_f_new[grid2index( x,y-1, z, 14,nx,ny,nz)] = fmax(0.0, f_17); | |
- else if (z < nz-1) | |
- dev_f_new[grid2index( x, y,z+1, 13,nx,ny,nz)] = fmax(0.0, f_17); | |
- else | |
- dev_f_new[grid2index( x, y, z, 18,nx,ny,nz)] = fmax(0.0, f_17); | |
- | |
- // Edge 18 (+y,-z) | |
- if (y < ny-1 && z > 0) | |
- dev_f_new[grid2index( x,y+1,z-1, 18,nx,ny,nz)] = fmax(0.0, f_18); | |
- else if (y < ny-1) | |
- dev_f_new[grid2index( x,y+1, z, 13,nx,ny,nz)] = fmax(0.0, f_18); | |
- else if (z > 0) | |
- dev_f_new[grid2index( x, y,z-1, 14,nx,ny,nz)] = fmax(0.0, f_18); | |
- else | |
- dev_f_new[grid2index( x, y, z, 17,nx,ny,nz)] = fmax(0.0, f_18); | |
- | |
- // */ | |
- | |
- /* | |
- | |
- // Face 1 (+x): Periodic | |
- if (x < nx-1) // not at boundary | |
- dev_f_new[grid2index( x+1, y, z, 1,nx,ny,nz)] = fmax(0.0, f_1… | |
- else // at boundary | |
- dev_f_new[grid2index( 0, y, z, 1,nx,ny,nz)] = fmax(0.0, f_1… | |
- | |
- // Face 2 (-x): Periodic | |
- if (x > 0) // not at boundary | |
- dev_f_new[grid2index( x-1, y, z, 2,nx,ny,nz)] = fmax(0.0, f_2… | |
- else // at boundary | |
- dev_f_new[grid2index(nx-1, y, z, 2,nx,ny,nz)] = fmax(0.0, f_2… | |
- | |
- // Face 3 (+y): Periodic | |
- if (y < ny-1) // not at boundary | |
- dev_f_new[grid2index( x, y+1, z, 3,nx,ny,nz)] = fmax(0.0, f_3… | |
- else // at boundary | |
- dev_f_new[grid2index( x, 0, z, 3,nx,ny,nz)] = fmax(0.0, f_3… | |
- | |
- // Face 4 (-y): Periodic | |
- if (y > 0) // not at boundary | |
- dev_f_new[grid2index( x, y-1, z, 4,nx,ny,nz)] = fmax(0.0, f_4… | |
- else // at boundary | |
- dev_f_new[grid2index( x,ny-1, z, 4,nx,ny,nz)] = fmax(0.0, f_4… | |
- | |
- // Face 5 (+z): Bounce back, free slip | |
- if (z < nz-1) // not at boundary | |
- dev_f_new[grid2index( x, y, z+1, 5,nx,ny,nz)] = fmax(0.0, f_5… | |
- else // at boundary | |
- dev_f_new[grid2index( x, y, z, 6,nx,ny,nz)] = fmax(0.0, f_5… | |
- | |
- // Face 6 (-z): Bounce back, free slip | |
- if (z > 0) // not at boundary | |
- dev_f_new[grid2index( x, y, z-1, 6,nx,ny,nz)] = fmax(0.0, f_6… | |
- else // at boundary | |
- dev_f_new[grid2index( x, y, z, 5,nx,ny,nz)] = fmax(0.0, f_6… | |
- | |
- // Edge 7 (+x,+y): Periodic | |
- if (x < nx-1 && y < ny-1) // not at boundary | |
- dev_f_new[grid2index( x+1, y+1, z, 7,nx,ny,nz)] = fmax(0.0, f_7… | |
- else if (x < nx-1) // at +y boundary | |
- dev_f_new[grid2index( x+1, 0, z, 7,nx,ny,nz)] = fmax(0.0, f_7… | |
- else if (y < ny-1) // at +x boundary | |
- dev_f_new[grid2index( 0, y+1, z, 7,nx,ny,nz)] = fmax(0.0, f_7… | |
- else // at +x+y boundary | |
- dev_f_new[grid2index( 0, 0, z, 7,nx,ny,nz)] = fmax(0.0, f_7… | |
- | |
- // Edge 8 (-x,-y): Periodic | |
- if (x > 0 && y > 0) // not at boundary | |
- dev_f_new[grid2index( x-1, y-1, z, 8,nx,ny,nz)] = fmax(0.0, f_8… | |
- else if (x > 0) // at -y boundary | |
- dev_f_new[grid2index( x-1,ny-1, z, 8,nx,ny,nz)] = fmax(0.0, f_8… | |
- else if (y > 0) // at -x boundary | |
- dev_f_new[grid2index(nx-1, y-1, z, 8,nx,ny,nz)] = fmax(0.0, f_8… | |
- else // at -x-y boundary | |
- dev_f_new[grid2index(nx-1,ny-1, z, 8,nx,ny,nz)] = fmax(0.0, f_8… | |
- | |
- // Edge 9 (-x,+y): Periodic | |
- if (x > 0 && y < ny-1) // not at boundary | |
- dev_f_new[grid2index( x-1, y+1, z, 9,nx,ny,nz)] = fmax(0.0, f_9… | |
- else if (x > 0) // at +y boundary | |
- dev_f_new[grid2index( x-1, 0, z, 9,nx,ny,nz)] = fmax(0.0, f_9… | |
- else if (y < ny-1) // at -x boundary | |
- dev_f_new[grid2index(nx-1, y+1, z, 9,nx,ny,nz)] = fmax(0.0, f_9… | |
- else // at -x+y boundary | |
- dev_f_new[grid2index(nx-1, 0, z, 9,nx,ny,nz)] = fmax(0.0, f_9… | |
- | |
- // Edge 10 (+x,-y): Periodic | |
- if (x < nx-1 && y > 0) // not at boundary | |
- dev_f_new[grid2index( x+1, y-1, z, 10,nx,ny,nz)] = fmax(0.0, f_1… | |
- else if (x < nx-1) // at -y boundary | |
- dev_f_new[grid2index( x+1,ny-1, z, 10,nx,ny,nz)] = fmax(0.0, f_1… | |
- else if (y > 0) // at +x boundary | |
- dev_f_new[grid2index( 0, y-1, z, 10,nx,ny,nz)] = fmax(0.0, f_1… | |
- else // at +x-y boundary | |
- dev_f_new[grid2index( 0,ny-1, z, 10,nx,ny,nz)] = fmax(0.0, f_1… | |
- | |
- // Edge 11 (+x,+z): Periodic & bounce-back (free slip) | |
- if (x < nx-1 && z < nz-1) // not at boundary | |
- dev_f_new[grid2index( x+1, y, z+1, 11,nx,ny,nz)] = fmax(0.0, f_1… | |
- else if (x < nx-1) // at +z boundary | |
- dev_f_new[grid2index( x+1, y, 0, 12,nx,ny,nz)] = fmax(0.0, f_1… | |
- else if (z < nz-1) // at +x boundary | |
- dev_f_new[grid2index( 0, y, z+1, 11,nx,ny,nz)] = fmax(0.0, f_1… | |
- else // at +x+z boundary | |
- dev_f_new[grid2index( 0, y, 0, 12,nx,ny,nz)] = fmax(0.0, f_1… | |
- | |
- // Edge 12 (-x,-z): Periodic & bounce back (free slip) | |
- if (x > 0 && z > 0) // not at boundary | |
- dev_f_new[grid2index( x-1, y, z-1, 12,nx,ny,nz)] = fmax(0.0, f_1… | |
- else if (x > 0) // at -z boundary | |
- dev_f_new[grid2index( x-1, y,nz-1, 11,nx,ny,nz)] = fmax(0.0, f_1… | |
- else if (z > 0) // at -x boundary | |
- dev_f_new[grid2index(nx-1, y, z-1, 12,nx,ny,nz)] = fmax(0.0, f_1… | |
- else // at -x-z boundary | |
- dev_f_new[grid2index(nx-1, y,nz-1, 11,nx,ny,nz)] = fmax(0.0, f_1… | |
- | |
- // Edge 13 (+y,+z): Periodic & bounce-back (free slip) | |
- if (y < ny-1 && z < nz-1) // not at boundary | |
- dev_f_new[grid2index( x, y+1, z+1, 13,nx,ny,nz)] = fmax(0.0, f_1… | |
- else if (y < ny-1) // at +z boundary | |
- dev_f_new[grid2index( x, y+1, 0, 14,nx,ny,nz)] = fmax(0.0, f_1… | |
- else if (z < nz-1) // at +y boundary | |
- dev_f_new[grid2index( x, 0, z+1, 13,nx,ny,nz)] = fmax(0.0, f_1… | |
- else // at +y+z boundary | |
- dev_f_new[grid2index( x, 0, 0, 14,nx,ny,nz)] = fmax(0.0, f_1… | |
- | |
- // Edge 14 (-y,-z): Periodic & bounce-back (free slip) | |
- if (y > 0 && z > 0) // not at boundary | |
- dev_f_new[grid2index( x, y-1, z-1, 14,nx,ny,nz)] = fmax(0.0, f_1… | |
- else if (y > 0) // at -z boundary | |
- dev_f_new[grid2index( x, y-1,nz-1, 13,nx,ny,nz)] = fmax(0.0, f_1… | |
- else if (z > 0) // at -y boundary | |
- dev_f_new[grid2index( x,ny-1, z-1, 14,nx,ny,nz)] = fmax(0.0, f_1… | |
- else // at -y-z boundary | |
- dev_f_new[grid2index( x,ny-1,nz-1, 13,nx,ny,nz)] = fmax(0.0, f_1… | |
- | |
- // Edge 15 (-x,+z): Periodic & bounce-back (free slip) | |
- if (x > 0 && z < nz-1) // not at boundary | |
- dev_f_new[grid2index( x-1, y, z+1, 15,nx,ny,nz)] = fmax(0.0, f_1… | |
- else if (x > 0) // at +z boundary | |
- dev_f_new[grid2index( x-1, y, 0, 16,nx,ny,nz)] = fmax(0.0, f_1… | |
- else if (z < nz-1) // at -x boundary | |
- dev_f_new[grid2index(nx-1, y, z+1, 15,nx,ny,nz)] = fmax(0.0, f_1… | |
- else // at -x+z boundary | |
- dev_f_new[grid2index(nx-1, y, 0, 16,nx,ny,nz)] = fmax(0.0, f_1… | |
- | |
- // Edge 16 (+x,-z): Periodic & bounce-back (free slip) | |
- if (x < nx-1 && z > 0) // not at boundary | |
- dev_f_new[grid2index( x+1, y, z-1, 16,nx,ny,nz)] = fmax(0.0, f_1… | |
- else if (x < nx-1) // at -z boundary | |
- dev_f_new[grid2index( x+1, y,nz-1, 15,nx,ny,nz)] = fmax(0.0, f_1… | |
- else if (z > 0) // at +x boundary | |
- dev_f_new[grid2index( 0, y, z-1, 16,nx,ny,nz)] = fmax(0.0, f_1… | |
- else // at +x-z boundary | |
- dev_f_new[grid2index( 0, y,nz-1, 15,nx,ny,nz)] = fmax(0.0, f_1… | |
- | |
- // Edge 17 (-y,+z): Periodic & bounce-back (free slip) | |
- if (y > 0 && z < nz-1) // not at boundary | |
- dev_f_new[grid2index( x, y-1, z+1, 17,nx,ny,nz)] = fmax(0.0, f_1… | |
- else if (y > 0) // at +z boundary | |
- dev_f_new[grid2index( x, y-1, 0, 18,nx,ny,nz)] = fmax(0.0, f_1… | |
- else if (z < nz-1) // at -y boundary | |
- dev_f_new[grid2index( x,ny-1, z+1, 17,nx,ny,nz)] = fmax(0.0, f_1… | |
- else // at -y+z boundary | |
- dev_f_new[grid2index( x,ny-1, 0, 18,nx,ny,nz)] = fmax(0.0, f_1… | |
- | |
- // Edge 18 (+y,-z): Periodic & bounce-back (free slip) | |
- if (y < ny-1 && z > 0) // not at boundary | |
- dev_f_new[grid2index( x, y+1, z-1, 18,nx,ny,nz)] = fmax(0.0, f_1… | |
- else if (y < ny-1) // at -z boundary | |
- dev_f_new[grid2index( x, y+1, 0, 17,nx,ny,nz)] = fmax(0.0, f_1… | |
- else if (z > 0) // at +y boundary | |
- dev_f_new[grid2index( x, 0, z-1, 18,nx,ny,nz)] = fmax(0.0, f_1… | |
- else // at +y-z boundary | |
- dev_f_new[grid2index( x, 0, 0, 17,nx,ny,nz)] = fmax(0.0, f_1… | |
- // */ | |
- | |
- | |
- } | |
-#ifndef LBM_GPU | |
- }}} | |
-#endif | |
-} | |
- | |
-#endif | |
-// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 | |
diff --git a/src/main.cpp b/src/main.cpp | |
t@@ -1,16 +1,17 @@ | |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
-/* SPHERE source code by Anders Damsgaard Christensen, 2010-12, */ | |
+/* SPHERE source code by Anders Damsgaard Christensen, 2010-15, */ | |
/* a 3D Discrete Element Method algorithm with CUDA GPU acceleration. */ | |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
-// Licence: GNU Public License (GPL) v. 3. See license.txt. | |
-// See doc/sphere-doc.pdf for full documentation. | |
-// Compile with GNU make by typing 'make' in the src/ directory. | |
-// SPHERE is called from the command line with './sphere_<architecture> projec… | |
+// Licence: GNU Public License (GPL) v. 3. See LICENSE.txt. | |
+// See doc/pdf/sphere.pdf for full documentation. | |
+// Compile with GNU make by typing 'cmake . && make' in the root directory. | |
+// SPHERE is called from the command line with './sphere projectname' | |
// Including library files | |
#include <iostream> | |
+#include <cstdio> | |
#include <string> | |
#include <cstdlib> | |
t@@ -33,8 +34,8 @@ int main(const int argc, const char *argv[]) | |
int render = 0; // whether to render an image | |
int method = 0; // visualization method | |
int nfiles = 0; // number of input files | |
- float max_val = 0.0f; // max value of colorbar | |
- float lower_cutoff = 0.0f; // lower cutoff, particles below will not be r… | |
+ float max_val = 0.0f; // max value of colorbar | |
+ float lower_cutoff = 0.0f;// lower cutoff, particles below won't be render… | |
// Process input parameters | |
int i; | |
t@@ -45,38 +46,42 @@ int main(const int argc, const char *argv[]) | |
// Display help if requested | |
if (argvi == "-h" || argvi == "--help") { | |
std::cout << argv[0] << ": particle dynamics simulator\n" | |
- << "Usage: " << argv[0] << " [OPTION[S]]... [FILE1 ...]\nOptio… | |
- << "-h, --help\t\tprint help\n" | |
- << "-V, --version\t\tprint version information and exit\n" | |
- << "-q, --quiet\t\tsuppress status messages to stdout\n" | |
- << "-n, --dry\t\tshow key experiment parameters and quit\n" | |
- << "-r, --render\t\trender input files instead of simulating t… | |
- << "-dc, --dont-check\tdon't check values before running\n" | |
- << "\nRaytracer (-r) specific options:\n" | |
- << "-m <method> <maxval> [-l <lower cutoff val>], or\n" | |
- << "--method <method> <maxval> [-l <lower cutoff val>]\n" | |
- << "\tcolor visualization method, possible values:\n" | |
- << "\tnormal, pres, vel, angvel, xdisp, angpos\n" | |
- << "\t'normal' is the default mode\n" | |
- << "\tif -l is appended, don't render particles with value bel… | |
+ << "Usage: " << argv[0] << " [OPTION[S]]... [FILE1 ...]\n" | |
+ "Options:\n" | |
+ "-h, --help\t\tprint help\n" | |
+ "-V, --version\t\tprint version information and exit\n" | |
+ "-q, --quiet\t\tsuppress status messages to stdout\n" | |
+ "-n, --dry\t\tshow key experiment parameters and quit\n" | |
+ "-r, --render\t\trender input files instead of simulating " | |
+ "temporal evolution\n" | |
+ "-dc, --dont-check\tdon't check values before running\n" | |
+ "\nRaytracer (-r) specific options:\n" | |
+ "-m <method> <maxval> [-l <lower cutoff val>], or\n" | |
+ "--method <method> <maxval> [-l <lower cutoff val>]\n" | |
+ "\tcolor visualization method, possible values:\n" | |
+ "\tnormal, pres, vel, angvel, xdisp, angpos\n" | |
+ "\t'normal' is the default mode\n" | |
+ "\tif -l is appended, don't render particles with value below\… | |
<< std::endl; | |
return 0; // Exit with success | |
} | |
// Display version with fancy ASCII art | |
else if (argvi == "-V" || argvi == "--version") { | |
- std::cout << ".-------------------------------------.\n" | |
- << "| _ Compiled for " << ND << "D |\n" | |
- << "| | | |\n" | |
- << "| ___ _ __ | |__ ___ _ __ ___ |\n" | |
- << "| / __| '_ \\| '_ \\ / _ \\ '__/ _ \\ |\n" | |
- << "| \\__ \\ |_) | | | | __/ | | __/ |\n" | |
- << "| |___/ .__/|_| |_|\\___|_| \\___| |\n" | |
- << "| | | |\n" | |
- << "| |_| Version: " << VERS << " |\n" … | |
- << "`-------------------------------------´\n" | |
- << " A discrete element method particle dynamics simulator.\n" | |
- << " Written by Anders Damsgaard Christensen, license GPLv3+.\… | |
+ printf( | |
+ ".-------------------------------------.\n" | |
+ "| _ |\n" | |
+ "| | | |\n" | |
+ "| ___ _ __ | |__ ___ _ __ ___ |\n" | |
+ "| / __| '_ \\| '_ \\ / _ \\ '__/ _ \\ |\n" | |
+ "| \\__ \\ |_) | | | | __/ | | __/ |\n" | |
+ "| |___/ .__/|_| |_|\\___|_| \\___| |\n" | |
+ "| | | |\n" | |
+ "| |_| Version: %.2f |\n" | |
+ "`-------------------------------------´\n" | |
+ " A discrete element method particle dynamics simulator.\n" | |
+ " Written by Anders Damsgaard, license GPLv3+.\n" | |
+ " https://cs.au.dk/~adc/\n", VERSION); | |
return 0; | |
} | |
t@@ -139,11 +144,13 @@ int main(const int argc, const char *argv[]) | |
nfiles++; | |
if (verbose == 1) | |
- std::cout << argv[0] << ": processing input file: " << argvi <… | |
+ std::cout << argv[0] << ": processing input file: " << argvi << | |
+ std::endl; | |
if (nfiles == 1) { | |
- // Create DEM class, read data from input binary, check values… | |
+ // Create DEM class, read data from input binary, check values, | |
+ // init cuda, transfer const mem | |
DEM dem(argvi, verbose, checkVals, dry, 1, 1); | |
// Render image if requested | |
if (render == 1) | |
diff --git a/src/navierstokes.cpp b/src/navierstokes.cpp | |
t@@ -0,0 +1,349 @@ | |
+#include <iostream> | |
+#include <cstdio> | |
+#include <cstdlib> | |
+#include <string> | |
+#include <vector> | |
+ | |
+#include "typedefs.h" | |
+#include "datatypes.h" | |
+#include "constants.h" | |
+#include "sphere.h" | |
+#include "utility.h" | |
+ | |
+// 1: Enable color output in array printing functions, 0: Disable | |
+const int color_output = 0; | |
+ | |
+// Initialize memory | |
+void DEM::initNSmem() | |
+{ | |
+ // Number of cells | |
+ ns.nx = floor(grid.num[0]); | |
+ ns.ny = floor(grid.num[1]); | |
+ ns.nz = floor(grid.num[2]); | |
+ unsigned int ncells = NScells(); | |
+ unsigned int ncells_st = NScellsVelocity(); | |
+ | |
+ ns.p = new Float[ncells]; // hydraulic pressure | |
+ ns.v = new Float3[ncells]; // hydraulic velocity | |
+ ns.v_x = new Float[ncells_st]; // hydraulic velocity in staggered grid | |
+ ns.v_y = new Float[ncells_st]; // hydraulic velocity in staggered grid | |
+ ns.v_z = new Float[ncells_st]; // hydraulic velocity in staggered grid | |
+ ns.v_p = new Float3[ncells]; // predicted hydraulic velocity | |
+ ns.v_p_x = new Float[ncells_st]; // pred. hydraulic velocity in st. grid | |
+ ns.v_p_y = new Float[ncells_st]; // pred. hydraulic velocity in st. grid | |
+ ns.v_p_z = new Float[ncells_st]; // pred. hydraulic velocity in st. grid | |
+ ns.phi = new Float[ncells]; // porosity | |
+ ns.dphi = new Float[ncells]; // porosity change | |
+ ns.norm = new Float[ncells]; // normalized residual of epsilon | |
+ ns.epsilon = new Float[ncells]; // normalized residual of epsilon | |
+ ns.epsilon_new = new Float[ncells]; // normalized residual of epsilon | |
+} | |
+ | |
+unsigned int DEM::NScells() | |
+{ | |
+ //return ns.nx*ns.ny*ns.nz; // without ghost nodes | |
+ return (ns.nx+2)*(ns.ny+2)*(ns.nz+2); // with ghost nodes | |
+} | |
+ | |
+// Returns the number of velocity nodes in a congruent padded grid. There are | |
+// velocity nodes between the boundary points and the pressure ghost nodes, but | |
+// not on the outer side of the ghost nodes | |
+unsigned int DEM::NScellsVelocity() | |
+{ | |
+ // Congruent padding for velocity grids. See "Cohen and Molemaker 'A fast | |
+ // double precision CFD code using CUDA'" for details | |
+ //return (ns.nx+3)*(ns.ny+3)*(ns.nz+3); | |
+ return (ns.nx+1)*(ns.ny+1)*(ns.nz+1); | |
+} | |
+ | |
+// Free memory | |
+void DEM::freeNSmem() | |
+{ | |
+ delete[] ns.p; | |
+ delete[] ns.v; | |
+ delete[] ns.v_x; | |
+ delete[] ns.v_y; | |
+ delete[] ns.v_z; | |
+ delete[] ns.v_p; | |
+ delete[] ns.v_p_x; | |
+ delete[] ns.v_p_y; | |
+ delete[] ns.v_p_z; | |
+ delete[] ns.phi; | |
+ delete[] ns.dphi; | |
+ delete[] ns.norm; | |
+ delete[] ns.epsilon; | |
+ delete[] ns.epsilon_new; | |
+} | |
+ | |
+// 3D index to 1D index | |
+unsigned int DEM::idx( | |
+ const int x, | |
+ const int y, | |
+ const int z) | |
+{ | |
+ // without ghost nodes | |
+ //return x + d.nx*y + d.nx*d.ny*z; | |
+ | |
+ // with ghost nodes | |
+ // the ghost nodes are placed at x,y,z = -1 and WIDTH | |
+ return (x+1) + (ns.nx+2)*(y+1) + (ns.nx+2)*(ns.ny+2)*(z+1); | |
+} | |
+ | |
+// 3D index to 1D index of cell-face velocity nodes. The cell-face velocities | |
+// are placed at x = [0;nx], y = [0;ny], z = [0;nz]. | |
+// The coordinate x,y,z corresponds to the lowest corner of cell(x,y,z). | |
+unsigned int DEM::vidx( | |
+ const int x, | |
+ const int y, | |
+ const int z) | |
+{ | |
+ return x + (ns.nx+1)*y + (ns.nx+1)*(ns.ny+1)*z; | |
+} | |
+ | |
+// Determine if the FTCS (forward time, central space) solution of the Navier | |
+// Stokes equations is unstable | |
+void DEM::checkNSstability() | |
+{ | |
+ // Cell dimensions | |
+ const Float dx = grid.L[0]/grid.num[0]; | |
+ const Float dy = grid.L[1]/grid.num[1]; | |
+ const Float dz = grid.L[2]/grid.num[2]; | |
+ | |
+ // The smallest grid spacing | |
+ const Float dmin = fmin(dx, fmin(dy, dz)); | |
+ | |
+ // Check the diffusion term using von Neumann stability analysis | |
+ if (params.mu*time.dt/(dmin*dmin) > 0.5) { | |
+ std::cerr << "Error: The time step is too large to ensure stability in… | |
+ "the diffusive term of the fluid momentum equation.\n" | |
+ "Decrease the viscosity, decrease the time step, and/or increase " | |
+ "the fluid grid cell size." << std::endl; | |
+ exit(1); | |
+ } | |
+ | |
+ int x,y,z; | |
+ Float3 v; | |
+ for (x=0; x<ns.nx; ++x) { | |
+ for (y=0; y<ns.ny; ++y) { | |
+ for (z=0; z<ns.nz; ++z) { | |
+ | |
+ v = ns.v[idx(x,y,z)]; | |
+ | |
+ // Check the advection term using the Courant-Friedrichs-Lewy | |
+ // condition | |
+ if (v.x*time.dt/dx + v.y*time.dt/dy + v.z*time.dt/dz > 1.0) { | |
+ std::cerr << "Error: The time step is too large to ensure " | |
+ "stability in the advective term of the fluid momentum… | |
+ "equation.\n" | |
+ "This is caused by too high fluid velocities. " | |
+ "You can try to decrease the time step, and/or " | |
+ "increase the fluid grid cell size.\n" | |
+ "v(" << x << ',' << y << ',' << z << ") = [" | |
+ << v.x << ',' << v.y << ',' << v.z << "] m/s" | |
+ << std::endl; | |
+ exit(1); | |
+ } | |
+ } | |
+ } | |
+ } | |
+ | |
+ | |
+ | |
+} | |
+ | |
+// Print array values to file stream (stdout, stderr, other file) | |
+void DEM::printNSarray(FILE* stream, Float* arr) | |
+{ | |
+ int x, y, z; | |
+ | |
+ // show ghost nodes | |
+ //for (z=-1; z<=ns.nz; z++) { // bottom to top | |
+ //for (z = ns.nz-1; z >= -1; z--) { // top to bottom | |
+ for (z = ns.nz; z >= -1; z--) { // top to bottom | |
+ | |
+ fprintf(stream, "z = %d\n", z); | |
+ | |
+ for (y=-1; y<=ns.ny; y++) { | |
+ for (x=-1; x<=ns.nx; x++) { | |
+ | |
+ // hide ghost nodes | |
+ /*for (z=0; z<ns.nz; z++) { | |
+ for (y=0; y<ns.ny; y++) { | |
+ for (x=0; x<ns.nx; x++) {*/ | |
+ | |
+ if (x > -1 && x < ns.nx && | |
+ y > -1 && y < ns.ny && | |
+ z > -1 && z < ns.nz) { | |
+ fprintf(stream, "%f\t", arr[idx(x,y,z)]); | |
+ } else { // ghost node | |
+ if (color_output) { | |
+ fprintf(stream, "\x1b[30;1m%f\x1b[0m\t", | |
+ arr[idx(x,y,z)]); | |
+ } else { | |
+ fprintf(stream, "%f\t", arr[idx(x,y,z)]); | |
+ } | |
+ } | |
+ } | |
+ fprintf(stream, "\n"); | |
+ } | |
+ fprintf(stream, "\n"); | |
+ } | |
+} | |
+ | |
+// Overload printNSarray to add optional description | |
+void DEM::printNSarray(FILE* stream, Float* arr, std::string desc) | |
+{ | |
+ std::cout << "\n" << desc << ":\n"; | |
+ printNSarray(stream, arr); | |
+} | |
+ | |
+// Print array values to file stream (stdout, stderr, other file) | |
+void DEM::printNSarray(FILE* stream, Float3* arr) | |
+{ | |
+ int x, y, z; | |
+ for (z=0; z<ns.nz; z++) { | |
+ for (y=0; y<ns.ny; y++) { | |
+ for (x=0; x<ns.nx; x++) { | |
+ fprintf(stream, "%f,%f,%f\t", | |
+ arr[idx(x,y,z)].x, | |
+ arr[idx(x,y,z)].y, | |
+ arr[idx(x,y,z)].z); | |
+ } | |
+ fprintf(stream, "\n"); | |
+ } | |
+ fprintf(stream, "\n"); | |
+ } | |
+} | |
+ | |
+// Overload printNSarray to add optional description | |
+void DEM::printNSarray(FILE* stream, Float3* arr, std::string desc) | |
+{ | |
+ std::cout << "\n" << desc << ":\n"; | |
+ printNSarray(stream, arr); | |
+} | |
+ | |
+// Returns the mean particle radius | |
+Float DEM::meanRadius() | |
+{ | |
+ unsigned int i; | |
+ Float r_sum; | |
+ for (i=0; i<np; ++i) | |
+ r_sum += k.x[i].w; | |
+ return r_sum/((Float)np); | |
+} | |
+ | |
+// Returns the average value of the normalized residuals | |
+double DEM::avgNormResNS() | |
+{ | |
+ double norm_res_sum, norm_res; | |
+ | |
+ // do not consider the values of the ghost nodes | |
+ for (int z=0; z<grid.num[2]; ++z) { | |
+ for (int y=0; y<grid.num[1]; ++y) { | |
+ for (int x=0; x<grid.num[0]; ++x) { | |
+ norm_res = static_cast<double>(ns.norm[idx(x,y,z)]); | |
+ if (norm_res != norm_res) { | |
+ std::cerr << "\nError: normalized residual is NaN (" | |
+ << norm_res << ") in cell " | |
+ << x << "," << y << "," << z << std::endl; | |
+ std::cerr << "\tt = " << time.current << ", iter = " | |
+ << int(time.current/time.dt) << std::endl; | |
+ std::cerr << "This often happens if the system has become " | |
+ "unstable." << std::endl; | |
+ exit(1); | |
+ } | |
+ norm_res_sum += norm_res; | |
+ } | |
+ } | |
+ } | |
+ return norm_res_sum/(grid.num[0]*grid.num[1]*grid.num[2]); | |
+} | |
+ | |
+ | |
+// Returns the average value of the normalized residuals | |
+double DEM::maxNormResNS() | |
+{ | |
+ double max_norm_res = -1.0e9; // initialized to a small number | |
+ double norm_res; | |
+ | |
+ // do not consider the values of the ghost nodes | |
+ for (int z=0; z<grid.num[2]; ++z) { | |
+ for (int y=0; y<grid.num[1]; ++y) { | |
+ for (int x=0; x<grid.num[0]; ++x) { | |
+ norm_res = static_cast<double>(ns.norm[idx(x,y,z)]); | |
+ if (norm_res != norm_res) { | |
+ std::cerr << "\nError: normalized residual is NaN (" | |
+ << norm_res << ") in cell " | |
+ << x << "," << y << "," << z << std::endl; | |
+ std::cerr << "\tt = " << time.current << ", iter = " | |
+ << int(time.current/time.dt) << std::endl; | |
+ exit(1); | |
+ } | |
+ if (norm_res > max_norm_res) | |
+ max_norm_res = norm_res; | |
+ } | |
+ } | |
+ } | |
+ return max_norm_res; | |
+} | |
+ | |
+// Initialize fluid parameters | |
+void DEM::initNS() | |
+{ | |
+ // Cell size | |
+ ns.dx = grid.L[0]/ns.nx; | |
+ ns.dy = grid.L[1]/ns.ny; | |
+ ns.dz = grid.L[2]/ns.nz; | |
+ | |
+ if (verbose == 1) { | |
+ std::cout << " - Fluid grid dimensions: " | |
+ << ns.nx << "*" | |
+ << ns.ny << "*" | |
+ << ns.nz << std::endl; | |
+ std::cout << " - Fluid grid cell size: " | |
+ << ns.dx << "*" | |
+ << ns.dy << "*" | |
+ << ns.dz << std::endl; | |
+ } | |
+} | |
+ | |
+// Write values in scalar field to file | |
+void DEM::writeNSarray(Float* array, const char* filename) | |
+{ | |
+ FILE* file; | |
+ if ((file = fopen(filename,"w"))) { | |
+ printNSarray(file, array); | |
+ fclose(file); | |
+ } else { | |
+ fprintf(stderr, "Error, could not open %s.\n", filename); | |
+ } | |
+} | |
+ | |
+// Write values in vector field to file | |
+void DEM::writeNSarray(Float3* array, const char* filename) | |
+{ | |
+ FILE* file; | |
+ if ((file = fopen(filename,"w"))) { | |
+ printNSarray(file, array); | |
+ fclose(file); | |
+ } else { | |
+ fprintf(stderr, "Error, could not open %s.\n", filename); | |
+ } | |
+} | |
+ | |
+ | |
+// Print final heads and free memory | |
+void DEM::endNS() | |
+{ | |
+ // Write arrays to stdout/text files for debugging | |
+ //writeNSarray(ns.phi, "ns_phi.txt"); | |
+ | |
+ //printNSarray(stdout, ns.K, "ns.K"); | |
+ //printNSarray(stdout, ns.H, "ns.H"); | |
+ //printNSarray(stdout, ns.H_new, "ns.H_new"); | |
+ //printNSarray(stdout, ns.V, "ns.V"); | |
+ | |
+ freeNSmem(); | |
+} | |
+ | |
+// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 | |
diff --git a/src/navierstokes.cuh b/src/navierstokes.cuh | |
t@@ -0,0 +1,2317 @@ | |
+// navierstokes.cuh | |
+// CUDA implementation of porous flow | |
+ | |
+#include <iostream> | |
+#include <cuda.h> | |
+//#include <cutil_math.h> | |
+#include <helper_math.h> | |
+ | |
+#include "vector_arithmetic.h" // for arbitrary prec. vectors | |
+#include "sphere.h" | |
+#include "datatypes.h" | |
+#include "utility.h" | |
+#include "constants.cuh" | |
+#include "debug.h" | |
+ | |
+// Arithmetic mean of two numbers | |
+__inline__ __device__ Float amean(Float a, Float b) { | |
+ return (a+b)*0.5; | |
+} | |
+ | |
+// Harmonic mean of two numbers | |
+__inline__ __device__ Float hmean(Float a, Float b) { | |
+ return (2.0*a*b)/(a+b); | |
+} | |
+ | |
+// Initialize memory | |
+void DEM::initNSmemDev(void) | |
+{ | |
+ // size of scalar field | |
+ unsigned int memSizeF = sizeof(Float)*NScells(); | |
+ | |
+ // size of velocity arrays in staggered grid discretization | |
+ unsigned int memSizeFvel = sizeof(Float)*NScellsVelocity(); | |
+ | |
+ cudaMalloc((void**)&dev_ns_p, memSizeF); // hydraulic pressure | |
+ cudaMalloc((void**)&dev_ns_v, memSizeF*3); // cell hydraulic velocity | |
+ cudaMalloc((void**)&dev_ns_v_x, memSizeFvel);// velocity in stag. grid | |
+ cudaMalloc((void**)&dev_ns_v_y, memSizeFvel);// velocity in stag. grid | |
+ cudaMalloc((void**)&dev_ns_v_z, memSizeFvel);// velocity in stag. grid | |
+ cudaMalloc((void**)&dev_ns_v_p, memSizeF*3); // predicted cell velocity | |
+ cudaMalloc((void**)&dev_ns_v_p_x, memSizeFvel); // pred. vel. in stag. grid | |
+ cudaMalloc((void**)&dev_ns_v_p_y, memSizeFvel); // pred. vel. in stag. grid | |
+ cudaMalloc((void**)&dev_ns_v_p_z, memSizeFvel); // pred. vel. in stag. grid | |
+ cudaMalloc((void**)&dev_ns_vp_avg, memSizeF*3); // avg. particle velocity | |
+ cudaMalloc((void**)&dev_ns_d_avg, memSizeF); // avg. particle diameter | |
+ cudaMalloc((void**)&dev_ns_fi, memSizeF*3); // interaction force | |
+ cudaMalloc((void**)&dev_ns_phi, memSizeF); // cell porosity | |
+ cudaMalloc((void**)&dev_ns_dphi, memSizeF); // cell porosity change | |
+ cudaMalloc((void**)&dev_ns_div_phi_v_v, memSizeF*3); // div(phi v v) | |
+ cudaMalloc((void**)&dev_ns_epsilon, memSizeF); // pressure difference | |
+ cudaMalloc((void**)&dev_ns_epsilon_new, memSizeF); // new pressure diff. | |
+ cudaMalloc((void**)&dev_ns_epsilon_old, memSizeF); // old pressure diff. | |
+ cudaMalloc((void**)&dev_ns_norm, memSizeF); // normalized residual | |
+ cudaMalloc((void**)&dev_ns_f, memSizeF); // forcing function value | |
+ cudaMalloc((void**)&dev_ns_f1, memSizeF); // constant addition in forci… | |
+ cudaMalloc((void**)&dev_ns_f2, memSizeF*3); // constant slope in forcing | |
+ cudaMalloc((void**)&dev_ns_tau, memSizeF*6); // stress tensor (symmetrical) | |
+ cudaMalloc((void**)&dev_ns_div_phi_vi_v, memSizeF*3); // div(phi*vi*v) | |
+ cudaMalloc((void**)&dev_ns_div_phi_tau, memSizeF*3); // div(phi*tau) | |
+ | |
+ checkForCudaErrors("End of initNSmemDev"); | |
+} | |
+ | |
+// Free memory | |
+void DEM::freeNSmemDev() | |
+{ | |
+ cudaFree(dev_ns_p); | |
+ cudaFree(dev_ns_v); | |
+ cudaFree(dev_ns_v_x); | |
+ cudaFree(dev_ns_v_y); | |
+ cudaFree(dev_ns_v_z); | |
+ cudaFree(dev_ns_v_p); | |
+ cudaFree(dev_ns_v_p_x); | |
+ cudaFree(dev_ns_v_p_y); | |
+ cudaFree(dev_ns_v_p_z); | |
+ cudaFree(dev_ns_vp_avg); | |
+ cudaFree(dev_ns_d_avg); | |
+ cudaFree(dev_ns_fi); | |
+ cudaFree(dev_ns_phi); | |
+ cudaFree(dev_ns_dphi); | |
+ cudaFree(dev_ns_div_phi_v_v); | |
+ cudaFree(dev_ns_epsilon); | |
+ cudaFree(dev_ns_epsilon_new); | |
+ cudaFree(dev_ns_epsilon_old); | |
+ cudaFree(dev_ns_norm); | |
+ cudaFree(dev_ns_f); | |
+ cudaFree(dev_ns_f1); | |
+ cudaFree(dev_ns_f2); | |
+ cudaFree(dev_ns_tau); | |
+ cudaFree(dev_ns_div_phi_vi_v); | |
+ cudaFree(dev_ns_div_phi_tau); | |
+} | |
+ | |
+// Transfer to device | |
+void DEM::transferNStoGlobalDeviceMemory(int statusmsg) | |
+{ | |
+ checkForCudaErrors("Before attempting cudaMemcpy in " | |
+ "transferNStoGlobalDeviceMemory"); | |
+ | |
+ //if (verbose == 1 && statusmsg == 1) | |
+ //std::cout << " Transfering fluid data to the device: "; | |
+ | |
+ // memory size for a scalar field | |
+ unsigned int memSizeF = sizeof(Float)*NScells(); | |
+ | |
+ //writeNSarray(ns.p, "ns.p.txt"); | |
+ | |
+ cudaMemcpy(dev_ns_p, ns.p, memSizeF, cudaMemcpyHostToDevice); | |
+ checkForCudaErrors("transferNStoGlobalDeviceMemory after first cudaMemcpy"… | |
+ cudaMemcpy(dev_ns_v, ns.v, memSizeF*3, cudaMemcpyHostToDevice); | |
+ cudaMemcpy(dev_ns_v_p, ns.v_p, memSizeF*3, cudaMemcpyHostToDevice); | |
+ cudaMemcpy(dev_ns_phi, ns.phi, memSizeF, cudaMemcpyHostToDevice); | |
+ cudaMemcpy(dev_ns_dphi, ns.dphi, memSizeF, cudaMemcpyHostToDevice); | |
+ | |
+ checkForCudaErrors("End of transferNStoGlobalDeviceMemory"); | |
+ //if (verbose == 1 && statusmsg == 1) | |
+ //std::cout << "Done" << std::endl; | |
+} | |
+ | |
+// Transfer from device | |
+void DEM::transferNSfromGlobalDeviceMemory(int statusmsg) | |
+{ | |
+ if (verbose == 1 && statusmsg == 1) | |
+ std::cout << " Transfering fluid data from the device: "; | |
+ | |
+ // memory size for a scalar field | |
+ unsigned int memSizeF = sizeof(Float)*NScells(); | |
+ | |
+ cudaMemcpy(ns.p, dev_ns_p, memSizeF, cudaMemcpyDeviceToHost); | |
+ checkForCudaErrors("In transferNSfromGlobalDeviceMemory, dev_ns_p", 0); | |
+ cudaMemcpy(ns.v, dev_ns_v, memSizeF*3, cudaMemcpyDeviceToHost); | |
+ cudaMemcpy(ns.v_p, dev_ns_v_p, memSizeF*3, cudaMemcpyDeviceToHost); | |
+ cudaMemcpy(ns.phi, dev_ns_phi, memSizeF, cudaMemcpyDeviceToHost); | |
+ cudaMemcpy(ns.dphi, dev_ns_dphi, memSizeF, cudaMemcpyDeviceToHost); | |
+ cudaMemcpy(ns.norm, dev_ns_norm, memSizeF, cudaMemcpyDeviceToHost); | |
+ | |
+ checkForCudaErrors("End of transferNSfromGlobalDeviceMemory", 0); | |
+ if (verbose == 1 && statusmsg == 1) | |
+ std::cout << "Done" << std::endl; | |
+} | |
+ | |
+// Transfer the normalized residuals from device to host | |
+void DEM::transferNSnormFromGlobalDeviceMemory() | |
+{ | |
+ cudaMemcpy(ns.norm, dev_ns_norm, sizeof(Float)*NScells(), | |
+ cudaMemcpyDeviceToHost); | |
+ checkForCudaErrors("End of transferNSnormFromGlobalDeviceMemory"); | |
+} | |
+ | |
+// Transfer the pressure change from device to host | |
+void DEM::transferNSepsilonFromGlobalDeviceMemory() | |
+{ | |
+ cudaMemcpy(ns.epsilon, dev_ns_epsilon, sizeof(Float)*NScells(), | |
+ cudaMemcpyDeviceToHost); | |
+ checkForCudaErrors("End of transferNSepsilonFromGlobalDeviceMemory"); | |
+} | |
+ | |
+// Transfer the pressure change from device to host | |
+void DEM::transferNSepsilonNewFromGlobalDeviceMemory() | |
+{ | |
+ cudaMemcpy(ns.epsilon_new, dev_ns_epsilon_new, sizeof(Float)*NScells(), | |
+ cudaMemcpyDeviceToHost); | |
+ checkForCudaErrors("End of transferNSepsilonFromGlobalDeviceMemory"); | |
+} | |
+ | |
+// Get linear index from 3D grid position | |
+__inline__ __device__ unsigned int idx( | |
+ const int x, const int y, const int z) | |
+{ | |
+ // without ghost nodes | |
+ //return x + dev_grid.num[0]*y + dev_grid.num[0]*dev_grid.num[1]*z; | |
+ | |
+ // with ghost nodes | |
+ // the ghost nodes are placed at x,y,z = -1 and WIDTH | |
+ return (x+1) + (devC_grid.num[0]+2)*(y+1) + | |
+ (devC_grid.num[0]+2)*(devC_grid.num[1]+2)*(z+1); | |
+} | |
+ | |
+// Get linear index of velocity node from 3D grid position in staggered grid | |
+__inline__ __device__ unsigned int vidx( | |
+ const int x, const int y, const int z) | |
+{ | |
+ return x + (devC_grid.num[0]+1)*y | |
+ + (devC_grid.num[0]+1)*(devC_grid.num[1]+1)*z; | |
+} | |
+ | |
+// Find averaged cell velocities from cell-face velocities. This function works | |
+// for both normal and predicted velocities. Launch for every cell in the | |
+// dev_ns_v or dev_ns_v_p array. This function does not set the averaged | |
+// velocity values in the ghost node cells. | |
+__global__ void findNSavgVel( | |
+ Float3* dev_ns_v, // out | |
+ Float* dev_ns_v_x, // in | |
+ Float* dev_ns_v_y, // in | |
+ Float* dev_ns_v_z) // in | |
+{ | |
+ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // check that we are not outside the fluid grid | |
+ if (x<devC_grid.num[0] && y<devC_grid.num[1] && z<devC_grid.num[2]-1) { | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ | |
+ // Read cell-face velocities | |
+ __syncthreads(); | |
+ const Float v_xn = dev_ns_v_x[vidx(x,y,z)]; | |
+ const Float v_xp = dev_ns_v_x[vidx(x+1,y,z)]; | |
+ const Float v_yn = dev_ns_v_y[vidx(x,y,z)]; | |
+ const Float v_yp = dev_ns_v_y[vidx(x,y+1,z)]; | |
+ const Float v_zn = dev_ns_v_z[vidx(x,y,z)]; | |
+ const Float v_zp = dev_ns_v_z[vidx(x,y,z+1)]; | |
+ | |
+ // Find average velocity using arithmetic means | |
+ const Float3 v_bar = MAKE_FLOAT3( | |
+ amean(v_xn, v_xp), | |
+ amean(v_yn, v_yp), | |
+ amean(v_zn, v_zp)); | |
+ | |
+ // Save value | |
+ __syncthreads(); | |
+ dev_ns_v[idx(x,y,z)] = v_bar; | |
+ } | |
+} | |
+ | |
+// Find cell-face velocities from averaged velocities. This function works for | |
+// both normal and predicted velocities. Launch for every cell in the dev_ns_v | |
+// or dev_ns_v_p array. Make sure that the averaged velocity ghost nodes are s… | |
+// beforehand. | |
+__global__ void findNScellFaceVel( | |
+ Float3* dev_ns_v, // in | |
+ Float* dev_ns_v_x, // out | |
+ Float* dev_ns_v_y, // out | |
+ Float* dev_ns_v_z) // out | |
+{ | |
+ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Grid dimensions | |
+ const unsigned int nx = devC_grid.num[0]; | |
+ const unsigned int ny = devC_grid.num[1]; | |
+ const unsigned int nz = devC_grid.num[2]; | |
+ | |
+ // check that we are not outside the fluid grid | |
+ if (x < nx && y < ny && x < nz) { | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ | |
+ // Read the averaged velocity from this cell as well as the required | |
+ // components from the neighbor cells | |
+ __syncthreads(); | |
+ const Float3 v = dev_ns_v[idx(x,y,z)]; | |
+ const Float v_xn = dev_ns_v[idx(x-1,y,z)].x; | |
+ const Float v_xp = dev_ns_v[idx(x+1,y,z)].x; | |
+ const Float v_yn = dev_ns_v[idx(x,y-1,z)].y; | |
+ const Float v_yp = dev_ns_v[idx(x,y+1,z)].y; | |
+ const Float v_zn = dev_ns_v[idx(x,y,z-1)].z; | |
+ const Float v_zp = dev_ns_v[idx(x,y,z+1)].z; | |
+ | |
+ // Find cell-face velocities and save them right away | |
+ __syncthreads(); | |
+ | |
+ // Values at the faces closest to the coordinate system origo | |
+ dev_ns_v_x[vidx(x,y,z)] = amean(v_xn, v.x); | |
+ dev_ns_v_y[vidx(x,y,z)] = amean(v_yn, v.y); | |
+ dev_ns_v_z[vidx(x,y,z)] = amean(v_zn, v.z); | |
+ | |
+ // Values at the cell faces furthest from the coordinate system origo. | |
+ // These values should only be written at the corresponding boundaries | |
+ // in order to avoid write conflicts. | |
+ if (x == nx-1) | |
+ dev_ns_v_x[vidx(x+1,y,z)] = amean(v.x, v_xp); | |
+ if (y == ny-1) | |
+ dev_ns_v_x[vidx(x+1,y,z)] = amean(v.y, v_yp); | |
+ if (z == nz-1) | |
+ dev_ns_v_x[vidx(x+1,y,z)] = amean(v.z, v_zp); | |
+ } | |
+} | |
+ | |
+ | |
+// Set the initial guess of the values of epsilon. | |
+__global__ void setNSepsilonInterior(Float* dev_ns_epsilon, Float value) | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // check that we are not outside the fluid grid | |
+ if (x < devC_grid.num[0] && y < devC_grid.num[1] && | |
+ z > 0 && z < devC_grid.num[2]-1) { | |
+ __syncthreads(); | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ dev_ns_epsilon[cellidx] = value; | |
+ } | |
+} | |
+ | |
+// The normalized residuals are given an initial value of 0, since the values … | |
+// the Dirichlet boundaries aren't written during the iterations. | |
+__global__ void setNSnormZero(Float* dev_ns_norm) | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // check that we are not outside the fluid grid | |
+ if (x < devC_grid.num[0] && y < devC_grid.num[1] && z < devC_grid.num[2]) { | |
+ __syncthreads(); | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ dev_ns_norm[idx(x,y,z)] = 0.0; | |
+ } | |
+} | |
+ | |
+ | |
+// Set the constant values of epsilon at the lower boundary. Since the | |
+// Dirichlet boundary values aren't transfered during array swapping, the valu… | |
+// also need to be written to the new array of epsilons. A value of 0 equals | |
+// the Dirichlet boundary condition: the new value should be identical to the | |
+// old value, i.e. the temporal gradient is 0 | |
+__global__ void setNSepsilonBottom( | |
+ Float* dev_ns_epsilon, | |
+ Float* dev_ns_epsilon_new, | |
+ const Float value) | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // check that we are not outside the fluid grid, and at the z boundaries | |
+ //if (x < devC_grid.num[0] && y < devC_grid.num[1] && | |
+ // (z == devC_grid.num[2]-1 || z == 0)) { | |
+ // check that we are not outside the fluid grid, and at the lower z bounda… | |
+ if (x < devC_grid.num[0] && y < devC_grid.num[1] && z == 0) { | |
+ | |
+ __syncthreads(); | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ dev_ns_epsilon[cellidx] = value; | |
+ dev_ns_epsilon_new[cellidx] = value; | |
+ } | |
+} | |
+ | |
+// Set the constant values of epsilon at the lower boundary. Since the | |
+// Dirichlet boundary values aren't transfered during array swapping, the valu… | |
+// also need to be written to the new array of epsilons. A value of 0 equals | |
+// the Dirichlet boundary condition: the new value should be identical to the | |
+// old value, i.e. the temporal gradient is 0 | |
+__global__ void setNSepsilonTop( | |
+ Float* dev_ns_epsilon, | |
+ Float* dev_ns_epsilon_new, | |
+ const Float value) | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // check that we are not outside the fluid grid, and at the upper z bounda… | |
+ if (x < devC_grid.num[0] && y < devC_grid.num[1] && | |
+ z == devC_grid.num[2]-1) { | |
+ | |
+ __syncthreads(); | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ dev_ns_epsilon[cellidx] = value; | |
+ dev_ns_epsilon_new[cellidx] = value; | |
+ } | |
+} | |
+__device__ void copyNSvalsDev( | |
+ unsigned int read, unsigned int write, | |
+ Float* dev_ns_p, | |
+ Float3* dev_ns_v, Float3* dev_ns_v_p, | |
+ Float* dev_ns_phi, Float* dev_ns_dphi, | |
+ Float* dev_ns_epsilon) | |
+{ | |
+ // Coalesced read | |
+ const Float p = dev_ns_p[read]; | |
+ const Float3 v = dev_ns_v[read]; | |
+ const Float3 v_p = dev_ns_v_p[read]; | |
+ const Float phi = dev_ns_phi[read]; | |
+ const Float dphi = dev_ns_dphi[read]; | |
+ const Float epsilon = dev_ns_epsilon[read]; | |
+ | |
+ // Coalesced write | |
+ __syncthreads(); | |
+ dev_ns_p[write] = p; | |
+ dev_ns_v[write] = v; | |
+ dev_ns_v_p[write] = v_p; | |
+ dev_ns_phi[write] = phi; | |
+ dev_ns_dphi[write] = dphi; | |
+ dev_ns_epsilon[write] = epsilon; | |
+} | |
+ | |
+ | |
+// Update ghost nodes from their parent cell values. The edge (diagonal) cells | |
+// are not written since they are not read. Launch this kernel for all cells in | |
+// the grid | |
+__global__ void setNSghostNodesDev( | |
+ Float* dev_ns_p, | |
+ Float3* dev_ns_v, Float3* dev_ns_v_p, | |
+ Float* dev_ns_phi, Float* dev_ns_dphi, | |
+ Float* dev_ns_epsilon) | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Grid dimensions | |
+ const unsigned int nx = devC_grid.num[0]; | |
+ const unsigned int ny = devC_grid.num[1]; | |
+ const unsigned int nz = devC_grid.num[2]; | |
+ | |
+ // 1D thread index | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ | |
+ // 1D position of ghost node | |
+ unsigned int writeidx; | |
+ | |
+ // check that we are not outside the fluid grid | |
+ if (x < nx && y < ny && z < nz) { | |
+ | |
+ if (x == 0) { | |
+ writeidx = idx(nx,y,z); | |
+ copyNSvalsDev(cellidx, writeidx, | |
+ dev_ns_p, | |
+ dev_ns_v, dev_ns_v_p, | |
+ dev_ns_phi, dev_ns_dphi, | |
+ dev_ns_epsilon); | |
+ } | |
+ if (x == nx-1) { | |
+ writeidx = idx(-1,y,z); | |
+ copyNSvalsDev(cellidx, writeidx, | |
+ dev_ns_p, | |
+ dev_ns_v, dev_ns_v_p, | |
+ dev_ns_phi, dev_ns_dphi, | |
+ dev_ns_epsilon); | |
+ } | |
+ | |
+ if (y == 0) { | |
+ writeidx = idx(x,ny,z); | |
+ copyNSvalsDev(cellidx, writeidx, | |
+ dev_ns_p, | |
+ dev_ns_v, dev_ns_v_p, | |
+ dev_ns_phi, dev_ns_dphi, | |
+ dev_ns_epsilon); | |
+ } | |
+ if (y == ny-1) { | |
+ writeidx = idx(x,-1,z); | |
+ copyNSvalsDev(cellidx, writeidx, | |
+ dev_ns_p, | |
+ dev_ns_v, dev_ns_v_p, | |
+ dev_ns_phi, dev_ns_dphi, | |
+ dev_ns_epsilon); | |
+ } | |
+ | |
+ // Z boundaries fixed | |
+ if (z == 0) { | |
+ writeidx = idx(x,y,-1); | |
+ copyNSvalsDev(cellidx, writeidx, | |
+ dev_ns_p, | |
+ dev_ns_v, dev_ns_v_p, | |
+ dev_ns_phi, dev_ns_dphi, | |
+ dev_ns_epsilon); | |
+ } | |
+ if (z == nz-1) { | |
+ writeidx = idx(x,y,nz); | |
+ copyNSvalsDev(cellidx, writeidx, | |
+ dev_ns_p, | |
+ dev_ns_v, dev_ns_v_p, | |
+ dev_ns_phi, dev_ns_dphi, | |
+ dev_ns_epsilon); | |
+ } | |
+ | |
+ // Z boundaries periodic | |
+ /*if (z == 0) { | |
+ writeidx = idx(x,y,nz); | |
+ copyNSvalsDev(cellidx, writeidx, | |
+ dev_ns_p, | |
+ dev_ns_v, dev_ns_v_p, | |
+ dev_ns_phi, dev_ns_dphi, | |
+ dev_ns_epsilon); | |
+ } | |
+ if (z == nz-1) { | |
+ writeidx = idx(x,y,-1); | |
+ copyNSvalsDev(cellidx, writeidx, | |
+ dev_ns_p, | |
+ dev_ns_v, dev_ns_v_p, | |
+ dev_ns_phi, dev_ns_dphi, | |
+ dev_ns_epsilon); | |
+ }*/ | |
+ } | |
+} | |
+ | |
+// Update a field in the ghost nodes from their parent cell values. The edge | |
+// (diagonal) cells are not written since they are not read. Launch this kernel | |
+// for all cells in the grid usind setNSghostNodes<datatype><<<.. , ..>>>( .. … | |
+template<typename T> | |
+__global__ void setNSghostNodes(T* dev_scalarfield) | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Grid dimensions | |
+ const unsigned int nx = devC_grid.num[0]; | |
+ const unsigned int ny = devC_grid.num[1]; | |
+ const unsigned int nz = devC_grid.num[2]; | |
+ | |
+ // check that we are not outside the fluid grid | |
+ if (x < nx && y < ny && z < nz) { | |
+ | |
+ const T val = dev_scalarfield[idx(x,y,z)]; | |
+ | |
+ if (x == 0) | |
+ dev_scalarfield[idx(nx,y,z)] = val; | |
+ if (x == nx-1) | |
+ dev_scalarfield[idx(-1,y,z)] = val; | |
+ | |
+ if (y == 0) | |
+ dev_scalarfield[idx(x,ny,z)] = val; | |
+ if (y == ny-1) | |
+ dev_scalarfield[idx(x,-1,z)] = val; | |
+ | |
+ if (z == 0) | |
+ dev_scalarfield[idx(x,y,-1)] = val; // Dirichlet | |
+ //dev_scalarfield[idx(x,y,nz)] = val; // Periodic -z | |
+ if (z == nz-1) | |
+ dev_scalarfield[idx(x,y,nz)] = val; // Dirichlet | |
+ //dev_scalarfield[idx(x,y,-1)] = val; // Periodic +z | |
+ } | |
+} | |
+ | |
+// Update a field in the ghost nodes from their parent cell values. The edge | |
+// (diagonal) cells are not written since they are not read. | |
+template<typename T> | |
+__global__ void setNSghostNodes( | |
+ T* dev_scalarfield, | |
+ int bc_bot, | |
+ int bc_top) | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Grid dimensions | |
+ const unsigned int nx = devC_grid.num[0]; | |
+ const unsigned int ny = devC_grid.num[1]; | |
+ const unsigned int nz = devC_grid.num[2]; | |
+ | |
+ // check that we are not outside the fluid grid | |
+ if (x < nx && y < ny && z < nz) { | |
+ | |
+ const T val = dev_scalarfield[idx(x,y,z)]; | |
+ | |
+ // x | |
+ if (x == 0) | |
+ dev_scalarfield[idx(nx,y,z)] = val; | |
+ if (x == nx-1) | |
+ dev_scalarfield[idx(-1,y,z)] = val; | |
+ | |
+ // y | |
+ if (y == 0) | |
+ dev_scalarfield[idx(x,ny,z)] = val; | |
+ if (y == ny-1) | |
+ dev_scalarfield[idx(x,-1,z)] = val; | |
+ | |
+ // z | |
+ if (z == 0 && bc_bot == 0) | |
+ dev_scalarfield[idx(x,y,-1)] = val; // Dirichlet | |
+ if (z == 1 && bc_bot == 1) | |
+ dev_scalarfield[idx(x,y,-1)] = val; // Neumann | |
+ if (z == 0 && bc_bot == 2) | |
+ dev_scalarfield[idx(x,y,nz)] = val; // Periodic -z | |
+ | |
+ if (z == nz-1 && bc_top == 0) | |
+ dev_scalarfield[idx(x,y,nz)] = val; // Dirichlet | |
+ if (z == nz-2 && bc_top == 1) | |
+ dev_scalarfield[idx(x,y,nz)] = val; // Neumann | |
+ if (z == nz-1 && bc_top == 2) | |
+ dev_scalarfield[idx(x,y,-1)] = val; // Periodic +z | |
+ } | |
+} | |
+ | |
+// Update the tensor field for the ghost nodes from their parent cell values. | |
+// The edge (diagonal) cells are not written since they are not read. Launch | |
+// this kernel for all cells in the grid. | |
+__global__ void setNSghostNodes_tau( | |
+ Float* dev_ns_tau, | |
+ int bc_bot, | |
+ int bc_top) | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Grid dimensions | |
+ const unsigned int nx = devC_grid.num[0]; | |
+ const unsigned int ny = devC_grid.num[1]; | |
+ const unsigned int nz = devC_grid.num[2]; | |
+ | |
+ // check that we are not outside the fluid grid | |
+ if (x < nx && y < ny && z < nz) { | |
+ | |
+ // Linear index of length-6 vector field entry | |
+ unsigned int cellidx6 = idx(x,y,z)*6; | |
+ | |
+ // Read parent values | |
+ __syncthreads(); | |
+ const Float tau_xx = dev_ns_tau[cellidx6]; | |
+ const Float tau_xy = dev_ns_tau[cellidx6+1]; | |
+ const Float tau_xz = dev_ns_tau[cellidx6+2]; | |
+ const Float tau_yy = dev_ns_tau[cellidx6+3]; | |
+ const Float tau_yz = dev_ns_tau[cellidx6+4]; | |
+ const Float tau_zz = dev_ns_tau[cellidx6+5]; | |
+ | |
+ // x | |
+ if (x == 0) { | |
+ cellidx6 = idx(nx,y,z)*6; | |
+ dev_ns_tau[cellidx6] = tau_xx; | |
+ dev_ns_tau[cellidx6+1] = tau_xy; | |
+ dev_ns_tau[cellidx6+2] = tau_xz; | |
+ dev_ns_tau[cellidx6+3] = tau_yy; | |
+ dev_ns_tau[cellidx6+4] = tau_yz; | |
+ dev_ns_tau[cellidx6+5] = tau_zz; | |
+ } | |
+ if (x == nx-1) { | |
+ cellidx6 = idx(-1,y,z)*6; | |
+ dev_ns_tau[cellidx6] = tau_xx; | |
+ dev_ns_tau[cellidx6+1] = tau_xy; | |
+ dev_ns_tau[cellidx6+2] = tau_xz; | |
+ dev_ns_tau[cellidx6+3] = tau_yy; | |
+ dev_ns_tau[cellidx6+4] = tau_yz; | |
+ dev_ns_tau[cellidx6+5] = tau_zz; | |
+ } | |
+ | |
+ // y | |
+ if (y == 0) { | |
+ cellidx6 = idx(x,ny,z)*6; | |
+ dev_ns_tau[cellidx6] = tau_xx; | |
+ dev_ns_tau[cellidx6+1] = tau_xy; | |
+ dev_ns_tau[cellidx6+2] = tau_xz; | |
+ dev_ns_tau[cellidx6+3] = tau_yy; | |
+ dev_ns_tau[cellidx6+4] = tau_yz; | |
+ dev_ns_tau[cellidx6+5] = tau_zz; | |
+ } | |
+ if (y == ny-1) { | |
+ cellidx6 = idx(x,-1,z)*6; | |
+ dev_ns_tau[cellidx6] = tau_xx; | |
+ dev_ns_tau[cellidx6+1] = tau_xy; | |
+ dev_ns_tau[cellidx6+2] = tau_xz; | |
+ dev_ns_tau[cellidx6+3] = tau_yy; | |
+ dev_ns_tau[cellidx6+4] = tau_yz; | |
+ dev_ns_tau[cellidx6+5] = tau_zz; | |
+ } | |
+ | |
+ // z | |
+ if (z == 0 && bc_bot == 0) { // Dirichlet | |
+ cellidx6 = idx(x,y,-1)*6; | |
+ dev_ns_tau[cellidx6] = tau_xx; | |
+ dev_ns_tau[cellidx6+1] = tau_xy; | |
+ dev_ns_tau[cellidx6+2] = tau_xz; | |
+ dev_ns_tau[cellidx6+3] = tau_yy; | |
+ dev_ns_tau[cellidx6+4] = tau_yz; | |
+ dev_ns_tau[cellidx6+5] = tau_zz; | |
+ } | |
+ if (z == 1 && bc_bot == 1) { // Neumann | |
+ cellidx6 = idx(x,y,-1)*6; | |
+ dev_ns_tau[cellidx6] = tau_xx; | |
+ dev_ns_tau[cellidx6+1] = tau_xy; | |
+ dev_ns_tau[cellidx6+2] = tau_xz; | |
+ dev_ns_tau[cellidx6+3] = tau_yy; | |
+ dev_ns_tau[cellidx6+4] = tau_yz; | |
+ dev_ns_tau[cellidx6+5] = tau_zz; | |
+ } | |
+ if (z == 0 && bc_bot == 2) { // Periodic | |
+ cellidx6 = idx(x,y,nz)*6; | |
+ dev_ns_tau[cellidx6] = tau_xx; | |
+ dev_ns_tau[cellidx6+1] = tau_xy; | |
+ dev_ns_tau[cellidx6+2] = tau_xz; | |
+ dev_ns_tau[cellidx6+3] = tau_yy; | |
+ dev_ns_tau[cellidx6+4] = tau_yz; | |
+ dev_ns_tau[cellidx6+5] = tau_zz; | |
+ } | |
+ | |
+ if (z == nz-1 && bc_top == 0) { // Dirichlet | |
+ cellidx6 = idx(x,y,nz)*6; | |
+ dev_ns_tau[cellidx6] = tau_xx; | |
+ dev_ns_tau[cellidx6+1] = tau_xy; | |
+ dev_ns_tau[cellidx6+2] = tau_xz; | |
+ dev_ns_tau[cellidx6+3] = tau_yy; | |
+ dev_ns_tau[cellidx6+4] = tau_yz; | |
+ dev_ns_tau[cellidx6+5] = tau_zz; | |
+ } | |
+ if (z == nz-2 && bc_top == 1) { // Neumann | |
+ cellidx6 = idx(x,y,nz)*6; | |
+ dev_ns_tau[cellidx6] = tau_xx; | |
+ dev_ns_tau[cellidx6+1] = tau_xy; | |
+ dev_ns_tau[cellidx6+2] = tau_xz; | |
+ dev_ns_tau[cellidx6+3] = tau_yy; | |
+ dev_ns_tau[cellidx6+4] = tau_yz; | |
+ dev_ns_tau[cellidx6+5] = tau_zz; | |
+ } | |
+ if (z == nz-1 && bc_top == 2) { // Periodic | |
+ cellidx6 = idx(x,y,-1)*6; | |
+ dev_ns_tau[cellidx6] = tau_xx; | |
+ dev_ns_tau[cellidx6+1] = tau_xy; | |
+ dev_ns_tau[cellidx6+2] = tau_xz; | |
+ dev_ns_tau[cellidx6+3] = tau_yy; | |
+ dev_ns_tau[cellidx6+4] = tau_yz; | |
+ dev_ns_tau[cellidx6+5] = tau_zz; | |
+ } | |
+ } | |
+} | |
+ | |
+// Update a the forcing values in the ghost nodes from their parent cell value… | |
+// The edge (diagonal) cells are not written since they are not read. Launch | |
+// this kernel for all cells in the grid. | |
+/* | |
+__global__ void setNSghostNodesForcing( | |
+ Float* dev_ns_f1, | |
+ Float3* dev_ns_f2, | |
+ Float* dev_ns_f, | |
+ unsigned int nijac) | |
+ | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Grid dimensions | |
+ const unsigned int nx = devC_grid.num[0]; | |
+ const unsigned int ny = devC_grid.num[1]; | |
+ const unsigned int nz = devC_grid.num[2]; | |
+ | |
+ // 1D thread index | |
+ unsigned int cellidx = idx(x,y,z); | |
+ | |
+ // check that we are not outside the fluid grid | |
+ if (x < nx && y < ny && z < nz) { | |
+ | |
+ __syncthreads(); | |
+ const Float f = dev_ns_f[cellidx]; | |
+ Float f1; | |
+ Float3 f2; | |
+ | |
+ if (nijac == 0) { | |
+ __syncthreads(); | |
+ f1 = dev_ns_f1[cellidx]; | |
+ f2 = dev_ns_f2[cellidx]; | |
+ } | |
+ | |
+ if (x == 0) { | |
+ cellidx = idx(nx,y,z); | |
+ dev_ns_f[cellidx] = f; | |
+ if (nijac == 0) { | |
+ dev_ns_f1[cellidx] = f1; | |
+ dev_ns_f2[cellidx] = f2; | |
+ } | |
+ } | |
+ if (x == nx-1) { | |
+ cellidx = idx(-1,y,z); | |
+ dev_ns_f[cellidx] = f; | |
+ if (nijac == 0) { | |
+ dev_ns_f1[cellidx] = f1; | |
+ dev_ns_f2[cellidx] = f2; | |
+ } | |
+ } | |
+ | |
+ if (y == 0) { | |
+ cellidx = idx(x,ny,z); | |
+ dev_ns_f[cellidx] = f; | |
+ if (nijac == 0) { | |
+ dev_ns_f1[cellidx] = f1; | |
+ dev_ns_f2[cellidx] = f2; | |
+ } | |
+ } | |
+ if (y == ny-1) { | |
+ cellidx = idx(x,-1,z); | |
+ dev_ns_f[cellidx] = f; | |
+ if (nijac == 0) { | |
+ dev_ns_f1[cellidx] = f1; | |
+ dev_ns_f2[cellidx] = f2; | |
+ } | |
+ } | |
+ | |
+ if (z == 0) { | |
+ cellidx = idx(x,y,nz); | |
+ dev_ns_f[cellidx] = f; | |
+ if (nijac == 0) { | |
+ dev_ns_f1[cellidx] = f1; | |
+ dev_ns_f2[cellidx] = f2; | |
+ } | |
+ } | |
+ if (z == nz-1) { | |
+ cellidx = idx(x,y,-1); | |
+ dev_ns_f[cellidx] = f; | |
+ if (nijac == 0) { | |
+ dev_ns_f1[cellidx] = f1; | |
+ dev_ns_f2[cellidx] = f2; | |
+ } | |
+ } | |
+ } | |
+} | |
+*/ | |
+ | |
+// Find the porosity in each cell on the base of a sphere, centered at the cell | |
+// center. | |
+__global__ void findPorositiesVelocitiesDiametersSpherical( | |
+ const unsigned int* dev_cellStart, | |
+ const unsigned int* dev_cellEnd, | |
+ const Float4* dev_x_sorted, | |
+ const Float4* dev_vel_sorted, | |
+ Float* dev_ns_phi, | |
+ Float* dev_ns_dphi, | |
+ Float3* dev_ns_vp_avg, | |
+ Float* dev_ns_d_avg, | |
+ const unsigned int iteration, | |
+ const unsigned int np) | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Grid dimensions | |
+ const unsigned int nx = devC_grid.num[0]; | |
+ const unsigned int ny = devC_grid.num[1]; | |
+ const unsigned int nz = devC_grid.num[2]; | |
+ | |
+ // Cell dimensions | |
+ const Float dx = devC_grid.L[0]/nx; | |
+ const Float dy = devC_grid.L[1]/ny; | |
+ const Float dz = devC_grid.L[2]/nz; | |
+ | |
+ // Cell sphere radius | |
+ const Float R = fmin(dx, fmin(dy,dz)) * 0.5; | |
+ const Float cell_volume = 4.0/3.0*M_PI*R*R*R; | |
+ | |
+ Float void_volume = cell_volume; | |
+ Float4 xr; // particle pos. and radius | |
+ | |
+ // check that we are not outside the fluid grid | |
+ if (x < nx && y < ny && z < nz) { | |
+ | |
+ if (np > 0) { | |
+ | |
+ // Cell sphere center position | |
+ const Float3 X = MAKE_FLOAT3( | |
+ x*dx + 0.5*dx, | |
+ y*dy + 0.5*dy, | |
+ z*dz + 0.5*dz); | |
+ | |
+ Float d, r; | |
+ Float phi = 1.00; | |
+ Float4 v; | |
+ unsigned int n = 0; | |
+ | |
+ Float3 v_avg = MAKE_FLOAT3(0.0, 0.0, 0.0); | |
+ Float d_avg = 0.0; | |
+ | |
+ // Read old porosity | |
+ __syncthreads(); | |
+ Float phi_0 = dev_ns_phi[idx(x,y,z)]; | |
+ | |
+ // The cell 3d index | |
+ const int3 gridPos = make_int3((int)x,(int)y,(int)z); | |
+ | |
+ // The neighbor cell 3d index | |
+ int3 targetCell; | |
+ | |
+ // The distance modifier for particles across periodic boundaries | |
+ Float3 dist, distmod; | |
+ | |
+ unsigned int cellID, startIdx, endIdx, i; | |
+ | |
+ // Iterate over 27 neighbor cells | |
+ for (int z_dim=-1; z_dim<2; ++z_dim) { // z-axis | |
+ for (int y_dim=-1; y_dim<2; ++y_dim) { // y-axis | |
+ for (int x_dim=-1; x_dim<2; ++x_dim) { // x-axis | |
+ | |
+ // Index of neighbor cell this iteration is looking at | |
+ targetCell = gridPos + make_int3(x_dim, y_dim, z_dim); | |
+ | |
+ // Get distance modifier for interparticle | |
+ // vector, if it crosses a periodic boundary | |
+ distmod = MAKE_FLOAT3(0.0, 0.0, 0.0); | |
+ if (findDistMod(&targetCell, &distmod) != -1) { | |
+ | |
+ // Calculate linear cell ID | |
+ cellID = targetCell.x + targetCell.y * devC_grid.n… | |
+ + (devC_grid.num[0] * devC_grid.num[1]) | |
+ * targetCell.z; | |
+ | |
+ // Lowest particle index in cell | |
+ startIdx = dev_cellStart[cellID]; | |
+ | |
+ // Make sure cell is not empty | |
+ if (startIdx != 0xffffffff) { | |
+ | |
+ // Highest particle index in cell | |
+ endIdx = dev_cellEnd[cellID]; | |
+ | |
+ // Iterate over cell particles | |
+ for (i=startIdx; i<endIdx; ++i) { | |
+ | |
+ // Read particle position and radius | |
+ __syncthreads(); | |
+ xr = dev_x_sorted[i]; | |
+ v = dev_vel_sorted[i]; | |
+ r = xr.w; | |
+ | |
+ // Find center distance | |
+ dist = MAKE_FLOAT3( | |
+ X.x - xr.x, | |
+ X.y - xr.y, | |
+ X.z - xr.z); | |
+ dist += distmod; | |
+ d = length(dist); | |
+ | |
+ // Lens shaped intersection | |
+ if ((R - r) < d && d < (R + r)) { | |
+ void_volume -= | |
+ 1.0/(12.0*d) * ( | |
+ M_PI*(R + r - d)*(R + r - … | |
+ *(d*d + 2.0*d*r - 3.0*r*r | |
+ + 2.0*d*R + 6.0*r*R | |
+ - 3.0*R*R) ); | |
+ v_avg += MAKE_FLOAT3(v.x, v.y, v.z); | |
+ d_avg += 2.0*r; | |
+ n++; | |
+ } | |
+ | |
+ // Particle fully contained in cell sphere | |
+ if (d <= R - r) { | |
+ void_volume -= 4.0/3.0*M_PI*r*r*r; | |
+ v_avg += MAKE_FLOAT3(v.x, v.y, v.z); | |
+ d_avg += 2.0*r; | |
+ n++; | |
+ } | |
+ } | |
+ } | |
+ } | |
+ } | |
+ } | |
+ } | |
+ | |
+ if (phi < 0.999) { | |
+ v_avg /= n; | |
+ d_avg /= n; | |
+ } | |
+ | |
+ // Make sure that the porosity is in the interval [0.0;1.0] | |
+ phi = fmin(1.00, fmax(0.00, void_volume/cell_volume)); | |
+ //phi = void_volume/cell_volume; | |
+ | |
+ Float dphi = phi - phi_0; | |
+ if (iteration == 0) | |
+ dphi = 0.0; | |
+ | |
+ // report values to stdout for debugging | |
+ //printf("%d,%d,%d\tphi = %f dphi = %f v_avg = %f,%f,%f d_avg = %f… | |
+ // x,y,z, phi, dphi, v_avg.x, v_avg.y, v_avg.z, d_avg); | |
+ | |
+ // Save porosity, porosity change, average velocity and average di… | |
+ __syncthreads(); | |
+ //phi = 1.0; dphi = 0.0; // disable porosity effects | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ dev_ns_phi[cellidx] = phi; | |
+ dev_ns_dphi[cellidx] = dphi; | |
+ dev_ns_vp_avg[cellidx] = v_avg; | |
+ dev_ns_d_avg[cellidx] = d_avg; | |
+ } else { | |
+ __syncthreads(); | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ dev_ns_phi[cellidx] = 1.0; | |
+ dev_ns_dphi[cellidx] = 0.0; | |
+ dev_ns_vp_avg[cellidx] = MAKE_FLOAT3(0.0, 0.0, 0.0); | |
+ dev_ns_d_avg[cellidx] = 0.0; | |
+ } | |
+ } | |
+} | |
+ | |
+// Modulate the hydraulic pressure at the upper boundary | |
+__global__ void setUpperPressureNS( | |
+ Float* dev_ns_p, | |
+ Float* dev_ns_epsilon, | |
+ Float* dev_ns_epsilon_new, | |
+ Float beta, | |
+ const Float new_pressure) | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // check that the thread is located at the top boundary | |
+ if (x < devC_grid.num[0] && | |
+ y < devC_grid.num[1] && | |
+ z == devC_grid.num[2]-1) { | |
+ | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ | |
+ // Read the current pressure | |
+ const Float pressure = dev_ns_p[cellidx]; | |
+ | |
+ // Determine the new epsilon boundary condition | |
+ const Float epsilon = new_pressure - beta*pressure; | |
+ | |
+ // Write the new pressure and epsilon values to the top boundary cells | |
+ __syncthreads(); | |
+ dev_ns_epsilon[cellidx] = epsilon; | |
+ dev_ns_epsilon_new[cellidx] = epsilon; | |
+ dev_ns_p[cellidx] = new_pressure; | |
+ } | |
+} | |
+ | |
+// Find the gradient in a cell in a homogeneous, cubic 3D scalar field using | |
+// finite central differences | |
+__device__ Float3 gradient( | |
+ const Float* dev_scalarfield, | |
+ const unsigned int x, | |
+ const unsigned int y, | |
+ const unsigned int z, | |
+ const Float dx, | |
+ const Float dy, | |
+ const Float dz) | |
+{ | |
+ // Read 6 neighbor cells | |
+ __syncthreads(); | |
+ //const Float p = dev_scalarfield[idx(x,y,z)]; | |
+ const Float xn = dev_scalarfield[idx(x-1,y,z)]; | |
+ const Float xp = dev_scalarfield[idx(x+1,y,z)]; | |
+ const Float yn = dev_scalarfield[idx(x,y-1,z)]; | |
+ const Float yp = dev_scalarfield[idx(x,y+1,z)]; | |
+ const Float zn = dev_scalarfield[idx(x,y,z-1)]; | |
+ const Float zp = dev_scalarfield[idx(x,y,z+1)]; | |
+ | |
+ //__syncthreads(); | |
+ //if (p != 0.0) | |
+ //printf("p[%d,%d,%d] =\t%f\n", x,y,z, p); | |
+ | |
+ // Calculate central-difference gradients | |
+ return MAKE_FLOAT3( | |
+ (xp - xn)/(2.0*dx), | |
+ (yp - yn)/(2.0*dy), | |
+ (zp - zn)/(2.0*dz)); | |
+} | |
+ | |
+// Find the gradients of the velocity at the center of the cell with index x,y… | |
+// and cell size dx,dy,dz | |
+__device__ Float3 v_gradient( | |
+ const Float* dev_ns_v_x, | |
+ const Float* dev_ns_v_y, | |
+ const Float* dev_ns_v_z, | |
+ const unsigned int x, | |
+ const unsigned int y, | |
+ const unsigned int z, | |
+ const Float dx, | |
+ const Float dy, | |
+ const Float dz) | |
+{ | |
+ // Read the six cell face velocities | |
+ __syncthreads(); | |
+ const Float v_xn = dev_ns_v_x[vidx(x,y,z)]; | |
+ const Float v_xp = dev_ns_v_x[vidx(x+1,y,z)]; | |
+ const Float v_yn = dev_ns_v_y[vidx(x,y,z)]; | |
+ const Float v_yp = dev_ns_v_y[vidx(x,y+1,z)]; | |
+ const Float v_zn = dev_ns_v_z[vidx(x,y,z)]; | |
+ const Float v_zp = dev_ns_v_y[vidx(x,y,z+1)]; | |
+ | |
+ // Calculate the velocity gradient | |
+ return MAKE_FLOAT3( | |
+ (v_xp - v_xn)/dx, | |
+ (v_yp - v_yn)/dy, | |
+ (v_zp - v_zn)/dz); | |
+} | |
+ | |
+// Find the dv_i/di gradients in a cell in a homogeneous, cubic 3D vector field | |
+// using finite central differences | |
+__device__ Float3 gradient( | |
+ const Float3* dev_vectorfield, | |
+ const unsigned int x, | |
+ const unsigned int y, | |
+ const unsigned int z, | |
+ const Float dx, | |
+ const Float dy, | |
+ const Float dz) | |
+{ | |
+ // Read 6 neighbor cells | |
+ __syncthreads(); | |
+ const Float xn = dev_vectorfield[idx(x-1,y,z)].x; | |
+ const Float xp = dev_vectorfield[idx(x+1,y,z)].x; | |
+ const Float yn = dev_vectorfield[idx(x,y-1,z)].y; | |
+ const Float yp = dev_vectorfield[idx(x,y+1,z)].y; | |
+ const Float zn = dev_vectorfield[idx(x,y,z-1)].z; | |
+ const Float zp = dev_vectorfield[idx(x,y,z+1)].z; | |
+ | |
+ //__syncthreads(); | |
+ //if (p != 0.0) | |
+ //printf("p[%d,%d,%d] =\t%f\n", x,y,z, p); | |
+ | |
+ // Calculate central-difference gradients | |
+ return MAKE_FLOAT3( | |
+ (xp - xn)/(2.0*dx), | |
+ (yp - yn)/(2.0*dy), | |
+ (zp - zn)/(2.0*dz)); | |
+} | |
+ | |
+// Find the divergence in a cell in a homogeneous, cubic, 3D vector field | |
+__device__ Float divergence( | |
+ const Float3* dev_vectorfield, | |
+ const unsigned int x, | |
+ const unsigned int y, | |
+ const unsigned int z, | |
+ const Float dx, | |
+ const Float dy, | |
+ const Float dz) | |
+{ | |
+ // Read 6 neighbor cells | |
+ __syncthreads(); | |
+ const Float3 xn = dev_vectorfield[idx(x-1,y,z)]; | |
+ const Float3 xp = dev_vectorfield[idx(x+1,y,z)]; | |
+ const Float3 yn = dev_vectorfield[idx(x,y-1,z)]; | |
+ const Float3 yp = dev_vectorfield[idx(x,y+1,z)]; | |
+ const Float3 zn = dev_vectorfield[idx(x,y,z-1)]; | |
+ const Float3 zp = dev_vectorfield[idx(x,y,z+1)]; | |
+ | |
+ // Calculate the central-difference gradients and divergence | |
+ return | |
+ (xp.x - xn.x)/(2.0*dx) + | |
+ (yp.y - yn.y)/(2.0*dy) + | |
+ (zp.z - zn.z)/(2.0*dz); | |
+} | |
+ | |
+// Find the spatial gradient in e.g. pressures per cell | |
+// using first order central differences | |
+__global__ void findNSgradientsDev( | |
+ Float* dev_scalarfield, // in | |
+ Float3* dev_vectorfield) // out | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Grid dimensions | |
+ const unsigned int nx = devC_grid.num[0]; | |
+ const unsigned int ny = devC_grid.num[1]; | |
+ const unsigned int nz = devC_grid.num[2]; | |
+ | |
+ // Grid sizes | |
+ const Float dx = devC_grid.L[0]/nx; | |
+ const Float dy = devC_grid.L[1]/ny; | |
+ const Float dz = devC_grid.L[2]/nz; | |
+ | |
+ // 1D thread index | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ | |
+ // Check that we are not outside the fluid grid | |
+ if (x < nx && y < ny && z < nz) { | |
+ | |
+ const Float3 grad = gradient(dev_scalarfield, x, y, z, dx, dy, dz); | |
+ | |
+ // Write gradient | |
+ __syncthreads(); | |
+ dev_vectorfield[cellidx] = grad; | |
+ } | |
+} | |
+ | |
+// Find the outer product of v v | |
+__global__ void findvvOuterProdNS( | |
+ Float3* dev_ns_v, // in | |
+ Float* dev_ns_v_prod) // out | |
+{ | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // 1D thread index | |
+ const unsigned int cellidx6 = idx(x,y,z)*6; | |
+ | |
+ // Check that we are not outside the fluid grid | |
+ if (x < devC_grid.num[0] && y < devC_grid.num[1] && z < devC_grid.num[2]) { | |
+ | |
+ __syncthreads(); | |
+ const Float3 v = dev_ns_v[idx(x,y,z)]; | |
+ | |
+ // The outer product (v v) looks like: | |
+ // [[ v_x^2 v_x*v_y v_x*v_z ] | |
+ // [ v_y*v_x v_y^2 v_y*v_z ] | |
+ // [ v_z*v_x v_z*v_y v_z^2 ]] | |
+ | |
+ // The tensor is symmetrical: value i,j = j,i. | |
+ // Only the upper triangle is saved, with the cells given a linear ind… | |
+ // enumerated as: | |
+ // [[ 0 1 2 ] | |
+ // [ 3 4 ] | |
+ // [ 5 ]] | |
+ | |
+ __syncthreads(); | |
+ dev_ns_v_prod[cellidx6] = v.x*v.x; | |
+ dev_ns_v_prod[cellidx6+1] = v.x*v.y; | |
+ dev_ns_v_prod[cellidx6+2] = v.x*v.z; | |
+ dev_ns_v_prod[cellidx6+3] = v.y*v.y; | |
+ dev_ns_v_prod[cellidx6+4] = v.y*v.z; | |
+ dev_ns_v_prod[cellidx6+5] = v.z*v.z; | |
+ } | |
+} | |
+ | |
+ | |
+// Find the fluid stress tensor. It is symmetrical, and can thus be saved in 6 | |
+// values in 3D. | |
+__global__ void findNSstressTensor( | |
+ Float3* dev_ns_v, // in | |
+ Float* dev_ns_tau) // out | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Grid dimensions | |
+ const unsigned int nx = devC_grid.num[0]; | |
+ const unsigned int ny = devC_grid.num[1]; | |
+ const unsigned int nz = devC_grid.num[2]; | |
+ | |
+ // Cell sizes | |
+ const Float dx = devC_grid.L[0]/nx; | |
+ const Float dy = devC_grid.L[1]/ny; | |
+ const Float dz = devC_grid.L[2]/nz; | |
+ | |
+ // 1D thread index | |
+ const unsigned int cellidx6 = idx(x,y,z)*6; | |
+ | |
+ // Check that we are not outside the fluid grid | |
+ if (x < devC_grid.num[0] && y < devC_grid.num[1] && z < devC_grid.num[2]) { | |
+ | |
+ // The fluid stress tensor (tau) looks like | |
+ // [[ tau_xx tau_xy tau_xz ] | |
+ // [ tau_yx tau_xy tau_yz ] | |
+ // [ tau_zx tau_zy tau_zz ]] | |
+ | |
+ // The tensor is symmetrical: value i,j = j,i. | |
+ // Only the upper triangle is saved, with the cells given a linear ind… | |
+ // enumerated as: | |
+ // [[ 0 1 2 ] | |
+ // [ 3 4 ] | |
+ // [ 5 ]] | |
+ | |
+ // Read neighbor values for central differences | |
+ __syncthreads(); | |
+ const Float3 xp = dev_ns_v[idx(x+1,y,z)]; | |
+ const Float3 xn = dev_ns_v[idx(x-1,y,z)]; | |
+ const Float3 yp = dev_ns_v[idx(x,y+1,z)]; | |
+ const Float3 yn = dev_ns_v[idx(x,y-1,z)]; | |
+ const Float3 zp = dev_ns_v[idx(x,y,z+1)]; | |
+ const Float3 zn = dev_ns_v[idx(x,y,z-1)]; | |
+ | |
+ // The diagonal stress tensor components | |
+ const Float tau_xx = 2.0*devC_params.mu*(xp.x - xn.x)/(2.0*dx); | |
+ const Float tau_yy = 2.0*devC_params.mu*(yp.y - yn.y)/(2.0*dy); | |
+ const Float tau_zz = 2.0*devC_params.mu*(zp.z - zn.z)/(2.0*dz); | |
+ | |
+ // The off-diagonal stress tensor components | |
+ const Float tau_xy = | |
+ devC_params.mu*((yp.x - yn.x)/(2.0*dy) + (xp.y - xn.y)/(2.0*dx)); | |
+ const Float tau_xz = | |
+ devC_params.mu*((zp.x - zn.x)/(2.0*dz) + (xp.z - xn.z)/(2.0*dx)); | |
+ const Float tau_yz = | |
+ devC_params.mu*((zp.y - zn.y)/(2.0*dz) + (yp.z - yn.z)/(2.0*dy)); | |
+ | |
+ /* | |
+ if (x == 0 && y == 0 && z == 0) | |
+ printf("mu = %f\n", mu); | |
+ if (tau_xz > 1.0e-6) | |
+ printf("%d,%d,%d\ttau_xx = %f\n", x,y,z, tau_xx); | |
+ if (tau_yz > 1.0e-6) | |
+ printf("%d,%d,%d\ttau_yy = %f\n", x,y,z, tau_yy); | |
+ if (tau_zz > 1.0e-6) | |
+ printf("%d,%d,%d\ttau_zz = %f\n", x,y,z, tau_zz); | |
+ */ | |
+ | |
+ // Store values in global memory | |
+ __syncthreads(); | |
+ dev_ns_tau[cellidx6] = tau_xx; | |
+ dev_ns_tau[cellidx6+1] = tau_xy; | |
+ dev_ns_tau[cellidx6+2] = tau_xz; | |
+ dev_ns_tau[cellidx6+3] = tau_yy; | |
+ dev_ns_tau[cellidx6+4] = tau_yz; | |
+ dev_ns_tau[cellidx6+5] = tau_zz; | |
+ } | |
+} | |
+ | |
+ | |
+// Find the divergence of phi*v*v | |
+__global__ void findNSdivphiviv( | |
+ Float* dev_ns_phi, // in | |
+ Float3* dev_ns_v, // in | |
+ Float3* dev_ns_div_phi_vi_v) // out | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Grid dimensions | |
+ const unsigned int nx = devC_grid.num[0]; | |
+ const unsigned int ny = devC_grid.num[1]; | |
+ const unsigned int nz = devC_grid.num[2]; | |
+ | |
+ // Cell sizes | |
+ const Float dx = devC_grid.L[0]/nx; | |
+ const Float dy = devC_grid.L[1]/ny; | |
+ const Float dz = devC_grid.L[2]/nz; | |
+ | |
+ // 1D thread index | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ | |
+ // Check that we are not outside the fluid grid | |
+ if (x < nx && y < ny && z < nz) { | |
+ | |
+ // Read porosity and velocity in the 6 neighbor cells | |
+ __syncthreads(); | |
+ const Float phi_xn = dev_ns_phi[idx(x-1,y,z)]; | |
+ const Float phi_xp = dev_ns_phi[idx(x+1,y,z)]; | |
+ const Float phi_yn = dev_ns_phi[idx(x,y-1,z)]; | |
+ const Float phi_yp = dev_ns_phi[idx(x,y+1,z)]; | |
+ const Float phi_zn = dev_ns_phi[idx(x,y,z-1)]; | |
+ const Float phi_zp = dev_ns_phi[idx(x,y,z+1)]; | |
+ | |
+ const Float3 v_xn = dev_ns_v[idx(x-1,y,z)]; | |
+ const Float3 v_xp = dev_ns_v[idx(x+1,y,z)]; | |
+ const Float3 v_yn = dev_ns_v[idx(x,y-1,z)]; | |
+ const Float3 v_yp = dev_ns_v[idx(x,y+1,z)]; | |
+ const Float3 v_zn = dev_ns_v[idx(x,y,z-1)]; | |
+ const Float3 v_zp = dev_ns_v[idx(x,y,z+1)]; | |
+ | |
+ // Calculate the divergence: div(phi*v_i*v) | |
+ const Float3 div_phi_vi_v = MAKE_FLOAT3( | |
+ // x | |
+ (phi_xp*v_xp.x*v_xp.x - phi_xn*v_xn.x*v_xn.x)/(2.0*dx) + | |
+ (phi_yp*v_yp.x*v_yp.y - phi_yn*v_yn.x*v_yn.y)/(2.0*dy) + | |
+ (phi_zp*v_zp.x*v_zp.z - phi_zn*v_zn.x*v_zn.z)/(2.0*dz), | |
+ // y | |
+ (phi_xp*v_xp.y*v_xp.x - phi_xn*v_xn.y*v_xn.x)/(2.0*dx) + | |
+ (phi_yp*v_yp.y*v_yp.y - phi_yn*v_yn.y*v_yn.y)/(2.0*dy) + | |
+ (phi_zp*v_zp.y*v_zp.z - phi_zn*v_zn.y*v_zn.z)/(2.0*dz), | |
+ // z | |
+ (phi_xp*v_xp.z*v_xp.x - phi_xn*v_xn.z*v_xn.x)/(2.0*dx) + | |
+ (phi_yp*v_yp.z*v_yp.y - phi_yn*v_yn.z*v_yn.y)/(2.0*dy) + | |
+ (phi_zp*v_zp.z*v_zp.z - phi_zn*v_zn.z*v_zn.z)/(2.0*dz)); | |
+ | |
+ // Write divergence | |
+ __syncthreads(); | |
+ dev_ns_div_phi_vi_v[cellidx] = div_phi_vi_v; | |
+ } | |
+} | |
+ | |
+// Find the divergence of phi*tau | |
+__global__ void findNSdivphitau( | |
+ Float* dev_ns_phi, // in | |
+ Float* dev_ns_tau, // in | |
+ Float3* dev_ns_div_phi_tau) // out | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Grid dimensions | |
+ const unsigned int nx = devC_grid.num[0]; | |
+ const unsigned int ny = devC_grid.num[1]; | |
+ const unsigned int nz = devC_grid.num[2]; | |
+ | |
+ // Cell sizes | |
+ const Float dx = devC_grid.L[0]/nx; | |
+ const Float dy = devC_grid.L[1]/ny; | |
+ const Float dz = devC_grid.L[2]/nz; | |
+ | |
+ // 1D thread index | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ | |
+ // Check that we are not outside the fluid grid | |
+ if (x < nx && y < ny && z < nz) { | |
+ | |
+ // Read the porosity in the 6 neighbor cells | |
+ __syncthreads(); | |
+ const Float phi_xn = dev_ns_phi[idx(x-1,y,z)]; | |
+ const Float phi_xp = dev_ns_phi[idx(x+1,y,z)]; | |
+ const Float phi_yn = dev_ns_phi[idx(x,y-1,z)]; | |
+ const Float phi_yp = dev_ns_phi[idx(x,y+1,z)]; | |
+ const Float phi_zn = dev_ns_phi[idx(x,y,z-1)]; | |
+ const Float phi_zp = dev_ns_phi[idx(x,y,z+1)]; | |
+ | |
+ // Read the stress tensor in the 6 neighbor cells | |
+ const Float tau_xx_xp = dev_ns_tau[idx(x+1,y,z)*6]; | |
+ const Float tau_xy_xp = dev_ns_tau[idx(x+1,y,z)*6+1]; | |
+ const Float tau_xz_xp = dev_ns_tau[idx(x+1,y,z)*6+2]; | |
+ const Float tau_yy_xp = dev_ns_tau[idx(x+1,y,z)*6+3]; | |
+ const Float tau_yz_xp = dev_ns_tau[idx(x+1,y,z)*6+4]; | |
+ const Float tau_zz_xp = dev_ns_tau[idx(x+1,y,z)*6+5]; | |
+ | |
+ const Float tau_xx_xn = dev_ns_tau[idx(x-1,y,z)*6]; | |
+ const Float tau_xy_xn = dev_ns_tau[idx(x-1,y,z)*6+1]; | |
+ const Float tau_xz_xn = dev_ns_tau[idx(x-1,y,z)*6+2]; | |
+ const Float tau_yy_xn = dev_ns_tau[idx(x-1,y,z)*6+3]; | |
+ const Float tau_yz_xn = dev_ns_tau[idx(x-1,y,z)*6+4]; | |
+ const Float tau_zz_xn = dev_ns_tau[idx(x-1,y,z)*6+5]; | |
+ | |
+ const Float tau_xx_yp = dev_ns_tau[idx(x,y+1,z)*6]; | |
+ const Float tau_xy_yp = dev_ns_tau[idx(x,y+1,z)*6+1]; | |
+ const Float tau_xz_yp = dev_ns_tau[idx(x,y+1,z)*6+2]; | |
+ const Float tau_yy_yp = dev_ns_tau[idx(x,y+1,z)*6+3]; | |
+ const Float tau_yz_yp = dev_ns_tau[idx(x,y+1,z)*6+4]; | |
+ const Float tau_zz_yp = dev_ns_tau[idx(x,y+1,z)*6+5]; | |
+ | |
+ const Float tau_xx_yn = dev_ns_tau[idx(x,y-1,z)*6]; | |
+ const Float tau_xy_yn = dev_ns_tau[idx(x,y-1,z)*6+1]; | |
+ const Float tau_xz_yn = dev_ns_tau[idx(x,y-1,z)*6+2]; | |
+ const Float tau_yy_yn = dev_ns_tau[idx(x,y-1,z)*6+3]; | |
+ const Float tau_yz_yn = dev_ns_tau[idx(x,y-1,z)*6+4]; | |
+ const Float tau_zz_yn = dev_ns_tau[idx(x,y-1,z)*6+5]; | |
+ | |
+ const Float tau_xx_zp = dev_ns_tau[idx(x,y,z+1)*6]; | |
+ const Float tau_xy_zp = dev_ns_tau[idx(x,y,z+1)*6+1]; | |
+ const Float tau_xz_zp = dev_ns_tau[idx(x,y,z+1)*6+2]; | |
+ const Float tau_yy_zp = dev_ns_tau[idx(x,y,z+1)*6+3]; | |
+ const Float tau_yz_zp = dev_ns_tau[idx(x,y,z+1)*6+4]; | |
+ const Float tau_zz_zp = dev_ns_tau[idx(x,y,z+1)*6+5]; | |
+ | |
+ const Float tau_xx_zn = dev_ns_tau[idx(x,y,z-1)*6]; | |
+ const Float tau_xy_zn = dev_ns_tau[idx(x,y,z-1)*6+1]; | |
+ const Float tau_xz_zn = dev_ns_tau[idx(x,y,z-1)*6+2]; | |
+ const Float tau_yy_zn = dev_ns_tau[idx(x,y,z-1)*6+3]; | |
+ const Float tau_yz_zn = dev_ns_tau[idx(x,y,z-1)*6+4]; | |
+ const Float tau_zz_zn = dev_ns_tau[idx(x,y,z-1)*6+5]; | |
+ | |
+ // Calculate div(phi*tau) | |
+ const Float3 div_phi_tau = MAKE_FLOAT3( | |
+ // x | |
+ (phi_xp*tau_xx_xp - phi_xn*tau_xx_xn)/dx + | |
+ (phi_yp*tau_xy_yp - phi_yn*tau_xy_yn)/dy + | |
+ (phi_zp*tau_xz_zp - phi_zn*tau_xz_zn)/dz, | |
+ // y | |
+ (phi_xp*tau_xy_xp - phi_xn*tau_xy_xn)/dx + | |
+ (phi_yp*tau_yy_yp - phi_yn*tau_yy_yn)/dy + | |
+ (phi_zp*tau_yz_zp - phi_zn*tau_yz_zn)/dz, | |
+ // z | |
+ (phi_xp*tau_xz_xp - phi_xn*tau_xz_xn)/dx + | |
+ (phi_yp*tau_yz_yp - phi_yn*tau_yz_yn)/dy + | |
+ (phi_zp*tau_zz_zp - phi_zn*tau_zz_zn)/dz); | |
+ | |
+ // Write divergence | |
+ __syncthreads(); | |
+ dev_ns_div_phi_tau[cellidx] = div_phi_tau; | |
+ } | |
+} | |
+ | |
+// Find the divergence of phi v v | |
+__global__ void findNSdivphivv( | |
+ Float* dev_ns_v_prod, // in | |
+ Float* dev_ns_phi, // in | |
+ Float3* dev_ns_div_phi_v_v) // out | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Grid dimensions | |
+ const unsigned int nx = devC_grid.num[0]; | |
+ const unsigned int ny = devC_grid.num[1]; | |
+ const unsigned int nz = devC_grid.num[2]; | |
+ | |
+ // Cell sizes | |
+ const Float dx = devC_grid.L[0]/nx; | |
+ const Float dy = devC_grid.L[1]/ny; | |
+ const Float dz = devC_grid.L[2]/nz; | |
+ | |
+ // 1D thread index | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ | |
+ // Check that we are not outside the fluid grid | |
+ if (x < nx && y < ny && z < nz) { | |
+ | |
+ // Read cell and 6 neighbor cells | |
+ __syncthreads(); | |
+ //const Float phi = dev_ns_phi[cellidx]; | |
+ const Float phi_xn = dev_ns_phi[idx(x-1,y,z)]; | |
+ const Float phi_xp = dev_ns_phi[idx(x+1,y,z)]; | |
+ const Float phi_yn = dev_ns_phi[idx(x,y-1,z)]; | |
+ const Float phi_yp = dev_ns_phi[idx(x,y+1,z)]; | |
+ const Float phi_zn = dev_ns_phi[idx(x,y,z-1)]; | |
+ const Float phi_zp = dev_ns_phi[idx(x,y,z+1)]; | |
+ | |
+ // The tensor is symmetrical: value i,j = j,i. | |
+ // Only the upper triangle is saved, with the cells given a linear ind… | |
+ // enumerated as: | |
+ // [[ 0 1 2 ] | |
+ // [ 3 4 ] | |
+ // [ 5 ]] | |
+ | |
+ // div(T) = | |
+ // [ de_xx/dx + de_xy/dy + de_xz/dz , | |
+ // de_yx/dx + de_yy/dy + de_yz/dz , | |
+ // de_zx/dx + de_zy/dy + de_zz/dz ] | |
+ | |
+ // This function finds the divergence of (phi v v), which is a vector | |
+ | |
+ // Calculate the divergence. See | |
+ // https://en.wikipedia.org/wiki/Divergence#Application_in_Cartesian_c… | |
+ // The symmetry described in findvvOuterProdNS is used | |
+ __syncthreads(); | |
+ const Float3 div = MAKE_FLOAT3( | |
+ ((dev_ns_v_prod[idx(x+1,y,z)*6]*phi_xp | |
+ - dev_ns_v_prod[idx(x-1,y,z)*6]*phi_xn)/(2.0*dx) + | |
+ (dev_ns_v_prod[idx(x,y+1,z)*6+1]*phi_yp | |
+ - dev_ns_v_prod[idx(x,y-1,z)*6+1]*phi_yn)/(2.0*dy) + | |
+ (dev_ns_v_prod[idx(x,y,z+1)*6+2]*phi_zp | |
+ - dev_ns_v_prod[idx(x,y,z-1)*6+2]*phi_zn)/(2.0*dz)), | |
+ ((dev_ns_v_prod[idx(x+1,y,z)*6+1]*phi_xp | |
+ - dev_ns_v_prod[idx(x-1,y,z)*6+1]*phi_xn)/(2.0*dx) + | |
+ (dev_ns_v_prod[idx(x,y+1,z)*6+3]*phi_yp | |
+ - dev_ns_v_prod[idx(x,y-1,z)*6+3]*phi_yn)/(2.0*dy) + | |
+ (dev_ns_v_prod[idx(x,y,z+1)*6+4]*phi_zp | |
+ - dev_ns_v_prod[idx(x,y,z-1)*6+4]*phi_zn)/(2.0*dz)), | |
+ ((dev_ns_v_prod[idx(x+1,y,z)*6+2]*phi_xp | |
+ - dev_ns_v_prod[idx(x-1,y,z)*6+2]*phi_xn)/(2.0*dx) + | |
+ (dev_ns_v_prod[idx(x,y+1,z)*6+4]*phi_yp | |
+ - dev_ns_v_prod[idx(x,y-1,z)*6+4]*phi_yn)/(2.0*dy) + | |
+ (dev_ns_v_prod[idx(x,y,z+1)*6+5]*phi_zp | |
+ - dev_ns_v_prod[idx(x,y,z-1)*6+5]*phi_zn)/(2.0*dz)) ); | |
+ | |
+ //printf("div[%d,%d,%d] = %f\t%f\t%f\n", x, y, z, div.x, div.y, div.z); | |
+ | |
+ // Write divergence | |
+ __syncthreads(); | |
+ dev_ns_div_phi_v_v[cellidx] = div; | |
+ } | |
+} | |
+ | |
+ | |
+// Find predicted fluid velocity | |
+__global__ void findPredNSvelocities( | |
+ Float* dev_ns_p, // in | |
+ Float3* dev_ns_v, // in | |
+ Float* dev_ns_phi, // in | |
+ Float* dev_ns_dphi, // in | |
+ Float3* dev_ns_div_phi_vi_v, // in | |
+ Float3* dev_ns_div_phi_tau, // in | |
+ int bc_bot, // in | |
+ int bc_top, // in | |
+ Float beta, // in | |
+ Float3* dev_ns_fi, // in | |
+ Float3* dev_ns_v_p) // out | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Grid dimensions | |
+ const unsigned int nx = devC_grid.num[0]; | |
+ const unsigned int ny = devC_grid.num[1]; | |
+ const unsigned int nz = devC_grid.num[2]; | |
+ | |
+ // Cell sizes | |
+ const Float dx = devC_grid.L[0]/nx; | |
+ const Float dy = devC_grid.L[1]/ny; | |
+ const Float dz = devC_grid.L[2]/nz; | |
+ | |
+ // 1D thread index | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ | |
+ // Check that we are not outside the fluid grid | |
+ if (x < nx && y < ny && z < nz) { | |
+ | |
+ // Values that are needed for calculating the predicted velocity | |
+ __syncthreads(); | |
+ const Float3 v = dev_ns_v[cellidx]; | |
+ const Float phi = dev_ns_phi[cellidx]; | |
+ const Float dphi = dev_ns_dphi[cellidx]; | |
+ const Float3 div_phi_vi_v = dev_ns_div_phi_vi_v[cellidx]; | |
+ const Float3 div_phi_tau = dev_ns_div_phi_tau[cellidx]; | |
+ | |
+ // The particle-fluid interaction force should only be incoorporated if | |
+ // there is a fluid viscosity | |
+ Float3 f_i; | |
+ if (devC_params.mu > 0.0) | |
+ f_i = dev_ns_fi[cellidx]; | |
+ else | |
+ f_i = MAKE_FLOAT3(0.0, 0.0, 0.0); | |
+ | |
+ // Gravitational drag force on cell fluid mass | |
+ //const Float3 g = MAKE_FLOAT3(0.0, 0.0, -10.0); | |
+ //const Float3 f_g = devC_params.rho_f*dx*dy*dz*phi*g; | |
+ const Float3 f_g | |
+ = MAKE_FLOAT3(devC_params.g[0], devC_params.g[1], devC_params.g[2]) | |
+ * devC_params.rho_f * dx*dy*dz * phi; | |
+ //const Float3 f_g = MAKE_FLOAT3(0.0, 0.0, 0.0); | |
+ | |
+ // Find pressure gradient | |
+ Float3 grad_p = MAKE_FLOAT3(0.0, 0.0, 0.0); | |
+ | |
+ // The pressure gradient is not needed in Chorin's projection method | |
+ // (ns.beta=0), so only has to be looked up in pressure-dependant | |
+ // projection methods | |
+ Float3 pressure_term = MAKE_FLOAT3(0.0, 0.0, 0.0); | |
+ if (beta > 0.0) { | |
+ grad_p = gradient(dev_ns_p, x, y, z, dx, dy, dz); | |
+ pressure_term = -beta/devC_params.rho_f*grad_p*devC_dt/phi; | |
+ } | |
+ | |
+ // Calculate the predicted velocity | |
+ Float3 v_p = v | |
+ + pressure_term | |
+ + 1.0/devC_params.rho_f*div_phi_tau*devC_dt/phi | |
+ + devC_dt*(f_g) // uncomment this line to disable gravity | |
+ - devC_dt*(f_i) | |
+ - v*dphi/phi | |
+ - div_phi_vi_v*devC_dt/phi; | |
+ | |
+ // Report velocity components to stdout for debugging | |
+ /*const Float3 dv_pres = -ns.beta/devC_params.rho_f*grad_p*devC_dt/phi; | |
+ const Float3 dv_diff = 1.0/devC_params.rho_f*div_phi_tau*devC_dt/phi; | |
+ const Float3 dv_f = devC_dt*f_g; | |
+ const Float3 dv_dphi = -1.0*v*dphi/phi; | |
+ const Float3 dv_adv = -1.0*div_phi_vi_v*devC_dt/phi; | |
+ printf("[%d,%d,%d]\tv_p = %f\t%f\t%f\tdv_pres = %f\t%f\t%f\t" | |
+ "dv_diff = %f\t%f\t%f\tdv_f = %f\t%f\t%f\tv_dphi = %f\t%f\t%f\… | |
+ "dv_adv = %f\t%f\t%f\n", | |
+ x, y, z, v_p.x, v_p.y, v_p.z, | |
+ dv_pres.x, dv_pres.y, dv_pres.z, | |
+ dv_diff.x, dv_diff.y, dv_diff.z, | |
+ dv_f.x, dv_f.y, dv_f.z, | |
+ dv_dphi.x, dv_dphi.y, dv_dphi.z, | |
+ dv_adv.x, dv_adv.y, dv_adv.z);*/ | |
+ | |
+ // Enforce Neumann BC if specified | |
+ if ((z == 0 && bc_bot == 1) || (z == nz-1 && bc_top == 1)) | |
+ v_p.z = v.z; | |
+ //v_p.z = 0.0; | |
+ | |
+ // Save the predicted velocity | |
+ __syncthreads(); | |
+ dev_ns_v_p[cellidx] = v_p; | |
+ } | |
+} | |
+ | |
+// Find the value of the forcing function. Only grad(epsilon) changes during | |
+// the Jacobi iterations. The remaining, constant terms are only calculated | |
+// during the first iteration. | |
+// The forcing function is: | |
+// f = (div(v_p)*rho)/dt | |
+// + (grad(phi) dot v_p*rho)/(dt*phi) | |
+// + (dphi*rho)/(dt*dt*phi) | |
+// - (grad(phi) dot grad(epsilon))/phi | |
+// The following is calculated in the first Jacobi iteration and saved: | |
+// f1 = (div(v_p)*rho)/dt | |
+// + (grad(phi) dot v_p*rho)/(dt*phi) | |
+// + (dphi*rho)/(dt*dt*phi) | |
+// f2 = grad(phi)/phi | |
+// At each iteration, the value of the forcing function is found as: | |
+// f = f1 - f2 dot grad(epsilon) | |
+__global__ void findNSforcing( | |
+ Float* dev_ns_epsilon, | |
+ Float* dev_ns_f1, | |
+ Float3* dev_ns_f2, | |
+ Float* dev_ns_f, | |
+ Float* dev_ns_phi, | |
+ Float* dev_ns_dphi, | |
+ Float3* dev_ns_v_p, | |
+ unsigned int nijac) | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Grid dimensions | |
+ const unsigned int nx = devC_grid.num[0]; | |
+ const unsigned int ny = devC_grid.num[1]; | |
+ const unsigned int nz = devC_grid.num[2]; | |
+ | |
+ // Cell sizes | |
+ const Float dx = devC_grid.L[0]/nx; | |
+ const Float dy = devC_grid.L[1]/ny; | |
+ const Float dz = devC_grid.L[2]/nz; | |
+ | |
+ // 1D thread index | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ | |
+ | |
+ // Check that we are not outside the fluid grid | |
+ if (x < nx && y < ny && z < nz) { | |
+ | |
+ // Constant forcing function terms | |
+ Float f1; | |
+ Float3 f2; | |
+ | |
+ // Check if this is the first Jacobi iteration. If it is, find f1 and … | |
+ if (nijac == 0) { | |
+ | |
+ // Read needed values | |
+ __syncthreads(); | |
+ const Float3 v_p = dev_ns_v_p[cellidx]; | |
+ const Float phi = dev_ns_phi[cellidx]; | |
+ const Float dphi = dev_ns_dphi[cellidx]; | |
+ | |
+ // Calculate derivatives | |
+ const Float div_v_p | |
+ = divergence(dev_ns_v_p, x, y, z, dx, dy, dz); | |
+ const Float3 grad_phi | |
+ = gradient(dev_ns_phi, x, y, z, dx, dy, dz); | |
+ | |
+ // Find forcing function coefficients | |
+ //f1 = 0.0; | |
+ f1 = div_v_p*devC_params.rho_f/devC_dt | |
+ + dot(grad_phi, v_p)*devC_params.rho_f/(devC_dt*phi) | |
+ + dphi*devC_params.rho_f/(devC_dt*devC_dt*phi); | |
+ f2 = grad_phi/phi; | |
+ | |
+ // Report values terms in the forcing function for debugging | |
+ /* | |
+ const Float f1t1 = div_v_p*devC_params.rho_f/devC_dt; | |
+ const Float f1t2 = dot(grad_phi, v_p)*devC_params.rho_f/(devC_dt*p… | |
+ const Float f1t3 = dphi*devC_params.rho_f/(devC_dt*devC_dt*phi); | |
+ printf("[%d,%d,%d] f1 = %f\t" | |
+ "f1t1 = %f\tf1t2 = %f\tf1t3 = %f\tf2 = %f\n", | |
+ x,y,z, f1, f1t1, f1t2, f1t3, f2); | |
+ printf("[%d,%d,%d] v_p = %f\tdiv_v_p = %f\tgrad_phi = %f,%f,%f\t" | |
+ "phi = %f\tdphi = %f\n", | |
+ x,y,z, v_p, div_v_p, grad_phi.x, grad_phi.y, grad_phi.z, | |
+ phi, dphi); | |
+ | |
+ const Float phi_xn = dev_ns_phi[idx(x-1,y,z)]; | |
+ const Float phi_xp = dev_ns_phi[idx(x+1,y,z)]; | |
+ const Float phi_yn = dev_ns_phi[idx(x,y-1,z)]; | |
+ const Float phi_yp = dev_ns_phi[idx(x,y+1,z)]; | |
+ const Float phi_zn = dev_ns_phi[idx(x,y,z-1)]; | |
+ const Float phi_zp = dev_ns_phi[idx(x,y,z+1)]; | |
+ | |
+ printf("[%d,%d,%d] phi: " | |
+ "xn = %f\t" | |
+ "xp = %f\t" | |
+ "yn = %f\t" | |
+ "yp = %f\t" | |
+ "zn = %f\t" | |
+ "zp = %f\n", | |
+ x,y,z, phi_xn, phi_xp, phi_yn, phi_yp, phi_zn, phi_zp);*/ | |
+ | |
+ // Save values | |
+ __syncthreads(); | |
+ dev_ns_f1[cellidx] = f1; | |
+ dev_ns_f2[cellidx] = f2; | |
+ | |
+ } else { | |
+ | |
+ // Read previously found values | |
+ __syncthreads(); | |
+ f1 = dev_ns_f1[cellidx]; | |
+ f2 = dev_ns_f2[cellidx]; | |
+ } | |
+ | |
+ // Find the gradient of epsilon, which changes during Jacobi iterations | |
+ // TODO: Should the gradient of epsilon also be fixed according to BC's | |
+ // here? | |
+ const Float3 grad_epsilon | |
+ = gradient(dev_ns_epsilon, x, y, z, dx, dy, dz); | |
+ | |
+ // Forcing function value | |
+ const Float f = f1 - dot(f2, grad_epsilon); | |
+ //printf("[%d,%d,%d]\tf1 = %f\tf2 = %f\tf = %f\n", x,y,z, f1, f2, f); | |
+ | |
+ // Save forcing function value | |
+ __syncthreads(); | |
+ dev_ns_f[cellidx] = f; | |
+ } | |
+} | |
+ | |
+// Spatial smoothing, used for the epsilon values. If there are several blocks, | |
+// there will be small errors at the block boundaries, since the update will m… | |
+// non-smoothed and smoothed values. | |
+template<typename T> | |
+__global__ void smoothing( | |
+ T* dev_arr, | |
+ const Float gamma, | |
+ const unsigned int bc_bot, | |
+ const unsigned int bc_top) | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Grid dimensions | |
+ const unsigned int nx = devC_grid.num[0]; | |
+ const unsigned int ny = devC_grid.num[1]; | |
+ const unsigned int nz = devC_grid.num[2]; | |
+ | |
+ // 1D thread index | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ | |
+ // Perform the epsilon updates for all non-ghost nodes except the | |
+ // Dirichlet boundaries at z=0 and z=nz-1. | |
+ // Adjust z range if a boundary has the Dirichlet boundary condition. | |
+ int z_min = 0; | |
+ int z_max = nz-1; | |
+ if (bc_bot == 0) | |
+ z_min = 1; | |
+ if (bc_top == 0) | |
+ z_max = nz-2; | |
+ | |
+ if (x < nx && y < ny && z >= z_min && z <= z_max) { | |
+ | |
+ __syncthreads(); | |
+ const T e_xn = dev_arr[idx(x-1,y,z)]; | |
+ const T e = dev_arr[cellidx]; | |
+ const T e_xp = dev_arr[idx(x+1,y,z)]; | |
+ const T e_yn = dev_arr[idx(x,y-1,z)]; | |
+ const T e_yp = dev_arr[idx(x,y+1,z)]; | |
+ const T e_zn = dev_arr[idx(x,y,z-1)]; | |
+ const T e_zp = dev_arr[idx(x,y,z+1)]; | |
+ | |
+ const T e_avg_neigbors = 1.0/6.0 * | |
+ (e_xn + e_xp + e_yn + e_yp + e_zn + e_zp); | |
+ | |
+ const T e_smooth = (1.0 - gamma)*e + gamma*e_avg_neigbors; | |
+ | |
+ __syncthreads(); | |
+ dev_arr[cellidx] = e_smooth; | |
+ | |
+ //printf("%d,%d,%d\te = %f e_smooth = %f\n", x,y,z, e, e_smooth); | |
+ /*printf("%d,%d,%d\te_xn = %f, e_xp = %f, e_yn = %f, e_yp = %f," | |
+ " e_zn = %f, e_zp = %f\n", x,y,z, e_xn, e_xp, | |
+ e_yn, e_yp, e_zn, e_zp);*/ | |
+ } | |
+} | |
+ | |
+// Perform a single Jacobi iteration | |
+__global__ void jacobiIterationNS( | |
+ const Float* dev_ns_epsilon, | |
+ Float* dev_ns_epsilon_new, | |
+ Float* dev_ns_norm, | |
+ const Float* dev_ns_f, | |
+ const int bc_bot, | |
+ const int bc_top, | |
+ const Float theta) | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Grid dimensions | |
+ const unsigned int nx = devC_grid.num[0]; | |
+ const unsigned int ny = devC_grid.num[1]; | |
+ const unsigned int nz = devC_grid.num[2]; | |
+ | |
+ // Cell sizes | |
+ const Float dx = devC_grid.L[0]/nx; | |
+ const Float dy = devC_grid.L[1]/ny; | |
+ const Float dz = devC_grid.L[2]/nz; | |
+ | |
+ // 1D thread index | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ | |
+ // Check that we are not outside the fluid grid | |
+ //if (x < nx && y < ny && z < nz) { | |
+ | |
+ // internal nodes only | |
+ //if (x > 0 && x < nx-1 && y > 0 && y < ny-1 && z > 0 && z < nz-1) { | |
+ | |
+ // Lower boundary: Dirichlet. Upper boundary: Dirichlet | |
+ //if (x < nx && y < ny && z > 0 && z < nz-1) { | |
+ | |
+ // Lower boundary: Neumann. Upper boundary: Dirichlet | |
+ //if (x < nx && y < ny && z < nz-1) { | |
+ | |
+ // Perform the epsilon updates for all non-ghost nodes except the Dirichlet | |
+ // boundaries at z=0 and z=nz-1. | |
+ // Adjust z range if a boundary has the Dirichlet boundary condition. | |
+ int z_min = 0; | |
+ int z_max = nz-1; | |
+ if (bc_bot == 0) | |
+ z_min = 1; | |
+ if (bc_top == 0) | |
+ z_max = nz-2; | |
+ | |
+ if (x < nx && y < ny && z >= z_min && z <= z_max) { | |
+ | |
+ // Read the epsilon values from the cell and its 6 neighbors | |
+ __syncthreads(); | |
+ const Float e_xn = dev_ns_epsilon[idx(x-1,y,z)]; | |
+ const Float e = dev_ns_epsilon[cellidx]; | |
+ const Float e_xp = dev_ns_epsilon[idx(x+1,y,z)]; | |
+ const Float e_yn = dev_ns_epsilon[idx(x,y-1,z)]; | |
+ const Float e_yp = dev_ns_epsilon[idx(x,y+1,z)]; | |
+ const Float e_zn = dev_ns_epsilon[idx(x,y,z-1)]; | |
+ const Float e_zp = dev_ns_epsilon[idx(x,y,z+1)]; | |
+ | |
+ // Read the value of the forcing function | |
+ const Float f = dev_ns_f[cellidx]; | |
+ | |
+ // New value of epsilon in 3D update, derived by rearranging the | |
+ // discrete Laplacian | |
+ const Float dxdx = dx*dx; | |
+ const Float dydy = dy*dy; | |
+ const Float dzdz = dz*dz; | |
+ Float e_new | |
+ = (-dxdx*dydy*dzdz*f | |
+ + dydy*dzdz*(e_xn + e_xp) | |
+ + dxdx*dzdz*(e_yn + e_yp) | |
+ + dxdx*dydy*(e_zn + e_zp)) | |
+ /(2.0*(dxdx*dydy + dxdx*dzdz + dydy*dzdz)); | |
+ | |
+ // New value of epsilon in 1D update | |
+ //const Float e_new = (e_zp + e_zn - dz*dz*f)/2.0; | |
+ | |
+ // Print values for debugging | |
+ /*printf("[%d,%d,%d]\t e = %f\tf = %f\te_new = %f\n", | |
+ x,y,z, e, f, e_new);*/ | |
+ | |
+ const Float res_norm = (e_new - e)*(e_new - e)/(e_new*e_new + 1.0e-16); | |
+ const Float e_relax = e*(1.0-theta) + e_new*theta; | |
+ | |
+ __syncthreads(); | |
+ dev_ns_epsilon_new[cellidx] = e_relax; | |
+ dev_ns_norm[cellidx] = res_norm; | |
+ } | |
+} | |
+ | |
+// Copy all values from one array to the other | |
+template<typename T> | |
+__global__ void copyValues( | |
+ T* dev_read, | |
+ T* dev_write) | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Internal nodes only | |
+ if (x < devC_grid.num[0] && y < devC_grid.num[1] && z < devC_grid.num[2]) { | |
+ | |
+ // Internal nodes + ghost nodes | |
+ /*if (x <= devC_grid.num[0]+1 && | |
+ y <= devC_grid.num[1]+1 && | |
+ z <= devC_grid.num[2]+1) {*/ | |
+ | |
+ const unsigned int cellidx = idx(x,y,z); // without ghost nodes | |
+ //const unsigned int cellidx = idx(x-1,y-1,z-1); // with ghost nodes | |
+ | |
+ // Read | |
+ __syncthreads(); | |
+ const T val = dev_read[cellidx]; | |
+ | |
+ //if (z == devC_grid.num[2]-1) | |
+ //printf("[%d,%d,%d] = %f\n", x, y, z, val); | |
+ | |
+ // Write | |
+ __syncthreads(); | |
+ dev_write[cellidx] = val; | |
+ } | |
+} | |
+ | |
+// Find and store the normalized residuals | |
+__global__ void findNormalizedResiduals( | |
+ Float* dev_ns_epsilon_old, | |
+ Float* dev_ns_epsilon, | |
+ Float* dev_ns_norm, | |
+ const unsigned int bc_bot, | |
+ const unsigned int bc_top) | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Grid dimensions | |
+ const unsigned int nx = devC_grid.num[0]; | |
+ const unsigned int ny = devC_grid.num[1]; | |
+ const unsigned int nz = devC_grid.num[2]; | |
+ | |
+ // 1D thread index | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ | |
+ // Perform the epsilon updates for all non-ghost nodes except the | |
+ // Dirichlet boundaries at z=0 and z=nz-1. | |
+ // Adjust z range if a boundary has the Dirichlet boundary condition. | |
+ int z_min = 0; | |
+ int z_max = nz-1; | |
+ if (bc_bot == 0) | |
+ z_min = 1; | |
+ if (bc_top == 0) | |
+ z_max = nz-2; | |
+ | |
+ if (x < nx && y < ny && z >= z_min && z <= z_max) { | |
+ | |
+ __syncthreads(); | |
+ const Float e = dev_ns_epsilon_old[cellidx]; | |
+ const Float e_new = dev_ns_epsilon[cellidx]; | |
+ | |
+ // Find the normalized residual value. A small value is added to the | |
+ // denominator to avoid a divide by zero. | |
+ const Float res_norm = (e_new - e)*(e_new - e)/(e_new*e_new + 1.0e-16); | |
+ | |
+ __syncthreads(); | |
+ dev_ns_norm[cellidx] = res_norm; | |
+ } | |
+} | |
+ | |
+ | |
+// Computes the new velocity and pressure using the corrector | |
+__global__ void updateNSvelocityPressure( | |
+ Float* dev_ns_p, | |
+ Float3* dev_ns_v, | |
+ Float3* dev_ns_v_p, | |
+ Float* dev_ns_epsilon, | |
+ Float beta, | |
+ int bc_bot, | |
+ int bc_top) | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Grid dimensions | |
+ const unsigned int nx = devC_grid.num[0]; | |
+ const unsigned int ny = devC_grid.num[1]; | |
+ const unsigned int nz = devC_grid.num[2]; | |
+ | |
+ // Cell sizes | |
+ const Float dx = devC_grid.L[0]/nx; | |
+ const Float dy = devC_grid.L[1]/ny; | |
+ const Float dz = devC_grid.L[2]/nz; | |
+ | |
+ // 1D thread index | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ | |
+ // Check that we are not outside the fluid grid | |
+ if (x < nx && y < ny && z < nz) { | |
+ | |
+ // Read values | |
+ __syncthreads(); | |
+ const Float p_old = dev_ns_p[cellidx]; | |
+ const Float epsilon = dev_ns_epsilon[cellidx]; | |
+ const Float3 v_p = dev_ns_v_p[cellidx]; | |
+ | |
+ // New pressure | |
+ Float p = beta*p_old + epsilon; | |
+ | |
+ // Find corrector gradient | |
+ const Float3 grad_epsilon | |
+ = gradient(dev_ns_epsilon, x, y, z, dx, dy, dz); | |
+ | |
+ // Find new velocity | |
+ Float3 v = v_p - devC_dt/devC_params.rho_f*grad_epsilon; | |
+ | |
+ // Print values for debugging | |
+ /* if (z == 0) { | |
+ Float e_up = dev_ns_epsilon[idx(x,y,z+1)]; | |
+ Float e_down = dev_ns_epsilon[idx(x,y,z-1)]; | |
+ printf("[%d,%d,%d]\tgrad_e = %f,%f,%f\te_up = %f\te_down = %f\n", | |
+ x,y,z, | |
+ grad_epsilon.x, | |
+ grad_epsilon.y, | |
+ grad_epsilon.z, | |
+ e_up, | |
+ e_down); | |
+ }*/ | |
+ | |
+ //if ((z == 0 && bc_bot == 1) || (z == nz-1 && bc_top == 1)) | |
+ //v.z = 0.0; | |
+ | |
+ // Write new values | |
+ __syncthreads(); | |
+ dev_ns_p[cellidx] = p; | |
+ //dev_ns_p[cellidx] = epsilon; | |
+ dev_ns_v[cellidx] = v; | |
+ } | |
+} | |
+ | |
+// Find the average particle diameter and velocity for each CFD cell. | |
+// UNUSED: The values are estimated in the porosity estimation function instead | |
+__global__ void findAvgParticleVelocityDiameter( | |
+ unsigned int* dev_cellStart, // in | |
+ unsigned int* dev_cellEnd, // in | |
+ Float4* dev_vel_sorted, // in | |
+ Float4* dev_x_sorted, // in | |
+ Float3* dev_ns_vp_avg, // out | |
+ Float* dev_ns_d_avg) // out | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // check that we are not outside the fluid grid | |
+ if (x < devC_grid.num[0] && y < devC_grid.num[1] && z < devC_grid.num[2]) { | |
+ | |
+ Float4 v; | |
+ Float d; | |
+ unsigned int startIdx, endIdx, i; | |
+ unsigned int n = 0; | |
+ | |
+ // average particle velocity | |
+ Float3 v_avg = MAKE_FLOAT3(0.0, 0.0, 0.0); | |
+ | |
+ // average particle diameter | |
+ Float d_avg = 0.0; | |
+ | |
+ const unsigned int cellID = x + y * devC_grid.num[0] | |
+ + (devC_grid.num[0] * devC_grid.num[1]) * z; | |
+ | |
+ // Lowest particle index in cell | |
+ startIdx = dev_cellStart[cellID]; | |
+ | |
+ // Make sure cell is not empty | |
+ if (startIdx != 0xffffffff) { | |
+ | |
+ // Highest particle index in cell | |
+ endIdx = dev_cellEnd[cellID]; | |
+ | |
+ // Iterate over cell particles | |
+ for (i=startIdx; i<endIdx; ++i) { | |
+ | |
+ // Read particle velocity | |
+ __syncthreads(); | |
+ v = dev_vel_sorted[i]; | |
+ d = 2.0*dev_x_sorted[i].w; | |
+ n++; | |
+ v_avg += MAKE_FLOAT3(v.x, v.y, v.z); | |
+ d_avg += d; | |
+ } | |
+ | |
+ v_avg /= n; | |
+ d_avg /= n; | |
+ } | |
+ | |
+ // save average radius and velocity | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ __syncthreads(); | |
+ dev_ns_vp_avg[cellidx] = v_avg; | |
+ dev_ns_d_avg[cellidx] = d_avg; | |
+ } | |
+} | |
+ | |
+// Find the drag coefficient as dictated by the Reynold's number | |
+// Shamy and Zeghal (2005). | |
+__device__ Float dragCoefficient(Float re) | |
+{ | |
+ Float cd; | |
+ if (re >= 1000.0) | |
+ cd = 0.44; | |
+ else | |
+ cd = 24.0/re*(1.0 + 0.15*pow(re, 0.687)); | |
+ return cd; | |
+} | |
+ | |
+// Determine the fluid-particle interaction drag force based on the Ergun (195… | |
+// equation for dense packed cells (phi <= 0.8), and the Wen and Yu (1966) | |
+// equation for dilate suspensions (phi > 0.8). Procedure outlined in Shamy and | |
+// Zeghal (2005) and Goniva et al (2010). | |
+// Other interaction forces, such as the pressure gradient in the flow field | |
+// (pressure force), particle rotation (Magnus force), particle acceleration | |
+// (virtual mass force) or a fluid velocity gradient leading to shear (Saffman | |
+// force). | |
+__global__ void findInteractionForce( | |
+ Float* dev_ns_phi, // in | |
+ Float* dev_ns_d_avg, // in | |
+ Float3* dev_ns_vp_avg, // in | |
+ Float3* dev_ns_v, // in | |
+ Float3* dev_ns_fi) // out | |
+{ | |
+ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Check that we are not outside the fluid grid | |
+ if (x < devC_grid.num[0] && y < devC_grid.num[1] && z < devC_grid.num[2]) { | |
+ | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ | |
+ __syncthreads(); | |
+ const Float phi = dev_ns_phi[cellidx]; | |
+ const Float3 vf_avg = dev_ns_v[cellidx]; | |
+ | |
+ Float d_avg; | |
+ Float3 vp_avg; | |
+ if (phi < 0.999) { | |
+ __syncthreads(); | |
+ d_avg = dev_ns_d_avg[cellidx]; | |
+ vp_avg = dev_ns_vp_avg[cellidx]; | |
+ } else { // cell is empty | |
+ d_avg = 1.0; // some value different from 0 | |
+ vp_avg = vf_avg; | |
+ } | |
+ | |
+ const Float3 v_rel = vf_avg - vp_avg; | |
+ const Float v_rel_length = length(v_rel); | |
+ | |
+ const Float not_phi = 1.0 - phi; | |
+ const Float re = (phi*devC_params.rho_f*d_avg)/devC_params.mu * v_rel_… | |
+ const Float cd = dragCoefficient(re); | |
+ | |
+ Float3 fi = MAKE_FLOAT3(0.0, 0.0, 0.0); | |
+ if (v_rel_length > 0.0) { | |
+ if (phi <= 0.8) // Ergun equation | |
+ fi = (150.0*devC_params.mu*not_phi*not_phi/(phi*d_avg*d_avg) | |
+ + 1.75*not_phi*devC_params.rho_f*v_rel_length/d_avg)*v… | |
+ else if (phi < 0.999) // Wen and Yu equation | |
+ fi = (3.0/4.0*cd*not_phi*pow(phi, | |
+ -2.65)*devC_params.mu*devC_params.rho_f | |
+ *v_rel_length/d_avg)*v_rel; | |
+ } | |
+ | |
+ /*printf("%d,%d,%d\tfi = %f,%f,%f" | |
+ "\tphi = %f\td_avg = %f" | |
+ "\tv_rel = %f,%f,%f\t" | |
+ "\tre = %f\tcd = %f\n", | |
+ x,y,z, fi.x, fi.y, fi.z, | |
+ phi, d_avg, | |
+ v_rel.x, v_rel.y, v_rel.z, | |
+ re, cd);*/ | |
+ | |
+ __syncthreads(); | |
+ dev_ns_fi[cellidx] = fi; | |
+ } | |
+} | |
+ | |
+// Apply the fluid-particle interaction force to all particles in each fluid | |
+// cell. | |
+__global__ void applyParticleInteractionForce( | |
+ Float3* dev_ns_fi, // in | |
+ Float* dev_ns_phi, // in | |
+ unsigned int* dev_gridParticleIndex, // in | |
+ unsigned int* dev_cellStart, // in | |
+ unsigned int* dev_cellEnd, // in | |
+ Float4* dev_x_sorted, // in | |
+ Float4* dev_force) // out | |
+{ | |
+ // 3D thread index | |
+ const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; | |
+ const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; | |
+ const unsigned int z = blockDim.z * blockIdx.z + threadIdx.z; | |
+ | |
+ // Check that we are not outside the fluid grid | |
+ if (x < devC_grid.num[0] && y < devC_grid.num[1] && z < devC_grid.num[2]) { | |
+ | |
+ const unsigned int cellidx = idx(x,y,z); | |
+ | |
+ __syncthreads(); | |
+ const Float3 fi = dev_ns_fi[cellidx]; | |
+ | |
+ // apply to all particle in the cell | |
+ // Calculate linear cell ID | |
+ const unsigned int cellID = x + y * devC_grid.num[0] | |
+ + (devC_grid.num[0] * devC_grid.num[1]) * z; | |
+ | |
+ const unsigned int startidx = dev_cellStart[cellID]; | |
+ unsigned int endidx, i, origidx; | |
+ | |
+ Float r, phi; | |
+ Float3 fd; | |
+ | |
+ if (startidx != 0xffffffff) { | |
+ | |
+ __syncthreads(); | |
+ endidx = dev_cellEnd[cellID]; | |
+ | |
+ for (i=startidx; i<endidx; ++i) { | |
+ | |
+ __syncthreads(); | |
+ origidx = dev_gridParticleIndex[i]; | |
+ r = dev_x_sorted[i].w; // radius | |
+ phi = dev_ns_phi[idx(x,y,z)]; | |
+ | |
+ // this term could include the pressure gradient | |
+ //fd = (-grad_p + fi/(1.0 - phi))*(4.0/3.0*M_PI*r*r*r); | |
+ fd = (fi/(1.0 - phi))*(4.0/3.0*M_PI*r*r*r); | |
+ | |
+ __syncthreads(); | |
+ dev_force[origidx] += MAKE_FLOAT4(fd.x, fd.y, fd.z, 0.0); | |
+ | |
+ // disable fluid->particle interaction | |
+ //dev_force[origidx] += MAKE_FLOAT4(0.0, 0.0, 0.0, 0.0); | |
+ | |
+ // report to stdout | |
+ //printf("%d,%d,%d\tapplying force (%f,%f,%f) to particle %d\n… | |
+ //x,y,z, fd.x, fd.y, fd.z, origidx); | |
+ } | |
+ } | |
+ } | |
+} | |
+ | |
+ | |
+// Print final heads and free memory | |
+void DEM::endNSdev() | |
+{ | |
+ freeNSmemDev(); | |
+} | |
+ | |
+// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 | |
diff --git a/src/navierstokes_solver_parameters.h b/src/navierstokes_solver_par… | |
t@@ -0,0 +1,46 @@ | |
+#ifndef NAVIERSTOKES_SOLVER_PARAMETERS_H_ | |
+#define NAVIERSTOKES_SOLVER_PARAMETERS_H_ | |
+ | |
+//// Parameters for the iterative Jacobi solver | |
+ | |
+// Define `CFDDEMCOUPLING` in order to enable the two-way coupling between the | |
+// fluid and particle phase. | |
+#define CFDDEMCOUPLING | |
+ | |
+// Solver parameter, used in velocity prediction and pressure iteration | |
+// 1.0: Use old pressures for fluid velocity prediction (see Langtangen et al. | |
+// 2002) | |
+// 0.0: Do not use old pressures for fluid velocity prediction (Chorin's | |
+// original projection method, see Chorin (1968) and "Projection method (fluid | |
+// dynamics)" page on Wikipedia. | |
+// The best results precision and performance-wise are obtained by using BETA=0 | |
+// and a very low tolerance criteria value (e.g. 1.0e-9) | |
+//#define BETA 0.0 | |
+ | |
+// Under-relaxation parameter, used in solution of Poisson equation. The value | |
+// should be within the range ]0.0;1.0]. At a value of 1.0, the new estimate of | |
+// epsilon values is used exclusively. At lower values, a linear interpolation | |
+// between new and old values is used. The solution typically converges faster | |
+// with a value of 1.0, but instabilities may be avoided with lower values. | |
+//#define THETA 1.0 | |
+ | |
+// Smoothing parameter. The epsilon (pressure) values are smoothed by including | |
+// the average epsilon value of the six closest (face) neighbor cells. This | |
+// parameter should be in the range [0.0;1.0[. The higher the value, the more | |
+// averaging is introduced. A value of 0.0 disables all averaging. | |
+//#define GAMMA 0.5 | |
+//#define GAMMA 0.0 | |
+ | |
+// Tolerance criteria for the normalized residual | |
+//const double tolerance = 1.0e-3; | |
+//const double tolerance = 1.0e-4; | |
+//const double tolerance = 1.0e-5; | |
+//const double tolerance = 1.0e-7; | |
+//const double tolerance = 1.0e-8; | |
+//const double tolerance = 1.0e-9; | |
+ | |
+// The maximum number of iterations to perform | |
+//const unsigned int maxiter = 1e4; | |
+ | |
+ | |
+#endif | |
diff --git a/src/porosity.cpp b/src/porosity.cpp | |
t@@ -1,14 +1,3 @@ | |
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
-/* SPHERE source code by Anders Damsgaard Christensen, 2010-12, */ | |
-/* a 3D Discrete Element Method algorithm with CUDA GPU acceleration. */ | |
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
- | |
-// Licence: GNU Public License (GPL) v. 3. See license.txt. | |
-// See doc/sphere-doc.pdf for full documentation. | |
-// Compile with GNU make by typing 'make' in the src/ directory. | |
-// SPHERE is called from the command line with './sphere_<architecture> projec… | |
- | |
- | |
// Including library files | |
#include <iostream> | |
#include <string> | |
t@@ -40,19 +29,21 @@ int main(const int argc, const char *argv[]) | |
// Display help if requested | |
if (argvi == "-h" || argvi == "--help") { | |
std::cout << argv[0] << ": sphere porosity calculator\n" | |
- << "Usage: " << argv[0] << " [OPTION[S]]... [FILE1 ...]\nOptio… | |
- << "-h, --help\t\tprint help\n" | |
- << "-V, --version\t\tprint version information and exit\n" | |
- << "-v, --verbose\t\tdisplay in-/output file names\n" | |
- << "-s. --slices\t\tnumber of vertical slices to find porosity… | |
- << "The porosity values are stored in the output/ folder" | |
+ "Usage: " << argv[0] << " [OPTION[S]]... [FILE1 ...]\n" | |
+ "Options:\n" | |
+ "-h, --help\t\tprint help\n" | |
+ "-V, --version\t\tprint version information and exit\n" | |
+ "-v, --verbose\t\tdisplay in-/output file names\n" | |
+ "-s. --slices\t\tnumber of vertical slices to find porosity " | |
+ "within\n" | |
+ "The porosity values are stored in the output/ folder" | |
<< std::endl; | |
return 0; // Exit with success | |
} | |
// Display version with fancy ASCII art | |
else if (argvi == "-V" || argvi == "--version") { | |
- std::cout << "Porosity calculator, sphere version " << VERS | |
+ std::cout << "Porosity calculator, sphere version " << VERSION | |
<< std::endl; | |
return 0; | |
} | |
t@@ -63,8 +54,8 @@ int main(const int argc, const char *argv[]) | |
else if (argvi == "-s" || argvi == "--slices") { | |
slices = atoi(argv[++i]); | |
if (slices < 1) { | |
- std::cerr << "Error: The number of slices must be a positive, … | |
- << slices << ")" << std::endl; | |
+ std::cerr << "Error: The number of slices must be a positive, " | |
+ "real number (was " << slices << ")" << std::endl; | |
return 1; | |
} | |
} | |
t@@ -74,7 +65,8 @@ int main(const int argc, const char *argv[]) | |
nfiles++; | |
if (verbose == 1) | |
- std::cout << argv[0] << ": processing input file: " << argvi <… | |
+ std::cout << argv[0] << ": processing input file: " << argvi << | |
+ std::endl; | |
// Create DEM class, read data from input binary, check values | |
DEM dem(argvi, verbose, 0, 0, 0, 0); | |
diff --git a/src/porousflow.cpp b/src/porousflow.cpp | |
t@@ -51,6 +51,9 @@ int main(const int argc, const char *argv[]) | |
else if (argvi == "-n" || argvi == "--dry") | |
dry = 1; | |
+ else if (argvi == "-q" || argvi == "--quiet") | |
+ verbose = 0; | |
+ | |
// The rest of the values must be input binary files | |
else { | |
nfiles++; | |
diff --git a/src/sorting.cuh b/src/sorting.cuh | |
t@@ -4,7 +4,7 @@ | |
// Returns the cellID containing the particle, based cubic grid | |
// See Bayraktar et al. 2009 | |
// Kernel is executed on the device, and is callable from the device only | |
-__device__ int calcCellID(Float3 x) | |
+__device__ unsigned int calcCellID(Float3 x) | |
{ | |
unsigned int i_x, i_y, i_z; | |
t@@ -14,7 +14,8 @@ __device__ int calcCellID(Float3 x) | |
i_z = floor((x.z - devC_grid.origo[2]) / (devC_grid.L[2]/devC_grid.num[2])… | |
// Integral coordinates are converted to 1D coordinate: | |
- return (i_z * devC_grid.num[1]) * devC_grid.num[0] + i_y * devC_grid.num[0… | |
+ return (i_z * devC_grid.num[1]) | |
+ * devC_grid.num[0] + i_y * devC_grid.num[0] + i_x; | |
} // End of calcCellID(...) | |
t@@ -25,7 +26,6 @@ __global__ void calcParticleCellID(unsigned int* dev_gridPar… | |
unsigned int* dev_gridParticleIndex, | |
Float4* dev_x) | |
{ | |
- //unsigned int idx = threadIdx.x + blockIdx.x * blockDim.x; // Thread id | |
unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x; | |
if (idx < devC_np) { // Condition prevents block size error | |
t@@ -35,7 +35,15 @@ __global__ void calcParticleCellID(unsigned int* dev_gridPa… | |
unsigned int cellID = calcCellID(MAKE_FLOAT3(x.x, x.y, x.z)); | |
+ // Check for NaN | |
+ if (x.x != x.x || x.y != x.y || x.z != x.z) | |
+ printf("\ncalcParticleCellID: Error! NaN encountered. " | |
+ "idx = %d: cellID = %d, " | |
+ "x = %f,%f,%f\n", | |
+ idx, cellID, x.x, x.y, x.z); | |
+ | |
// Store values | |
+ __syncthreads(); | |
dev_gridParticleCellID[idx] = cellID; | |
dev_gridParticleIndex[idx] = idx; | |
t@@ -43,9 +51,8 @@ __global__ void calcParticleCellID(unsigned int* dev_gridPar… | |
} // End of calcParticleCellID(...) | |
-// Reorder particle data into sorted order, and find the start and end particl… | |
-// of each cell in the sorted hash array. | |
-// Kernel executed on device, and callable from host only. | |
+// Reorder particle data into sorted order, and find the start and end particle | |
+// indexes of each cell in the sorted hash array. | |
__global__ void reorderArrays(unsigned int* dev_cellStart, | |
unsigned int* dev_cellEnd, | |
unsigned int* dev_gridParticleCellID, | |
t@@ -75,7 +82,8 @@ __global__ void reorderArrays(unsigned int* dev_cellStart, | |
if (idx < devC_np) { // Condition prevents block size error | |
cellID = dev_gridParticleCellID[idx]; | |
- // Load hash data into shared memory, allowing access to neighbor part… | |
+ // Load hash data into shared memory, allowing access to neighbor | |
+ // particle cellID values | |
shared_data[tidx+1] = cellID; | |
if (idx > 0 && tidx == 0) { | |
t@@ -83,6 +91,8 @@ __global__ void reorderArrays(unsigned int* dev_cellStart, | |
shared_data[0] = dev_gridParticleCellID[idx-1]; | |
} | |
} | |
+ //if (cellID != 0) | |
+ //printf("reorderArrays: %d,%d\tcellID = %d\n", tidx, idx, cellID); | |
// Pause completed threads in this block, until all | |
// threads are done loading data into shared memory | |
t@@ -90,9 +100,10 @@ __global__ void reorderArrays(unsigned int* dev_cellStart, | |
// Find lowest and highest particle index in each cell | |
if (idx < devC_np) { // Condition prevents block size error | |
- // If this particle has a different cell index to the previous particl… | |
- // particle in the cell -> Store the index of this particle in the cel… | |
- // The previous particle must be the last particle in the previous cel… | |
+ // If this particle has a different cell index to the previous particl… | |
+ // it's the first particle in the cell -> Store the index of this | |
+ // particle in the cell. The previous particle must be the last partic… | |
+ // in the previous cell. | |
if (idx == 0 || cellID != shared_data[tidx]) { | |
dev_cellStart[cellID] = idx; | |
if (idx > 0) | |
t@@ -120,6 +131,5 @@ __global__ void reorderArrays(unsigned int* dev_cellStart, | |
} | |
} // End of reorderArrays(...) | |
- | |
#endif | |
// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 | |
diff --git a/src/sphere.cpp b/src/sphere.cpp | |
t@@ -19,8 +19,8 @@ DEM::DEM(const std::string inputbin, | |
const int dry, | |
const int initCuda, | |
const int transferConstMem, | |
- const int darcyflow) | |
-: verbose(verbosity), darcy(darcyflow) | |
+ const int porousflow) | |
+: verbose(verbosity), navierstokes(porousflow) | |
{ | |
using std::cout; | |
using std::cerr; | |
t@@ -49,8 +49,8 @@ DEM::DEM(const std::string inputbin, | |
if (dry == 1) | |
exit(0); | |
- if (params.nu > 0.0 && darcy == 1) { | |
- initDarcy(); | |
+ if (navierstokes == 1) { | |
+ initNS(); | |
} | |
if (initCuda == 1) { | |
t@@ -63,8 +63,8 @@ DEM::DEM(const std::string inputbin, | |
transferToConstantDeviceMemory(); | |
} | |
- if (params.nu > 0.0 && darcy == 1) { | |
- initDarcyMemDev(); | |
+ if (navierstokes == 1) { | |
+ initNSmemDev(); | |
} | |
// Allocate device memory for particle variables, | |
t@@ -99,17 +99,40 @@ DEM::~DEM(void) | |
std::cout << "Done" << std::endl; | |
} | |
+void checkIfNaN(Float3 vec, std::string description, unsigned int idx) | |
+{ | |
+ if (vec.x != vec.x || vec.y != vec.y || vec.z != vec.z) { | |
+ std::cerr << "Error: Particle " << idx << " has a " | |
+ << description << " with one or more NaN values: " | |
+ << vec.x << ", " << vec.y << ", " << vec.z << std::endl; | |
+ exit(1); | |
+ } | |
+} | |
+ | |
+void checkIfNaN(Float4 vec, std::string description, unsigned int idx) | |
+{ | |
+ if (vec.x != vec.x || vec.y != vec.y || | |
+ vec.z != vec.z || vec.w != vec.w) { | |
+ std::cerr << "Error: Particle " << idx << " has a " | |
+ << description << " with one or more NaN values: " | |
+ << vec.x << ", " << vec.y << ", " << vec.z << ", " << vec.w | |
+ << std::endl; | |
+ exit(1); | |
+ } | |
+} | |
+ | |
// Check numeric values of selected parameters | |
void DEM::checkValues(void) | |
{ | |
using std::cerr; | |
+ using std::endl; | |
unsigned int i; | |
// Check the number of dimensions | |
if (nd != ND) { | |
- cerr << "Error: nd = " << nd << ", ND = " << ND << '\n'; | |
+ cerr << "Error: nd = " << nd << ", ND = " << ND << endl; | |
exit(1); | |
} | |
t@@ -119,19 +142,19 @@ void DEM::checkValues(void) | |
exit(1); | |
} else if (NC < 8) { | |
cerr << "Warning: NC has a low value (" << NC << "). " | |
- << "Consider increasing it in 'constants.h'\n"; | |
+ << "Consider increasing it in 'constants.h'" << endl; | |
} | |
// Check that we have a positive number of particles | |
if (np < 1) { | |
- cerr << "Error: np = " << np << '\n'; | |
- exit(1); | |
+ cerr << "Warning: No particles are being simulated (np = " << np | |
+ << ")" << endl; | |
} | |
// Check that the current time | |
if (time.current > time.total || time.current < 0.0) { | |
cerr << "Error: time.current = " << time.current | |
- << " s, time.total = " << time.total << " s\n"; | |
+ << " s, time.total = " << time.total << " s" << endl; | |
exit(1); | |
} | |
t@@ -141,7 +164,7 @@ void DEM::checkValues(void) | |
<< "problems. \n" | |
<< "grid.origo[0] = " << grid.origo[0] << " m, " | |
<< "grid.origo[1] = " << grid.origo[1] << " m, " | |
- << "grid.origo[2] = " << grid.origo[2] << " m.\n"; | |
+ << "grid.origo[2] = " << grid.origo[2] << " m." << endl; | |
exit(1); | |
} | |
t@@ -149,7 +172,7 @@ void DEM::checkValues(void) | |
if (grid.L[0] <= 0.0 || grid.L[1] <= 0.0 || grid.L[2] <= 0.0) { | |
cerr << "Error: grid.L[0] = " << grid.L[0] << " m, " | |
<< "grid.L[1] = " << grid.L[1] << " m, " | |
- << "grid.L[2] = " << grid.L[2] << " m.\n"; | |
+ << "grid.L[2] = " << grid.L[2] << " m." << endl; | |
exit(1); | |
} | |
t@@ -157,38 +180,51 @@ void DEM::checkValues(void) | |
if (grid.num[0] <= 0 || grid.num[1] <= 0 || grid.num[2] <= 0) { | |
cerr << "Error: grid.num[0] = " << grid.num[0] << ", " | |
<< "grid.num[1] = " << grid.num[1] << ", " | |
- << "grid.num[2] = " << grid.num[2] << ".\n"; | |
+ << "grid.num[2] = " << grid.num[2] << "." << endl; | |
exit(1); | |
} | |
// Check grid size again | |
if (grid.periodic == 2 && grid.num[0] < 3) { | |
cerr << "Error: When 1st dimension boundaries are periodic, " | |
- << "there must be at least 3 cells in that dimension."; | |
+ << "there must be at least 3 cells in that dimension." << endl; | |
exit(1); | |
} | |
if (grid.periodic == 1 && (grid.num[0] < 3 || grid.num[1] < 3)) { | |
cerr << "Error: When 1st and 2nd dimension boundaries are periodic, " | |
- << "there must be at least 3 cells in each of those dimensions."; | |
+ << "there must be at least 3 cells in each of those dimensions." | |
+ << endl; | |
exit(1); | |
} | |
// Per-particle checks | |
- Float4 x; | |
+ Float4 x, vel, acc, angpos, angvel, angacc; | |
for (i = 0; i < np; ++i) { | |
- // Read value into register | |
+ // Read values. Accelerations can't be checked by default, since these | |
+ // aren't initialized when this function is run after reading the input | |
+ // file from disk | |
x = k.x[i]; | |
+ vel = k.vel[i]; | |
+ //acc = k.acc[i]; | |
+ angpos = k.angpos[i]; | |
+ angvel = k.angvel[i]; | |
+ //angacc = k.angacc[i]; | |
// Check that radii are positive values | |
if (x.w <= 0.0) { | |
cerr << "Error: Particle " << i << " has a radius of " | |
- << k.x[i].w << " m." << std::endl; | |
+ << k.x[i].w << " m." << endl; | |
exit(1); | |
} | |
+ checkIfNaN(x, "position", i); | |
+ checkIfNaN(vel, "velocity", i); | |
+ checkIfNaN(angpos, "angular position", i); | |
+ checkIfNaN(angvel, "angular velocity", i); | |
+ | |
// Check that all particles are inside of the grid | |
if (x.x < grid.origo[0] || | |
x.y < grid.origo[1] || | |
t@@ -210,7 +246,7 @@ void DEM::checkValues(void) | |
<< grid.L[0] << ", " | |
<< grid.L[1] << ", " | |
<< grid.L[2] << "]." | |
- << std::endl; | |
+ << endl; | |
exit(1); | |
} | |
t@@ -226,7 +262,7 @@ void DEM::checkValues(void) | |
if (walls.nx[0].w < z_max) { | |
cerr << "Error: One or more particles have centres above " | |
- << "the upper, dynamic wall"; | |
+ << "the upper, dynamic wall" << endl; | |
exit(1); | |
} | |
} | |
t@@ -235,16 +271,17 @@ void DEM::checkValues(void) | |
// Check constant, global parameters | |
if (params.k_n <= 0.0) { | |
- cerr << "Error: k_n = " << params.k_n << " N/m\n"; | |
+ cerr << "Error: k_n = " << params.k_n << " N/m" << endl; | |
exit(1); | |
} | |
if (params.rho <= 0.0) { | |
- cerr << "Error: rho = " << params.rho << " kg/m3\n"; | |
+ cerr << "Error: rho = " << params.rho << " kg/m3" << endl; | |
exit(1); | |
} | |
} | |
+ | |
// Report key parameter values to stdout | |
void DEM::reportValues() | |
{ | |
t@@ -252,8 +289,8 @@ void DEM::reportValues() | |
using std::cerr; | |
using std::endl; | |
- cout << " - Number of dimensions: nd = " << nd << "\n" | |
- << " - Number of particles: np = " << np << "\n"; | |
+ cout << " - Number of dimensions: nd = " << nd << '\n' | |
+ << " - Number of particles: np = " << np << endl; | |
// Check precision choice | |
cout << " - Compiled for "; | |
t@@ -264,7 +301,8 @@ void DEM::reportValues() | |
if (verbose == 1) | |
cout << "double"; | |
} else { | |
- cerr << "Error! Chosen precision not available. Check datatypes.h\n"; | |
+ cerr << "Error! Chosen precision not available. Check datatypes.h" | |
+ << endl; | |
exit(1); | |
} | |
cout << " precision\n"; | |
diff --git a/src/sphere.h b/src/sphere.h | |
t@@ -31,7 +31,7 @@ class DEM { | |
// HOST STRUCTURES | |
// Structure containing individual particle kinematics | |
- Kinematics k; // host | |
+ Kinematics k; | |
// Structure containing energy values | |
Energies e; | |
t@@ -55,55 +55,74 @@ class DEM { | |
// DEVICE ARRAYS | |
- Float4 *dev_x; | |
- Float2 *dev_xysum; | |
- Float4 *dev_vel; | |
- Float4 *dev_vel0; | |
- Float4 *dev_acc; | |
- Float4 *dev_force; | |
- Float4 *dev_angpos; | |
- Float4 *dev_angvel; | |
- Float4 *dev_angvel0; | |
- Float4 *dev_angacc; | |
- Float4 *dev_torque; | |
- unsigned int *dev_contacts; | |
- Float4 *dev_distmod; | |
- Float4 *dev_delta_t; | |
- Float *dev_es_dot; | |
- Float *dev_es; | |
- Float *dev_ev_dot; | |
- Float *dev_ev; | |
- Float *dev_p; | |
- Float4 *dev_x_sorted; | |
- Float4 *dev_vel_sorted; | |
- Float4 *dev_angvel_sorted; | |
- unsigned int *dev_gridParticleCellID; | |
- unsigned int *dev_gridParticleIndex; | |
- unsigned int *dev_cellStart; | |
- unsigned int *dev_cellEnd; | |
- int *dev_walls_wmode; | |
- Float4 *dev_walls_nx; // normal, pos. | |
- Float4 *dev_walls_mvfd; // Mass, velocity, force, dev. stress | |
- Float *dev_walls_force_partial; // Pre-sum per wall | |
- Float *dev_walls_force_pp; // Force per particle per wall | |
- Float *dev_walls_vel0; // Half-step velocity | |
- uint2 *dev_bonds; // Particle bond pairs | |
- Float4 *dev_bonds_delta; // Particle bond displacement | |
- Float4 *dev_bonds_omega; // Particle bond rotation | |
+ | |
+ // Particle kinematics arrays | |
+ Float4 *dev_x; | |
+ Float2 *dev_xysum; | |
+ Float4 *dev_vel; | |
+ Float4 *dev_vel0; | |
+ Float4 *dev_acc; | |
+ Float4 *dev_force; | |
+ Float4 *dev_angpos; | |
+ Float4 *dev_angvel; | |
+ Float4 *dev_angvel0; | |
+ Float4 *dev_angacc; | |
+ Float4 *dev_torque; | |
+ unsigned int *dev_contacts; | |
+ Float4 *dev_distmod; | |
+ Float4 *dev_delta_t; | |
+ Float *dev_es_dot; | |
+ Float *dev_es; | |
+ Float *dev_ev_dot; | |
+ Float *dev_ev; | |
+ Float *dev_p; | |
+ | |
+ // Sorted kinematics arrays | |
+ Float4 *dev_x_sorted; | |
+ Float4 *dev_vel_sorted; | |
+ Float4 *dev_angvel_sorted; | |
+ | |
+ // Sorting grid arrays | |
+ unsigned int *dev_gridParticleCellID; | |
+ unsigned int *dev_gridParticleIndex; | |
+ unsigned int *dev_cellStart; | |
+ unsigned int *dev_cellEnd; | |
+ | |
+ // Wall arrays | |
+ int *dev_walls_wmode; | |
+ Float4 *dev_walls_nx; // normal, pos. | |
+ Float4 *dev_walls_mvfd; // mass, velo., force, dev. stress | |
+ Float *dev_walls_force_partial; // Pre-sum per wall | |
+ Float *dev_walls_force_pp; // Force per particle per wall | |
+ Float *dev_walls_acc; // Wall acceleration | |
+ | |
+ // Bond arrays | |
+ uint2 *dev_bonds; // Particle bond pairs | |
+ Float4 *dev_bonds_delta; // Particle bond displacement | |
+ Float4 *dev_bonds_omega; // Particle bond rotation | |
+ | |
+ // Raytracer arrays | |
unsigned char *dev_img; | |
- float4 *dev_ray_origo; // Ray data always single precision | |
- float4 *dev_ray_direction; | |
+ float4 *dev_ray_origo; // Ray data always single precision | |
+ float4 *dev_ray_direction; | |
// GPU initialization, must be called before startTime() | |
- void initializeGPU(void); | |
+ void initializeGPU(); | |
// Copy all constant data to constant device memory | |
- void transferToConstantDeviceMemory(void); | |
- void rt_transferToConstantDeviceMemory(void); | |
+ void transferToConstantDeviceMemory(); | |
+ void rt_transferToConstantDeviceMemory(); | |
+ | |
+ // Check for CUDA errors | |
+ void checkForCudaErrors(const char* checkpoint_description, | |
+ const int run_diagnostics = 1); | |
+ void checkForCudaErrorsIter(const char* checkpoint_description, | |
+ const unsigned int iteration, | |
+ const int run_diagnostics = 1); | |
// Check values stored in constant device memory | |
- void checkConstantMemory(void); | |
+ void checkConstantMemory(); | |
// Initialize camera values and transfer to constant device memory | |
void cameraInit(const float3 eye, | |
t@@ -112,22 +131,22 @@ class DEM { | |
const float focalLength); | |
// Allocate global device memory to hold data | |
- void allocateGlobalDeviceMemory(void); | |
- void rt_allocateGlobalDeviceMemory(void); | |
+ void allocateGlobalDeviceMemory(); | |
+ void rt_allocateGlobalDeviceMemory(); | |
// Free dynamically allocated global device memory | |
- void freeGlobalDeviceMemory(void); | |
- void rt_freeGlobalDeviceMemory(void); | |
+ void freeGlobalDeviceMemory(); | |
+ void rt_freeGlobalDeviceMemory(); | |
// Copy non-constant data to global GPU memory | |
void transferToGlobalDeviceMemory(int status = 1); | |
// Copy non-constant data from global GPU memory to host RAM | |
- void transferFromGlobalDeviceMemory(void); | |
- void rt_transferFromGlobalDeviceMemory(void); | |
+ void transferFromGlobalDeviceMemory(); | |
+ void rt_transferFromGlobalDeviceMemory(); | |
// Find and return the max. radius | |
- Float r_max(void); | |
+ Float r_max(); | |
// Write porosities found in porosity() to text file | |
void writePorosities( | |
t@@ -144,101 +163,84 @@ class DEM { | |
Float4 *v_rho; // Fluid velocity v (xyz), and pressure rho (w) | |
Float4 *dev_v_rho; // Device equivalent | |
- //// Darcy-flow | |
- int darcy; // 0: no, 1: yes | |
- | |
- // Darcy values, host | |
- Darcy d; | |
- | |
- // Darcy values, device | |
- Float* dev_d_H; // Cell hydraulic heads | |
- Float* dev_d_H_new; // Cell hydraulic heads | |
- Float3* dev_d_V; // Cell fluid velocity | |
- Float3* dev_d_dH; // Cell spatial gradient in heads | |
- Float* dev_d_K; // Cell hydraulic conductivities | |
- Float3* dev_d_T; // Cell hydraulic transmissivity | |
- Float* dev_d_Ss; // Cell hydraulic storativity | |
- Float* dev_d_W; // Cell hydraulic recharge | |
- Float* dev_d_phi; // Cell porosity | |
- Float* dev_d_dphi; // Cell porosity change | |
- | |
- //// Darcy functions | |
+ //// Porous flow | |
+ int navierstokes; // 0: no, 1: yes | |
+ | |
+ // Navier Stokes values, host | |
+ NavierStokes ns; | |
+ | |
+ // Navier Stokes values, device | |
+ Float* dev_ns_p; // Cell hydraulic pressure | |
+ Float3* dev_ns_v; // Cell fluid velocity | |
+ Float* dev_ns_v_x; // Cell fluid velocity in staggered grid | |
+ Float* dev_ns_v_y; // Cell fluid velocity in staggered grid | |
+ Float* dev_ns_v_z; // Cell fluid velocity in staggered grid | |
+ Float3* dev_ns_v_p; // Predicted cell fluid velocity | |
+ Float* dev_ns_v_p_x; // Predicted cell fluid velocity in st. g… | |
+ Float* dev_ns_v_p_y; // Predicted cell fluid velocity in st. g… | |
+ Float* dev_ns_v_p_z; // Predicted cell fluid velocity in st. g… | |
+ Float3* dev_ns_vp_avg; // Average particle velocity in cell | |
+ Float* dev_ns_d_avg; // Average particle diameter in cell | |
+ Float3* dev_ns_fi; // Particle-fluid interaction force | |
+ Float* dev_ns_phi; // Cell porosity | |
+ Float* dev_ns_dphi; // Cell porosity change | |
+ Float3* dev_ns_div_phi_v_v; // Divegence used in velocity prediction | |
+ Float* dev_ns_epsilon; // Pressure difference | |
+ Float* dev_ns_epsilon_new; // Pressure diff. after Jacobi iteration | |
+ Float* dev_ns_epsilon_old; // Pressure diff. before Jacobi iteration | |
+ Float* dev_ns_norm; // Normalized residual of epsilon values | |
+ Float* dev_ns_f; // Values of forcing function | |
+ Float* dev_ns_f1; // Constant terms in forcing function | |
+ Float3* dev_ns_f2; // Constant slopes in forcing function | |
+ Float* dev_ns_v_prod; // Outer product of fluid velocities | |
+ Float* dev_ns_tau; // Fluid stress tensor | |
+ Float3* dev_ns_div_phi_vi_v; // div(phi*vi*v) | |
+ Float3* dev_ns_div_phi_tau; // div(phi*tau) | |
+ | |
+ | |
+ //// Navier Stokes functions | |
// Memory allocation | |
- void initDarcyMem(const Float cellsizemultiplier = 1.0); | |
- void freeDarcyMem(); | |
- | |
- // Set some values for the Darcy parameters | |
- void initDarcyVals(); | |
+ void initNSmem(); | |
+ void freeNSmem(); | |
- // Copy Darcy values from cell to cell (by index) | |
- void copyDarcyVals(unsigned int read, unsigned int write); | |
- | |
- // Update ghost nodes from their parent cell values | |
- void setDarcyGhostNodes(); | |
- | |
- // Find cell transmissivities from hydraulic conductivities and cell | |
- // dimensions | |
- void findDarcyTransmissivities(); | |
- | |
- // Finds central difference gradients | |
- void findDarcyGradients(); | |
+ // Returns the number of fluid cells | |
+ unsigned int NScells(); // Pressure and other centered nodes | |
+ unsigned int NScellsVelocity(); // Inter-cell nodes (velocity) | |
- // Set gradient to zero at grid edges | |
- void setDarcyBCNeumannZero(); | |
- | |
- // Find particles in cell | |
- std::vector<unsigned int> particlesInCell( | |
- const Float3 min, const Float3 max); | |
- | |
- // Return the lower corner coordinates of a cell | |
- Float3 cellMinBoundaryDarcy(const int x, const int y, const int z); | |
- | |
- // Return the upper corner coordinates of a cell | |
- Float3 cellMaxBoundaryDarcy(const int x, const int y, const int z); | |
- | |
- // Returns the cell volume | |
- Float cellVolumeDarcy(); | |
- | |
- // Add fluid drag to the particles inside each cell | |
- void fluidDragDarcy(); | |
- | |
- // Find porosity of cell | |
- Float cellPorosity(const int x, const int y, const int z); | |
- | |
- // Find and save all cell porosities | |
- void findPorosities(); | |
- | |
- // Find darcy flow velocities from specific flux (q) | |
- void findDarcyVelocities(); | |
- | |
// Returns the mean particle radius | |
Float meanRadius(); | |
// Get linear (1D) index from 3D coordinate | |
- unsigned int idx(const int x, const int y, const int z); | |
+ unsigned int idx(const int x, const int y, const int z); // pres. nodes | |
+ unsigned int vidx(const int x, const int y, const int z); // vel. nodes | |
// Initialize Darcy values and arrays | |
- void initDarcy(); | |
+ void initNS(); | |
+ | |
+ // Clean up Navier Stokes arrays | |
+ void endNS(); | |
+ void endNSdev(); | |
- // Clean up Darcy arrays | |
- void endDarcy(); | |
- void endDarcyDev(); | |
+ // Check for stability in the FTCS solution | |
+ void checkNSstability(); | |
- // Check whether the explicit integration is going to meet the | |
- // stability criteria | |
- Float getTmax(); | |
- Float getSsmin(); | |
- void checkDarcyTimestep(); | |
+ // Returns the average value of the normalized residual norm in host m… | |
+ double avgNormResNS(); | |
- // Perform a single time step, explicit integration | |
- void explDarcyStep(); | |
+ // Returns the maximum value of the normalized residual norm in host m… | |
+ double maxNormResNS(); | |
- //// Darcy functions, device | |
- void initDarcyMemDev(); | |
- void freeDarcyMemDev(); | |
- void transferDarcyToGlobalDeviceMemory(int statusmsg); | |
- void transferDarcyFromGlobalDeviceMemory(int statusmsg); | |
+ // Allocate and free memory for NS arrays on device | |
+ void initNSmemDev(); | |
+ void freeNSmemDev(); | |
+ | |
+ // Transfer array values between GPU and CPU | |
+ void transferNStoGlobalDeviceMemory(int statusmsg); | |
+ void transferNSfromGlobalDeviceMemory(int statusmsg); | |
+ void transferNSnormFromGlobalDeviceMemory(); | |
+ void transferNSepsilonFromGlobalDeviceMemory(); | |
+ void transferNSepsilonNewFromGlobalDeviceMemory(); | |
public: | |
t@@ -263,14 +265,15 @@ class DEM { | |
void writebin(const char *target); | |
// Check numeric values of selected parameters | |
- void checkValues(void); | |
+ void diagnostics(); | |
+ void checkValues(); | |
// Report key parameter values to stdout | |
- void reportValues(void); | |
+ void reportValues(); | |
// Iterate through time, using temporal limits | |
// described in "time" struct. | |
- void startTime(void); | |
+ void startTime(); | |
// Render particles using raytracing | |
void render( | |
t@@ -288,10 +291,10 @@ class DEM { | |
void porosity(const int z_slices = 10); | |
// find and return the min. position of any particle in each dimension | |
- Float3 minPos(void); | |
+ Float3 minPos(); | |
// find and return the max. position of any particle in each dimension | |
- Float3 maxPos(void); | |
+ Float3 maxPos(); | |
// Find particle-particle intersections, saves the indexes | |
// and the overlap sizes | |
t@@ -307,18 +310,17 @@ class DEM { | |
const double upper_cutoff = 1.0e9); | |
- ///// Darcy flow functions | |
- | |
+ ///// Porous flow functions | |
- // Print Darcy arrays to file stream | |
- void printDarcyArray(FILE* stream, Float* arr); | |
- void printDarcyArray(FILE* stream, Float* arr, std::string desc); | |
- void printDarcyArray(FILE* stream, Float3* arr); | |
- void printDarcyArray(FILE* stream, Float3* arr, std::string desc); | |
+ // Print fluid arrays to file stream | |
+ void printNSarray(FILE* stream, Float* arr); | |
+ void printNSarray(FILE* stream, Float* arr, std::string desc); | |
+ void printNSarray(FILE* stream, Float3* arr); | |
+ void printNSarray(FILE* stream, Float3* arr, std::string desc); | |
- // Write Darcy arrays to file | |
- void writeDarcyArray(Float* array, const char* filename); | |
- void writeDarcyArray(Float3* array, const char* filename); | |
+ // Write fluid arrays to file | |
+ void writeNSarray(Float* array, const char* filename); | |
+ void writeNSarray(Float3* array, const char* filename); | |
}; | |
#endif | |
diff --git a/src/utility.cu b/src/utility.cu | |
t@@ -1,28 +1,57 @@ | |
#include <iostream> | |
+#include "sphere.h" | |
// MISC. UTILITY FUNCTIONS | |
// Error handler for CUDA GPU calls. | |
-// Returns error number, filename and line number containing the error to th… | |
-// Please refer to CUDA_Toolkit_Reference_Manual.pdf, section 4.23.3.3 enum … | |
-// for error discription. Error enumeration starts from 0. | |
-void checkForCudaErrors(const char* checkpoint_description) | |
+// Returns error number, filename and line number containing the error to the | |
+// terminal. Please refer to CUDA_Toolkit_Reference_Manual.pdf, section | |
+// 4.23.3.3 enum cudaError for error discription. Error enumeration starts from | |
+// 0. | |
+void DEM::diagnostics() | |
+{ | |
+ // Retrieve information from device to host and run diagnostic tests | |
+ transferFromGlobalDeviceMemory(); | |
+ checkValues(); | |
+ | |
+ // Clean up memory before exiting | |
+ if (navierstokes == 1) { | |
+ freeNSmemDev(); | |
+ freeNSmem(); | |
+ } | |
+ freeGlobalDeviceMemory(); | |
+ // CPU memory freed upon object destruction | |
+} | |
+ | |
+void DEM::checkForCudaErrors(const char* checkpoint_description, | |
+ const int run_diagnostics) | |
{ | |
cudaError_t err = cudaGetLastError(); | |
if (err != cudaSuccess) { | |
- std::cerr << "\nCuda error detected, checkpoint: " << checkpoint_descr… | |
- << "\nError string: " << cudaGetErrorString(err) << std::endl; | |
+ std::cerr << "\nCuda error detected, checkpoint: " | |
+ << checkpoint_description << "\nError string: " | |
+ << cudaGetErrorString(err) << std::endl; | |
+ | |
+ if (run_diagnostics == 1) | |
+ diagnostics(); | |
+ | |
exit(EXIT_FAILURE); | |
} | |
} | |
-void checkForCudaErrors(const char* checkpoint_description, const unsigned int… | |
+void DEM::checkForCudaErrorsIter(const char* checkpoint_description, | |
+ const unsigned int iteration, | |
+ const int run_diagnostics) | |
{ | |
cudaError_t err = cudaGetLastError(); | |
if (err != cudaSuccess) { | |
- std::cerr << "\nCuda error detected, checkpoint: " << checkpoint_descr… | |
- << "\nduring iteration " << iteration | |
+ std::cerr << "\nCuda error detected, checkpoint: " | |
+ << checkpoint_description << "\nduring iteration " << iteration | |
<< "\nError string: " << cudaGetErrorString(err) << std::endl; | |
+ | |
+ if (run_diagnostics == 1) | |
+ diagnostics(); | |
+ | |
exit(EXIT_FAILURE); | |
} | |
} | |
diff --git a/src/utility.cuh b/src/utility.cuh | |
t@@ -1,10 +0,0 @@ | |
-// Avoiding multiple inclusions of header file | |
-#ifndef UTILITY_CUH_ | |
-#define UTILITY_CUH_ | |
- | |
-unsigned int iDivUp(unsigned int a, unsigned int b); | |
-void checkForCudaErrors(const char* checkpoint_description); | |
-void checkForCudaErrors(const char* checkpoint_description, const unsigned int… | |
- | |
-#endif | |
-// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 | |
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt | |
t@@ -4,6 +4,9 @@ find_package(PythonInterp REQUIRED) | |
add_test(io_tests ${PYTHON_EXECUTABLE} | |
${CMAKE_CURRENT_BINARY_DIR}/io_tests.py) | |
+add_test(wall_contact_model_tests ${PYTHON_EXECUTABLE} | |
+ ${CMAKE_CURRENT_BINARY_DIR}/contactmodel_wall.py) | |
+ | |
add_test(io_tests_fluid ${PYTHON_EXECUTABLE} | |
${CMAKE_CURRENT_BINARY_DIR}/io_tests_fluid.py) | |
t@@ -15,3 +18,9 @@ add_test(memory_tests ${PYTHON_EXECUTABLE} | |
add_test(bond_tests ${PYTHON_EXECUTABLE} | |
${CMAKE_CURRENT_BINARY_DIR}/bond_tests.py) | |
+ | |
+add_test(cfd_tests ${PYTHON_EXECUTABLE} | |
+ ${CMAKE_CURRENT_BINARY_DIR}/cfd_tests.py) | |
+ | |
+add_test(cfd_tests_neumann ${PYTHON_EXECUTABLE} | |
+ ${CMAKE_CURRENT_BINARY_DIR}/cfd_tests_neumann.py) | |
diff --git a/tests/bond_tests.py b/tests/bond_tests.py | |
t@@ -1,6 +1,6 @@ | |
#!/usr/bin/env python | |
from pytestutils import * | |
-from sphere import * | |
+import sphere | |
def printKinematics(sb): | |
print('bonds_delta_n'); print(sb.bonds_delta_n) | |
t@@ -27,7 +27,8 @@ s2_1 = numpy.ones((2,1))*smallval | |
# Inter-particle distances to try (neg. for overlap) | |
#distances = [0.2, 0.0, -0.2] | |
-distances = [0.2, 0.0] | |
+#distances = [0.2, 0.0] | |
+distances = [] | |
#distances = [0.2] | |
for d in distances: | |
t@@ -35,7 +36,7 @@ for d in distances: | |
radii = 0.5 | |
print("## Inter-particle distance: " + str(d/radii) + " radii") | |
- sb = Spherebin(np=2, sid='bondtest') | |
+ sb = sphere.sim(np=2, sid='bondtest') | |
cleanup(sb) | |
# setup particles, bond, and simulation | |
t@@ -56,7 +57,6 @@ for d in distances: | |
sb.zeroKinematics() | |
sb.initTemporal(total=0.2, file_dt=0.01) | |
#sb.initTemporal(total=0.01, file_dt=0.0001) | |
- sb.writebin(verbose=False) | |
sb.run(verbose=False) | |
#sb.run() | |
sb.readlast(verbose=False) | |
t@@ -75,7 +75,6 @@ for d in distances: | |
sb.initTemporal(total=0.2, file_dt=0.01) | |
sb.vel[1,0] = 1e-4 | |
Ekinrot0 = sb.energy("kin") + sb.energy("rot") | |
- sb.writebin(verbose=False) | |
sb.run(verbose=False) | |
sb.readlast(verbose=False) | |
compareFloats(Ekinrot0, sb.energy("kin") + sb.energy("rot") + sb.energy("b… | |
t@@ -95,7 +94,6 @@ for d in distances: | |
sb.initTemporal(total=0.2, file_dt=0.01) | |
sb.vel[1,0] = -1e-4 | |
Ekinrot0 = sb.energy("kin") + sb.energy("rot") | |
- sb.writebin(verbose=False) | |
sb.run(verbose=False) | |
sb.readlast(verbose=False) | |
compareFloats(Ekinrot0, sb.energy("kin") + sb.energy("rot") + sb.energy("b… | |
t@@ -115,7 +113,6 @@ for d in distances: | |
sb.initTemporal(total=0.2, file_dt=0.01) | |
sb.vel[1,2] = 1e-4 | |
Ekinrot0 = sb.energy("kin") + sb.energy("rot") | |
- sb.writebin(verbose=False) | |
sb.run(verbose=False) | |
sb.readlast(verbose=False) | |
compareFloats(Ekinrot0, sb.energy("kin") + sb.energy("rot") + sb.energy("b… | |
t@@ -150,7 +147,6 @@ for d in distances: | |
#sb.initTemporal(total=0.001, file_dt=0.00001) | |
sb.angvel[1,0] = 1e-4 | |
Ekinrot0 = sb.energy("kin") + sb.energy("rot") | |
- sb.writebin(verbose=False) | |
sb.run(verbose=False) | |
sb.readlast(verbose=False) | |
compareFloats(Ekinrot0, sb.energy("kin") + sb.energy("rot") + sb.energy("b… | |
t@@ -175,7 +171,6 @@ for d in distances: | |
sb.angvel[0,1] = -1e-4 | |
sb.angvel[1,1] = 1e-4 | |
Ekinrot0 = sb.energy("kin") + sb.energy("rot") | |
- sb.writebin(verbose=False) | |
sb.run(verbose=False) | |
sb.readlast(verbose=False) | |
compareFloats(Ekinrot0, sb.energy("kin") + sb.energy("rot") + sb.energy("b… | |
t@@ -191,4 +186,3 @@ for d in distances: | |
#visualize(sb.sid, "energy") | |
#''' | |
-cleanup(sb) | |
diff --git a/tests/cfd_tests.py b/tests/cfd_tests.py | |
t@@ -0,0 +1,222 @@ | |
+#!/usr/bin/env python | |
+from pytestutils import * | |
+ | |
+import sphere | |
+import sys | |
+import numpy | |
+import matplotlib.pyplot as plt | |
+ | |
+print("### CFD tests - Dirichlet BCs ###") | |
+ | |
+# Iteration and conservation of mass test | |
+# No gravity, no pressure gradients => no flow | |
+orig = sphere.sim(np = 0, nd = 3, nw = 0, sid = "cfdtest", fluid = True) | |
+cleanup(orig) | |
+orig.defaultParams() | |
+orig.addParticle([0.5,0.5,0.5], 0.05) | |
+orig.defineWorldBoundaries([1.0,1.0,1.0]) | |
+orig.initFluid(mu = 0.0) | |
+#orig.initFluid(mu = 8.9e-4) | |
+orig.initTemporal(total = 0.2, file_dt = 0.01) | |
+#orig.g[2] = -10.0 | |
+orig.time_file_dt = orig.time_dt*0.99 | |
+orig.time_total = orig.time_dt*10 | |
+#orig.run(dry=True) | |
+orig.run(verbose=False) | |
+py = sphere.sim(sid = orig.sid, fluid = True) | |
+ | |
+ones = numpy.ones((orig.num)) | |
+py.readlast(verbose = False) | |
+#py.writeVTKall() | |
+compareNumpyArrays(ones, py.p_f, "Conservation of pressure:") | |
+ | |
+# Convergence rate (1/2) | |
+it = numpy.loadtxt("../output/" + orig.sid + "-conv.log") | |
+compare(it[:,1].sum(), 0.0, "Convergence rate (1/2):\t") | |
+ | |
+ | |
+# Add pressure gradient | |
+# This test passes with BETA=0.0 and tolerance=1.0e-9 | |
+orig.p_f[:,:,-1] = 1.1 | |
+orig.run(verbose=False) | |
+#orig.run(verbose=True) | |
+py.readlast(verbose = False) | |
+ideal_grad_p_z = numpy.linspace(orig.p_f[0,0,0], orig.p_f[0,0,-1], orig.num[2]) | |
+#py.writeVTKall() | |
+compareNumpyArraysClose(numpy.zeros((1,orig.num[2])),\ | |
+ ideal_grad_p_z - py.p_f[0,0,:],\ | |
+ "Pressure gradient:\t", tolerance=1.0e-1) | |
+ #"Pressure gradient:\t", tolerance=1.0e-2) | |
+ | |
+# Fluid flow direction, opposite of gradient (i.e. towards -z) | |
+if ((py.v_f[:,:,:,2] < 0.0).all() and (py.v_f[:,:,:,0:1] < 1.0e-7).all()): | |
+ print("Flow field:\t\t" + passed()) | |
+else: | |
+ print("Flow field:\t\t" + failed()) | |
+ raise Exception("Failed") | |
+ | |
+# Convergence rate (2/2) | |
+# This test passes with BETA=0.0 and tolerance=1.0e-9 | |
+it = numpy.loadtxt("../output/" + orig.sid + "-conv.log") | |
+if ((it[0:6,1] < 1000).all() and (it[6:,1] < 20).all()): | |
+ print("Convergence rate (2/2):\t" + passed()) | |
+else: | |
+ print("Convergence rate (2/2):\t" + failed()) | |
+''' | |
+# Long test | |
+# This test passes with BETA=0.0 and tolerance=1.0e-9 | |
+orig.p_f[:,:,-1] = 1.1 | |
+orig.time_total[0] = 5.0 | |
+orig.time_file_dt[0] = orig.time_total[0]/10.0 | |
+orig.run(verbose=True) | |
+py.readlast(verbose = False) | |
+ideal_grad_p_z = numpy.linspace(orig.p_f[0,0,0], orig.p_f[0,0,-1], orig.num[2]) | |
+py.writeVTKall() | |
+compareNumpyArraysClose(numpy.zeros((1,orig.num[2])),\ | |
+ ideal_grad_p_z - py.p_f[0,0,:],\ | |
+ "Pressure gradient (long test):", tolerance=1.0e-2) | |
+ | |
+# Fluid flow direction, opposite of gradient (i.e. towards -z) | |
+if ((py.v_f[:,:,:,2] < 0.0).all() and (py.v_f[:,:,:,0:1] < 1.0e-7).all()): | |
+ print("Flow field:\t\t" + passed()) | |
+else: | |
+ print("Flow field:\t\t" + failed()) | |
+ | |
+# Convergence rate (2/2) | |
+# This test passes with BETA=0.0 and tolerance=1.0e-9 | |
+it = numpy.loadtxt("../output/" + orig.sid + "-conv.log") | |
+if (it[0,1] < 700 and it[1,1] < 250 and (it[2:,1] < 20).all()): | |
+ print("Convergence rate (2/2):\t" + passed()) | |
+else: | |
+ print("Convergence rate (2/2):\t" + failed()) | |
+''' | |
+# Add viscosity which will limit the fluid flow. Used to test the stress tensor | |
+# in the fluid velocity prediction | |
+#print(numpy.mean(py.v_f[:,:,:,2])) | |
+orig.time_file_dt[0] = 1.0e-4 | |
+orig.time_total[0] = 1.0e-3 | |
+orig.initFluid(mu = 8.9-4) # water at 25 deg C | |
+orig.p_f[:,:,-1] = 2.0 | |
+orig.run(verbose=False) | |
+#orig.writeVTKall() | |
+ | |
+#py.plotConvergence() | |
+ | |
+py.readsecond(verbose=False) | |
+#py.plotFluidDiffAdvPresZ() | |
+ | |
+# The v_z values are read from sb.v_f[0,0,:,2] | |
+dz = py.L[2]/py.num[2] | |
+rho = 1000.0 # fluid density | |
+ | |
+# Central difference gradients | |
+dvz_dz = (py.v_f[0,0,1:,2] - py.v_f[0,0,:-1,2])/(2.0*dz) | |
+dvzvz_dz = (py.v_f[0,0,1:,2]**2 - py.v_f[0,0,:-1,2]**2)/(2.0*dz) | |
+ | |
+# Diffusive contribution to velocity change | |
+dvz_diff = 2.0*py.mu/rho*dvz_dz*py.time_dt | |
+ | |
+# Advective contribution to velocity change | |
+dvz_adv = dvzvz_dz*py.time_dt | |
+ | |
+# Diffusive and advective terms should have opposite terms | |
+if ((numpy.sign(dvz_diff) == numpy.sign(-dvz_adv)).all()): | |
+ print("Diffusion-advection (1/2):" + passed()) | |
+else: | |
+ print("Diffusion-advection (1/2):" + failed()) | |
+ raise Exception("Failed") | |
+ | |
+ | |
+py.readlast(verbose=False) | |
+#py.plotFluidDiffAdvPresZ() | |
+ | |
+# The v_z values are read from sb.v_f[0,0,:,2] | |
+dz = py.L[2]/py.num[2] | |
+rho = 1000.0 # fluid density | |
+ | |
+# Central difference gradients | |
+dvz_dz = (py.v_f[0,0,1:,2] - py.v_f[0,0,:-1,2])/(2.0*dz) | |
+dvzvz_dz = (py.v_f[0,0,1:,2]**2 - py.v_f[0,0,:-1,2]**2)/(2.0*dz) | |
+ | |
+# Diffusive contribution to velocity change | |
+dvz_diff = 2.0*py.mu/rho*dvz_dz*py.time_dt | |
+ | |
+# Advective contribution to velocity change | |
+dvz_adv = dvzvz_dz*py.time_dt | |
+ | |
+# Diffusive and advective terms should have opposite terms | |
+if ((numpy.sign(dvz_diff) == numpy.sign(-dvz_adv)).all()): | |
+ print("Diffusion-advection (2/2):" + passed()) | |
+else: | |
+ print("Diffusion-advection (2/2):" + failed()) | |
+ | |
+ | |
+# Slow pressure modulation test | |
+''' | |
+orig.time_total[0] = 1.0e-1 | |
+orig.time_file_dt[0] = 0.101*orig.time_total[0] | |
+orig.mu[0] = 0.0 # dont let diffusion add transient effects | |
+orig.setFluidPressureModulation(A=1.0, f=1.0/orig.time_total[0]) | |
+#orig.plotPrescribedFluidPressures() | |
+orig.run(verbose=False) | |
+#py.readlast() | |
+#py.plotConvergence() | |
+#py.plotFluidDiffAdvPresZ() | |
+#py.writeVTKall() | |
+for it in range(1,py.status()): # gradient should be smooth in all output files | |
+ py.readstep(it) | |
+ ideal_grad_p_z =\ | |
+ numpy.linspace(py.p_f[0,0,0], py.p_f[0,0,-1], py.num[2]) | |
+ compareNumpyArraysClose(numpy.zeros((1,py.num[2])),\ | |
+ ideal_grad_p_z - py.p_f[0,0,:],\ | |
+ 'Slow pressure modulation (' + | |
+ str(it+1) + '/' + str(py.status()) + '):', tolerance=1.0e-1) | |
+''' | |
+ | |
+# Fast pressure modulation test | |
+orig.time_total[0] = 1.0e-2 | |
+orig.time_file_dt[0] = 0.101*orig.time_total[0] | |
+orig.mu[0] = 0.0 # dont let diffusion add transient effects | |
+orig.setFluidPressureModulation(A=1.0, f=1.0/orig.time_total[0]) | |
+orig.plotPrescribedFluidPressures() | |
+orig.run(verbose=False) | |
+py.plotConvergence() | |
+py.plotFluidDiffAdvPresZ() | |
+#py.writeVTKall() | |
+for it in range(1,py.status()+1): # gradient should be smooth in all output fi… | |
+ py.readstep(it, verbose=False) | |
+ py.plotFluidDiffAdvPresZ() | |
+ ideal_grad_p_z =\ | |
+ numpy.linspace(py.p_f[0,0,0], py.p_f[0,0,-1], py.num[2]) | |
+ compareNumpyArraysClose(numpy.zeros((1,py.num[2])),\ | |
+ ideal_grad_p_z - py.p_f[0,0,:],\ | |
+ 'Fast pressure modulation (' + | |
+ str(it) + '/' + str(py.status()) + '):', tolerance=5.0e-1) | |
+ | |
+''' | |
+# Top: Dirichlet, bot: Neumann | |
+orig.disableFluidPressureModulation() | |
+orig.time_total[0] = 1.0e-2 | |
+orig.time_file_dt = orig.time_total/20 | |
+orig.p_f[:,:,-1] = 1.0 | |
+orig.g[2] = -1.0 | |
+orig.mu[0] = 8.9e-4 # water | |
+orig.bc_bot[0] = 1 # No-flow BC at bottom | |
+#orig.run(dry=True) | |
+orig.run(verbose=False) | |
+orig.writeVTKall() | |
+py.readlast(verbose = False) | |
+#ideal_grad_p_z = numpy.linspace(orig.p_f[0,0,0], orig.p_f[0,0,-1], orig.num[2… | |
+#compareNumpyArraysClose(numpy.zeros((1,orig.num[2])),\ | |
+ #ideal_grad_p_z - py.p_f[0,0,:],\ | |
+ #"Pressure gradient:\t", tolerance=1.0e-2) | |
+ | |
+# Fluid flow direction, opposite of gradient (i.e. towards -z) | |
+#if ((py.v_f[:,:,:,2] < 0.0).all() and (py.v_f[:,:,:,0:1] < 1.0e-7).all()): | |
+ #print("Flow field:\t\t" + passed()) | |
+#else: | |
+ #print("Flow field:\t\t" + failed()) | |
+ #raise Exception("Failed") | |
+ | |
+''' | |
+cleanup(orig) | |
diff --git a/tests/cfd_tests_neumann.py b/tests/cfd_tests_neumann.py | |
t@@ -0,0 +1,65 @@ | |
+#!/usr/bin/env python | |
+from pytestutils import * | |
+ | |
+import sphere | |
+import sys | |
+import numpy | |
+import matplotlib.pyplot as plt | |
+ | |
+print('### CFD tests - Dirichlet/Neumann BCs ###') | |
+ | |
+print('''# Neumann bottom, Dirichlet top BC. | |
+# No gravity, no pressure gradients => no flow''') | |
+orig = sphere.sim("neumann", fluid = True) | |
+cleanup(orig) | |
+orig.defaultParams(mu_s = 0.4, mu_d = 0.4) | |
+orig.defineWorldBoundaries([0.4, 0.4, 1], dx = 0.1) | |
+orig.initFluid(mu = 8.9e-4) | |
+orig.initTemporal(total = 1.0, file_dt = 0.05, dt = 1.0e-4) | |
+py = sphere.sim(sid = orig.sid, fluid = True) | |
+orig.bc_bot[0] = 1 # No-flow BC at bottom (Neumann) | |
+#orig.run(dry=True) | |
+orig.run(verbose=False) | |
+#orig.writeVTKall() | |
+py.readlast(verbose = False) | |
+ones = numpy.ones((orig.num)) | |
+py.readlast(verbose = False) | |
+#py.writeVTKall() | |
+compareNumpyArraysClose(ones, py.p_f, "Conservation of pressure:", | |
+ tolerance = 1.0e-1) | |
+ | |
+# Fluid flow along z should be very small | |
+if ((numpy.abs(py.v_f[:,:,:,2]) < 1.0e-4).all()): | |
+ print("Flow field:\t\t" + passed()) | |
+else: | |
+ print("Flow field:\t\t" + failed()) | |
+ raise Exception("Failed") | |
+ | |
+print('''# Neumann bottom, Dirichlet top BC. | |
+# Gravity, pressure gradients => transient flow''') | |
+orig = sphere.sim("neumann", fluid = True) | |
+cleanup(orig) | |
+orig.defaultParams(mu_s = 0.4, mu_d = 0.4) | |
+orig.defineWorldBoundaries([0.4, 0.4, 1], dx = 0.1) | |
+orig.initFluid(mu = 8.9e-4) | |
+orig.initTemporal(total = 0.5, file_dt = 0.05, dt = 1.0e-4) | |
+py = sphere.sim(sid = orig.sid, fluid = True) | |
+orig.g[2] = -10.0 | |
+orig.bc_bot[0] = 1 # No-flow BC at bottom (Neumann) | |
+#orig.run(dry=True) | |
+orig.run(verbose=False) | |
+#orig.writeVTKall() | |
+py.readlast(verbose = False) | |
+ideal_grad_p_z = numpy.linspace( | |
+ orig.p_f[0,0,0] + orig.L[2]*orig.rho_f*numpy.abs(orig.g[2]), | |
+ orig.p_f[0,0,-1], orig.num[2]) | |
+compareNumpyArraysClose(ideal_grad_p_z, py.p_f[0,0,:], | |
+ "Pressure gradient:\t", tolerance=1.0e3) | |
+ | |
+# Fluid flow along z should be very small | |
+if ((numpy.abs(py.v_f[:,:,:,2]) < 5.0e-2).all()): | |
+ print("Flow field:\t\t" + passed()) | |
+else: | |
+ print("Flow field:\t\t" + failed()) | |
+ raise Exception("Failed") | |
+ | |
diff --git a/tests/contactmodel_wall.py b/tests/contactmodel_wall.py | |
t@@ -0,0 +1,107 @@ | |
+#!/usr/bin/env python | |
+''' | |
+Validate the implemented contact models by observing the behavior of one or two | |
+particles. | |
+''' | |
+ | |
+import sphere | |
+import numpy | |
+import pytestutils | |
+ | |
+### Wall-particle interaction ################################################… | |
+ | |
+## Linear elastic collisions | |
+ | |
+# Normal impact: Check for conservation of momentum (sum(v_i*m_i)) | |
+orig = sphere.sim(np=1, nw=0, sid='contactmodeltest') | |
+sphere.cleanup(orig) | |
+orig.radius[:] = 1.0 | |
+orig.x[0,:] = [5.0, 5.0, 1.05] | |
+orig.vel[0,2] = -0.1 | |
+orig.defineWorldBoundaries(L=[10,10,10]) | |
+orig.gamma_wn[0] = 0.0 # Disable wall viscosity | |
+orig.gamma_wt[0] = 0.0 # Disable wall viscosity | |
+orig.initTemporal(total = 1.0, file_dt = 0.01) | |
+#orig.time_dt = orig.time_dt*0.1 | |
+moment_before = orig.totalMomentum() | |
+orig.run(verbose=False) | |
+#orig.writeVTKall() | |
+orig.readlast(verbose=False) | |
+pytestutils.compareFloats(orig.vel[0,2], 0.1,\ | |
+ "Elastic normal wall collision (1/2):") | |
+moment_after = orig.totalMomentum() | |
+#print(moment_before) | |
+#print(moment_after) | |
+#print("time step: " + str(orig.time_dt[0])) | |
+#print(str((moment_after[0]-moment_before[0])/moment_before[0]*100.0) + " %") | |
+pytestutils.compareFloats(moment_before, moment_after,\ | |
+ "Elastic normal wall collision (2/2):") | |
+ | |
+# Oblique impact: Check for conservation of momentum (sum(v_i*m_i)) | |
+orig = sphere.sim(np=1, sid='contactmodeltest') | |
+orig.radius[:] = 1.0 | |
+orig.x[0,:] = [5.0, 5.0, 1.05] | |
+orig.vel[0,2] = -0.1 | |
+orig.vel[0,0] = 0.1 | |
+orig.defineWorldBoundaries(L=[10,10,10]) | |
+orig.gamma_wn[0] = 0.0 # Disable wall viscosity | |
+orig.gamma_wt[0] = 0.0 # Disable wall viscosity | |
+orig.initTemporal(total = 1.0, file_dt = 0.01) | |
+moment_before = orig.totalMomentum() | |
+orig.run(verbose=False) | |
+#orig.writeVTKall() | |
+orig.readlast(verbose=False) | |
+moment_after = orig.totalMomentum() | |
+pytestutils.compareFloats(moment_before, moment_after,\ | |
+ " 45 deg. wall collision:\t") | |
+ | |
+## Visco-elastic collisions | |
+ | |
+# Normal impact with normal viscous damping. Test that the lost kinetic energy | |
+# is saved as dissipated viscous energy | |
+orig = sphere.sim(np=1, sid='contactmodeltest') | |
+orig.radius[:] = 1.0 | |
+orig.x[0,:] = [5.0, 5.0, 1.05] | |
+orig.vel[0,2] = -0.1 | |
+orig.defineWorldBoundaries(L=[10,10,10]) | |
+orig.gamma_wn[0] = 1.0e6 | |
+orig.gamma_wt[0] = 0.0 | |
+orig.initTemporal(total = 1.0, file_dt = 0.01) | |
+Ekin_before = orig.energy('kin') | |
+orig.run(verbose=False) | |
+#orig.writeVTKall() | |
+orig.readlast(verbose=False) | |
+Ekin_after = orig.energy('kin') | |
+Ev_after = orig.energy('visc_n') | |
+pytestutils.compareFloats(Ekin_before, Ekin_after+Ev_after,\ | |
+ "Viscoelastic normal wall collision:", tolerance=0.03) | |
+ | |
+# Oblique impact: Check for conservation of momentum (sum(v_i*m_i)) | |
+orig = sphere.sim(np=1, sid='contactmodeltest') | |
+orig.radius[:] = 1.0 | |
+orig.x[0,:] = [5.0, 5.0, 1.05] | |
+orig.vel[0,2] = -0.1 | |
+orig.vel[0,0] = 0.1 | |
+orig.defineWorldBoundaries(L=[10,10,10]) | |
+orig.gamma_wn[0] = 1.0e6 | |
+orig.gamma_wt[0] = 1.0e6 | |
+orig.initTemporal(total = 1.0, file_dt = 0.01) | |
+E_kin_before = orig.energy('kin') | |
+orig.run(verbose=False) | |
+#orig.writeVTKall() | |
+orig.readlast(verbose=False) | |
+#Ekin_after = orig.energy('kin') | |
+#Erot_after = orig.energy('rot') | |
+#Es_after = orig.energy('shear') | |
+#pytestutils.compareFloats(Ekin_before,\ | |
+ #Ekin_after+Erot_after+Es_after,\ | |
+ #" 45 deg. wall collision:", tolerance=0.03) | |
+pytestutils.test(Ekin_before > Ekin_after, | |
+ " 45 deg. wall collision (1/2):") | |
+pytestutils.test((orig.angvel[0,0] == 0.0 and orig.angvel[0,1] > 0.0 \ | |
+ and orig.angvel[0,2] == 0.0), | |
+ " 45 deg. wall collision (2/2):") | |
+ | |
+ | |
+ | |
+sphere.cleanup(orig) | |
diff --git a/tests/dem_cfd_tests.py b/tests/dem_cfd_tests.py | |
t@@ -0,0 +1,50 @@ | |
+#!/usr/bin/env python | |
+from pytestutils import * | |
+ | |
+import sphere | |
+import sys | |
+import numpy | |
+import matplotlib.pyplot as plt | |
+ | |
+print('### DEM/CFD tests - Dirichlet/Neumann BCs and a single particle ###') | |
+ | |
+print('# No gravity') | |
+orig = sphere.sim('dem_cfd', fluid = True) | |
+cleanup(orig) | |
+orig.defaultParams(mu_s = 0.4, mu_d = 0.4) | |
+orig.addParticle([0.2, 0.2, 0.6], 0.05) | |
+orig.defineWorldBoundaries([0.4, 0.4, 1.0], dx = 0.1) | |
+orig.initFluid(mu = 8.9e-4) | |
+orig.initTemporal(total = 0.5, file_dt = 0.05, dt = 1.0e-4) | |
+py = sphere.sim(sid = orig.sid, fluid = True) | |
+orig.bc_bot[0] = 1 # No-flow BC at bottom (Neumann) | |
+#orig.run(dry=True) | |
+orig.run(verbose=False) | |
+#orig.writeVTKall() | |
+py.readlast(verbose = False) | |
+ones = numpy.ones((orig.num)) | |
+zeros = numpy.zeros((orig.num[0], orig.num[1], orig.num[2], 3)) | |
+compareNumpyArraysClose(ones, py.p_f, 'Conservation of pressure:', | |
+ tolerance = 1.0e-5) | |
+compareNumpyArraysClose([0,0,0], py.vel[0], 'Particle velocity:\t', | |
+ tolerance = 1.0e-5) | |
+compareNumpyArraysClose(zeros, py.v_f, 'Fluid velocities:\t', | |
+ tolerance = 1.0e-5) | |
+ | |
+print('# Gravity') | |
+orig = sphere.sim('dem_cfd', fluid = True) | |
+cleanup(orig) | |
+orig.defaultParams(mu_s = 0.4, mu_d = 0.4) | |
+orig.addParticle([0.2, 0.2, 0.6], 0.02) | |
+orig.defineWorldBoundaries([0.4, 0.4, 1], dx = 0.04) | |
+orig.initFluid(mu = 8.9e-4) | |
+orig.initTemporal(total = 0.2, file_dt = 0.01) | |
+py = sphere.sim(sid = orig.sid, fluid = True) | |
+orig.g[2] = -10.0 | |
+orig.bc_bot[0] = 1 # No-flow BC at bottom (Neumann) | |
+orig.run(dry=True) | |
+orig.run(verbose=True) | |
+orig.writeVTKall() | |
+py.readlast(verbose = False) | |
+ones = numpy.ones((orig.num)) | |
+zeros = numpy.zeros((orig.num[0], orig.num[1], orig.num[2], 3)) | |
diff --git a/tests/io_tests.py b/tests/io_tests.py | |
t@@ -1,33 +1,38 @@ | |
#!/usr/bin/env python | |
from pytestutils import * | |
+import sphere | |
#### Input/output tests #### | |
print("### Input/output tests ###") | |
# Generate data in python | |
-orig = Spherebin(np=100, nw=1, sid="test-initgrid") | |
+orig = sphere.sim(np=100, nw=1, sid="test-initgrid") | |
orig.generateRadii(histogram=False) | |
orig.defaultParams() | |
-orig.initRandomGridPos(g=numpy.zeros(orig.nd)) | |
+orig.g[2] = 0.0 | |
+orig.initRandomGridPos() | |
orig.initTemporal(current=0.0, total=0.0) | |
orig.time_total=2.0*orig.time_dt | |
orig.time_file_dt = orig.time_dt | |
orig.writebin(verbose=False) | |
+# Test the test | |
+compare(orig, orig, "Comparison:") | |
+ | |
# Test Python IO routines | |
-py = Spherebin() | |
+py = sphere.sim() | |
py.readbin("../input/" + orig.sid + ".bin", verbose=False) | |
compare(orig, py, "Python IO:") | |
# Test C++ IO routines | |
#orig.run(verbose=False, hideinputfile=True) | |
orig.run(verbose=True, hideinputfile=True) | |
-cpp = Spherebin() | |
+cpp = sphere.sim() | |
cpp.readbin("../output/" + orig.sid + ".output00000.bin", verbose=False) | |
compare(orig, cpp, "C++ IO: ") | |
# Test CUDA IO routines | |
-cuda = Spherebin() | |
+cuda = sphere.sim() | |
cuda.readbin("../output/" + orig.sid + ".output00001.bin", verbose=False) | |
cuda.time_current = orig.time_current | |
cuda.time_step_count = orig.time_step_count | |
diff --git a/tests/io_tests_fluid.py b/tests/io_tests_fluid.py | |
t@@ -1,33 +1,36 @@ | |
#!/usr/bin/env python | |
from pytestutils import * | |
+import sphere | |
#### Input/output tests #### | |
print("### Fluid input/output tests ###") | |
# Generate data in python | |
-orig = Spherebin(np=100, nw=0, sid="test-initgrid-fluid") | |
+orig = sphere.sim(np=100, sid="test-initgrid-fluid", fluid=True) | |
orig.generateRadii(histogram=False, radius_mean=1.0) | |
-orig.defaultParams(nu=1e-5) | |
-orig.initRandomGridPos(g=numpy.zeros(orig.nd)) | |
+orig.defaultParams() | |
+orig.initRandomGridPos() | |
+orig.initFluid() | |
orig.initTemporal(current=0.0, total=0.0) | |
+orig.g[2] = 0.0 | |
orig.time_total=2.0*orig.time_dt | |
orig.time_file_dt = orig.time_dt | |
orig.writebin(verbose=False) | |
# Test Python IO routines | |
-py = Spherebin() | |
+py = sphere.sim(fluid=True) | |
py.readbin("../input/" + orig.sid + ".bin", verbose=False) | |
compare(orig, py, "Python IO:") | |
# Test C++ IO routines | |
-orig.run(verbose=True, hideinputfile=True, darcyflow=True) | |
-#orig.run(verbose=True, hideinputfile=False, darcyflow=True) | |
-cpp = Spherebin() | |
+orig.run(verbose=True, hideinputfile=True) | |
+#orig.run(verbose=True, hideinputfile=False, cfd=True) | |
+cpp = sphere.sim(fluid=True) | |
cpp.readbin("../output/" + orig.sid + ".output00000.bin", verbose=False) | |
compare(orig, cpp, "C++ IO: ") | |
# Test CUDA IO routines | |
-cuda = Spherebin() | |
+cuda = sphere.sim(fluid=True) | |
cuda.readbin("../output/" + orig.sid + ".output00001.bin", verbose=False) | |
cuda.time_current = orig.time_current | |
cuda.time_step_count = orig.time_step_count | |
diff --git a/tests/memcheck_tests.py b/tests/memcheck_tests.py | |
t@@ -1,18 +1,18 @@ | |
#!/usr/bin/env python | |
from pytestutils import * | |
+import sphere | |
#### Input/output tests #### | |
print("### Memory tests ###") | |
# Generate data in python | |
-orig = Spherebin(np = 100, nw = 1, sid = "test-initgrid") | |
+orig = sphere.sim(np = 100, nw = 1, sid = "test-initgrid") | |
orig.generateRadii(histogram = False) | |
orig.defaultParams() | |
-orig.initRandomGridPos(g = numpy.zeros(orig.nd)) | |
+orig.initRandomGridPos() | |
orig.initTemporal(current = 0.0, total = 0.0) | |
orig.time_total = 2.0*orig.time_dt; | |
orig.time_file_dt = orig.time_dt; | |
-orig.writebin(verbose=False) | |
# Test C++ routines | |
print("Valgrind: C++ routines") | |
diff --git a/tests/porosity_tests.py b/tests/porosity_tests.py | |
t@@ -1,20 +1,21 @@ | |
#!/usr/bin/env python | |
from pytestutils import * | |
+import sphere | |
#### Porosity tests #### | |
print("### porosity tests ###") | |
# Generate data in python | |
-orig = Spherebin(np = 100, nw = 1, sid = "test-initgrid") | |
+orig = sphere.sim(np = 100, nw = 1, sid = "test-initgrid") | |
orig.generateRadii(histogram = False) | |
orig.defaultParams() | |
-orig.initRandomGridPos(g = numpy.zeros(orig.nd)) | |
+orig.initRandomGridPos() | |
orig.initTemporal(current = 0.0, total = 0.0) | |
orig.time_total = 2.0*orig.time_dt; | |
orig.time_file_dt = orig.time_dt; | |
orig.writebin(verbose=False) | |
-def testPorosities(spherebin): | |
+def testPorosities(sim): | |
# Number of vertical slices | |
slicevals = [1, 2, 4] | |
t@@ -22,16 +23,16 @@ def testPorosities(spherebin): | |
for slices in slicevals: | |
# Find correct value of bulk porosity | |
- n_bulk = spherebin.bulkPorosity() | |
+ n_bulk = sim.bulkPorosity() | |
#print("Bulk: " + str(n_bulk)) | |
- porosity = spherebin.porosity(slices = slices)[0] | |
+ porosity = sim.porosity(slices = slices)[0] | |
#print("Avg: " + str(numpy.average(porosity))) | |
#print(porosity) | |
# Check if average of porosity function values matches the bulk porosi… | |
compareFloats(n_bulk, numpy.average(porosity), \ | |
- spherebin.sid + ": Porosity average to bulk porosity ("\ | |
+ sim.sid + ": Porosity average to bulk porosity ("\ | |
+ str(i) + "/" + str(len(slicevals)) + "):") | |
i += 1 | |
t@@ -41,7 +42,7 @@ testPorosities(orig) | |
# Simple cubic packing of uniform spheres | |
# The theoretical porosity is (4/3*pi*r^3)/(2r)^3 = 0.476 | |
sidelen = 10 | |
-cubic = Spherebin(np = sidelen**3, sid='cubic') | |
+cubic = sphere.sim(np = sidelen**3, sid='cubic') | |
radius = 1.0 | |
cubic.generateRadii(psd='uni', radius_mean=radius, radius_variance=0.0, histog… | |
for ix in range(sidelen): | |
diff --git a/tests/pytestutils.py b/tests/pytestutils.py | |
t@@ -8,24 +8,35 @@ def passed(): | |
return "\tPassed" | |
def failed(): | |
- raise Exception("Failed") | |
return "\tFailed" | |
+def test(statement, string): | |
+ if (statement == True): | |
+ print(string + passed()) | |
+ else: | |
+ print(string + failed()) | |
+ raise Exception("Failed") | |
+ | |
def compare(first, second, string): | |
- if (first == second): | |
- print(string + passed()) | |
- else: | |
- print(string + failed()) | |
- return(1) | |
- | |
-def compareFloats(first, second, string, criterion=1e-5): | |
- if abs(first-second) < criterion: | |
+ returnvalue = (first == second) | |
+ if (returnvalue == True or returnvalue > 0): | |
+ print(string + passed()) | |
+ else: | |
+ print(string + failed() + ' (' + str(returnvalue) + ')') | |
+ raise Exception("Failed") | |
+ return(returnvalue) | |
+ | |
+def compareFloats(first, second, string, tolerance=1e-3): | |
+ #if abs(first-second) < tolerance: | |
+ if abs((first-second)/first) < tolerance: | |
print(string + passed()) | |
else : | |
print(string + failed()) | |
print("First: " + str(first)) | |
print("Second: " + str(second)) | |
- print("Difference: " + str(second-first)) | |
+ print("Abs. difference: " + str(second-first)) | |
+ print("Rel. difference: " + str(abs((first-second)/first))) | |
+ raise Exception("Failed") | |
return(1) | |
def compareNumpyArrays(first, second, string): | |
t@@ -33,13 +44,13 @@ def compareNumpyArrays(first, second, string): | |
print(string + passed()) | |
else : | |
print(string + failed()) | |
+ raise Exception("Failed") | |
return(1) | |
- | |
-def cleanup(spherebin): | |
- 'Remove temporary files' | |
- subprocess.call("rm -f ../input/" + spherebin.sid + ".bin", shell=True) | |
- subprocess.call("rm -f ../output/" + spherebin.sid + ".*.bin", shell=True) | |
- print("") | |
- | |
- | |
+def compareNumpyArraysClose(first, second, string, tolerance=1e-5): | |
+ if (numpy.allclose(first, second, atol=tolerance)): | |
+ print(string + passed()) | |
+ else : | |
+ print(string + failed()) | |
+ raise Exception("Failed") | |
+ return(1) | |
diff --git a/tests/sphere.py b/tests/sphere.py | |
diff --git a/wc.sh b/wc.sh | |
t@@ -1,2 +1,2 @@ | |
#!/bin/sh | |
-wc -l python/sphere.py src/*.cu src/*.cuh src/*.cpp src/*.h | |
+wc -l python/*.py src/*.cu src/*.cuh src/*.cpp src/*.h |