Introduction
Introduction Statistics Contact Development Disclaimer Help
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: {
+ '&': '&amp;',
+ '<': '&lt;',
+ '>': '&gt;',
+ '"': '&quot;',
+ "'": '&#x27;',
+ '/': '&#x2F;'
+ }
+ };
+ 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 &mdash; 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> &raquo;</…
+ </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} &amp; \tau_{xy} &amp; \tau_{xz}\\
+ \tau_{yx} &amp; \tau_{yy} &amp; \tau_{yz}\\
+ \tau_{zx} &amp; \tau_{zy} &amp; \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 &amp; \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] &amp; \textit{if } R_i - r_j &lt; d_{ij} &lt; R_i + r_j \\
+ \frac{4}{3} \pi r^3_j &amp; \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&#8217;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} &amp; \textit{if } Re &lt; 1,000 \\
+0.44 &amp; \textit{if } Re \geq 1,000
+\end{cases}"/></p>
+</div><p>where the Reynold&#8217;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>&#8212;</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> &raquo;</…
+ </ul>
+ </div>
+ <div class="footer">
+ &copy; 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 &mdash; sphere 0.35 documentation</title>
+ <title>Discrete element method &mdash; 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> &raquo;</li>
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> &raquo;</…
</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&#8217;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&#8217;s behavior hasn&#8217;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|| &amp;
+ \text{if} \quad ||\boldsymbol{f}_t^{ij}|| = 0 \\
+\mu_d ||\boldsymbol{f}^{ij}_n|| &amp;
+ \text{if} \quad ||\boldsymbol{f}_t^{ij}|| &gt; 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> &raquo;</li>
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> &raquo;</…
</ul>
</div>
<div class="footer">
- &copy; Copyright 2012, Anders Damsgaard.
+ &copy; 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 &mdash; sphere 0.35 documentation</title>
+ <title>Index &mdash; 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> &raquo;</li>
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> &raquo;</…
</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> &raquo;</li>
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> &raquo;</…
</ul>
</div>
<div class="footer">
- &copy; Copyright 2012, Anders Damsgaard.
+ &copy; 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! &mdash; sphere 0.35 documentat…
+ <title>The sphere documentation &mdash; 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> &raquo;</li>
+ <li><a href="#">sphere 1.00-alpha documentation</a> &raquo;</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&#8217;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&#46;damsgaard&#37;&#52;&#48;…
-<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&#8217;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> &raquo;</li>
+ <li><a href="#">sphere 1.00-alpha documentation</a> &raquo;</li>
</ul>
</div>
<div class="footer">
- &copy; Copyright 2012, Anders Damsgaard.
+ &copy; 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 &mdash; sphere 0.35 documentation</title>
+ <title>Introduction &mdash; 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> &raquo;</li>
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> &raquo;</…
</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&#8217;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>&#8216;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 . &amp;&amp; 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>&#8216;s. T…
+the root directory, and invoke CMake and GNU Make:</p>
<div class="highlight-python"><pre>$ cmake . &amp;&amp; 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 &amp;&amp; cmake . &amp;&amp; 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&#8217;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> &raquo;</li>
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> &raquo;</…
</ul>
</div>
<div class="footer">
- &copy; Copyright 2012, Anders Damsgaard.
+ &copy; 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 &mdash; sphere 0.35 documentation</title>
+ <title>Python Module Index &mdash; 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> &raquo;</li>
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> &raquo;</…
</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> &raquo;</li>
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> &raquo;</…
</ul>
</div>
<div class="footer">
- &copy; Copyright 2012, Anders Damsgaard.
+ &copy; 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 &mdash; sphere 0.35 documentation</title>
+ <title>Python API &mdash; 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> &raquo;</li>
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> &raquo;</…
</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">&quot;&quot;&quot;</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">&quot;&quot;&quot;</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 &#39;dry&#39; 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>) &#8211; Convert the images…
+<li><strong>folder</strong> (<em>str</em>) &#8211; 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>) &#8211; The color visualization met…
+Possible values are: &#8216;normal&#8217;: color all particles with the same
+color, &#8216;pres&#8217;: color by pressure, &#8216;vel&#8217;: color by tran…
+velocity, &#8216;angvel&#8217;: color by rotational velocity, &#8216;xdisp&#82…
+total displacement along the x-axis, &#8216;angpos&#8217;: color by angular
+position.</li>
+<li><strong>max_val</strong> (<em>float</em>) &#8211; The maximum value of the…
+<li><strong>lower_cutoff</strong> (<em>float</em>) &#8211; 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>) &#8211; Convert the PPM im…
+tracer to this image format using Imagemagick</li>
+<li><strong>verbose</strong> (<em>bool</em>) &#8211; 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>) &#8211; Input file for <tt class="d…
+<li><strong>verbose</strong> (<em>bool</em>) &#8211; Show <tt class="docutils …
+<li><strong>hideinputfile</strong> (<em>bool</em>) &#8211; 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>) &#8211; The number of particles to allo…
+<li><strong>nd</strong> (<em>int</em>) &#8211; The number of spatial dimension…
+1D simulations currently are not possible.</li>
+<li><strong>nw</strong> (<em>int</em>) &#8211; The number of dynamic walls (de…
+<li><strong>sid</strong> (<em>str</em>) &#8211; The simulation id (default = &…
+will be written with this base name.</li>
+<li><strong>fluid</strong> (<em>bool</em>) &#8211; 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>) &#8211; A vector pointing to the…
+<li><strong>radius</strong> (<em>float</em>) &#8211; The particle radius</li>
+<li><strong>vel</strong> (<em>numpy.array</em>) &#8211; The particle linear ve…
+<li><strong>fixvel</strong> (<em>float</em>) &#8211; Fix horizontal linear vel…
+<li><strong>angpos</strong> (<em>numpy.array</em>) &#8211; The particle angula…
+<li><strong>angvel</strong> (<em>numpy.array</em>) &#8211; The particle angula…
+<li><strong>torque</strong> (<em>numpy.array</em>) &#8211; The particle torque…
+<li><strong>es_dot</strong> (<em>float</em>) &#8211; The particle shear energy…
+<li><strong>es</strong> (<em>float</em>) &#8211; The particle shear energy los…
+<li><strong>ev_dot</strong> (<em>float</em>) &#8211; The particle viscous ener…
+<li><strong>ev</strong> (<em>float</em>) &#8211; The particle viscous energy l…
+<li><strong>p</strong> (<em>float</em>) &#8211; 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>) &#8211; Index of first particle in bond<…
+<li><strong>j</strong> (<em>int</em>) &#8211; 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
+&#8216;bonds-&lt;simulation id&gt;-rose.&lt;graphics_format&gt;&#8217;.</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.
-&#64;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>) &#8211; Index of first particle in bond<…
+<li><strong>j</strong> (<em>int</em>) &#8211; Index of second particle in bond…
+<li><strong>spacing</strong> (<em>float</em>) &#8211; 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>) &#8211; The coefficient of static f…
+<li><strong>mu_d</strong> (<em>float</em>) &#8211; The coefficient of dynamic …
+<li><strong>rho</strong> (<em>float</em>) &#8211; The density of the particle …
+<li><strong>k_n</strong> (<em>float</em>) &#8211; The normal stiffness of the …
+<li><strong>k_t</strong> (<em>float</em>) &#8211; The tangential stiffness of …
+<li><strong>k_r</strong> (<em>float</em>) &#8211; The rolling stiffness of the…
+not used</em></li>
+<li><strong>gamma_n</strong> (<em>float</em>) &#8211; Particle-particle contac…
+<li><strong>gamma_t</strong> (<em>float</em>) &#8211; Particle-particle contac…
+<li><strong>gamma_r</strong> (<em>float</em>) &#8211; Particle-particle contac…
+not used</em></li>
+<li><strong>gamma_wn</strong> (<em>float</em>) &#8211; Wall-particle contact n…
+<li><strong>gamma_wt</strong> (<em>float</em>) &#8211; Wall-particle contact t…
+<li><strong>capillaryCohesion</strong> (<em>int</em>) &#8211; 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>) &#8211; The upper boundary of th…
+<li><strong>origo</strong> (<em>numpy.array</em>) &#8211; The lower boundary o…
+won&#8217;t work. Default = [0.0, 0.0, 0.0].</li>
+<li><strong>dx</strong> (<em>float</em>) &#8211; 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], &#8216;kin&#8217; for kinetic energy [J], &#8216;rot…
+rotational energy [J], &#8216;shear&#8217; for energy lost by friction,
+&#8216;shearrate&#8217; for the rate of frictional energy loss [W], &#8216;vis…
+viscous losses normal to the contact [J], &#8216;visc_n_rate&#8217; for the ra…
+of viscous losses normal to the contact [W], and finally &#8216;bondpot&#8217;
+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.
-&#64;param lc: Lower cutoff of contact forces. Contacts below are not
-visualized (float)
-&#64;param uc: Upper cutoff of contact forces. Contacts above are
-visualized with this value (float)
-&#64;param outformat: Format of output image. Possible values are
-&#8216;interactive&#8217;, &#8216;png&#8217;, &#8216;epslatex&#8217;, &#8216;e…
-&#64;param disp: Display forcechains in &#8216;2d&#8217; or &#8216;3d&#8217;
-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>) &#8211; Lower cutoff of contact force…
+visualized</li>
+<li><strong>uc</strong> (<em>float</em>) &#8211; Upper cutoff of contact force…
+visualized with this value</li>
+<li><strong>outformat</strong> (<em>str</em>) &#8211; Format of output image. …
+&#8216;interactive&#8217;, &#8216;png&#8217;, &#8216;epslatex&#8217;, &#8216;e…
+<li><strong>disp</strong> (<em>str</em>) &#8211; Display forcechains in &#8216…
+</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
+&#8216;fc-&lt;simulation id&gt;-rose.pdf&#8217;.</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>) &#8211; Do not visualize for…
+contact force magnitude, in ]0;1[</li>
+<li><strong>graphics_format</strong> (<em>str</em>) &#8211; 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
-&#64;param r_small: Radii of small population (float), in ]0;r_large[
-&#64;param r_large: Radii of large population (float), in ]r_small;inf[
-&#64;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>) &#8211; Radii of small populatio…
+<li><strong>r_large</strong> (<em>float</em>) &#8211; Radii of large populatio…
+<li><strong>ratio</strong> (<em>float</em>) &#8211; 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>) &#8211; 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>) &#8211; The mean radius [m] …
+<li><strong>radius_variance</strong> (<em>float</em>) &#8211; 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&#8217;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>) &#8211; The number of sort…
+(default = [12, 12, 36])</li>
+<li><strong>dx</strong> (<em>float</em>) &#8211; 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&#8217;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>) &#8211; The time at which to end t…
+<li><strong>current</strong> &#8211; The current time [s] (default = 0.0 s)</l…
+<li><strong>file_dt</strong> &#8211; The interval between output files [s] (de…
+<li><strong>dt</strong> &#8211; 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
-&#64;param ratio: The amount of particles to bond, values in ]0.0;1.0] (float)
-&#64;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
+&#8216;&lt;simulation id&gt;-conv.&lt;graphics_format&gt;&#8217;.</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 &#8216;&lt;simulation id&gt;-diff_adv-t=&lt;current time&gt;s-mu=&…
+viscosity&gt;Pa-s.&lt;graphics_format&gt;&#8217;.</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
+&#8216;p_f-&lt;simulation id&gt;-y&lt;y value&gt;.&lt;graphics_format&gt;&#821…
+<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>) &#8211; 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>) &#8211; 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
+&#8216;p_f-&lt;simulation id&gt;-z&lt;z value&gt;.&lt;graphics_format&gt;&#821…
+<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>) &#8211; 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>) &#8211; 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
+&#8216;v_f-&lt;simulation id&gt;-z&lt;z value&gt;.&lt;graphics_format&gt;&#821…
+<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>) &#8211; 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>) &#8211; 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
+&#8216;v_f-&lt;simulation id&gt;-z&lt;z value&gt;.&lt;graphics_format&gt;&#821…
+<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>) &#8211; 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>) &#8211; 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
+&#8216;&lt;simulation id&gt;-pres.&lt;graphics_format&gt;&#8217;.</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
+&#8216;&lt;simulation id&gt;-porosity.&lt;graphics_format&gt;&#8217;.</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>) &#8211; Save the plot in t…
+<li><strong>zslices</strong> (<em>int</em>) &#8211; 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 &#8216;&lt;sid&gt;-ts-x1x3.txt&#8217; 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>) &#8211; The number of vertical slab…
+<li><strong>verbose</strong> (<em>bool</em>) &#8211; 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>) &#8211; The amount of particles to…
+<li><strong>spacing</strong> (<em>float</em>) &#8211; 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>) &#8211; The path to the binary <…
+<li><strong>verbose</strong> (<em>bool</em>) &#8211; Show diagnostic informati…
+<li><strong>bonds</strong> (<em>bool</em>) &#8211; 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>) &#8211; 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>) &#8211; 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>) &#8211; The output file number to rea…
+<li><strong>verbose</strong> (<em>bool</em>) &#8211; 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>) &#8211; The color visualization met…
+Possible values are: &#8216;normal&#8217;: color all particles with the same
+color, &#8216;pres&#8217;: color by pressure, &#8216;vel&#8217;: color by tran…
+velocity, &#8216;angvel&#8217;: color by rotational velocity, &#8216;xdisp&#82…
+total displacement along the x-axis, &#8216;angpos&#8217;: color by angular
+position.</li>
+<li><strong>max_val</strong> (<em>float</em>) &#8211; The maximum value of the…
+<li><strong>lower_cutoff</strong> (<em>float</em>) &#8211; 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>) &#8211; Convert the PPM im…
+tracer to this image format using Imagemagick</li>
+<li><strong>verbose</strong> (<em>bool</em>) &#8211; 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>) &#8211; Show <tt class="docutils …
+<li><strong>hideinputfile</strong> (<em>bool</em>) &#8211; Hide the file name …
+<li><strong>dry</strong> (<em>bool</em>) &#8211; Perform a dry run. Important …
+the <tt class="docutils literal"><span class="pre">sphere</span></tt> program,…
+<li><strong>valgrind</strong> (<em>bool</em>) &#8211; 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>) &#8211; 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&#8217;s original projection method, see Chorin
+(1968) and &#8220;Projection method (fluid dynamics)&#8221; 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>) &#8211; Fluctuation amplitude [Pa]</li>
+<li><strong>f</strong> (<em>float</em>) &#8211; Fluctuation frequency [Hz]</li>
+<li><strong>phi</strong> (<em>float</em>) &#8211; 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 &#8216;&lt;simulation id&gt;-sheardisp.&lt;graphics_format&gt;&#8217…
+<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 &#8216;&lt;sid&gt;-ts-x1x3.txt&#8217; 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&gt; set size ratio -1
gnuplot&gt; set palette defined (0 &#8220;blue&#8221;, 0.5 &#8220;gray&#8221;,…
-gnuplot&gt; plot &#8216;&lt;sid&gt;-ts-x1x3.txt&#8217; with circles palette fs…
+gnuplot&gt; plot &#8216;&lt;sid&gt;-ts-x1x3.txt&#8217; 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>) &#8211; The position along the second …
+<li><strong>graphics_format</strong> (<em>str</em>) &#8211; Save the slip angl…
+<li><strong>cbmax</strong> (<em>float</em>) &#8211; The maximal value of the p…
+<li><strong>arrowscale</strong> (<em>float</em>) &#8211; Scale the rotational …
+<li><strong>velarrowscale</strong> (<em>float</em>) &#8211; Scale the translat…
+<li><strong>slipscale</strong> (<em>float</em>) &#8211; Scale the slip arrows …
+<li><strong>verbose</strong> (<em>bool</em>) &#8211; 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&#64;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&#64;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>) &#8211; The e-mail address that Torq…
+<li><strong>email_alerts</strong> (<em>str</em>) &#8211; The type of Torque me…
+address. The character &#8216;b&#8217; causes a mail to be sent when the
+execution begins. The character &#8216;e&#8217; causes a mail to be sent when
+the execution ends normally. The character &#8216;a&#8217; 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>) &#8211; The maximal allowed time …
+&#8216;HH:MM:SS&#8217;.</li>
+<li><strong>queue</strong> (<em>str</em>) &#8211; The Torque queue to schedule…
+<li><strong>cudapath</strong> (<em>str</em>) &#8211; The path of the CUDA libr…
+nodes</li>
+<li><strong>spheredir</strong> (<em>str</em>) &#8211; The path to the root dir…
+cluster</li>
+<li><strong>use_workdir</strong> (<em>bool</em>) &#8211; Use a different worki…
+folder</li>
+<li><strong>workdir</strong> (<em>str</em>) &#8211; 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>) &#8211; Upper wall velocity. Negati…
+moves downwards.</li>
+<li><strong>normal_stress</strong> (<em>float</em>) &#8211; 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>) &#8211; The output folder for t…
+<li><strong>video_format</strong> (<em>str</em>) &#8211; The format of the out…
+<li><strong>graphics_folder</strong> (<em>str</em>) &#8211; The folder contain…
+<li><strong>graphics_format</strong> (<em>str</em>) &#8211; The format of the …
+<li><strong>fps</strong> (<em>int</em>) &#8211; The number of frames per secon…
+<li><strong>qscale</strong> (<em>float</em>) &#8211; The output video quality,…
+<li><strong>bitrate</strong> (<em>int</em>) &#8211; The bitrate to use in the …
+<li><strong>verbose</strong> (<em>bool</em>) &#8211; 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
+&#8220;Apply&#8221; 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 &#8220;Surface&#8221; or &#8220;Surface With Edges&…
+&#8220;Representation&#8221;. Choose the desired property as the &#8220;Colori…
+It may be desirable to show the color bar by pressing the &#8220;Show&#8221; b…
+and &#8220;Rescale&#8221; to fit the color range limits to the current file. T…
+coordinate system can be displayed by checking the &#8220;Show Axis&#8221; fie…
+All adjustments by default require the &#8220;Apply&#8221; 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 &#8220;Pipeline
+Browser&#8221;. Press &#8220;Glyph&#8221; from the &#8220;Common&#8221; toolba…
+&#8220;Filters&#8221; mennu, and press &#8220;Glyph&#8221; from the &#8220;Com…
+that &#8220;Arrow&#8221; is selected as the &#8220;Glyph type&#8221;, and &#82…
+&#8220;Vectors&#8221; value. Adjust the &#8220;Maximum Number of Points&#8221;…
+big as the number of fluid cells in the grid. Press &#8220;Apply&#8221; 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>) &#8211; The folder where to place t…
+(default = &#8216;../output/&#8217;)</li>
+<li><strong>verbose</strong> (<em>bool</em>) &#8211; 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
+&#8220;Properties&#8221; list. Press &#8220;Apply&#8221; to import all fields …
+session. The particles are visualized by selecting the imported data in
+the &#8220;Pipeline Browser&#8221;. Afterwards, click the &#8220;Glyph&#8221; …
+&#8220;Common&#8221; toolbar, or go to the &#8220;Filters&#8221; menu, and pre…
+the &#8220;Common&#8221; list. Choose &#8220;Sphere&#8221; as the &#8220;Glyph…
+1.0, choose &#8220;scalar&#8221; as the &#8220;Scale Mode&#8221;. Check the &#…
+set the &#8220;Set Scale Factor&#8221; to 1.0. The field &#8220;Maximum Number…
+may be increased if the number of particles exceed the default value.
+Finally press &#8220;Apply&#8221;, and the particles will appear in the main w…
+<p>The sphere resolution may be adjusted (&#8220;Theta resolution&#8221;, &#82…
+resolution&#8221;) to increase the quality and the computational requirements
+of the rendering. All adjustments by default require the &#8220;Apply&#8221; 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>) &#8211; The folder where to place t…
+(default = &#8216;../output/&#8217;)</li>
+<li><strong>verbose</strong> (<em>bool</em>) &#8211; 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-&lt;self.sid&gt;.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
+&#8220;Properties&#8221; list. Press &#8220;Apply&#8221; to import all fields …
+session. The particles are visualized by selecting the imported data in
+the &#8220;Pipeline Browser&#8221;. Afterwards, click the &#8220;Glyph&#8221; …
+&#8220;Common&#8221; toolbar, or go to the &#8220;Filters&#8221; menu, and pre…
+the &#8220;Common&#8221; list. Choose &#8220;Sphere&#8221; as the &#8220;Glyph…
+1.0, choose &#8220;scalar&#8221; as the &#8220;Scale Mode&#8221;. Check the &#…
+set the &#8220;Set Scale Factor&#8221; to 1.0. The field &#8220;Maximum Number…
+may be increased if the number of particles exceed the default value.
+Finally press &#8220;Apply&#8221;, and the particles will appear in the main w…
+<p>The sphere resolution may be adjusted (&#8220;Theta resolution&#8221;, &#82…
+resolution&#8221;) 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
+&#8220;Apply&#8221; 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 &#8220;Surface&#8221; or &#8220;Surface With Edges&…
+&#8220;Representation&#8221;. Choose the desired property as the &#8220;Colori…
+It may be desirable to show the color bar by pressing the &#8220;Show&#8221; b…
+and &#8220;Rescale&#8221; to fit the color range limits to the current file. T…
+coordinate system can be displayed by checking the &#8220;Show Axis&#8221; fie…
+All adjustments by default require the &#8220;Apply&#8221; 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 &#8220;Pipeline
+Browser&#8221;. Press &#8220;Glyph&#8221; from the &#8220;Common&#8221; toolba…
+&#8220;Filters&#8221; mennu, and press &#8220;Glyph&#8221; from the &#8220;Com…
+that &#8220;Arrow&#8221; is selected as the &#8220;Glyph type&#8221;, and &#82…
+&#8220;Vectors&#8221; value. Adjust the &#8220;Maximum Number of Points&#8221;…
+big as the number of fluid cells in the grid. Press &#8220;Apply&#8221; 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>) &#8211; The folder where to place t…
+<li><strong>verbose</strong> (<em>bool</em>) &#8211; 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>) &#8211; The simulation id of the p…
+<li><strong>out_folder</strong> (<em>str</em>) &#8211; The output folder for t…
+<li><strong>video_format</strong> (<em>str</em>) &#8211; The format of the out…
+<li><strong>fps</strong> (<em>int</em>) &#8211; The number of frames per secon…
+<li><strong>qscale</strong> (<em>float</em>) &#8211; The output video quality,…
+<li><strong>bitrate</strong> (<em>int</em>) &#8211; The bitrate to use in the …
+<li><strong>verbose</strong> (<em>bool</em>) &#8211; 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>) &#8211; The e-mail address that Torq…
+<li><strong>email_alerts</strong> (<em>str</em>) &#8211; The type of Torque me…
+address. The character &#8216;b&#8217; causes a mail to be sent when the
+execution begins. The character &#8216;e&#8217; causes a mail to be sent when
+the execution ends normally. The character &#8216;a&#8217; 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>) &#8211; The maximal allowed time …
+&#8216;HH:MM:SS&#8217;.</li>
+<li><strong>queue</strong> (<em>str</em>) &#8211; The Torque queue to schedule…
+<li><strong>cudapath</strong> (<em>str</em>) &#8211; The path of the CUDA libr…
+<li><strong>spheredir</strong> (<em>str</em>) &#8211; The path to the root dir…
+<li><strong>use_workdir</strong> (<em>bool</em>) &#8211; Use a different worki…
+<li><strong>workdir</strong> (<em>str</em>) &#8211; 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>) &#8211; The simulation id of the p…
+<li><strong>out_folder</strong> (<em>str</em>) &#8211; The output folder for t…
+<li><strong>video_format</strong> (<em>str</em>) &#8211; The format of the out…
+<li><strong>graphics_folder</strong> (<em>str</em>) &#8211; The folder contain…
+<li><strong>graphics_format</strong> (<em>str</em>) &#8211; The format of the …
+<li><strong>fps</strong> (<em>int</em>) &#8211; The number of frames per secon…
+<li><strong>qscale</strong> (<em>float</em>) &#8211; The output video quality,…
+<li><strong>bitrate</strong> (<em>int</em>) &#8211; The bitrate to use in the …
+<li><strong>verbose</strong> (<em>bool</em>) &#8211; 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>) &#8211; The simulation id of the p…
+<li><strong>method</strong> (<em>str</em>) &#8211; The type of plot to render.…
+&#8216;walls&#8217;, &#8216;triaxial&#8217; and &#8216;shear&#8217;</li>
+<li><strong>savefig</strong> (<em>bool</em>) &#8211; Save the image instead of…
+<li><strong>outformat</strong> &#8211; The output format of the plot data. Thi…
+format, or in text (&#8216;txt&#8217;).</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> &raquo;</li>
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> &raquo;</…
</ul>
</div>
<div class="footer">
- &copy; Copyright 2012, Anders Damsgaard.
+ &copy; 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 &mdash; sphere 0.35 documentation</title>
+ <title>Search &mdash; 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> &raquo;</li>
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> &raquo;</…
</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> &raquo;</li>
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> &raquo;</…
</ul>
</div>
<div class="footer">
- &copy; Copyright 2012, Anders Damsgaard.
+ &copy; 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 &mdash; sphere 0.35 documentation</title>
+ <title>sphere internals &mdash; 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> &raquo;</li>
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> &raquo;</…
</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> &raquo;</li>
+ <li><a href="index.html">sphere 1.00-alpha documentation</a> &raquo;</…
</ul>
</div>
<div class="footer">
- &copy; Copyright 2012, Anders Damsgaard.
+ &copy; 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
You are viewing proxied material from mx1.adamsgaard.dk. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.