yade-dev team mailing list archive
-
yade-dev team
-
Mailing list archive
-
Message #11393
[Branch ~yade-pkg/yade/git-trunk] Rev 3389: Merge github.com:yade/trunk into chaoUnsat
Merge authors:
Anton Gladky (gladky-anton)
Anton Gladky (gladky-anton)
Bruno Chareyre (bruno-chareyre)
Bruno Chareyre (bruno-chareyre)
Chiara Modenese (chiara-modenese)...
------------------------------------------------------------
revno: 3389 [merge]
committer: Chao Yuan <chaoyuan2012@xxxxxxxxx>
timestamp: Wed 2013-11-20 09:40:45 +0100
message:
Merge github.com:yade/trunk into chaoUnsat
Conflicts:
lib/triangulation/def_types.h
removed:
.kdev4/
.kdev4/yade.kdev4
Yade.kdevelop
examples/test/CundallStrackTest.py
examples/test/Dem3DofGeom.py
examples/test/spheresFactory.py
pkg/dem/CohesiveFrictionalPM.cpp
pkg/dem/CohesiveFrictionalPM.hpp
pkg/dem/Dem3DofGeom_FacetSphere.cpp
pkg/dem/Dem3DofGeom_FacetSphere.hpp
pkg/dem/Dem3DofGeom_SphereSphere.cpp
pkg/dem/Dem3DofGeom_SphereSphere.hpp
pkg/dem/Dem3DofGeom_WallSphere.cpp
pkg/dem/Dem3DofGeom_WallSphere.hpp
pkg/dem/Shop.cpp
py/_eudoxos.cpp
py/eudoxos.py
yade.kdev4
added:
doc/sphinx/fig/micro-domains.png
examples/baraban/BicyclePedalEngine.py
examples/jointedCohesiveFrictionalPM/
examples/jointedCohesiveFrictionalPM/README
examples/jointedCohesiveFrictionalPM/gravityBis.py
examples/jointedCohesiveFrictionalPM/gravityLoading.py
examples/jointedCohesiveFrictionalPM/gravityLoading_velocityField_arrows.png
examples/jointedCohesiveFrictionalPM/gravityLoading_velocityField_spheres.png
examples/jointedCohesiveFrictionalPM/identifBis.py
examples/jointedCohesiveFrictionalPM/identificationSpheresOnJoint.py
examples/jointedCohesiveFrictionalPM/jointedModel.png
examples/jointedCohesiveFrictionalPM/packInGtsSurface.py
examples/jointedCohesiveFrictionalPM/parallellepiped.gts
examples/jointedCohesiveFrictionalPM/persistentPlane30Deg.stl
examples/jointedCohesiveFrictionalPM/testingJoint.py
examples/polyhedra/
examples/polyhedra/ball.py
examples/polyhedra/free-fall.py
examples/polyhedra/irregular.py
examples/polyhedra/wall.py
examples/spheresFactory.py
examples/test/unstructuredGrid.py
examples/test/vtkPeriodicCell.py
examples/tetra/
examples/tetra/oneTetra.py
examples/tetra/oneTetraPoly.py
examples/tetra/twoTetras.py
examples/tetra/twoTetrasPoly.py
gui/qt4/GLViewerDisplay.cpp
gui/qt4/GLViewerMouse.cpp
pkg/common/Grid_GUI.cpp
pkg/dem/Ip2_ElastMat.cpp
pkg/dem/Ip2_ElastMat.hpp
pkg/dem/JointedCohesiveFrictionalPM.cpp
pkg/dem/JointedCohesiveFrictionalPM.hpp
pkg/dem/Polyhedra.cpp
pkg/dem/Polyhedra.hpp
pkg/dem/Polyhedra_Ig2.cpp
pkg/dem/Polyhedra_Ig2.hpp
pkg/dem/Polyhedra_splitter.cpp
pkg/dem/Polyhedra_splitter.hpp
pkg/dem/Polyhedra_support.cpp
pkg/dem/Shop_01.cpp
pkg/dem/Shop_02.cpp
py/_polyhedra_utils.cpp
py/polyhedra_utils.py
scripts/checks-and-tests/checks/DEM-PFV-check.py
scripts/checks-and-tests/checks/data/100spheres
scripts/ppa/
scripts/ppa/createtar.py
modified:
.gitignore
CMakeLists.txt
ChangeLog
README.rst
cMake/FindMetis.cmake
core/Cell.hpp
core/Clump.cpp
core/Clump.hpp
core/ForceContainer.hpp
core/Functor.hpp
core/Scene.cpp
core/Scene.hpp
core/main/main.py.in
doc/references.bib
doc/sphinx/conf.py
doc/sphinx/formulation.rst
doc/sphinx/installation.rst
doc/sphinx/introduction.rst
doc/sphinx/prog.rst
doc/sphinx/references.bib
doc/sphinx/templates/index.html
doc/sphinx/user.rst
doc/sphinx/yadeSphinx.py
doc/yade-articles.bib
doc/yade-conferences.bib
doc/yade-theses.bib
examples/LudingPM.py
examples/clumps/addToClump-example.py
examples/clumps/apply-buoyancy-clumps.py
examples/clumps/replaceByClumps-example.py
examples/concrete/triax.py
examples/concrete/uniax.py
examples/gts-horse/gts-random-pack-obb.py
examples/gts-horse/gts-random-pack.py
examples/packs/packs.py
examples/periodicSandPile.py
examples/simple-scene/simple-scene-plot.py*
examples/simple-shear/simpleShear.py
examples/tesselationwrapper/tesselationWrapper.py
examples/tunnel-pack.py
gui/CMakeLists.txt
gui/qt4/GLViewer.cpp
gui/qt4/__init__.py
lib/base/Math.hpp
lib/import/STLReader.hpp
lib/opengl/GLUtils.cpp
lib/opengl/OpenGLWrapper.hpp
lib/pyutil/README
lib/pyutil/numpy_boost.hpp
lib/triangulation/FlowBoundingSphere.hpp
lib/triangulation/Network.hpp
lib/triangulation/PeriodicFlow.hpp
lib/triangulation/PeriodicFlowLinSolv.hpp
lib/triangulation/RegularTriangulation.h
lib/triangulation/Tenseur3.h
lib/triangulation/TriaxialState.h
lib/triangulation/def_types.h
pkg/common/Cylinder.cpp
pkg/common/Cylinder.hpp
pkg/common/Facet.cpp
pkg/common/Facet.hpp
pkg/common/Gl1_NormPhys.cpp
pkg/common/Gl1_Sphere.cpp
pkg/common/Grid.cpp
pkg/common/KinematicEngines.cpp
pkg/common/KinematicEngines.hpp
pkg/dem/CapillaryStressRecorder.cpp
pkg/dem/ConcretePM.cpp
pkg/dem/ConcretePM.hpp
pkg/dem/DemXDofGeom.cpp
pkg/dem/DemXDofGeom.hpp
pkg/dem/DomainLimiter.cpp
pkg/dem/FlowEngine.cpp
pkg/dem/FlowEngine.hpp
pkg/dem/GlobalStiffnessTimeStepper.cpp
pkg/dem/GlobalStiffnessTimeStepper.hpp
pkg/dem/HertzMindlin.cpp
pkg/dem/HertzMindlin.hpp
pkg/dem/L3Geom.cpp
pkg/dem/L3Geom.hpp
pkg/dem/LudingPM.cpp
pkg/dem/LudingPM.hpp
pkg/dem/NewtonIntegrator.hpp
pkg/dem/Shop.hpp
pkg/dem/SpherePack.cpp
pkg/dem/TesselationWrapper.hpp
pkg/dem/Tetra.cpp
pkg/dem/Tetra.hpp
pkg/dem/TriaxialTest.hpp
pkg/dem/VTKRecorder.cpp
pkg/dem/VTKRecorder.hpp
pkg/dem/ViscoelasticCapillarPM.cpp
pkg/dem/ViscoelasticPM.cpp
pkg/dem/ViscoelasticPM.hpp
pkg/dem/WirePM.hpp
py/CMakeLists.txt
py/_extraDocs.py
py/_utils.cpp
py/export.py
py/pack/_packSpheres.cpp
py/pack/pack.py
py/system.py
py/tests/__init__.py
py/utils.py
py/wrapper/yadeWrapper.cpp
py/ymport.py
scripts/checks-and-tests/checks/README
scripts/checks-and-tests/checks/checkGravity.py
scripts/checks-and-tests/checks/checkList.py
scripts/checks-and-tests/checks/checkTestDummy.py
scripts/checks-and-tests/checks/checkTestTriax.py
scripts/checks-and-tests/checks/checkWirePM.py
scripts/erskine3.py*
scripts/hackett.py*
scripts/linkdeps.py*
scripts/py2wiki.py*
scripts/rename-class.py*
--
lp:yade
https://code.launchpad.net/~yade-pkg/yade/git-trunk
Your team Yade developers is subscribed to branch lp:yade.
To unsubscribe from this branch go to https://code.launchpad.net/~yade-pkg/yade/git-trunk/+edit-subscription
=== modified file '.gitignore'
--- .gitignore 2012-07-13 18:11:18 +0000
+++ .gitignore 2013-10-15 16:14:46 +0000
@@ -25,6 +25,8 @@
doc/sphinx/yade.utils.rst
doc/sphinx/yade.wrapper.rst
doc/sphinx/yade.ymport.rst
-.kdev4/yade.kdev4
-yade.kdev4
+doc/sphinx/yade.polyhedra_utils.rst
+.kdev4/
+*.kdev4
*.pyc
+
=== removed directory '.kdev4'
=== removed file '.kdev4/yade.kdev4'
--- .kdev4/yade.kdev4 2012-04-11 12:40:01 +0000
+++ .kdev4/yade.kdev4 1970-01-01 00:00:00 +0000
@@ -1,35 +0,0 @@
-[Buildset]
-BuildItems=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x01\x00\x00\x00\x08\x00y\x00a\x00d\x00e)
-
-[Launch]
-Launch Configurations=Launch Configuration 0
-
-[Launch][Launch Configuration 0]
-Configured Launch Modes=execute
-Configured Launchers=nativeAppLauncher
-Name=yade-debug
-Type=Native Application
-
-[Launch][Launch Configuration 0][Data]
-Arguments=\s/home/3S-LAB/bchareyre/yade/yade-test-kdev/bin/yade-true-true --debug -j 1 /home/3S-LAB/bchareyre2/yade/yade-test-kdev/yade/scripts/test/chained-cylinder-spring.py
-Debugger Shell=
-Dependencies=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x00)
-Dependency Action=Nothing
-Display Demangle Names=true
-Display Static Members=false
-EnvironmentGroup=default
-Executable=python
-GDB Path=gdb
-Remote GDB Config Script=
-Remote GDB Run Script=
-Remote GDB Shell Script=
-Try Setting Breakpoints On Loading Libraries=true
-Working Directory=
-isExecutable=true
-
-[MakeBuilder]
-Make Binary=scons
-Number Of Jobs=3
-
-[Project]
-VersionControlSupport=kdevgit
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2013-07-18 12:40:45 +0000
+++ CMakeLists.txt 2013-11-12 07:18:09 +0000
@@ -12,8 +12,8 @@
# ENABLE_OPENMP: enable OpenMP-parallelizing option (ON by default)
# ENABLE_GTS: enable GTS-option (ON by default)
# ENABLE_GL2PS: enable GL2PS-option (ON by default)
-# ENABLE_LINSOLV: enable LINSOLV-option (OFF by default)
-# ENABLE_PFVFLOW: enable PFVFLOW-option, FlowEngine (OFF by default)
+# ENABLE_LINSOLV: enable LINSOLV-option (ON by default)
+# ENABLE_PFVFLOW: enable PFVFLOW-option, FlowEngine (ON by default)
# runtimePREFIX: used for packaging, when install directory is not the same is runtime directory (/usr/local by default)
# CHUNKSIZE: set >1, if you want several sources to be compiled at once. Increases compilation speed and RAM-consumption during it (1 by default).
@@ -39,31 +39,46 @@
INCLUDE(FindPythonModule)
INCLUDE(GNUInstallDirs)
#===========================================================
+# HACK!!! If the version of gcc is 4.8 or greater, we add -ftrack-macro-expansion=0
+# and -save-temps into compiler to reduce the memory consumption during compilation.
+# See http://bugs.debian.org/726009 for more information
+# Can be removed later, if gcc fixes its regression
+# Taken from http://stackoverflow.com/questions/4058565/check-gcc-minor-in-cmake
+
+EXECUTE_PROCESS(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
+IF (GCC_VERSION VERSION_GREATER 4.8 OR GCC_VERSION VERSION_EQUAL 4.8)
+ MESSAGE(STATUS "GCC Version >= 4.8. Adding -ftrack-macro-expansion=0 and -save-temps")
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftrack-macro-expansion=0 -save-temps")
+ENDIF()
+
+#===========================================================
+ADD_DEFINITIONS(" -DYADE_PTR_CAST=static_pointer_cast -DYADE_CAST=static_cast ")
IF (CMAKE_CXX_FLAGS)
#If flags are set, add only neccessary flags
IF (DEBUG)
SET(CMAKE_VERBOSE_MAKEFILE 1)
SET(CMAKE_BUILD_TYPE Debug)
- SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DYADE_PTR_CAST=static_pointer_cast -DYADE_CAST=static_cast -DYADE_DEBUG")
+ ADD_DEFINITIONS("-DYADE_DEBUG")
ELSE (DEBUG)
SET(CMAKE_BUILD_TYPE Release)
- SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DYADE_PTR_CAST=static_pointer_cast -DYADE_CAST=static_cast -fPIC")
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
ENDIF (DEBUG)
ELSE (CMAKE_CXX_FLAGS)
#If flags are not set, add all useful flags
IF (DEBUG)
SET(CMAKE_VERBOSE_MAKEFILE 1)
SET(CMAKE_BUILD_TYPE Debug)
- SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DYADE_PTR_CAST=static_pointer_cast -DYADE_CAST=static_cast -DYADE_DEBUG -Wall -fPIC -g -fstack-protector --param=ssp-buffer-size=4 -Wformat -Wformat-security -Werror=format-security")
+ ADD_DEFINITIONS("-DYADE_DEBUG")
+ SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -fPIC -g -fstack-protector --param=ssp-buffer-size=4 -Wformat -Wformat-security -Werror=format-security")
ELSE (DEBUG)
SET(CMAKE_BUILD_TYPE Release)
- SET(CMAKE_CXX_FLAGS "-DYADE_PTR_CAST=static_pointer_cast -DYADE_CAST=static_cast -Wall -fPIC -g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Wformat-security -Werror=format-security -s")
+ SET(CMAKE_CXX_FLAGS "-Wall -fPIC -g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Wformat-security -Werror=format-security -s")
ENDIF (DEBUG)
ENDIF (CMAKE_CXX_FLAGS)
#===========================================================
# Add possibility to use local boost installation (e.g. -DLocalBoost=1.46.1)
-FIND_PACKAGE(Boost ${LocalBoost} COMPONENTS python thread date_time filesystem iostreams regex serialization REQUIRED)
+FIND_PACKAGE(Boost ${LocalBoost} COMPONENTS python thread date_time filesystem iostreams regex serialization system REQUIRED)
INCLUDE_DIRECTORIES (${Boost_INCLUDE_DIRS})
# for checking purpose
MESSAGE("-- Boost_VERSION: " ${Boost_VERSION})
@@ -73,10 +88,13 @@
#===========================================================
FIND_PACKAGE(NumPy REQUIRED)
-INCLUDE_DIRECTORIES(${NUMPY_INCLUDE_DIRS})
+ INCLUDE_DIRECTORIES(${NUMPY_INCLUDE_DIRS})
FIND_PACKAGE(Loki REQUIRED)
+ INCLUDE_DIRECTORIES(${LOKI_INCLUDE_DIR})
FIND_PACKAGE(Eigen3 REQUIRED)
+FIND_PACKAGE(BZip2 REQUIRED)
+FIND_PACKAGE(ZLIB REQUIRED)
#===========================================================
SET(DEFAULT ON CACHE INTERNAL "Default value for enabled by default options")
@@ -90,8 +108,8 @@
OPTION(ENABLE_GUI "Enable GUI" ${DEFAULT})
OPTION(ENABLE_CGAL "Enable CGAL" ${DEFAULT})
OPTION(ENABLE_GL2PS "Enable GL2PS" ${DEFAULT})
-OPTION(ENABLE_LINSOLV "Enable direct solver for the flow engines (experimental)" OFF)
-OPTION(ENABLE_PFVFLOW "Enable flow engine (experimental)" OFF)
+OPTION(ENABLE_LINSOLV "Enable direct solver for the flow engines (experimental)" ${DEFAULT})
+OPTION(ENABLE_PFVFLOW "Enable flow engine (experimental)" ${DEFAULT})
#===========================================================
# Use Eigen3 by default
@@ -101,8 +119,12 @@
SET(CONFIGURED_FEATS "${CONFIGURED_FEATS} Eigen3")
ENDIF(EIGEN3_FOUND)
#===========================================================
+INCLUDE_DIRECTORIES(${BZIP2_INCLUDE_DIR})
+INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS})
+SET(LINKLIBS "${LINKLIBS};${BZIP2_LIBRARIES};${ZLIB_LIBRARIES};")
+#===========================================================
IF(ENABLE_VTK)
- FIND_PACKAGE(VTK COMPONENTS Common)
+ FIND_PACKAGE(VTK)
IF(VTK_FOUND)
INCLUDE_DIRECTORIES(${VTK_INCLUDE_DIRS})
LINK_DIRECTORIES( ${VTK_LIBRARY_DIRS} )
@@ -206,6 +228,8 @@
IF(ENABLE_PFVFLOW)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DFLOW_ENGINE")
SET(CONFIGURED_FEATS "${CONFIGURED_FEATS} PFVflow")
+ ELSE(ENABLE_PFVFLOW)
+ SET(DISABLED_FEATS "${DISABLED_FEATS} PFVflow")
ENDIF(ENABLE_PFVFLOW)
ELSE(CGAL_FOUND AND GMP_FOUND AND (NOT("${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1}" MATCHES ".*clang")))
@@ -239,6 +263,8 @@
SET(DISABLED_FEATS "${DISABLED_FEATS} LinSolv")
SET(ENABLE_LINSOLV OFF)
ENDIF(CHOLMOD_FOUND AND OPENBLAS_FOUND AND METIS_FOUND)
+ELSE(ENABLE_LINSOLV)
+ SET(DISABLED_FEATS "${DISABLED_FEATS} LinSolv")
ENDIF(ENABLE_LINSOLV)
#===============================================
IF(ENABLE_GL2PS)
@@ -351,11 +377,16 @@
ADD_LIBRARY(boot SHARED ${CMAKE_CURRENT_SOURCE_DIR}/core/main/pyboot.cpp)
SET_TARGET_PROPERTIES(boot PROPERTIES PREFIX "")
-TARGET_LINK_LIBRARIES(yade ${Boost_LIBRARIES} ${PYTHON_LIBRARIES} ${LINKLIBS} -lrt)
+TARGET_LINK_LIBRARIES(yade ${Boost_LIBRARIES} ${PYTHON_LIBRARIES} ${LINKLIBS} ${LOKI_LIBRARY} -lrt)
TARGET_LINK_LIBRARIES(boot yade)
IF(ENABLE_VTK)
- TARGET_LINK_LIBRARIES(yade vtkHybrid)
+ IF(${VTK_MAJOR_VERSION} EQUAL 6)
+ TARGET_LINK_LIBRARIES(yade ${VTK_LIBRARIES})
+ ADD_DEFINITIONS("-DYADE_VTK6")
+ ELSE(${VTK_MAJOR_VERSION} EQUAL 6)
+ TARGET_LINK_LIBRARIES(yade vtkHybrid)
+ ENDIF(${VTK_MAJOR_VERSION} EQUAL 6)
ENDIF(ENABLE_VTK)
IF(ENABLE_GUI)
TARGET_LINK_LIBRARIES(yade _GLViewer ${GUI_LIBS})
@@ -375,7 +406,6 @@
SET (pyExecutable ${PYTHON_EXECUTABLE})
SET (profile "default")
SET (sourceRoot "${CMAKE_CURRENT_SOURCE_DIR}")
-SET (libstdcxx "/usr/lib/libstdc++.so.6") #This is a hack, which is not needed for modern systems. Should be deleted.
#====================================
CONFIGURE_FILE(core/main/yade-batch.in "${CMAKE_BINARY_DIR}/bins/yade${SUFFIX}-batch")
@@ -404,6 +434,7 @@
MESSAGE(STATUS "Disabled features:${DISABLED_FEATS}")
IF (DEBUG)
MESSAGE(STATUS "Debug build")
+ SET (debugbuild " (debug build)")
ELSE (DEBUG)
MESSAGE(STATUS "Optimized build")
ENDIF (DEBUG)
=== modified file 'ChangeLog'
--- ChangeLog 2013-05-21 09:58:26 +0000
+++ ChangeLog 2013-10-28 18:49:33 +0000
@@ -1,4 +1,306 @@
==================================================
+yade-1.05.0
+Mon Oct 28 19:49:48 2013 +0200
+
+Anton Gladky (19):
+ Add system-component of boost to be linked.
+ Fix typo in equations in getViscoelasticFromSpheresInteraction (documentation). Thanks to Medack (TU Freiberg).
+ Explicitly link BZip2-library
+ Explicitly link ZLIB-library
+ Update installation docs, add libbz2-dev zlib1g-dev.
+ Fix LudingPM.
+ Rename variables in LudingPM (Theta->Delta).
+ Fix k2-calculation in LudingPM.
+ Update script for LudingPM.
+ Split gui/qt4/GLViewer.cpp.
+ Remove pkg/dem/DomainLimiter.*
+ Recover DomainLimiter (LawTester should be moved somewhere).
+ Remove some warnings.
+ Update numpy_boost.hpp from svn.
+ Remove executes bit on py, cpp and h-files.
+ Split Shop.cpp on Shop_01.cpp and Shop_02.cpp
+ Fix compilation.
+ Remove stable-PPA reference from documentation.
+ Replace all libboost-*-dev by libboost-all-dev.
+
+Christian Jakob (19):
+ complete description of clump methods in users manual
+ fix a bug in inertia tensor approximation scheme
+ fix some links in users manual and Scene doc
+ fix description of default material in users manual
+ fixing/updating refs and links in users manual - part 1
+ removed SpherePadder description from users manual
+ fixing/updating refs and links in users manual - part 2 (finished)
+ adapt buoyancy example with new O.forces.addF() method and make it look nicer
+ update in Clump::updateProperties: included new bool integrateInertia and int discretization and adapt clump(), appendClumpend(), addToClump(), releaseFromClump(), replaceByClumps() and growParticles()
+ create links to some example scripts in wrapper
+ increase initialization speed of examples/packs/packs.py (fix https://bugs.launchpad.net/yade/+bug/1229783)
+ try to fix (still broken) refs in users manual
+ Merge branch 'master' of github.com:yade/trunk
+ make getRoundness() more flexible: empty excludeList is no more needed as input argument; adapt replaceByClumps-example.py and associated part in users manual
+ small fix in users manual
+ fix a link in TesselationWrapper
+ fix a link in TesselationWrapper (second try)
+ fix buoyancy example for clumps
+ removed typenames from Polyhedra.cpp, make gcc < version 4.7 happy
+
+Donia (4):
+ Save the relative velocities of particles.
+ Compute edgesSurfaces if viscousShear is true.
+ Save the interactions only between spheres into a vector.
+ get the shear and normal viscous stress in each interaction.
+
+Jan Stransky (10):
+ marginal modifications
+ Merge branch 'master' of github.com:yade/trunk
+ Added periodic cell VTK export (to VTKRecorder and export.VTKExporter)
+ Merge branch 'master' of github.com:yade/trunk
+ added forgotten example file
+ fixed bug in export.VTKExporter from one previous commits
+ Merge branch 'master' of github.com:yade/trunk
+ Polyhedra implementation + examples (Contributed by Jan Elias).
+ adding examples with tetrehadron modeled by new Polyhedron class
+ CombinedKinematicEngine will not call its 'dead' subengines (question #237437)
+
+Jerome Duriez (6):
+ Correction of typo in comment
+ Proposal of new (similar but more compact) examples using JCFpm classes. A choice can be made one day after some feedback ?
+ Some changes in JCFpm documentation (add of external references, and removal of cross reference towards the CFpm model which does not exist anymore). In cpp, some commented lines to add (in the future ?) some post-pro features that could maybe break current saves of (existing ??) current users
+ Commiting changes discussed briefly in https://lists.launchpad.net/yade-dev/msg09979.html. Mainly written by L. Scholtes, they allow to vizualize with paraview some features of JCFpm classes. It adds then 2 recorders, "jcfpm" and "cracks", that are separated mainly for historical reasons.. They are anyway now documented
+ Add of pylab.ion() in plotNumInteractionsHistogram() and plotDirections() so that yade does not hang anymore after launch of these functions (even after closing the plot window, here). Still thanks to Vaclav ! (http://stackoverflow.com/questions/9753885/pylab-matplotlib-show-waits-until-window-closes)
+ Anecdotic changes in an example script
+
+Klaus Thoeni (2):
+ resolve problem with mask in GridConnection
+ ignore local kdevelope specific files on git
+
+==================================================
+yade-1.00.0
+Sun Sep 29 23:10:48 2013 +0200
+
+Anton Gladky (64):
+ Remove release file.
+ Add documentation, how to render particle with ParaView`s PointSprite plugin.
+ Fix spelling errors.
+ Add Changelog.
+ Add different capillar models into ViscoElasticPM.
+ Add links to equations, consider not only monodisperse particles.
+ Fix s parameter for Willet-formulation of capillar.
+ Add one more critical-calculation to capillar model.
+ Cleanings in viscoelastic capillar modell.
+ Move capillar calculation into another function not to overload VPM.
+ Modify Weigart`s model.
+ Update equations for Weigart`s capillar model.
+ Update capillar equations and names of schemas.
+ Move references to a references.bib.
+ Add comment on liquid bridges.
+ Fix some warnings (clang).
+ Fix compilation.
+ Implement rotational resistance in ViscoElasticPM.
+ Add massMultiply-parameter to ViscoElasticPM to have an opportunity to set kn, kt, cn and ct without multiplication on mass.
+ Implement Rabinovich`s capillar model.
+ Fix wrong calculation of the average rotational resistance.
+ Split ViscoelasticPM on capillar and dry parts.
+ Prevent devision by 0 in capillar Rabinovich model.
+ Prepare for flowengine compilation.
+ Merge libplugins, libsupport and libcore in libyade.
+ Fix FindCholmod.cmake.
+ Fix compilation of FlowBoundingSphere.ipp
+ Replace Cholmod-Feature by LinSolv.
+ Minor fix in CMakeList (FlowEnginge).
+ Implement PID controller to have a servo-engine.
+ Fix tests due to failing clumps-autotests.
+ Use combinedengine for ServoPIDController.
+ Fix hopefully clumps-autotests.
+ Add LudingPM.
+ Merge branch 'ndyck'
+ Add an example for LudingPM.
+ Use libyade again. Revert Bruno`s revert.
+ Fix configuration issue with CHUNKSIZE>0.
+ Drop RockPM.
+ Remove ParticleSizeDistrbutionRPMRecorder.
+ Remove CohesiveStateRPMRecorder.
+ Show PFVflow and LinSolv in disabled features, if they are disabled.
+ Include loki-library dirs and link against them. Fixes compilation on HPC TU Freiberg.
+ Set LinSolv and PFVFLOW ON by default.
+ Add recommended build-dependency for LinSolv and PFVFlow into documentation.
+ Replace ifndef/define-constructions by "#pragma once" in lib/triangulation, where it is possible.
+ Minor fix in capillary models in ViscoelasticPM.
+ Add [Pournin2001] into the description of ViscoelasticPM.
+ Minor fix in calculation s_crit in ViscoelasticPM.
+ Add information about github-hosting into the main page.
+ Add a notification to the screen, whether the current build is debugbuild.
+ Remove explicit linking of libstdcxx.
+ Use skipScripts variable to skip some scripts on check-tests.
+ Skip temporarly DEM-PFV-check.py to let daily-builds packages be built.
+ Remove kde-files from the trunk.
+ Minor fix in installation section of documentation.
+ Remove Fedora list package from documentation. The list is unsupported.
+ Add Antypov2011 reference and some links in Hertz-Mindlin model.
+ Fix some compilation warnings (GCC).
+ Replace libqglviewer-qt4-dev on libqglviewer-dev in installation section.
+ Fix errors in ForceContainer, detected by clang.
+ Fix compilation error.
+ Add export into LIGGGHTS-format.
+ 1.00.0
+
+Bruno Chareyre (68):
+ +1 journal article
+ Add the color of the periodic cell a registered attribute.
+ - fix a bug that would let the capillary tables empty when postLoad was not triggered
+ Add function getCapillaryStress(), equivalent of getStress() but for capillary forces
+ -code cleaning
+ fix a latex equation
+ FlowEngine: exclude corner cases when listing constrictions sizes along the void path
+ +1 article
+ -fix url of an article
+ +4 journal papers
+ +1 journal article
+ - add "LINSOLV" source code for DEM-PFV coupling with direct sparse solvers
+ add strainRate attribute to TriaxialStressController + fix bug in example script
+ Revert "Merge libplugins, libsupport and libcore in libyade." Cause: the new cmake system breaks buildbot and does not work for chunkSize>0
+ - add getSceneAsString() to pass scenes between instances without going through the hard drive
+ fix CMakeList for chunkSize>=1
+ - enable load/save from/to python strings with O.sceneToString and O.stringToScene (for communication between parallel instances of yade)
+ -fix the return type of stringToScene()
+ clean the flow code (part 1)
+ cleaning the flow code (smallfix of part 1)
+ cleaning the flow code (part2)
+ cleaning the flow code (part3 and last)
+ -first example script for the fluid coupling with the DEM-PFV method
+ - new conference and journal papers
+ fixed reference
+ +1 PhD thesis
+ - more documentation of TesselationWrapper, with a section in user manual and pointer to published equations.
+ git ignore changes to .gitignore
+ undo previous change in gitignore, not the right way to fix
+ Turn a LOG_DEBUG into LOG_ERROR/return, to escape segfault. (ref: https://bugs.launchpad.net/bugs/1208878)
+ Add an optional argument to O.forces.f() and O.forces.t() to get correct forces on clumps.
+ doc improvement and more cross-links for TesselationWrapper and FlowEngine
+ -enable user defined forces on bodies applying permanently (with updated user manual)
+ +1 conference paper
+ -workaround https://bugs.launchpad.net/yade/+bug/1183402#3, allowing startup imports with optional argument example: $yade -lscipy.interpolate
+ -fix '-l' command line option (no import lib was crashing)
+ fix uninitialized member of ForceContainer
+ fix compile warnings (unused variables)
+ - smallfixes in documentation
+ fix FlowEngine docstring
+ fix duplicate bib reference
+ small fix in bib entries
+ remove duplicate bib reference
+ commit small fix and update in bib files
+ make default FlowEngine::permeabilityFactor less absurd (crashing by default sucks)
+ regression (check-)test for FlowEngine
+ remove Dem3Dof form Yade (also eudoxos module, mostly based on Dem3Dof)
+ - remove Dem3Dof from documentation
+ remove eudoxos from build system (+ remove from VTKRecorder, that was left behind)
+ remove Dem3Dof from a docstring
+ remove Dem3Dof from alias
+ various fixes and improvements in checkTests
+ - increase verbosity of DEM-PFV checkTest
+ remove some debugging code left in chekList.py
+ make sure FlowEngine has been compiled before trying the checkTest DEM-PFV, as suggested by Klaus
+ make the DEM-PFV checktest fully determinist with a data file for initial positions
+ new attributes in FlowEngine to access low level cholmod/metis data
+ \#include <cholmod.h> in FlowEngine.cpp (why is it not needed on lucid?!)
+ add #ifdef LINSOLV guards in appropriate places in case someone (e.g. buildbot) compiles without cholmod
+ one more #ifdef LINSOLV guard
+ add a cmake path for metis.h on wheezy
+ Return a warning at startup if X rendering is unavailable despite gui is required.
+ User manual: correct the meaning of body.dynamic + remove utils. prefixes
+ Typo fix in prog.rst
+ Fix sphinx warning (no :gui: role)
+ yadeSphinx.py: remove eudoxos module from the build list
+ -remove empty section of doc: External modules
+ revert 3d7ca8577 (doc/current hyperlink), see also https://lists.launchpad.net/yade-dev/msg10035.html.
+
+Chiara Modenese (1):
+ Prevent the normal force become negative when viscous damping is applied.
+
+Christian Jakob (23):
+ added new example showing implementation of buoyancy
+ small fix in new example from previous commit
+ small fix of description in new buoyancy example
+ include integration scheme for inertia tensors of clumps; inertia/masses/volumes of clumps are updated automatically by call of updateProperties method; removed adaptClumpMasses method and example script
+ Merge branch 'master' of github.com:yade/trunk
+ make getRoundness() faster
+ shorten the code of Clump.cpp: switched getClumpVolumeAndAdaptInertia() method into updateProperties()
+ small fix of a comment
+ small fix in an example script
+ Merge branch 'master' of github.com:yade/trunk
+ remove unused #include in Clump.cpp
+ limit number of cubes for clump inertia integration scheme to 1000000; avoids worst case scenario with R_clump_member(s)<<R_clump
+ shorten code of Clump.cpp; improve inertia intergration scheme
+ undo unwanted changes in addToClump example from previous commit
+ bug fix of a previous commit
+ set maximum number of cubes for clump inertia integration scheme to 3375000 in Clump.cpp
+ fix inertia calculation and clump test
+ add output of inertia and mass to addToClump-example.py
+ Merge branch 'master' of github.com:yade/trunk
+ Merge branch 'master' of github.com:yade/trunk
+ Merge branch 'master' of github.com:yade/trunk
+ fix colors in releaseFromClump-example.py
+ improve addToClump() method, make it possible to add body lists
+
+Francois Kneib (3):
+ Fix bug #1161847 : Cannot create two clumpClouds in the same simulation (https://bugs.launchpad.net/yade/+bug/1161847). Modified makeClumpCloud(...) in SpherePack.cpp : the random generator was wrong, fixed it.
+ Make the interaction between two Grids possible. It comes with a set of new classes, all in Grid.cpp/hpp : - The geometry of interaction : GridCoGridCoGeom - Its instanciation : Ig2_GridConnection_GridConnection_GridCoGridCoGeom - An elastic frictional law : Law2_GridCoGridCoGeom_FrictPhys_CundallStrack
+ Lots of changes in the inelastic cohesive set of classes. - correct a lot of bugs and comment the inelastic cohesive law : pkg/dem/Law2_ScGeom6D_InelastCohFrictPhys_CohesionMoment - change some variables names and comments : pkg/dem/InelastCohFrictMat pkg/dem/InelastCohFrictPhys pkg/dem/Ip2_2xInelastCohFrictMat_InelastCohFrictPhys
+
+Jan Stránský (14):
+ added --diable-pynotify to yade-batch Removed #include Shop.hpp from Cell.*pp added area attribute to Facet
+ added triaxal test on cylinder example
+ Created classes for bubble interaction according to question #230574
+ Tetra modification (Ig2_Tetra_Tetra_TTtraSimpleGeom, Law2_TTetraSimpleGeom_NormPhys_Simple, modified GL functor, examples, utils.tetra) added Ip2_ElastMat_... functors fixed bug in utils.kineticEnergy for aspherical bodies
+ Merge branch 'master' of github.com:yade/trunk
+ added utils.UnstructuredGrid class for manipulating (external forces evaluation and positin changes) of FEM-like triangular and tetrahedral meshes hopefully fixed CGAL compilation problem in pkg/dem/Tetra.*pp reported by Klaus
+ Improved documentation about O.load and O.save, motivated by question #230900
+ export.VTKExporter improvement (intaractions export), SVD matrix decomposition in python, Peri3dController improvement (compatibility with O.save/O.load)
+ doc improvement of Peri3dController
+ Merge branch 'master' of http://github.com/yade/trunk
+ midified exporter.VTKExporter for correct interactions export in periodic case
+ ConcretePM: modified damage tensor computetion, added elasticEnergy function, added export.VTKExporter.exportInteractions function, modified ymport.iges function
+ Merge branch 'master' of http://github.com/yade/trunk
+ Modifying code to enable compilation with QUAD_PRECISION without errors (does not work yet for CGAL enabled)
+
+Jerome Duriez (7):
+ - few changes in comments in JointedCohesiveFrictionalPM.* - typos corrected in doxygen and sphinx docs of TriaxialTest (there was some "Trixial" instead of "Triaxial") - correction of typo, broken links, and add of two precisions in the doc of a function of pack.py
+ Typo in NewtonIntegrator doc
+ Correction of link-syntax in doc of Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM
+ Typo and redundancies corrected in doc of Functor.label
+ Typo in a figure legend
+ Precision in the doc relative to dt (6 is the maximal contact number in 2D)
+ Correction of a broken link in GSTS doc
+
+Klaus Thoeni (11):
+ various wire contact laws available now, add reference with details, add a check script for the wire model
+ various wire contact laws available now (new version), add reference with details
+ resolve clash between utils.box and box loaded from pylab
+ correct some spelling and make some example scripts working (more checks needed)
+ move one level up
+ use returnSpherePack=True in randomDensePack
+ fix some typos and links in documentation
+ Merge branch 'master' of github.com:yade/trunk
+ make script faster
+ introduce tolerence in overlap check for intersecting spheres
+ add MatchMaker for friction angle
+
+Luc Scholtes (4):
+ Merge branch 'master' of github.com:yade/trunk
+ cleanup in yade publications list
+ remove CohesiveFrictionalPM from trunk
+ add a new class for modelling jointed media (JointedCohesiveFrictionalPM) and examples to illustrate how to use it (examples/jointedCohesiveFrictionalPM/)
+ new reference added
+
+Nolan Dyck (2):
+ Updated function descriptions for BubbleMat
+ Merge branch 'master' of https://github.com/yade/trunk
+
+Raphaël Maurin (1):
+ Modify GlobalStiffnessTimeStepper for visco-elastic contact law.
+
+
+==================================================
yade-0.97.0
Sun May 12 14:48:29 2013 +0200
=== modified file 'README.rst'
--- README.rst 2013-04-26 20:00:56 +0000
+++ README.rst 2013-11-10 21:23:19 +0000
@@ -18,7 +18,6 @@
- Packages for Ubuntu:
- - Stable-builds: https://launchpad.net/~yade-pkg/+archive/stable
- Daily-builds: https://launchpad.net/~yade-pkg/+archive/snapshots
- External packages: https://launchpad.net/~yade-users/+archive/external
=== removed file 'Yade.kdevelop'
--- Yade.kdevelop 2010-09-30 09:05:24 +0000
+++ Yade.kdevelop 1970-01-01 00:00:00 +0000
@@ -1,229 +0,0 @@
-<?xml version = '1.0'?>
-<kdevelop>
- <general>
- <author>Vaclav Smilauer</author>
- <email>eudoxos@xxxxxxxx</email>
- <version>$VERSION$</version>
- <projectmanagement>KDevCustomProject</projectmanagement>
- <primarylanguage>C++</primarylanguage>
- <ignoreparts/>
- <projectname>Yade</projectname>
- <projectdirectory>.</projectdirectory>
- <absoluteprojectpath>false</absoluteprojectpath>
- <description></description>
- <defaultencoding></defaultencoding>
- <versioncontrol/>
- </general>
- <kdevcustomproject>
- <run>
- <mainprogram>/usr/bin/python</mainprogram>
- <directoryradio>executable</directoryradio>
- <programargs>/home/chia/yade/inst/bin/yade-r2446</programargs>
- <globaldebugarguments>/home/chia/yade/inst/bin/yade-r2446-dbg</globaldebugarguments>
- <globalcwd>/tmp</globalcwd>
- <useglobalprogram>false</useglobalprogram>
- <terminal>true</terminal>
- <autocompile>true</autocompile>
- <autoinstall>true</autoinstall>
- <autokdesu>false</autokdesu>
- <envvars/>
- </run>
- <build>
- <buildtool>make</buildtool>
- <builddir></builddir>
- </build>
- <make>
- <abortonerror>false</abortonerror>
- <numberofjobs>4</numberofjobs>
- <prio>0</prio>
- <dontact>false</dontact>
- <makebin>scons</makebin>
- <defaulttarget></defaulttarget>
- <makeoptions></makeoptions>
- <selectedenvironment>default</selectedenvironment>
- <environments>
- <default>
- <envvar value="localhost/1 192.168.27.125/2 192.168.27.91/1 192.168.23.204/2 192.168.27.201/2 192.168.27.118/2 194.254.65.169/2" name="DISTCC_HOSTS" />
- </default>
- </environments>
- </make>
- <filetypes>
- <filetype>*.java</filetype>
- <filetype>*.h</filetype>
- <filetype>*.H</filetype>
- <filetype>*.hh</filetype>
- <filetype>*.hxx</filetype>
- <filetype>*.hpp</filetype>
- <filetype>*.c</filetype>
- <filetype>*.C</filetype>
- <filetype>*.cc</filetype>
- <filetype>*.cpp</filetype>
- <filetype>*.c++</filetype>
- <filetype>*.cxx</filetype>
- <filetype>Makefile</filetype>
- <filetype>CMakeLists.txt</filetype>
- </filetypes>
- <blacklist/>
- <other>
- <prio>0</prio>
- <otherbin>scons</otherbin>
- <defaulttarget></defaulttarget>
- <otheroptions></otheroptions>
- <selectedenvironment>default</selectedenvironment>
- <environments>
- <default/>
- </environments>
- </other>
- </kdevcustomproject>
- <kdevdebugger>
- <general>
- <dbgshell></dbgshell>
- <gdbpath></gdbpath>
- <configGdbScript></configGdbScript>
- <runShellScript></runShellScript>
- <runGdbScript></runGdbScript>
- <breakonloadinglibs>true</breakonloadinglibs>
- <separatetty>false</separatetty>
- <floatingtoolbar>false</floatingtoolbar>
- <raiseGDBOnStart>false</raiseGDBOnStart>
- </general>
- <display>
- <staticmembers>true</staticmembers>
- <demanglenames>true</demanglenames>
- <outputradix>10</outputradix>
- </display>
- </kdevdebugger>
- <kdevdoctreeview>
- <ignoretocs>
- <toc>ada</toc>
- <toc>ada_bugs_gcc</toc>
- <toc>bash</toc>
- <toc>bash_bugs</toc>
- <toc>clanlib</toc>
- <toc>fortran_bugs_gcc</toc>
- <toc>gnome1</toc>
- <toc>gnustep</toc>
- <toc>gtk</toc>
- <toc>gtk_bugs</toc>
- <toc>haskell</toc>
- <toc>haskell_bugs_ghc</toc>
- <toc>java_bugs_gcc</toc>
- <toc>java_bugs_sun</toc>
- <toc>kde2book</toc>
- <toc>opengl</toc>
- <toc>pascal_bugs_fp</toc>
- <toc>php</toc>
- <toc>php_bugs</toc>
- <toc>perl</toc>
- <toc>perl_bugs</toc>
- <toc>python</toc>
- <toc>python_bugs</toc>
- <toc>qt-kdev3</toc>
- <toc>ruby</toc>
- <toc>ruby_bugs</toc>
- <toc>sdl</toc>
- <toc>sw</toc>
- <toc>w3c-dom-level2-html</toc>
- <toc>w3c-svg</toc>
- <toc>w3c-uaag10</toc>
- <toc>wxwidgets_bugs</toc>
- </ignoretocs>
- <ignoreqt_xml>
- <toc>Guide to the Qt Translation Tools</toc>
- <toc>Qt Assistant Manual</toc>
- <toc>Qt Designer Manual</toc>
- <toc>Qt Reference Documentation</toc>
- <toc>qmake User Guide</toc>
- </ignoreqt_xml>
- <ignoredoxygen>
- <toc>KDE Libraries (Doxygen)</toc>
- </ignoredoxygen>
- </kdevdoctreeview>
- <kdevfilecreate>
- <filetypes/>
- <useglobaltypes>
- <type ext="ui" />
- <type ext="cpp" />
- </useglobaltypes>
- </kdevfilecreate>
- <kdevcppsupport>
- <qt>
- <used>false</used>
- <version>3</version>
- <includestyle>3</includestyle>
- <root>/usr/share/qt3</root>
- <designerintegration>EmbeddedKDevDesigner</designerintegration>
- <qmake>/usr/bin/qmake-qt3</qmake>
- <designer>/usr/bin/designer</designer>
- <designerpluginpaths/>
- </qt>
- <codecompletion>
- <automaticCodeCompletion>false</automaticCodeCompletion>
- <automaticArgumentsHint>true</automaticArgumentsHint>
- <automaticHeaderCompletion>true</automaticHeaderCompletion>
- <codeCompletionDelay>250</codeCompletionDelay>
- <argumentsHintDelay>400</argumentsHintDelay>
- <headerCompletionDelay>250</headerCompletionDelay>
- <showOnlyAccessibleItems>false</showOnlyAccessibleItems>
- <completionBoxItemOrder>0</completionBoxItemOrder>
- <howEvaluationContextMenu>true</howEvaluationContextMenu>
- <showCommentWithArgumentHint>true</showCommentWithArgumentHint>
- <statusBarTypeEvaluation>false</statusBarTypeEvaluation>
- <namespaceAliases>std=_GLIBCXX_STD;__gnu_cxx=std</namespaceAliases>
- <processPrimaryTypes>true</processPrimaryTypes>
- <processFunctionArguments>false</processFunctionArguments>
- <preProcessAllHeaders>false</preProcessAllHeaders>
- <parseMissingHeaders>false</parseMissingHeaders>
- <resolveIncludePaths>true</resolveIncludePaths>
- <alwaysParseInBackground>true</alwaysParseInBackground>
- <usePermanentCaching>true</usePermanentCaching>
- <alwaysIncludeNamespaces>false</alwaysIncludeNamespaces>
- <includePaths>.;</includePaths>
- <parseMissingHeadersExperimental>false</parseMissingHeadersExperimental>
- <resolveIncludePathsUsingMakeExperimental>false</resolveIncludePathsUsingMakeExperimental>
- </codecompletion>
- <creategettersetter>
- <prefixGet></prefixGet>
- <prefixSet>set</prefixSet>
- <prefixVariable>m_,_</prefixVariable>
- <parameterName>theValue</parameterName>
- <inlineGet>true</inlineGet>
- <inlineSet>true</inlineSet>
- </creategettersetter>
- <splitheadersource>
- <enabled>false</enabled>
- <synchronize>true</synchronize>
- <orientation>Vertical</orientation>
- </splitheadersource>
- <references/>
- </kdevcppsupport>
- <cppsupportpart>
- <filetemplates>
- <interfacesuffix>.hpp</interfacesuffix>
- <implementationsuffix>.cpp</implementationsuffix>
- </filetemplates>
- </cppsupportpart>
- <kdevdocumentation>
- <projectdoc>
- <docsystem></docsystem>
- <docurl></docurl>
- <usermanualurl></usermanualurl>
- </projectdoc>
- </kdevdocumentation>
- <kdevfileview>
- <groups>
- <hidenonprojectfiles>false</hidenonprojectfiles>
- <hidenonlocation>false</hidenonlocation>
- </groups>
- <tree>
- <hidepatterns>*.o,*.lo,CVS</hidepatterns>
- <hidenonprojectfiles>false</hidenonprojectfiles>
- <showvcsfields>false</showvcsfields>
- </tree>
- </kdevfileview>
- <ctagspart>
- <customArguments>-R --extra=+q --fields=+n --exclude='.*' --exclude=attic --exclude=doc --exclude=scons-local --exclude=include --exclude=lib/triangulation --exclude=debian --exclude='*.so' --exclude='*.s' --exclude='*.ii' --langmap=c++:+.inl,c++:+.tpp,c++:+.ipp</customArguments>
- <activeTagsFiles/>
- <customTagfilePath>/home/chia/yade/src/r2446/tags</customTagfilePath>
- </ctagspart>
-</kdevelop>
=== modified file 'cMake/FindMetis.cmake'
--- cMake/FindMetis.cmake 2013-06-26 18:15:55 +0000
+++ cMake/FindMetis.cmake 2013-08-30 13:49:57 +0000
@@ -5,8 +5,8 @@
# METIS_LIBRARY, libraries to link against to use GL2PS.
# METIS_FOUND, If false, do not try to use GL2PS.
-FIND_PATH(METIS_INCLUDE_DIR metis.h)
-FIND_LIBRARY(METIS_LIBRARY NAMES metis )
+FIND_PATH(METIS_INCLUDE_DIR metis.h PATHS /usr/include/metis)
+FIND_LIBRARY(METIS_LIBRARY NAMES metis PATHS /usr/lib)
# handle the QUIETLY and REQUIRED arguments and set LOKI_FOUND to TRUE if
# all listed variables are TRUE
=== modified file 'core/Cell.hpp'
--- core/Cell.hpp 2013-05-24 16:48:19 +0000
+++ core/Cell.hpp 2013-09-11 10:54:47 +0000
@@ -150,7 +150,7 @@
YADE_CLASS_BASE_DOC_ATTRS_DEPREC_INIT_CTOR_PY(Cell,Serializable,"Parameters of periodic boundary conditions. Only applies if O.isPeriodic==True.",
/* overridden below to be modified by getters/setters because of intended side-effects */
((Matrix3r,trsf,Matrix3r::Identity(),,"[overridden]")) //"Current transformation matrix of the cell, which defines how far is the current cell geometry (:yref:`hSize<Cell.hSize>`) from the reference configuration. Changing trsf will not change :yref:`hSize<Cell.hSize>`, it serves only as accumulator for transformations applied via :yref:`velGrad<Cell.velGrad>`."))
- ((Matrix3r,refHSize,Matrix3r::Identity(),,"Reference cell configuration, only used with :yref:`OpenGLRenderer.dispScale`. Updated automatically when :yref:`hSize<Cell.hSize>` or :yref:`trsf<Cell.trsf>` is assigned directly; also modified by :yref:`yade.utils.setRefSe3` (called e.g. by the :gui:`Reference` button in the UI)."))
+ ((Matrix3r,refHSize,Matrix3r::Identity(),,"Reference cell configuration, only used with :yref:`OpenGLRenderer.dispScale`. Updated automatically when :yref:`hSize<Cell.hSize>` or :yref:`trsf<Cell.trsf>` is assigned directly; also modified by :yref:`yade.utils.setRefSe3` (called e.g. by the ``Reference`` button in the UI)."))
((Matrix3r,hSize,Matrix3r::Identity(),,"[overridden below]"))
((Matrix3r,prevHSize,Matrix3r::Identity(),Attr::readonly,":yref:`hSize<Cell.hSize>` from the previous step, used in the definition of relative velocity across periods."))
/* normal attributes */
@@ -167,7 +167,7 @@
// override some attributes above
.add_property("hSize",&Cell::getHSize,&Cell::setHSize,"Base cell vectors (columns of the matrix), updated at every step from :yref:`velGrad<Cell.velGrad>` (:yref:`trsf<Cell.trsf>` accumulates applied :yref:`velGrad<Cell.velGrad>` transformations). Setting *hSize* during a simulation is not supported by most contact laws, it is only meant to be used at iteration 0 before any interactions have been created.")
.add_property("size",&Cell::getSize_copy,&Cell::setSize,"Current size of the cell, i.e. lengths of the 3 cell lateral vectors contained in :yref:`Cell.hSize` columns. Updated automatically at every step. Assigning a value will change the lengths of base vectors (see :yref:`Cell.hSize`), keeping their orientations unchanged.")
- .add_property("refSize",&Cell::getRefSize,&Cell::setRefSize,"Reference size of the cell (lengths of initial cell vectors, i.e. column norms of :yref:`hSize<Cell.hSize>`).\n\n.. note:: Modifying this value is deprecated, use :yref:`setBox<Cell.setBox>` instead.\n\n")
+ .add_property("refSize",&Cell::getRefSize,&Cell::setRefSize,"Reference size of the cell (lengths of initial cell vectors, i.e. column norms of :yref:`hSize<Cell.hSize>`).\n\n.. note::\n\t Modifying this value is deprecated, use :yref:`setBox<Cell.setBox>` instead.")
// useful properties
.add_property("trsf",&Cell::getTrsf,&Cell::setTrsf,"Current transformation matrix of the cell, obtained from time integration of :yref:`Cell.velGrad`.")
.add_property("velGrad",&Cell::getVelGrad,&Cell::setVelGrad,"Velocity gradient of the transformation; used in :yref:`NewtonIntegrator`. Values of :yref:`velGrad<Cell.velGrad>` accumulate in :yref:`trsf<Cell.trsf>` at every step.\n\n NOTE: changing velGrad at the beginning of a simulation loop would lead to inacurate integration for one step, as it should normaly be changed after the contact laws (but before Newton). To avoid this problem, assignment is deferred automatically. The target value typed in terminal is actually stored in :yref:`Cell.nextVelGrad` and will be applied right in time by Newton integrator.")
=== modified file 'core/Clump.cpp'
--- core/Clump.cpp 2013-07-05 07:33:47 +0000
+++ core/Clump.cpp 2013-10-04 11:41:10 +0000
@@ -82,7 +82,7 @@
*/
-void Clump::updateProperties(const shared_ptr<Body>& clumpBody){
+void Clump::updateProperties(const shared_ptr<Body>& clumpBody, unsigned int discretization, bool integrateInertia){
LOG_DEBUG("Updating clump #"<<clumpBody->id<<" parameters");
const shared_ptr<State> state(clumpBody->state);
const shared_ptr<Clump> clump(YADE_PTR_CAST<Clump>(clumpBody->shape));
@@ -110,16 +110,18 @@
bool intersecting = false;
shared_ptr<Sphere> sph (new Sphere);
int Sph_Index = sph->getClassIndexStatic(); // get sphere index for checking if bodies are spheres
- FOREACH(MemberMap::value_type& mm, clump->members){
- const shared_ptr<Body> subBody1=Body::byId(mm.first);
+ if (integrateInertia){
FOREACH(MemberMap::value_type& mm, clump->members){
- const shared_ptr<Body> subBody2=Body::byId(mm.first);
- if ((subBody1->shape->getClassIndex() == Sph_Index) && (subBody2->shape->getClassIndex() == Sph_Index) && (subBody1!=subBody2)){//clump members should be spheres
- Vector3r dist = subBody1->state->pos - subBody2->state->pos;
- const Sphere* sphere1 = YADE_CAST<Sphere*> (subBody1->shape.get());
- const Sphere* sphere2 = YADE_CAST<Sphere*> (subBody2->shape.get());
- Real un = (sphere1->radius+sphere2->radius) - dist.norm();
- if (un > 0.) {intersecting = true; break;}
+ const shared_ptr<Body> subBody1=Body::byId(mm.first);
+ FOREACH(MemberMap::value_type& mm, clump->members){
+ const shared_ptr<Body> subBody2=Body::byId(mm.first);
+ if ((subBody1->shape->getClassIndex() == Sph_Index) && (subBody2->shape->getClassIndex() == Sph_Index) && (subBody1!=subBody2)){//clump members should be spheres
+ Vector3r dist = subBody1->state->pos - subBody2->state->pos;
+ const Sphere* sphere1 = YADE_CAST<Sphere*> (subBody1->shape.get());
+ const Sphere* sphere2 = YADE_CAST<Sphere*> (subBody2->shape.get());
+ Real un = (sphere1->radius+sphere2->radius) - dist.norm();
+ if (un > -0.001*min(sphere1->radius,sphere2->radius)) {intersecting = true; break;}
+ }
}
}
}
@@ -150,8 +152,7 @@
}
}
//get volume and inertia tensor using regular cubic cell array inside bounding box of the clump:
- int divisor = 15; //TODO: make it choosable by users
- Real dx = rMin/divisor; //edge length of cell
+ Real dx = rMin/discretization; //edge length of cell
Real aabbMax = max(max(aabb.max().x()-aabb.min().x(),aabb.max().y()-aabb.min().y()),aabb.max().z()-aabb.min().z());
if (aabbMax/dx > 150) dx = aabbMax/150;//limit dx
Real dv = pow(dx,3); //volume of cell
@@ -169,6 +170,7 @@
Sg += dv*x;
//inertia I = sum_i( mass_i*dist^2 + I_s) ) //steiners theorem
Ig += dv*( x.dot(x)*Matrix3r::Identity()-x*x.transpose()/*dist^2*/+Matrix3r(Vector3r::Constant(dv*pow(dx,2)/6.).asDiagonal())/*I_s/m = d^2: along princial axes of dv; perhaps negligible?*/);
+ break;
}
}
}
=== modified file 'core/Clump.hpp'
--- core/Clump.hpp 2013-06-21 06:53:43 +0000
+++ core/Clump.hpp 2013-10-04 11:41:10 +0000
@@ -61,7 +61,7 @@
static void add(const shared_ptr<Body>& clump, const shared_ptr<Body>& subBody);
static void del(const shared_ptr<Body>& clump, const shared_ptr<Body>& subBody);
//! Recalculate physical properties of Clump.
- static void updateProperties(const shared_ptr<Body>& clump);
+ static void updateProperties(const shared_ptr<Body>& clump, unsigned int discretization, bool integrateInertia);
//! Calculate positions and orientations of members based on relative Se3; newton pointer (if non-NULL) calls NewtonIntegrator::saveMaximaVelocity
// done as template to avoid cross-dependency between clump and newton (not necessary if all plugins are linked together)
template<class IntegratorT>
=== modified file 'core/ForceContainer.hpp'
--- core/ForceContainer.hpp 2011-05-03 13:18:48 +0000
+++ core/ForceContainer.hpp 2013-09-24 06:14:29 +0000
@@ -50,18 +50,20 @@
std::vector<vvector> _torqueData;
std::vector<vvector> _moveData;
std::vector<vvector> _rotData;
- vvector _force, _torque, _move, _rot;
+ vvector _force, _torque, _move, _rot, _permForce, _permTorque;
std::vector<size_t> sizeOfThreads;
size_t size;
+ size_t permSize;
bool syncedSizes;
int nThreads;
- bool synced,moveRotUsed;
+ bool synced,moveRotUsed,permForceUsed;
boost::mutex globalMutex;
Vector3r _zero;
inline void ensureSize(Body::id_t id, int threadN){
assert(nThreads>omp_get_thread_num());
- if (sizeOfThreads[threadN]<=(size_t)id) resize(min((size_t)1.5*(id+100),(size_t)(id+2000)),threadN);
+ if (threadN<0) resizePerm(min((size_t)1.5*(id+100),(size_t)(id+2000)));
+ else if (sizeOfThreads[threadN]<=(size_t)id) resize(min((size_t)1.5*(id+100),(size_t)(id+2000)),threadN);
}
inline void ensureSynced(){ if(!synced) throw runtime_error("ForceContainer not thread-synchronized; call sync() first!"); }
@@ -70,7 +72,7 @@
// dummy function to avoid template resolution failure
friend class boost::serialization::access; template<class ArchiveT> void serialize(ArchiveT & ar, unsigned int version){}
public:
- ForceContainer(): size(0),syncedSizes(true),synced(true),moveRotUsed(false),_zero(Vector3r::Zero()),syncCount(0),lastReset(0){
+ ForceContainer(): size(0), permSize(0),syncedSizes(true),synced(true),moveRotUsed(false),permForceUsed(false),_zero(Vector3r::Zero()),syncCount(0),lastReset(0){
nThreads=omp_get_max_threads();
for(int i=0; i<nThreads; i++){
_forceData.push_back(vvector()); _torqueData.push_back(vvector());
@@ -86,6 +88,11 @@
void addMove(Body::id_t id, const Vector3r& m) { ensureSize(id,omp_get_thread_num()); synced=false; moveRotUsed=true; _moveData[omp_get_thread_num()][id]+=m;}
const Vector3r& getRot(Body::id_t id) { ensureSynced(); return ((size_t)id<size)?_rot[id]:_zero; }
void addRot(Body::id_t id, const Vector3r& r) { ensureSize(id,omp_get_thread_num()); synced=false; moveRotUsed=true; _rotData[omp_get_thread_num()][id]+=r;}
+
+ void addPermForce(Body::id_t id, const Vector3r& f){ ensureSize(id,-1); synced=false; _permForce[id]=f; permForceUsed=true;}
+ void addPermTorque(Body::id_t id, const Vector3r& t){ ensureSize(id,-1); synced=false; _permTorque[id]=t; permForceUsed=true;}
+ const Vector3r& getPermForce(Body::id_t id) { ensureSynced(); return ((size_t)id<size)?_permForce[id]:_zero; }
+ const Vector3r& getPermTorque(Body::id_t id) { ensureSynced(); return ((size_t)id<size)?_permTorque[id]:_zero; }
/*! Function to allow friend classes to get force even if not synced. Used for clumps by NewtonIntegrator.
* Dangerous! The caller must know what it is doing! (i.e. don't read after write
@@ -96,8 +103,8 @@
/* To be benchmarked: sum thread data in getForce/getTorque upon request for each body individually instead of by the sync() function globally */
// this function is used from python so that running simulation is not slowed down by sync'ing on occasions
// since Vector3r writes are not atomic, it might (rarely) return wrong value, if the computation is running meanwhile
- Vector3r getForceSingle (Body::id_t id){ Vector3r ret(Vector3r::Zero()); for(int t=0; t<nThreads; t++){ ret+=((size_t)id<sizeOfThreads[t])?_forceData [t][id]:_zero; } return ret; }
- Vector3r getTorqueSingle(Body::id_t id){ Vector3r ret(Vector3r::Zero()); for(int t=0; t<nThreads; t++){ ret+=((size_t)id<sizeOfThreads[t])?_torqueData[t][id]:_zero; } return ret; }
+ Vector3r getForceSingle (Body::id_t id){ Vector3r ret(Vector3r::Zero()); for(int t=0; t<nThreads; t++){ ret+=((size_t)id<sizeOfThreads[t])?_forceData [t][id]:_zero; } if (permForceUsed) ret+=_permForce[id]; return ret; }
+ Vector3r getTorqueSingle(Body::id_t id){ Vector3r ret(Vector3r::Zero()); for(int t=0; t<nThreads; t++){ ret+=((size_t)id<sizeOfThreads[t])?_torqueData[t][id]:_zero; } if (permForceUsed) ret+=_permTorque[id]; return ret; }
Vector3r getMoveSingle (Body::id_t id){ Vector3r ret(Vector3r::Zero()); for(int t=0; t<nThreads; t++){ ret+=((size_t)id<sizeOfThreads[t])?_moveData [t][id]:_zero; } return ret; }
Vector3r getRotSingle (Body::id_t id){ Vector3r ret(Vector3r::Zero()); for(int t=0; t<nThreads; t++){ ret+=((size_t)id<sizeOfThreads[t])?_rotData [t][id]:_zero; } return ret; }
@@ -109,6 +116,8 @@
}
_force.resize(size,Vector3r::Zero());
_torque.resize(size,Vector3r::Zero());
+ _permForce.resize(size,Vector3r::Zero());
+ _permTorque.resize(size,Vector3r::Zero());
_move.resize(size,Vector3r::Zero());
_rot.resize(size,Vector3r::Zero());
syncedSizes=true;
@@ -127,6 +136,7 @@
Vector3r sumF(Vector3r::Zero()), sumT(Vector3r::Zero());
for(int thread=0; thread<nThreads; thread++){ sumF+=_forceData[thread][id]; sumT+=_torqueData[thread][id];}
_force[id]=sumF; _torque[id]=sumT;
+ if (permForceUsed) {_force[id]+=_permForce[id]; _torque[id]+=_permTorque[id];}
}
if(moveRotUsed){
for(long id=0; id<(long)size; id++){
@@ -149,9 +159,17 @@
if (size<newSize) size=newSize;
syncedSizes=false;
}
- /*! Reset all data, also reset summary forces/torques and mark the container clean. */
+ void resizePerm(size_t newSize){
+ _permForce.resize(newSize,Vector3r::Zero());
+ _permTorque.resize(newSize,Vector3r::Zero());
+ permSize = newSize;
+ if (size<newSize) size=newSize;
+ syncedSizes=false;
+ }
+ /*! Reset all resetable data, also reset summary forces/torques and mark the container clean.
+ If resetAll, reset also user defined forces and torques*/
// perhaps should be private and friend Scene or whatever the only caller should be
- void reset(long iter){
+ void reset(long iter, bool resetAll=false){
syncSizesOfContainers();
for(int thread=0; thread<nThreads; thread++){
memset(&_forceData [thread][0],0,sizeof(Vector3r)*sizeOfThreads[thread]);
@@ -167,12 +185,19 @@
memset(&_move [0], 0,sizeof(Vector3r)*size);
memset(&_rot [0], 0,sizeof(Vector3r)*size);
}
- synced=true; moveRotUsed=false;
+ if (resetAll){
+ memset(&_permForce [0], 0,sizeof(Vector3r)*size);
+ memset(&_permTorque[0], 0,sizeof(Vector3r)*size);
+ permForceUsed = false;
+ }
+ if (!permForceUsed) synced=true; else synced=false;
+ moveRotUsed=false;
lastReset=iter;
}
//! say for how many threads we have allocated space
const int& getNumAllocatedThreads() const {return nThreads;}
const bool& getMoveRotUsed() const {return moveRotUsed;}
+ const bool& getPermForceUsed() const {return permForceUsed;}
};
#else
@@ -183,17 +208,19 @@
std::vector<Vector3r> _torque;
std::vector<Vector3r> _move;
std::vector<Vector3r> _rot;
+ std::vector<Vector3r> _permForce, _permTorque;
size_t size;
+ size_t permSize;
inline void ensureSize(Body::id_t id){ if(size<=(size_t)id) resize(min((size_t)1.5*(id+100),(size_t)(id+2000)));}
#if 0
const Vector3r& getForceUnsynced (Body::id_t id){ return getForce(id);}
const Vector3r& getTorqueUnsynced(Body::id_t id){ return getForce(id);}
#endif
- bool moveRotUsed;
+ bool moveRotUsed, permForceUsed;
// dummy function to avoid template resolution failure
friend class boost::serialization::access; template<class ArchiveT> void serialize(ArchiveT & ar, unsigned int version){}
public:
- ForceContainer(): size(0), moveRotUsed(false), syncCount(0), lastReset(0){}
+ ForceContainer(): size(0), permSize(0), moveRotUsed(false), permForceUsed(false), syncCount(0), lastReset(0){}
const Vector3r& getForce(Body::id_t id){ensureSize(id); return _force[id];}
void addForce(Body::id_t id,const Vector3r& f){ensureSize(id); _force[id]+=f;}
const Vector3r& getTorque(Body::id_t id){ensureSize(id); return _torque[id];}
@@ -202,14 +229,32 @@
void addMove(Body::id_t id,const Vector3r& f){ensureSize(id); moveRotUsed=true; _move[id]+=f;}
const Vector3r& getRot(Body::id_t id){ensureSize(id); return _rot[id];}
void addRot(Body::id_t id,const Vector3r& f){ensureSize(id); moveRotUsed=true; _rot[id]+=f;}
+ void addPermForce(Body::id_t id, const Vector3r& f){ ensureSize(id); _permForce[id]=f; permForceUsed=true;}
+ void addPermTorque(Body::id_t id, const Vector3r& t){ ensureSize(id); _permTorque[id]=t; permForceUsed=true;}
+ const Vector3r& getPermForce(Body::id_t id) { ensureSize(id); return _permForce[id]; }
+ const Vector3r& getPermTorque(Body::id_t id) { ensureSize(id); return _permTorque[id]; }
// single getters do the same as globally synced ones in the non-parallel flavor
- const Vector3r& getForceSingle (Body::id_t id){ ensureSize(id); return _force [id]; }
- const Vector3r& getTorqueSingle(Body::id_t id){ ensureSize(id); return _torque[id]; }
+ const Vector3r& getForceSingle (Body::id_t id){
+ ensureSize(id);
+ if (permForceUsed) {
+ return _force [id] + _permForce[id];
+ } else {
+ return _force [id];
+ }
+ }
+ const Vector3r& getTorqueSingle(Body::id_t id){
+ ensureSize(id);
+ if (permForceUsed) {
+ return _torque[id] + _permTorque[id];
+ } else {
+ return _torque[id];
+ }
+ }
const Vector3r& getMoveSingle (Body::id_t id){ ensureSize(id); return _move [id]; }
const Vector3r& getRotSingle (Body::id_t id){ ensureSize(id); return _rot [id]; }
//! Set all forces to zero
- void reset(long iter){
+ void reset(long iter, bool resetAll=false){
memset(&_force [0],0,sizeof(Vector3r)*size);
memset(&_torque[0],0,sizeof(Vector3r)*size);
if(moveRotUsed){
@@ -217,10 +262,18 @@
memset(&_rot [0],0,sizeof(Vector3r)*size);
moveRotUsed=false;
}
+ if (resetAll){
+ memset(&_permForce [0], 0,sizeof(Vector3r)*size);
+ memset(&_permTorque[0], 0,sizeof(Vector3r)*size);
+ permForceUsed = false;
+ }
lastReset=iter;
}
- //! No-op for API compatibility with the threaded version
- void sync(){return;}
+
+ void sync(){
+ if (permForceUsed) for(long id=0; id<(long)size; id++)
+ {_force[id]+=_permForce[id]; _torque[id]+=_permTorque[id];}
+ return;}
unsigned long syncCount;
// interaction in which the container was last reset; used by NewtonIntegrator to detect whether ForceResetter was not forgotten
long lastReset;
@@ -229,12 +282,15 @@
void resize(size_t newSize){
_force.resize(newSize,Vector3r::Zero());
_torque.resize(newSize,Vector3r::Zero());
+ _permForce.resize(newSize,Vector3r::Zero());
+ _permTorque.resize(newSize,Vector3r::Zero());
_move.resize(newSize,Vector3r::Zero());
_rot.resize(newSize,Vector3r::Zero());
size=newSize;
}
const int getNumAllocatedThreads() const {return 1;}
const bool& getMoveRotUsed() const {return moveRotUsed;}
+ const bool& getPermForceUsed() const {return permForceUsed;}
};
#endif
=== modified file 'core/Functor.hpp'
--- core/Functor.hpp 2013-04-23 14:07:34 +0000
+++ core/Functor.hpp 2013-09-27 12:11:28 +0000
@@ -25,7 +25,7 @@
Scene* scene;
virtual ~Functor(); // defined in Dispatcher.cpp
YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(Functor,Serializable,"Function-like object that is called by Dispatcher, if types of arguments match those the Functor declares to accept.",
- ((string,label,,,"Textual label for this object; must be valid python identifier, you can refer to it directly fron python (must be a valid python identifier).")),
+ ((string,label,,,"Textual label for this object; must be a valid python identifier, you can refer to it directly from python.")),
/*ctor*/
#ifdef USE_TIMING_DELTAS
timingDeltas=shared_ptr<TimingDeltas>(new TimingDeltas);
=== modified file 'core/Scene.cpp'
--- core/Scene.cpp 2013-03-07 18:28:01 +0000
+++ core/Scene.cpp 2013-08-23 15:21:20 +0000
@@ -107,11 +107,7 @@
long dif = duration.total_microseconds();
SpeedElements(iter%nSpeedIter,0)=1000000.0 / dif;
- #if EIGEN_WORLD_VERSION==2
- speed = SpeedElements.sum()/nSpeedIter;
- #elif EIGEN_WORLD_VERSION==3
- speed = SpeedElements.mean();
- #endif
+ speed = SpeedElements.mean();
prevTime = timeNow;
}
=== modified file 'core/Scene.hpp'
--- core/Scene.hpp 2012-07-16 20:13:46 +0000
+++ core/Scene.hpp 2013-10-02 12:47:38 +0000
@@ -111,8 +111,8 @@
SpeedElements.Zero();
,
/* py */
- .add_property("localCoords",&Scene::usesLocalCoords,"Whether local coordianate system is used on interactions (set by :yref:`Ig2Functor`.")
- .add_property("compressionNegative",&Scene::usesLocalCoords,"Whether the convention is that compression has negative sign (set by :yref:`Ig2Functor`.")
+ .add_property("localCoords",&Scene::usesLocalCoords,"Whether local coordianate system is used on interactions (set by :yref:`IGeomFunctor`).")
+ .add_property("compressionNegative",&Scene::usesLocalCoords,"Whether the convention is that compression has negative sign (set by :yref:`IGeomFunctor`).")
);
DECLARE_LOGGER;
};
=== modified file 'core/main/main.py.in'
--- core/main/main.py.in 2013-05-07 09:21:35 +0000
+++ core/main/main.py.in 2013-09-10 14:53:54 +0000
@@ -11,7 +11,7 @@
# get yade path (allow YADE_PREFIX to override)
prefix,suffix='${runtimePREFIX}' if not os.environ.has_key('YADE_PREFIX') else os.environ['YADE_PREFIX'],'${SUFFIX}'
# duplicate some items from yade.config here, so that we can increase verbosity when the c++ part is booting
-features,version='${features}'.split(' '),'${realVersion}'
+features,version,debugbuild='${features}'.split(' '),'${realVersion}',' ${debugbuild}'
libPATH='${LIBRARY_OUTPUT_PATH}'
if (libPATH[1:] == '{LIBRARY_OUTPUT_PATH}'): libPATH='lib'
@@ -52,11 +52,17 @@
par.add_argument('--performance',help='Starts a test to measure the productivity',dest='performance',action='store_true')
par.add_argument('script',nargs='?',default='',type=str,help=argparse.SUPPRESS)
par.add_argument('args',nargs=argparse.REMAINDER,help=argparse.SUPPRESS) # see argparse doc, par.disable_interspersed_args() from optargs module
+par.add_argument('-l',help='import libraries at startup before importing yade libs. May be used when the ordering of imports matter (see e.g. https://bugs.launchpad.net/yade/+bug/1183402/comments/3). The option can be use multiple times, as in "yade -llib1 -llib2"',default=None,action='append',dest='impLibraries',type=str)
opts=par.parse_args()
args = opts.args
+if opts.impLibraries:
+ sys.path.append('.')
+ for lib in opts.impLibraries:
+ __import__(lib)
+
if opts.version:
- print 'Yade version: %s'%version
+ print 'Yade version: %s%s'%(version,debugbuild)
sys.exit(0)
if opts.script:
@@ -109,7 +115,7 @@
else: os.environ['OMP_NUM_THREADS']='1'
if __name__ == "__main__": # do not print this while importing yade in other python application
- sys.stderr.write('Welcome to Yade '+version+'\n')
+ sys.stderr.write('Welcome to Yade '+version+debugbuild+'\n')
# initialization and c++ plugins import
import yade
@@ -242,6 +248,7 @@
# usually Xlib.error.DisplayError, but there can be Xlib.error.XauthError etc as well
# let's just pretend any exception means the display would not work
gui=None
+ print 'Warning: no X rendering available (see https://bbs.archlinux.org/viewtopic.php?id=13189)'
# run remote access things, before actually starting the user session (not while imported by other python application)
if __name__ == "__main__":
=== modified file 'doc/references.bib'
--- doc/references.bib 2013-07-08 08:52:03 +0000
+++ doc/references.bib 2013-10-04 12:35:58 +0000
@@ -57,14 +57,6 @@
doi = "10.1061/(ASCE)0733-9399(2005)131:7(689)"
}
-@Article{ Duriez2010,
- author = "J. Duriez and F.Darve and F.-V.Donze",
- title = "A discrete modeling-based constitutive relation for infilled rock joints",
- year = "2010",
- journal = "International Journal of Rock Mechanics \& Mining Sciences",
- note = "in press"
-}
-
@PhDThesis{ Chareyre2003,
author = "Bruno Chareyre",
title = "Modélisation du comportement d'ouvrages composites sol-géosynthétique par éléments discrets - Application aux tranchées d'ancrage en tête de talus.",
@@ -560,7 +552,7 @@
}
@article{Herminghaus2005,
- author = {Herminghaus * , S.},
+ author = {Herminghaus, S.},
title = {Dynamics of wet granular matter},
journal = {Advances in Physics},
volume = {54},
@@ -588,18 +580,6 @@
url = "http://www.refdoc.fr/Detailnotice?idarticle=7435486"
}
-@article{Thoeni2013,
- author = "K. Thoeni and C. Lambert and A. Giacomini and S.W. Sloan",
- doi = "10.1016/j.compgeo.2012.10.014",
- issn = "0266-352X",
- journal = "Computers and Geotechnics",
- pages = "158--169",
- title = "{Discrete modelling of hexagonal wire meshes with a stochastically distorted contact model}",
- url = "http://www.sciencedirect.com/science/article/pii/S0266352X12002121",
- volume = "49",
- year = "2013"
-}
-
@article{Luding2008,
year={2008},
issn={1434-5021},
@@ -615,3 +595,38 @@
pages={235-246},
language={English}
}
+
+@article{Zhou1999536,
+ title = "Rolling friction in the dynamic simulation of sandpile formation",
+ journal = "Physica A: Statistical Mechanics and its Applications",
+ volume = "269",
+ number = "2–4",
+ pages = "536--553",
+ year = "1999",
+ issn = "0378-4371",
+ doi = "10.1016/S0378-4371(99)00183-1",
+ url = "http://www.sciencedirect.com/science/article/pii/S0378437199001831",
+ author = "Y.C. Zhou and B.D. Wright and R.Y. Yang and B.H. Xu and A.B. Yu",
+}
+
+@article{Antypov2011,
+ author={D. Antypov and J. A. Elliott},
+ title={On an analytical solution for the damped Hertzian spring},
+ journal={EPL (Europhysics Letters)},
+ volume={94},
+ number={5},
+ pages={50004},
+ url={http://stacks.iop.org/0295-5075/94/i=5/a=50004},
+ year={2011}
+}
+
+@article{Ivars2011,
+title = "The synthetic rock mass approach for jointed rock mass modelling ",
+journal = "International Journal of Rock Mechanics and Mining Sciences ",
+volume = "48",
+number = "2",
+pages = "219 - 244",
+year = "2011",
+doi = "10.1016/j.ijrmms.2010.11.014",
+author = "Diego Mas Ivars and Matthew E. Pierce and Caroline Darcel and Juan Reyes-Montes and David O. Potyondy and R. Paul Young and Peter A. Cundall"
+}
=== modified file 'doc/sphinx/conf.py'
--- doc/sphinx/conf.py 2013-03-21 19:28:57 +0000
+++ doc/sphinx/conf.py 2013-08-14 08:01:55 +0000
@@ -64,7 +64,7 @@
m=re.match('(.*)\s*<(.*)>\s*',id)
if m:
txt,id=m.group(1),m.group(2)
- return [nodes.reference(rawtext,docutils.utils.unescape(txt),refuri='http://bazaar.launchpad.net/~yade-dev/yade/trunk/%s/head%%3A/%s'%('files' if txt.endswith('/') else 'annotate',id))],[] ### **options should be passed to nodes.reference as well
+ return [nodes.reference(rawtext,docutils.utils.unescape(txt),refuri='https://github.com/yade/trunk/blob/master/%s'%id)],[] ### **options should be passed to nodes.reference as well
# map modules to their html (rst) filenames. Used for sub-modules, where e.g. SpherePack is yade._packSphere.SpherePack, but is documented from yade.pack.rst
moduleMap={
=== added file 'doc/sphinx/fig/micro-domains.png'
Binary files doc/sphinx/fig/micro-domains.png 1970-01-01 00:00:00 +0000 and doc/sphinx/fig/micro-domains.png 2013-08-19 17:51:24 +0000 differ
=== modified file 'doc/sphinx/formulation.rst'
--- doc/sphinx/formulation.rst 2013-05-14 21:24:11 +0000
+++ doc/sphinx/formulation.rst 2013-11-06 15:53:55 +0000
@@ -314,8 +314,6 @@
-------------
In order to keep $\vec{u}_T$ consistent (e.g. that $\vec{u}_T$ must be constant if two spheres retain mutually constant configuration but move arbitrarily in space), then either $\vec{u}_T$ must track spheres' spatial motion or must (somehow) rely on sphere-local data exclusively.
-These two possibilities lead to two algorithms of computing shear strains. They should give the same results (disregarding numerical imprecision), but there is a trade-off between computational cost of the incremental method and robustness of the total one.
-
Geometrical meaning of shear strain is shown in `fig-shear-2d`_.
.. _fig-shear-2d:
@@ -323,9 +321,7 @@
Evolution of shear displacement $\vec{u}_T$ due to mutual motion of spheres, both linear and rotational. Left configuration is the initial contact, right configuration is after displacement and rotation of one particle.
-Incremental algorithm
-^^^^^^^^^^^^^^^^^^^^^
-The incremental algorithm is widely used in DEM codes and is described frequently ([Luding2008]_, [Alonso2004]_). Yade implements this algorithm in the :yref:`ScGeom` class. At each step, shear displacement $\uT$ is updated; the update increment can be decomposed in 2 parts: motion of the interaction (i.e. $\vec{C}$ and $\vec{n}$) in global space and mutual motion of spheres.
+The classical incremental algorithm is widely used in DEM codes and is described frequently ([Luding2008]_, [Alonso2004]_). Yade implements this algorithm in the :yref:`ScGeom` class. At each step, shear displacement $\uT$ is updated; the update increment can be decomposed in 2 parts: motion of the interaction (i.e. $\vec{C}$ and $\vec{n}$) in global space and mutual motion of spheres.
#. Contact moves dues to changes of the spheres' positions $\vec{C}_1$ and $\vec{C}_2$, which updates current $\currC$ and $\currn$ as per :eq:`eq-contact-point` and :eq:`eq-contact-normal`. $\prevuT$ is perpendicular to the contact plane at the previous step $\prevn$ and must be updated so that $\prevuT+(\Delta\uT)=\curruT\perp\currn$; this is done by perpendicular projection to the plane first (which might decrease $|\uT|$) and adding what corresponds to spatial rotation of the interaction instead:
@@ -353,74 +349,6 @@
.. math:: \curruT=\prevuT+(\Delta\uT)_1 + (\Delta\uT)_2 + (\Delta\uT)_3.
-.. _sect-formulation-total-shear:
-
-Total algorithm
-^^^^^^^^^^^^^^^
-The following algorithm, aiming at stabilization of response even with large rotation speeds or $\Delta t$ approaching stability limit, was designed in [Smilauer2010b]_. (A similar algorithm based on total formulation, which covers additionally bending and torsion, was proposed in [Wang2009]_.) It is based on tracking original contact points (with zero shear) in the particle-local frame.
-
-In this section, variable symbols implicitly denote their current values unless explicitly stated otherwise.
-
-Shear strain may have two sources: mutual rotation of spheres or transversal displacement of one sphere with respect to the other. Shear strain does not change if both spheres move or rotate but are not in linear or angular motion mutually. To accurately and reliably model this situation, for every new contact the initial contact point $\bar{\vec{C}}$ is mapped into local sphere coordinates ($\vec{p}_{01}$, $\vec{p}_{02}$). As we want to determine the distance between both points (i.e. how long the trajectory in on both spheres' surfaces together), the shortest path from current $\vec{C}$ to the initial locally mapped point on the sphere's surface is „unrolled“ to the contact plane ($\vec{p}'_{01}$, $\vec{p}'_{02}$); then we can measure their linear distance $\uT$ and define shear strain $\vec{\eps}_T=\uT/d_0$ (fig. `fig-shear-displacement`_).
-
-More formally, taking $\bar{\vec{C}}_i$, $\bar{q}_i$ for the sphere initial positions and orientations (as quaterions) in global coordinates, the initial sphere-local contact point *orientation* (relative to sphere-local axis $\hat{x}$) is remembered:
-
-.. math::
- :nowrap:
-
- \begin{align*}
- \bar{\vec{n}}&=\normalized{{\vec{C}}_1-{\vec{C}}_2}, \\
- \bar{q}_{01}&=\Align(\hat x,\bar{q}_1^*\bar{\vec{n}} \bar{q}_1^{**}), \\
- \bar{q}_{02}&=\Align(\hat x,\bar{q}_2^* (-\bar{\vec{n}}) \bar{q}_2^{**}).
- \end{align*}
-
-.. (See \autoref{sect-quaternions} for definition of $\Align$.)
-
-
-After some spheres motion, the original point can be "unrolled" to the current contact plane:
-
-.. math::
- :nowrap:
-
- \begin{align*}
- q&=\Align(\vec{n},q_1 \bar{q}_{01} \hat x (q_1 \bar{q}_{01})^*) \quad\hbox{(auxiliary)} \\
- \vec{p}'_{01}&=q_{\theta}d_1(q_{\vec{u}} \times \vec{n})
- \end{align*}
-
-where $q_{\vec{u}}$, $q_{\theta}$ are axis and angle components of $q$ and $p_{01}'$ is the unrolled point. Similarly,
-
-.. math::
- :nowrap:
-
- \begin{align*}
- q&=\Align(\vec{n},q_2 \bar{q}_{02} \hat x (q_2 \bar{q}_{02})^*) \\
- \vec{p}'_{02}&=q_{\theta}d_1(q_{\vec{u}} \times (-\vec{n})).
- \end{align*}
-
-Shear displacement and strain are then computed easily:
-
-.. math::
- :nowrap:
-
- \begin{align*}
- \uT&=\vec{p}'_{02}-\vec{p}'_{01} \\
- \vec{\eps}_T&=\frac{\uT}{d_0}
- \end{align*}
-
-When using material law with plasticity in shear, it may be necessary to limit maximum shear strain, in which case the mapped points are moved closer together to the requested distance (without changing $\hat{\vec{u}}_T$). This allows us to remember the previous strain direction and also avoids summation of increments of plastic strain at every step (`fig-shear-slip`_).
-
-.. _fig-shear-displacement:
-.. figure:: fig/shear-displacement.*
-
- Shear displacement computation for two spheres in relative motion.
-
-
-.. _fig-shear-slip:
-.. figure:: fig/shear-slip.*
-
- Shear plastic slip for two spheres.
-
-This algorithm is straightforwardly modified to facet-sphere interactions. In Yade, it is implemented by :yref:`Dem3DofGeom` and related classes.
.. _sect-formulation-stress-cundall:
@@ -736,7 +664,7 @@
In Yade, particles have associated :yref:`Material` which defines density $\rho$ (:yref:`Material.density`), and also may define (in :yref:`ElastMat` and derived classes) particle's "Young's modulus" $E$ (:yref:`ElastMat.young`). $\rho$ is used when particle's mass $m$ is initially computed from its $\rho$, while $E$ is taken in account when creating new interaction between particles, affecting stiffness $K_N$. Knowing $m$ and $K_N$, we can estimate :eq:`eq-dtcr-particle-stiffness` for each particle; we obviously neglect
-* number of interactions per particle $N_i$; for a "reasonable" radius distribution, however, there is a geometrically imposed upper limit (6 for a packing of spheres with equal radii, for instance);
+* number of interactions per particle $N_i$; for a "reasonable" radius distribution, however, there is a geometrically imposed upper limit (6 for a 2D-packing of spheres with equal radii, for instance);
* the exact relationship the between particles' rigidities $E_i$, $E_j$, supposing only that $K_N$ is somehow proportional to them.
By defining $E$ and $\rho$, particles have continuum-like quantities. Explicit integration schemes for continuum equations impose a critical timestep based on sonic speed $\sqrt{E/\rho}$; the elastic wave must not propagate farther than the minimum distance of integration points $l_{\rm min}$ during one step. Since $E$, $\rho$ are parameters of the elastic continuum and $l_{\rm min}$ is fixed beforehand, we obtain
@@ -800,13 +728,13 @@
K_N&=\frac{A_{\rm eq}E}{d_0}.
\end{align*}
- $d_0$ is the initial contact length, which will be, for interaction radius :eq:`eq-strain-interaction-radius` $R_I>1$, in average larger than $2R$. For $R_I=1.5$ (sect.~\ref{sect-calibration-elastic-properties}), we can roughly estimate $\overline{d}_0=1.25\cdot2R=\frac{5}{2}R$, getting
+ $d_0$ is the initial contact length, which will be, for interaction radius :eq:`eq-strain-interaction-radius` $R_I>1$, in average larger than $2R$. For $R_I=1.5$ (sect. :ref:`sect-calibration-elastic-properties`), we can roughly estimate $\overline{d}_0=1.25\cdot2R=\frac{5}{2}R$, getting
.. math:: K_N=E\left(\frac{2}{5}\pi\right)R
where $\frac{2}{5}\pi=\pi'$ by comparison with :eq:`eq-dt-kn`.
- Interaction radius $R_I=1.5$ leads to average $N\approx12$ interactions per sphere for dense packing of spheres with the same radius $R$. $\xi=0.2$ is calibrated (sect.~\ref{sect-calibration-elastic-properties}) to match the desired macroscopic Poisson's ratio $\nu=0.2$.
+ Interaction radius $R_I=1.5$ leads to average $N\approx12$ interactions per sphere for dense packing of spheres with the same radius $R$. $\xi=0.2$ is calibrated (sect. :ref:`sect-calibration-elastic-properties`) to match the desired macroscopic Poisson's ratio $\nu=0.2$.
Finally, we obtain the ratio
@@ -815,7 +743,7 @@
showing significant overestimation by the p-wave algorithm.
Non-cohesive dry friction model
- is the basic model proposed by Cundall explained in \ref{sect-formulation-stress-cundall}. Supposing almost-constant sphere radius $R$ and rather dense packing, each sphere will have $N=6$ interactions on average (that corresponds to maximally dense packing of spheres with a constant radius). If we use the :yref:`Ip2_FrictMat_FrictMat_FrictPhys` class, we have $\pi'=2$, as $K_N=E2R$; we again use $\xi=0.2$ (for lack of a more significant value). In this case, we obtain the result
+ is the basic model proposed by Cundall explained in :ref:`sect-formulation-stress-cundall`. Supposing almost-constant sphere radius $R$ and rather dense packing, each sphere will have $N=6$ interactions on average (that corresponds to maximally dense packing of spheres with a constant radius). If we use the :yref:`Ip2_FrictMat_FrictMat_FrictPhys` class, we have $\pi'=2$, as $K_N=E2R$; we again use $\xi=0.2$ (for lack of a more significant value). In this case, we obtain the result
.. math:: \frac{\Dtcr^{(p)}}{\Dtcr}=2\sqrt{\frac{6(1-2\cdot0.2)}{\pi/2}}=3.02
@@ -861,7 +789,7 @@
Approximate collision detection
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Pass 1 collision detection (based on sweep and prune algorithm, sect.~\ref{sect-sweep-and-prune}) operates on axis-aligned bounding boxes (:yref:`Aabb`) of particles. During the collision detection phase, bounds of all :yref:`Aabb's<Aabb>` are wrapped inside the cell in the first step. At subsequent runs, every bound remembers by how many cells it was initially shifted from coordinate given by the :yref:`Aabb` and uses this offset repeatedly as it is being updated from :yref:`Aabb` during particle's motion. Bounds are sorted using the periodic insertion sort algorithm (sect.~\ref{sect-periodic-insertion-sort}), which tracks periodic cell boundary $||$.
+Pass 1 collision detection (based on sweep and prune algorithm, sect. :ref:`sect-sweep-and-prune`) operates on axis-aligned bounding boxes (:yref:`Aabb`) of particles. During the collision detection phase, bounds of all :yref:`Aabb's<Aabb>` are wrapped inside the cell in the first step. At subsequent runs, every bound remembers by how many cells it was initially shifted from coordinate given by the :yref:`Aabb` and uses this offset repeatedly as it is being updated from :yref:`Aabb` during particle's motion. Bounds are sorted using the periodic insertion sort algorithm (sect. :ref:`sect-periodic-insertion-sort`), which tracks periodic cell boundary $||$.
Upon inversion of two :yref:`Aabb`'s, their collision along all three axes is checked, wrapping real coordinates inside the cell for that purpose.
@@ -970,7 +898,7 @@
----
The DEM computation using an explicit integration scheme demands a relatively high number of steps during simulation, compared to implicit scehemes. The total computation time $Z$ of simulation spanning $T$ seconds (of simulated time), containing $N$ particles in volume $V$ depends on:
-* linearly, the number of steps $i=T/(s_t \Dtcr)$, where $s_t$ is timestep safety factor; $\Dtcr$ can be estimated by p-wave velocity using $E$ and $\rho$ (sect.~\ref{sect-dt-pwave}) as $\Dtcr^{(p)}=r\sqrt{\frac{\rho}{E}}$. Therefore
+* linearly, the number of steps $i=T/(s_t \Dtcr)$, where $s_t$ is timestep safety factor; $\Dtcr$ can be estimated by p-wave velocity using $E$ and $\rho$ (sect. :ref:`sect-dt-pwave`) as $\Dtcr^{(p)}=r\sqrt{\frac{\rho}{E}}$. Therefore
.. math:: i=\frac{T}{s_t r}\sqrt{\frac{E}{\rho}}.
@@ -980,7 +908,7 @@
where $p$ is packing porosity, roughly $\frac{1}{2}$ for dense irregular packings of spheres of similar radius.
- The dependency is not strictly linear (which would be the best case), as some algorithms do not scale linearly; a case in point is the sweep and prune collision detection algorithm introduced in sect.~\ref{sect-sweep-and-prune}, with scaling roughly $\bigO{N \log N}$.
+ The dependency is not strictly linear (which would be the best case), as some algorithms do not scale linearly; a case in point is the sweep and prune collision detection algorithm introduced in sect. :ref:`sect-sweep-and-prune`, with scaling roughly $\bigO{N \log N}$.
The number of interactions scales with $N$, as long as packing characteristics are the same.
* the number of computational cores $\numCPU$; in the ideal case, the dependency would be inverse-linear were all algorithms parallelized (in Yade, collision detection is not).
=== modified file 'doc/sphinx/installation.rst'
--- doc/sphinx/installation.rst 2013-03-26 19:21:11 +0000
+++ doc/sphinx/installation.rst 2013-11-14 09:27:47 +0000
@@ -12,27 +12,19 @@
Packages from Launchpad PPA service (package personal archive) are
provided for all currently supported Ubuntu versions for
-`stable <https://launchpad.net/~yade-pkg/+archive/stable>`_ and
`daily <https://launchpad.net/~yade-pkg/+archive/snapshots>`_ releases.
``yade-daily`` is a automatically daily (if there were some commtis during
-the previous days) geenrated package, which includes all the newly added
+the previous days) generated package, which includes all the newly added
features. To install version from PPA, run the following:
-* For stable releases::
-
- sudo add-apt-repository ppa:yade-pkg/stable # for stable releases
- sudo add-apt-repository ppa:yade-users/external # optional (updates of other packages)
- sudo apt-get update
- sudo apt-get install yade-stable
-
* For latest builds from trunk::
sudo add-apt-repository ppa:yade-pkg/snapshots # for latest daily (mostly) releases
sudo apt-get update
sudo apt-get install yade-daily
-After you added whether stable or snapshot PPAs, you will get automatically
-the updates of the package, when they arrive the PPA.
+After you added snapshot PPA, you will get automatically the updates of the package,
+when they arrive the PPA.
More detailed instructions are available at the corresponding pages of
ppa`s (links above).
@@ -87,8 +79,8 @@
Release and trunk sources are compiled in the same way.
-Prerequisities
-^^^^^^^^^^^^^^^
+Prerequisites
+^^^^^^^^^^^^^
Yade relies on a number of external software to run; its installation is checked before the compilation starts.
@@ -105,6 +97,7 @@
* `sqlite3 <http://www.sqlite.org>`_ database engine
* `Loki <http://loki-lib.sf.net>`_ library
* `VTK <http://www.vtk.org/>`_ library (optional but recommended)
+* `CGAL <http://www.cgal.org/>`_ library (optional)
Most of the list above is very likely already packaged for your distribution.
The following commands have to be executed in command line of corresponding
@@ -114,25 +107,34 @@
* **Ubuntu**, **Debian** and their derivatives::
sudo apt-get install cmake git freeglut3-dev libloki-dev \
- libboost-date-time-dev libboost-filesystem-dev libboost-thread-dev \
- libboost-program-options-dev \
- libboost-regex-dev fakeroot dpkg-dev build-essential g++ \
- libboost-iostreams-dev python-dev libboost-python-dev ipython \
- python-matplotlib libsqlite3-dev python-numpy python-tk gnuplot \
+ libboost-all-dev fakeroot dpkg-dev build-essential g++ \
+ python-dev ipython python-matplotlib libsqlite3-dev python-numpy python-tk gnuplot \
libgts-dev python-pygraphviz libvtk5-dev python-scientific libeigen3-dev \
- binutils-gold python-xlib python-qt4 pyqt4-dev-tools \
- gtk2-engines-pixbuf python-argparse \
- libqglviewer-qt4-dev python-imaging libjs-jquery python-sphinx python-git python-bibtex \
- libxmu-dev libxi-dev libgmp3-dev libcgal-dev help2man
-
- * **Fedora**::
-
- yum install cmake qt3-devel freeglut-devel boost-devel boost-date-time \
- boost-filesystem boost-thread boost-regex fakeroot gcc gcc-c++ boost-iostreams \
- python-devel boost-python ipython python-matplotlib \
- sqlite-devel python-numeric ScientificPython-tk gnuplot doxygen gts-devel \
- graphviz-python vtk-devel ScientificPython eigen2-devel libQGLViewer-devel \
- loki-lib-devel python-xlib PyQt4 PyQt4-devel python-imaging python-sphinx python-bibtex
+ python-xlib python-qt4 pyqt4-dev-tools gtk2-engines-pixbuf python-argparse \
+ libqglviewer-dev python-imaging libjs-jquery python-sphinx python-git python-bibtex \
+ libxmu-dev libxi-dev libcgal-dev help2man libsuitesparse-dev \
+ libopenblas-dev libbz2-dev zlib1g-dev
+
+Additional packages, which can become mandatory later::
+
+ sudo apt-get install libmetis-dev python-gts python-minieigen \
+
+Some packages, which are listed here, are relatively new and they can absent
+in your distribution (for example, libmetis-dev or python-gts). They can be
+installed from our `external PPA <https://launchpad.net/~yade-users/+archive/external/>`_
+or just ignored. In this case some features can be disabled.
+
+If you are using other distribuition, than Debian or its derivatives, you should
+install the software, which is listed above. Their names can differ from the
+names of Debian-packages.
+
+Some of packages (for example, cmake, eigen3) are mandatory, some of them
+are optional. Watch for notes and warnings/errors, which are shown
+by cmake during configuration step. If the missing package is optional,
+some of Yade features will be disabled (see the messages at the end of configuration).
+
+.. warning:: if you have Ubuntu 12.10 or older, you should install libqglviewer-qt4-dev
+ package instead of libqglviewer-dev.
Compilation
@@ -164,6 +166,8 @@
* ENABLE_OPENMP: enable OpenMP-parallelizing option (ON by default)
* ENABLE_GTS: enable GTS-option (ON by default)
* ENABLE_GL2PS: enable GL2PS-option (ON by default)
+ * ENABLE_LINSOLV: enable LINSOLV-option (ON by default)
+ * ENABLE_PFVFLOW: enable PFVFLOW-option, FlowEngine (ON by default)
* runtimePREFIX: used for packaging, when install directory is not the same is runtime directory (/usr/local by default)
* CHUNKSIZE: used, if you want several sources to be compiled at once. Increases compilation speed and RAM-consumption during it (1 by default).
=== modified file 'doc/sphinx/introduction.rst'
--- doc/sphinx/introduction.rst 2013-04-24 19:49:00 +0000
+++ doc/sphinx/introduction.rst 2013-09-27 12:13:27 +0000
@@ -15,7 +15,7 @@
Before you start moving around in Yade, you should have some prior knowledge.
* Basics of command line in your Linux system are necessary for running yade. Look on the web for tutorials.
-* Python language; we recommend the official `Python tutorial <http://docs.python.org/tutorial>`_. Reading further documents on the topis, such as `Dive into Python <http://diveintopython.org/>`_ will certainly not hurt either.
+* Python language; we recommend the official `Python tutorial <http://docs.python.org/tutorial>`_. Reading further documents on the topis, such as `Dive into Python <http://www.diveintopython.net/>`_ will certainly not hurt either.
You are advised to try all commands described yourself. Don't be afraid to experiment.
@@ -343,7 +343,7 @@
:yref:`IGeom`
holding geometrical configuration of the two particles in collision; it is updated automatically as the particles in question move and can be queried for various geometrical characteristics, such as penetration distance or shear strain.
- Based on combination of types of :yref:`Shapes<Shape>` of the particles, there might be different storage requirements; for that reason, a number of derived classes exists, e.g. for representing geometry of contact between :yref:`Sphere+Sphere<Dem3DofGeom_SphereSphere>`, :yref:`Facet+Sphere<Dem3DofGeom_FacetSphere>` etc.
+ Based on combination of types of :yref:`Shapes<Shape>` of the particles, there might be different storage requirements; for that reason, a number of derived classes exists, e.g. for representing geometry of contact between :yref:`Sphere+Sphere<ScGeom>`, :yref:`Cylinder+Sphere<CylScGeom>` etc. Note, however, that it is possible to represent many type of contacts with the basic sphere-sphere geometry (for instance in :yref:`Ig2_Wall_Sphere_ScGeom`).
:yref:`IPhys`
representing non-geometrical features of the interaction; some are computed from :yref:`Materials<Material>` of the particles in contact using some averaging algorithm (such as contact stiffness from Young's moduli of particles), others might be internal variables like damage.
@@ -417,7 +417,7 @@
.. _img-yade-iter-loop:
.. figure:: fig/yade-iter-loop.*
- Typical simulation loop; each step begins at body-cented bit at 11 o'clock, continues with interaction bit, force application bit, miscillanea and ends with time update.
+ Typical simulation loop; each step begins at body-centered bit at 11 o'clock, continues with interaction bit, force application bit, miscillanea and ends with time update.
Each of these actions is represented by an :yref:`Engine<Engine>`, functional element of simulation. The sequence of engines is called *simulation loop*.
=== modified file 'doc/sphinx/prog.rst'
--- doc/sphinx/prog.rst 2013-04-24 19:49:00 +0000
+++ doc/sphinx/prog.rst 2013-09-11 10:53:43 +0000
@@ -146,7 +146,7 @@
Check tests
^^^^^^^^^^^
-Check tests perform comparisons of simulation results between different versions of yade, as discussed in http://www.mail-archive.com/yade-dev@xxxxxxxxxxxxxxxxxxx/msg05784.html and the whole thread. They differ with regression tests in the sense that they simulate more complex situations and combinations of different engines, and usually don't have a mathematical proof (though there is no restriction on the latest). They compare the values obtained in version N with values obtained in a previous version or any other "expected" results. The reference values must be hardcoded in the script itself or in data files provided with the script. Check tests are based on regular yade scripts, so that users can easily commit their own scripts to trunk in order to get some automatized testing after commits from other developers.
+Check tests perform comparisons of simulation results between different versions of yade, as discussed `here <http://www.mail-archive.com/yade-dev@xxxxxxxxxxxxxxxxxxx/msg05784.html>`__. They differ with regression tests in the sense that they simulate more complex situations and combinations of different engines, and usually don't have a mathematical proof (though there is no restriction on the latest). They compare the values obtained in version N with values obtained in a previous version or any other "expected" results. The reference values must be hardcoded in the script itself or in data files provided with the script. Check tests are based on regular yade scripts, so that users can easily commit their own scripts to trunk in order to get some automatized testing after commits from other developers.
Since the check tests history will be mostly based on standard output generated by "yade ---check", a meaningfull checkTest should include some "print" command telling if something went wrong. If the script itself fails for some reason and can't generate an output, the log will contain "scriptName failure". If the script defines differences on obtained and awaited data, it should print some useful information about the problem and increase the value of global variable resultStatus. After this occurs, the automatic test will stop the execution with error message.
@@ -247,7 +247,7 @@
^^^^^^^^^^^^^^^^^^^^^
Most c++ classes are wrapped in Python, which provides good introspection and interactive documentation (try writing ``Material?`` in the ipython prompt; or ``help(CpmState)``).
-Syntax of documentation is `ReST <http://docutils.sourceforge.net/rst.html`__ (reStructuredText, see `reStructuredText Primer <http://sphinx.pocoo.org/rest.html>`__). It is the same for c++ and python code.
+Syntax of documentation is `ReST <http://docutils.sourceforge.net/rst.html>`__ (reStructuredText, see `reStructuredText Primer <http://sphinx.pocoo.org/rest.html>`__). It is the same for c++ and python code.
* Documentation of c++ classes exposed to python is given as 3rd argument to :ref:`YADE_CLASS_BASE_DOC` introduced below.
@@ -872,7 +872,7 @@
.. code-block:: c++
- class Ig2_Facet_Sphere_Dem3DofGeom: public IGeomFunctor{
+ class Ig2_Facet_Sphere_ScGeom: public IGeomFunctor{
public:
// override the IGeomFunctor::go
@@ -914,7 +914,7 @@
If no functor is able to accept given types (first condition violated) or multiple functors have the same distance (in condition 2), an exception is thrown.
-This resolution mechanism makes it possible, for instance, to have a hierarchy of :yref:`Dem3DofGeom` classes (for different combination of shapes: :yref:`Dem3DofGeom_SphereSphere`, :yref:`Dem3DofGeom_FacetSphere`, :yref:`Dem3DofGeom_WallSphere`), but only provide a :yref:`LawFunctor` accepting ``Dem3DofGeom``, rather than having different laws for each shape combination.
+This resolution mechanism makes it possible, for instance, to have a hierarchy of :yref:`ScGeom` classes (for different combination of shapes), but only provide a :yref:`LawFunctor` accepting ``ScGeom``, rather than having different laws for each shape combination.
.. note:: Performance implications of dispatch resolution are relatively low. The dispatcher lookup is only done once, and uses fast lookup matrix (1D or 2D); then, the functor found for this type(s) is cached within the ``Interaction`` (or ``Body``) instance. Thus, regular functor call costs the same as dereferencing pointer and calling virtual method. There is `blueprint <https://blueprints.launchpad.net/yade/+spec/devirtualize-functor-calls>`__ to avoid virtual function call as well.
=== modified file 'doc/sphinx/references.bib'
--- doc/sphinx/references.bib 2013-06-06 09:27:41 +0000
+++ doc/sphinx/references.bib 2013-10-04 12:35:58 +0000
@@ -79,6 +79,17 @@
url={ http://geo.hmg.inpg.fr/frederic/Discrete_Element_Group_FVD.html }
}
+@article{Ivars2011,
+title = "The synthetic rock mass approach for jointed rock mass modelling ",
+journal = "International Journal of Rock Mechanics and Mining Sciences ",
+volume = "48",
+number = "2",
+pages = "219 - 244",
+year = "2011",
+doi = "10.1016/j.ijrmms.2010.11.014",
+author = "Diego Mas Ivars and Matthew E. Pierce and Caroline Darcel and Juan Reyes-Montes and David O. Potyondy and R. Paul Young and Peter A. Cundall"
+}
+
@article{Jerier2009,
journal={Granular Matter},
title={A geometric algorithm based on tetrahedral meshes to generate a dense polydisperse sphere packing},
@@ -305,12 +316,5 @@
doi = "10.1016/S0378-4371(99)00183-1",
url = "http://www.sciencedirect.com/science/article/pii/S0378437199001831",
author = "Y.C. Zhou and B.D. Wright and R.Y. Yang and B.H. Xu and A.B. Yu",
- keywords = "Static sandpiles",
- keywords = "Granular compaction",
- keywords = "Dynamics and kinematics of a particle and a system of particles",
- keywords = "Granular flow",
- keywords = "Mixing",
- keywords = "Segregation and stratification",
- keywords = "Granular systems "
}
=== modified file 'doc/sphinx/templates/index.html'
--- doc/sphinx/templates/index.html 2013-02-26 19:28:47 +0000
+++ doc/sphinx/templates/index.html 2013-09-26 13:26:34 +0000
@@ -8,6 +8,12 @@
Yade is located at <a href="https://www.yade-dem.org">www.yade-dem.org</a>, which contains <a href="https://www.yade-dem.org/doc/">this documentation</a> and <a href="https://www.yade-dem.org/wiki/">wiki</a>. Development is kindly hosted at <a href="http://www.launchpad.net/yade">launchpad</a>; it is used for <a href="http://code.launchpad.net/yade">source code</a>, <a href="http://bugs.launchpad.net/yade">bug tracking</a>, <a href="http://blueprints.launchpad.net/yade">planning</a>, <a href="https://launchpad.net/~yade-pkg/+archive/snapshots">package</a> and <a href="https://launchpad.net/yade/+download">source</a> downloads</a> and more.
</p>
<p>
+ Since March 2012 Yade is using GIT as VCS and placing the source code on
+ <a href="https://github.com/yade/trunk">github</a>. The Launchpad is importing
+ source code several times per day for the further buiding of "daily"-packages
+ for Ubuntu.
+ </p>
+ <p>
Please make sure you read the <a href="{{ pathto("citing") }}">"Acknowledging Yade"</a> section if you plan to cite Yade in publications.
</p>
<p>
@@ -22,7 +28,7 @@
<p class="biglink"><a class="biglink" href="https://launchpad.net/yade/+announcements">NEWS</a><br/>
<span class="linkdescr">latest news about Yade project</span></p>
<p class="biglink"><a class="biglink" href="https://yade-dem.org/wiki/Authors_and_contributors">Authors and contributors</a><br/>
- <span class="linkdescr">information about people, who are creating Yade</span></p>
+ <span class="linkdescr">information about the people working on Yade</span></p>
<p class="biglink"><a class="biglink" href="https://yade-dem.org/wiki/Contact">Contact</a><br/>
<span class="linkdescr">contact details, mailing lists</span></p>
<p class="biglink"><a class="biglink" href="https://yade-dem.org/wiki/WhoIsDoingWhat">Who Is Doing What (Wiki)</a><br/>
@@ -56,7 +62,7 @@
<p class="biglink"><a class="biglink" href="{{ pathto("user") }}">User's Manual</a><br/>
<span class="linkdescr">functionality guide and description</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("prog") }}">Programmer's Manual</a><br/>
- <span class="linkdescr">understanding and modifying the internals</span></p>
+ <span class="linkdescr">understanding and modifying the internals of Yade</span></p>
</td>
<td width="50%">
<p class="biglink"><a class="biglink" href="{{ pathto("formulation") }}">DEM Formulation</a><br/>
@@ -65,8 +71,6 @@
<span class="linkdescr">simulation building blocks, c++ & python</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("modules") }}">Yade modules</a><br/>
<span class="linkdescr">python modules extending Yade</span></p>
- <p class="biglink"><a class="biglink" href="{{ pathto("external") }}">External modules</a><br/>
- <span class="linkdescr">3rd party modules coming with Yade</span></p>
</td>
</tr>
<tr>
=== modified file 'doc/sphinx/user.rst'
--- doc/sphinx/user.rst 2013-05-14 20:32:05 +0000
+++ doc/sphinx/user.rst 2013-10-07 06:14:15 +0000
@@ -2,6 +2,17 @@
User's manual
###################
+
+.. The imports below are done at startup normaly, but it seems ineffective in the context of sphinx build, let us import silently
+.. ipython::
+
+ @suppress
+ Yade [0]: from yade.utils import *
+
+ @suppress
+ Yade [0]: from math import *
+
+
*******************
Scene construction
*******************
@@ -14,16 +25,16 @@
Creating Body objects
----------------------
-:yref:`Body` objects are only rarely constructed by hand by their components (:yref:`Shape`, :yref:`Bound`, :yref:`State`, :yref:`Material`); instead, convenience functions :yref:`yade.utils.sphere`, :yref:`yade.utils.facet` and :yref:`yade.utils.wall` are used to create them. Using these functions also ensures better future compatibility, if internals of :yref:`Body` change in some way. These functions receive geometry of the particle and several other characteristics. See their documentation for details. If the same :yref:`Material` is used for several (or many) bodies, it can be shared by adding it in ``O.materials``, as explained below.
+:yref:`Body` objects are only rarely constructed by hand by their components (:yref:`Shape`, :yref:`Bound`, :yref:`State`, :yref:`Material`); instead, convenience functions :yref:`sphere<yade.utils.sphere>`, :yref:`facet<yade.utils.facet>` and :yref:`wall<yade.utils.wall>` are used to create them. Using these functions also ensures better future compatibility, if internals of :yref:`Body` change in some way. These functions receive geometry of the particle and several other characteristics. See their documentation for details. If the same :yref:`Material` is used for several (or many) bodies, it can be shared by adding it in ``O.materials``, as explained below.
Defining materials
------------------
The ``O.materials`` object (instance of :yref:`Omega.materials`) holds defined shared materials for bodies. It only supports addition, and will typically hold only a few instance (though there is no limit).
-``label`` given to each material is optional, but can be passed to :yref:`yade.utils.sphere` and other functions forconstructing body. The value returned by ``O.materials.append`` is an ``id`` of the material, which can be also passed to :yref:`yade.utils.sphere` -- it is a little bit faster than using label, though not noticeable for small number of particles and perhaps less convenient.
+``label`` given to each material is optional, but can be passed to :yref:`sphere<yade.utils.sphere>` and other functions for constructing body. The value returned by ``O.materials.append`` is an ``id`` of the material, which can be also passed to :yref:`sphere<yade.utils.sphere>` -- it is a little bit faster than using label, though not noticeable for small number of particles and perhaps less convenient.
-If no :yref:`Material` is specified when calling :yref:`yade.utils.sphere`, the *last* defined material is used; that is a convenient default. If no material is defined yet (hence there is no last material), a default material will be created using :yref:`yade.utils.defaultMaterial`; this should not happen for serious simulations, but is handy in simple scripts, where exact material properties are more or less irrelevant.
+If no :yref:`Material` is specified when calling :yref:`sphere<yade.utils.sphere>`, the *last* defined material is used; that is a convenient default. If no material is defined yet (hence there is no last material), a default material will be created: FrictMat(density=2e3,young=30e9,poisson=.3,frictionAngle=.5236). This should not happen for serious simulations, but is handy in simple scripts, where exact material properties are more or less irrelevant.
.. ipython::
@@ -38,15 +49,15 @@
# uses the last defined material
- Yade [3]: O.bodies.append(utils.sphere(center=(0,0,0),radius=1))
+ Yade [3]: O.bodies.append(sphere(center=(0,0,0),radius=1))
# material given by id
- Yade [4]: O.bodies.append(utils.sphere((0,0,2),1,material=idConcrete))
+ Yade [4]: O.bodies.append(sphere((0,0,2),1,material=idConcrete))
# material given by label
- Yade [5]: O.bodies.append(utils.sphere((0,2,0),1,material="concrete"))
+ Yade [5]: O.bodies.append(sphere((0,2,0),1,material="concrete"))
Yade [3]: idSteel=O.materials.append(FrictMat(young=210e9,poisson=.25,frictionAngle=.8,label="steel"))
@@ -54,7 +65,7 @@
# implicitly uses "steel" material, as it is the last one now
- Yade [6]: O.bodies.append(utils.facet([(1,0,0),(0,1,0),(-1,-1,0)]))
+ Yade [6]: O.bodies.append(facet([(1,0,0),(0,1,0),(-1,-1,0)]))
Adding multiple particles
-------------------------
@@ -66,15 +77,15 @@
@suppress
Yade [0]: O.reset()
- Yade [1]: O.bodies.append(utils.sphere((0,10,0),1))
+ Yade [1]: O.bodies.append(sphere((0,10,0),1))
- Yade [2]: O.bodies.append(utils.sphere((0,0,2),1))
+ Yade [2]: O.bodies.append(sphere((0,0,2),1))
# this is the same, but in one function call
Yade [3]: O.bodies.append([
- ...: utils.sphere((0,0,0),1),
- ...: utils.sphere((0,0,2),1)
+ ...: sphere((0,0,0),1),
+ ...: sphere((0,0,2),1)
...: ])
Many functions introduced in next sections return list of bodies which can be readily added to the simulation, including
@@ -83,7 +94,7 @@
* surface function :yref:`yade.pack.gtsSurface2Facets`
* import functions :yref:`yade.ymport.gmsh`, :yref:`yade.ymport.stl`, …
-As those functions use :yref:`yade.utils.sphere` and :yref:`yade.utils.facet` internally, they accept additional argument passed to those function. In particular, material for each body is selected following the rules above (last one if not specified, by label, by index, etc.).
+As those functions use :yref:`sphere<yade.utils.sphere>` and :yref:`facet<yade.utils.facet>` internally, they accept additional argument passed to those function. In particular, material for each body is selected following the rules above (last one if not specified, by label, by index, etc.).
Clumping particles together
@@ -91,8 +102,8 @@
In some cases, you might want to create rigid aggregate of individual particles (i.e. particles will retain their mutual position during simulation). This we call a :yref:`clump<Clump>`.
A clump is internally represented by a special :yref:`body<Body>`, referenced by :yref:`clumpId<Body.clumpId>` of its members (see also :yref:`isClump<Body.isClump>`, :yref:`isClumpMember<Body.isClumpMember>` and :yref:`isStandalone<Body.isStandalone>`).
-Like every body a clump has a :yref:`position<State.pos>`, which is the balance point between all members.
-A clump body itself has no :yref:`interactions<Interaction>` with other bodies. Interactions between clumps is internally represented by interactions between clump members. There are also no interactions between clump members with same clumpId.
+Like every body a clump has a :yref:`position<State.pos>`, which is the (mass) balance point between all members.
+A clump body itself has no :yref:`interactions<Interaction>` with other bodies. Interactions between clumps is represented by interactions between clump members. There are no interactions between clump members of the same clump.
YADE supports different ways of creating clumps:
@@ -106,8 +117,8 @@
Yade [0]: O.reset()
Yade [1]: O.bodies.appendClumped([
- ...: utils.sphere([0,0,0],1),
- ...: utils.sphere([0,0,2],1)
+ ...: sphere([0,0,0],1),
+ ...: sphere([0,0,2],1)
...: ])
Yade [2]: len(O.bodies)
@@ -130,7 +141,7 @@
Yade [1]: bodyList = []
Yade [2]: for ii in range(0,5):
- ...: bodyList.append(O.bodies.append(utils.sphere([ii,0,1],.5)))#create a "chain" of 5 spheres
+ ...: bodyList.append(O.bodies.append(sphere([ii,0,1],.5)))#create a "chain" of 5 spheres
...:
Yade [3]: print bodyList
@@ -139,15 +150,37 @@
-> :yref:`clump()<BodyContainer.clump>` returns ``clumpId``
-* Another option is to replace :yref:`standalone<Body.isStandalone>` spheres from a given packing (see :yref:`SpherePack<yade._packSpheres.SpherePack>` and :yref:`makeCloud<yade._packSpheres.SpherePack.makeCloud>`) using clump templates.
+* Another option is to replace :yref:`standalone<Body.isStandalone>` spheres from a given packing (see :yref:`SpherePack<yade._packSpheres.SpherePack>` and :yref:`makeCloud<yade._packSpheres.SpherePack.makeCloud>`) by clumps using clump templates.
-This is done by a function called :yref:`replaceByClumps()<BodyContainer.replaceByClumps>`. This function takes a list of :yref:`clumpTemplates()<yade.utils.clumpTemplate>` and a list of amounts and replaces spheres by clumps. The volume of a new clump will be the same as the volume of the sphere, that was replaced (clump volume/mass/inertia is accounting for overlaps assuming that there are only pair overlaps).
+This is done by a function called :yref:`replaceByClumps()<BodyContainer.replaceByClumps>`. This function takes a list of :yref:`clumpTemplates()<yade.clumpTemplate>` and a list of amounts and replaces spheres by clumps. The volume of a new clump will be the same as the volume of the sphere, that was replaced (clump volume/mass/inertia is accounting for overlaps assuming that there are only pair overlaps).
-> :yref:`replaceByClumps()<yade.wrapper.BodyContainer.replaceByClumps>` returns a list of tuples: ``[(clumpId1,[memberId1,memberId2,...]),(clumpId2,[memberId1,memberId2,...]),...]``
It is also possible to :yref:`add<BodyContainer.addToClump>` bodies to a clump and :yref:`release<BodyContainer.releaseFromClump>` bodies from a clump. Also you can :yref:`erase<BodyContainer.erase>` the clump (clump members will get standalone spheres).
-.. note:: Have a look at ``examples/clumps/`` folder. There you will find some examples, that show usage of different functions for clumps.
+Additionally YADE supports to achieve the :yref:`roundness<BodyContainer.getRoundness>` of a clump or roundness coefficient of a packing. Parts of the packing can be excluded from roundness measurement via exclude list.
+
+.. ipython::
+
+ @suppress
+ Yade [0]: O.reset()
+
+ Yade [1]: bodyList = []
+
+ Yade [2]: for ii in range(1,5):
+ ...: bodyList.append(O.bodies.append(sphere([ii,ii,ii],.5)))
+ ...:
+
+ Yade [4]: O.bodies.clump(bodyList)
+
+ Yade [5]: RC=O.bodies.getRoundness()
+
+ Yade [3]: print RC
+
+-> :yref:`getRoundness()<BodyContainer.getRoundness>` returns roundness coefficient RC of a packing or a part of the packing
+
+.. note:: Have a look at :ysrc:`examples/clumps/` folder. There you will find some examples, that show usage of different functions for clumps.
+
Sphere packings
===============
@@ -164,7 +197,7 @@
Volume representation
----------------------
-There are 2 methods for representing exact volume of the solid in question in Yade: boundary representation and constructive solid geometry. Despite their fundamental differences, they are abstracted in Yade in the :yref:`Predicate<yade.utils._packPredicates.Predicate>` class. Predicate provides the following functionality:
+There are 2 methods for representing exact volume of the solid in question in Yade: boundary representation and constructive solid geometry. Despite their fundamental differences, they are abstracted in Yade in the :yref:`Predicate<yade._packPredicates.Predicate>` class. Predicate provides the following functionality:
#. defines axis-aligned bounding box for the associated solid (optionally defines oriented bounding box);
#. can decide whether given point is inside or outside the solid; most predicates can also (exactly or approximately) tell whether the point is inside *and* satisfies some given padding distance from the represented solid boundary (so that sphere of that volume doesn't stick out of the solid).
@@ -235,7 +268,7 @@
#. :yref:`yade.pack.gtsSurface2Facets` function can create the triangulated surface (from :yref:`Facet` particles) in the simulation itself, as shown in the funnel example. (Triangulated surface can also be imported directly from a STL file using :yref:`yade.ymport.stl`.)
#. :yref:`yade._packPredicates.inGtsSurface` predicate can be created, using the surface as boundary representation of the enclosed volume.
-The :ysrc:`scripts/test/gts-horse.py` (img. img-horse_) shows both possibilities; first, a GTS surface is imported::
+The :ysrc:`examples/gts-horse/gts-horse.py` (img. img-horse_) shows both possibilities; first, a GTS surface is imported::
import gts
surf=gts.read(open('horse.coarse.gts'))
@@ -284,7 +317,7 @@
Algorithms presented below operate on geometric spheres, defined by their center and radius. With a few exception documented below, the procedure is as follows:
#. Sphere positions and radii are computed (some functions use volume predicate for this, some do not)
-#. :yref:`yade.utils.sphere` is called for each position and radius computed; it receives extra `keyword arguments <http://docs.python.org/glossary.html#term-keyword-argument>`_ of the packing function (i.e. arguments that the packing function doesn't specify in its definition; they are noted ``**kw``). Each :yref:`yade.utils.sphere` call creates actual :yref:`Body` objects with :yref:`Sphere` :yref:`shape<Shape>`. List of :yref:`Body` objects is returned.
+#. :yref:`sphere<yade.utils.sphere>` is called for each position and radius computed; it receives extra `keyword arguments <http://docs.python.org/glossary.html#term-keyword-argument>`_ of the packing function (i.e. arguments that the packing function doesn't specify in its definition; they are noted ``**kw``). Each :yref:`sphere<yade.utils.sphere>` call creates actual :yref:`Body` objects with :yref:`Sphere` :yref:`shape<Shape>`. List of :yref:`Body` objects is returned.
#. List returned from the packing function can be added to simulation using ``O.bodies.append``.
Taking the example of pierced box::
@@ -292,7 +325,7 @@
pred=pack.inAlignedBox((-2,-2,-2),(2,2,2))-pack.inCylinder((0,-2,0),(0,2,0),1)
spheres=pack.randomDensePack(pred,spheresInCell=2000,radius=.1,rRelFuzz=.4,wire=True,color=(0,0,1),material=1)
-Keyword arguments ``wire``, ``color`` and ``material`` are not declared in :yref:`yade.pack.randomDensePack`, therefore will be passed to :yref:`yade.utils.sphere`, where they are also documented. ``spheres`` is now list of :yref:`Body` objects, which we add to the simulation::
+Keyword arguments ``wire``, ``color`` and ``material`` are not declared in :yref:`yade.pack.randomDensePack`, therefore will be passed to :yref:`sphere<yade.utils.sphere>`, where they are also documented. ``spheres`` is now list of :yref:`Body` objects, which we add to the simulation::
O.bodies.append(spheres)
@@ -304,11 +337,11 @@
will fill given box with spheres, until no more spheres can be placed. The object can be used to add spheres to simulation::
- for c,r in sp: O.bodies.append(utils.sphere(c,r))
+ for c,r in sp: O.bodies.append(sphere(c,r))
or, in a more pythonic way, with one single ``O.bodies.append`` call::
- O.bodies.append([utils.sphere(c,r) for c,r in sp])
+ O.bodies.append([sphere(c,r) for c,r in sp])
Geometric
@@ -331,12 +364,10 @@
""""""""""
Random geometric algorithms do not integrate at all with volume predicates described above; rather, they take their own boundary/volume definition, which is used during sphere positioning. On the other hand, this makes it possible for them to respect boundary in the sense of making spheres touch it at appropriate places, rather than leaving empty space in-between.
-:yref:`yade._packSpherePadder.SpherePadder`
- constructs dense sphere packing based on pre-computed tetrahedron mesh; it is documented in :yref:`yade._packSpherePadder.SpherePadder` documentation; sample script is in :ysrc:`scripts/test/SpherePadder.py`. :yref:`yade._packSpherePadder.SpherePadder` does not return :yref:`Body` list as other algorithms, but a :yref:`yade._packSpheres.SpherePack` object; it can be iterated over, adding spheres to the simulation, as shown in its documentation.
GenGeo
is library (python module) for packing generation developed with `ESyS-Particle <http://www.launchpad.net/esys-particle>`_. It creates packing by random insertion of spheres with given radius range. Inserted spheres touch each other exactly and, more importantly, they also touch the boundary, if in its neighbourhood. Boundary is represented as special object of the GenGeo library (Sphere, cylinder, box, convex polyhedron, …). Therefore, GenGeo cannot be used with volume represented by yade predicates as explained above.
- Packings generated by this module can be imported directly via :yref:`yade.ymport.gengeo`, or from saved file via :yref:`yade.ymport.gengeoFile`. There is an example script :ysrc:`scripts/test/genCylLSM.py`. Full documentation for GenGeo can be found at `ESyS documentation website <http://esys.esscc.uq.edu.au/docs.html>`_.
+ Packings generated by this module can be imported directly via :yref:`yade.ymport.gengeo`, or from saved file via :yref:`yade.ymport.gengeoFile`. There is an example script :ysrc:`examples/test/genCylLSM.py`. Full documentation for GenGeo can be found at `ESyS documentation website <http://esys.esscc.uq.edu.au/docs.html>`_.
To our knowledge, the GenGeo library is not currently packaged. It can be downloaded from current subversion repository ::
@@ -361,9 +392,9 @@
Triangulated surfaces
=====================
-Yade integrates with the the `GNU Triangulated Surface library <http://gts.sourceforge.net>`_, exposed in python via the 3rd party :yref:`external:gts` module. GTS provides variety of functions for surface manipulation (coarsening, tesselation, simplification, import), to be found in its documentation.
+Yade integrates with the the `GNU Triangulated Surface library <http://gts.sourceforge.net>`_, exposed in python via GTS module. GTS provides variety of functions for surface manipulation (coarsening, tesselation, simplification, import), to be found in its documentation.
-GTS surfaces are geometrical objects, which can be inserted into simulation as set of particles whose :yref:`Body.shape` is of type :yref:`Facet` -- single triangulation elements. :yref:`pack.gtsSurface2Facets` can be used to convert GTS surface triangulation into list of :yref:`bodies<Body>` ready to be inserted into simulation via ``O.bodies.append``.
+GTS surfaces are geometrical objects, which can be inserted into simulation as set of particles whose :yref:`Body.shape` is of type :yref:`Facet` -- single triangulation elements. :yref:`yade.pack.gtsSurface2Facets` can be used to convert GTS surface triangulation into list of :yref:`bodies<Body>` ready to be inserted into simulation via ``O.bodies.append``.
Facet particles are created by default as non-:yref:`Body.dynamic` (they have zero inertial mass). That means that they are fixed in space and will not move if subject to forces. You can however
@@ -391,7 +422,7 @@
Parametric construction
------------------------
-The :yref:`external:gts` module provides convenient way of creating surface by vertices, edges and triangles.
+The GTS module provides convenient way of creating surface by vertices, edges and triangles.
Frequently, though, the surface can be conveniently described as surface between polylines in space. For instance, cylinder is surface between two polygons (closed polylines). The :yref:`yade.pack.sweptPolylines2gtsSurface` offers the functionality of connecting several polylines with triangulation.
@@ -418,7 +449,7 @@
#. Approximate collision detection is adjusted so that approximate contacts are detected also between particles within the interaction radius. This consists in setting value of :yref:`Bo1_Sphere_Aabb.aabbEnlargeFactor` to the interaction radius value.
#. The geometry functor (``Ig2``)
- would normally say that "there is no contact" if given 2 spheres that are not in contact. Therefore, the same value as for :yref:`Bo1_Sphere_Aabb.aabbEnlargeFactor` must be given to it. (Either :yref:`Ig2_Sphere_Sphere_Dem3DofGeom.distFactor` or :yref:`Ig2_Sphere_Sphere_ScGeom.interactionDetectionFactor`, depending on the functor that is in use.
+ would normally say that "there is no contact" if given 2 spheres that are not in contact. Therefore, the same value as for :yref:`Bo1_Sphere_Aabb.aabbEnlargeFactor` must be given to it (:yref:`Ig2_Sphere_Sphere_ScGeom.interactionDetectionFactor` ).
Note that only :yref:`Sphere` + :yref:`Sphere` interactions are supported; there is no parameter analogous to :yref:`distFactor<Ig2_Sphere_Sphere_ScGeom.interactionDetectionFactor>` in :yref:`Ig2_Facet_Sphere_ScGeom`. This is on purpose, since the interaction radius is meaningful in bulk material represented by sphere packing, whereas facets usually represent boundary conditions which should be exempt from this dense interaction network.
@@ -464,7 +495,7 @@
Individual interactions on demand
----------------------------------
-It is possible to create an interaction between a pair of particles independently of collision detection using :yref:`yade.utils.createInteraction`. This function looks for and uses matching ``Ig2`` and ``Ip2`` functors. Interaction will be created regardless of distance between given particles (by passing a special parameter to the ``Ig2`` functor to force creation of the interaction even without any geometrical contact). Appropriate constitutive law should be used to avoid deletion of the interaction at the next simulation step.
+It is possible to create an interaction between a pair of particles independently of collision detection using :yref:`createInteraction<yade._utils.createInteraction>`. This function looks for and uses matching ``Ig2`` and ``Ip2`` functors. Interaction will be created regardless of distance between given particles (by passing a special parameter to the ``Ig2`` functor to force creation of the interaction even without any geometrical contact). Appropriate constitutive law should be used to avoid deletion of the interaction at the next simulation step.
.. ipython::
@@ -474,8 +505,8 @@
Yade [1]: O.materials.append(FrictMat(young=3e10,poisson=.2,density=1000))
Yade [1]: O.bodies.append([
- ...: utils.sphere([0,0,0],1),
- ...: utils.sphere([0,0,1000],1)
+ ...: sphere([0,0,0],1),
+ ...: sphere([0,0,1000],1)
...: ])
# only add InteractionLoop, no other engines are needed now
@@ -487,12 +518,12 @@
...: )
...: ]
- Yade [1]: i=utils.createInteraction(0,1)
+ Yade [1]: i=createInteraction(0,1)
# created by functors in InteractionLoop
Yade [2]: i.geom, i.phys
-This method will be rather slow if many interaction are to be created (the functor lookup will be repeated for each of them). In such case, ask on yade-dev@xxxxxxxxxxxxxxxxxxx to have the :yref:`yade.utils.createInteraction` function accept list of pairs id's as well.
+This method will be rather slow if many interaction are to be created (the functor lookup will be repeated for each of them). In such case, ask on yade-dev@xxxxxxxxxxxxxxxxxxx to have the :yref:`createInteraction<yade._utils.createInteraction>` function accept list of pairs id's as well.
Base engines
=============
@@ -576,12 +607,7 @@
Again, missing combination will cause given shape combinations to freely interpenetrate one another.
-#. :yref:`IGeom` type accepted by the ``Law2`` functor (below); it is the first part of functor's name after ``Law2`` (for instance, :yref:`Law2_ScGeom_CpmPhys_Cpm` accepts :yref:`ScGeom`). This is (for most cases) either :yref:`Dem3DofGeom` (total shear formulation) or :yref:`ScGeom` (incremental shear formulation). For :yref:`Dem3DofGeom`, the above example would simply change to::
-
- InteractionLoop([
- Ig2_Sphere_Sphere_Dem3DofGeom(),
- Ig2_Facet_Sphere_Dem3DofGeom()
- ],[],[])
+#. :yref:`IGeom` type accepted by the ``Law2`` functor (below); it is the first part of functor's name after ``Law2`` (for instance, :yref:`Law2_ScGeom_CpmPhys_Cpm` accepts :yref:`ScGeom`).
Ip2 functors
^^^^^^^^^^^^
@@ -603,7 +629,7 @@
.. note:: As in the case of ``Ip2`` functors, receiving a combination of :yref:`IGeom` and :yref:`IPhys` which is not handled by any ``Law2`` functor is an error.
-.. warning:: Many ``Law2`` exist in Yade, and new ones can appear at any time. In some cases different functors are only different implementations of the same contact law (e.g. :yref:`Law2_ScGeom_FrictPhys_CundallStrack` and :yref:`Law2_L3Geom_FrictPhys_ElPerfPl`). Also, sometimes, the peculiarity of one functor may be reproduced as a special case of a more general one. Therefore, for a given constitutive behavior, the user may have the choice between different functors. It is strongly recommended to favor the most used and most validated implementation when facing such choice. A list of available functors classified from mature to unmaintained is updated `here <https://yade-dem.org/wiki/ConstitutiveLaws`_ to guide this choice.
+.. warning:: Many ``Law2`` exist in Yade, and new ones can appear at any time. In some cases different functors are only different implementations of the same contact law (e.g. :yref:`Law2_ScGeom_FrictPhys_CundallStrack` and :yref:`Law2_L3Geom_FrictPhys_ElPerfPl`). Also, sometimes, the peculiarity of one functor may be reproduced as a special case of a more general one. Therefore, for a given constitutive behavior, the user may have the choice between different functors. It is strongly recommended to favor the most used and most validated implementation when facing such choice. A list of available functors classified from mature to unmaintained is updated `here <https://yade-dem.org/wiki/ConstitutiveLaws>`_ to guide this choice.
Examples
^^^^^^^^
@@ -651,15 +677,14 @@
Motion constraints
------------------
-* :yref:`Body.dynamic` determines whether a body will be moved by :yref:`NewtonIntegrator`; it is mandatory for bodies with zero mass, where applying non-zero force would result in infinite displacement.
+* :yref:`Body.dynamic` determines whether a body will be accelerated by :yref:`NewtonIntegrator`; it is mandatory to make it false for bodies with zero mass, where applying non-zero force would result in infinite displacement.
- :yref:`Facets<Facet>` are case in the point: :yref:`yade.utils.facet` makes them non-dynamic by default, as they have zero volume and zero mass (this can be changed, by passing ``dynamic=True`` to :yref:`yade.utils.facet` or setting :yref:`Body.dynamic`; setting :yref:`State.mass` to a non-zero value must be done as well). The same is true for :yref:`yade.utils.wall`.
+ :yref:`Facets<Facet>` are case in the point: :yref:`facet<yade.utils.facet>` makes them non-dynamic by default, as they have zero volume and zero mass (this can be changed, by passing ``dynamic=True`` to :yref:`facet<yade.utils.facet>` or setting :yref:`Body.dynamic`; setting :yref:`State.mass` to a non-zero value must be done as well). The same is true for :yref:`wall<yade.utils.wall>`.
Making sphere non-dynamic is achieved simply by::
- utils.sphere([x,y,z],radius,dynamic=False)
-
- .. note:: There is an open `bug #398089 <https://bugs.launchpad.net/yade/+bug/398089>`_ to define exactly what the ``dynamic`` flag does. Please read it before writing a new engine relying on this flag.
+ b = sphere([x,y,z],radius,dynamic=False)
+ b.dynamic=True #revert the previous
* :yref:`State.blockedDOFs` permits selective blocking
of any of 6 degrees of freedom in global space. For instance, a sphere can be made to move only in the xy plane by saying:
@@ -669,12 +694,43 @@
@suppress
Yade [1]: O.reset()
- Yade [1]: O.bodies.append(utils.sphere((0,0,0),1))
-
- Yade [1]: O.bodies[0].state.blockedDOFs=['z','rx','ry']
-
- In contrast to :yref:`Body.dynamic`, :yref:`blockedDOFs<State.blockedDOFs>` will only block forces (and acceleration) in that direction being effective; if you prescribed linear or angular velocity, they will be applied regardless of :yref:`blockedDOFs<State.blockedDOFs>`. (This is also related to `bug #398089 <https://bugs.launchpad.net/yade/+bug/398089>`_ mentioned above)
-
+ Yade [1]: O.bodies.append(sphere((0,0,0),1))
+
+ Yade [1]: O.bodies[0].state.blockedDOFs='zXY'
+
+ In contrast to :yref:`Body.dynamic`, :yref:`blockedDOFs<State.blockedDOFs>` will only block forces (and acceleration) in selected directions. Actually, ``b.dynamic=False`` is nearly only a shorthand for ``b.state.blockedDOFs=='xyzXYZ'`` . A subtle difference is that the former does reset the velocity components automaticaly, while the latest does not. If you prescribed linear or angular velocity, they will be applied regardless of :yref:`blockedDOFs<State.blockedDOFs>`. It also implies that if the velocity is not zero when degrees of freedom are blocked via blockedDOFs assignements, the body will keep moving at the velocity it has at the time of blocking. The differences are shown below:
+
+ .. ipython::
+
+ @suppress
+ Yade [1]: O.reset()
+
+ Yade [1]: b1 = sphere([0,0,0],1,dynamic=True)
+
+ Yade [1]: b1.state.blockedDOFs
+
+ Yade [1]: b1.state.vel = Vector3(1,0,0) #we want it to move...
+
+ Yade [1]: b1.dynamic = False #... at a constant velocity
+
+ Yade [1]: print b1.state.blockedDOFs, b1.state.vel
+
+ Yade [1]: # oops, velocity has been reset when setting dynamic=False
+
+ Yade [1]: b1.state.vel = (1,0,0) # we can still assign it now
+
+ Yade [1]: print b1.state.blockedDOFs, b1.state.vel
+
+ Yade [1]: b2 = sphere([0,0,0],1,dynamic=True) #another try
+
+ Yade [1]: b2.state.vel = (1,0,0)
+
+ Yade [1]: b2.state.blockedDOFs = "xyzXYZ" #this time we assign blockedDOFs directly, velocity is unchanged
+
+ Yade [1]: print b2.state.blockedDOFs, b2.state.vel
+
+
+
It might be desirable to constrain motion of some particles constructed from a generated sphere packing, following some condition, such as being at the bottom of a specimen; this can be done by looping over all bodies with a conditional::
for b in O.bodies:
@@ -690,6 +746,40 @@
# ask the predicate if we are inside
if pred(b.state.pos,b.shape.radius): b.dynamic=False
+.. _imposing_motion_force:
+
+Imposing motion and forces
+--------------------------
+
+Imposed velocity
+^^^^^^^^^^^^^^^^
+
+If a degree of freedom is blocked and a velocity is assigned along that direction (translational or rotational velocity), then the body will move at constant velocity. This is the simpler and recommended method to impose the motion of a body. This, for instance, will result in a constant velocity along $x$ (it can still be freely accelerated along $y$ and $z$)::
+
+ O.bodies.append(sphere((0,0,0),1))
+ O.bodies[0].state.blockedDOFs='x'
+ O.bodies[0].state.vel=(10,0,0)
+
+Conversely, modifying the position directly is likely to break Yade's algorithms, especially those related to collision detection and contact laws, as they are based on bodies velocities. Therefore, unless you really know what you are doing, don't do that for imposing a motion::
+
+ O.bodies.append(sphere((0,0,0),1))
+ O.bodies[0].state.blockedDOFs='x'
+ O.bodies[0].state.pos=10*O.dt #REALLY BAD! Don't assign position
+
+Imposed force
+^^^^^^^^^^^^^
+
+Applying a force or a torque on a body is done via functions of the :yref:`ForceContainer`. It is as simple as this::
+
+ O.forces.addF(0,(1,0,0)) #applies for one step
+
+By default, the force applies for one time step only, and is resetted at the beginning of each step. For this reason, imposing a force at the begining of one step will have no effect at all, since it will be immediatly resetted. The only way is to place a :yref:`PyRunner` inside the simulation loop.
+
+Applying the force permanently is possible with an optional argument (in this case it does not matter if the command comes at the begining of the time step)::
+
+ O.forces.addF(0,(1,0,0),permanent=True) #applies permanently
+
+The force will persist across iterations, until it is overwritten by another call to ``O.forces.addF(id,f,True)`` or erased by ``O.forces.reset(resetAll=True)``. The permanent force on a body can be checked with ``O.forces.permF(id)``.
Boundary controllers
--------------------
@@ -703,18 +793,18 @@
Field appliers
---------------
-Engines deriving from :yref:`FieldApplier` acting on all particles. The one most used is :yref:`GravityEngine` applying uniform acceleration field.
+Engines deriving from :yref:`FieldApplier` acting on all particles. The one most used is :yref:`GravityEngine` applying uniform acceleration field (:yref:`GravityEngine` is deprecated, use :yref:`NewtonIntegrator.gravity` instead!).
Partial engines
---------------
-Engines deriving from :yref:`PartialEngine` define the :yref:`ids<PartialEngine.subscribedBodies>` attribute determining bodies which will be affected. Several of them warrant explicit mention here:
+Engines deriving from :yref:`PartialEngine` define the :yref:`ids<PartialEngine.ids>` attribute determining bodies which will be affected. Several of them warrant explicit mention here:
* :yref:`TranslationEngine` and :yref:`RotationEngine` for applying constant speed linear and rotational motion on subscribers.
* :yref:`ForceEngine` and :yref:`TorqueEngine` applying given values of force/torque on subscribed bodies at every step.
* :yref:`StepDisplacer` for applying generalized displacement delta at every timestep; designed for precise control of motion when testing constitutive laws on 2 particles.
-If you need an engine applying non-constant value instead, there are several interpolating engines (:yref:`InterpolatingDirectedForceEngine` for applying force with varying magnitude, :yref:`InterpolatingSpiralEngine` for applying spiral displacement with varying angular velocity and possibly others); writing a new interpolating engine is rather simple using examples of those that already exist.
+The real value of partial engines is if you need to prescribe complex types of force or displacement fields. For moving a body at constant velocity or for imposing a single force, the methods explained in `Imposing motion and forces`_ are much simpler. There are several interpolating engines (:yref:`InterpolatingDirectedForceEngine` for applying force with varying magnitude, :yref:`InterpolatingHelixEngine` for applying spiral displacement with varying angular velocity and possibly others); writing a new interpolating engine is rather simple using examples of those that already exist.
Convenience features
@@ -767,21 +857,21 @@
Saving python variables
------------------------
-Python variable lifetime is limited; in particular, if you save simulation, variables will be lost after reloading. Yade provides limited support for data persistence for this reason (internally, it uses special values of ``O.tags``). The functions in question are :yref:`yade.utils.saveVars` and :yref:`yade.utils.loadVars`.
+Python variable lifetime is limited; in particular, if you save simulation, variables will be lost after reloading. Yade provides limited support for data persistence for this reason (internally, it uses special values of ``O.tags``). The functions in question are :yref:`saveVars<yade.utils.saveVars>` and :yref:`loadVars<yade.utils.loadVars>`.
-:yref:`yade.utils.saveVars` takes dictionary (variable names and their values) and a *mark* (identification string for the variable set); it saves the dictionary inside the simulation. These variables can be re-created (after the simulation was loaded from a XML file, for instance) in the ``yade.params.``\ *mark* namespace by calling :yref:`yade.utils.loadVars` with the same identification *mark*:
+:yref:`saveVars<yade.utils.saveVars>` takes dictionary (variable names and their values) and a *mark* (identification string for the variable set); it saves the dictionary inside the simulation. These variables can be re-created (after the simulation was loaded from a XML file, for instance) in the ``yade.params.``\ *mark* namespace by calling :yref:`loadVars<yade.utils.loadVars>` with the same identification *mark*:
.. ipython::
Yade [3]: a=45; b=pi/3
- Yade [3]: utils.saveVars('ab',a=a,b=b)
+ Yade [3]: saveVars('ab',a=a,b=b)
# save simulation (we could save to disk just as well)
Yade [3]: O.saveTmp()
Yade [4]: O.loadTmp()
- Yade [4]: utils.loadVars('ab')
+ Yade [4]: loadVars('ab')
Yade [5]: yade.params.ab.a
@@ -806,13 +896,13 @@
# define as much as you want here
# it all appears in locals() (and nothing else does)
#
- utils.saveVars('geom',loadNow=True,**locals())
+ saveVars('geom',loadNow=True,**locals())
setGeomVars()
from yade.params.geom import *
# use the variables now
-.. note:: Only types that can be `pickled <http://docs.python.org/library/pickle.html>`_ can be passed to :yref:`yade.utils.saveVars`.
+.. note:: Only types that can be `pickled <http://docs.python.org/library/pickle.html>`_ can be passed to :yref:`saveVars<yade.utils.saveVars>`.
@@ -829,8 +919,8 @@
A special engine :yref:`PyRunner` can be used to periodically call python code, specified via the ``command`` parameter. Periodicity can be controlled by specifying computation time (``realPeriod``), virutal time (``virtPeriod``) or iteration number (``iterPeriod``).
-For instance, to print kinetic energy (using :yref:`yade.utils.kineticEnergy`) every 5 seconds, this engine will be put to ``O.engines``::
- PyRunner(command="print 'kinetic energy',utils.kineticEnergy()",realPeriod=5)
+For instance, to print kinetic energy (using :yref:`kineticEnergy<yade._utils.kineticEnergy>`) every 5 seconds, this engine will be put to ``O.engines``::
+ PyRunner(command="print 'kinetic energy',kineticEnergy()",realPeriod=5)
For running more complex commands, it is convenient to define an external function and only call it from within the engine. Since the ``command`` is run in the script's namespace, functions defined within scripts can be called. Let us print information on interaction between bodies 0 and 1 periodically::
@@ -905,9 +995,9 @@
UniaxialStrainer(...,label='strainer')
]
def myAddData():
- plot.addData(sigma=strainer.stress,eps=strainer.strain)
+ plot.addData(sigma=strainer.avgStress,eps=strainer.strain)
-In that case, naturally, the labeled object must define attributes which are used (:yref:`UniaxialStrainer.strain` and :yref:`UniaxialStrainer.stress` in this case).
+In that case, naturally, the labeled object must define attributes which are used (:yref:`UniaxialStrainer.strain` and :yref:`UniaxialStrainer.avgStress` in this case).
Plotting variables
-------------------
@@ -1023,7 +1113,7 @@
By editing the generated .gnuplot file you can plot any of the added Data afterwards.
-* Data file is saved (compressed using bzip2) separately from the gnuplot file, so any other programs can be used to process them. In particular, the ``numpy.genfromtxt`` (documented `here <http://docs.scipy.org/doc/numpy/reference/generated/numpy.genfromtxt.html>`_) can be useful to import those data back to python; the decompression happens automatically.
+* Data file is saved (compressed using bzip2) separately from the gnuplot file, so any other programs can be used to process them. In particular, the ``numpy.genfromtxt`` (`documented here <http://docs.scipy.org/doc/numpy/reference/generated/numpy.genfromtxt.html>`_) can be useful to import those data back to python; the decompression happens automatically.
* The gnuplot file can be run through gnuplot to produce the figure; see :yref:`yade.plot.saveGnuplot` documentation for details.
@@ -1048,16 +1138,16 @@
Frequently, decisions have to be made based on evolution of the simulation itself, which is not yet known. In such case, a function checking some specific condition is called periodically; if the condition is satisfied, ``O.pause`` or other functions can be called to stop the stimulation. See documentation for :yref:`Omega.run`, :yref:`Omega.pause`, :yref:`Omega.step`, :yref:`Omega.stopAtIter` for details.
-For simulations that seek static equilibrium, the :yref:`yade._utils.unbalancedForce` can provide a useful metrics (see its documentation for details); for a desired value of ``1e-2`` or less, for instance, we can use::
+For simulations that seek static equilibrium, the :yref:`unbalancedForce<yade._utils.unbalancedForce>` can provide a useful metrics (see its documentation for details); for a desired value of ``1e-2`` or less, for instance, we can use::
def checkUnbalanced():
- if utils.unbalancedForce<1e-2: O.pause()
+ if unbalancedForce<1e-2: O.pause()
O.engines=O.engines+[PyRunner(command="checkUnbalanced()",iterPeriod=100)]
# this would work as well, without the function defined apart:
- # PyRunner(command="if utils.unablancedForce<1e-2: O.pause()",iterPeriod=100)
+ # PyRunner(command="if unablancedForce<1e-2: O.pause()",iterPeriod=100)
O.run(); O.wait()
# will continue after O.pause() will have been called
@@ -1165,7 +1255,7 @@
-------------
``TCP Info provider`` listens at port 21000 (or higher) and returns some basic information about current simulation upon connection; the connection terminates immediately afterwards. The information is python dictionary represented as string (serialized) using standard `pickle <http://docs.python.org/library/pickle.html>`_ module.
-This functionality is used by the batch system (described below) to be informed about individual simulation progress and estimated times. If you want to access this information yourself, you can study :ysrc:`core/main/yade-multi.in` for details.
+This functionality is used by the batch system (described below) to be informed about individual simulation progress and estimated times. If you want to access this information yourself, you can study :ysrc:`core/main/yade-batch.in` for details.
Batch queuing and execution (yade-batch)
========================================
@@ -1173,12 +1263,12 @@
Yade features light-weight system for running one simulation with different parameters; it handles assignment of parameter values to python variables in simulation script, scheduling jobs based on number of available and required cores and more. The whole batch consists of 2 files:
simulation script
- regular Yade script, which calls :yref:`yade.utils.readParamsFromTable` to obtain parameters from parameter table. In order to make the script runnable outside the batch, :yref:`yade.utils.readParamsFromTable` takes default values of parameters, which might be overridden from the parameter table.
+ regular Yade script, which calls :yref:`readParamsFromTable<yade.utils.readParamsFromTable>` to obtain parameters from parameter table. In order to make the script runnable outside the batch, :yref:`readParamsFromTable<yade.utils.readParamsFromTable>` takes default values of parameters, which might be overridden from the parameter table.
- :yref:`yade.utils.readParamsFromTable` knows which parameter file and which line to read by inspecting the ``PARAM_TABLE`` environment variable, set by the batch system.
+ :yref:`readParamsFromTable<yade.utils.readParamsFromTable>` knows which parameter file and which line to read by inspecting the ``PARAM_TABLE`` environment variable, set by the batch system.
parameter table
- simple text file, each line representing one parameter set. This file is read by :yref:`yade.utils.readParamsFromTable` (using :yref:`yade.utils.TableParamReader` class), called from simulation script, as explained above. For better reading of the text file you can make use of tabulators, these will be ignored by :yref:`yade.utils.readParamsFromTable`. Parameters are not restricted to numerical values. You can also make use of strings by "quoting" them (' ' may also be used instead of " "). This can be useful for nominal parameters.
+ simple text file, each line representing one parameter set. This file is read by :yref:`readParamsFromTable<yade.utils.readParamsFromTable>` (using :yref:`TableParamReader<yade.utils.TableParamReader>` class), called from simulation script, as explained above. For better reading of the text file you can make use of tabulators, these will be ignored by :yref:`readParamsFromTable<yade.utils.readParamsFromTable>`. Parameters are not restricted to numerical values. You can also make use of strings by "quoting" them (' ' may also be used instead of " "). This can be useful for nominal parameters.
The batch can be run as ::
@@ -1208,8 +1298,8 @@
.. code-block:: python
- from yade import utils
- utils.readParamsFromTable(
+
+ readParamsFromTable(
gravity=-9.81,
density=2400,
initialVelocity=20,
@@ -1218,7 +1308,7 @@
from yade.params.table import *
print gravity, density, initialVelocity
-after the call to :yref:`yade.utils.readParamsFromTable`, corresponding python variables are created in the ``yade.params.table`` module and can be readily used in the script, e.g.
+after the call to :yref:`readParamsFromTable<yade.utils.readParamsFromTable>`, corresponding python variables are created in the ``yade.params.table`` module and can be readily used in the script, e.g.
.. code-block:: python
@@ -1274,7 +1364,7 @@
.. code-block:: python
- utils.saveGnuplot(O.tags['id'])
+ saveGnuplot(O.tags['id'])
For larger simulations, it is advisable to create separate directory of that name first, putting all files inside afterwards:
@@ -1311,7 +1401,7 @@
print 'gnuplot',plot.saveGnuplot(O.tags['id'])
-and the end of the script (even after utils.waitIfBatch()) , which prints::
+and the end of the script (even after waitIfBatch()) , which prints::
gnuplot 20100413T144723p7625.gnuplot
@@ -1353,16 +1443,16 @@
#. Capture screen output (the 3d rendering window) during the simulation − there are tools available for that (such as `Istanbul <http://live.gnome.org/Istanbul>`_ or `RecordMyDesktop <http://recordmydesktop.sourceforge.net/about.php>`_, which are also packaged for most Linux distributions). The output is "what you see is what you get", with all the advantages and disadvantages.
-#. Periodic frame snapshot using :yref:`SnapshotEngine` (see :ysrc:`examples/bulldozer.py` for a full example)::
+#. Periodic frame snapshot using :yref:`SnapshotEngine` (see :ysrc:`examples/bulldozer/bulldozer.py` for a full example)::
O.engines=[
#...
SnapshotEngine(iterPeriod=100,fileBase='/tmp/bulldozer-',viewNo=0,label='snapshooter')
]
- which will save numbered files like ``/tmp/bulldozer-0000.png``. These files can be processed externally (with `mencoder <http://www.mplayerhq.hu>`_ and similar tools) or directly with the :yref:`yade.utils.makeVideo`::
+ which will save numbered files like ``/tmp/bulldozer-0000.png``. These files can be processed externally (with `mencoder <http://www.mplayerhq.hu>`_ and similar tools) or directly with the :yref:`makeVideo<yade.utils.makeVideo>`::
- utils.makeVideo(frameSpec,out,renameNotOverwrite=True,fps=24,kbps=6000,bps=None)
+ makeVideo(frameSpec,out,renameNotOverwrite=True,fps=24,kbps=6000,bps=None)
The video is encoded using the default mencoder codec (mpeg4).
@@ -1383,7 +1473,9 @@
* :yref:`iterPeriod<PeriodicEngine.iterPeriod>` determines how often to save simulation data (besides :yref:`iterPeriod<PeriodicEngine.iterPeriod>`, you can also use :yref:`virtPeriod<PeriodicEngine.virtPeriod>` or :yref:`realPeriod<PeriodicEngine.realPeriod>`). If the period is too high (and data are saved only few times), the video will have few frames.
* :yref:`fileName<VTKRecorder.fileName>` is the prefix for files being saved. In this case, output files will be named ``/tmp/p1-spheres.0.vtu`` and ``/tmp/p1-facets.0.vtu``, where the number is the number of iteration; many files are created, putting them in a separate directory is advisable.
-* :yref:`recorders<VTKRecorder.recorders>` determines what data to save (see the :yref:`documentation<VTKRecorder.recorders>`)
+* :yref:`recorders<VTKRecorder.recorders>` determines what data to save
+
+:yref:`yade.exporter.VTKExporter` plays a similar role, with the difference that it is more flexible. It will save any user defined variable associated to the bodies.
Loading data into Paraview
^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -1434,6 +1526,48 @@
""""""""""
You can move between frames (snapshots that were saved) via the "Animation" menu. After setting the view angle, zoom etc to your satisfaction, the animation can be saved with *File/Save animation*.
+Micro-stress and micro-strain
+=============================
+It is sometimes useful to visualize a DEM simulation through equivalent strain fields or stress fields. This is possible with :yref:`TesselationWrapper`. This class handles the triangulation of spheres in a scene, build tesselation on request, and give access to computed quantities: volume, porosity and local deformation for each sphere. The definition of microstrain and microstress is at the scale of particle-centered subdomains shown below, as explained in [Catalano2013a]_ .
+
+.. figure:: fig/micro-domains.*
+
+Micro-strain
+------------
+Below is an output of the :yref:`defToVtk<TesselationWrapper::defToVtk>` function visualized with paraview (in this case Yade's TesselationWrapper was used to process experimental data obtained on sand by Edward Ando at Grenoble University, 3SR lab.). The output is visualized with paraview, as explained in the previous section. Similar results can be generated from simulations:
+
+.. code-block:: python
+
+ tt=TriaxialTest()
+ tt.generate("test.yade")
+ O.load("test.yade")
+ O.run(100,True)
+ TW=TesselationWrapper()
+ TW.triangulate() #compute regular Delaunay triangulation, don’t construct tesselation
+ TW.computeVolumes() #will silently tesselate the packing, then compute volume of each Voronoi cell
+ TW.volume(10) #get volume associated to sphere of id 10
+ TW.setState(0) #store current positions internaly for later use as the "0" state
+ O.run(100,True) #make particles move a little (let's hope they will!)
+ TW.setState(1) #store current positions internaly in the "1" (deformed) state
+ #Now we can define strain by comparing states 0 and 1, and average them at the particles scale
+ TW.defToVtk("strain.vtk")
+
+
+.. figure:: fig/localstrain.*
+
+Micro-stress
+------------
+Stress fields can be generated by combining the volume returned by TesselationWrapper to per-particle stress given by :yref:`bodyStressTensors<yade._utils.bodyStressTensors>`. Since the stress $\sigma$ from bodyStressTensor implies a division by the volume $V_b$ of the solid particle, one has to re-normalize it in order to obtain the micro-stress as defined in [Catalano2013a]_ (equation 39 therein), i.e. $\overline{\sigma}^k = \sigma^k \times V_b^k / V_{\sigma}^k$ where $V_{\sigma}^k$ is the volume assigned to particle $k$ in the tesselation. For instance:
+
+.. code-block:: python
+
+ #"b" being a body
+ TW=TesselationWrapper()
+ TW.computeVolumes()
+ s=bodyStressTensors()
+ stress = s[b.id]**4.*pi/3.*b.shape.radius**3/TW.volume(b.id)
+
+As any other value, the stress can be exported to a vtk file for display in Paraview using :yref:`yade.exporter.VTKExporter`.
******************************
Python specialties and tricks
=== modified file 'doc/sphinx/yadeSphinx.py'
--- doc/sphinx/yadeSphinx.py 2013-03-21 19:28:57 +0000
+++ doc/sphinx/yadeSphinx.py 2013-10-15 16:14:46 +0000
@@ -4,6 +4,7 @@
# module documentation
#
import sys,os,os.path
+
outDir=sys.argv[2] if len(sys.argv)>2 else '_build'
for d in (outDir,outDir+'/latex',outDir+'/html'):
if not os.path.exists(d):
@@ -34,7 +35,7 @@
#
# don't forget to put the module in index.rst as well!
#
-mods={'export':[],'eudoxos':['_eudoxos'],'post2d':[],'pack':['_packSpheres','_packPredicates','_packObb'],'plot':[],'timing':[],'utils':['_utils'],'ymport':[],'geom':[],'bodiesHandling':[],'qt':['qt._GLViewer'],'linterpolation':[]}
+mods={'export':[],'post2d':[],'pack':['_packSpheres','_packPredicates','_packObb'],'plot':[],'timing':[],'utils':['_utils'],'polyhedra_utils':['_polyhedra_utils'],'ymport':[],'geom':[],'bodiesHandling':[],'qt':['qt._GLViewer'],'linterpolation':[]}
#
# generate documentation, in alphabetical order
mm=mods.keys(); mm.sort()
@@ -110,7 +111,8 @@
global docClasses
docClasses=set() # reset globals
wrapper=file('yade.wrapper.rst','w')
- wrapper.write(""".. _yade.wrapper
+ wrapper.write(""".. _yade.wrapper::
+
Class reference (yade.wrapper module)
=======================================
=== modified file 'doc/yade-articles.bib'
--- doc/yade-articles.bib 2013-07-29 17:37:33 +0000
+++ doc/yade-articles.bib 2013-08-26 21:57:21 +0000
@@ -30,7 +30,7 @@
url = {http://arxiv.org/pdf/1304.4895.pdf}
}
-@article {Chareyre2012a,
+@article{Chareyre2012a,
author = {Chareyre, B. and Cortis, A. and Catalano, E. and Barthélemy, E.},
title = {Pore-Scale Modeling of Viscous Flow and Induced Forces in Dense Sphere Packings},
journal = {Transport in Porous Media},
@@ -110,36 +110,17 @@
year={2008}
}
-@article{Duriez2011a,
+@article{Duriez2011,
author = {Duriez,J. and Darve, F. and Donzé, F.V.},
title = {A discrete modeling-based constitutive relation for infilled rock joints},
year = {2011},
- journal = {International Journal of Rock Mechanics & Mining Sciences},
+ journal = {International Journal of Rock Mechanics \& Mining Sciences},
volume = {48},
number = {3},
pages = {458--468},
doi = {10.1016/j.ijrmms.2010.09.008}
}
-@article{Duriez2011b,
- title={Incrementally non-linear plasticity applied to rock joint modelling},
- author={Duriez, J. and Darve, F. and Donzé, F.V.},
- journal={International Journal for Numerical and Analytical Methods in Geomechanics},
- year={2011},
- doi = {10.1002/nag.1105}
-}
-
-@Article{Duriez2011c,
- author = {Duriez,J. and Darve, F. and Donzé, F.V.},
- title = {A discrete modeling-based constitutive relation for infilled rock joints},
- year = {2011},
- journal = {International Journal of Rock Mechanics \& Mining Sciences},
- volume = {48},
- number = {3},
- pages = {458--468},
- doi = {10.1016/j.ijrmms.2010.09.008}
-}
-
@article{Duriez2013,
author = {Duriez, J. and Darve, F. and Donzé, F.V.},
title = {Incrementally non-linear plasticity applied to rock joint modelling},
@@ -194,7 +175,7 @@
@article{Gusenbauer2012,
title={Self-organizing magnetic beads for biomedical applications},
- author={Gusenbauer, M. and Kovacs, A. and Reichel, F. and Exl, L. and Bance, S. and {\"O}zelt, H. and Schrefl, T.},
+ author={Gusenbauer, M. and Kovacs, A. and Reichel, F. and Exl, L. and Bance, S. and Özelt, H. and Schrefl, T.},
journal={Journal of Magnetism and Magnetic Materials},
volume={324},
number={6},
@@ -378,15 +359,15 @@
}
@article{Puckett2011,
- title = {Local origins of volume fraction fluctuations in dense granular materials},
- author = {Puckett, J.G. and Lechenault, F. and Daniels, K.E.},
- journal = {Physical Review E},
- volume = {83},
- issue = {4},
- pages = {041301},
- year = {2011},
- doi = {10.1103/PhysRevE.83.041301},
- url = {http://link.aps.org/doi/10.1103/PhysRevE.83.041301}
+ title = {Local origins of volume fraction fluctuations in dense granular materials},
+ author = {Puckett, J.G. and Lechenault, F. and Daniels, K.E.},
+ journal = {Physical Review E},
+ volume = {83},
+ issue = {4},
+ pages = {041301},
+ year = {2011},
+ doi = {10.1103/PhysRevE.83.041301},
+ url = {http://link.aps.org/doi/10.1103/PhysRevE.83.041301}
}
@article{Sayeed2011,
=== modified file 'doc/yade-conferences.bib'
--- doc/yade-conferences.bib 2013-07-29 17:38:12 +0000
+++ doc/yade-conferences.bib 2013-08-26 21:53:51 +0000
@@ -29,7 +29,7 @@
@inproceedings{Chen2008a,
title={Discrete Element Simulation of 1D Upward Seepage Flow with Particle-Fluid Interaction Using Coupled Open Source Software},
- author={Chen, F. and Drumm, E.C. and Guiochon, G. and Suzuki, K},
+ author={Chen, F. and Drumm, E.C. and Guiochon, G. and Suzuki, K.},
booktitle={Proceedings of The 12th International Conference of the International Association for Computer Methods and Advances in Geomechanics (IACMAG) Goa, India},
year={2008},
month={October}
@@ -563,7 +563,7 @@
doi = {10.1063/1.4812158}
}
-@InProceedings{ Hadda2013,
+@InProceedings{ Hadda2013b,
author = {Nejib Hadda and François Nicot and Luc Sibille and Farhang Radjai and Antoinette Tordesillas and Félix Darve},
title = {A multiscale description of failure in granular materials},
publisher = {AIP},
@@ -590,3 +590,14 @@
url = {https://www.yade-dem.org/publi/APC000495.pdf},
doi = {10.1063/1.4811976}
}
+
+@InProceedings{Maurin2013,
+ title={Discrete element modelling of bedload transport},
+ author={Maurin, Raphael and Chareyre, Bruno and Chauchat, Julien and Frey, Philippe},
+ booktitle={Proceedings of THESIS 2013, Two-pHase modElling for Sediment dynamIcS in Geophysical Flows},
+ adress = {Chatou, France},
+ pages={6p},
+ url = {https://www.yade-dem.org/publi/Maurin_et_al_THESIS_meta.pdf},
+ year={2013}
+}
+
=== modified file 'doc/yade-theses.bib'
--- doc/yade-theses.bib 2012-12-11 21:53:45 +0000
+++ doc/yade-theses.bib 2013-08-28 10:38:02 +0000
@@ -86,15 +86,29 @@
title={Discrete Element Method (DEM) Analyses for Hot-Mix Asphalt (HMA) Mixture Compaction},
author={Chen, J.},
school = {University of Tennessee, Knoxville},
- journal={Doctoral Dissertations},
year={2011},
url = {http://trace.tennessee.edu/cgi/viewcontent.cgi?article=2102&context=utk_graddiss}
}
-@article{Favier2009c,
+@phdthesis{Favier2009c,
title={Approche numérique par éléments discrets 3D de la sollicitation d'un écoulement granulaire sur un obstacle},
school = {Université Grenoble I – Joseph Fourier},
author={Favier, L.},
- journal={Doctoral Dissertations},
year={2009}
}
+
+@phdthesis{Charlas2013,
+ author = {Benoit Charlas},
+ school = {Université de Grenoble},
+ title = {Etude du comportement mécanique d'un hydrure intermétallique utilisé pour le stockage d'hydrogène},
+ year = {2013},
+ url = {https://www.yade-dem.org/w/images/8/89/These_BenoitCharlas.pdf}
+}
+
+@phdthesis{Catalano2012,
+ author = {Emanuele Catalano},
+ school = {Université de Grenoble},
+ title = {A pore-scale coupled hydromechanical model for biphasic granular media},
+ year = {2012},
+ url = {https://yade-dem.org/publi/Catalano_Thesis.pdf}
+}
\ No newline at end of file
=== modified file 'examples/LudingPM.py'
--- examples/LudingPM.py 2013-07-09 12:02:21 +0000
+++ examples/LudingPM.py 2013-10-11 19:59:57 +0000
@@ -5,17 +5,52 @@
fr = 0.5;rho=2000
-o.dt = 0.000002
-
-r1 = 10.0e-2
-r2 = 10.0e-2
-
-mat1 = O.materials.append(LudingMat(frictionAngle=fr,density=rho,k1=0.2, kp=0.9, kc=0.1,PhiF=0.01, G0 = 0.0))
-
-id1 = O.bodies.append(sphere(center=[0,0,0],radius=r1,material=mat1,fixed=True))
-id2 = O.bodies.append(sphere(center=[0,0,(r1 + r2)],radius=r2,material=mat1,fixed=True))
-
-iterN = 10000000
+o.dt = 0.00002
+
+r1 = 2.0e-1
+r2 = 2.0e-1
+#=======================================
+k1 = 105.0
+kp = 5.0*k1
+kc = k1
+
+DeltaPMax = 0.031
+Chi1 = 0.34
+Chi2 = 0.69
+Chi3 = 1.1
+Chi4 = 1.39
+#=======================================
+
+particleMass = 4.0/3.0*math.pi*r1*r1*r1*rho
+Vi1 = math.sqrt(k1/particleMass)*DeltaPMax*Chi1
+Vi2 = math.sqrt(k1/particleMass)*DeltaPMax*Chi2
+Vi3 = math.sqrt(k1/particleMass)*DeltaPMax*Chi3
+Vi4 = math.sqrt(k1/particleMass)*DeltaPMax*Chi4
+
+PhiF1 = DeltaPMax*(kp-k1)*(r1+r2)/(kp*2*r1*r2)
+PhiF2 = DeltaPMax*(kp-k1)*(r1+r2)/(kp*2*r1*r2)
+PhiF3 = DeltaPMax*(kp-k1)*(r1+r2)/(kp*2*r1*r2)
+PhiF4 = DeltaPMax*(kp-k1)*(r1+r2)/(kp*2*r1*r2)
+
+#=======================================
+
+mat1 = O.materials.append(LudingMat(frictionAngle=fr,density=rho,k1=k1, kp=kp, kc=kc, PhiF=PhiF1, G0 = 0.0))
+mat2 = O.materials.append(LudingMat(frictionAngle=fr,density=rho,k1=k1, kp=kp, kc=kc, PhiF=PhiF2, G0 = 0.0))
+mat3 = O.materials.append(LudingMat(frictionAngle=fr,density=rho,k1=k1, kp=kp, kc=kc, PhiF=PhiF3, G0 = 0.0))
+mat4 = O.materials.append(LudingMat(frictionAngle=fr,density=rho,k1=k1, kp=kp, kc=kc, PhiF=PhiF4, G0 = 0.0))
+
+id11 = O.bodies.append(sphere(center=[0,0,0],radius=r1,material=mat1,fixed=True))
+id12 = O.bodies.append(sphere(center=[0,0,(r1 + r2)],radius=r2,material=mat1,fixed=False))
+
+id21 = O.bodies.append(sphere(center=[0,3.0*r1,0],radius=r1,material=mat2,fixed=True))
+id22 = O.bodies.append(sphere(center=[0,3.0*r1,(r1 + r2)],radius=r2,material=mat2,fixed=False))
+
+id31 = O.bodies.append(sphere(center=[0,6.0*r1,0],radius=r1,material=mat3,fixed=True))
+id32 = O.bodies.append(sphere(center=[0,6.0*r1,(r1 + r2)],radius=r2,material=mat3,fixed=False))
+
+id41 = O.bodies.append(sphere(center=[0,9.0*r1,0],radius=r1,material=mat4,fixed=True))
+id42 = O.bodies.append(sphere(center=[0,9.0*r1,(r1 + r2)],radius=r2,material=mat4,fixed=False))
+
o.engines = [
ForceResetter(),
@@ -27,41 +62,43 @@
),
NewtonIntegrator(damping=0,gravity=[0,0,0]),
PyRunner(command='addPlotData()',iterPeriod=1000),
- PyRunner(command='changeDirection()',iterPeriod=iterN,label="cDir")
]
-velTmp = 0.001
-
-O.bodies[id2].state.vel=[0,0,-velTmp]
-
-def changeDirection():
- global iterN
- if (O.bodies[id2].state.vel[2]<0.0):
- O.bodies[id2].state.vel*=-1.0
- cDir.iterPeriod = int(iterN/10000.0)
- elif (O.bodies[id2].state.pos[2]-O.bodies[id1].state.pos[2])-(r1 + r2) > 0.0:
- iterN = int(iterN*1.2)
- O.bodies[id2].state.vel[2]*=-1.0
- cDir.iterPeriod = iterN
-
+O.bodies[id12].state.vel=[0,0,-Vi1]
+O.bodies[id22].state.vel=[0,0,-Vi2]
+O.bodies[id32].state.vel=[0,0,-Vi3]
+O.bodies[id42].state.vel=[0,0,-Vi4]
+
def addPlotData():
- f = [0,0,0]
- sc = 0
+ f1 = [0,0,0]
+ f2 = [0,0,0]
+ f3 = [0,0,0]
+ f4 = [0,0,0]
+
try:
- f=O.forces.f(id2)
+ f1=O.forces.f(id12)
+ f2=O.forces.f(id22)
+ f3=O.forces.f(id32)
+ f4=O.forces.f(id42)
except:
- f = [0,0,0]
-
- s1 = (O.bodies[id2].state.pos[2]-O.bodies[id1].state.pos[2])-(r1 + r2)
-
- fc1 = f[2]
- sc1 = -s1/r1
+ f1 = [0,0,0]
+ f2 = [0,0,0]
+ f3 = [0,0,0]
+ f4 = [0,0,0]
+
+ s1 = (O.bodies[id12].state.pos[2]-O.bodies[id11].state.pos[2])-(r1 + r2)
+ s2 = (O.bodies[id22].state.pos[2]-O.bodies[id21].state.pos[2])-(r1 + r2)
+ s3 = (O.bodies[id32].state.pos[2]-O.bodies[id31].state.pos[2])-(r1 + r2)
+ s4 = (O.bodies[id42].state.pos[2]-O.bodies[id41].state.pos[2])-(r1 + r2)
+
- plot.addData(fc1=fc1, sc=sc1)
+ plot.addData(fc1=f1[2], sc1=-s1, fc2=f2[2], sc2=-s2, fc3=f3[2], sc3=-s3, fc4=f4[2], sc4=-s4)
-plot.plots={'sc':('fc1')}; plot.plot()
+plot.plots={'sc1':('fc1'), 'sc2':('fc2'), 'sc3':('fc3'), 'sc4':('fc4')}; plot.plot()
from yade import qt
qt.View()
+O.run(320000)
+O.wait()
plot.saveGnuplot('sim-data_LudigPM')
=== added file 'examples/baraban/BicyclePedalEngine.py'
--- examples/baraban/BicyclePedalEngine.py 1970-01-01 00:00:00 +0000
+++ examples/baraban/BicyclePedalEngine.py 2013-11-11 16:22:15 +0000
@@ -0,0 +1,62 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+import time
+
+## PhysicalParameters
+Density=2400
+frictionAngle=radians(35)
+tc = 0.001
+en = 0.3
+es = 0.3
+
+## Import wall's geometry
+params=getViscoelasticFromSpheresInteraction(tc,en,es)
+facetMat=O.materials.append(ViscElMat(frictionAngle=frictionAngle,**params)) # **params sets kn, cn, ks, cs
+sphereMat=O.materials.append(ViscElMat(density=Density,frictionAngle=frictionAngle,**params))
+from yade import ymport
+fctIds=O.bodies.append(ymport.stl('baraban.stl',color=(1,0,0),material=facetMat))
+## Spheres
+sphereRadius = 0.2
+nbSpheres = (10,10,10)
+#nbSpheres = (50,50,50)
+for i in xrange(nbSpheres[0]):
+ for j in xrange(nbSpheres[1]):
+ for k in xrange(nbSpheres[2]):
+ x = (i*2 - nbSpheres[0])*sphereRadius*1.1
+ y = (j*2 - nbSpheres[1])*sphereRadius*1.1
+ z = (k*2 - nbSpheres[2])*sphereRadius*1.1
+ s=sphere([x,y,z],sphereRadius,material=sphereMat)
+ O.bodies.append(s)
+
+## Timestep
+O.dt=.2*tc
+
+## Engines
+O.engines=[
+ ## Resets forces and momenta the act on bodies
+ ForceResetter(),
+ ## Using bounding boxes find possible body collisions.
+ InsertionSortCollider([Bo1_Sphere_Aabb(),Bo1_Facet_Aabb()]),
+ ## Interactions
+ InteractionLoop(
+ ## Create geometry information about each potential collision.
+ [Ig2_Sphere_Sphere_ScGeom(), Ig2_Facet_Sphere_ScGeom()],
+ ## Create physical information about the interaction.
+ [Ip2_ViscElMat_ViscElMat_ViscElPhys()],
+ ## Constitutive law
+ [Law2_ScGeom_ViscElPhys_Basic()],
+ ),
+ ## Apply gravity
+ ## Cundall damping must been disabled!
+ NewtonIntegrator(damping=0,gravity=[0,-9.81,0]),
+## Saving results
+ #VTKRecorder(virtPeriod=0.04,fileName='/tmp/stlimp-',recorders=['spheres','facets']),
+ ## Apply kinematics to walls
+ BicyclePedalEngine(ids=fctIds,rotationAxis=[0,0,1],radius = 1.2,angularVelocity=4.0)
+]
+
+from yade import qt
+qt.View()
+#O.saveTmp()
+#O.run()
+
=== modified file 'examples/clumps/addToClump-example.py'
--- examples/clumps/addToClump-example.py 2013-07-05 08:21:09 +0000
+++ examples/clumps/addToClump-example.py 2013-08-21 06:35:10 +0000
@@ -62,13 +62,13 @@
getClumpInfo()
#add a sphere to the clump:
-O.bodies.addToClump(id_new,id_clump1)
+O.bodies.addToClump([id_new],id_clump1)
print '\nSTATE after adding sphere to clump ------------'
getClumpInfo()
#add a clump to a clump:
-O.bodies.addToClump(id_clump2,id_clump1)
+O.bodies.addToClump([id_clump2],id_clump1)
print '\nSTATE after adding the second clump to clump ------------'
getClumpInfo()
=== modified file 'examples/clumps/apply-buoyancy-clumps.py'
--- examples/clumps/apply-buoyancy-clumps.py 2013-06-24 13:23:34 +0000
+++ examples/clumps/apply-buoyancy-clumps.py 2013-10-17 09:11:53 +0000
@@ -4,26 +4,26 @@
''' The script shows how to include the effect of buoyancy in a particle assembly
under condition of an increasing water-level. Water-level at start of the sim-
ulation is at the lower boundary of the model. During the calculation particles
- gets partly surrounded by water and buoyancy (=density*volumeOfDisplacedWater)
+ get partly surrounded by water and buoyancy (=fluidDensity*volumeOfDisplacedWater)
is increasing until particle is completely surrounded. When particle is sur-
rounded volumeOfDisplacedWater is equal to the volume of the particle.
- For calculation of buoyancy of a clump the theoretical radius
- R = (3*mass/(4*pi*density))^1/3
+ For calculation of buoyancy of a clump the equivalent radius
+ R = (3*clumpMass/(4*pi*particleDensity))^1/3
of a sphere with clump mass and clump volume
- V = (4*pi*R^3)/3 = mass/density
+ V = (4*pi*R^3)/3 = clumpMass/particleDensity
is used. This approximation can be used for well rounded clumps.
- Buoyancy is included via reduced mass (=massAtStart - dm), where dm is the
- buoyancy. Reduced mass must be corrected via density scaling for calculation
- of correct particle accelerations.'''
+ Buoyancy is included with an additional force
+ F_buo = -volumeOfDisplacedWater*fluidDensity*gravityAcceleration.'''
#define material properties:
-shear_modulus = 3.2e10
-poisson_ratio = 0.15
-young_modulus = 2*shear_modulus*(1+poisson_ratio)
+shearModulus = 3.2e10
+poissonRatio = 0.15
+youngModulus = 2*shearModulus*(1+poissonRatio)
angle = 0.5 #friction angle in radians
rho_p = 2650 #density of particles
+rho_f = 5000 #density of the fluid rho_f > rho_p = floating particles
v_iw = 1 #velocity of increasing water-level
@@ -31,21 +31,31 @@
boundaryMin = Vector3(-1.5,-1.5,0)
boundaryMax = Vector3(1.5,1.5,2)
+#define colors:
+sphereColor = (.8,.8,0.)#dirty yellow
+clumpColor = (1.,.55,0.)#dark orange
+boxColor = (.1,.5,.1)#green
+waterColor = (.2,.2,.7)#blue
+
#material:
-id_Mat=O.materials.append(FrictMat(young=young_modulus,poisson=poisson_ratio,density=rho_p,frictionAngle=angle))
+id_Mat=O.materials.append(FrictMat(young=youngModulus,poisson=poissonRatio,density=rho_p,frictionAngle=angle))
Mat=O.materials[id_Mat]
#create assembly of spheres:
sp=pack.SpherePack()
sp.makeCloud(minCorner=boundaryMin,maxCorner=boundaryMax,rMean=.2,rRelFuzz=.5,num=100,periodic=False)
-O.bodies.append([sphere(c,r,material=Mat) for c,r in sp])
+O.bodies.append([sphere(c,r,material=Mat,color=sphereColor) for c,r in sp])
#create template for clumps and replace 10% of spheres by clumps:
templateList = [clumpTemplate(relRadii=[1,.5],relPositions=[[0,0,0],[.7,0,0]])]
O.bodies.replaceByClumps(templateList,[.1])
+#color clumps:
+for b in O.bodies:
+ if b.isClumpMember:
+ b.shape.color=clumpColor
#create boundary:
-O.bodies.append(facetBox((0,0,1), (boundaryMax-boundaryMin)/2, fixed=True, material=Mat))#boundaryMax-boundaryMin
+O.bodies.append(geom.facetBox((0,0,1), (boundaryMax-boundaryMin)/2, fixed=True, material=Mat, color=boxColor))
#define engines:
O.engines=[
@@ -53,93 +63,74 @@
InsertionSortCollider([Bo1_Sphere_Aabb(),Bo1_Facet_Aabb()]),
InteractionLoop(
[Ig2_Sphere_Sphere_ScGeom(),Ig2_Facet_Sphere_ScGeom()],
- [Ip2_FrictMat_FrictMat_MindlinPhys(betan=0.0,betas=0.0,label='ContactModel')],
- [Law2_ScGeom_MindlinPhys_Mindlin(neverErase=False,label='Mindlin')]
+ [Ip2_FrictMat_FrictMat_MindlinPhys()],
+ [Law2_ScGeom_MindlinPhys_Mindlin(neverErase=False)]
),
- GlobalStiffnessTimeStepper(active=1,timeStepUpdateInterval=1,timestepSafetyCoefficient=0.8, defaultDt=0.9*PWaveTimeStep(),label='ts'),
NewtonIntegrator(gravity=(0,0,-10),damping=0.7,label='integrator')
]
-ts.dead=True
#definition to apply buoyancy:
-def apply_buo(water_height,saturatedList,startMass):
+def applyBuoyancy():
+ global waterLevel
+ waterLevel = (O.time-t0) * v_iw + boundaryMin[2]
for b in O.bodies:
- if b not in saturatedList:
- h_low = 1e9
- h_high = -1e9
- if b.isStandalone and isinstance(b.shape,Sphere):
- rad = b.shape.radius
- pos = b.state.pos
- h_low = pos[2] - rad
- h_high = pos[2] + rad
- elif b.isClump: #determine rad, h_high and h_low for clumps:
- keys = O.bodies[b.id].shape.members.keys()
- for ii in range(0,len(keys)):
- pos = O.bodies[keys[ii]].state.pos
- h_low = min(h_low,pos[2]-O.bodies[keys[ii]].shape.radius)
- h_high = max(h_high,pos[2]+O.bodies[keys[ii]].shape.radius)
- rad = ( 3*startMass[b.id]/(4*pi*O.bodies[keys[0]].mat.density) )**(1./3.) #get radius from startMass
- else:
- continue
- if water_height > h_low:
- dh = water_height - h_low
- dh = min(dh,2*rad) #to get sure, that dh is not bigger than 2*radius
- dm = (pi/3)*dh*dh*(3*rad - dh)*1000 #buoyancy acts via reduced mass dm=rho*V_cap
- b.state.mass = startMass[b.id] - dm #needs a list of masses of all particles at start (see 5-final.py)
- #reduced mass must be corrected via density scaling for accelerations a = F/m_reduced, a_correct = a*densityScaling, see line 179 in pkg/dem/NewtonIntegrator.cpp:
- b.state.densityScaling = (startMass[b.id] - dm)/startMass[b.id]
- if water_height > h_high:
- saturatedList.append(b)
- return saturatedList
+ zMin = 1e9
+ zMax = -1e9
+ if b.isStandalone and isinstance(b.shape,Sphere):
+ rad = b.shape.radius
+ zMin = b.state.pos[2] - rad
+ dh = min((waterLevel - zMin),2*rad) #to get sure, that dh is not bigger than 2*radius
+ elif b.isClump: #determine rad, zMin and zMax for clumps:
+ for ii in b.shape.members.keys():
+ pos = O.bodies[ii].state.pos
+ zMin = min(zMin,pos[2]-O.bodies[ii].shape.radius)
+ zMax = max(zMax,pos[2]+O.bodies[ii].shape.radius)
+ #get equivalent radius from clump mass:
+ rad = ( 3*b.state.mass/(4*pi*O.bodies[b.shape.members.keys()[0]].mat.density) )**(1./3.)
+ #get dh relative to equivalent sphere, but acting when waterLevel is between clumps z-dimensions zMin and zMax:
+ dh = min((waterLevel - zMin)*2*rad/(zMax - zMin),2*rad)
+ else:
+ continue
+ if dh > 0:
+ F_buo = -1*(pi/3)*dh*dh*(3*rad - dh)*rho_f*integrator.gravity # = -V*rho*g
+ O.forces.addF(b.id,F_buo,permanent=True)
#STEP1: reduce overlaps from replaceByClumps() method:
O.dt=1e-6 #small time step for preparation steps via calm()
print '\nSTEP1 in progress. Please wait a minute ...\n'
O.engines=O.engines+[PyRunner(iterPeriod=10000,command='calm()',label='calmRunner')]
-O.run(1000000,True)
+O.run(100000,True)
+
+#STEP2: let particles settle down
calmRunner.dead=True
-
-#STEP2: let particles settle down:
-O.dt=1e-5
+O.dt=2e-5
print '\nSTEP2 in progress. Please wait a minute ...\n'
O.run(50000,True)
-#get maximum body id:
-idmax=0
-for b in O.bodies:
- idmax=max(idmax,b.id)
-integrator.densityScaling=True #activate density scaling
-
-#get a list of masses at start:
-startMass = [0 for ii in range(0,idmax+1)]
-for b in O.bodies:
- if (b.isStandalone and isinstance(b.shape,Sphere)) or b.isClump:
- startMass[b.id] = b.state.mass
-
#start PyRunner engine to apply buoyancy:
-saturatedList = []
t0 = O.time
-O.engines=O.engines+[PyRunner(iterPeriod=100,command='saturatedList=apply_buo(((O.time-t0) * v_iw),saturatedList,startMass)',label='buoLabel')]
+waterLevel = boundaryMin[2]
+O.engines=O.engines+[PyRunner(iterPeriod=100,command='applyBuoyancy()',label='buoLabel')]
#create 2 facets, that show water height:
idsWaterFacets = []
idsWaterFacets.append(O.bodies.append(facet( \
[ boundaryMin, [boundaryMax[0],boundaryMin[1],boundaryMin[2]], [boundaryMax[0],boundaryMax[1],boundaryMin[2]] ], \
- fixed=True,material=FrictMat(young=0),color=(0,0,1),wire=False)))#no interactions will appear
+ fixed=True,material=FrictMat(young=0),color=waterColor,wire=False)))#no interactions will appear
idsWaterFacets.append(O.bodies.append(facet( \
[ [boundaryMax[0],boundaryMax[1],boundaryMin[2]], [boundaryMin[0],boundaryMax[1],boundaryMin[2]], boundaryMin ], \
- fixed=True,material=FrictMat(young=0),color=(0,0,1),wire=False)))#no interactions will appear
+ fixed=True,material=FrictMat(young=0),color=waterColor,wire=False)))#no interactions will appear
#set velocity of incr. water level to the facets:
for ids in idsWaterFacets:
O.bodies[ids].state.vel = [0,0,v_iw]
-#STEP3: simulate buoyancy#
+#STEP3: simulate buoyancy with increasing water table condition
O.dt=3e-5
from yade import qt
qt.Controller()
v=qt.View()
v.eyePosition=(-7,0,2); v.upVector=(0,0,1); v.viewDir=(1,0,-.1); v.axes=True; v.sceneRadius=1.9
print '\nSTEP3 started ...\n'
-O.run(60000)
+O.run(70000)
=== modified file 'examples/clumps/replaceByClumps-example.py'
--- examples/clumps/replaceByClumps-example.py 2013-04-16 12:37:01 +0000
+++ examples/clumps/replaceByClumps-example.py 2013-10-04 13:41:43 +0000
@@ -33,7 +33,7 @@
O.bodies.append([sphere(c,r,material=Mat) for c,r in sp])
print len(sp),' particles generated.'
-print 'Roundness coefficient without clumps is: ',O.bodies.getRoundness([]) #give an empty list [] if no body should be excluded
+print 'Roundness coefficient without clumps is: ',O.bodies.getRoundness()
#### show how to use makeClumpTemplate():
@@ -73,7 +73,7 @@
if b.isStandalone:
standaloneList.append(b.id)
-print 'Roundness coefficient for spheres and clumps is: ',O.bodies.getRoundness([]) #give an empty list [] if no body should be excluded
+print 'Roundness coefficient for spheres and clumps is: ',O.bodies.getRoundness()
print 'Roundness coefficient just for clumps is: ',O.bodies.getRoundness(standaloneList)
O.dt=1e-6
=== modified file 'examples/concrete/triax.py'
--- examples/concrete/triax.py 2013-05-27 06:02:45 +0000
+++ examples/concrete/triax.py 2013-08-09 02:27:56 +0000
@@ -56,12 +56,12 @@
# spheres
pred = pack.inCylinder((0,0,0),(0,0,height),.5*width) if testType=='cyl' else pack.inAlignedBox((-.5*width,-.5*width,0),(.5*width,.5*width,height)) if testType=='cube' else None
-sp = pack.randomDensePack(pred,spheresInCell=2000,radius=rParticle,memoizeDb='/tmp/triaxTestOnCylinder.sqlite',color=(0,1,1),material=concMat)
-O.bodies.append(sp)
+sp=SpherePack()
+sp = pack.randomDensePack(pred,spheresInCell=2000,radius=rParticle,memoizeDb='/tmp/triaxTestOnCylinder.sqlite',material=concMat,returnSpherePack=True)
+spheres=sp.toSimulation(color=(0,1,1))
# bottom and top of specimen. Will have prescribed velocity
-bot = [s for s in sp if s.state.pos[2]<rParticle*bcCoeff]
-top = [s for s in sp if s.state.pos[2]>height-rParticle*bcCoeff]
-for s in sp: s.shape.color = (0,1,1)
+bot = [O.bodies[s] for s in spheres if O.bodies[s].state.pos[2]<rParticle*bcCoeff]
+top = [O.bodies[s] for s in spheres if O.bodies[s].state.pos[2]>height-rParticle*bcCoeff]
vel = strainRate*(height-rParticle*2*bcCoeff)
for s in bot:
s.shape.color = (1,0,0)
=== modified file 'examples/concrete/uniax.py'
--- examples/concrete/uniax.py 2013-05-27 06:02:45 +0000
+++ examples/concrete/uniax.py 2013-08-09 02:27:56 +0000
@@ -74,9 +74,10 @@
# using spheres 7mm of diameter
concreteId=O.materials.append(CpmMat(young=young,frictionAngle=frictionAngle,poisson=poisson,density=4800,sigmaT=sigmaT,relDuctility=relDuctility,epsCrackOnset=epsCrackOnset,isoPrestress=isoPrestress))
-spheres=pack.randomDensePack(pack.inHyperboloid((0,0,-.5*specimenLength),(0,0,.5*specimenLength),.25*specimenLength,.17*specimenLength),spheresInCell=2000,radius=sphereRadius,memoizeDb='/tmp/triaxPackCache.sqlite',material=concreteId)
-#spheres=pack.randomDensePack(pack.inAlignedBox((-.25*specimenLength,-.25*specimenLength,-.5*specimenLength),(.25*specimenLength,.25*specimenLength,.5*specimenLength)),spheresInCell=2000,radius=sphereRadius,memoizeDb='/tmp/triaxPackCache.sqlite')
-O.bodies.append(spheres)
+sps=SpherePack()
+sp=pack.randomDensePack(pack.inHyperboloid((0,0,-.5*specimenLength),(0,0,.5*specimenLength),.25*specimenLength,.17*specimenLength),spheresInCell=2000,radius=sphereRadius,memoizeDb='/tmp/triaxPackCache.sqlite',returnSpherePack=True)
+#sp=pack.randomDensePack(pack.inAlignedBox((-.25*specimenLength,-.25*specimenLength,-.5*specimenLength),(.25*specimenLength,.25*specimenLength,.5*specimenLength)),spheresInCell=2000,radius=sphereRadius,memoizeDb='/tmp/triaxPackCache.sqlite',returnSpherePack=True)
+sp.toSimulation(material=concreteId)
bb=uniaxialTestFeatures()
negIds,posIds,axis,crossSectionArea=bb['negIds'],bb['posIds'],bb['axis'],bb['area']
O.dt=dtSafety*PWaveTimeStep()
=== modified file 'examples/gts-horse/gts-random-pack-obb.py'
--- examples/gts-horse/gts-random-pack-obb.py 2012-03-21 13:01:46 +0000
+++ examples/gts-horse/gts-random-pack-obb.py 2013-08-09 02:27:56 +0000
@@ -19,12 +19,16 @@
# fill this solid with triaxial packing; it will compute minimum-volume oriented bounding box
# to minimize the number of throw-away spheres.
# It does away with about 3k spheres for radius 3e-2
-O.bodies.append(pack.randomDensePack(pack.inGtsSurface(surf),radius=3e-2,rRelFuzz=1e-1,memoizeDb='/tmp/gts-triax.sqlite'))
+sp1=SpherePack()
+sp1=pack.randomDensePack(pack.inGtsSurface(surf),radius=3e-2,rRelFuzz=1e-1,memoizeDb='/tmp/gts-triax.sqlite',returnSpherePack=True)
+sp1.toSimulation()
# translate the surface away and pack it again with sphere, but without the oriented bounding box (useOBB=False)
# Here, we need 20k spheres (with more or less the same result)
surf.translate(0,0,1);
O.bodies.append(pack.gtsSurface2Facets(surf,color=(1,0,0)))
-O.bodies.append(pack.randomDensePack(pack.inGtsSurface(surf),radius=3e-2,rRelFuzz=1e-1,memoizeDb='/tmp/gts-triax-packings.sqlite',useOBB=False))
+sp2=SpherePack()
+sp2=pack.randomDensePack(pack.inGtsSurface(surf),radius=3e-2,rRelFuzz=1e-1,memoizeDb='/tmp/gts-triax-packings.sqlite',useOBB=False,returnSpherePack=True)
+sp2.toSimulation()
from yade import qt
qt.View()
=== modified file 'examples/gts-horse/gts-random-pack.py'
--- examples/gts-horse/gts-random-pack.py 2012-03-21 13:01:46 +0000
+++ examples/gts-horse/gts-random-pack.py 2013-08-09 02:27:56 +0000
@@ -37,7 +37,9 @@
# it will load the packing instead of running the triaxial compaction again.
# Try running for the second time to see the speed difference!
memoizeDb='/tmp/gts-triax-packings.sqlite'
-O.bodies.append(pack.randomDensePack(pack.inGtsSurface(surf),radius=5e-3,rRelFuzz=1e-4,memoizeDb=memoizeDb))
+sp=SpherePack()
+sp=pack.randomDensePack(pack.inGtsSurface(surf),radius=5e-3,rRelFuzz=1e-4,memoizeDb=memoizeDb,returnSpherePack=True)
+sp.toSimulation()
# We could also fill the horse with triaxial packing, but have nice approximation, the triaxial would run terribly long,
# since horse discard most volume of its bounding box
# Here, we would use a very crude one, however
@@ -45,8 +47,10 @@
import gts
horse=gts.read(open('horse.coarse.gts')) #; horse.scale(.25,.25,.25)
O.bodies.append(pack.gtsSurface2Facets(horse))
- O.bodies.append(pack.randomDensePack(pack.inGtsSurface(horse),radius=5e-3,memoizeDb=memoizeDb))
+ sp=pack.randomDensePack(pack.inGtsSurface(horse),radius=5e-3,memoizeDb=memoizeDb,returnSpherePack=True)
+ sp.toSimulation()
horse.translate(.07,0,0)
O.bodies.append(pack.gtsSurface2Facets(horse))
# specifying spheresInCell makes the packing periodic, with the given number of spheres, proportions being equal to that of the predicate
- O.bodies.append(pack.randomDensePack(pack.inGtsSurface(horse),radius=1e-3,spheresInCell=2000,memoizeDb=memoizeDb))
+ sp=pack.randomDensePack(pack.inGtsSurface(horse),radius=1e-3,spheresInCell=2000,memoizeDb=memoizeDb,returnSpherePack=True)
+ sp.toSimulation()
=== added directory 'examples/jointedCohesiveFrictionalPM'
=== added file 'examples/jointedCohesiveFrictionalPM/README'
--- examples/jointedCohesiveFrictionalPM/README 1970-01-01 00:00:00 +0000
+++ examples/jointedCohesiveFrictionalPM/README 2013-10-04 09:47:21 +0000
@@ -0,0 +1,16 @@
+Every files contained in this folder are part of ongoing work initiated by luc scholtes and phd students on the modelling of fractured/jointed media. For details/remarks/improvements, please send emails to lscholtes63@xxxxxxxxx. Also, please refer to following references for more details and explananations:
+-[1] Harthong B., Scholtès L., Donzé F.V. (2012). Strength characterisation of rock masses using a DEM-DFN model, Geophys J Int, 191, 467-480.
+-[2] Scholtès L., Donzé F.V. (2012). Modelling progressive failure in fractured rock masses using a 3D Discrete Element Method, Int J Rock Mech Min Sci, 52,18-30.
+-[3] Scholtès L., Donzé F.V. (2011). Scale effects on strength of geomaterials, case study: coal, J Mech Phys Solids, 59(5), 1131-1146.
+
+jcfPM folder contains files illustrating how Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM can be used to deal with jointed media (media containing pre-existing discontinuity surfaces (fractures)).
+
+1-packInGtsSurface.py is a simple script used to generate a packing (parallellepiped_10.spheres) from a meshed closed surface (parallellepiped.gts).
+Packing generation can be done with other methods. The point here is to generate a packing and to save it in a text file (parallellepiped_10.spheres).
+
+2-identificationSpheresOnJoint.py is a script used to identify the spheres belonging to a predefined packing (parallellepiped_10.spheres) interacting along pre-existing discontinuity surfaces defined by a meshed surface (persistentPlane30Deg.stl). Executing this script produces 2 text files containing respectively spheres from packing (parallellepiped_10_persistentPlane30Deg.spheres) and spheres attributes regarding jcfPM variables (parallellepiped_10_persistentPlane30Deg_jointedPM.spheres).
+
+3-gravityLoading.py is a simple example showing how to use Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM. It simulates the loading of a pre-fractured packing (parallellepiped_10_persistentPlane30Deg.spheres) by gravity to emphasize how the pre-existing discontinuity affects the behavior. User can play along with joint surface properties (smooth contact logic or not, joint friction angle,...) to see the effect on the simulated behavior.
+
+
+A more compact way of use is proposed in gravityBis.py. This script is stand-alone, and calls itself the scrit identifBis.py. This later script can be called from any script in JCFpm modelling to detect spheres that should interact according to smooth contact logic.
\ No newline at end of file
=== added file 'examples/jointedCohesiveFrictionalPM/gravityBis.py'
--- examples/jointedCohesiveFrictionalPM/gravityBis.py 1970-01-01 00:00:00 +0000
+++ examples/jointedCohesiveFrictionalPM/gravityBis.py 2013-11-04 14:30:22 +0000
@@ -0,0 +1,129 @@
+# encoding: utf-8
+
+# example of use JCFpm classes : Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM...
+# a cubic rock massif, containing a rock joint with ~ 31 deg. dip angle. At one time, the mechanical properties of the joint are degraded, triggering a smooth sliding
+
+
+# definition of a predicate for use of randomDensePack() function
+
+from yade import pack
+dimModele = 10.0
+pred = pack.inAlignedBox((0,0,0),(dimModele,dimModele,dimModele))
+
+
+# the packing of spheres :
+
+def mat(): return JCFpmMat(type=1,young=1e8,poisson=0.3,frictionAngle=radians(30),density=3000,tensileStrength=1e6,cohesion=1e6,jointNormalStiffness=1e7,jointShearStiffness=1e7,jointCohesion=1e6,jointFrictionAngle=radians(20),jointDilationAngle=0.0)
+nSpheres = 3000.0
+poros=0.13
+rMeanSpheres = dimModele * pow(3.0/4.0*(1-poros)/(pi*nSpheres),1.0/3.0)
+print ''
+print 'Creating a cubic sample of spheres (may take some time)'
+print ''
+sp = pack.randomDensePack(pred,radius=rMeanSpheres,rRelFuzz=0.3,memoizeDb='/tmp/gts-triax-packings.sqlite',returnSpherePack=True)
+sp.toSimulation(color=(0.9,0.8,0.6),wire=False,material=mat)
+print ''
+print 'Sample created !'
+
+
+# Definition of the facets for joint's geometry
+
+import gts
+# joint with ~ 31 deg. dip angle
+v1 = gts.Vertex(0 , 0 , 0.8*dimModele)
+v2 = gts.Vertex(0 , dimModele , 0.8*dimModele )
+v3 = gts.Vertex(dimModele , dimModele , 0.2*dimModele)
+v4 = gts.Vertex(dimModele , 0 , 0.2*dimModele)
+
+e1 = gts.Edge(v1,v2)
+e2 = gts.Edge(v2,v4)
+e3 = gts.Edge(v4,v1)
+f1 = gts.Face(e1,e2,e3)
+
+e4 = gts.Edge(v4,v3)
+e5 = gts.Edge(v3,v2)
+f2 = gts.Face(e2,e4,e5)
+
+s1 = gts.Surface()
+s1.add(f1)
+s1.add(f2)
+
+facet = gtsSurface2Facets(s1,wire = False,material=mat)
+O.bodies.append(facet)
+
+yade.qt.View()
+yade.qt.Controller()
+
+O.saveTmp()
+# identification of spheres onJoint, and so on:
+execfile('identifBis.py')
+dim=utils.aabbExtrema()
+xsup=dim[1][0]
+yinf=dim[0][1]
+ysup=dim[1][1]
+zinf=dim[0][2]
+zsup=dim[1][2]
+
+e=2*rMeanSpheres
+
+for o in O.bodies:
+ if isinstance(o.shape,Sphere):
+ o.shape.color=(0.9,0.8,0.6)
+ ## to fix boundary particles on ground
+ if o.state.pos[2]<(zinf+2*e) :
+ o.state.blockedDOFs+='xyz'
+ o.shape.color=(1,1,1)
+
+ ## to identify indicator on top
+ if o.state.pos[2]>(zsup-e) and o.state.pos[0]>(xsup-e) and o.state.pos[1]>((yinf+ysup-e)/2.0) and o.state.pos[1]<((yinf+ysup+e)/2) :
+ refPoint=o.id
+
+O.bodies[refPoint].shape.highlight=True
+
+#### Engines definition
+O.engines=[
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Sphere_Aabb(),]),
+ InteractionLoop(
+ [Ig2_Sphere_Sphere_ScGeom()],
+# [Ip2_JCFpmMat_JCFpmMat_JCFpmPhys(cohesiveTresholdIteration=1,alpha=0.3,tensileStrength=1e6,cohesion=1e6,jointNormalStiffness=1e7,jointShearStiffness=1e7,jointCohesion=1e6,jointFrictionAngle=radians(20),jointDilationAngle=0.0)],
+ [Ip2_JCFpmMat_JCFpmMat_JCFpmPhys(cohesiveTresholdIteration=1)],
+ [Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM(smoothJoint=True)]
+ ),
+ GlobalStiffnessTimeStepper(timestepSafetyCoefficient=0.8),
+ PyRunner(iterPeriod=1000,initRun=False,command='jointStrengthDegradation()'),
+ PyRunner(iterPeriod=10,initRun=True,command='dataCollector()'),
+ NewtonIntegrator(damping=0.7,gravity=(0.,0.,-10.)),
+
+]
+
+#### dataCollector
+from yade import plot
+plot.plots={'iterations':('v',)}
+#plot.plots={'x':('z',)}
+def dataCollector():
+ R=O.bodies[refPoint]
+ plot.addData(v=R.state.vel[2],z=R.state.pos[2],x=R.state.pos[0],iterations=O.iter,t=O.realtime)
+
+#### joint strength degradation
+stableIter=2000
+stableVel=0.001
+degrade=True
+def jointStrengthDegradation():
+ global degrade
+ if degrade and O.iter>=stableIter and abs(O.bodies[refPoint].state.vel[2])<stableVel :
+ print '!joint cohesion total degradation!', ' | iteration=', O.iter
+ degrade=False
+ for i in O.interactions:
+ if i.phys.isOnJoint :
+ if i.phys.isCohesive:
+ i.phys.isCohesive=False
+ i.phys.FnMax=0.
+ i.phys.FsMax=0.
+
+O.step()
+
+print 'Seeking after an initial equilibrium state'
+print ''
+O.run(10000)
+plot.plot()# note the straight line (during sliding step, before free fall) despite the discretization of joint plane with spheres
=== added file 'examples/jointedCohesiveFrictionalPM/gravityLoading.py'
--- examples/jointedCohesiveFrictionalPM/gravityLoading.py 1970-01-01 00:00:00 +0000
+++ examples/jointedCohesiveFrictionalPM/gravityLoading.py 2013-11-04 15:56:32 +0000
@@ -0,0 +1,152 @@
+# -*- coding: utf-8 -*-
+
+O=Omega()
+from yade import plot, pack,utils,ymport
+
+#### controling parameters
+packing='parallellepiped_10_persistentPlane30Deg'
+smoothContact=True
+jointFrict=radians(20)
+jointDil=radians(0)
+output='jointDip30_jointFrict20'
+maxIter=10000
+
+#### Import of the sphere assembly
+def sphereMat(): return JCFpmMat(type=1,young=1e8,frictionAngle=radians(30),density=3000,poisson=0.3,tensileStrength=1e6,cohesion=1e6,jointNormalStiffness=1e7,jointShearStiffness=1e7,jointCohesion=1e6,jointFrictionAngle=jointFrict,jointDilationAngle=jointDil) ## Rq: density needs to be adapted as porosity of real rock is different to granular assembly due to difference in porosity (utils.sumForces(baseBodies,(0,1,0))/(Z*X) should be equal to Gamma*g*h with h=Y, g=9.82 and Gamma=2700 kg/m3
+O.bodies.append(ymport.text(packing+'.spheres',scale=1.,shift=Vector3(0,0,0),material=sphereMat))
+
+## preprocessing to get dimensions of the packing
+dim=utils.aabbExtrema()
+dim=utils.aabbExtrema()
+xinf=dim[0][0]
+xsup=dim[1][0]
+X=xsup-xinf
+yinf=dim[0][1]
+ysup=dim[1][1]
+Y=ysup-yinf
+zinf=dim[0][2]
+zsup=dim[1][2]
+Z=zsup-zinf
+
+## preprocessing to get spheres dimensions
+R=0
+Rmax=0
+numSpheres=0.
+for o in O.bodies:
+ if isinstance(o.shape,Sphere):
+ numSpheres+=1
+ R+=o.shape.radius
+ if o.shape.radius>Rmax:
+ Rmax=o.shape.radius
+Rmean=R/numSpheres
+
+#### Identification of the spheres on joint (some DIY here!) -> work to do on import function textExt to directly load material properties from the ascii file
+inFile=open(packing+'_jointedPM.spheres','r')
+for line in inFile:
+ if '#' in line : continue
+ id = int(line.split()[0])
+ onJ = int(line.split()[1])
+ nj = int(line.split()[2])
+ j11 = float(line.split()[3])
+ j12 = float(line.split()[4])
+ j13 = float(line.split()[5])
+ j21 = float(line.split()[6])
+ j22 = float(line.split()[7])
+ j23 = float(line.split()[8])
+ j31 = float(line.split()[9])
+ j32 = float(line.split()[10])
+ j33 = float(line.split()[11])
+ O.bodies[id].state.onJoint=onJ
+ O.bodies[id].state.joint=nj
+ O.bodies[id].state.jointNormal1=(j11,j12,j13)
+ O.bodies[id].state.jointNormal2=(j21,j22,j23)
+ O.bodies[id].state.jointNormal3=(j31,j32,j33)
+inFile.close
+
+#### Boundary conditions
+e=2*Rmean
+Xmax=0
+Ymax=0
+baseBodies=[]
+
+for o in O.bodies:
+ if isinstance(o.shape,Sphere):
+ o.shape.color=(0.9,0.8,0.6)
+ ## to fix boundary particles on ground
+ if o.state.pos[1]<(yinf+2*e) :
+ o.state.blockedDOFs+='xyz'
+ baseBodies.append(o.id)
+ o.shape.color=(1,1,1)
+
+ ## to identify indicator on top
+ if o.state.pos[1]>(ysup-e) and o.state.pos[0]>(xsup-e) and o.state.pos[2]>(zinf+(Z-e)/2) and o.state.pos[2]<(zsup-(Z-e)/2) :
+ refPoint=o.id
+ p0=o.state.pos[1]
+
+baseBodies=tuple(baseBodies)
+O.bodies[refPoint].shape.color=(1,0,0)
+
+#### Engines definition
+interactionRadius=1. # to set initial contacts to larger neighbours
+O.engines=[
+
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Sphere_Aabb(aabbEnlargeFactor=interactionRadius,label='is2aabb'),]),
+ InteractionLoop(
+ [Ig2_Sphere_Sphere_ScGeom(interactionDetectionFactor=interactionRadius,label='ss2d3dg')],
+ [Ip2_JCFpmMat_JCFpmMat_JCFpmPhys(cohesiveTresholdIteration=1,label='interactionPhys')],
+ [Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM(smoothJoint=smoothContact,label='interactionLaw')]
+ ),
+ GlobalStiffnessTimeStepper(timestepSafetyCoefficient=0.8),
+ PyRunner(iterPeriod=1000,initRun=False,command='jointStrengthDegradation()'),
+ VTKRecorder(iterPeriod=5000,initRun=True,fileName=(output+'-'),recorders=['spheres','velocity','intr']),
+ PyRunner(iterPeriod=10,initRun=True,command='dataCollector()'),
+ NewtonIntegrator(damping=0.7,gravity=(0.,-9.82,0.)),
+
+]
+
+#### dataCollector
+plot.plots={'iterations':('p',)}
+def dataCollector():
+ R=O.bodies[refPoint]
+ plot.addData(v=R.state.vel[1],p=R.state.pos[1]-p0,iterations=O.iter,t=O.realtime)
+ plot.saveDataTxt(output)
+
+#### joint strength degradation
+stableIter=2000
+stableVel=0.001
+degrade=True
+def jointStrengthDegradation():
+ global degrade
+ if degrade and O.iter>=stableIter and abs(O.bodies[refPoint].state.vel[1])<stableVel :
+ print '!joint cohesion total degradation!', ' | iteration=', O.iter
+ degrade=False
+ for i in O.interactions:
+ if i.phys.isOnJoint :
+ if i.phys.isCohesive:
+ i.phys.isCohesive=False
+ i.phys.FnMax=0.
+ i.phys.FsMax=0.
+
+#### YADE windows
+from yade import qt
+v=qt.Controller()
+v=qt.View()
+
+#### time step definition (low here to create cohesive links without big changes in the assembly)
+O.dt=0.1*utils.PWaveTimeStep()
+
+#### set cohesive links with interaction radius>=1
+O.step();
+#### initializes now the interaction detection factor to strictly 1
+ss2d3dg.interactionDetectionFactor=-1.
+is2aabb.aabbEnlargeFactor=-1.
+#### if you want to avoid contact detection (Lattice like)
+#O.engines=O.engines[:1]+O.engines[3:]
+
+#### RUN!!!
+O.dt=-0.1*utils.PWaveTimeStep()
+
+O.run(maxIter)
+plot.plot()
+
=== added file 'examples/jointedCohesiveFrictionalPM/gravityLoading_velocityField_arrows.png'
Binary files examples/jointedCohesiveFrictionalPM/gravityLoading_velocityField_arrows.png 1970-01-01 00:00:00 +0000 and examples/jointedCohesiveFrictionalPM/gravityLoading_velocityField_arrows.png 2013-08-01 11:54:13 +0000 differ
=== added file 'examples/jointedCohesiveFrictionalPM/gravityLoading_velocityField_spheres.png'
Binary files examples/jointedCohesiveFrictionalPM/gravityLoading_velocityField_spheres.png 1970-01-01 00:00:00 +0000 and examples/jointedCohesiveFrictionalPM/gravityLoading_velocityField_spheres.png 2013-08-01 11:54:13 +0000 differ
=== added file 'examples/jointedCohesiveFrictionalPM/identifBis.py'
--- examples/jointedCohesiveFrictionalPM/identifBis.py 1970-01-01 00:00:00 +0000
+++ examples/jointedCohesiveFrictionalPM/identifBis.py 2013-11-04 14:20:24 +0000
@@ -0,0 +1,160 @@
+# -*- coding: utf-8 -*-
+
+# ---- Script to detect spheres which are "onJoint", according to JCFpm. -----
+# To be called directly within an other script, for example, with execfile('identifBis.py')
+# The sample (spheres + facets) has to exist already, with their JCFpmMat
+
+
+############################ engines definition
+interactionRadius=1.;
+O.engines=[
+
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Sphere_Aabb(aabbEnlargeFactor=interactionRadius,label='is2aabb'),Bo1_Facet_Aabb()]),
+ InteractionLoop(
+ [Ig2_Sphere_Sphere_ScGeom(interactionDetectionFactor=interactionRadius,label='ss2d3dg'),Ig2_Facet_Sphere_ScGeom()],
+ [Ip2_JCFpmMat_JCFpmMat_JCFpmPhys(cohesiveTresholdIteration=1,label='interactionPhys')],
+ [Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM(smoothJoint=True,label='interactionLaw')]
+ ),
+ NewtonIntegrator(damping=1)
+
+]
+
+############################ timestep + opening yade windows
+O.dt=0.001*utils.PWaveTimeStep()
+
+# from yade import qt
+# v=qt.Controller()
+# v=qt.View()
+
+############################ Identification spheres on joint
+#### color set for particles on joint
+jointcolor1=(0,1,0)
+jointcolor2=(1,0,0)
+jointcolor3=(0,0,1)
+jointcolor4=(1,1,1)
+jointcolor5=(0,0,0)
+
+#### first step-> find spheres on facet
+O.step();
+
+for i in O.interactions:
+ ##if not i.isReal : continue
+ ### Rk: facet are only stored in id1
+ if isinstance(O.bodies[i.id1].shape,Facet) and isinstance(O.bodies[i.id2].shape,Sphere):
+ vertices=O.bodies[i.id1].shape.vertices
+ normalRef=vertices[0].cross(vertices[1]) # defines the normal to the facet normalRef
+ nRef=normalRef/(normalRef.norm()) ## normalizes normalRef
+ normalFacetSphere=i.geom.normal # geom.normal is oriented from id1 to id2 -> normalFacetSphere from facet (i.id1) to sphere (i.id2)
+
+ if O.bodies[i.id2].state.onJoint==False : ## particles has not yet been identified as belonging to a joint plane
+ O.bodies[i.id2].state.onJoint=True
+ O.bodies[i.id2].state.joint=1
+ O.bodies[i.id2].shape.color=jointcolor1
+ if nRef.dot(normalFacetSphere)>=0 :
+ O.bodies[i.id2].state.jointNormal1=nRef
+ elif nRef.dot(normalFacetSphere)<0 :
+ O.bodies[i.id2].state.jointNormal1=-nRef
+ elif O.bodies[i.id2].state.onJoint==True : ## particles has already been identified as belonging to, at least, 1 facet
+ if O.bodies[i.id2].state.joint==1 and ((O.bodies[i.id2].state.jointNormal1.cross(nRef)).norm()>0.05) : ## particles has already been identified as belonging to only 1 facet
+ O.bodies[i.id2].state.joint=2
+ O.bodies[i.id2].shape.color=jointcolor2
+ if nRef.dot(normalFacetSphere)>=0 :
+ O.bodies[i.id2].state.jointNormal2=nRef
+ elif nRef.dot(normalFacetSphere)<0 :
+ O.bodies[i.id2].state.jointNormal2=-nRef
+ elif O.bodies[i.id2].state.joint==2 and ((O.bodies[i.id2].state.jointNormal1.cross(nRef)).norm()>0.05) and ((O.bodies[i.id2].state.jointNormal2.cross(nRef)).norm()>0.05): ## particles has already been identified as belonging to more than 1 facet
+ O.bodies[i.id2].state.joint=3
+ O.bodies[i.id2].shape.color=jointcolor3
+ if nRef.dot(normalFacetSphere)>=0 :
+ O.bodies[i.id2].state.jointNormal3=nRef
+ elif nRef.dot(normalFacetSphere)<0 :
+ O.bodies[i.id2].state.jointNormal3=-nRef
+ elif O.bodies[i.id2].state.joint==3 and ((O.bodies[i.id2].state.jointNormal1.cross(nRef)).norm()>0.05) and ((O.bodies[i.id2].state.jointNormal2.cross(nRef)).norm()>0.05) and ((O.bodies[i.id2].state.jointNormal3.cross(nRef)).norm()>0.05):
+ O.bodies[i.id2].state.joint=4
+ O.bodies[i.id2].shape.color=jointcolor5
+
+#### second step -> find spheres interacting with spheres on facet (could be executed in the same timestep as step 1?)
+for j in O.interactions:
+ #if not i.isReal : continue
+ ## Rk: facet are only stored in id1
+ if isinstance(O.bodies[j.id1].shape,Facet) and isinstance(O.bodies[j.id2].shape,Sphere):
+ vertices=O.bodies[j.id1].shape.vertices
+ normalRef=vertices[0].cross(vertices[1]) # defines the normal to the facet normalRef
+ nRef=normalRef/(normalRef.norm()) ## normalizes normalRef
+ if ((O.bodies[j.id2].state.jointNormal1.cross(nRef)).norm()<0.05) :
+ jointNormalRef=O.bodies[j.id2].state.jointNormal1
+ elif ((O.bodies[j.id2].state.jointNormal2.cross(nRef)).norm()<0.05) :
+ jointNormalRef=O.bodies[j.id2].state.jointNormal2
+ elif ((O.bodies[j.id2].state.jointNormal3.cross(nRef)).norm()<0.05) :
+ jointNormalRef=O.bodies[j.id2].state.jointNormal3
+ else : continue
+ facetCenter=O.bodies[j.id1].state.pos
+ #### seek for each sphere interacting with the identified sphere j.id2
+ for n in O.interactions.withBody(j.id2) :
+ if isinstance(O.bodies[n.id1].shape,Sphere) and isinstance(O.bodies[n.id2].shape,Sphere):
+ if j.id2==n.id1: # the sphere that was detected on facet (that is, j.id2) is id1 of interaction n
+ sphOnF=n.id1
+ othSph=n.id2
+ elif j.id2==n.id2: # here, this sphere that was detected on facet (that is, j.id2) is id2 of interaction n
+ sphOnF=n.id2
+ othSph=n.id1
+ facetSphereDir=(O.bodies[othSph].state.pos-facetCenter)
+ if O.bodies[othSph].state.onJoint==True :
+ if O.bodies[othSph].state.joint==3 and ((O.bodies[othSph].state.jointNormal1.cross(jointNormalRef)).norm()>0.05) and ((O.bodies[othSph].state.jointNormal2.cross(jointNormalRef)).norm()>0.05) and ((O.bodies[othSph].state.jointNormal3.cross(jointNormalRef)).norm()>0.05):
+ O.bodies[othSph].state.joint=4
+ O.bodies[othSph].shape.color=jointcolor5
+ elif O.bodies[othSph].state.joint==2 and ((O.bodies[othSph].state.jointNormal1.cross(jointNormalRef)).norm()>0.05) and ((O.bodies[othSph].state.jointNormal2.cross(jointNormalRef)).norm()>0.05):
+ O.bodies[othSph].state.joint=3
+ if facetSphereDir.dot(jointNormalRef)>=0:
+ O.bodies[othSph].state.jointNormal3=jointNormalRef
+ elif facetSphereDir.dot(jointNormalRef)<0:
+ O.bodies[othSph].state.jointNormal3=-jointNormalRef
+ elif O.bodies[othSph].state.joint==1 and ((O.bodies[othSph].state.jointNormal1.cross(jointNormalRef)).norm()>0.05) :
+ O.bodies[othSph].state.joint=2
+ if facetSphereDir.dot(jointNormalRef)>=0:
+ O.bodies[othSph].state.jointNormal2=jointNormalRef
+ elif facetSphereDir.dot(jointNormalRef)<0:
+ O.bodies[othSph].state.jointNormal2=-jointNormalRef
+ elif O.bodies[othSph].state.onJoint==False :
+ O.bodies[othSph].state.onJoint=True
+ O.bodies[othSph].state.joint=1
+ O.bodies[othSph].shape.color=jointcolor4
+ if facetSphereDir.dot(jointNormalRef)>=0:
+ O.bodies[othSph].state.jointNormal1=jointNormalRef
+ elif facetSphereDir.dot(jointNormalRef)<0:
+ O.bodies[othSph].state.jointNormal1=-jointNormalRef
+
+#### for visualization:
+#bj=0
+#vert=(0.,1.,0.)
+#hor=(0.,1.,1.)
+#for o in O.bodies:
+ #if o.state.onJoint==True : # or o.shape.name=='Facet':
+ ##if o.shape.name=='Facet':
+ ##o.shape.wire=True
+ ##o.state.pos+=(0,50,0)
+ ##bj+=1
+ #if o.state.jointNormal1.dot(hor)>0 :
+ ##o.state.pos+=(0,50,0)
+ #o.shape.color=jointcolor1
+ #elif o.state.jointNormal1.dot(hor)<0 :
+ ##o.state.pos+=(0,55,0)
+ #o.shape.color=jointcolor2
+ #if o.mat.type>2 :
+ #bj+=1
+ #o.shape.color=jointcolor5
+ ##print o.state.jointNormal.dot(vert)
+
+
+##### to delete facets (should be OK to start the simulation after that!
+for b in O.bodies:
+ if isinstance(b.shape,Facet):
+ O.bodies.erase(b.id)
+
+O.resetTime()
+O.interactions.clear()
+print '\n IdentificationSpheresOnJoint executed ! Spheres onJoint (and so on...) detected, facets deleted\n\n'
+
+
+
=== added file 'examples/jointedCohesiveFrictionalPM/identificationSpheresOnJoint.py'
--- examples/jointedCohesiveFrictionalPM/identificationSpheresOnJoint.py 1970-01-01 00:00:00 +0000
+++ examples/jointedCohesiveFrictionalPM/identificationSpheresOnJoint.py 2013-11-04 15:56:32 +0000
@@ -0,0 +1,209 @@
+# -*- coding: utf-8 -*-
+
+from yade import pack, utils, ymport, export
+
+packing='parallellepiped_10'
+DFN='persistentPlane30Deg'
+
+############################ material definition
+facetMat = O.materials.append(JCFpmMat(type=0,young=1,frictionAngle=radians(1),poisson=0.4,density=1))
+def sphereMat(): return JCFpmMat(type=1,young=1,frictionAngle=radians(1),density=1,poisson=1,tensileStrength=1e6,cohesion=1e6,jointNormalStiffness=1,jointShearStiffness=1,jointTensileStrength=1e6,jointCohesion=1e6,jointFrictionAngle=1)
+
+############################ Import of the sphere assembly
+O.bodies.append(ymport.text(packing+'.spheres',scale=1,shift=Vector3(0,0,0),material=sphereMat)) #(-3,-4,-8)
+
+#### some preprocessing (not mandatory)
+dim=utils.aabbExtrema()
+xinf=dim[0][0]
+xsup=dim[1][0]
+yinf=dim[0][1]
+ysup=dim[1][1]
+zinf=dim[0][2]
+zsup=dim[1][2]
+
+R=0
+Rmax=0
+numSpheres=0.
+for o in O.bodies:
+ if isinstance(o.shape,Sphere):
+ o.shape.color=(0,0,1)
+ numSpheres+=1
+ R+=o.shape.radius
+ if o.shape.radius>Rmax:
+ Rmax=o.shape.radius
+ else :
+ o.shape.color=(0,0,0)
+Rmean=R/numSpheres
+
+print 'number of spheres=', numSpheres, ' | Rmean=', Rmean, ' | dim=', dim
+
+############################ import stl file
+O.bodies.append(ymport.stl(DFN+'.stl',color=(0.9,0.9,0.9),wire=False,material=facetMat))
+
+############################ engines definition
+interactionRadius=1.;
+O.engines=[
+
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Sphere_Aabb(aabbEnlargeFactor=interactionRadius,label='is2aabb'),Bo1_Facet_Aabb()]),
+ InteractionLoop(
+ [Ig2_Sphere_Sphere_ScGeom(interactionDetectionFactor=interactionRadius,label='ss2d3dg'),Ig2_Facet_Sphere_ScGeom()],
+ [Ip2_JCFpmMat_JCFpmMat_JCFpmPhys(cohesiveTresholdIteration=1,label='interactionPhys')],
+ [Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM(smoothJoint=True,label='interactionLaw')]
+ ),
+ NewtonIntegrator(damping=1)
+
+]
+
+############################ timestep + opening yade windows
+O.dt=0.001*utils.PWaveTimeStep()
+
+from yade import qt
+v=qt.Controller()
+v=qt.View()
+
+############################ Identification spheres on joint
+#### color set for particles on joint
+jointcolor1=(0,1,0)
+jointcolor2=(1,0,0)
+jointcolor3=(0,0,1)
+jointcolor4=(1,1,1)
+jointcolor5=(0,0,0)
+
+#### first step-> find spheres on facet
+O.step();
+
+for i in O.interactions:
+ ##if not i.isReal : continue
+ if isinstance(O.bodies[i.id1].shape,Facet) and isinstance(O.bodies[i.id2].shape,Sphere):
+ vertices=O.bodies[i.id1].shape.vertices
+ normalRef=vertices[0].cross(vertices[1]) # defines the normal to the facet normalRef
+ nRef=normalRef/(normalRef.norm()) ## normalizes normalRef
+ normalFacetSphere=i.geom.normal # geom.normal is oriented from id1 to id2 -> normalFacetSphere from facet (i.id1) to sphere (i.id2)
+
+ if O.bodies[i.id2].state.onJoint==False : ## particles has not yet been identified as belonging to a joint plane
+ O.bodies[i.id2].state.onJoint=True
+ O.bodies[i.id2].state.joint=1
+ O.bodies[i.id2].shape.color=jointcolor1
+ if nRef.dot(normalFacetSphere)>=0 :
+ O.bodies[i.id2].state.jointNormal1=nRef
+ elif nRef.dot(normalFacetSphere)<0 :
+ O.bodies[i.id2].state.jointNormal1=-nRef
+ elif O.bodies[i.id2].state.onJoint==True : ## particles has already been identified as belonging to, at least, 1 facet
+ if O.bodies[i.id2].state.joint==1 and ((O.bodies[i.id2].state.jointNormal1.cross(nRef)).norm()>0.05) : ## particles has already been identified as belonging to only 1 facet
+ O.bodies[i.id2].state.joint=2
+ O.bodies[i.id2].shape.color=jointcolor2
+ if nRef.dot(normalFacetSphere)>=0 :
+ O.bodies[i.id2].state.jointNormal2=nRef
+ elif nRef.dot(normalFacetSphere)<0 :
+ O.bodies[i.id2].state.jointNormal2=-nRef
+ elif O.bodies[i.id2].state.joint==2 and ((O.bodies[i.id2].state.jointNormal1.cross(nRef)).norm()>0.05) and ((O.bodies[i.id2].state.jointNormal2.cross(nRef)).norm()>0.05): ## particles has already been identified as belonging to more than 1 facet
+ O.bodies[i.id2].state.joint=3
+ O.bodies[i.id2].shape.color=jointcolor3
+ if nRef.dot(normalFacetSphere)>=0 :
+ O.bodies[i.id2].state.jointNormal3=nRef
+ elif nRef.dot(normalFacetSphere)<0 :
+ O.bodies[i.id2].state.jointNormal3=-nRef
+ elif O.bodies[i.id2].state.joint==3 and ((O.bodies[i.id2].state.jointNormal1.cross(nRef)).norm()>0.05) and ((O.bodies[i.id2].state.jointNormal2.cross(nRef)).norm()>0.05) and ((O.bodies[i.id2].state.jointNormal3.cross(nRef)).norm()>0.05):
+ O.bodies[i.id2].state.joint=4
+ O.bodies[i.id2].shape.color=jointcolor5
+
+#### second step -> find spheres interacting with spheres on facet (could be executed in the same timestep as step 1?)
+for j in O.interactions:
+ #if not i.isReal : continue
+ if isinstance(O.bodies[j.id1].shape,Facet) and isinstance(O.bodies[j.id2].shape,Sphere):
+ vertices=O.bodies[j.id1].shape.vertices
+ normalRef=vertices[0].cross(vertices[1]) # defines the normal to the facet normalRef
+ nRef=normalRef/(normalRef.norm()) ## normalizes normalRef
+ if ((O.bodies[j.id2].state.jointNormal1.cross(nRef)).norm()<0.05) :
+ jointNormalRef=O.bodies[j.id2].state.jointNormal1
+ elif ((O.bodies[j.id2].state.jointNormal2.cross(nRef)).norm()<0.05) :
+ jointNormalRef=O.bodies[j.id2].state.jointNormal2
+ elif ((O.bodies[j.id2].state.jointNormal3.cross(nRef)).norm()<0.05) :
+ jointNormalRef=O.bodies[j.id2].state.jointNormal3
+ else : continue
+ facetCenter=O.bodies[j.id1].state.pos
+ #### seek for each sphere interacting with the identified sphere j.id2
+ for n in O.interactions.withBody(j.id2) :
+ if n.id1==j.id2 and isinstance(O.bodies[n.id2].shape,Sphere):
+ facetSphereDir=(O.bodies[n.id2].state.pos-facetCenter)
+ if O.bodies[n.id2].state.onJoint==True :
+ if O.bodies[n.id2].state.joint==3 and ((O.bodies[n.id2].state.jointNormal1.cross(jointNormalRef)).norm()>0.05) and ((O.bodies[n.id2].state.jointNormal2.cross(jointNormalRef)).norm()>0.05) and ((O.bodies[n.id2].state.jointNormal3.cross(jointNormalRef)).norm()>0.05):
+ O.bodies[n.id2].state.joint=4
+ O.bodies[n.id2].shape.color=jointcolor5
+ elif O.bodies[n.id2].state.joint==2 and ((O.bodies[n.id2].state.jointNormal1.cross(jointNormalRef)).norm()>0.05) and ((O.bodies[n.id2].state.jointNormal2.cross(jointNormalRef)).norm()>0.05):
+ O.bodies[n.id2].state.joint=3
+ if facetSphereDir.dot(jointNormalRef)>=0:
+ O.bodies[n.id2].state.jointNormal3=jointNormalRef
+ elif facetSphereDir.dot(jointNormalRef)<0:
+ O.bodies[n.id2].state.jointNormal3=-jointNormalRef
+ elif O.bodies[n.id2].state.joint==1 and ((O.bodies[n.id2].state.jointNormal1.cross(jointNormalRef)).norm()>0.05) :
+ O.bodies[n.id2].state.joint=2
+ if facetSphereDir.dot(jointNormalRef)>=0:
+ O.bodies[n.id2].state.jointNormal2=jointNormalRef
+ elif facetSphereDir.dot(jointNormalRef)<0:
+ O.bodies[n.id2].state.jointNormal2=-jointNormalRef
+ elif O.bodies[n.id2].state.onJoint==False :
+ O.bodies[n.id2].state.onJoint=True
+ O.bodies[n.id2].state.joint=1
+ O.bodies[n.id2].shape.color=jointcolor4
+ if facetSphereDir.dot(jointNormalRef)>=0:
+ O.bodies[n.id2].state.jointNormal1=jointNormalRef
+ elif facetSphereDir.dot(jointNormalRef)<0:
+ O.bodies[n.id2].state.jointNormal1=-jointNormalRef
+
+ elif n.id2==j.id2 and isinstance(O.bodies[n.id1].shape,Sphere):
+ facetSphereDir=(O.bodies[n.id1].state.pos-facetCenter)
+ if O.bodies[n.id1].state.onJoint==True :
+ if O.bodies[n.id1].state.joint==3 and ((O.bodies[n.id1].state.jointNormal1.cross(jointNormalRef)).norm()>0.05) and ((O.bodies[n.id1].state.jointNormal2.cross(jointNormalRef)).norm()>0.05) and ((O.bodies[n.id1].state.jointNormal3.cross(jointNormalRef)).norm()>0.05):
+ O.bodies[n.id1].state.joint=4
+ O.bodies[n.id1].shape.color=jointcolor5
+ elif O.bodies[n.id1].state.joint==2 and ((O.bodies[n.id1].state.jointNormal1.cross(jointNormalRef)).norm()>0.05) and ((O.bodies[n.id1].state.jointNormal2.cross(jointNormalRef)).norm()>0.05):
+ O.bodies[n.id1].state.joint=3
+ if facetSphereDir.dot(jointNormalRef)>=0:
+ O.bodies[n.id1].state.jointNormal3=jointNormalRef
+ elif facetSphereDir.dot(jointNormalRef)<0:
+ O.bodies[n.id1].state.jointNormal3=-jointNormalRef
+ elif O.bodies[n.id1].state.joint==1 and ((O.bodies[n.id1].state.jointNormal1.cross(jointNormalRef)).norm()>0.05) :
+ O.bodies[n.id1].state.joint=2
+ if facetSphereDir.dot(jointNormalRef)>=0:
+ O.bodies[n.id1].state.jointNormal2=jointNormalRef
+ elif facetSphereDir.dot(jointNormalRef)<0:
+ O.bodies[n.id1].state.jointNormal2=-jointNormalRef
+ elif O.bodies[n.id1].state.onJoint==False :
+ O.bodies[n.id1].state.onJoint=True
+ O.bodies[n.id1].state.joint=1
+ O.bodies[n.id1].shape.color=jointcolor4
+ if facetSphereDir.dot(jointNormalRef)>=0:
+ O.bodies[n.id1].state.jointNormal1=jointNormalRef
+ elif facetSphereDir.dot(jointNormalRef)<0:
+ O.bodies[n.id1].state.jointNormal1=-jointNormalRef
+
+
+#### for visualization:
+#bj=0
+#vert=(0.,1.,0.)
+#hor=(0.,1.,1.)
+#for o in O.bodies:
+ #if o.state.onJoint==True : # or o.shape.name=='Facet':
+ ##if o.shape.name=='Facet':
+ ##o.shape.wire=True
+ ##o.state.pos+=(0,50,0)
+ ##bj+=1
+ #if o.state.jointNormal1.dot(hor)>0 :
+ ##o.state.pos+=(0,50,0)
+ #o.shape.color=jointcolor1
+ #elif o.state.jointNormal1.dot(hor)<0 :
+ ##o.state.pos+=(0,55,0)
+ #o.shape.color=jointcolor2
+ #if o.mat.type>2 :
+ #bj+=1
+ #o.shape.color=jointcolor5
+ ##print o.state.jointNormal.dot(vert)
+
+
+#### Save text file with informations on each sphere
+export.text(packing+'_'+DFN+'.spheres')
+export.textExt(packing+'_'+DFN+'_jointedPM.spheres',format='jointedPM')
+
+O.wait()
=== added file 'examples/jointedCohesiveFrictionalPM/jointedModel.png'
Binary files examples/jointedCohesiveFrictionalPM/jointedModel.png 1970-01-01 00:00:00 +0000 and examples/jointedCohesiveFrictionalPM/jointedModel.png 2013-08-01 11:54:13 +0000 differ
=== added file 'examples/jointedCohesiveFrictionalPM/packInGtsSurface.py'
--- examples/jointedCohesiveFrictionalPM/packInGtsSurface.py 1970-01-01 00:00:00 +0000
+++ examples/jointedCohesiveFrictionalPM/packInGtsSurface.py 2013-08-09 02:27:56 +0000
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+
+from yade import pack, export, ymport
+import gts, os.path, locale
+
+#### controling parameters
+mesh='parallellepiped' #name of gts mesh
+sizeRatio=10. # defines discretisation (sizeRatio=meshLength/particleDiameter)
+
+#### import mesh
+locale.setlocale(locale.LC_ALL,'en_US.UTF-8') #gts is locale-dependend. If, for example, german locale is used, gts.read()-function does not import floats normally
+surface=gts.read(open(mesh+'.gts'))
+
+print 'closed? ', surface.is_closed()
+
+#### generate packing
+if surface.is_closed():
+ pred=pack.inGtsSurface(surface)
+ # get characteristic dimensions
+ aabb=pred.aabb()
+ dim=pred.dim()
+ center=pred.center()
+ minDim=min(dim[0],dim[1],dim[2])
+ # define discretisation
+ radius=minDim/(2*sizeRatio)
+ print center, dim, ' | minDim=', minDim, ' | diameter=', 2*radius
+ ### regular packing
+ #O.bodies.append(pack.regularHexa(pred,radius=radius,gap=0.,color=(0.9,0.8,0.6)))
+ #O.bodies.append(pack.regularOrtho(pred,radius=radius,gap=0.,color=(0.9,0.8,0.6)))
+ sp=SpherePack()
+ # random packing
+ sp=pack.randomDensePack(pred,radius=radius,rRelFuzz=0.3,useOBB=True,memoizeDb='/tmp/gts-triax-packings.sqlite',returnSpherePack=True) # cropLayers=5 (not to use)
+ # periodic random packing
+ #sp=pack.randomDensePack(pred,radius=radius,rRelFuzz=0.3,useOBB=True,spheresInCell=2000,memoizeDb='/tmp/gts-triax-packings.sqlite',returnSpherePack=True) # cropLayers=5 (not to use)
+ sp.toSimulation(color=(0.9,0.8,0.6))
+
+#### import mesh
+O.bodies.append(pack.gtsSurface2Facets(surface,color=(0.8,0.8,0.8),wire=True))
+
+#### export packing
+export.text(mesh+'_'+str(int(sizeRatio))+'.spheres')
+
+#### VIEW
+from yade import qt
+qt.View()
+
=== added file 'examples/jointedCohesiveFrictionalPM/parallellepiped.gts'
--- examples/jointedCohesiveFrictionalPM/parallellepiped.gts 1970-01-01 00:00:00 +0000
+++ examples/jointedCohesiveFrictionalPM/parallellepiped.gts 2013-08-01 11:54:13 +0000
@@ -0,0 +1,39 @@
+8 18 12 GtsSurface GtsFace GtsEdge GtsVertex
+-0.4999999404 8.344650269e-07 0.5
+-0.4999998212 6.556510925e-07 -0.5
+-0.4999997318 1.999999523 0.5
+0.4999997616 1.999999523 -0.5
+0.5 1.999999046 0.5
+0.4999994338 -1.192092896e-07 0.5
+-0.4999995828 1.999999881 -0.5
+0.4999997616 4.768371582e-07 -0.5
+1 2
+3 1
+3 2
+4 5
+6 5
+6 4
+3 7
+5 3
+7 5
+7 4
+6 8
+4 8
+1 6
+1 5
+8 2
+1 8
+2 7
+2 4
+1 2 3
+4 5 6
+7 8 9
+4 10 9
+11 12 6
+13 5 14
+1 15 16
+8 2 14
+17 10 18
+11 13 16
+12 15 18
+7 17 3
=== added file 'examples/jointedCohesiveFrictionalPM/persistentPlane30Deg.stl'
Binary files examples/jointedCohesiveFrictionalPM/persistentPlane30Deg.stl 1970-01-01 00:00:00 +0000 and examples/jointedCohesiveFrictionalPM/persistentPlane30Deg.stl 2013-08-01 11:54:13 +0000 differ
=== added file 'examples/jointedCohesiveFrictionalPM/testingJoint.py'
--- examples/jointedCohesiveFrictionalPM/testingJoint.py 1970-01-01 00:00:00 +0000
+++ examples/jointedCohesiveFrictionalPM/testingJoint.py 2013-11-04 15:32:10 +0000
@@ -0,0 +1,135 @@
+# encoding: utf-8
+
+
+# Abstract : this script defines a "rock joint" sample : two rectangular blocks separated by an horizontal joint surface. Imposing relative movements of the blocks, that are clumps, allows to test directly the behaviour of the joint, described by JCFpm model.
+# jerome.duriez@xxxxxxxxxxxxxxx
+
+
+# Mechanical properties of rock matrix and rock joint :
+def mat(): return JCFpmMat(type=1,young=15.e9,frictionAngle=radians(35),density=3000,poisson=0.35,tensileStrength=4.5e6,cohesion=45.e6,jointNormalStiffness=5.e7,jointShearStiffness=2.5e7,jointCohesion=0.,jointTensileStrength=0.,jointFrictionAngle=radians(35.),jointDilationAngle=0.0)
+
+
+# --- Creating a sample of spheres
+
+# definition of a predicate
+from yade import pack
+Lx = 10
+Ly = 10
+Lz = 6
+pred = pack.inAlignedBox((0,0,0),(Lx,Ly,Lz))
+# use of randomDensePack() function
+nSpheres = 1500.0
+poros=0.13 # apparently the value of porosity of samples generated by pack.randomDensePack
+rMeanSpheres = pow(Lx*Ly*Lz*3.0/4.0*(1-poros)/(pi*nSpheres),1.0/3.0)
+print '\nGenerating sphere sample, be patient'
+sp = pack.randomDensePack(pred,radius=rMeanSpheres,rRelFuzz=0.3,memoizeDb='/tmp/gts-triax-packings.sqlite',returnSpherePack=True)
+sp.toSimulation(color=(0.9,0.8,0.6),wire=False,material=mat)
+print 'Sphere sample generated !'
+
+
+# --- The joint surface : half of the height
+import gts
+v1 = gts.Vertex(0 , 0 , Lz/2.0)
+v2 = gts.Vertex(Lx, 0 , Lz/2.0)
+v3 = gts.Vertex(Lx, Ly, Lz/2.0)
+v4 = gts.Vertex(0 , Ly, Lz/2.0)
+
+e1 = gts.Edge(v1,v2)
+e2 = gts.Edge(v2,v4)
+e3 = gts.Edge(v4,v1)
+f1 = gts.Face(e1,e2,e3)
+
+e4 = gts.Edge(v4,v3)
+e5 = gts.Edge(v3,v2)
+f2 = gts.Face(e2,e4,e5)
+
+s1 = gts.Surface()
+s1.add(f1)
+s1.add(f2)
+
+facet = gtsSurface2Facets(s1,wire = False,material=mat)
+O.bodies.append(facet)
+
+
+# --- Identification of spheres onJoint, and so on:
+execfile('identifBis.py')
+
+
+# --- Engines definition
+O.engines=[
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Sphere_Aabb()]),
+ InteractionLoop(
+ [Ig2_Sphere_Sphere_ScGeom()],
+ [Ip2_JCFpmMat_JCFpmMat_JCFpmPhys(cohesiveTresholdIteration=1)],
+ [Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM(smoothJoint=True)]),
+ GlobalStiffnessTimeStepper(timestepSafetyCoefficient=0.8),
+ NewtonIntegrator(damping=0.2),
+ PyRunner(command='afficheIt()',initRun=True,iterPeriod=1000),
+]
+def afficheIt():
+ print 'It', O.iter
+
+O.step()
+
+# --- Clumping the blocks
+upperBlock=[]
+lowerBlock=[]
+for inte in O.interactions:
+ if not inte.phys.isOnJoint:
+ bod1 = O.bodies[inte.id1]
+ bod2 = O.bodies[inte.id2]
+ if bod1.state.pos[2]<Lz/2.0:
+ if not (bod1.id in lowerBlock):
+ lowerBlock.append(bod1.id)
+ bod1.shape.color=Vector3(1,0,0)
+ if bod2.state.pos[2]>Lz/2.0:
+ print '\n **** ERROR !!!! ******* \n\n'
+ else:
+ if not (bod2.id in lowerBlock):
+ lowerBlock.append(bod2.id)
+ bod2.shape.color=Vector3(1,0,0)
+ else:
+ if not (bod1.id in upperBlock):
+ upperBlock.append(bod1.id)
+ bod1.shape.color=Vector3(0,0,1)
+ if bod2.state.pos[2]<Lz/2.0:
+ print '\n **** ERROR !!!! ******* \n\n'
+ else:
+ if not (bod2.id in upperBlock):
+ upperBlock.append(bod2.id)
+ bod2.shape.color=Vector3(0,0,1)
+print '\n Clumping upper block, be patient'
+idUpperClump=O.bodies.clump(upperBlock)
+print 'Clumped !'
+
+print '\n Clumping lower block, be patient'
+idLowerClump=O.bodies.clump(lowerBlock)
+print 'Clumped !'
+
+upperClump = O.bodies[idUpperClump]
+lowerClump = O.bodies[idLowerClump]
+z0 = upperClump.state.pos[2]
+upperClump.dynamic=False
+lowerClump.dynamic=False
+
+
+# --- Saving data
+O.engines =O.engines+[PyRunner(command='dataCollector()',initRun=True,iterPeriod=500)]
+def dataCollector():
+ plot.addData(iterations=O.iter,un=-(upperClump.state.pos[2]-z0),Fz=O.forces.f(idUpperClump,sync=True)[2],Ft=sqrt(pow(O.forces.f(idUpperClump,sync=True)[1],2.)+pow(O.forces.f(idUpperClump,sync=True)[0],2.)),Fzinf=O.forces.f(idLowerClump,sync=True)[2],uf=unbalancedForce(),ec=kineticEnergy())
+
+from yade import plot
+plot.plots={'un':('Fz')}
+
+# --- Simulation !
+compSpeed = 0.0075 # put here a positive value, for compression
+upperClump.state.vel=Vector3(0,0,-compSpeed)
+nIt = 10000
+print '\nComputation begins now, for', nIt,'iterations'
+O.run(nIt,wait=True)
+print 'Computation just finished ! \n'
+
+yade.qt.View()
+yade.qt.Controller()
+plot.plot()
=== modified file 'examples/packs/packs.py'
--- examples/packs/packs.py 2013-03-28 16:15:54 +0000
+++ examples/packs/packs.py 2013-10-04 11:58:44 +0000
@@ -43,7 +43,7 @@
pack.regularHexa (pack.inCylinder((+1,1,-2.5),(0,3,-5),1),radius=rad,gap=gap,color=(0,1,1),**kw), # right leg
pack.regularHexa (pack.inHyperboloid((+2,0,1),(+6,0,0),1,.5),radius=rad,gap=gap,color=(0,0,1),**kw), # right hand
pack.regularOrtho(pack.inCylinder((-2,0,2),(-5,0,4),1),radius=rad,gap=gap,color=(0,0,1),**kw) # left hand
- ]: O.bodies.appendClumped(part)
+ ]: O.bodies.appendClumped(part,integrateInertia=False)
# Example of geom.facetBox usage
=== modified file 'examples/periodicSandPile.py'
--- examples/periodicSandPile.py 2013-03-28 10:53:06 +0000
+++ examples/periodicSandPile.py 2013-08-01 05:04:49 +0000
@@ -4,7 +4,7 @@
"""Script showing how large bodies can be combined with periodic boundary conditions using InsertioSortCollider::allowBiggerThanPeriod=True (1)."""
from yade import pack
-from pylab import *
+from pylab import rand
from yade import qt
O.periodic=True
=== added directory 'examples/polyhedra'
=== added file 'examples/polyhedra/ball.py'
--- examples/polyhedra/ball.py 1970-01-01 00:00:00 +0000
+++ examples/polyhedra/ball.py 2013-10-16 15:24:49 +0000
@@ -0,0 +1,47 @@
+from yade import plot, polyhedra_utils
+from yade import qt
+
+m = PolyhedraMat()
+m.density = 2600 #kg/m^3
+m.Ks = 20000
+m.Kn = 1E6 #Pa
+m.frictionAngle = 0.6 #rad
+
+maxLoad = 3E6
+minLoad = 1E3
+
+O.bodies.append(utils.wall(0,axis=2,sense=1, material = m))
+
+t = polyhedra_utils.polyhedralBall(0.025, 100, m, (0,0,0))
+t.state.pos = (0,0,0.5)
+O.bodies.append(t)
+
+def checkUnbalanced():
+ print "unbalanced forces = %.5f, position %f, %f, %f"%(utils.unbalancedForce(), t.state.pos[0], t.state.pos[1], t.state.pos[2])
+
+
+
+O.engines=[
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Polyhedra_Aabb(),Bo1_Wall_Aabb(),Bo1_Facet_Aabb()]),
+ InteractionLoop(
+ [Ig2_Wall_Polyhedra_PolyhedraGeom(), Ig2_Polyhedra_Polyhedra_PolyhedraGeom(), Ig2_Facet_Polyhedra_PolyhedraGeom()],
+ [Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys()], # collision "physics"
+ [PolyhedraVolumetricLaw()] # contact law -- apply forces
+ ),
+ #GravityEngine(gravity=(0,0,-9.81)),
+ NewtonIntegrator(damping=0.5,gravity=(0,0,-9.81)),
+ PyRunner(command='checkUnbalanced()',realPeriod=3,label='checker')
+]
+
+
+#O.dt=0.025*polyhedra_utils.PWaveTimeStep()
+O.dt=0.00025
+
+
+qt.Controller()
+V = qt.View()
+
+
+O.saveTmp()
+#O.run()
=== added file 'examples/polyhedra/free-fall.py'
--- examples/polyhedra/free-fall.py 1970-01-01 00:00:00 +0000
+++ examples/polyhedra/free-fall.py 2013-10-16 15:24:49 +0000
@@ -0,0 +1,58 @@
+from yade import plot, polyhedra_utils
+
+
+gravel = PolyhedraMat()
+gravel.density = 2600 #kg/m^3
+gravel.Ks = 20000
+gravel.Kn = 1E7 #Pa
+gravel.frictionAngle = 0.5 #rad
+
+steel = PolyhedraMat()
+steel.density = 7850 #kg/m^3
+steel.Ks = 10*gravel.Ks
+steel.Kn = 10*gravel.Kn
+steel.frictionAngle = 0.4 #rad
+
+rubber = PolyhedraMat()
+rubber.density = 1000 #kg/m^3
+rubber.Ks = gravel.Ks/10
+rubber.Kn = gravel.Kn/10
+rubber.frictionAngle = 0.7 #rad
+
+O.bodies.append(polyhedra_utils.polyhedra(gravel,v=((0,0,-0.05),(0.3,0,-0.05),(0.3,0.3,-0.05),(0,0.3,-0.05),(0,0,0),(0.3,0,0),(0.3,0.3,0),(0,0.3,0)),fixed=True, color=(0.35,0.35,0.35)))
+#O.bodies.append(utils.wall(0,axis=1,sense=1, material = gravel))
+#O.bodies.append(utils.wall(0,axis=0,sense=1, material = gravel))
+#O.bodies.append(utils.wall(0.3,axis=1,sense=-1, material = gravel))
+#O.bodies.append(utils.wall(0.3,axis=0,sense=-1, material = gravel))
+
+polyhedra_utils.fillBox((0,0,0), (0.3,0.3,0.3),gravel,sizemin=[0.025,0.025,0.025],sizemax=[0.05,0.05,0.05],seed=4)
+
+def checkUnbalancedI():
+ print "iter %d, time elapsed %f, time step %.5e, unbalanced forces = %.5f"%(O.iter, O.realtime, O.dt, utils.unbalancedForce())
+
+O.engines=[
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Polyhedra_Aabb(),Bo1_Wall_Aabb(),Bo1_Facet_Aabb()]),
+ InteractionLoop(
+ [Ig2_Wall_Polyhedra_PolyhedraGeom(), Ig2_Polyhedra_Polyhedra_PolyhedraGeom(), Ig2_Facet_Polyhedra_PolyhedraGeom()],
+ [Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys()], # collision "physics"
+ [PolyhedraVolumetricLaw()] # contact law -- apply forces
+ ),
+ #GravityEngine(gravity=(0,0,-9.81)),
+ NewtonIntegrator(damping=0.3,gravity=(0,0,-9.81)),
+ PyRunner(command='checkUnbalancedI()',realPeriod=5,label='checker')
+]
+
+
+#O.dt=0.25*polyhedra_utils.PWaveTimeStep()
+O.dt=0.0025*polyhedra_utils.PWaveTimeStep()
+
+
+from yade import qt
+qt.Controller()
+V = qt.View()
+V.screenSize = (550,450)
+V.sceneRadius = 1
+V.eyePosition = (0.7,0.5,0.1)
+V.upVector = (0,0,1)
+V.lookAt = (0.15,0.15,0.1)
=== added file 'examples/polyhedra/irregular.py'
--- examples/polyhedra/irregular.py 1970-01-01 00:00:00 +0000
+++ examples/polyhedra/irregular.py 2013-10-16 15:24:49 +0000
@@ -0,0 +1,63 @@
+# gravity deposition, continuing with oedometric test after stabilization
+# shows also how to run parametric studies with yade-batch
+
+# The components of the batch are:
+# 1. table with parameters, one set of parameters per line (ccc.table)
+# 2. utils.readParamsFromTable which reads respective line from the parameter file
+# 3. the simulation muse be run using yade-batch, not yade
+#
+# $ yade-batch --job-threads=1 03-oedometric-test.table 03-oedometric-test.py
+#
+
+
+# create box with free top, and ceate loose packing inside the box
+from yade import plot, polyhedra_utils
+from yade import qt
+
+m = PolyhedraMat()
+m.density = 2600 #kg/m^3
+m.Ks = 20000
+m.Kn = 1E6 #Pa
+m.frictionAngle = 0.6 #rad
+
+O.bodies.append(utils.wall(0,axis=2,sense=1, material = m))
+
+t = polyhedra_utils.polyhedra(m,size = (0.06,0.06,0.06),seed = 5)
+t.state.pos = (0.,0.,0.5)
+O.bodies.append(t)
+
+def checkUnbalanced():
+ # at the very start, unbalanced force can be low as there is only few contacts, but it does not mean the packing is stable
+ print "unbalanced forces = %.5f, position %f, %f, %f"%(utils.unbalancedForce(), t.state.pos[0], t.state.pos[1], t.state.pos[2])
+
+
+
+O.engines=[
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Polyhedra_Aabb(),Bo1_Wall_Aabb(),Bo1_Facet_Aabb()]),
+ InteractionLoop(
+ [Ig2_Wall_Polyhedra_PolyhedraGeom(), Ig2_Polyhedra_Polyhedra_PolyhedraGeom(), Ig2_Facet_Polyhedra_PolyhedraGeom()],
+ [Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys()], # collision "physics"
+ [PolyhedraVolumetricLaw()] # contact law -- apply forces
+ ),
+ #GravityEngine(gravity=(0,0,-9.81)),
+ NewtonIntegrator(damping=0.5,gravity=(0,0,-9.81)),
+ PyRunner(command='checkUnbalanced()',realPeriod=3,label='checker')
+
+]
+
+
+#O.dt=0.025*polyhedra_utils.PWaveTimeStep()
+O.dt=0.00025
+
+
+
+
+qt.Controller()
+V = qt.View()
+
+
+O.saveTmp()
+#O.run()
+#O.save('./done')
+utils.waitIfBatch()
=== added file 'examples/polyhedra/wall.py'
--- examples/polyhedra/wall.py 1970-01-01 00:00:00 +0000
+++ examples/polyhedra/wall.py 2013-10-16 15:24:49 +0000
@@ -0,0 +1,72 @@
+from yade import qt
+import numpy as np
+import random
+from yade import polyhedra_utils
+
+m = PolyhedraMat()
+m.density = 1000
+m.Ks = 5E6
+m.Kn = 5E8
+m.frictionAngle = 0.7
+
+size = 0.1;
+vertices = [[0,0,0],[size,0,0],[size,size,0],[size,size,size],[0,size,0],[0,size,size],[0,0,size],[size,0,size]]
+
+for i in range(0,10):
+ for j in range(0,10):
+ t = polyhedra_utils.polyhedra(m,v=vertices)
+ t.state.pos = (0,(i+0.5)*size-5*size,(j+0.5)*size*1)
+ O.bodies.append(t)
+
+
+
+qt.Controller()
+V = qt.View()
+V.screenSize = (750,550)
+V.sceneRadius = 1
+V.eyePosition = (-1.5,1.5,0.5)
+V.lookAt = (0,-0.5,0)
+V.upVector = (0,0,1)
+
+
+O.bodies.append(utils.wall(0,axis=2,sense=1,material=m))
+
+def checkUnbalanced():
+ print ('iter %d, time elapsed %f, unbalanced forces = %f'%(O.iter, O.realtime, utils.unbalancedForce()))
+ if O.iter<200: return
+ if utils.unbalancedForce()>0.01: return
+ AddBall()
+
+def AddBall():
+ ball = polyhedra_utils.polyhedralBall(size*1, 40, m, (-size*2.5,0,size*7),mask=1)
+ ball.shape.color = (0,0,0.9)
+ ball.state.vel = (15,0,0)
+ O.bodies.append(ball)
+ checker.dead = True
+
+O.engines=[
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Sphere_Aabb(),Bo1_Polyhedra_Aabb(),Bo1_Wall_Aabb()]),
+ InteractionLoop(
+ [Ig2_Wall_Polyhedra_PolyhedraGeom(), Ig2_Polyhedra_Polyhedra_PolyhedraGeom(), Ig2_Facet_Polyhedra_PolyhedraGeom()],
+ [Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys()], # collision "physics"
+ [PolyhedraVolumetricLaw()] # contact law -- apply forces
+ ),
+ #GravityEngine(gravity=(0,0,-9.81)),
+ NewtonIntegrator(damping=0.5,gravity=(0,0,-9.81)),
+ PyRunner(command='checkUnbalanced()',realPeriod=3,label='checker')#,
+ # wideo_recording
+ #qt.SnapshotEngine(fileBase='W',iterPeriod=50,label='snapshot')
+]
+
+#O.dt=.5*polyhedra_utils.PWaveTimeStep()
+O.dt = 0.000025
+O.saveTmp()
+
+#O.run()
+
+
+#comment ball to see stability
+
+
+
=== modified file 'examples/simple-scene/simple-scene-plot.py' (properties changed: +x to -x)
=== modified file 'examples/simple-shear/simpleShear.py'
--- examples/simple-shear/simpleShear.py 2013-03-28 11:09:16 +0000
+++ examples/simple-shear/simpleShear.py 2013-08-09 02:27:56 +0000
@@ -15,7 +15,7 @@
O=Omega()
#Def of the material which will be used
-O.materials.append(NormalInelasticMat(density=2600,young=4.0e9,poisson=.04,frictionAngle=.6,coeff_dech=3.0,label='Materiau1'))
+O.materials.append(NormalInelasticMat(density=2600,young=4.0e9,poisson=.04,frictionAngle=.6,coeff_dech=3.0,label='Material1'))
#Def of dimensions of the box
#length=5
@@ -47,14 +47,13 @@
O.bodies.append([leftBox,lowBox,rightBox,upBox,behindBox,inFrontBox])
+
+sp=SpherePack()
#memoizeDb='/tmp/simpleshear-triax-packings.sqlite'
-#ListSph=randomDensePack(pred,radius=0.002,rRelFuzz=0.15,memoDbg=True,memoizeDb=memoizeDb)
-#ListSph=randomDensePack(pred,radius=0.002,rRelFuzz=0.15,memoDbg=True,memoizeDb=memoizeDb,spheresInCell=100)
-#O.bodies.append(ListSph)
-
-sp=yade._packSpheres.SpherePack()
+#sp=randomDensePack(pred,radius=0.002,rRelFuzz=0.15,memoDbg=True,memoizeDb=memoizeDb,returnSpherePack=True)
+#sp=randomDensePack(pred,radius=0.002,rRelFuzz=0.15,memoDbg=True,memoizeDb=memoizeDb,spheresInCell=100,returnSpherePack=True)
sp.makeCloud(Vector3(0,0.0,-width/2.0),Vector3(length,height,width/2.0),rMean,.15)
-O.bodies.append([sphere(s[0],s[1]) for s in sp])
+sp.toSimulation()
#---- Def of the engines ----#
=== added file 'examples/spheresFactory.py'
--- examples/spheresFactory.py 1970-01-01 00:00:00 +0000
+++ examples/spheresFactory.py 2013-08-05 05:32:22 +0000
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+"""Simple script which shows how to create an inlet.
+"""
+
+shotsId,steelId=O.materials.append([
+ FrictMat(young=50e9,density=6000,poisson=.2,label='shots'),
+ FrictMat(young=210e9,density=7800, poisson=.3,label='steel'),
+])
+## same as
+#
+# shotsId,steelId=O.materials.index('shots'),O.materials.index('steel')
+#
+
+O.bodies.append(geom.facetBox(center=(0,0,0),extents=(30e-3,30e-3,0),wallMask=32,wire=False,material='steel',color=(0,1,.3)))
+
+O.engines=[
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Sphere_Aabb(),Bo1_Facet_Aabb()],verletDist=.05*.29e-3),
+ InteractionLoop(
+ [Ig2_Sphere_Sphere_ScGeom(),Ig2_Facet_Sphere_ScGeom()],
+ [Ip2_FrictMat_FrictMat_MindlinPhys(
+ # define restitution coefficients between different pairs of material ids, see the functor's documentation for details
+ #en=MatchMaker(fallback='zero',matches=((steelId,shotsId,.4),(shotsId,shotsId,1)))
+ en=MatchMaker(matches=((steelId,shotsId,.4),(shotsId,shotsId,1)))
+ )],
+ [Law2_ScGeom_MindlinPhys_Mindlin(label='contactLaw')]
+ ),
+ NewtonIntegrator(damping=0),
+ ## CircularFactory: disk if length=0 or cylinder if length>0
+ #CircularFactory(maxParticles=10000,radius=8e-3,length=16e-3,center=(0,-15e-3,15e-3),rMin=0.28e-3,rMax=0.29e-3,vMin=100,vMax=100,vAngle=0,massFlowRate=100./60,normal=(0,1.5,-1),label='factory',materialId=shotsId),
+ ## BoxFactory: a line, plane or cuboid
+ BoxFactory(maxParticles=10000,extents=(8e-3,8e-3,8e-3),center=(0,-15e-3,15e-3),rMin=0.28e-3,rMax=0.29e-3,vMin=100,vMax=100,vAngle=0,massFlowRate=100./60,normal=(0,1.5,-1),label='factory',materialId=shotsId),
+ DomainLimiter(lo=(-30e-3,-30e-3,0),hi=(30e-3,30e-3,60e-3),iterPeriod=200),
+ #VTKRecorder(recorders=['spheres','facets','velocity'],fileName='/tmp/nozzle-',iterPeriod=500),
+
+ # run this every once in a while, to finalize the simulation at some point
+ PyRunner(iterPeriod=10000,command='if factory.numParticles>=factory.maxParticles: O.stopAtIter=O.iter+8000; timing.stats()')
+]
+# the timestep must be smaller because of high linear velocities of particles
+# we cannot use PWaveTimeStep directly, since there are no spheres generated yet
+O.dt=SpherePWaveTimeStep(factory.rMin,O.materials[factory.materialId].density,O.materials[factory.materialId].young)
+O.saveTmp()
+#O.timingEnabled=True
+from yade import timing
+try:
+ from yade import qt
+ # setup 3d view
+ v=qt.View()
+ v.upVector=(0,0,1); v.viewDir=(-1,0,-.3); v.center(median=False)
+except ImportError: pass
+O.run()
=== modified file 'examples/tesselationwrapper/tesselationWrapper.py'
--- examples/tesselationwrapper/tesselationWrapper.py 2012-10-05 14:33:55 +0000
+++ examples/tesselationwrapper/tesselationWrapper.py 2013-08-19 17:51:24 +0000
@@ -2,6 +2,8 @@
# -*- coding: utf-8 -*-
#2012 Bruno Chareyre <bruno.chareyre@xxxxxxxxxxx>
"""Example usage of a TesselationWrapper object for getting microscale quantities."""
+# See Catalano2013a for the definition of micro-strain
+# (http://dx.doi.org/10.1002/nag.2198 or free-access at arxiv http://arxiv.org/pdf/1304.4895.pdf)
tt=TriaxialTest()
tt.generate("test.yade")
=== removed file 'examples/test/CundallStrackTest.py'
--- examples/test/CundallStrackTest.py 2013-03-28 11:13:12 +0000
+++ examples/test/CundallStrackTest.py 1970-01-01 00:00:00 +0000
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-
-
-O.engines=[
- ForceResetter(),
- InsertionSortCollider([Bo1_Sphere_Aabb(),]),
- IGeomDispatcher([Ig2_Sphere_Sphere_Dem3DofGeom()]),
- IPhysDispatcher([Ip2_2xFrictMat_CSPhys()]),
- LawDispatcher([Law2_Dem3Dof_CSPhys_CundallStrack()]),
- NewtonIntegrator(damping = 0.01,gravity=[0,0,-9.81])
-]
-
-
-
-O.bodies.append(sphere([0,0,6],1,fixed=False, color=[0,1,0]))
-O.bodies.append(sphere([0,0,0],1,fixed=True, color=[0,0,1]))
-O.dt=.2*PWaveTimeStep()
-
-from yade import qt
-qt.Controller()
-qt.View()
=== removed file 'examples/test/Dem3DofGeom.py'
--- examples/test/Dem3DofGeom.py 2013-03-28 16:15:54 +0000
+++ examples/test/Dem3DofGeom.py 1970-01-01 00:00:00 +0000
@@ -1,40 +0,0 @@
-"Script showing shear interaction between facet/wall and sphere."
-O.bodies.append([
- #sphere([0,0,0],1,dynamic=False,color=(0,1,0),wire=True),
- facet(([2,2,1],[-2,0,1],[2,-2,1]),fixed=True,color=(0,1,0),wire=False),
- #wall([0,0,1],axis=2,color=(0,1,0)),
- sphere([-1,0,2],1,fixed=False,color=(1,0,0),wire=True),
-])
-O.engines=[
- ForceResetter(),
- InsertionSortCollider([
- Bo1_Sphere_Aabb(),Bo1_Facet_Aabb(),Bo1_Wall_Aabb()
- ]),
- IGeomDispatcher([
- Ig2_Sphere_Sphere_Dem3DofGeom(),
- Ig2_Facet_Sphere_Dem3DofGeom(),
- Ig2_Wall_Sphere_Dem3DofGeom()
- ]),
- RotationEngine(rotationAxis=[0,1,0],angularVelocity=10,ids=[1]),
- TranslationEngine(translationAxis=[1,0,0],velocity=10,ids=[1]),
- NewtonIntegrator()#gravity=(0,0,-10))
-]
-O.miscParams=[
- Gl1_Dem3DofGeom_SphereSphere(normal=True,rolledPoints=True,unrolledPoints=True,shear=True,shearLabel=True),
- Gl1_Dem3DofGeom_FacetSphere(normal=False,rolledPoints=True,unrolledPoints=True,shear=True,shearLabel=True),
- Gl1_Dem3DofGeom_WallSphere(normal=False,rolledPoints=True,unrolledPoints=True,shear=True,shearLabel=True),
- Gl1_Sphere(wire=True)
-]
-
-try:
- from yade import qt
- renderer=qt.Renderer()
- renderer.wire=True
- renderer.intrGeom=True
- qt.Controller()
- qt.View()
-except ImportError: pass
-
-O.dt=1e-6
-O.saveTmp('init')
-O.run(1)
=== removed file 'examples/test/spheresFactory.py'
--- examples/test/spheresFactory.py 2013-03-28 11:13:12 +0000
+++ examples/test/spheresFactory.py 1970-01-01 00:00:00 +0000
@@ -1,50 +0,0 @@
-
-from yade import geom,log
-
-shotsId,steelId=O.materials.append([
- FrictMat(young=50e9,density=6000,poisson=.2,label='shots'),
- FrictMat(young=210e9,density=7800, poisson=.3,label='steel'),
-])
-## same as
-#
-# shotsId,steelId=O.materials.index('shots'),O.materials.index('steel')
-#
-
-O.bodies.append(geom.facetBox(center=(0,0,0),extents=(30e-3,30e-3,0),wallMask=32,wire=False,material='steel',color=(0,1,.3)))
-
-O.engines=[
- ForceResetter(),
- InsertionSortCollider([Bo1_Sphere_Aabb(),Bo1_Facet_Aabb()],verletDist=.05*.29e-3),
- InteractionLoop(
- [Ig2_Sphere_Sphere_ScGeom(),Ig2_Facet_Sphere_ScGeom()],
- [Ip2_FrictMat_FrictMat_MindlinPhys(
- # define restitution coefficients between different pairs of material ids, see the functor's documentation for details
- #en=MatchMaker(fallback='zero',matches=((steelId,shotsId,.4),(shotsId,shotsId,1)))
- en=MatchMaker(matches=((steelId,shotsId,.4),(shotsId,shotsId,1)))
- )],
- [Law2_ScGeom_MindlinPhys_Mindlin(label='contactLaw')]
- ),
- NewtonIntegrator(damping=0),
- ## CircularFactory: disk if length=0 or cylinder if length>0
- #CircularFactory(maxParticles=10000,radius=8e-3,length=16e-3,center=(0,-15e-3,15e-3),rMin=0.28e-3,rMax=0.29e-3,vMin=100,vMax=100,vAngle=0,massFlowRate=100./60,normal=(0,1.5,-1),label='factory',materialId=shotsId),
- ## BoxFactory: a line, plane or cuboid
- BoxFactory(maxParticles=10000,extents=(8e-3,8e-3,8e-3),center=(0,-15e-3,15e-3),rMin=0.28e-3,rMax=0.29e-3,vMin=100,vMax=100,vAngle=0,massFlowRate=100./60,normal=(0,1.5,-1),label='factory',materialId=shotsId),
- DomainLimiter(lo=(-30e-3,-30e-3,0),hi=(30e-3,30e-3,60e-3),iterPeriod=200),
- #VTKRecorder(recorders=['spheres','facets','velocity'],fileName='/tmp/nozzle-',iterPeriod=500),
-
- # run this every once in a while, to finalize the simulation at some point
- PyRunner(iterPeriod=10000,command='if factory.numParticles>=factory.maxParticles: O.stopAtIter=O.iter+8000; timing.stats()')
-]
-# the timestep must be smaller because of high linear velocities of particles
-# we cannot use PWaveTimeStep directly, since there are no spheres generated yet
-O.dt=SpherePWaveTimeStep(factory.rMin,O.materials[factory.materialId].density,O.materials[factory.materialId].young)
-O.saveTmp()
-#O.timingEnabled=True
-from yade import timing
-try:
- from yade import qt
- # setup 3d view
- v=qt.View()
- v.upVector=(0,0,1); v.viewDir=(-1,0,-.3); v.center(median=False)
-except ImportError: pass
-O.run()
=== added file 'examples/test/unstructuredGrid.py'
--- examples/test/unstructuredGrid.py 1970-01-01 00:00:00 +0000
+++ examples/test/unstructuredGrid.py 2013-10-04 17:41:37 +0000
@@ -0,0 +1,38 @@
+################################################################################
+#
+# TODO
+#
+################################################################################
+
+O.engines = [
+ InsertionSortCollider([Bo1_Tetra_Aabb(),Bo1_Facet_Aabb()]),
+]
+
+ug = utils.UnstructuredGrid()
+v = {
+ 1: (0,0,0),
+ 3: (1,0,0),
+ 5: (0,1,0),
+ 6: (0,0,1),
+ 2: (-1,0,0),
+}
+ct = {
+ 1: (1,3,5),
+ 4: (1,3,6),
+ 8: (1,5,2,6),
+}
+ug.setup(v,ct,wire=False)
+ug.toSimulation()
+O.step()
+
+from yade import qt
+qt.View()
+
+def setNewCoords():
+ ug.setPositionsOfNodes({
+ 1: (-1,-1,-1),
+ 3: (2,0,.5),
+ 5: (0,3,0),
+ 6: (0,0,.5),
+ 2: (-1,0,1)
+ })
=== added file 'examples/test/vtkPeriodicCell.py'
--- examples/test/vtkPeriodicCell.py 1970-01-01 00:00:00 +0000
+++ examples/test/vtkPeriodicCell.py 2013-10-04 15:25:30 +0000
@@ -0,0 +1,23 @@
+######################################################################
+# Simple script to test VTK export of periodic cell
+######################################################################
+# enable periodic cell
+O.periodic=True
+# insert some bodies
+sp = randomPeriPack(radius=1,initSize=(10,20,30),memoizeDb='/tmp/vtkPeriodicCell.sqlite')
+sp.toSimulation()
+# transform the cell a bit
+O.cell.hSize *= Matrix3(1,.1,.1, .1,1,0, .1,0,1) # skew the cell in xy and xz plane
+O.cell.hSize *= Matrix3(1,0,0, 0,.8,.6, 0,-.6,.8) # rotate it along x axis
+
+O.step()
+
+# test of export.VTKExporter
+from yade import export
+vtk1 = export.VTKExporter('/tmp/vtkPeriodicCell-VTKExporter')
+vtk1.exportSpheres()
+vtk1.exportPeriodicCell()
+
+# test of VTKReorder
+vtk2 = VTKRecorder(fileName='/tmp/vtkPeriodicCell-VTKRecorder-',recorders=['spheres','pericell'])
+vtk2() # do the export
=== added directory 'examples/tetra'
=== added file 'examples/tetra/oneTetra.py'
--- examples/tetra/oneTetra.py 1970-01-01 00:00:00 +0000
+++ examples/tetra/oneTetra.py 2013-10-15 17:11:37 +0000
@@ -0,0 +1,43 @@
+################################################################################
+#
+# Script to test tetra gl functions and prescribed motion
+#
+################################################################################
+v1 = (0,0,0)
+v2 = (1,0,0)
+v3 = (0,1,0)
+v4 = (0,0,1)
+
+t1 = tetraOld((v1,v2,v3,v4),color=(0,1,0))
+O.bodies.append((t1))
+
+def changeVelocity():
+ if O.iter == 50000:
+ t1.state.vel = (-1,0,0)
+ if O.iter == 100000:
+ t1.state.vel = (0,1,-1)
+ if O.iter == 150000:
+ t1.state.vel = (0,0,0)
+ t1.state.angMom = (0,0,10)
+ if O.iter == 200000:
+ O.pause()
+
+O.engines = [
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Tetra_Aabb()]),
+ InteractionLoop([],[],[]),
+ NewtonIntegrator(),
+ PyRunner(iterPeriod=1,command="changeVelocity()"),
+]
+O.step()
+
+
+try:
+ from yade import qt
+ qt.View()
+except:
+ pass
+
+O.dt = 1e-5
+t1.state.vel = (1,0,0)
+O.run()
=== added file 'examples/tetra/oneTetraPoly.py'
--- examples/tetra/oneTetraPoly.py 1970-01-01 00:00:00 +0000
+++ examples/tetra/oneTetraPoly.py 2013-10-15 17:11:37 +0000
@@ -0,0 +1,43 @@
+################################################################################
+#
+# Script to test tetra gl functions and prescribed motion
+#
+################################################################################
+v1 = (0,0,0)
+v2 = (1,0,0)
+v3 = (0,1,0)
+v4 = (0,0,1)
+
+t1 = tetraPoly((v1,v2,v3,v4),color=(0,1,0))
+O.bodies.append((t1))
+
+def changeVelocity():
+ if O.iter == 50000:
+ t1.state.vel = (-1,0,0)
+ if O.iter == 100000:
+ t1.state.vel = (0,1,-1)
+ if O.iter == 150000:
+ t1.state.vel = (0,0,0)
+ t1.state.angMom = (0,0,10)
+ if O.iter == 200000:
+ O.pause()
+
+O.engines = [
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Polyhedra_Aabb()]),
+ InteractionLoop([],[],[]),
+ NewtonIntegrator(),
+ PyRunner(iterPeriod=1,command="changeVelocity()"),
+]
+O.step()
+
+
+try:
+ from yade import qt
+ qt.View()
+except:
+ pass
+
+O.dt = 1e-5
+t1.state.vel = (1,0,0)
+O.run()
=== added file 'examples/tetra/twoTetras.py'
--- examples/tetra/twoTetras.py 1970-01-01 00:00:00 +0000
+++ examples/tetra/twoTetras.py 2013-10-04 12:03:13 +0000
@@ -0,0 +1,110 @@
+################################################################################
+#
+# Python script to test tetra-tetra contact detection for different possible
+# contact modes. Some tests are run twice to test the symmetry of the law.
+#
+# It runs several tests, making pause before each one. If run with GUI, you can
+# adjust the viewer
+#
+# During the test, momentum, angular momentum and kinetic energy is tracked in
+# plot.data. You can use plot.plot() to see the results (in sime modes the
+# energy is not conserved for instace).
+#
+################################################################################
+from yade import qt,plot
+
+O.materials.append(ElastMat(young=1e10))
+O.dt = 4e-5
+O.engines = [
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Tetra_Aabb()]),
+ InteractionLoop(
+ [Ig2_Tetra_Tetra_TTetraSimpleGeom()],
+ [Ip2_ElastMat_ElastMat_NormPhys()],
+ [Law2_TTetraSimpleGeom_NormPhys_Simple()]
+ ),
+ NewtonIntegrator(damping=0),
+ PyRunner(iterPeriod=500,command="addPlotData()"),
+ PyRunner(iterPeriod=1,command="runExamples()"),
+]
+
+def addPlotData():
+ p = utils.momentum()
+ l = utils.angularMomentum()
+ plot.addData(
+ i = O.iter,
+ e = utils.kineticEnergy(),
+ px = p[0],
+ py = p[1],
+ pz = p[2],
+ lx = l[0],
+ ly = l[1],
+ lz = l[2],
+ )
+
+def prepareExample(vertices1,vertices2,vel1=(0,0,0),vel2=(0,0,0),amom1=(0,0,0),amom2=(0,0,0),label=''):
+ O.interactions.clear()
+ O.bodies.clear()
+ t1 = tetra(vertices1,color=(0,1,0),wire=False)
+ t2 = tetra(vertices2,color=(0,1,1),wire=False)
+ O.bodies.append((t1,t2))
+ t1.state.vel = vel1
+ t2.state.vel = vel2
+ t1.state.angMom = amom1
+ t2.state.angMom = amom2
+ if label: print label
+ O.pause()
+ O.step()
+
+def runExamples():
+ dt = 20000
+ if O.iter == 1:
+ vertices1 = ((0,0,0),(2,0,0),(0,2,0),(0,0,2))
+ vertices2 = ((1,1,1),(3,1,1),(1,3,1),(1,1,3))
+ prepareExample(vertices1,vertices2,vel2=(-1,-1,-1),label='\ntesting vertex-triangle contact...\n')
+ if O.iter == 1*dt:
+ plot.data = {}
+ vertices1 = ((1,1,1),(3,1,1),(1,3,1),(1,1,3))
+ vertices2 = ((0,0,0),(2,0,0),(0,2,0),(0,0,2))
+ prepareExample(vertices1,vertices2,vel1=(-1,-1,-1),label='\ntesting vertex-triangle contact 2...\n')
+ elif O.iter == 2*dt:
+ vertices1 = ((0,0,0),(0,0,1.1),(1,0,1),(0,1,.9))
+ vertices2 = ((0,.5,1.4),(-.5,.5,.6),(-1.6,0,1),(-1.6,1.1,1))
+ prepareExample(vertices1,vertices2,vel2=(1,0,0),amom2=(0,10,0),label='\ntesting edge-edge contact\n')
+ elif O.iter == 3*dt:
+ vertices1 = ((0,0,0),(0,0,1.1),(1,0,1),(0,1,.9))
+ vertices2 = ((-.5,.5,.6),(0,.5,1.4),(-1.6,1.1,1),(-1.6,0,1))
+ prepareExample(vertices1,vertices2,vel2=(1,0,0),amom2=(0,10,0),label='\ntesting edge-edge contact\n')
+ elif O.iter == 4*dt:
+ vertices1 = ((.1,-.4,-.3),(-.3,-.4,2),(3,-.2,2),(-.1,3,2))
+ vertices2 = ((.5,1.5,2.3),(1.5,.5,2.3),(2,2,3),(0,0,3))
+ prepareExample(vertices1,vertices2,vel2=(0,0,-1),amom2=(0,0,0),label='\ntesting edge-triangle contact\n')
+ elif O.iter == 5*dt:
+ vertices1 = ((.1,-.4,-.3),(-.3,-.4,2),(3,-.2,2),(-.1,3,2))
+ vertices2 = ((-.5,2.5,2.3),(2.5,-.5,2.3),(2,2,3),(0,0,3))
+ prepareExample(vertices1,vertices2,vel2=(0,0,-1),amom2=(0,0,0),label='\ntesting edge-triangle contact 2\n')
+ elif O.iter == 6*dt:
+ vertices1 = ((1,0,0),(0,1,0),(0,0,1),(1,1,1))
+ vertices2 = ((.5,.5,1.2),(0,.1,2),(2,0,2),(0,1,2))
+ prepareExample(vertices1,vertices2,vel2=(0,0,-1),label='\ntesting vertex-edge contact\n')
+ elif O.iter == 7*dt:
+ vertices1 = ((.5,.5,1.2),(0,.1,2),(2,0,2),(0,1,2))
+ vertices2 = ((1,0,0),(0,1,0),(0,0,1),(1,1,1))
+ prepareExample(vertices1,vertices2,vel1=(0,0,-1),label='\ntesting vertex-edge contact 2\n')
+ elif O.iter == 8*dt:
+ vertices1 = ((0,0,0),(1,0,0),(0,1,0),(0,0,1))
+ vertices2 = ((0,0,1.2),(.9,.8,2),(-.7,.61,2.3),(0,-.7,2.1))
+ prepareExample(vertices1,vertices2,vel2=(0,0,-1),label='\ntesting vertex-edge contact\n')
+ elif O.iter == 9*dt:
+ vertices1 = ((0,0,1.2),(.9,.8,2),(-.7,.61,2.3),(0,-.7,2.1))
+ vertices2 = ((0,0,0),(1,0,0),(0,1,0),(0,0,1))
+ prepareExample(vertices1,vertices2,vel1=(0,0,-1),label='\ntesting vertex-edge contact 2\n')
+ elif O.iter == 10*dt:
+ O.pause()
+
+viewer = qt.View()
+plot.plots = {'i':'e','i ':('px','py','pz'),'i ':('lx','ly','lz')}
+
+O.step()
+O.step()
+O.step()
=== added file 'examples/tetra/twoTetrasPoly.py'
--- examples/tetra/twoTetrasPoly.py 1970-01-01 00:00:00 +0000
+++ examples/tetra/twoTetrasPoly.py 2013-10-15 17:11:37 +0000
@@ -0,0 +1,116 @@
+################################################################################
+#
+# Python script to test tetra-tetra contact detection for different possible
+# contact modes. Some tests are run twice to test the symmetry of the law.
+#
+# It runs several tests, making pause before each one. If run with GUI, you can
+# adjust the viewer
+#
+# During the test, momentum, angular momentum and kinetic energy is tracked in
+# plot.data. You can use plot.plot() to see the results (in sime modes the
+# energy is not conserved for instace).
+#
+################################################################################
+from yade import qt,plot
+
+mat = PolyhedraMat()
+mat.density = 2600 #kg/m^3
+mat.Ks = 2000000
+mat.Kn = 1E9 #Pa
+mat.frictionAngle = 0.5 #rad
+O.materials.append(mat)
+
+O.dt = 4e-5
+O.engines = [
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Polyhedra_Aabb()]),
+ InteractionLoop(
+ [Ig2_Polyhedra_Polyhedra_PolyhedraGeom()],
+ [Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys()],
+ [PolyhedraVolumetricLaw()]
+ ),
+ NewtonIntegrator(damping=0),
+ PyRunner(iterPeriod=500,command="addPlotData()"),
+ PyRunner(iterPeriod=1,command="runExamples()"),
+]
+
+def addPlotData():
+ p = utils.momentum()
+ l = utils.angularMomentum()
+ plot.addData(
+ i = O.iter,
+ e = utils.kineticEnergy(),
+ px = p[0],
+ py = p[1],
+ pz = p[2],
+ lx = l[0],
+ ly = l[1],
+ lz = l[2],
+ )
+
+def prepareExample(vertices1,vertices2,vel1=(0,0,0),vel2=(0,0,0),amom1=(0,0,0),amom2=(0,0,0),label=''):
+ O.interactions.clear()
+ O.bodies.clear()
+ t1 = tetraPoly(vertices1,color=(0,1,0),wire=False)
+ t2 = tetraPoly(vertices2,color=(0,1,1),wire=False)
+ O.bodies.append((t1,t2))
+ t1.state.vel = vel1
+ t2.state.vel = vel2
+ t1.state.angMom = amom1
+ t2.state.angMom = amom2
+ if label: print label
+ O.pause()
+ O.step()
+
+def runExamples():
+ dt = 20000
+ if O.iter == 1:
+ vertices1 = ((0,0,0),(2,0,0),(0,2,0),(0,0,2))
+ vertices2 = ((1,1,1),(3,1,1),(1,3,1),(1,1,3))
+ prepareExample(vertices1,vertices2,vel2=(-1,-1,-1),label='\ntesting vertex-triangle contact...\n')
+ if O.iter == 1*dt:
+ plot.data = {}
+ vertices1 = ((1,1,1),(3,1,1),(1,3,1),(1,1,3))
+ vertices2 = ((0,0,0),(2,0,0),(0,2,0),(0,0,2))
+ prepareExample(vertices1,vertices2,vel1=(-1,-1,-1),label='\ntesting vertex-triangle contact 2...\n')
+ elif O.iter == 2*dt:
+ vertices1 = ((0,0,0),(0,0,1.1),(1,0,1),(0,1,.9))
+ vertices2 = ((0,.5,1.4),(-.5,.5,.6),(-1.6,0,1),(-1.6,1.1,1))
+ prepareExample(vertices1,vertices2,vel2=(1,0,0),amom2=(0,10,0),label='\ntesting edge-edge contact\n')
+ elif O.iter == 3*dt:
+ vertices1 = ((0,0,0),(0,0,1.1),(1,0,1),(0,1,.9))
+ vertices2 = ((-.5,.5,.6),(0,.5,1.4),(-1.6,1.1,1),(-1.6,0,1))
+ prepareExample(vertices1,vertices2,vel2=(1,0,0),amom2=(0,10,0),label='\ntesting edge-edge contact\n')
+ elif O.iter == 4*dt:
+ vertices1 = ((.1,-.4,-.3),(-.3,-.4,2),(3,-.2,2),(-.1,3,2))
+ vertices2 = ((.5,1.5,2.3),(1.5,.5,2.3),(2,2,3),(0,0,3))
+ prepareExample(vertices1,vertices2,vel2=(0,0,-1),amom2=(0,0,0),label='\ntesting edge-triangle contact\n')
+ elif O.iter == 5*dt:
+ vertices1 = ((.1,-.4,-.3),(-.3,-.4,2),(3,-.2,2),(-.1,3,2))
+ vertices2 = ((-.5,2.5,2.3),(2.5,-.5,2.3),(2,2,3),(0,0,3))
+ prepareExample(vertices1,vertices2,vel2=(0,0,-1),amom2=(0,0,0),label='\ntesting edge-triangle contact 2\n')
+ elif O.iter == 6*dt:
+ vertices1 = ((1,0,0),(0,1,0),(0,0,1),(1,1,1))
+ vertices2 = ((.5,.5,1.2),(0,.1,2),(2,0,2),(0,1,2))
+ prepareExample(vertices1,vertices2,vel2=(0,0,-1),label='\ntesting vertex-edge contact\n')
+ elif O.iter == 7*dt:
+ vertices1 = ((.5,.5,1.2),(0,.1,2),(2,0,2),(0,1,2))
+ vertices2 = ((1,0,0),(0,1,0),(0,0,1),(1,1,1))
+ prepareExample(vertices1,vertices2,vel1=(0,0,-1),label='\ntesting vertex-edge contact 2\n')
+ elif O.iter == 8*dt:
+ vertices1 = ((0,0,0),(1,0,0),(0,1,0),(0,0,1))
+ vertices2 = ((0,0,1.2),(.9,.8,2),(-.7,.61,2.3),(0,-.7,2.1))
+ prepareExample(vertices1,vertices2,vel2=(0,0,-1),label='\ntesting vertex-edge contact\n')
+ elif O.iter == 9*dt:
+ vertices1 = ((0,0,1.2),(.9,.8,2),(-.7,.61,2.3),(0,-.7,2.1))
+ vertices2 = ((0,0,0),(1,0,0),(0,1,0),(0,0,1))
+ prepareExample(vertices1,vertices2,vel1=(0,0,-1),label='\ntesting vertex-edge contact 2\n')
+ elif O.iter == 10*dt:
+ O.pause()
+
+viewer = qt.View()
+plot.plots = {'i':'e','i ':('px','py','pz'),'i ':('lx','ly','lz')}
+
+O.step()
+O.step()
+O.step()
=== modified file 'examples/tunnel-pack.py'
--- examples/tunnel-pack.py 2010-05-29 20:52:10 +0000
+++ examples/tunnel-pack.py 2013-08-05 05:32:22 +0000
@@ -1,6 +1,5 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-from yade import pack
"""Simple script to create tunnel with random dense packing of spheres.
The tunnel is difference between an axis-aligned box and cylinder, or which
@@ -16,17 +15,20 @@
tunnelRad=2
rSphere=.1
# construct spatial predicate as difference of box and cylinder:
-# (see scripts/test/pack-predicates.py for details)
+# (see examples/test/pack-predicates.py for details)
#
-# http://beta.arcig.cz/~eudoxos/yade/epydoc/yade._packPredicates.inAlignedBox-class.html
-# http://beta.arcig.cz/~eudoxos/yade/epydoc/yade._packPredicates.inCylinder-class.html
+# https://yade-dem.org/doc/yade.pack.html?highlight=inalignedbox#yade._packPredicates.inAlignedBox
+# https://yade-dem.org/doc/yade.pack.html?highlight=incylinder#yade._packPredicates.inCylinder
pred=pack.inAlignedBox((-.5*boxSize[0],-.5*boxSize[1],0),(.5*boxSize[0],.5*boxSize[1],boxSize[2])) - pack.inCylinder((-.5*boxSize[0],0,0),(.5*boxSize[0],0,0),tunnelRad)
# Use the predicate to generate sphere packing inside
#
-# http://beta.arcig.cz/~eudoxos/yade/epydoc/yade.pack-module.html#randomDensePack
-O.bodies.append(pack.randomDensePack(pred,radius=rSphere,rRelFuzz=.3,memoizeDb='/tmp/triaxPackCache.sqlite',spheresInCell=3000))
+# https://yade-dem.org/doc/yade.pack.html?highlight=randomdensepack#yade.pack.randomDensePack
+sp=SpherePack()
+sp=pack.randomDensePack(pred,radius=rSphere,rRelFuzz=.3,memoizeDb='/tmp/triaxPackCache.sqlite',spheresInCell=3000,returnSpherePack=True)
+sp.toSimulation()
+# to see it
from yade import qt
qt.Controller()
qt.View()
=== modified file 'gui/CMakeLists.txt'
--- gui/CMakeLists.txt 2013-07-01 18:57:33 +0000
+++ gui/CMakeLists.txt 2013-10-14 20:51:11 +0000
@@ -3,7 +3,7 @@
INCLUDE_DIRECTORIES(${QGLVIEWER_INCLUDE_DIR})
SET(_GLViewer_MOC_HEADERS qt4/GLViewer.hpp;qt4/OpenGLManager.hpp)
- SET(_GLViewer_SOURCE_FILES qt4/GLViewer.cpp;qt4/_GLViewer.cpp;qt4/OpenGLManager.cpp)
+ SET(_GLViewer_SOURCE_FILES qt4/GLViewer.cpp;qt4/_GLViewer.cpp;qt4/OpenGLManager.cpp;qt4/GLViewerDisplay.cpp;qt4/GLViewerMouse.cpp)
QT4_WRAP_CPP(_GLViewer_MOC_OUTFILES ${_GLViewer_MOC_HEADERS})
=== modified file 'gui/qt4/GLViewer.cpp'
--- gui/qt4/GLViewer.cpp 2013-03-07 18:28:01 +0000
+++ gui/qt4/GLViewer.cpp 2013-10-14 20:51:11 +0000
@@ -46,6 +46,8 @@
GLLock::~GLLock(){ glv->doneCurrent(); }
+#define _W3 setw(3)<<setfill('0')
+#define _W2 setw(2)<<setfill('0')
GLViewer::~GLViewer(){ /* get the GL mutex when closing */ GLLock lock(this); /* cerr<<"Destructing view #"<<viewId<<endl;*/ }
void GLViewer::closeEvent(QCloseEvent *e){
@@ -125,32 +127,6 @@
}
-void GLViewer::mouseMovesCamera(){
- camera()->frame()->setWheelSensitivity(-1.0f);
-
- setMouseBinding(Qt::SHIFT + Qt::LeftButton, SELECT);
- //setMouseBinding(Qt::RightButton, NO_CLICK_ACTION);
- setMouseBinding(Qt::SHIFT + Qt::LeftButton + Qt::RightButton, FRAME, ZOOM);
- setMouseBinding(Qt::SHIFT + Qt::MidButton, FRAME, TRANSLATE);
- setMouseBinding(Qt::SHIFT + Qt::RightButton, FRAME, ROTATE);
- setWheelBinding(Qt::ShiftModifier , FRAME, ZOOM);
-
- setMouseBinding(Qt::LeftButton + Qt::RightButton, CAMERA, ZOOM);
- setMouseBinding(Qt::MidButton, CAMERA, ZOOM);
- setMouseBinding(Qt::LeftButton, CAMERA, ROTATE);
- setMouseBinding(Qt::RightButton, CAMERA, TRANSLATE);
- setWheelBinding(Qt::NoModifier, CAMERA, ZOOM);
-};
-
-void GLViewer::mouseMovesManipulatedFrame(qglviewer::Constraint* c){
- setMouseBinding(Qt::LeftButton + Qt::RightButton, FRAME, ZOOM);
- setMouseBinding(Qt::MidButton, FRAME, ZOOM);
- setMouseBinding(Qt::LeftButton, FRAME, ROTATE);
- setMouseBinding(Qt::RightButton, FRAME, TRANSLATE);
- setWheelBinding(Qt::NoModifier , FRAME, ZOOM);
- manipulatedFrame()->setConstraint(c);
-}
-
bool GLViewer::isManipulating(){
return isMoving || manipulatedClipPlane>=0;
}
@@ -173,32 +149,6 @@
displayMessage("Manipulating clip plane #"+lexical_cast<string>(planeNo+1)+(grp.empty()?grp:" (bound planes:"+grp+")"));
}
-void GLViewer::useDisplayParameters(size_t n){
- LOG_DEBUG("Loading display parameters from #"<<n);
- vector<shared_ptr<DisplayParameters> >& dispParams=Omega::instance().getScene()->dispParams;
- if(dispParams.size()<=(size_t)n){ throw std::invalid_argument(("Display parameters #"+lexical_cast<string>(n)+" don't exist (number of entries "+lexical_cast<string>(dispParams.size())+")").c_str());; return;}
- const shared_ptr<DisplayParameters>& dp=dispParams[n];
- string val;
- if(dp->getValue("OpenGLRenderer",val)){ istringstream oglre(val);
- yade::ObjectIO::load<typeof(renderer),boost::archive::xml_iarchive>(oglre,"renderer",renderer);
- }
- else { LOG_WARN("OpenGLRenderer configuration not found in display parameters, skipped.");}
- if(dp->getValue("GLViewer",val)){ GLViewer::setState(val); displayMessage("Loaded view configuration #"+lexical_cast<string>(n)); }
- else { LOG_WARN("GLViewer configuration not found in display parameters, skipped."); }
-}
-
-void GLViewer::saveDisplayParameters(size_t n){
- LOG_DEBUG("Saving display parameters to #"<<n);
- vector<shared_ptr<DisplayParameters> >& dispParams=Omega::instance().getScene()->dispParams;
- if(dispParams.size()<=n){while(dispParams.size()<=n) dispParams.push_back(shared_ptr<DisplayParameters>(new DisplayParameters));} assert(n<dispParams.size());
- shared_ptr<DisplayParameters>& dp=dispParams[n];
- ostringstream oglre;
- yade::ObjectIO::save<typeof(renderer),boost::archive::xml_oarchive>(oglre,"renderer",renderer);
- dp->setValue("OpenGLRenderer",oglre.str());
- dp->setValue("GLViewer",GLViewer::getState());
- displayMessage("Saved view configuration ot #"+lexical_cast<string>(n));
-}
-
string GLViewer::getState(){
QString origStateFileName=stateFileName();
string tmpFile=Omega::instance().tmpFilename();
@@ -426,88 +376,6 @@
showEntireScene();
}
-void GLViewer::draw()
-{
-#ifdef YADE_GL2PS
- if(!nextFrameSnapshotFilename.empty() && boost::algorithm::ends_with(nextFrameSnapshotFilename,".pdf")){
- gl2psStream=fopen(nextFrameSnapshotFilename.c_str(),"wb");
- if(!gl2psStream){ int err=errno; throw runtime_error(string("Error opening file ")+nextFrameSnapshotFilename+": "+strerror(err)); }
- LOG_DEBUG("Start saving snapshot to "<<nextFrameSnapshotFilename);
- size_t nBodies=Omega::instance().getScene()->bodies->size();
- int sortAlgo=(nBodies<100 ? GL2PS_BSP_SORT : GL2PS_SIMPLE_SORT);
- gl2psBeginPage(/*const char *title*/"Some title", /*const char *producer*/ "Yade",
- /*GLint viewport[4]*/ NULL,
- /*GLint format*/ GL2PS_PDF, /*GLint sort*/ sortAlgo, /*GLint options*/GL2PS_SIMPLE_LINE_OFFSET|GL2PS_USE_CURRENT_VIEWPORT|GL2PS_TIGHT_BOUNDING_BOX|GL2PS_COMPRESS|GL2PS_OCCLUSION_CULL|GL2PS_NO_BLENDING,
- /*GLint colormode*/ GL_RGBA, /*GLint colorsize*/0,
- /*GL2PSrgba *colortable*/NULL,
- /*GLint nr*/0, /*GLint ng*/0, /*GLint nb*/0,
- /*GLint buffersize*/4096*4096 /* 16MB */, /*FILE *stream*/ gl2psStream,
- /*const char *filename*/NULL);
- }
-#endif
-
- qglviewer::Vec vd=camera()->viewDirection(); renderer->viewDirection=Vector3r(vd[0],vd[1],vd[2]);
- if(Omega::instance().getScene()){
- const shared_ptr<Scene>& scene=Omega::instance().getScene();
- int selection = selectedName();
- if(selection!=-1 && (*(Omega::instance().getScene()->bodies)).exists(selection) && isMoving){
- static Real lastTimeMoved(0);
- static Real initv0(0); static Real initv1(0); static Real initv2(0);
- float v0,v1,v2; manipulatedFrame()->getPosition(v0,v1,v2);
- if(last == selection) // delay by one redraw, so the body will not jump into 0,0,0 coords
- {
- Quaternionr& q = (*(Omega::instance().getScene()->bodies))[selection]->state->ori;
- Vector3r& v = (*(Omega::instance().getScene()->bodies))[selection]->state->pos;
- Vector3r& vel = (*(Omega::instance().getScene()->bodies))[selection]->state->vel;
- Vector3r& angVel = (*(Omega::instance().getScene()->bodies))[selection]->state->angVel;
- angVel=Vector3r::Zero();
- if (!initv0 && !initv1 && !initv2){initv0=v0;initv1=v1;initv2=v2;}
- if (initv0!=v0 || initv1!=v1 || initv2!=v2) {
- Real dt=(scene->time-lastTimeMoved); lastTimeMoved=scene->time;
- if (dt!=0) {
- vel[0]=-(v[0]-v0)/dt; vel[1]=-(v[1]-v1)/dt; vel[2]=-(v[2]-v2)/dt;
- vel[0]=-(initv0-v0)/dt; vel[1]=-(initv1-v1)/dt; vel[2]=-(initv2-v2)/dt;
- initv0=v0;initv1=v1;initv2=v2;
- }
- }
- else {vel[0]=vel[1]=vel[2]=0; /*v[0]=v0;v[1]=v1;v[2]=v2;*/}
- v[0]=v0;v[1]=v1;v[2]=v2;
- //FIXME: should update spin like velocity above, when the body is rotated:
- double q0,q1,q2,q3; manipulatedFrame()->getOrientation(q0,q1,q2,q3); q.x()=q0;q.y()=q1;q.z()=q2;q.w()=q3;
- }
- (*(Omega::instance().getScene()->bodies))[selection]->userForcedDisplacementRedrawHook();
- last=selection;
- }
- if(manipulatedClipPlane>=0){
- assert(manipulatedClipPlane<renderer->numClipPlanes);
- float v0,v1,v2; manipulatedFrame()->getPosition(v0,v1,v2);
- double q0,q1,q2,q3; manipulatedFrame()->getOrientation(q0,q1,q2,q3);
- Se3r newSe3(Vector3r(v0,v1,v2),Quaternionr(q0,q1,q2,q3)); newSe3.orientation.normalize();
- const Se3r& oldSe3=renderer->clipPlaneSe3[manipulatedClipPlane];
- FOREACH(int planeId, boundClipPlanes){
- if(planeId>=renderer->numClipPlanes || !renderer->clipPlaneActive[planeId] || planeId==manipulatedClipPlane) continue;
- Se3r& boundSe3=renderer->clipPlaneSe3[planeId];
- Quaternionr relOrient=oldSe3.orientation.conjugate()*boundSe3.orientation; relOrient.normalize();
- Vector3r relPos=oldSe3.orientation.conjugate()*(boundSe3.position-oldSe3.position);
- boundSe3.position=newSe3.position+newSe3.orientation*relPos;
- boundSe3.orientation=newSe3.orientation*relOrient;
- boundSe3.orientation.normalize();
- }
- renderer->clipPlaneSe3[manipulatedClipPlane]=newSe3;
- }
- scene->renderer=renderer;
- renderer->render(scene, selectedName());
- }
-}
-
-void GLViewer::drawWithNames(){
- qglviewer::Vec vd=camera()->viewDirection(); renderer->viewDirection=Vector3r(vd[0],vd[1],vd[2]);
- const shared_ptr<Scene> scene(Omega::instance().getScene());
- scene->renderer=renderer;
- renderer->scene=scene;
- renderer->renderShape();
-}
-
// new object selected.
// set frame coordinates, and isDynamic=false;
void GLViewer::postSelection(const QPoint& point)
@@ -561,153 +429,6 @@
QGLViewer::endSelection(point);
}
-qglviewer::Vec GLViewer::displayedSceneCenter(){
- return camera()->unprojectedCoordinatesOf(qglviewer::Vec(width()/2 /* pixels */ ,height()/2 /* pixels */, /*middle between near plane and far plane*/ .5));
-}
-
-float GLViewer::displayedSceneRadius(){
- return (camera()->unprojectedCoordinatesOf(qglviewer::Vec(width()/2,height()/2,.5))-camera()->unprojectedCoordinatesOf(qglviewer::Vec(0,0,.5))).norm();
-}
-
-void GLViewer::postDraw(){
- Real wholeDiameter=QGLViewer::camera()->sceneRadius()*2;
-
- renderer->viewInfo.sceneRadius=QGLViewer::camera()->sceneRadius();
- qglviewer::Vec c=QGLViewer::camera()->sceneCenter();
- renderer->viewInfo.sceneCenter=Vector3r(c[0],c[1],c[2]);
-
- Real dispDiameter=min(wholeDiameter,max((Real)displayedSceneRadius()*2,wholeDiameter/1e3)); // limit to avoid drawing 1e5 lines with big zoom level
- //qglviewer::Vec center=QGLViewer::camera()->sceneCenter();
- Real gridStep=pow(10,(floor(log10(dispDiameter)-.7)));
- Real scaleStep=pow(10,(floor(log10(displayedSceneRadius()*2)-.7))); // unconstrained
- int nSegments=((int)(wholeDiameter/gridStep))+1;
- Real realSize=nSegments*gridStep;
- //LOG_TRACE("nSegments="<<nSegments<<",gridStep="<<gridStep<<",realSize="<<realSize);
- glPushMatrix();
-
- nSegments *= 2; // there's an error in QGLViewer::drawGrid(), so we need to mitigate it by '* 2'
- // XYZ grids
- glLineWidth(.5);
- if(drawGrid & 1) {glColor3f(0.6,0.3,0.3); glPushMatrix(); glRotated(90.,0.,1.,0.); QGLViewer::drawGrid(realSize,nSegments); glPopMatrix();}
- if(drawGrid & 2) {glColor3f(0.3,0.6,0.3); glPushMatrix(); glRotated(90.,1.,0.,0.); QGLViewer::drawGrid(realSize,nSegments); glPopMatrix();}
- if(drawGrid & 4) {glColor3f(0.3,0.3,0.6); glPushMatrix(); /*glRotated(90.,0.,1.,0.);*/ QGLViewer::drawGrid(realSize,nSegments); glPopMatrix();}
- if(gridSubdivide){
- if(drawGrid & 1) {glColor3f(0.4,0.1,0.1); glPushMatrix(); glRotated(90.,0.,1.,0.); QGLViewer::drawGrid(realSize,nSegments*10); glPopMatrix();}
- if(drawGrid & 2) {glColor3f(0.1,0.4,0.1); glPushMatrix(); glRotated(90.,1.,0.,0.); QGLViewer::drawGrid(realSize,nSegments*10); glPopMatrix();}
- if(drawGrid & 4) {glColor3f(0.1,0.1,0.4); glPushMatrix(); /*glRotated(90.,0.,1.,0.);*/ QGLViewer::drawGrid(realSize,nSegments*10); glPopMatrix();}
- }
-
- // scale
- if(drawScale){
- Real segmentSize=scaleStep;
- qglviewer::Vec screenDxDy[3]; // dx,dy for x,y,z scale segments
- int extremalDxDy[2]={0,0};
- for(int axis=0; axis<3; axis++){
- qglviewer::Vec delta(0,0,0); delta[axis]=segmentSize;
- qglviewer::Vec center=displayedSceneCenter();
- screenDxDy[axis]=camera()->projectedCoordinatesOf(center+delta)-camera()->projectedCoordinatesOf(center);
- for(int xy=0;xy<2;xy++)extremalDxDy[xy]=(axis>0 ? min(extremalDxDy[xy],(int)screenDxDy[axis][xy]) : screenDxDy[axis][xy]);
- }
- //LOG_DEBUG("Screen offsets for axes: "<<" x("<<screenDxDy[0][0]<<","<<screenDxDy[0][1]<<") y("<<screenDxDy[1][0]<<","<<screenDxDy[1][1]<<") z("<<screenDxDy[2][0]<<","<<screenDxDy[2][1]<<")");
- int margin=10; // screen pixels
- int scaleCenter[2]; scaleCenter[0]=abs(extremalDxDy[0])+margin; scaleCenter[1]=abs(extremalDxDy[1])+margin;
- //LOG_DEBUG("Center of scale "<<scaleCenter[0]<<","<<scaleCenter[1]);
- //displayMessage(QString().sprintf("displayed scene radius %g",displayedSceneRadius()));
- startScreenCoordinatesSystem();
- glDisable(GL_LIGHTING);
- glDisable(GL_DEPTH_TEST);
- glLineWidth(3.0);
- for(int axis=0; axis<3; axis++){
- Vector3r color(.4,.4,.4); color[axis]=.9;
- glColor3v(color);
- glBegin(GL_LINES);
- glVertex2f(scaleCenter[0],scaleCenter[1]);
- glVertex2f(scaleCenter[0]+screenDxDy[axis][0],scaleCenter[1]+screenDxDy[axis][1]);
- glEnd();
- }
- glLineWidth(1.);
- glEnable(GL_DEPTH_TEST);
- QGLViewer::drawText(scaleCenter[0],scaleCenter[1],QString().sprintf("%.3g",(double)scaleStep));
- stopScreenCoordinatesSystem();
- }
-
- // cutting planes (should be moved to OpenGLRenderer perhaps?)
- // only painted if one of those is being manipulated
- if(manipulatedClipPlane>=0){
- for(int planeId=0; planeId<renderer->numClipPlanes; planeId++){
- if(!renderer->clipPlaneActive[planeId] && planeId!=manipulatedClipPlane) continue;
- glPushMatrix();
- const Se3r& se3=renderer->clipPlaneSe3[planeId];
- AngleAxisr aa(se3.orientation);
- glTranslatef(se3.position[0],se3.position[1],se3.position[2]);
- glRotated(aa.angle()*Mathr::RAD_TO_DEG,aa.axis()[0],aa.axis()[1],aa.axis()[2]);
- Real cff=1;
- if(!renderer->clipPlaneActive[planeId]) cff=.4;
- glColor3f(max((Real)0.,cff*cos(planeId)),max((Real)0.,cff*sin(planeId)),planeId==manipulatedClipPlane); // variable colors
- QGLViewer::drawGrid(realSize,2*nSegments);
- drawArrow(wholeDiameter/6);
- glPopMatrix();
- }
- }
-
- Scene* rb=Omega::instance().getScene().get();
- #define _W3 setw(3)<<setfill('0')
- #define _W2 setw(2)<<setfill('0')
- if(timeDispMask!=0){
- const int lineHt=13;
- unsigned x=10,y=height()-3-lineHt*2;
- glColor3v(Vector3r(1,1,1));
- if(timeDispMask & GLViewer::TIME_VIRT){
- ostringstream oss;
- const Real& t=Omega::instance().getScene()->time;
- unsigned min=((unsigned)t/60),sec=(((unsigned)t)%60),msec=((unsigned)(1e3*t))%1000,usec=((unsigned long)(1e6*t))%1000,nsec=((unsigned long)(1e9*t))%1000;
- if(min>0) oss<<_W2<<min<<":"<<_W2<<sec<<"."<<_W3<<msec<<"m"<<_W3<<usec<<"u"<<_W3<<nsec<<"n";
- else if (sec>0) oss<<_W2<<sec<<"."<<_W3<<msec<<"m"<<_W3<<usec<<"u"<<_W3<<nsec<<"n";
- else if (msec>0) oss<<_W3<<msec<<"m"<<_W3<<usec<<"u"<<_W3<<nsec<<"n";
- else if (usec>0) oss<<_W3<<usec<<"u"<<_W3<<nsec<<"n";
- else oss<<_W3<<nsec<<"ns";
- QGLViewer::drawText(x,y,oss.str().c_str());
- y-=lineHt;
- }
- glColor3v(Vector3r(0,.5,.5));
- if(timeDispMask & GLViewer::TIME_REAL){
- QGLViewer::drawText(x,y,getRealTimeString().c_str() /* virtual, since player gets that from db */);
- y-=lineHt;
- }
- if(timeDispMask & GLViewer::TIME_ITER){
- ostringstream oss;
- oss<<"#"<<rb->iter;
- if(rb->stopAtIter>rb->iter) oss<<" ("<<setiosflags(ios::fixed)<<setw(3)<<setprecision(1)<<setfill('0')<<(100.*rb->iter)/rb->stopAtIter<<"%)";
- QGLViewer::drawText(x,y,oss.str().c_str());
- y-=lineHt;
- }
- if(drawGrid){
- glColor3v(Vector3r(1,1,0));
- ostringstream oss;
- oss<<"grid: "<<setprecision(4)<<gridStep;
- if(gridSubdivide) oss<<" (minor "<<setprecision(3)<<gridStep*.1<<")";
- QGLViewer::drawText(x,y,oss.str().c_str());
- y-=lineHt;
- }
- }
- QGLViewer::postDraw();
- if(!nextFrameSnapshotFilename.empty()){
- #ifdef YADE_GL2PS
- if(boost::algorithm::ends_with(nextFrameSnapshotFilename,".pdf")){
- gl2psEndPage();
- LOG_DEBUG("Finished saving snapshot to "<<nextFrameSnapshotFilename);
- fclose(gl2psStream);
- } else
- #endif
- {
- // save the snapshot
- saveSnapshot(QString(nextFrameSnapshotFilename.c_str()),/*overwrite*/ true);
- }
- // notify the caller that it is done already (probably not an atomic op :-|, though)
- nextFrameSnapshotFilename.clear();
- }
-}
-
string GLViewer::getRealTimeString(){
ostringstream oss;
time_duration t=Omega::instance().getRealTime_duration();
@@ -721,54 +442,6 @@
#undef _W2
#undef _W3
-void GLViewer::mouseMoveEvent(QMouseEvent *e){
- last_user_event = boost::posix_time::second_clock::local_time();
- QGLViewer::mouseMoveEvent(e);
-}
-
-void GLViewer::mousePressEvent(QMouseEvent *e){
- last_user_event = boost::posix_time::second_clock::local_time();
- QGLViewer::mousePressEvent(e);
-}
-
-/* Handle double-click event; if clipping plane is manipulated, align it with the global coordinate system.
- * Otherwise pass the event to QGLViewer to handle it normally.
- *
- * mostly copied over from ManipulatedFrame::mouseDoubleClickEvent
- */
-void GLViewer::mouseDoubleClickEvent(QMouseEvent *event){
- last_user_event = boost::posix_time::second_clock::local_time();
-
- if(manipulatedClipPlane<0) { /* LOG_DEBUG("Double click not on clipping plane"); */ QGLViewer::mouseDoubleClickEvent(event); return; }
-#if QT_VERSION >= 0x040000
- if (event->modifiers() == Qt::NoModifier)
-#else
- if (event->state() == Qt::NoButton)
-#endif
- switch (event->button()){
- case Qt::LeftButton: manipulatedFrame()->alignWithFrame(NULL,true); LOG_DEBUG("Aligning cutting plane"); break;
- // case Qt::RightButton: projectOnLine(camera->position(), camera->viewDirection()); break;
- default: break; // avoid warning
- }
-}
-
-void GLViewer::wheelEvent(QWheelEvent* event){
- last_user_event = boost::posix_time::second_clock::local_time();
-
- if(manipulatedClipPlane<0){ QGLViewer::wheelEvent(event); return; }
- assert(manipulatedClipPlane<renderer->numClipPlanes);
- float distStep=1e-3*sceneRadius();
- //const float wheelSensitivityCoef = 8E-4f;
- //Vec trans(0.0, 0.0, -event->delta()*wheelSensitivity()*wheelSensitivityCoef*(camera->position()-position()).norm());
- float dist=event->delta()*manipulatedFrame()->wheelSensitivity()*distStep;
- Vector3r normal=renderer->clipPlaneSe3[manipulatedClipPlane].orientation*Vector3r(0,0,1);
- qglviewer::Vec newPos=manipulatedFrame()->position()+qglviewer::Vec(normal[0],normal[1],normal[2])*dist;
- manipulatedFrame()->setPosition(newPos);
- renderer->clipPlaneSe3[manipulatedClipPlane].position=Vector3r(newPos[0],newPos[1],newPos[2]);
- updateGL();
- /* in draw, bound cutting planes will be moved as well */
-}
-
// cut&paste from QGLViewer::domElement documentation
QDomElement GLViewer::domElement(const QString& name, QDomDocument& document) const{
QDomElement de=document.createElement("grid");
=== added file 'gui/qt4/GLViewerDisplay.cpp'
--- gui/qt4/GLViewerDisplay.cpp 1970-01-01 00:00:00 +0000
+++ gui/qt4/GLViewerDisplay.cpp 2013-10-16 05:48:14 +0000
@@ -0,0 +1,296 @@
+/*************************************************************************
+* Copyright (C) 2004 by Olivier Galizzi *
+* olivier.galizzi@xxxxxxx *
+* Copyright (C) 2005 by Janek Kozicki *
+* cosurgi@xxxxxxxxxx *
+* *
+* This program is free software; it is licensed under the terms of the *
+* GNU General Public License v2 or later. See file LICENSE for details. *
+*************************************************************************/
+
+#include"GLViewer.hpp"
+#include"OpenGLManager.hpp"
+
+#include<yade/lib/opengl/OpenGLWrapper.hpp>
+#include<yade/core/Body.hpp>
+#include<yade/core/Scene.hpp>
+#include<yade/core/Interaction.hpp>
+#include<yade/core/DisplayParameters.hpp>
+#include<boost/filesystem/operations.hpp>
+#include<boost/algorithm/string.hpp>
+#include<boost/version.hpp>
+#include<boost/python.hpp>
+#include<sstream>
+#include<iomanip>
+#include<boost/algorithm/string/case_conv.hpp>
+#include<yade/lib/serialization/ObjectIO.hpp>
+#include<yade/lib/pyutil/gil.hpp>
+
+
+#include<QtGui/qevent.h>
+
+using namespace boost;
+
+#ifdef YADE_GL2PS
+ #include<gl2ps.h>
+#endif
+
+static int last(-1);
+
+void GLViewer::useDisplayParameters(size_t n){
+ LOG_DEBUG("Loading display parameters from #"<<n);
+ vector<shared_ptr<DisplayParameters> >& dispParams=Omega::instance().getScene()->dispParams;
+ if(dispParams.size()<=(size_t)n){ throw std::invalid_argument(("Display parameters #"+lexical_cast<string>(n)+" don't exist (number of entries "+lexical_cast<string>(dispParams.size())+")").c_str());; return;}
+ const shared_ptr<DisplayParameters>& dp=dispParams[n];
+ string val;
+ if(dp->getValue("OpenGLRenderer",val)){ istringstream oglre(val);
+ yade::ObjectIO::load<typeof(renderer),boost::archive::xml_iarchive>(oglre,"renderer",renderer);
+ }
+ else { LOG_WARN("OpenGLRenderer configuration not found in display parameters, skipped.");}
+ if(dp->getValue("GLViewer",val)){ GLViewer::setState(val); displayMessage("Loaded view configuration #"+lexical_cast<string>(n)); }
+ else { LOG_WARN("GLViewer configuration not found in display parameters, skipped."); }
+}
+
+void GLViewer::saveDisplayParameters(size_t n){
+ LOG_DEBUG("Saving display parameters to #"<<n);
+ vector<shared_ptr<DisplayParameters> >& dispParams=Omega::instance().getScene()->dispParams;
+ if(dispParams.size()<=n){while(dispParams.size()<=n) dispParams.push_back(shared_ptr<DisplayParameters>(new DisplayParameters));} assert(n<dispParams.size());
+ shared_ptr<DisplayParameters>& dp=dispParams[n];
+ ostringstream oglre;
+ yade::ObjectIO::save<typeof(renderer),boost::archive::xml_oarchive>(oglre,"renderer",renderer);
+ dp->setValue("OpenGLRenderer",oglre.str());
+ dp->setValue("GLViewer",GLViewer::getState());
+ displayMessage("Saved view configuration ot #"+lexical_cast<string>(n));
+}
+
+void GLViewer::draw()
+{
+#ifdef YADE_GL2PS
+ if(!nextFrameSnapshotFilename.empty() && boost::algorithm::ends_with(nextFrameSnapshotFilename,".pdf")){
+ gl2psStream=fopen(nextFrameSnapshotFilename.c_str(),"wb");
+ if(!gl2psStream){ int err=errno; throw runtime_error(string("Error opening file ")+nextFrameSnapshotFilename+": "+strerror(err)); }
+ LOG_DEBUG("Start saving snapshot to "<<nextFrameSnapshotFilename);
+ size_t nBodies=Omega::instance().getScene()->bodies->size();
+ int sortAlgo=(nBodies<100 ? GL2PS_BSP_SORT : GL2PS_SIMPLE_SORT);
+ gl2psBeginPage(/*const char *title*/"Some title", /*const char *producer*/ "Yade",
+ /*GLint viewport[4]*/ NULL,
+ /*GLint format*/ GL2PS_PDF, /*GLint sort*/ sortAlgo, /*GLint options*/GL2PS_SIMPLE_LINE_OFFSET|GL2PS_USE_CURRENT_VIEWPORT|GL2PS_TIGHT_BOUNDING_BOX|GL2PS_COMPRESS|GL2PS_OCCLUSION_CULL|GL2PS_NO_BLENDING,
+ /*GLint colormode*/ GL_RGBA, /*GLint colorsize*/0,
+ /*GL2PSrgba *colortable*/NULL,
+ /*GLint nr*/0, /*GLint ng*/0, /*GLint nb*/0,
+ /*GLint buffersize*/4096*4096 /* 16MB */, /*FILE *stream*/ gl2psStream,
+ /*const char *filename*/NULL);
+ }
+#endif
+
+ qglviewer::Vec vd=camera()->viewDirection(); renderer->viewDirection=Vector3r(vd[0],vd[1],vd[2]);
+ if(Omega::instance().getScene()){
+ const shared_ptr<Scene>& scene=Omega::instance().getScene();
+ int selection = selectedName();
+ if(selection!=-1 && (*(Omega::instance().getScene()->bodies)).exists(selection) && isMoving){
+ static Real lastTimeMoved(0);
+ static Real initv0(0); static Real initv1(0); static Real initv2(0);
+ float v0,v1,v2; manipulatedFrame()->getPosition(v0,v1,v2);
+ if(last == selection) // delay by one redraw, so the body will not jump into 0,0,0 coords
+ {
+ Quaternionr& q = (*(Omega::instance().getScene()->bodies))[selection]->state->ori;
+ Vector3r& v = (*(Omega::instance().getScene()->bodies))[selection]->state->pos;
+ Vector3r& vel = (*(Omega::instance().getScene()->bodies))[selection]->state->vel;
+ Vector3r& angVel = (*(Omega::instance().getScene()->bodies))[selection]->state->angVel;
+ angVel=Vector3r::Zero();
+ if (!initv0 && !initv1 && !initv2){initv0=v0;initv1=v1;initv2=v2;}
+ if (initv0!=v0 || initv1!=v1 || initv2!=v2) {
+ Real dt=(scene->time-lastTimeMoved); lastTimeMoved=scene->time;
+ if (dt!=0) {
+ vel[0]=-(v[0]-v0)/dt; vel[1]=-(v[1]-v1)/dt; vel[2]=-(v[2]-v2)/dt;
+ vel[0]=-(initv0-v0)/dt; vel[1]=-(initv1-v1)/dt; vel[2]=-(initv2-v2)/dt;
+ initv0=v0;initv1=v1;initv2=v2;
+ }
+ }
+ else {vel[0]=vel[1]=vel[2]=0; /*v[0]=v0;v[1]=v1;v[2]=v2;*/}
+ v[0]=v0;v[1]=v1;v[2]=v2;
+ //FIXME: should update spin like velocity above, when the body is rotated:
+ double q0,q1,q2,q3; manipulatedFrame()->getOrientation(q0,q1,q2,q3); q.x()=q0;q.y()=q1;q.z()=q2;q.w()=q3;
+ }
+ (*(Omega::instance().getScene()->bodies))[selection]->userForcedDisplacementRedrawHook();
+ last=selection;
+ }
+ if(manipulatedClipPlane>=0){
+ assert(manipulatedClipPlane<renderer->numClipPlanes);
+ float v0,v1,v2; manipulatedFrame()->getPosition(v0,v1,v2);
+ double q0,q1,q2,q3; manipulatedFrame()->getOrientation(q0,q1,q2,q3);
+ Se3r newSe3(Vector3r(v0,v1,v2),Quaternionr(q0,q1,q2,q3)); newSe3.orientation.normalize();
+ const Se3r& oldSe3=renderer->clipPlaneSe3[manipulatedClipPlane];
+ FOREACH(int planeId, boundClipPlanes){
+ if(planeId>=renderer->numClipPlanes || !renderer->clipPlaneActive[planeId] || planeId==manipulatedClipPlane) continue;
+ Se3r& boundSe3=renderer->clipPlaneSe3[planeId];
+ Quaternionr relOrient=oldSe3.orientation.conjugate()*boundSe3.orientation; relOrient.normalize();
+ Vector3r relPos=oldSe3.orientation.conjugate()*(boundSe3.position-oldSe3.position);
+ boundSe3.position=newSe3.position+newSe3.orientation*relPos;
+ boundSe3.orientation=newSe3.orientation*relOrient;
+ boundSe3.orientation.normalize();
+ }
+ renderer->clipPlaneSe3[manipulatedClipPlane]=newSe3;
+ }
+ scene->renderer=renderer;
+ renderer->render(scene, selectedName());
+ }
+}
+
+void GLViewer::drawWithNames(){
+ qglviewer::Vec vd=camera()->viewDirection(); renderer->viewDirection=Vector3r(vd[0],vd[1],vd[2]);
+ const shared_ptr<Scene> scene(Omega::instance().getScene());
+ scene->renderer=renderer;
+ renderer->scene=scene;
+ renderer->renderShape();
+}
+
+
+qglviewer::Vec GLViewer::displayedSceneCenter(){
+ return camera()->unprojectedCoordinatesOf(qglviewer::Vec(width()/2 /* pixels */ ,height()/2 /* pixels */, /*middle between near plane and far plane*/ .5));
+}
+
+float GLViewer::displayedSceneRadius(){
+ return (camera()->unprojectedCoordinatesOf(qglviewer::Vec(width()/2,height()/2,.5))-camera()->unprojectedCoordinatesOf(qglviewer::Vec(0,0,.5))).norm();
+}
+
+void GLViewer::postDraw(){
+ Real wholeDiameter=QGLViewer::camera()->sceneRadius()*2;
+
+ renderer->viewInfo.sceneRadius=QGLViewer::camera()->sceneRadius();
+ qglviewer::Vec c=QGLViewer::camera()->sceneCenter();
+ renderer->viewInfo.sceneCenter=Vector3r(c[0],c[1],c[2]);
+
+ Real dispDiameter=min(wholeDiameter,max((Real)displayedSceneRadius()*2,wholeDiameter/1e3)); // limit to avoid drawing 1e5 lines with big zoom level
+ //qglviewer::Vec center=QGLViewer::camera()->sceneCenter();
+ Real gridStep=pow(10,(floor(log10(dispDiameter)-.7)));
+ Real scaleStep=pow(10,(floor(log10(displayedSceneRadius()*2)-.7))); // unconstrained
+ int nSegments=((int)(wholeDiameter/gridStep))+1;
+ Real realSize=nSegments*gridStep;
+ //LOG_TRACE("nSegments="<<nSegments<<",gridStep="<<gridStep<<",realSize="<<realSize);
+ glPushMatrix();
+
+ nSegments *= 2; // there's an error in QGLViewer::drawGrid(), so we need to mitigate it by '* 2'
+ // XYZ grids
+ glLineWidth(.5);
+ if(drawGrid & 1) {glColor3f(0.6,0.3,0.3); glPushMatrix(); glRotated(90.,0.,1.,0.); QGLViewer::drawGrid(realSize,nSegments); glPopMatrix();}
+ if(drawGrid & 2) {glColor3f(0.3,0.6,0.3); glPushMatrix(); glRotated(90.,1.,0.,0.); QGLViewer::drawGrid(realSize,nSegments); glPopMatrix();}
+ if(drawGrid & 4) {glColor3f(0.3,0.3,0.6); glPushMatrix(); /*glRotated(90.,0.,1.,0.);*/ QGLViewer::drawGrid(realSize,nSegments); glPopMatrix();}
+ if(gridSubdivide){
+ if(drawGrid & 1) {glColor3f(0.4,0.1,0.1); glPushMatrix(); glRotated(90.,0.,1.,0.); QGLViewer::drawGrid(realSize,nSegments*10); glPopMatrix();}
+ if(drawGrid & 2) {glColor3f(0.1,0.4,0.1); glPushMatrix(); glRotated(90.,1.,0.,0.); QGLViewer::drawGrid(realSize,nSegments*10); glPopMatrix();}
+ if(drawGrid & 4) {glColor3f(0.1,0.1,0.4); glPushMatrix(); /*glRotated(90.,0.,1.,0.);*/ QGLViewer::drawGrid(realSize,nSegments*10); glPopMatrix();}
+ }
+
+ // scale
+ if(drawScale){
+ Real segmentSize=scaleStep;
+ qglviewer::Vec screenDxDy[3]; // dx,dy for x,y,z scale segments
+ int extremalDxDy[2]={0,0};
+ for(int axis=0; axis<3; axis++){
+ qglviewer::Vec delta(0,0,0); delta[axis]=segmentSize;
+ qglviewer::Vec center=displayedSceneCenter();
+ screenDxDy[axis]=camera()->projectedCoordinatesOf(center+delta)-camera()->projectedCoordinatesOf(center);
+ for(int xy=0;xy<2;xy++)extremalDxDy[xy]=(axis>0 ? min(extremalDxDy[xy],(int)screenDxDy[axis][xy]) : screenDxDy[axis][xy]);
+ }
+ //LOG_DEBUG("Screen offsets for axes: "<<" x("<<screenDxDy[0][0]<<","<<screenDxDy[0][1]<<") y("<<screenDxDy[1][0]<<","<<screenDxDy[1][1]<<") z("<<screenDxDy[2][0]<<","<<screenDxDy[2][1]<<")");
+ int margin=10; // screen pixels
+ int scaleCenter[2]; scaleCenter[0]=abs(extremalDxDy[0])+margin; scaleCenter[1]=abs(extremalDxDy[1])+margin;
+ //LOG_DEBUG("Center of scale "<<scaleCenter[0]<<","<<scaleCenter[1]);
+ //displayMessage(QString().sprintf("displayed scene radius %g",displayedSceneRadius()));
+ startScreenCoordinatesSystem();
+ glDisable(GL_LIGHTING);
+ glDisable(GL_DEPTH_TEST);
+ glLineWidth(3.0);
+ for(int axis=0; axis<3; axis++){
+ Vector3r color(.4,.4,.4); color[axis]=.9;
+ glColor3v(color);
+ glBegin(GL_LINES);
+ glVertex2f(scaleCenter[0],scaleCenter[1]);
+ glVertex2f(scaleCenter[0]+screenDxDy[axis][0],scaleCenter[1]+screenDxDy[axis][1]);
+ glEnd();
+ }
+ glLineWidth(1.);
+ glEnable(GL_DEPTH_TEST);
+ QGLViewer::drawText(scaleCenter[0],scaleCenter[1],QString().sprintf("%.3g",(double)scaleStep));
+ stopScreenCoordinatesSystem();
+ }
+
+ // cutting planes (should be moved to OpenGLRenderer perhaps?)
+ // only painted if one of those is being manipulated
+ if(manipulatedClipPlane>=0){
+ for(int planeId=0; planeId<renderer->numClipPlanes; planeId++){
+ if(!renderer->clipPlaneActive[planeId] && planeId!=manipulatedClipPlane) continue;
+ glPushMatrix();
+ const Se3r& se3=renderer->clipPlaneSe3[planeId];
+ AngleAxisr aa(se3.orientation);
+ glTranslatef(se3.position[0],se3.position[1],se3.position[2]);
+ glRotated(aa.angle()*Mathr::RAD_TO_DEG,aa.axis()[0],aa.axis()[1],aa.axis()[2]);
+ Real cff=1;
+ if(!renderer->clipPlaneActive[planeId]) cff=.4;
+ glColor3f(max((Real)0.,cff*cos(planeId)),max((Real)0.,cff*sin(planeId)),planeId==manipulatedClipPlane); // variable colors
+ QGLViewer::drawGrid(realSize,2*nSegments);
+ drawArrow(wholeDiameter/6);
+ glPopMatrix();
+ }
+ }
+
+ Scene* rb=Omega::instance().getScene().get();
+ #define _W3 setw(3)<<setfill('0')
+ #define _W2 setw(2)<<setfill('0')
+ if(timeDispMask!=0){
+ const int lineHt=13;
+ unsigned x=10,y=height()-3-lineHt*2;
+ glColor3v(Vector3r(1,1,1));
+ if(timeDispMask & GLViewer::TIME_VIRT){
+ ostringstream oss;
+ const Real& t=Omega::instance().getScene()->time;
+ unsigned min=((unsigned)t/60),sec=(((unsigned)t)%60),msec=((unsigned)(1e3*t))%1000,usec=((unsigned long)(1e6*t))%1000,nsec=((unsigned long)(1e9*t))%1000;
+ if(min>0) oss<<_W2<<min<<":"<<_W2<<sec<<"."<<_W3<<msec<<"m"<<_W3<<usec<<"u"<<_W3<<nsec<<"n";
+ else if (sec>0) oss<<_W2<<sec<<"."<<_W3<<msec<<"m"<<_W3<<usec<<"u"<<_W3<<nsec<<"n";
+ else if (msec>0) oss<<_W3<<msec<<"m"<<_W3<<usec<<"u"<<_W3<<nsec<<"n";
+ else if (usec>0) oss<<_W3<<usec<<"u"<<_W3<<nsec<<"n";
+ else oss<<_W3<<nsec<<"ns";
+ QGLViewer::drawText(x,y,oss.str().c_str());
+ y-=lineHt;
+ }
+ glColor3v(Vector3r(0,.5,.5));
+ if(timeDispMask & GLViewer::TIME_REAL){
+ QGLViewer::drawText(x,y,getRealTimeString().c_str() /* virtual, since player gets that from db */);
+ y-=lineHt;
+ }
+ if(timeDispMask & GLViewer::TIME_ITER){
+ ostringstream oss;
+ oss<<"#"<<rb->iter;
+ if(rb->stopAtIter>rb->iter) oss<<" ("<<setiosflags(ios::fixed)<<setw(3)<<setprecision(1)<<setfill('0')<<(100.*rb->iter)/rb->stopAtIter<<"%)";
+ QGLViewer::drawText(x,y,oss.str().c_str());
+ y-=lineHt;
+ }
+ if(drawGrid){
+ glColor3v(Vector3r(1,1,0));
+ ostringstream oss;
+ oss<<"grid: "<<setprecision(4)<<gridStep;
+ if(gridSubdivide) oss<<" (minor "<<setprecision(3)<<gridStep*.1<<")";
+ QGLViewer::drawText(x,y,oss.str().c_str());
+ y-=lineHt;
+ }
+ }
+ QGLViewer::postDraw();
+ if(!nextFrameSnapshotFilename.empty()){
+ #ifdef YADE_GL2PS
+ if(boost::algorithm::ends_with(nextFrameSnapshotFilename,".pdf")){
+ gl2psEndPage();
+ LOG_DEBUG("Finished saving snapshot to "<<nextFrameSnapshotFilename);
+ fclose(gl2psStream);
+ } else
+ #endif
+ {
+ // save the snapshot
+ saveSnapshot(QString(nextFrameSnapshotFilename.c_str()),/*overwrite*/ true);
+ }
+ // notify the caller that it is done already (probably not an atomic op :-|, though)
+ nextFrameSnapshotFilename.clear();
+ }
+}
+
+
=== added file 'gui/qt4/GLViewerMouse.cpp'
--- gui/qt4/GLViewerMouse.cpp 1970-01-01 00:00:00 +0000
+++ gui/qt4/GLViewerMouse.cpp 2013-10-16 05:48:14 +0000
@@ -0,0 +1,112 @@
+/*************************************************************************
+* Copyright (C) 2004 by Olivier Galizzi *
+* olivier.galizzi@xxxxxxx *
+* Copyright (C) 2005 by Janek Kozicki *
+* cosurgi@xxxxxxxxxx *
+* *
+* This program is free software; it is licensed under the terms of the *
+* GNU General Public License v2 or later. See file LICENSE for details. *
+*************************************************************************/
+
+#include"GLViewer.hpp"
+#include"OpenGLManager.hpp"
+
+#include<yade/lib/opengl/OpenGLWrapper.hpp>
+#include<yade/core/Body.hpp>
+#include<yade/core/Scene.hpp>
+#include<yade/core/Interaction.hpp>
+#include<yade/core/DisplayParameters.hpp>
+#include<boost/filesystem/operations.hpp>
+#include<boost/algorithm/string.hpp>
+#include<boost/version.hpp>
+#include<boost/python.hpp>
+#include<sstream>
+#include<iomanip>
+#include<boost/algorithm/string/case_conv.hpp>
+#include<yade/lib/serialization/ObjectIO.hpp>
+#include<yade/lib/pyutil/gil.hpp>
+
+
+#include<QtGui/qevent.h>
+
+using namespace boost;
+
+#ifdef YADE_GL2PS
+ #include<gl2ps.h>
+#endif
+
+void GLViewer::mouseMovesCamera(){
+ camera()->frame()->setWheelSensitivity(-1.0f);
+
+ setMouseBinding(Qt::SHIFT + Qt::LeftButton, SELECT);
+ //setMouseBinding(Qt::RightButton, NO_CLICK_ACTION);
+ setMouseBinding(Qt::SHIFT + Qt::LeftButton + Qt::RightButton, FRAME, ZOOM);
+ setMouseBinding(Qt::SHIFT + Qt::MidButton, FRAME, TRANSLATE);
+ setMouseBinding(Qt::SHIFT + Qt::RightButton, FRAME, ROTATE);
+ setWheelBinding(Qt::ShiftModifier , FRAME, ZOOM);
+
+ setMouseBinding(Qt::LeftButton + Qt::RightButton, CAMERA, ZOOM);
+ setMouseBinding(Qt::MidButton, CAMERA, ZOOM);
+ setMouseBinding(Qt::LeftButton, CAMERA, ROTATE);
+ setMouseBinding(Qt::RightButton, CAMERA, TRANSLATE);
+ setWheelBinding(Qt::NoModifier, CAMERA, ZOOM);
+};
+
+void GLViewer::mouseMovesManipulatedFrame(qglviewer::Constraint* c){
+ setMouseBinding(Qt::LeftButton + Qt::RightButton, FRAME, ZOOM);
+ setMouseBinding(Qt::MidButton, FRAME, ZOOM);
+ setMouseBinding(Qt::LeftButton, FRAME, ROTATE);
+ setMouseBinding(Qt::RightButton, FRAME, TRANSLATE);
+ setWheelBinding(Qt::NoModifier , FRAME, ZOOM);
+ manipulatedFrame()->setConstraint(c);
+}
+
+
+void GLViewer::mouseMoveEvent(QMouseEvent *e){
+ last_user_event = boost::posix_time::second_clock::local_time();
+ QGLViewer::mouseMoveEvent(e);
+}
+
+void GLViewer::mousePressEvent(QMouseEvent *e){
+ last_user_event = boost::posix_time::second_clock::local_time();
+ QGLViewer::mousePressEvent(e);
+}
+
+/* Handle double-click event; if clipping plane is manipulated, align it with the global coordinate system.
+ * Otherwise pass the event to QGLViewer to handle it normally.
+ *
+ * mostly copied over from ManipulatedFrame::mouseDoubleClickEvent
+ */
+void GLViewer::mouseDoubleClickEvent(QMouseEvent *event){
+ last_user_event = boost::posix_time::second_clock::local_time();
+
+ if(manipulatedClipPlane<0) { /* LOG_DEBUG("Double click not on clipping plane"); */ QGLViewer::mouseDoubleClickEvent(event); return; }
+#if QT_VERSION >= 0x040000
+ if (event->modifiers() == Qt::NoModifier)
+#else
+ if (event->state() == Qt::NoButton)
+#endif
+ switch (event->button()){
+ case Qt::LeftButton: manipulatedFrame()->alignWithFrame(NULL,true); LOG_DEBUG("Aligning cutting plane"); break;
+ // case Qt::RightButton: projectOnLine(camera->position(), camera->viewDirection()); break;
+ default: break; // avoid warning
+ }
+}
+
+void GLViewer::wheelEvent(QWheelEvent* event){
+ last_user_event = boost::posix_time::second_clock::local_time();
+
+ if(manipulatedClipPlane<0){ QGLViewer::wheelEvent(event); return; }
+ assert(manipulatedClipPlane<renderer->numClipPlanes);
+ float distStep=1e-3*sceneRadius();
+ //const float wheelSensitivityCoef = 8E-4f;
+ //Vec trans(0.0, 0.0, -event->delta()*wheelSensitivity()*wheelSensitivityCoef*(camera->position()-position()).norm());
+ float dist=event->delta()*manipulatedFrame()->wheelSensitivity()*distStep;
+ Vector3r normal=renderer->clipPlaneSe3[manipulatedClipPlane].orientation*Vector3r(0,0,1);
+ qglviewer::Vec newPos=manipulatedFrame()->position()+qglviewer::Vec(normal[0],normal[1],normal[2])*dist;
+ manipulatedFrame()->setPosition(newPos);
+ renderer->clipPlaneSe3[manipulatedClipPlane].position=Vector3r(newPos[0],newPos[1],newPos[2]);
+ updateGL();
+ /* in draw, bound cutting planes will be moved as well */
+}
+
=== modified file 'gui/qt4/__init__.py'
--- gui/qt4/__init__.py 2012-08-23 12:04:26 +0000
+++ gui/qt4/__init__.py 2013-11-06 15:33:02 +0000
@@ -1,6 +1,11 @@
# encoding: utf-8
import yade.runtime
-if not yade.runtime.hasDisplay: raise ImportError("Connecting to DISPLAY at Yade startup failed, unable to activate the qt4 interface.")
+if not yade.runtime.hasDisplay:
+ msg = "Connecting to DISPLAY at Yade startup failed, unable to activate the qt4 interface."
+ import os
+ if 'YADE_BATCH' in os.environ:
+ msg += "\nDo not import qt when running in batch mode."
+ raise ImportError(msg)
from PyQt4.QtGui import *
from PyQt4 import QtCore
=== modified file 'lib/base/Math.hpp'
--- lib/base/Math.hpp 2013-07-09 12:42:00 +0000
+++ lib/base/Math.hpp 2013-08-23 15:21:20 +0000
@@ -116,28 +116,20 @@
template<typename MatrixT>
void Matrix_computeUnitaryPositive(const MatrixT& in, MatrixT* unitary, MatrixT* positive){
assert(unitary); assert(positive);
- #if EIGEN_WORLD_VERSION==2
- Eigen::SVD<MatrixT>(in).computeUnitaryPositive(unitary,positive);
- #elif EIGEN_WORLD_VERSION==3
- Eigen::JacobiSVD<MatrixT> svd(in, Eigen::ComputeThinU | Eigen::ComputeThinV);
- MatrixT mU, mV, mS;
- mU = svd.matrixU();
- mV = svd.matrixV();
- mS = svd.singularValues().asDiagonal();
+ Eigen::JacobiSVD<MatrixT> svd(in, Eigen::ComputeThinU | Eigen::ComputeThinV);
+ MatrixT mU, mV, mS;
+ mU = svd.matrixU();
+ mV = svd.matrixV();
+ mS = svd.singularValues().asDiagonal();
- *unitary=mU * mV.adjoint();
- *positive=mV * mS * mV.adjoint();
- #endif
+ *unitary=mU * mV.adjoint();
+ *positive=mV * mS * mV.adjoint();
}
template<typename MatrixT>
void Matrix_SVD(const MatrixT& in, MatrixT* mU, MatrixT* mS, MatrixT* mV){
assert(mU); assert(mS); assert(mV);
- #if EIGEN_WORLD_VERSION==2 // see Matrix_computeUnitaryPositive
- Eigen::SVD<MatrixT> svd = Eigen::SVD<MatrixT>(in);
- #elif EIGEN_WORLD_VERSION==3
- Eigen::JacobiSVD<MatrixT> svd(in, Eigen::ComputeThinU | Eigen::ComputeThinV);
- #endif
+ Eigen::JacobiSVD<MatrixT> svd(in, Eigen::ComputeThinU | Eigen::ComputeThinV);
*mU = svd.matrixU();
*mV = svd.matrixV();
*mS = svd.singularValues().asDiagonal();
=== modified file 'lib/import/STLReader.hpp'
--- lib/import/STLReader.hpp 2013-05-29 09:48:51 +0000
+++ lib/import/STLReader.hpp 2013-09-23 17:39:59 +0000
@@ -111,20 +111,19 @@
vector<Vrtx> vcs;
set<pair<int,int> > egs;
- size_t ret;
/* Read a single facet from an ASCII .STL file */
while(!feof(fp))
{
float n[3];
Vrtx v[3];
- ret=fscanf(fp, "%*s %*s %f %f %f\n", &n[0], &n[1], &n[2]);
- ret=fscanf(fp, "%*s %*s");
- ret=fscanf(fp, "%*s %f %f %f\n", &v[0][0], &v[0][1], &v[0][2]);
- ret=fscanf(fp, "%*s %f %f %f\n", &v[1][0], &v[1][1], &v[1][2]);
- ret=fscanf(fp, "%*s %f %f %f\n", &v[2][0], &v[2][1], &v[2][2]);
- ret=fscanf(fp, "%*s"); // end loop
- ret=fscanf(fp, "%*s"); // end facet
+ fscanf(fp, "%*s %*s %f %f %f\n", &n[0], &n[1], &n[2]);
+ fscanf(fp, "%*s %*s");
+ fscanf(fp, "%*s %f %f %f\n", &v[0][0], &v[0][1], &v[0][2]);
+ fscanf(fp, "%*s %f %f %f\n", &v[1][0], &v[1][1], &v[1][2]);
+ fscanf(fp, "%*s %f %f %f\n", &v[2][0], &v[2][1], &v[2][2]);
+ fscanf(fp, "%*s"); // end loop
+ fscanf(fp, "%*s"); // end facet
if(feof(fp)) break;
int vid[3];
=== modified file 'lib/opengl/GLUtils.cpp'
--- lib/opengl/GLUtils.cpp 2011-01-29 22:47:18 +0000
+++ lib/opengl/GLUtils.cpp 2013-08-23 15:21:20 +0000
@@ -53,7 +53,8 @@
glPushMatrix();
glTranslatef(from[0],from[1],from[2]);
Quaternionr q(Quaternionr().setFromTwoVectors(Vector3r(0,0,1),to-from));
- glMultMatrixd(q.toRotationMatrix().data());
+ //glMultMatrixd(q.toRotationMatrix().data());
+ glMultMatrix(q.toRotationMatrix().data());
drawArrow((to-from).norm(), radius, nbSubdivisions);
glPopMatrix();
}
=== modified file 'lib/opengl/OpenGLWrapper.hpp'
--- lib/opengl/OpenGLWrapper.hpp 2012-08-02 18:13:22 +0000
+++ lib/opengl/OpenGLWrapper.hpp 2013-09-08 11:12:42 +0000
@@ -26,71 +26,76 @@
/// Primary Templates
-template< typename Type > inline void glRotate ( Type ,Type ,Type , Type ) { STATIC_ASSERT(false); };
-template< typename Type > inline void glScale ( Type ,Type , Type ) { STATIC_ASSERT(false); };
+template< typename Type > inline void glRotate ( Type, Type, Type, Type ) { STATIC_ASSERT(false); };
+template< typename Type > inline void glScale ( Type, Type, Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glScalev ( const Type ) { STATIC_ASSERT(false); };
-template< typename Type > inline void glTranslate ( Type ,Type , Type ) { STATIC_ASSERT(false); };
+template< typename Type > inline void glTranslate ( Type, Type, Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glTranslatev ( const Type ) { STATIC_ASSERT(false); };
-template< typename Type > inline void glVertex2 ( Type ,Type ) { STATIC_ASSERT(false); };
-template< typename Type > inline void glVertex3 ( Type ,Type , Type ) { STATIC_ASSERT(false); };
-template< typename Type > inline void glVertex4 ( Type ,Type ,Type , Type ) { STATIC_ASSERT(false); };
+template< typename Type > inline void glVertex2 ( Type, Type ) { STATIC_ASSERT(false); };
+template< typename Type > inline void glVertex3 ( Type, Type, Type ) { STATIC_ASSERT(false); };
+template< typename Type > inline void glVertex4 ( Type, Type, Type, Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glVertex2v ( const Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glVertex3v ( const Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glVertex4v ( const Type ) { STATIC_ASSERT(false); };
-template< typename Type > inline void glNormal3 ( Type ,Type ,Type ) { STATIC_ASSERT(false); };
+template< typename Type > inline void glNormal3 ( Type, Type, Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glNormal3v ( const Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glIndex ( Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glIndexv ( Type ) { STATIC_ASSERT(false); };
-template< typename Type > inline void glColor3 ( Type ,Type ,Type ) { STATIC_ASSERT(false); };
-template< typename Type > inline void glColor4 ( Type ,Type ,Type , Type ) { STATIC_ASSERT(false); };
+template< typename Type > inline void glColor3 ( Type, Type, Type ) { STATIC_ASSERT(false); };
+template< typename Type > inline void glColor4 ( Type, Type, Type, Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glColor3v ( const Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glColor4v ( const Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glTexCoord1 ( Type ) { STATIC_ASSERT(false); };
-template< typename Type > inline void glTexCoord2 ( Type ,Type ) { STATIC_ASSERT(false); };
-template< typename Type > inline void glTexCoord3 ( Type ,Type , Type ) { STATIC_ASSERT(false); };
-template< typename Type > inline void glTexCoord4 ( Type ,Type ,Type , Type ) { STATIC_ASSERT(false); };
+template< typename Type > inline void glTexCoord2 ( Type, Type ) { STATIC_ASSERT(false); };
+template< typename Type > inline void glTexCoord3 ( Type, Type, Type ) { STATIC_ASSERT(false); };
+template< typename Type > inline void glTexCoord4 ( Type, Type, Type, Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glTexCoord1v ( const Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glTexCoord2v ( const Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glTexCoord3v ( const Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glTexCoord4v ( const Type ) { STATIC_ASSERT(false); };
-template< typename Type > inline void glRasterPos2 ( Type ,Type ) { STATIC_ASSERT(false); };
-template< typename Type > inline void glRasterPos3 ( Type ,Type , Type ) { STATIC_ASSERT(false); };
-template< typename Type > inline void glRasterPos4 ( Type ,Type ,Type , Type ) { STATIC_ASSERT(false); };
+template< typename Type > inline void glRasterPos2 ( Type, Type ) { STATIC_ASSERT(false); };
+template< typename Type > inline void glRasterPos3 ( Type, Type, Type ) { STATIC_ASSERT(false); };
+template< typename Type > inline void glRasterPos4 ( Type, Type, Type, Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glRasterPos2v ( const Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glRasterPos3v ( const Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glRasterPos4v ( const Type ) { STATIC_ASSERT(false); };
-template< typename Type > inline void glRect ( Type ,Type ,Type , Type ) { STATIC_ASSERT(false); };
+template< typename Type > inline void glRect ( Type, Type, Type, Type ) { STATIC_ASSERT(false); };
template< typename Type > inline void glMaterial ( GLenum face, GLenum pname, Type param ){ STATIC_ASSERT(false); };
template< typename Type > inline void glMaterialv ( GLenum face, GLenum pname, Type param ){ STATIC_ASSERT(false); };
template< typename Type > inline void glMultMatrix (const Type*){ STATIC_ASSERT(false); };
+#define LDOUBL long double
/// Template Specializations
template< > inline void glMultMatrix<double>(const double* m){glMultMatrixd(m); };
-template< > inline void glMultMatrix<long double>(const long double* m){double mm[16]; for(int i=0;i<16;i++)mm[i]=(double)m[i]; glMultMatrixd(mm);};
+template< > inline void glMultMatrix<LDOUBL>(const LDOUBL* m){double mm[16]; for(int i=0;i<16;i++)mm[i]=(double)m[i]; glMultMatrixd(mm);};
template< > inline void glRotate< double > (double angle,double x,double y, double z ) { glRotated(angle,x,y,z); };
+template< > inline void glRotate< LDOUBL > (LDOUBL angle, LDOUBL x, LDOUBL y, LDOUBL z ) { glRotated((double)angle,(double)x,(double)y,(double)z); };
template< > inline void glRotate< float > (float angle,float x,float y, float z ) { glRotatef(angle,x,y,z); };
template< > inline void glScale< double > ( double x,double y, double z ) { glScaled(x,y,z); };
-template< > inline void glScale< long double > ( long double x,long double y, long double z ) { glScaled(x,y,z); };
+template< > inline void glScale< LDOUBL > ( LDOUBL x,LDOUBL y, LDOUBL z ) { glScaled(x,y,z); };
template< > inline void glScale< float > ( float x,float y,float z ) { glScalef(x,y,z); };
template< > inline void glScalev< Vector3r > ( const Vector3r v ) { glScaled(v[0],v[1],v[2]);};
template< > inline void glTranslate< double > ( double x,double y, double z ) { glTranslated(x,y,z); };
-template< > inline void glTranslate< long double > ( long double x, long double y, long double z ) { glTranslated(x,y,z); };
+template< > inline void glTranslate< LDOUBL > ( LDOUBL x, LDOUBL y, LDOUBL z ) { glTranslated(x,y,z); };
template< > inline void glTranslate< float > ( float x,float y,float z ) { glTranslatef(x,y,z); };
template< > inline void glTranslatev< Vector3r > ( const Vector3r v ) { glTranslated(v[0],v[1],v[2]);};
template< > inline void glVertex2< double > ( double x,double y ) { glVertex2d(x,y); };
+template< > inline void glVertex2< LDOUBL > ( LDOUBL x, LDOUBL y ) { glVertex2d((double)x,(double)y); };
template< > inline void glVertex2< float > ( float x,float y ) { glVertex2f(x,y); };
template< > inline void glVertex2< int > ( int x,int y ) { glVertex2i(x,y); };
template< > inline void glVertex3< double > ( double x,double y, double z ) { glVertex3d(x,y,z); };
+template< > inline void glVertex3< LDOUBL > ( LDOUBL x, LDOUBL y, LDOUBL z ) { glVertex3d((double)x,(double)y,(double)z); };
template< > inline void glVertex3< float > ( float x,float y,float z ) { glVertex3f(x,y,z); };
template< > inline void glVertex3< int > ( int x, int y, int z ) { glVertex3i(x,y,z); };
template< > inline void glVertex4< double > ( double x,double y,double z, double w ){ glVertex4d(x,y,z,w); };
+template< > inline void glVertex4< LDOUBL > ( LDOUBL x, LDOUBL y, LDOUBL z, LDOUBL w ){ glVertex4d((double)x,(double)y,(double)z,(double)w); };
template< > inline void glVertex4< float > ( float x,float y,float z, float w ) { glVertex4f(x,y,z,w); };
template< > inline void glVertex4< int > ( int x,int y,int z, int w ) { glVertex4i(x,y,z,w); };
@@ -105,12 +110,14 @@
template< > inline void glVertex4v< Vector3i > ( const Vector3i v ) { glVertex4iv((int*)&v); };
template< > inline void glNormal3< double > (double nx,double ny,double nz ) { glNormal3d(nx,ny,nz); };
+template< > inline void glNormal3< LDOUBL > ( LDOUBL nx, LDOUBL ny, LDOUBL nz ) { glNormal3d((double)nx,(double)ny,(double)nz); };
template< > inline void glNormal3< int > (int nx,int ny,int nz ) { glNormal3i(nx,ny,nz); };
template< > inline void glNormal3v< Vector3r > ( const Vector3r v ) { glNormal3dv((double*)&v); };
template< > inline void glNormal3v< Vector3i > ( const Vector3i v ) { glNormal3iv((int*)&v); };
template< > inline void glIndex< double > ( double c ) { glIndexd(c); };
+template< > inline void glIndex< LDOUBL > ( LDOUBL c ) { glIndexd((double)c); };
template< > inline void glIndex< float > ( float c ) { glIndexf(c); };
template< > inline void glIndex< int > ( int c ) { glIndexi(c); };
template< > inline void glIndex< unsigned char > ( unsigned char c ) { glIndexub(c); };
@@ -119,10 +126,12 @@
template< > inline void glIndexv<const Vector3i > ( const Vector3i c) { glIndexiv((int*)&c); }
template< > inline void glColor3< double > (double red,double green,double blue ) { glColor3d(red,green,blue); };
+template< > inline void glColor3< LDOUBL > ( LDOUBL red, LDOUBL green, LDOUBL blue ) { glColor3d((double)red,(double)green,(double)blue); };
template< > inline void glColor3< float > (float red,float green,float blue ) { glColor3f(red,green,blue); };
template< > inline void glColor3< int > (int red,int green,int blue ) { glColor3i(red,green,blue); };
template< > inline void glColor4< double > (double red,double green,double blue, double alpha ) { glColor4d(red,green,blue,alpha); };
+template< > inline void glColor4< LDOUBL > (LDOUBL red,LDOUBL green,LDOUBL blue, LDOUBL alpha ) { glColor4d((double)red,(double)green,(double)blue,(double)alpha); };
template< > inline void glColor4< float > (float red,float green,float blue, float alpha ) { glColor4f(red,green,blue,alpha); };
template< > inline void glColor4< int > (int red,int green,int blue, int alpha ) { glColor4i(red,green,blue,alpha); };
@@ -135,18 +144,22 @@
template< > inline void glTexCoord1< double > ( double s ) { glTexCoord1d(s); };
+template< > inline void glTexCoord1< LDOUBL > ( LDOUBL s ) { glTexCoord1d((double)s); };
template< > inline void glTexCoord1< float > ( float s ) { glTexCoord1f(s); };
template< > inline void glTexCoord1< int > ( int s ) { glTexCoord1i(s); };
template< > inline void glTexCoord2< double > ( double s,double t ) { glTexCoord2d(s,t); };
+template< > inline void glTexCoord2< LDOUBL > ( LDOUBL s,LDOUBL t ) { glTexCoord2d((double)s,(double)t); };
template< > inline void glTexCoord2< float > ( float s,float t ) { glTexCoord2f(s,t); };
template< > inline void glTexCoord2< int > ( int s,int t ) { glTexCoord2i(s,t); };
template< > inline void glTexCoord3< double > ( double s,double t, double r ) { glTexCoord3d(s,t,r); };
+template< > inline void glTexCoord3< LDOUBL > ( LDOUBL s,LDOUBL t, LDOUBL r ) { glTexCoord3d((double)s,(double)t,(double)r); };
template< > inline void glTexCoord3< float > ( float s,float t,float r ) { glTexCoord3f(s,t,r); };
template< > inline void glTexCoord3< int > ( int s, int t, int r ) { glTexCoord3i(s,t,r); };
template< > inline void glTexCoord4< double > (double s,double t,double r, double q ) { glTexCoord4d(s,t,r,q); };
+template< > inline void glTexCoord4< LDOUBL > (LDOUBL s,LDOUBL t,LDOUBL r, LDOUBL q ) { glTexCoord4d((double)s,(double)t,(double)r,(double)q); };
template< > inline void glTexCoord4< float > (float s,float t,float r, float q ) { glTexCoord4f(s,t,r,q); };
template< > inline void glTexCoord4< int > (int s,int t,int r, int q ) { glTexCoord4i(s,t,r,q); };
@@ -164,14 +177,17 @@
template< > inline void glRasterPos2< double > ( double x,double y ) { glRasterPos2d(x,y); };
+template< > inline void glRasterPos2< LDOUBL > ( LDOUBL x,LDOUBL y ) { glRasterPos2d((double)x,(double)y); };
template< > inline void glRasterPos2< float > ( float x,float y ) { glRasterPos2f(x,y); };
template< > inline void glRasterPos2< int > ( int x,int y ) { glRasterPos2i(x,y); };
template< > inline void glRasterPos3< double > ( double x,double y, double z ) { glRasterPos3d(x,y,z); };
+template< > inline void glRasterPos3< LDOUBL > ( LDOUBL x,LDOUBL y, LDOUBL z ) { glRasterPos3d((double)x,(double)y,(double)z); };
template< > inline void glRasterPos3< float > ( float x,float y,float z ) { glRasterPos3f(x,y,z); };
template< > inline void glRasterPos3< int > ( int x, int y, int z ) { glRasterPos3i(x,y,z); };
template< > inline void glRasterPos4< double > (double x,double y,double z, double w ) { glRasterPos4d(x,y,z,w); };
+template< > inline void glRasterPos4< LDOUBL > (LDOUBL x,LDOUBL y,LDOUBL z, LDOUBL w ) { glRasterPos4d((double)x,(double)y,(double)z,(double)w); };
template< > inline void glRasterPos4< float > (float x,float y,float z, float w ) { glRasterPos4f(x,y,z,w); };
template< > inline void glRasterPos4< int > (int x,int y,int z, int w ) { glRasterPos4i(x,y,z,w); };
@@ -188,6 +204,7 @@
template< > inline void glRect< double > (double x1,double y1,double x2, double y2 ) { glRectd(x1,y1,x2,y2); };
+template< > inline void glRect< LDOUBL > (LDOUBL x1,LDOUBL y1,LDOUBL x2, LDOUBL y2 ) { glRectd((double)x1,(double)y1,(double)x2,(double)y2); };
template< > inline void glRect< float > (float x1,float y1,float x2,float y2 ) { glRectf(x1,y1,x2,y2); };
template< > inline void glRect< int > (int x1,int y1,int x2, int y2 ) { glRecti(x1,y1,x2,y2); };
@@ -197,4 +214,4 @@
template< > inline void glMaterialv< Vector3r > ( GLenum face, GLenum pname, const Vector3r params ) { const GLfloat _p[3]={(float) params[0], (float) params[1], (float) params[2]}; glMaterialfv(face,pname,_p); };
template< > inline void glMaterialv< Vector3i > ( GLenum face, GLenum pname, const Vector3i params ) { glMaterialiv(face,pname,(int*)¶ms); };
-
+#undef LDOUBL
=== modified file 'lib/pyutil/README'
--- lib/pyutil/README 2010-01-01 15:21:30 +0000
+++ lib/pyutil/README 2013-10-16 05:58:05 +0000
@@ -1,2 +1,2 @@
numpy_boost.cpp:
- http://code.google.com/p/numpy-boost/source/checkout (svn rev 2)
+ http://code.google.com/p/numpy-boost/source/checkout (svn rev 9)
=== modified file 'lib/pyutil/numpy_boost.hpp'
--- lib/pyutil/numpy_boost.hpp 2010-01-21 07:53:17 +0000
+++ lib/pyutil/numpy_boost.hpp 2013-10-16 05:58:05 +0000
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2008, Michael Droettboom
+Copyright (c) 2012, Michael Droettboom
All rights reserved.
Licensed under the BSD license.
@@ -33,10 +33,6 @@
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-/*
- Documentation to come
-*/
-
#ifndef __NUMPY_BOOST_HPP__
#define __NUMPY_BOOST_HPP__
@@ -44,10 +40,15 @@
#include <numpy/arrayobject.h>
#include <boost/multi_array.hpp>
#include <boost/cstdint.hpp>
+#include <boost/python.hpp>
#include <complex>
#include <algorithm>
-namespace numpy_boost_detail {
+/* numpy_type_map<T>
+
+ Provides a mapping from C++ datatypes to Numpy type
+ numbers. */
+namespace detail {
template<class T>
class numpy_type_map {
public:
@@ -94,42 +95,87 @@
const int numpy_type_map<boost::int64_t>::typenum = NPY_INT64;
template<>
- const int numpy_type_map<boost::uint64_t>::typenum = NPY_INT64;
+ const int numpy_type_map<boost::uint64_t>::typenum = NPY_UINT64;
}
-class numpy_boost_exception : public std::exception {
+class python_exception : public std::exception {
};
+/* An array that acts like a boost::multi_array, but is backed by the
+ memory of a Numpy array. Provides nice C++ interface to a Numpy
+ array without any copying of the data.
+
+ It may be constructed one of two ways:
+
+ 1) With an existing Numpy array. The boost::multi_array will
+ then have the data, dimensions and strides of the Numpy array.
+
+ 2) With a list of dimensions, in which case a new contiguous
+ Numpy array will be created and the new boost::array will
+ point to it.
+
+ */
template<class T, int NDims>
class numpy_boost : public boost::multi_array_ref<T, NDims>
{
public:
- typedef numpy_boost<T, NDims> self_type;
+ typedef numpy_boost<T, NDims> self_type;
typedef boost::multi_array_ref<T, NDims> super;
- typedef typename super::size_type size_type;
- typedef T* TPtr;
+ typedef typename super::size_type size_type;
+ typedef T* TPtr;
private:
PyArrayObject* array;
- void init_from_array(PyArrayObject* a) {
+ void init_from_array(PyArrayObject* a) throw() {
+ /* Upon calling init_from_array, a should already have been
+ incref'd for ownership by this object. */
+
+ /* Store a reference to the Numpy array so we can DECREF it in the
+ destructor. */
array = a;
+
+ /* Point the boost::array at the Numpy array data.
+
+ We don't need to worry about free'ing this pointer, because it
+ will always point to memory allocated as part of the data of a
+ Numpy array. That memory is managed by Python reference
+ counting. */
super::base_ = (TPtr)PyArray_DATA(a);
- // It would seem like we would want to choose C or Fortran
- // ordering here based on the flags in the Numpy array. However,
- // those flags are purely informational, the actually information
- // about storage order is recorded in the strides.
+ /* Set the storage order.
+
+ It would seem like we would want to choose C or Fortran
+ ordering here based on the flags in the Numpy array. However,
+ those flags are purely informational, the actually information
+ about storage order is recorded in the strides. */
super::storage_ = boost::c_storage_order();
+ /* Copy the dimensions from the Numpy array to the boost::array. */
boost::detail::multi_array::copy_n(PyArray_DIMS(a), NDims, super::extent_list_.begin());
+
+ /* Copy the strides from the Numpy array to the boost::array.
+
+ Numpy strides are in bytes. boost::array strides are in
+ elements, so we need to divide. */
for (size_t i = 0; i < NDims; ++i) {
super::stride_list_[i] = PyArray_STRIDE(a, i) / sizeof(T);
}
+
+ /* index_base_list_ stores the bases of the indices in each
+ dimension. Since we want C-style and Numpy-style zero-based
+ indexing, just fill it with zeros. */
std::fill_n(super::index_base_list_.begin(), NDims, 0);
+
+ /* We don't want any additional offsets. If they exist, Numpy has
+ already handled that for us when calculating the data pointer
+ and strides. */
super::origin_offset_ = 0;
super::directional_offset_ = 0;
+
+ /* Calculate the number of elements. This has nothing to do with
+ memory layout. */
super::num_elements_ = std::accumulate(super::extent_list_.begin(),
super::extent_list_.end(),
size_type(1),
@@ -137,32 +183,35 @@
}
public:
- numpy_boost(PyObject* obj) :
- super(NULL, std::vector<boost::uint32_t>(NDims, 0)),
+ /* Construct from an existing Numpy array */
+ numpy_boost(PyObject* obj) throw () :
+ super(NULL, std::vector<typename super::index>(NDims, 0)),
array(NULL)
{
PyArrayObject* a;
- a = (PyArrayObject*)PyArray_FromObject(obj, numpy_boost_detail::numpy_type_map<T>::typenum, NDims, NDims);
+ a = (PyArrayObject*)PyArray_FromObject(
+ obj, detail::numpy_type_map<T>::typenum, NDims, NDims);
if (a == NULL) {
- // TODO: Extract Python exception
- throw numpy_boost_exception();
+ throw boost::python::error_already_set();
}
init_from_array(a);
}
- numpy_boost(const self_type &other) :
- super(NULL, std::vector<boost::uint32_t>(NDims, 0)),
+ /* Copy constructor */
+ numpy_boost(const self_type &other) throw() :
+ super(NULL, std::vector<typename super::index>(NDims, 0)),
array(NULL)
{
Py_INCREF(other.array);
init_from_array(other.array);
}
+ /* Construct a new array based on the given dimensions */
template<class ExtentsList>
- explicit numpy_boost(const ExtentsList& extents) :
- super(NULL, std::vector<boost::uint32_t>(NDims, 0)),
+ explicit numpy_boost(const ExtentsList& extents) throw () :
+ super(NULL, std::vector<typename super::index>(NDims, 0)),
array(NULL)
{
npy_intp shape[NDims];
@@ -170,28 +219,32 @@
boost::detail::multi_array::copy_n(extents, NDims, shape);
- a = (PyArrayObject*)PyArray_SimpleNew(NDims, shape, numpy_boost_detail::numpy_type_map<T>::typenum);
+ a = (PyArrayObject*)PyArray_SimpleNew(
+ NDims, shape, detail::numpy_type_map<T>::typenum);
if (a == NULL) {
- // TODO: Extract Python exception
- throw numpy_boost_exception();
+ throw boost::python::error_already_set();
}
init_from_array(a);
}
+ /* Destructor */
~numpy_boost() {
- Py_DECREF(array);
+ /* Dereference the numpy array. */
+ Py_XDECREF(array);
}
- void operator=(const self_type &other) {
- Py_DECREF(array);
+ /* Assignment operator */
+ void operator=(const self_type &other) throw() {
Py_INCREF(other.array);
+ Py_DECREF(array);
init_from_array(other.array);
}
+ /* Return the underlying Numpy array object. [Borrowed
+ reference] */
PyObject*
- py_ptr() {
- Py_INCREF(array);
+ py_ptr() const throw() {
return (PyObject*)array;
}
};
=== modified file 'lib/triangulation/FlowBoundingSphere.hpp'
--- lib/triangulation/FlowBoundingSphere.hpp 2013-07-26 18:16:04 +0000
+++ lib/triangulation/FlowBoundingSphere.hpp 2013-10-25 07:27:55 +0000
@@ -1,171 +1,175 @@
-/*************************************************************************
-* Copyright (C) 2009 by Emanuele Catalano <catalano@xxxxxxxxxxxxxxx> *
-* Copyright (C) 2009 by Bruno Chareyre <bruno.chareyre@xxxxxxxxxxx> *
-* Copyright (C) 2012 by Donia Marzougui <donia.marzougui@xxxxxxxxxxxxxxx>*
-* *
-* This program is free software; it is licensed under the terms of the *
-* GNU General Public License v2 or later. See file LICENSE for details. *
-*************************************************************************/
-#ifdef FLOW_ENGINE
-
-#ifndef _FLOWBOUNDINGSPHERE_H
-#define _FLOWBOUNDINGSPHERE_H
-
-#include "Network.hpp"
-#include "Timer.h"
-#include "basicVTKwritter.hpp"
-#include "Timer.h"
-
-typedef pair<pair<int,int>, vector<double> > Constriction;
-
-using namespace std;
-namespace CGT {
-
-template<class _Tesselation>
-class FlowBoundingSphere : public Network<_Tesselation>
-{
- public:
- typedef _Tesselation Tesselation;
- typedef Network<Tesselation> _N;
- DECLARE_TESSELATION_TYPES(Network<Tesselation>)
-
- //painfull, but we need that for templates inheritance...
- using _N::T; using _N::x_min; using _N::x_max; using _N::y_min; using _N::y_max; using _N::z_min; using _N::z_max; using _N::Rmoy; using _N::SectionArea; using _N::Height; using _N::Vtotale; using _N::currentTes; using _N::DEBUG_OUT; using _N::nOfSpheres; using _N::x_min_id; using _N::x_max_id; using _N::y_min_id; using _N::y_max_id; using _N::z_min_id; using _N::z_max_id; using _N::boundsIds; using _N::Corner_min; using _N::Corner_max; using _N::Vsolid_tot; using _N::Vtotalissimo; using _N::Vporale; using _N::Ssolid_tot; using _N::V_porale_porosity; using _N::V_totale_porosity; using _N::boundaries; using _N::id_offset; using _N::vtk_infinite_vertices; using _N::vtk_infinite_cells; using _N::num_particles; using _N::boundingCells; using _N::facetVertices; using _N::facetNFictious;
- //same for functions
- using _N::Define_fictious_cells; using _N::AddBoundingPlanes; using _N::boundary;
-
- virtual ~FlowBoundingSphere();
- FlowBoundingSphere();
-
- bool SLIP_ON_LATERALS;
-// bool areaR2Permeability;
- double TOLERANCE;
- double RELAX;
- double ks; //Hydraulic Conductivity
- bool clampKValues, meanKStat, distance_correction;
- bool OUTPUT_BOUDARIES_RADII;
- bool noCache;//flag for checking if cached values cell->unitForceVectors have been defined
- bool computedOnce;//flag for checking if current triangulation has been computed at least once
- bool pressureChanged;//are imposed pressures modified (on python side)? When it happens, we have to reApplyBoundaryConditions
- int errorCode;
-
- //Handling imposed pressures/fluxes on elements in the form of {point,value} pairs, IPCells contains the cell handles corresponding to point
- vector<pair<Point,Real> > imposedP;
- vector<Cell_handle> IPCells;
- vector<pair<Point,Real> > imposedF;
- vector<Cell_handle> IFCells;
-
- void initNewTri () {noCache=true; /*isLinearSystemSet=false; areCellsOrdered=false;*/}//set flags after retriangulation
- bool permeability_map;
-
- bool computeAllCells;//exececute computeHydraulicRadius for all facets and all spheres (double cpu time but needed for now in order to define crossSections correctly)
- double K_opt_factor;
- double minKdivKmean;
- double maxKdivKmean;
- int Iterations;
-
- bool RAVERAGE;
- int walls_id[6];
- #define parallel_forces
- #ifdef parallel_forces
- int ompThreads;
- vector< vector<const Vecteur*> > perVertexUnitForce;
- vector< vector<const Real*> > perVertexPressure;
- #endif
- vector <Finite_edges_iterator> Edge_list;
- vector <double> Edge_Surfaces;
- vector <pair<int,int> > Edge_ids;
- vector <Real> edgeNormalLubF;
- vector <Vector3r> viscousShearForces;
- vector <Vector3r> viscousShearTorques;
- vector <Vector3r> normLubForce;
- vector <Matrix3r> viscousBodyStress;
- vector <Matrix3r> lubBodyStress;
-
- void Localize();
- void Compute_Permeability();
- virtual void GaussSeidel (Real dt=0);
- virtual void ResetNetwork();
-
- void Fictious_cells ( );
-
- double k_factor; //permeability moltiplicator
- std::string key; //to give to consolidation files a name with iteration number
- std::vector<double> Pressures; //for automatic write maximum pressures during consolidation
- bool tess_based_force; //allow the force computation method to be chosen from FlowEngine
- Real minPermLength; //min branch length for Poiseuille
-
- double P_SUP, P_INF, P_INS, VISCOSITY;
- double fluidBulkModulus;
-
- Tesselation& Compute_Action ( );
- Tesselation& Compute_Action ( int argc, char *argv[ ], char *envp[ ] );
- Tesselation& LoadPositions(int argc, char *argv[ ], char *envp[ ]);
- void SpheresFileCreator ();
- void DisplayStatistics();
- void Initialize_pressures ( double P_zero );
- bool reApplyBoundaryConditions ();
- /// Define forces using the same averaging volumes as for permeability
- void ComputeTetrahedralForces();
- /// Define forces spliting drag and buoyancy terms
- void ComputeFacetForcesWithCache(bool onlyCache=false);
- void saveVtk ( );
-#ifdef XVIEW
- void Dessine_Triangulation ( Vue3D &Vue, RTriangulation &T );
- void Dessine_Short_Tesselation ( Vue3D &Vue, Tesselation &Tes );
-#endif
- double Permeameter ( double P_Inf, double P_Sup, double Section, double DeltaY, const char *file );
- double Sample_Permeability( double& x_Min,double& x_Max ,double& y_Min,double& y_Max,double& z_Min,double& z_Max);
- double Compute_HydraulicRadius (Cell_handle cell, int j );
- Real checkSphereFacetOverlap(const Sphere& v0, const Sphere& v1, const Sphere& v2);
-
- double dotProduct ( Vecteur x, Vecteur y );
- double Compute_EffectiveRadius(Cell_handle cell, int j);
- double Compute_EquivalentRadius(Cell_handle cell, int j);
- //return the list of constriction values
- vector<double> getConstrictions();
- vector<Constriction> getConstrictionsFull();
-
- void GenerateVoxelFile ( );
-
- void computeEdgesSurfaces();
- Vector3r computeViscousShearForce(const Vector3r& deltaV, const int& edge_id, const Real& Rh);
- Real computeNormalLubricationForce(const Real& deltaNormV, const Real& dist, const int& edge_id, const Real& eps, const Real& stiffness, const Real& dt, const Real& meanRad);
- Vector3r computeShearLubricationForce(const Vector3r& deltaShearV, const Real& dist, const int& edge_id, const Real& eps, const Real& centerDist, const Real& meanRad);
-
- RTriangulation& Build_Triangulation ( Real x, Real y, Real z, Real radius, unsigned const id );
-
- bool isInsideSphere ( double& x, double& y, double& z );
-
- void SliceField (const char *filename);
- void ComsolField();
-
- void Interpolate ( Tesselation& Tes, Tesselation& NewTes );
- virtual void Average_Relative_Cell_Velocity();
- void Average_Fluid_Velocity();
- void ApplySinusoidalPressure(RTriangulation& Tri, double Amplitude, double Average_Pressure, double load_intervals);
- bool isOnSolid (double X, double Y, double Z);
- double getPorePressure (double X, double Y, double Z);
- void measurePressureProfile(double Wall_up_y, double Wall_down_y);
- double averageSlicePressure(double Y);
- double averagePressure();
- double getCell (double X,double Y,double Z);
- double boundaryFlux(unsigned int boundaryId);
-
- vector<Real> Average_Fluid_Velocity_On_Sphere(unsigned int Id_sph);
- //Solver?
- int useSolver;//(0 : GaussSeidel, 1 : TAUCS, 2 : PARDISO, 3:CHOLMOD)
-};
-
-} //namespace CGT
-
-#ifdef LINSOLV
-#include "yade/lib/triangulation/FlowBoundingSphereLinSolv.hpp"
-#endif
-
-/// _____ Template Implementation ____
-#include "yade/lib/triangulation/FlowBoundingSphere.ipp"
-
-#endif //FLOW_ENGINE
-
-#endif
+/*************************************************************************
+* Copyright (C) 2009 by Emanuele Catalano <catalano@xxxxxxxxxxxxxxx> *
+* Copyright (C) 2009 by Bruno Chareyre <bruno.chareyre@xxxxxxxxxxx> *
+* Copyright (C) 2012 by Donia Marzougui <donia.marzougui@xxxxxxxxxxxxxxx>*
+* *
+* This program is free software; it is licensed under the terms of the *
+* GNU General Public License v2 or later. See file LICENSE for details. *
+*************************************************************************/
+#ifdef FLOW_ENGINE
+
+#pragma once
+
+#include "Network.hpp"
+#include "Timer.h"
+#include "basicVTKwritter.hpp"
+#include "Timer.h"
+
+typedef pair<pair<int,int>, vector<double> > Constriction;
+
+using namespace std;
+namespace CGT {
+
+template<class _Tesselation>
+class FlowBoundingSphere : public Network<_Tesselation>
+{
+ public:
+ typedef _Tesselation Tesselation;
+ typedef Network<Tesselation> _N;
+ DECLARE_TESSELATION_TYPES(Network<Tesselation>)
+
+ //painfull, but we need that for templates inheritance...
+ using _N::T; using _N::x_min; using _N::x_max; using _N::y_min; using _N::y_max; using _N::z_min; using _N::z_max; using _N::Rmoy; using _N::SectionArea; using _N::Height; using _N::Vtotale; using _N::currentTes; using _N::DEBUG_OUT; using _N::nOfSpheres; using _N::x_min_id; using _N::x_max_id; using _N::y_min_id; using _N::y_max_id; using _N::z_min_id; using _N::z_max_id; using _N::boundsIds; using _N::Corner_min; using _N::Corner_max; using _N::Vsolid_tot; using _N::Vtotalissimo; using _N::Vporale; using _N::Ssolid_tot; using _N::V_porale_porosity; using _N::V_totale_porosity; using _N::boundaries; using _N::id_offset; using _N::vtk_infinite_vertices; using _N::vtk_infinite_cells; using _N::num_particles; using _N::boundingCells; using _N::facetVertices; using _N::facetNFictious;
+ //same for functions
+ using _N::Define_fictious_cells; using _N::AddBoundingPlanes; using _N::boundary;
+
+ virtual ~FlowBoundingSphere();
+ FlowBoundingSphere();
+
+ bool SLIP_ON_LATERALS;
+// bool areaR2Permeability;
+ double TOLERANCE;
+ double RELAX;
+ double ks; //Hydraulic Conductivity
+ bool clampKValues, meanKStat, distance_correction;
+ bool OUTPUT_BOUDARIES_RADII;
+ bool noCache;//flag for checking if cached values cell->unitForceVectors have been defined
+ bool computedOnce;//flag for checking if current triangulation has been computed at least once
+ bool pressureChanged;//are imposed pressures modified (on python side)? When it happens, we have to reApplyBoundaryConditions
+ int errorCode;
+
+ //Handling imposed pressures/fluxes on elements in the form of {point,value} pairs, IPCells contains the cell handles corresponding to point
+ vector<pair<Point,Real> > imposedP;
+ vector<Cell_handle> IPCells;
+ vector<pair<Point,Real> > imposedF;
+ vector<Cell_handle> IFCells;
+
+ void initNewTri () {noCache=true; /*isLinearSystemSet=false; areCellsOrdered=false;*/}//set flags after retriangulation
+ bool permeability_map;
+
+ bool computeAllCells;//exececute computeHydraulicRadius for all facets and all spheres (double cpu time but needed for now in order to define crossSections correctly)
+ double K_opt_factor;
+ double minKdivKmean;
+ double maxKdivKmean;
+ int Iterations;
+
+ bool RAVERAGE;
+ int walls_id[6];
+ #define parallel_forces
+ #ifdef parallel_forces
+ int ompThreads;
+ vector< vector<const Vecteur*> > perVertexUnitForce;
+ vector< vector<const Real*> > perVertexPressure;
+ #endif
+ vector <Finite_edges_iterator> Edge_list;
+ vector <double> Edge_Surfaces;
+ vector <pair<int,int> > Edge_ids;
+ vector <Real> edgeNormalLubF;
+ vector <Vector3r> viscousShearForces;
+ vector <Vector3r> viscousShearTorques;
+ vector <Vector3r> normLubForce;
+ vector <Matrix3r> viscousBodyStress;
+ vector <Matrix3r> lubBodyStress;
+ vector <Vector3r> deltaNormVel;
+ vector <Vector3r> deltaShearVel;
+ vector <Vector3r> normalV;
+ vector <Real> surfaceDistance;
+ vector <int> onlySpheresInteractions;
+ vector <Matrix3r> shearStressInteraction;
+ vector <Matrix3r> normalStressInteraction;
+
+ void Localize();
+ void Compute_Permeability();
+ virtual void GaussSeidel (Real dt=0);
+ virtual void ResetNetwork();
+
+ void Fictious_cells ( );
+
+ double k_factor; //permeability moltiplicator
+ std::string key; //to give to consolidation files a name with iteration number
+ std::vector<double> Pressures; //for automatic write maximum pressures during consolidation
+ bool tess_based_force; //allow the force computation method to be chosen from FlowEngine
+ Real minPermLength; //min branch length for Poiseuille
+
+ double P_SUP, P_INF, P_INS, VISCOSITY;
+ double fluidBulkModulus;
+
+ Tesselation& Compute_Action ( );
+ Tesselation& Compute_Action ( int argc, char *argv[ ], char *envp[ ] );
+ Tesselation& LoadPositions(int argc, char *argv[ ], char *envp[ ]);
+ void SpheresFileCreator ();
+ void DisplayStatistics();
+ void Initialize_pressures ( double P_zero );
+ bool reApplyBoundaryConditions ();
+ /// Define forces using the same averaging volumes as for permeability
+ void ComputeTetrahedralForces();
+ /// Define forces spliting drag and buoyancy terms
+ void ComputeFacetForcesWithCache(bool onlyCache=false);
+ void saveVtk ( );
+#ifdef XVIEW
+ void Dessine_Triangulation ( Vue3D &Vue, RTriangulation &T );
+ void Dessine_Short_Tesselation ( Vue3D &Vue, Tesselation &Tes );
+#endif
+ double Permeameter ( double P_Inf, double P_Sup, double Section, double DeltaY, const char *file );
+ double Sample_Permeability( double& x_Min,double& x_Max ,double& y_Min,double& y_Max,double& z_Min,double& z_Max);
+ double Compute_HydraulicRadius (Cell_handle cell, int j );
+ Real checkSphereFacetOverlap(const Sphere& v0, const Sphere& v1, const Sphere& v2);
+
+ double dotProduct ( Vecteur x, Vecteur y );
+ double Compute_EffectiveRadius(Cell_handle cell, int j);
+ double Compute_EquivalentRadius(Cell_handle cell, int j);
+ //return the list of constriction values
+ vector<double> getConstrictions();
+ vector<Constriction> getConstrictionsFull();
+
+ void GenerateVoxelFile ( );
+
+ void computeEdgesSurfaces();
+ Vector3r computeViscousShearForce(const Vector3r& deltaV, const int& edge_id, const Real& Rh);
+ Real computeNormalLubricationForce(const Real& deltaNormV, const Real& dist, const int& edge_id, const Real& eps, const Real& stiffness, const Real& dt, const Real& meanRad);
+ Vector3r computeShearLubricationForce(const Vector3r& deltaShearV, const Real& dist, const int& edge_id, const Real& eps, const Real& centerDist, const Real& meanRad);
+
+ RTriangulation& Build_Triangulation ( Real x, Real y, Real z, Real radius, unsigned const id );
+
+ bool isInsideSphere ( double& x, double& y, double& z );
+
+ void SliceField (const char *filename);
+ void ComsolField();
+
+ void Interpolate ( Tesselation& Tes, Tesselation& NewTes );
+ virtual void Average_Relative_Cell_Velocity();
+ void Average_Fluid_Velocity();
+ void ApplySinusoidalPressure(RTriangulation& Tri, double Amplitude, double Average_Pressure, double load_intervals);
+ bool isOnSolid (double X, double Y, double Z);
+ double getPorePressure (double X, double Y, double Z);
+ void measurePressureProfile(double Wall_up_y, double Wall_down_y);
+ double averageSlicePressure(double Y);
+ double averagePressure();
+ double getCell (double X,double Y,double Z);
+ double boundaryFlux(unsigned int boundaryId);
+
+ vector<Real> Average_Fluid_Velocity_On_Sphere(unsigned int Id_sph);
+ //Solver?
+ int useSolver;//(0 : GaussSeidel, 1 : TAUCS, 2 : PARDISO, 3:CHOLMOD)
+};
+
+} //namespace CGT
+
+#ifdef LINSOLV
+#include "yade/lib/triangulation/FlowBoundingSphereLinSolv.hpp"
+#endif
+
+/// _____ Template Implementation ____
+#include "yade/lib/triangulation/FlowBoundingSphere.ipp"
+
+#endif //FLOW_ENGINE
=== modified file 'lib/triangulation/Network.hpp'
--- lib/triangulation/Network.hpp 2013-07-26 13:52:11 +0000
+++ lib/triangulation/Network.hpp 2013-08-22 14:32:01 +0000
@@ -6,9 +6,7 @@
*************************************************************************/
#ifdef FLOW_ENGINE
-#ifndef _NETWORK_H
-#define _NETWORK_H
-#endif
+#pragma once
#include "Tesselation.h"
#include "Timer.h"
=== modified file 'lib/triangulation/PeriodicFlow.hpp'
--- lib/triangulation/PeriodicFlow.hpp 2012-12-03 08:19:32 +0000
+++ lib/triangulation/PeriodicFlow.hpp 2013-08-22 14:32:01 +0000
@@ -1,7 +1,6 @@
#ifdef FLOW_ENGINE
-#ifndef _PERIFLOWBOUNDINGSPHERE_H
-#define _PERIFLOWBOUNDINGSPHERE_H
+#pragma once
#include<yade/lib/triangulation/FlowBoundingSphere.hpp>//include after #define XVIEW
// #include "Timer.h"
@@ -44,5 +43,3 @@
#endif //FLOW_ENGINE
-
-#endif
=== modified file 'lib/triangulation/PeriodicFlowLinSolv.hpp'
--- lib/triangulation/PeriodicFlowLinSolv.hpp 2013-06-27 11:29:17 +0000
+++ lib/triangulation/PeriodicFlowLinSolv.hpp 2013-08-22 14:32:01 +0000
@@ -1,38 +1,36 @@
-/*************************************************************************
-* Copyright (C) 2010 by Bruno Chareyre <bruno.chareyre@xxxxxxxxxxx> *
-* *
-* This program is free software; it is licensed under the terms of the *
-* GNU General Public License v2 or later. See file LICENSE for details. *
-*************************************************************************/
-
-#ifndef _PERIODICFLOWLINSOLV_H
-#define _PERIODICFLOWLINSOLV_H
-#include "FlowBoundingSphere.hpp"
-
-#ifdef FLOW_ENGINE
-
-using namespace std;
-
-namespace CGT {
-
-typedef FlowBoundingSphereLinSolv<PeriodicFlow> LinSolver;
-
-class PeriodicFlowLinSolv : public LinSolver
-{
-public:
- typedef PeriodicFlow FlowType;
- vector<int> indices;//redirection vector containing the rank of cell so that T_cells[indices[cell->info().index]]=cell
-
- virtual ~PeriodicFlowLinSolv();
- PeriodicFlowLinSolv();
-
- ///Linear system solve
- virtual int SetLinearSystem(Real dt=0);
- virtual int SetLinearSystemFullGS(Real dt=0);
-};
-
-} //namespace CGTF
-
-#endif //FLOW_ENGINE
-
-#endif
+/*************************************************************************
+* Copyright (C) 2010 by Bruno Chareyre <bruno.chareyre@xxxxxxxxxxx> *
+* *
+* This program is free software; it is licensed under the terms of the *
+* GNU General Public License v2 or later. See file LICENSE for details. *
+*************************************************************************/
+
+#pragma once
+
+#include "FlowBoundingSphere.hpp"
+
+#ifdef FLOW_ENGINE
+
+using namespace std;
+
+namespace CGT {
+
+typedef FlowBoundingSphereLinSolv<PeriodicFlow> LinSolver;
+
+class PeriodicFlowLinSolv : public LinSolver
+{
+public:
+ typedef PeriodicFlow FlowType;
+ vector<int> indices;//redirection vector containing the rank of cell so that T_cells[indices[cell->info().index]]=cell
+
+ virtual ~PeriodicFlowLinSolv();
+ PeriodicFlowLinSolv();
+
+ ///Linear system solve
+ virtual int SetLinearSystem(Real dt=0);
+ virtual int SetLinearSystemFullGS(Real dt=0);
+};
+
+} //namespace CGTF
+
+#endif //FLOW_ENGINE
=== modified file 'lib/triangulation/RegularTriangulation.h'
--- lib/triangulation/RegularTriangulation.h 2013-07-26 14:55:13 +0000
+++ lib/triangulation/RegularTriangulation.h 2013-11-20 08:40:45 +0000
@@ -51,4 +51,3 @@
typedef TriangulationTypes<UnsatVertexInfo,UnsatCellInfo> UnsatFlowTriangulationTypes;
} // namespace CGT
-
=== modified file 'lib/triangulation/Tenseur3.h'
--- lib/triangulation/Tenseur3.h 2012-01-20 17:31:56 +0000
+++ lib/triangulation/Tenseur3.h 2013-08-22 14:32:01 +0000
@@ -1,116 +1,113 @@
-#ifndef TENSEUR3_H
-#define TENSEUR3_H
-
-#include "def_types.h"
-#include <iostream>
-#include <fstream>
-
-namespace CGT {
-
-#define NORMALIZE(vecteur) ((vecteur) = (vecteur)*(1.0/sqrt(pow((vecteur)[0],2)+pow((vecteur)[1],2)+pow((vecteur)[2],2))))
-
-class Tens;
-class Tenseur3;
-class Tenseur_sym3;
-class Tenseur_anti3;
-
-Vecteur operator* ( Tens& tens, Vecteur& vect );
-Vecteur& NormalizedVecteur ( Vecteur& vect );
-
-
-void Tenseur_produit ( Vecteur &v1, Vecteur &v2, Tenseur3 &result );
-void Somme ( Tenseur3 &result, Vecteur &v1, Vecteur &v2 );
-
-std::ostream& operator<< ( std::ostream& os,const Tenseur3& T );
-std::ostream& operator<< ( std::ostream& os,const Tenseur_sym3& T );
-std::ostream& operator<< ( std::ostream& os,const Tenseur_anti3& T );
-
-class Tens
-{
- public:
- Tens ( void ) {}
- virtual ~Tens ( void ) {}
- virtual Real operator() ( int i, int j ) const {return 0;}
- Real Norme2 ( void );
- Real Norme ( void ) {return sqrt ( Norme2() );}
- Real Trace ( void )
- {
- return this->operator () ( 1,1 )
- + this->operator () ( 2,2 )
- + this->operator () ( 3,3 );
- }
-};
-
-class Tenseur3 : public Tens
-{
- private:
- Real T [3] [3];
-
- public:
- Tenseur3 ( bool init = true );// Sp�cifier "false" pour �conomiser le temps d'initialisation du tableau
- virtual ~Tenseur3 ( void );
- Tenseur3 ( const Tenseur3& source );
- Tenseur3 ( Real a11, Real a12, Real a13,
- Real a21, Real a22, Real a23,
- Real a31, Real a32, Real a33 );
-
- Tenseur3& operator= ( const Tenseur3& source );
- Tenseur3& operator/= ( Real d );
- Tenseur3& operator+= ( const Tenseur3& source );
- Real operator() ( int i, int j ) const {return T[i-1][j-1];}
- Real &operator() ( int i, int j ) {return T[i-1][j-1];}
-
- virtual void reset ( void ) {for ( int i=0; i<3; i++ ) for ( int j=0; j<3; j++ ) T[i][j] = 0;}
-
-};
-
-class Tenseur_sym3 : public Tens
-{
- private:
- Real T [6];
-
- public:
- Tenseur_sym3 ( bool init = true );// Sp�cifier "false" pour �conomiser le temps d'initialisation du tableau
- ~Tenseur_sym3 ( void );
- Tenseur_sym3 ( const Tenseur_sym3& source );
- Tenseur_sym3 ( const Tenseur3& source );
- Tenseur_sym3 ( Real a11, Real a22, Real a33,
- Real a12, Real a13, Real a23 );
-
- Tenseur_sym3& operator= ( const Tenseur_sym3& source );
- Tenseur_sym3& operator/= ( Real d );
- Tenseur_sym3 Deviatoric ( void ) const; //retourne la partie d�viatoire
- Real operator() ( int i, int j ) const;
- Real &operator() ( int i, int j );
-
- void reset ( void ) {for ( int i=0; i<6; i++ ) T[i] = 0;}
-
-};
-
-class Tenseur_anti3 : public Tens
-{
- private:
- Real T [6];
-
- public:
- Tenseur_anti3 ( bool init = true );// Sp�cifier "false" pour �conomiser le temps d'initialisation du tableau
- virtual ~Tenseur_anti3 ( void );
- Tenseur_anti3 ( const Tenseur_anti3& source );
- Tenseur_anti3 ( const Tenseur3& source );
- Tenseur_anti3 ( Real a11, Real a22, Real a33,
- Real a12, Real a13, Real a23 );
-
- Tenseur_anti3& operator= ( const Tenseur_anti3& source );
- Tenseur_anti3& operator/= ( Real d );
- Real operator() ( int i, int j ) const;
- //Real &operator() (int i, int j); //Supprim�e car pb. pour retourner une r�f�rence vers -T[i+j]
-
- void reset ( void ) {for ( int i=0; i<6; i++ ) T[i] = 0;}
-
-};
-
-static const Tenseur3 NULL_TENSEUR3 ( 0,0,0,0,0,0,0,0,0 );
-
-}
-
-#endif
+#pragma once
+
+#include "def_types.h"
+#include <iostream>
+#include <fstream>
+
+namespace CGT {
+
+#define NORMALIZE(vecteur) ((vecteur) = (vecteur)*(1.0/sqrt(pow((vecteur)[0],2)+pow((vecteur)[1],2)+pow((vecteur)[2],2))))
+
+class Tens;
+class Tenseur3;
+class Tenseur_sym3;
+class Tenseur_anti3;
+
+Vecteur operator* ( Tens& tens, Vecteur& vect );
+Vecteur& NormalizedVecteur ( Vecteur& vect );
+
+
+void Tenseur_produit ( Vecteur &v1, Vecteur &v2, Tenseur3 &result );
+void Somme ( Tenseur3 &result, Vecteur &v1, Vecteur &v2 );
+
+std::ostream& operator<< ( std::ostream& os,const Tenseur3& T );
+std::ostream& operator<< ( std::ostream& os,const Tenseur_sym3& T );
+std::ostream& operator<< ( std::ostream& os,const Tenseur_anti3& T );
+
+class Tens
+{
+ public:
+ Tens ( void ) {}
+ virtual ~Tens ( void ) {}
+ virtual Real operator() ( int i, int j ) const {return 0;}
+ Real Norme2 ( void );
+ Real Norme ( void ) {return sqrt ( Norme2() );}
+ Real Trace ( void )
+ {
+ return this->operator () ( 1,1 )
+ + this->operator () ( 2,2 )
+ + this->operator () ( 3,3 );
+ }
+};
+
+class Tenseur3 : public Tens
+{
+ private:
+ Real T [3] [3];
+
+ public:
+ Tenseur3 ( bool init = true );// Sp�cifier "false" pour �conomiser le temps d'initialisation du tableau
+ virtual ~Tenseur3 ( void );
+ Tenseur3 ( const Tenseur3& source );
+ Tenseur3 ( Real a11, Real a12, Real a13,
+ Real a21, Real a22, Real a23,
+ Real a31, Real a32, Real a33 );
+
+ Tenseur3& operator= ( const Tenseur3& source );
+ Tenseur3& operator/= ( Real d );
+ Tenseur3& operator+= ( const Tenseur3& source );
+ Real operator() ( int i, int j ) const {return T[i-1][j-1];}
+ Real &operator() ( int i, int j ) {return T[i-1][j-1];}
+
+ virtual void reset ( void ) {for ( int i=0; i<3; i++ ) for ( int j=0; j<3; j++ ) T[i][j] = 0;}
+
+};
+
+class Tenseur_sym3 : public Tens
+{
+ private:
+ Real T [6];
+
+ public:
+ Tenseur_sym3 ( bool init = true );// Sp�cifier "false" pour �conomiser le temps d'initialisation du tableau
+ ~Tenseur_sym3 ( void );
+ Tenseur_sym3 ( const Tenseur_sym3& source );
+ Tenseur_sym3 ( const Tenseur3& source );
+ Tenseur_sym3 ( Real a11, Real a22, Real a33,
+ Real a12, Real a13, Real a23 );
+
+ Tenseur_sym3& operator= ( const Tenseur_sym3& source );
+ Tenseur_sym3& operator/= ( Real d );
+ Tenseur_sym3 Deviatoric ( void ) const; //retourne la partie d�viatoire
+ Real operator() ( int i, int j ) const;
+ Real &operator() ( int i, int j );
+
+ void reset ( void ) {for ( int i=0; i<6; i++ ) T[i] = 0;}
+
+};
+
+class Tenseur_anti3 : public Tens
+{
+ private:
+ Real T [6];
+
+ public:
+ Tenseur_anti3 ( bool init = true );// Sp�cifier "false" pour �conomiser le temps d'initialisation du tableau
+ virtual ~Tenseur_anti3 ( void );
+ Tenseur_anti3 ( const Tenseur_anti3& source );
+ Tenseur_anti3 ( const Tenseur3& source );
+ Tenseur_anti3 ( Real a11, Real a22, Real a33,
+ Real a12, Real a13, Real a23 );
+
+ Tenseur_anti3& operator= ( const Tenseur_anti3& source );
+ Tenseur_anti3& operator/= ( Real d );
+ Real operator() ( int i, int j ) const;
+ //Real &operator() (int i, int j); //Supprim�e car pb. pour retourner une r�f�rence vers -T[i+j]
+
+ void reset ( void ) {for ( int i=0; i<6; i++ ) T[i] = 0;}
+
+};
+
+static const Tenseur3 NULL_TENSEUR3 ( 0,0,0,0,0,0,0,0,0 );
+
+}
=== modified file 'lib/triangulation/TriaxialState.h'
--- lib/triangulation/TriaxialState.h 2012-02-14 16:58:21 +0000
+++ lib/triangulation/TriaxialState.h 2013-08-22 14:32:01 +0000
@@ -9,9 +9,7 @@
/**
@author Bruno Chareyre
*/
-#ifndef TRIAXIALSTATE_H
-#define TRIAXIALSTATE_H
-
+#pragma once
#include "Tesselation.h"
#include <vector>
@@ -108,4 +106,3 @@
};
} // namespace CGT
-#endif
=== modified file 'lib/triangulation/def_types.h'
--- lib/triangulation/def_types.h 2013-11-19 08:34:00 +0000
+++ lib/triangulation/def_types.h 2013-11-20 08:40:45 +0000
@@ -8,8 +8,7 @@
#include <yade/lib/base/Math.hpp>
//Define basic types from CGAL templates
-#ifndef _Def_types
-#define _Def_types
+#pragma once
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Cartesian.h>
@@ -120,7 +119,7 @@
RayHydr.resize(4, 0);
// isInside = false;
inv_sum_k=0;
- isFictious=false; Pcondition = false; isGhost = false;
+ isFictious=false; Pcondition = false; isGhost = false;
// isInferior = false; isSuperior = false; isLateral = false; isExternal=false;
isvisited = false;
index=0;
@@ -219,7 +218,6 @@
virtual bool isReal (void) {return !(isFictious || isGhost);}
};
-
class UnsatCellInfo : public FlowCellInfo {
public:
UnsatCellInfo& operator= (const Point &p) { Point::operator= (p); return *this; }
@@ -246,6 +244,4 @@
}
};
-} // namespace CGT
-
-#endif
+} // namespace CGT
\ No newline at end of file
=== modified file 'pkg/common/Cylinder.cpp'
--- pkg/common/Cylinder.cpp 2013-03-06 20:47:43 +0000
+++ pkg/common/Cylinder.cpp 2013-08-23 15:21:20 +0000
@@ -430,10 +430,10 @@
//FIXME : contact following have to be done for parallel cylinders.
colinearVectors=1;
insideCyl1=1 ; insideCyl2=1;
- Real PA=(A-B).dot(b)/(b.norm()*b.norm()); PA=min(1.0,max(0.0,PA));
- Real Pa=(A+a-B).dot(b)/(b.norm()*b.norm()); Pa=min(1.0,max(0.0,Pa));
- Real PB=(B-A).dot(a)/(a.norm()*a.norm()); PB=min(1.0,max(0.0,PB));
- Real Pb=(B+b-A).dot(a)/(a.norm()*a.norm()); Pb=min(1.0,max(0.0,Pb));
+ Real PA=(A-B).dot(b)/(b.norm()*b.norm()); PA=min((Real)1.0,max((Real)0.0,PA));
+ Real Pa=(A+a-B).dot(b)/(b.norm()*b.norm()); Pa=min((Real)1.0,max((Real)0.0,Pa));
+ Real PB=(B-A).dot(a)/(a.norm()*a.norm()); PB=min((Real)1.0,max((Real)0.0,PB));
+ Real Pb=(B+b-A).dot(a)/(a.norm()*a.norm()); Pb=min((Real)1.0,max((Real)0.0,Pb));
Real h1=(A+0.5*a-B).dot(b)/(b.norm()*b.norm()); //Projection parameter of center of a on b
Real h2=(B+0.5*b-A).dot(a)/(a.norm()*a.norm()); //Projection parameter of center of b on a
=== modified file 'pkg/common/Cylinder.hpp'
--- pkg/common/Cylinder.hpp 2013-05-29 09:48:51 +0000
+++ pkg/common/Cylinder.hpp 2013-08-29 10:30:31 +0000
@@ -265,7 +265,7 @@
public:
//OpenMPAccumulator<Real> plasticDissipation;
virtual void go(shared_ptr<IGeom>& _geom, shared_ptr<IPhys>& _phys, Interaction* I);
- YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(Law2_ChCylGeom6D_CohFrictPhys_CohesionMoment,LawFunctor,"Law for linear compression, and Mohr-Coulomb plasticity surface without cohesion.\nThis law implements the classical linear elastic-plastic law from [CundallStrack1979]_ (see also [Pfc3dManual30]_). The normal force is (with the convention of positive tensile forces) $F_n=\\min(k_n u_n, 0)$. The shear force is $F_s=k_s u_s$, the plasticity condition defines the maximum value of the shear force : $F_s^{\\max}=F_n\\tan(\\phi)$, with $\\phi$ the friction angle.\n\n.. note::\n This law uses :yref:`ScGeom`; there is also functionally equivalent :yref:`Law2_Dem3DofGeom_FrictPhys_CundallStrack`, which uses :yref:`Dem3DofGeom` (sphere-box interactions are not implemented for the latest).\n\n.. note::\n This law is well tested in the context of triaxial simulation, and has been used for a number of published results (see e.g. [Scholtes2009b]_ and other papers from the same authors). It is generalised by :yref:`Law2_ScGeom6D_CohFrictPhys_CohesionMoment`, which adds cohesion and moments at contact.",
+ YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(Law2_ChCylGeom6D_CohFrictPhys_CohesionMoment,LawFunctor,"Law for linear compression, and Mohr-Coulomb plasticity surface without cohesion.\nThis law implements the classical linear elastic-plastic law from [CundallStrack1979]_ (see also [Pfc3dManual30]_). The normal force is (with the convention of positive tensile forces) $F_n=\\min(k_n u_n, 0)$. The shear force is $F_s=k_s u_s$, the plasticity condition defines the maximum value of the shear force : $F_s^{\\max}=F_n\\tan(\\phi)$, with $\\phi$ the friction angle.\n\n.. note::\n This law is well tested in the context of triaxial simulation, and has been used for a number of published results (see e.g. [Scholtes2009b]_ and other papers from the same authors). It is generalised by :yref:`Law2_ScGeom6D_CohFrictPhys_CohesionMoment`, which adds cohesion and moments at contact.",
((bool,neverErase,false,,"Keep interactions even if particles go away from each other (only in case another constitutive law is in the scene, e.g. :yref:`Law2_ScGeom_CapillaryPhys_Capillarity`)"))
((bool,traceEnergy,false,Attr::hidden,"Define the total energy dissipated in plastic slips at all contacts."))
((int,plastDissipIx,-1,(Attr::hidden|Attr::noSave),"Index for plastic dissipation (with O.trackEnergy)"))
=== modified file 'pkg/common/Facet.cpp'
--- pkg/common/Facet.cpp 2013-05-24 16:48:19 +0000
+++ pkg/common/Facet.cpp 2013-09-08 11:12:42 +0000
@@ -28,7 +28,6 @@
normal = e[0].cross(e[1]);
area = .5*normal.norm();
normal /= 2*area;
- //normal.normalize();
for(int i=0; i<3; ++i){
ne[i]=e[i].cross(normal); ne[i].normalize();
vl[i]=vertices[i].norm();
=== modified file 'pkg/common/Facet.hpp'
--- pkg/common/Facet.hpp 2013-05-24 16:48:19 +0000
+++ pkg/common/Facet.hpp 2013-09-08 11:12:42 +0000
@@ -37,7 +37,7 @@
YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(Facet,Shape,"Facet (triangular particle) geometry.",
((vector<Vector3r>,vertices,vector<Vector3r>(3,Vector3r(NaN,NaN,NaN)),(Attr::triggerPostLoad | Attr::noResize),"Vertex positions in local coordinates."))
- ((Vector3r,normal,Vector3r(NaN,NaN,NaN),(Attr::readonly | Attr::noSave),"Facet's normal"))
+ ((Vector3r,normal,Vector3r(NaN,NaN,NaN),(Attr::readonly | Attr::noSave),"Facet's normal (in local coordinate system)"))
((Real,area,NaN,(Attr::readonly | Attr::noSave),"Facet's area"))
#ifdef FACET_TOPO
((vector<Body::id_t>,edgeAdjIds,vector<Body::id_t>(3,Body::ID_NONE),,"Facet id's that are adjacent to respective edges [experimental]"))
=== modified file 'pkg/common/Gl1_NormPhys.cpp'
--- pkg/common/Gl1_NormPhys.cpp 2013-03-07 18:28:01 +0000
+++ pkg/common/Gl1_NormPhys.cpp 2013-08-23 15:21:20 +0000
@@ -8,77 +8,77 @@
#include<yade/pkg/dem/Shop.hpp>
-YADE_PLUGIN((Gl1_NormPhys));
-
-GLUquadric* Gl1_NormPhys::gluQuadric=NULL;
-Real Gl1_NormPhys::maxFn;
-Real Gl1_NormPhys::refRadius;
-Real Gl1_NormPhys::maxRadius;
-int Gl1_NormPhys::signFilter;
-int Gl1_NormPhys::slices;
-int Gl1_NormPhys::stacks;
-
-Real Gl1_NormPhys::maxWeakFn;
-int Gl1_NormPhys::weakFilter;
-Real Gl1_NormPhys::weakScale;
-
-void Gl1_NormPhys::go(const shared_ptr<IPhys>& ip, const shared_ptr<Interaction>& i, const shared_ptr<Body>& b1, const shared_ptr<Body>& b2, bool wireFrame){
- if(!gluQuadric){ gluQuadric=gluNewQuadric(); if(!gluQuadric) throw runtime_error("Gl1_NormPhys::go unable to allocate new GLUquadric object (out of memory?)."); }
- NormPhys* np=static_cast<NormPhys*>(ip.get());
- shared_ptr<IGeom> ig(i->geom); if(!ig) return; // changed meanwhile?
- GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(ig.get());
- //if(!geom) cerr<<"Gl1_NormPhys: IGeom is not a GenericSpheresContact, but a "<<ig->getClassName()<<endl;
- Real fnNorm=np->normalForce.dot(geom->normal);
- if((signFilter>0 && fnNorm<0) || (signFilter<0 && fnNorm>0)) return;
- int fnSign=fnNorm>0?1:-1;
- fnNorm=abs(fnNorm);
- Real radiusScale=1.;
- // weak/strong fabric, only used if maxWeakFn is set
- if(!isnan(maxWeakFn)){
- if(fnNorm*fnSign<maxWeakFn){ // weak fabric
- if(weakFilter>0) return;
- radiusScale=weakScale;
- } else { // strong fabric
- if(weakFilter<0) return;
- }
- }
-
- maxFn=max(fnNorm,maxFn);
- Real realMaxRadius;
- if(maxRadius<0){
- if(geom->refR1>0) refRadius=min(geom->refR1,refRadius);
- if(geom->refR2>0) refRadius=min(geom->refR2,refRadius);
- realMaxRadius=refRadius;
- }
- else realMaxRadius=maxRadius;
- Real radius=radiusScale*realMaxRadius*(fnNorm/maxFn); // use logarithmic scale here?
- Vector3r color=Shop::scalarOnColorScale(fnNorm*fnSign,-maxFn,maxFn);
- # if 0
- // get endpoints from body positions
- Vector3r p1=b1->state->pos, p2=b2->state->pos;
- Vector3r relPos;
- if(scene->isPeriodic){
- relPos=p2+scene->cell->Hsize*i->cellDist.cast<Real>()-p1;
- p1=scene->cell->wrapShearedPt(p1);
- p2=p1+relPos;
- } else {
- relPos=p2-p1;
- }
- Real dist=relPos.norm();
- #else
- // get endpoints from geom
- // max(r,0) handles r<0 which is the case for "radius" of the facet in Dem3DofGeom_FacetSphere
- Vector3r cp=scene->isPeriodic? scene->cell->wrapShearedPt(geom->contactPoint) : geom->contactPoint;
- Vector3r p1=cp-max(geom->refR1,0.)*geom->normal;
- Vector3r p2=cp+max(geom->refR2,0.)*geom->normal;
- const Vector3r& dispScale=scene->renderer ? scene->renderer->dispScale : Vector3r::Ones();
- if(dispScale!=Vector3r::Ones()){
- // move p1 and p2 by the same amounts as particles themselves would be moved
- p1+=dispScale.cwiseProduct(Vector3r(b1->state->pos-b1->state->refPos));
- p2+=dispScale.cwiseProduct(Vector3r(b2->state->pos-b2->state->refPos));
- }
- Vector3r relPos=p2-p1;
- Real dist=relPos.norm(); //max(geom->refR1,0.)+max(geom->refR2,0.);
+ YADE_PLUGIN((Gl1_NormPhys));
+
+ GLUquadric* Gl1_NormPhys::gluQuadric=NULL;
+ Real Gl1_NormPhys::maxFn;
+ Real Gl1_NormPhys::refRadius;
+ Real Gl1_NormPhys::maxRadius;
+ int Gl1_NormPhys::signFilter;
+ int Gl1_NormPhys::slices;
+ int Gl1_NormPhys::stacks;
+
+ Real Gl1_NormPhys::maxWeakFn;
+ int Gl1_NormPhys::weakFilter;
+ Real Gl1_NormPhys::weakScale;
+
+ void Gl1_NormPhys::go(const shared_ptr<IPhys>& ip, const shared_ptr<Interaction>& i, const shared_ptr<Body>& b1, const shared_ptr<Body>& b2, bool wireFrame){
+ if(!gluQuadric){ gluQuadric=gluNewQuadric(); if(!gluQuadric) throw runtime_error("Gl1_NormPhys::go unable to allocate new GLUquadric object (out of memory?)."); }
+ NormPhys* np=static_cast<NormPhys*>(ip.get());
+ shared_ptr<IGeom> ig(i->geom); if(!ig) return; // changed meanwhile?
+ GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(ig.get());
+ //if(!geom) cerr<<"Gl1_NormPhys: IGeom is not a GenericSpheresContact, but a "<<ig->getClassName()<<endl;
+ Real fnNorm=np->normalForce.dot(geom->normal);
+ if((signFilter>0 && fnNorm<0) || (signFilter<0 && fnNorm>0)) return;
+ int fnSign=fnNorm>0?1:-1;
+ fnNorm=abs(fnNorm);
+ Real radiusScale=1.;
+ // weak/strong fabric, only used if maxWeakFn is set
+ if(!isnan(maxWeakFn)){
+ if(fnNorm*fnSign<maxWeakFn){ // weak fabric
+ if(weakFilter>0) return;
+ radiusScale=weakScale;
+ } else { // strong fabric
+ if(weakFilter<0) return;
+ }
+ }
+
+ maxFn=max(fnNorm,maxFn);
+ Real realMaxRadius;
+ if(maxRadius<0){
+ if(geom->refR1>0) refRadius=min(geom->refR1,refRadius);
+ if(geom->refR2>0) refRadius=min(geom->refR2,refRadius);
+ realMaxRadius=refRadius;
+ }
+ else realMaxRadius=maxRadius;
+ Real radius=radiusScale*realMaxRadius*(fnNorm/maxFn); // use logarithmic scale here?
+ Vector3r color=Shop::scalarOnColorScale(fnNorm*fnSign,-maxFn,maxFn);
+ # if 0
+ // get endpoints from body positions
+ Vector3r p1=b1->state->pos, p2=b2->state->pos;
+ Vector3r relPos;
+ if(scene->isPeriodic){
+ relPos=p2+scene->cell->Hsize*i->cellDist.cast<Real>()-p1;
+ p1=scene->cell->wrapShearedPt(p1);
+ p2=p1+relPos;
+ } else {
+ relPos=p2-p1;
+ }
+ Real dist=relPos.norm();
+ #else
+ // get endpoints from geom
+ // max(r,0) handles r<0 which is the case for "radius" of the facet in Dem3DofGeom_FacetSphere
+ Vector3r cp=scene->isPeriodic? scene->cell->wrapShearedPt(geom->contactPoint) : geom->contactPoint;
+ Vector3r p1=cp-max(geom->refR1,(Real)0.)*geom->normal;
+ Vector3r p2=cp+max(geom->refR2,(Real)0.)*geom->normal;
+ const Vector3r& dispScale=scene->renderer ? scene->renderer->dispScale : Vector3r::Ones();
+ if(dispScale!=Vector3r::Ones()){
+ // move p1 and p2 by the same amounts as particles themselves would be moved
+ p1+=dispScale.cwiseProduct(Vector3r(b1->state->pos-b1->state->refPos));
+ p2+=dispScale.cwiseProduct(Vector3r(b2->state->pos-b2->state->refPos));
+ }
+ Vector3r relPos=p2-p1;
+ Real dist=relPos.norm(); //max(geom->refR1,0.)+max(geom->refR2,0.);
#endif
@@ -87,11 +87,8 @@
glTranslatef(p1[0],p1[1],p1[2]);
Quaternionr q(Quaternionr().setFromTwoVectors(Vector3r(0,0,1),relPos/dist /* normalized */));
// using Transform with OpenGL: http://eigen.tuxfamily.org/dox/TutorialGeometry.html
- #if EIGEN_WORLD_VERSION==2
- glMultMatrixd(Eigen::Transform3d(q).data());
- #elif EIGEN_WORLD_VERSION==3
- glMultMatrixd(Eigen::Affine3d(q).data());
- #endif
+ //glMultMatrixd(Eigen::Affine3d(q).data());
+ glMultMatrix(Eigen::Transform<Real,3,Eigen::Affine>(q).data());
glColor3v(color);
gluCylinder(gluQuadric,radius,radius,dist,slices,stacks);
glPopMatrix();
=== modified file 'pkg/common/Gl1_Sphere.cpp'
--- pkg/common/Gl1_Sphere.cpp 2011-01-11 14:47:55 +0000
+++ pkg/common/Gl1_Sphere.cpp 2013-08-23 15:21:20 +0000
@@ -137,7 +137,7 @@
glNewList(glGlutSphereList,GL_COMPILE);
glEnable(GL_LIGHTING);
glShadeModel(GL_SMOOTH);
- glutSolidSphere(1.0,max(quality*glutSlices,2.),max(quality*glutStacks,3.));
+ glutSolidSphere(1.0,max(quality*glutSlices,(Real)2.),max(quality*glutStacks,(Real)3.));
glEndList();
}
=== modified file 'pkg/common/Grid.cpp'
--- pkg/common/Grid.cpp 2013-06-03 15:28:41 +0000
+++ pkg/common/Grid.cpp 2013-11-12 08:29:26 +0000
@@ -6,9 +6,6 @@
*************************************************************************/
#include "Grid.hpp"
-#ifdef YADE_OPENGL
- #include<yade/lib/opengl/OpenGLWrapper.hpp>
-#endif
//!################## SHAPES #####################
@@ -109,9 +106,9 @@
//This is a little bit tricky : if we haven't 0<k,m<1, it means that the intersection is not inside both segments,
//but the contact can occurs anyway between a connection's extremity and a connection's edge or between two connection's extremity.
//So the three next lines : don't modify k and m if (0<k,m<1), but modify them otherwise to compute later the right normal and penetrationDepth of the contact.
- k = max(min( k,1.0),0.0);
- m = max(min( (A+a*k-B).dot(b)/(pow(b.norm(),2.0)) ,1.0),0.0);
- k = max(min( (B+b*m-A).dot(a)/(pow(a.norm(),2.0)) ,1.0),0.0);
+ k = max(min( k,(Real)1.0),(Real)0.0);
+ m = max(min( (A+a*k-B).dot(b)/(pow(b.norm(),2.0)) ,(Real)1.0),(Real)0.0);
+ k = max(min( (B+b*m-A).dot(a)/(pow(a.norm(),2.0)) ,(Real)1.0),(Real)0.0);
}
else {//should never happen
k=0;m=0;
@@ -120,10 +117,10 @@
}
}
else{ //this is a special case for perfectly colinear vectors ("a" and "b")
- Real PA=(A-B).dot(b)/(b.norm()*b.norm()); PA=min(1.0,max(0.0,PA));
- Real Pa=(A+a-B).dot(b)/(b.norm()*b.norm()); Pa=min(1.0,max(0.0,Pa));
- Real PB=(B-A).dot(a)/(a.norm()*a.norm()); PB=min(1.0,max(0.0,PB));
- Real Pb=(B+b-A).dot(a)/(a.norm()*a.norm()); Pb=min(1.0,max(0.0,Pb));
+ Real PA=(A-B).dot(b)/(b.norm()*b.norm()); PA=min((Real)1.0,max((Real)0.0,PA));
+ Real Pa=(A+a-B).dot(b)/(b.norm()*b.norm()); Pa=min((Real)1.0,max((Real)0.0,Pa));
+ Real PB=(B-A).dot(a)/(a.norm()*a.norm()); PB=min((Real)1.0,max((Real)0.0,PB));
+ Real Pb=(B+b-A).dot(a)/(a.norm()*a.norm()); Pb=min((Real)1.0,max((Real)0.0,Pb));
k=(PB+Pb)/2. ; m=(PA+Pa)/2.;
}
@@ -580,61 +577,3 @@
}
YADE_PLUGIN((Bo1_GridConnection_Aabb));
-
-#ifdef YADE_OPENGL
-//!################## Rendering #####################
-
-bool Gl1_GridConnection::wire;
-bool Gl1_GridConnection::glutNormalize;
-int Gl1_GridConnection::glutSlices;
-int Gl1_GridConnection::glutStacks;
-
-void Gl1_GridConnection::out( Quaternionr q )
-{
- AngleAxisr aa(q);
- std::cout << " axis: " << aa.axis()[0] << " " << aa.axis()[1] << " " << aa.axis()[2] << ", angle: " << aa.angle() << " | ";
-}
-
-void Gl1_GridConnection::go(const shared_ptr<Shape>& cm, const shared_ptr<State>& st ,bool wire2, const GLViewInfo&)
-{
- GridConnection *GC=static_cast<GridConnection*>(cm.get());
- Real r=GC->radius;
- Real length=GC->getLength();
- const shared_ptr<Interaction> intr = scene->interactions->find((int)GC->node1->getId(),(int)GC->node2->getId());
- Vector3r segt = GC->node2->state->pos - GC->node1->state->pos;
- if (scene->isPeriodic && intr) segt+=scene->cell->intrShiftPos(intr->cellDist);
- //glMaterialv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, Vector3f(cm->color[0],cm->color[1],cm->color[2]));
-
- glColor3v(cm->color);
- if(glutNormalize) glPushAttrib(GL_NORMALIZE);
-// glPushMatrix();
- Quaternionr shift;
- shift.setFromTwoVectors(Vector3r::UnitZ(),segt);
- if(intr){drawCylinder(wire || wire2, r,length,shift);}
-// if (intr && scene->isPeriodic) { glTranslatef(-segt[0],-segt[1],-segt[2]); drawCylinder(wire || wire2, r,length,-shift);}
- if(glutNormalize) glPopAttrib();
-// glPopMatrix();
- return;
-}
-
-void Gl1_GridConnection::drawCylinder(bool wire, Real radius, Real length, const Quaternionr& shift)
-{
- glPushMatrix();
- GLUquadricObj *quadObj = gluNewQuadric();
- gluQuadricDrawStyle(quadObj, (GLenum) (wire ? GLU_SILHOUETTE : GLU_FILL));
- gluQuadricNormals(quadObj, (GLenum) GLU_SMOOTH);
- gluQuadricOrientation(quadObj, (GLenum) GLU_OUTSIDE);
- AngleAxisr aa(shift);
- glRotatef(aa.angle()*180.0/Mathr::PI,aa.axis()[0],aa.axis()[1],aa.axis()[2]);
- gluCylinder(quadObj, radius, radius, length, glutSlices,glutStacks);
- gluQuadricOrientation(quadObj, (GLenum) GLU_INSIDE);
- //glutSolidSphere(radius,glutSlices,glutStacks);
- glTranslatef(0.0,0.0,length);
-
- //glutSolidSphere(radius,glutSlices,glutStacks);
-// gluDisk(quadObj,0.0,radius,glutSlices,_loops);
- gluDeleteQuadric(quadObj);
- glPopMatrix();
-}
-YADE_PLUGIN((Gl1_GridConnection));
-#endif
=== added file 'pkg/common/Grid_GUI.cpp'
--- pkg/common/Grid_GUI.cpp 1970-01-01 00:00:00 +0000
+++ pkg/common/Grid_GUI.cpp 2013-11-12 08:29:26 +0000
@@ -0,0 +1,67 @@
+/*************************************************************************
+* Copyright (C) 2012 by François Kneib francois.kneib@xxxxxxxxx *
+* Copyright (C) 2012 by Bruno Chareyre bruno.chareyre@xxxxxxxxxxx *
+* This program is free software; it is licensed under the terms of the *
+* GNU General Public License v2 or later. See file LICENSE for details. *
+*************************************************************************/
+
+#include "Grid.hpp"
+#ifdef YADE_OPENGL
+ #include<yade/lib/opengl/OpenGLWrapper.hpp>
+
+//!################## Rendering #####################
+
+bool Gl1_GridConnection::wire;
+bool Gl1_GridConnection::glutNormalize;
+int Gl1_GridConnection::glutSlices;
+int Gl1_GridConnection::glutStacks;
+
+void Gl1_GridConnection::out( Quaternionr q )
+{
+ AngleAxisr aa(q);
+ std::cout << " axis: " << aa.axis()[0] << " " << aa.axis()[1] << " " << aa.axis()[2] << ", angle: " << aa.angle() << " | ";
+}
+
+void Gl1_GridConnection::go(const shared_ptr<Shape>& cm, const shared_ptr<State>& st ,bool wire2, const GLViewInfo&)
+{
+ GridConnection *GC=static_cast<GridConnection*>(cm.get());
+ Real r=GC->radius;
+ Real length=GC->getLength();
+ const shared_ptr<Interaction> intr = scene->interactions->find((int)GC->node1->getId(),(int)GC->node2->getId());
+ Vector3r segt = GC->node2->state->pos - GC->node1->state->pos;
+ if (scene->isPeriodic && intr) segt+=scene->cell->intrShiftPos(intr->cellDist);
+ //glMaterialv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, Vector3f(cm->color[0],cm->color[1],cm->color[2]));
+
+ glColor3v(cm->color);
+ if(glutNormalize) glPushAttrib(GL_NORMALIZE);
+// glPushMatrix();
+ Quaternionr shift;
+ shift.setFromTwoVectors(Vector3r::UnitZ(),segt);
+ if(intr){drawCylinder(wire || wire2, r,length,shift);}
+// if (intr && scene->isPeriodic) { glTranslatef(-segt[0],-segt[1],-segt[2]); drawCylinder(wire || wire2, r,length,-shift);}
+ if(glutNormalize) glPopAttrib();
+// glPopMatrix();
+ return;
+}
+
+void Gl1_GridConnection::drawCylinder(bool wire, Real radius, Real length, const Quaternionr& shift)
+{
+ glPushMatrix();
+ GLUquadricObj *quadObj = gluNewQuadric();
+ gluQuadricDrawStyle(quadObj, (GLenum) (wire ? GLU_SILHOUETTE : GLU_FILL));
+ gluQuadricNormals(quadObj, (GLenum) GLU_SMOOTH);
+ gluQuadricOrientation(quadObj, (GLenum) GLU_OUTSIDE);
+ AngleAxisr aa(shift);
+ glRotatef(aa.angle()*180.0/Mathr::PI,aa.axis()[0],aa.axis()[1],aa.axis()[2]);
+ gluCylinder(quadObj, radius, radius, length, glutSlices,glutStacks);
+ gluQuadricOrientation(quadObj, (GLenum) GLU_INSIDE);
+ //glutSolidSphere(radius,glutSlices,glutStacks);
+ glTranslatef(0.0,0.0,length);
+
+ //glutSolidSphere(radius,glutSlices,glutStacks);
+// gluDisk(quadObj,0.0,radius,glutSlices,_loops);
+ gluDeleteQuadric(quadObj);
+ glPopMatrix();
+}
+YADE_PLUGIN((Gl1_GridConnection));
+#endif
=== modified file 'pkg/common/KinematicEngines.cpp'
--- pkg/common/KinematicEngines.cpp 2013-07-04 17:36:34 +0000
+++ pkg/common/KinematicEngines.cpp 2013-11-11 16:22:15 +0000
@@ -4,7 +4,7 @@
#include<yade/pkg/dem/Shop.hpp>
#include<yade/lib/smoothing/LinearInterpolate.hpp>
-YADE_PLUGIN((KinematicEngine)(CombinedKinematicEngine)(TranslationEngine)(HarmonicMotionEngine)(RotationEngine)(HelixEngine)(InterpolatingHelixEngine)(HarmonicRotationEngine)(ServoPIDController));
+YADE_PLUGIN((KinematicEngine)(CombinedKinematicEngine)(TranslationEngine)(HarmonicMotionEngine)(RotationEngine)(HelixEngine)(InterpolatingHelixEngine)(HarmonicRotationEngine)(ServoPIDController)(BicyclePedalEngine));
CREATE_LOGGER(KinematicEngine);
@@ -31,6 +31,7 @@
}
// apply one engine after another
FOREACH(const shared_ptr<KinematicEngine>& e, comb){
+ if (e->dead) continue;
e->scene=scene; e->apply(ids);
}
} else {
@@ -108,7 +109,6 @@
}
}
-
void RotationEngine::apply(const vector<Body::id_t>& ids){
if (ids.size()>0) {
#ifdef YADE_OPENMP
@@ -182,3 +182,33 @@
TranslationEngine::apply(ids);
}
+
+void BicyclePedalEngine::apply(const vector<Body::id_t>& ids){
+ if (ids.size()>0) {
+ Quaternionr qRotateZVec(Quaternionr().setFromTwoVectors(Vector3r(0,0,1), rotationAxis));
+
+ Vector3r newPos = Vector3r(cos(fi + angularVelocity*scene->dt)*radius, sin(fi + angularVelocity*scene->dt)*radius, 0.0);
+ Vector3r oldPos = Vector3r(cos(fi)*radius, sin(fi)*radius, 0.0);
+
+ Vector3r newVel = (oldPos - newPos)/scene->dt;
+
+ fi += angularVelocity*scene->dt;
+ newVel = qRotateZVec*newVel;
+
+ #ifdef YADE_OPENMP
+ const long size=ids.size();
+ #pragma omp parallel for schedule(static)
+ for(long i=0; i<size; i++){
+ const Body::id_t& id=ids[i];
+ #else
+ FOREACH(Body::id_t id,ids){
+ #endif
+ assert(id<(Body::id_t)scene->bodies->size());
+ Body* b=Body::byId(id,scene).get();
+ if(!b) continue;
+ b->state->vel+=newVel;
+ }
+ } else {
+ LOG_WARN("The list of ids is empty! Can't move any body.");
+ }
+}
=== modified file 'pkg/common/KinematicEngines.hpp'
--- pkg/common/KinematicEngines.hpp 2013-07-04 17:36:34 +0000
+++ pkg/common/KinematicEngines.hpp 2013-11-11 16:22:15 +0000
@@ -115,3 +115,15 @@
DECLARE_LOGGER;
};
REGISTER_SERIALIZABLE(ServoPIDController);
+
+struct BicyclePedalEngine: public KinematicEngine{
+ virtual void apply(const vector<Body::id_t>& ids);
+ void postLoad(BicyclePedalEngine&){ rotationAxis.normalize(); }
+ YADE_CLASS_BASE_DOC_ATTRS(BicyclePedalEngine,KinematicEngine,"Engine applying the linear motion of ``bicycle pedal`` e.g. moving points around the axis without rotation",
+ ((Real,angularVelocity,0,,"Angular velocity. [rad/s]"))
+ ((Vector3r,rotationAxis,Vector3r::UnitX(),Attr::triggerPostLoad,"Axis of rotation (direction); will be normalized automatically."))
+ ((Real,radius,-1.0,,"Rotation radius. [m]"))
+ ((Real,fi,Mathr::PI/2.0,,"Initial phase [radians]"))
+ );
+};
+REGISTER_SERIALIZABLE(BicyclePedalEngine);
=== modified file 'pkg/dem/CapillaryStressRecorder.cpp'
--- pkg/dem/CapillaryStressRecorder.cpp 2013-05-30 20:17:45 +0000
+++ pkg/dem/CapillaryStressRecorder.cpp 2013-08-20 11:13:01 +0000
@@ -40,11 +40,11 @@
triaxialCompressionEngine = YADE_PTR_CAST<TriaxialCompressionEngine> ( *itFirst );
}
}
- if ( !triaxialCompressionEngine ) LOG_DEBUG ( "stress controller engine NOT found" );
+ if ( !triaxialCompressionEngine ) {
+ LOG_ERROR ("Stress controller engine not found, the recorder cannot be used."); return;}
}
Real f1_cap_x=0, f1_cap_y=0, f1_cap_z=0, x1=0, y1=0, z1=0, x2=0, y2=0, z2=0;
-
Real sig11_cap=0, sig22_cap=0, sig33_cap=0, sig12_cap=0, sig13_cap=0,
sig23_cap=0, Vwater = 0, capillaryPressure = 0;
int j = 0;
=== removed file 'pkg/dem/CohesiveFrictionalPM.cpp'
--- pkg/dem/CohesiveFrictionalPM.cpp 2012-06-28 19:34:29 +0000
+++ pkg/dem/CohesiveFrictionalPM.cpp 1970-01-01 00:00:00 +0000
@@ -1,200 +0,0 @@
-/* LucScholtes2010 */
-
-#include"CohesiveFrictionalPM.hpp"
-#include<yade/core/Scene.hpp>
-#include<yade/pkg/dem/ScGeom.hpp>
-#include<yade/core/Omega.hpp>
-
-YADE_PLUGIN((CFpmMat)(CFpmState)(CFpmPhys)(Ip2_CFpmMat_CFpmMat_CFpmPhys)(Law2_ScGeom_CFpmPhys_CohesiveFrictionalPM));
-
-/********************** Law2_ScGeom_CFpmPhys_CohesiveFrictionalPM ****************************/
-CREATE_LOGGER(Law2_ScGeom_CFpmPhys_CohesiveFrictionalPM);
-
-void Law2_ScGeom_CFpmPhys_CohesiveFrictionalPM::go(shared_ptr<IGeom>& ig, shared_ptr<IPhys>& ip, Interaction* contact){
- ScGeom* geom = static_cast<ScGeom*>(ig.get());
- CFpmPhys* phys = static_cast<CFpmPhys*>(ip.get());
- const int &id1 = contact->getId1();
- const int &id2 = contact->getId2();
- Body* b1 = Body::byId(id1,scene).get();
- Body* b2 = Body::byId(id2,scene).get();
-
- //FIXME changing the sign convention here will break a couple algorithms, including periodicity (Bruno)
- Real displN = geom->penetrationDepth; // NOTE: the sign for penetrationdepth is different from ScGeom and Dem3DofGeom: geom->penetrationDepth>0 when spheres interpenetrate
- Real Dtensile=phys->FnMax/phys->kn;
- Real Dsoftening = phys->strengthSoftening*Dtensile;
-
- /*to set the equilibrium distance between all cohesive elements when they first meet -> allows one to work with initial stress-free assembly*/
- if ( contact->isFresh(scene) ) { phys->initD = displN; phys->normalForce = Vector3r::Zero(); phys->shearForce = Vector3r::Zero();}
- Real D = displN - phys->initD; // interparticular distance is computed depending on the equilibrium distance
-
- /* Determination of interaction */
- if (D < 0){ //spheres do not touch
- if (!phys->isCohesive){ scene->interactions->requestErase(contact); return; } // destroy the interaction before calculation
- if ((phys->isCohesive) && (abs(D) > (Dtensile + Dsoftening))) { // spheres are bonded and the interacting distance is greater than the one allowed ny the defined cohesion
- phys->isCohesive=false;
- // update body state with the number of broken bonds
- CFpmState* st1=dynamic_cast<CFpmState*>(b1->state.get());
- CFpmState* st2=dynamic_cast<CFpmState*>(b2->state.get());
- st1->numBrokenCohesive+=1;
- st2->numBrokenCohesive+=1;
- //// the same thing but from ConcretePM
- //const shared_ptr<Body>& body1=Body::byId(contact->getId1(),scene), body2=Body::byId(contact->getId2(),scene); assert(body1); assert(body2);
- //const shared_ptr<CFpmState>& st1=YADE_PTR_CAST<CFpmState>(body1->state), st2=YADE_PTR_CAST<CFpmState>(body2->state);
- //{ boost::mutex::scoped_lock lock(st1->updateMutex); st1->numBrokenCohesive+=1; }
- //{ boost::mutex::scoped_lock lock(st2->updateMutex); st2->numBrokenCohesive+=1; }
- // end of update
- scene->interactions->requestErase(contact); return;
- }
- }
-
- /*NormalForce*/
- Real Fn=0, Dsoft=0;
-
- if ((D < 0) && (abs(D) > Dtensile)) { //to take into account strength softening
- Dsoft = D+Dtensile; // Dsoft<0 for a negative value of Fn (attractive force)
- Fn = -(phys->FnMax+(phys->kn/phys->strengthSoftening)*Dsoft); // computes FnMax - FnSoftening
- }
- else {
- Fn = phys->kn*D;
- }
- phys->normalForce = Fn*geom->normal; // NOTE normal is position2-position1 - It is directed from particle1 to particle2
-
- /*ShearForce*/
- Vector3r& shearForce = phys->shearForce;
-
- // using scGeom function rotateAndGetShear
- State* st1 = Body::byId(id1,scene)->state.get();
- State* st2 = Body::byId(id2,scene)->state.get();
-
- geom->rotate(phys->shearForce);
- const Vector3r& dus = geom->shearIncrement();
-
- //Linear elasticity giving "trial" shear force
- shearForce -= phys->ks*dus;
- // needed for the next timestep
- phys->prevNormal = geom->normal;
-
- /* Morh-Coulomb criterion */
- Real maxFs = phys->FsMax + Fn*phys->tanFrictionAngle;
-
- if (shearForce.squaredNorm() > maxFs*maxFs){
- shearForce*=maxFs/shearForce.norm(); // to fix the shear force to its yielding value
- }
-
- /* Apply forces */
- Vector3r f = phys->normalForce + shearForce;
- // these lines to adapt to periodic boundary conditions (NOTE applyForceAtContactPoint computes torque induced by normal and shear force too)
- if (!scene->isPeriodic)
- applyForceAtContactPoint(f , geom->contactPoint , id2, st2->se3.position, id1, st1->se3.position);
- else { // in scg we do not wrap particles positions, hence "applyForceAtContactPoint" cannot be used when scene is periodic
- scene->forces.addForce(id1,-f);
- scene->forces.addForce(id2,f);
- scene->forces.addTorque(id1,(geom->radius1-0.5*geom->penetrationDepth)* geom->normal.cross(-f));
- scene->forces.addTorque(id2,(geom->radius2-0.5*geom->penetrationDepth)* geom->normal.cross(-f));
- }
-
- /* Moment Rotation Law */
- // NOTE this part could probably be computed in ScGeom to avoid copy/paste multiplication !!!
- Quaternionr delta( b1->state->ori * phys->initialOrientation1.conjugate() *phys->initialOrientation2 * b2->state->ori.conjugate()); delta.normalize(); //relative orientation
- AngleAxisr aa(delta); // axis of rotation - this is the Moment direction UNIT vector; angle represents the power of resistant ELASTIC moment
- if(aa.angle() > Mathr::PI) aa.angle() -= Mathr::TWO_PI; // angle is between 0 and 2*pi, but should be between -pi and pi
-
- phys->cumulativeRotation = aa.angle();
-
- //Find angle*axis. That's all. But first find angle about contact normal. Result is scalar. Axis is contact normal.
- Real angle_twist(aa.angle() * aa.axis().dot(geom->normal) ); //rotation about normal
- Vector3r axis_twist(angle_twist * geom->normal);
- Vector3r moment_twist(axis_twist * phys->kr);
-
- Vector3r axis_bending(aa.angle()*aa.axis() - axis_twist); //total rotation minus rotation about normal
- Vector3r moment_bending(axis_bending * phys->kr);
- Vector3r moment = moment_twist + moment_bending;
-
- Real MomentMax = phys->maxBend*std::fabs(phys->normalForce.norm());
- Real scalarMoment = moment.norm();
-
- /*Plastic moment */
- if(scalarMoment > MomentMax)
- {
- Real ratio = 0;
- ratio *= MomentMax/scalarMoment; // to fix the moment to its yielding value
- moment *= ratio;
- moment_twist *= ratio;
- moment_bending *= ratio;
- }
-
- phys->moment_twist = moment_twist;
- phys->moment_bending = moment_bending;
-
- scene->forces.addTorque(id1,-moment);
- scene->forces.addTorque(id2, moment);
-
-}
-
-CREATE_LOGGER(Ip2_CFpmMat_CFpmMat_CFpmPhys);
-
-void Ip2_CFpmMat_CFpmMat_CFpmPhys::go(const shared_ptr<Material>& b1, const shared_ptr<Material>& b2, const shared_ptr<Interaction>& interaction){
-
- /* avoid any updates if the interaction already exists */
- if(interaction->phys) return;
-
- ScGeom* geom=dynamic_cast<ScGeom*>(interaction->geom.get());
- assert(geom);
-
- const shared_ptr<CFpmMat>& yade1 = YADE_PTR_CAST<CFpmMat>(b1);
- const shared_ptr<CFpmMat>& yade2 = YADE_PTR_CAST<CFpmMat>(b2);
-
- shared_ptr<CFpmPhys> contactPhysics(new CFpmPhys());
-
- /* From interaction physics */
- Real E1 = yade1->young;
- Real E2 = yade2->young;
- Real V1 = yade1->poisson;
- Real V2 = yade2->poisson;
- Real f1 = yade1->frictionAngle;
- Real f2 = yade2->frictionAngle;
-
- /* From interaction geometry */
- Real R1= geom->radius1;
- Real R2= geom->radius2;
- Real rMean = 0.5*(R1+R2);
- Real crossSection = Mathr::PI*pow(min(R1,R2),2);
-
- /* calculate stiffness */
- Real kNormal=0, kShear=0, kRotate=0;
-
- if(useAlphaBeta == true){
- kNormal = 2.*E1*R1*E2*R2/(E1*R1+E2*R2); // harmonic average of two stiffnesses
- kShear = Alpha*kNormal;
- kRotate = Beta*kShear*rMean*rMean;
- }
- else {
- kNormal = 2.*E1*R1*E2*R2/(E1*R1+E2*R2); // harmonic average of two stiffnesses
- kShear = 2.*E1*R1*V1*E2*R2*V2/(E1*R1*V1+E2*R2*V2); // harmonic average of two stiffnesses with ks=V*kn for each sphere
- kRotate = 0.;
- }
-
- /* Pass values calculated from above to CFpmPhys */
- contactPhysics->kn = kNormal;
- contactPhysics->ks = kShear;
- contactPhysics->kr = kRotate;
- contactPhysics->frictionAngle = std::min(f1,f2);
- contactPhysics->tanFrictionAngle = std::tan(contactPhysics->frictionAngle);
- contactPhysics->maxBend = eta*rMean;
- contactPhysics->prevNormal = geom->normal;
- contactPhysics->initialOrientation1 = Body::byId(interaction->getId1())->state->ori;
- contactPhysics->initialOrientation2 = Body::byId(interaction->getId2())->state->ori;
-
- ///to set if the contact is cohesive or not
- if ( (scene->iter < cohesiveTresholdIteration) && ((tensileStrength>0) || (cohesion>0)) && (yade1->type == yade2->type)){ contactPhysics->isCohesive=true; }
-
- if ( contactPhysics->isCohesive ) {
- contactPhysics->FnMax = tensileStrength*crossSection;
- contactPhysics->strengthSoftening = strengthSoftening;
- contactPhysics->FsMax = cohesion*crossSection;
- }
-
- interaction->phys = contactPhysics;
-}
-
-CFpmPhys::~CFpmPhys(){}
=== removed file 'pkg/dem/CohesiveFrictionalPM.hpp'
--- pkg/dem/CohesiveFrictionalPM.hpp 2010-11-07 11:46:20 +0000
+++ pkg/dem/CohesiveFrictionalPM.hpp 1970-01-01 00:00:00 +0000
@@ -1,114 +0,0 @@
-/* lucScholtes2010 */
-
-#pragma once
-
-#include<yade/pkg/common/ElastMat.hpp>
-#include<yade/pkg/common/Dispatching.hpp>
-#include<yade/pkg/common/NormShearPhys.hpp>
-#include<yade/pkg/dem/ScGeom.hpp>
-
-/*
-=== OVERVIEW OF CohesiveFrictionalPM ===
-
-CohesiveFrictional Particle Model (CohesiveFrictionalPM, CFpm) is a set of classes for modelling mechanical behavior of cohesive frictional material. It is a quite general contact model starting from the basic frictional model with the possible addition of a moment between particles, a tensile strength and a cohesion.
-
-Features of the interaction law:
-
-1. If useAlphaBeta=False, Kn, Ks are calculated from the harmonic average of the "macro" material parameters young (E) and poisson (V) as defined in SimpleElasticRelationships.cpp and Kr=0.
-
-2. If useAlphaBeta=True, users have to input Alpha=Ks/Kn, Beta=Kr/(Ks*meanRadius^2), eta=MtPlastic/(meanRadius*Fn) as defined in Plassiard et al. (Granular Matter, 2009) A spherical discrete element model.
-
-3. Users can input a tensile strength ( FnMax = tensileStrength*pi*Rmin^2 ) and a cohesion ( FsMax = cohesion*pi*Rmin^2 )
-
- Remark: - This contact law works well in the case of sphere/sphere and sphere/box interaction as it uses ScGeom to compute the interaction geometry (suitable for the triaxialTest)
- - It has not been tested for sphere/facet or sphere/wall interactions and could be updated to be used by DemXDofGeom
-*/
-
-/** This class holds information associated with each body state*/
-class CFpmState: public State {
- YADE_CLASS_BASE_DOC_ATTRS_CTOR(CFpmState,State,"CFpm state information about each body.\n\nNone of that is used for computation (at least not now), only for post-processing.",
- ((int,numBrokenCohesive,0,,"Number of broken cohesive links. [-]")),
- createIndex();
- );
- REGISTER_CLASS_INDEX(CFpmState,State);
-};
-REGISTER_SERIALIZABLE(CFpmState);
-
-/** This class holds information associated with each body */
-class CFpmMat: public FrictMat {
- public:
- virtual shared_ptr<State> newAssocState() const { return shared_ptr<State>(new CFpmState); }
- virtual bool stateTypeOk(State* s) const { return (bool)dynamic_cast<CFpmState*>(s); }
-
- YADE_CLASS_BASE_DOC_ATTRS_CTOR(CFpmMat,FrictMat,"cohesive frictional material, for use with other CFpm classes",
- ((int,type,0,,"Type of the particle. If particles of two different types interact, it will be with friction only (no cohesion).[-]")),
- createIndex();
- );
- REGISTER_CLASS_INDEX(CFpmMat,FrictMat);
-};
-REGISTER_SERIALIZABLE(CFpmMat);
-
-/** This class holds information associated with each interaction */
-class CFpmPhys: public NormShearPhys {
- public:
- virtual ~CFpmPhys();
-
- YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(CFpmPhys,NormShearPhys,"Representation of a single interaction of the CFpm type, storage for relevant parameters",
- ((Real,initD,0,,"equilibrium distance for particles. Computed as the initial interparticular distance when bonded particle interact. initD=0 for non cohesive interactions."))
- ((bool,isCohesive,false,,"If false, particles interact in a frictional way. If true, particles are bonded regarding the given cohesion and tensileStrength."))
- ((Real, frictionAngle,0,,"defines Coulomb friction. [deg]"))
- ((Real,tanFrictionAngle,0,,"Tangent of frictionAngle. [-]"))
- ((Real,FnMax,0,,"Defines the maximum admissible normal force in traction FnMax=tensileStrength*crossSection, with crossSection=pi*Rmin^2. [Pa]"))
- ((Real,FsMax,0,,"Defines the maximum admissible tangential force in shear FsMax=cohesion*FnMax, with crossSection=pi*Rmin^2. [Pa]"))
- ((Real,strengthSoftening,0,,"Defines the softening when Dtensile is reached to avoid explosion. Typically, when D > Dtensile, Fn=FnMax - (kn/strengthSoftening)*(Dtensile-D). [-]"))
- ((Real,cumulativeRotation,0,,"Cumulated rotation... [-]"))
- ((Real,kr,0,,"Defines the stiffness to compute the resistive moment in rotation. [-]"))
- ((Real,maxBend,0,,"Defines the maximum admissible resistive moment in rotation Mtmax=maxBend*Fn, maxBend=eta*meanRadius. [m]"))
- ((Vector3r,prevNormal,Vector3r::Zero(),,"Normal to the contact at previous time step."))
- ((Vector3r,moment_twist,Vector3r::Zero(),," [N.m]"))
- ((Vector3r,moment_bending,Vector3r::Zero(),," [N.m]"))
- ((Quaternionr,initialOrientation1,Quaternionr(1.0,0.0,0.0,0.0),,"Used for moment computation."))
- ((Quaternionr,initialOrientation2,Quaternionr(1.0,0.0,0.0,0.0),,"Used for moment computation."))
- ,
- createIndex();
- ,
- );
- DECLARE_LOGGER;
- REGISTER_CLASS_INDEX(CFpmPhys,NormShearPhys);
-};
-REGISTER_SERIALIZABLE(CFpmPhys);
-
-/** 2d functor creating IPhys (Ip2) taking CFpmMat and CFpmMat of 2 bodies, returning type CFpmPhys */
-class Ip2_CFpmMat_CFpmMat_CFpmPhys: public IPhysFunctor{
- public:
- virtual void go(const shared_ptr<Material>& pp1, const shared_ptr<Material>& pp2, const shared_ptr<Interaction>& interaction);
-
- FUNCTOR2D(CFpmMat,CFpmMat);
- DECLARE_LOGGER;
-
- YADE_CLASS_BASE_DOC_ATTRS(Ip2_CFpmMat_CFpmMat_CFpmPhys,IPhysFunctor,"Converts 2 CFpmmat instances to CFpmPhys with corresponding parameters.",
- ((int,cohesiveTresholdIteration,1,,"Should new contacts be cohesive? They will before this iter, they won't afterward."))
- ((bool,useAlphaBeta,false,,"If true, stiffnesses are computed based on Alpha and Beta."))
- ((Real,Alpha,0,,"Defines the ratio ks/kn."))
- ((Real,Beta,0,,"Defines the ratio kr/(ks*meanRadius^2) to compute the resistive moment in rotation. [-]"))
- ((Real,eta,0,,"Defines the maximum admissible resistive moment in rotation MtMax=eta*meanRadius*Fn. [-]"))
- ((Real,tensileStrength,0,,"Defines the maximum admissible normal force in traction FnMax=tensileStrength*crossSection. [Pa]"))
- ((Real,cohesion,0,,"Defines the maximum admissible tangential force in shear FsMax=cohesion*crossSection. [Pa]"))
- ((Real,strengthSoftening,0,,"Defines the softening when Dtensile is reached to avoid explosion of the contact. Typically, when D > Dtensile, Fn=FnMax - (kn/strengthSoftening)*(Dtensile-D). [-]"))
- );
-};
-REGISTER_SERIALIZABLE(Ip2_CFpmMat_CFpmMat_CFpmPhys);
-
-
-/** 2d functor creating the interaction law (Law2) based on SphereContactGeometry (ScGeom) and CFpmPhys of 2 bodies, returning type CohesiveFrictionalPM */
-class Law2_ScGeom_CFpmPhys_CohesiveFrictionalPM: public LawFunctor{
- public:
- virtual void go(shared_ptr<IGeom>& _geom, shared_ptr<IPhys>& _phys, Interaction* I);
- FUNCTOR2D(ScGeom,CFpmPhys);
-
- YADE_CLASS_BASE_DOC_ATTRS(Law2_ScGeom_CFpmPhys_CohesiveFrictionalPM,LawFunctor,"Constitutive law for the CFpm model.",
- ((bool,preventGranularRatcheting,true,,"If true rotations are computed such as granular ratcheting is prevented. See article [Alonso2004]_, pg. 3-10 -- and a lot more papers from the same authors)."))
- );
- DECLARE_LOGGER;
-};
-REGISTER_SERIALIZABLE(Law2_ScGeom_CFpmPhys_CohesiveFrictionalPM);
=== modified file 'pkg/dem/ConcretePM.cpp'
--- pkg/dem/ConcretePM.cpp 2013-05-02 12:19:32 +0000
+++ pkg/dem/ConcretePM.cpp 2013-10-04 12:03:13 +0000
@@ -238,12 +238,31 @@
/********************** Law2_ScGeom_CpmPhys_Cpm ****************************/
CREATE_LOGGER(Law2_ScGeom_CpmPhys_Cpm);
+
+
#ifdef YADE_CPM_FULL_MODEL_AVAILABLE
#include"../../../brefcom-mm.hh"
#endif
// #undef CPM_MATERIAL_MODEL (force trunk version of the model)
+Real Law2_ScGeom_CpmPhys_Cpm::elasticEnergy() {
+ #ifdef YADE_CPM_FULL_MODEL_AVAILABLE
+ CPM_MATERIAL_MODEL_ELE
+ #else
+ Real ret = 0.;
+ FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
+ if(!I->isReal()) continue;
+ CpmPhys* phys = dynamic_cast<CpmPhys*>(I->phys.get());
+ if(phys) {
+ ret += 0.5*phys->normalForce.squaredNorm()/((1-(phys->epsN>0?phys->omega:0))*phys->kn);
+ ret += 0.5*phys->shearForce.squaredNorm()/phys->ks;
+ }
+ }
+ return ret;
+ #endif
+}
+
@@ -326,7 +345,9 @@
Vector3r& Fs(phys->Fs); /* for python access */
const bool& isCohesive(phys->isCohesive);
+
#ifdef CPM_MATERIAL_MODEL
+ Vector3r& epsTPl(phys->epsTPl);
Real& epsNPl(phys->epsNPl);
const Real& dt = scene->dt;
const Real& dmgTau(phys->dmgTau);
@@ -341,8 +362,10 @@
TIMING_DELTAS_CHECKPOINT("GO A");
epsN = - (-phys->refPD + geom->penetrationDepth) / phys->refLength;
- epsT = geom->rotate(epsT);
- epsT += geom->shearIncrement() / (phys->refLength + phys->refPD) ;
+ //epsT = geom->rotate(epsT);
+ geom->rotate(epsT);
+ //epsT += geom->shearIncrement() / (phys->refLength + phys->refPD) ;
+ epsT += geom->shearIncrement() / phys->refLength;
/* debugging */
CPM_YADE_DEBUG_A
@@ -356,7 +379,7 @@
/* simplified public model */
epsN += phys->isoPrestress/E;
/* very simplified version of the constitutive law */
- kappaD = max(max(0.,epsN),kappaD); /* internal variable, max positive strain (non-decreasing) */
+ kappaD = max(max((Real)0.,epsN),kappaD); /* internal variable, max positive strain (non-decreasing) */
omega = isCohesive? phys->funcG(kappaD,epsCrackOnset,epsFracture,neverDamage,damLaw) : 1.; /* damage variable (non-decreasing, as funcG is also non-decreasing) */
sigmaN = (1-(epsN>0?omega:0))*E*epsN; /* damage taken in account in tension only */
sigmaT = G*epsT; /* trial stress */
@@ -364,16 +387,22 @@
if (sigmaT.squaredNorm() > yieldSigmaT*yieldSigmaT) {
Real scale = yieldSigmaT/sigmaT.norm();
sigmaT *= scale; /* stress return */
- epsT *= scale;
+ //epsT *= scale;
/* epsPlSum += yieldSigmaT*geom->slipToStrainTMax(yieldSigmaT/G);*/ /* adjust strain */
}
relResidualStrength = isCohesive? (kappaD<epsCrackOnset? 1. : (1-omega)*(kappaD)/epsCrackOnset) : 0;
#endif
sigmaN -= phys->isoPrestress;
-
- NNAN(kappaD); NNAN(epsFracture); NNAN(omega);
- NNAN(sigmaN); NNANV(sigmaT); NNAN(crossSection);
+
+ NNAN(sigmaN);
+ NNANV(sigmaT);
+ NNAN(crossSection);
+ if (!neverDamage) {
+ NNAN(kappaD);
+ NNAN(epsFracture);
+ NNAN(omega);
+ }
/* handle broken contacts */
if (epsN>0. && ((isCohesive && omega>omegaThreshold) || !isCohesive)) {
@@ -529,6 +558,9 @@
vector<BodyStats> bodyStats; bodyStats.resize(scene->bodies->size());
assert(bodyStats[0].nCohLinks == 0); // should be initialized by dfault ctor
avgRelResidual = 0; Real nAvgRelResidual = 0;
+ Matrix3r identity = Matrix3r::Identity();
+ Real dmg;
+ Matrix3r incr;
FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
if (!I) continue;
if (!I->isReal()) continue;
@@ -556,13 +588,14 @@
nAvgRelResidual += 1;
for (int i=0; i<3; i++) {
for (int j=0; j<3; j++) {
- bodyStats[id1].dmgRhs += n*n.transpose()*(1-phys->relResidualStrength);
- bodyStats[id2].dmgRhs += n*n.transpose()*(1-phys->relResidualStrength);
+ dmg = 1-phys->relResidualStrength;
+ incr = -identity*dmg*1.5 + n*n.transpose()*dmg*7.5;
+ bodyStats[id1].damageTensor += incr;
+ bodyStats[id2].damageTensor += incr;
}
}
}
- Matrix3r identity = Matrix3r::Identity();
- Real tr;
+// Real tr;
FOREACH(shared_ptr<Body> B, *scene->bodies){
if (!B) continue;
const Body::id_t& id = B->getId();
@@ -577,10 +610,7 @@
if (state->normDmg>1) {
LOG_WARN("#"<<id<<" normDmg="<<state->normDmg<<" nCohLinks="<<bodyStats[id].nCohLinks<<", numBrokenCohesive="<<state->numBrokenCohesive<<", dmgSum="<<bodyStats[id].dmgSum<<", numAllCohLinks"<<cohLinksWhenever);
}
- Matrix3r& dmgRhs = bodyStats[id].dmgRhs;
- dmgRhs *= 15./cohLinksWhenever;
- tr = 3*state->normDmg;
- state->damageTensor = .5 * (dmgRhs - tr*identity);
+ state->damageTensor = bodyStats[id].damageTensor / cohLinksWhenever;
}
else { state->normDmg = 0; /*state->normEpsPl=0;*/ state->damageTensor = Matrix3r::Zero(); }
B->shape->color = Vector3r(state->normDmg,1-state->normDmg,B->state->blockedDOFs==State::DOF_ALL?0:1);
=== modified file 'pkg/dem/ConcretePM.hpp'
--- pkg/dem/ConcretePM.hpp 2013-04-24 19:49:00 +0000
+++ pkg/dem/ConcretePM.hpp 2013-08-14 08:59:42 +0000
@@ -175,6 +175,7 @@
((Real,isoPrestress,0,,"\"prestress\" of this link (used to simulate isotropic stress)"))
((Real,kappaD,0,,"Up to now maximum normal strain (semi-norm), non-decreasing in time."))
((Real,epsNPl,0,,"normal plastic strain (initially zero)"))
+ ((Vector3r,epsTPl,Vector3r::Zero(),,"shear plastic strain (initially zero)"))
((bool,neverDamage,false,,"the damage evolution function will always return virgin state"))
((int,damLaw,1,,"Law for softening part of uniaxial tension. 0 for linear, 1 for exponential (default)"))
((Real,epsTrans,0,,"Transversal strain (perpendicular to the contact axis)"))
@@ -251,6 +252,7 @@
class Law2_ScGeom_CpmPhys_Cpm: public LawFunctor{
public:
void go(shared_ptr<IGeom>& _geom, shared_ptr<IPhys>& _phys, Interaction* I);
+ Real elasticEnergy();
Real yieldSigmaTMagnitude(Real sigmaN, Real omega, Real undamagedCohesion, Real tanFrictionAngle) {
#ifdef CPM_MATERIAL_MODEL
@@ -273,6 +275,7 @@
((Real,relKnSoft,.3,,"Relative rigidity of the softening branch in compression (0=perfect elastic-plastic, <0 softening, >0 hardening)")),
/*ctor*/,
.def("yieldSigmaTMagnitude",&Law2_ScGeom_CpmPhys_Cpm::yieldSigmaTMagnitude,(py::arg("sigmaN"),py::arg("omega"),py::arg("undamagedCohesion"),py::arg("tanFrictionAngle")),"Return radius of yield surface for given material and state parameters; uses attributes of the current instance (*yieldSurfType* etc), change them before calling if you need that.")
+ .def("elasticEnergy",&Law2_ScGeom_CpmPhys_Cpm::elasticEnergy,"Compute and return the total elastic energy in all \"CpmPhys\" contacts")
);
DECLARE_LOGGER;
};
@@ -328,7 +331,7 @@
*********************************************************************************/
class CpmStateUpdater: public PeriodicEngine {
- struct BodyStats{ int nCohLinks; int nLinks; Real dmgSum /*, epsPlSum*/; Matrix3r stress; Matrix3r dmgRhs; BodyStats(): nCohLinks(0), nLinks(0), dmgSum(0.) /*, epsPlSum(0.)*/, stress(Matrix3r::Zero()), dmgRhs(Matrix3r::Zero()) {} };
+ struct BodyStats{ int nCohLinks; int nLinks; Real dmgSum /*, epsPlSum*/; Matrix3r stress; Matrix3r damageTensor; BodyStats(): nCohLinks(0), nLinks(0), dmgSum(0.) /*, epsPlSum(0.)*/, stress(Matrix3r::Zero()), damageTensor(Matrix3r::Zero()) {} };
public:
virtual void action(){ update(scene); }
void update(Scene* rb=NULL);
=== removed file 'pkg/dem/Dem3DofGeom_FacetSphere.cpp'
--- pkg/dem/Dem3DofGeom_FacetSphere.cpp 2011-02-14 08:05:09 +0000
+++ pkg/dem/Dem3DofGeom_FacetSphere.cpp 1970-01-01 00:00:00 +0000
@@ -1,221 +0,0 @@
-// © Václav Šmilauer <eudoxos@xxxxxxxx>
-#include "Dem3DofGeom_FacetSphere.hpp"
-#include<yade/pkg/common/Sphere.hpp>
-#include<yade/pkg/common/Facet.hpp>
-YADE_PLUGIN((Dem3DofGeom_FacetSphere)
- #ifdef YADE_OPENGL
- (Gl1_Dem3DofGeom_FacetSphere)
- #endif
- (Ig2_Facet_Sphere_Dem3DofGeom));
-
-CREATE_LOGGER(Dem3DofGeom_FacetSphere);
-Dem3DofGeom_FacetSphere::~Dem3DofGeom_FacetSphere(){}
-
-void Dem3DofGeom_FacetSphere::setTgPlanePts(const Vector3r& p1new, const Vector3r& p2new){
- TRVAR3(cp1pt,cp2rel,contPtInTgPlane2()-contPtInTgPlane1());
- cp1pt=se31.orientation.conjugate()*(turnPlanePt(p1new,normal,se31.orientation*localFacetNormal)+contactPoint-se31.position);
- cp2rel=se32.orientation.conjugate()*Dem3DofGeom_SphereSphere::rollPlanePtToSphere(p2new,effR2,-normal);
- TRVAR3(cp1pt,cp2rel,contPtInTgPlane2()-contPtInTgPlane1());
-}
-
-void Dem3DofGeom_FacetSphere::relocateContactPoints(const Vector3r& p1, const Vector3r& p2){
- //TRVAR2(p2.norm(),effR2);
- if(p2.squaredNorm()>pow(effR2,2)){
- setTgPlanePts(Vector3r::Zero(),p2-p1);
- }
-}
-
-Real Dem3DofGeom_FacetSphere::slipToDisplacementTMax(Real displacementTMax){
- //FIXME: not yet tested
- // negative or zero: reset shear
- if(displacementTMax<=0.){ setTgPlanePts(Vector3r(0,0,0),Vector3r(0,0,0)); return displacementTMax;}
- // otherwise
- Vector3r p1=contPtInTgPlane1(), p2=contPtInTgPlane2();
- Real currDistSq=(p2-p1).squaredNorm();
- if(currDistSq<pow(displacementTMax,2)) return 0; // close enough, no slip needed
- //Vector3r diff=.5*(sqrt(currDistSq)/displacementTMax-1)*(p2-p1); setTgPlanePts(p1+diff,p2-diff);
- Real scale=displacementTMax/sqrt(currDistSq); setTgPlanePts(scale*p1,scale*p2);
- return (displacementTMax/scale)*(1-scale);
-}
-
-CREATE_LOGGER(Ig2_Facet_Sphere_Dem3DofGeom);
-bool Ig2_Facet_Sphere_Dem3DofGeom::go(const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c){
- Facet* facet=static_cast<Facet*>(cm1.get());
- Real sphereRadius=static_cast<Sphere*>(cm2.get())->radius;
-
- // IGeomFunctor::go(cm1,cm2,state1,state2,shift2,force,c);
-
- #if 1
- /* new code written from scratch, to make sure the algorithm is correct; it is about the same speed
- as sega's algo below, but seems more readable to me.
- The FACET_TOPO thing is still missing here but can be copied literally once it is tested */
- // begin facet-local coordinates
- Vector3r cogLine=state1.ori.conjugate()*(state2.pos+shift2-state1.pos); // connect centers of gravity
- //TRVAR4(state1.pos,state1.ori,state2.pos,cogLine);
- Vector3r normal=facet->normal;
- Real planeDist=normal.dot(cogLine);
- if(planeDist<0){normal*=-1; planeDist*=-1; }
- if(planeDist>sphereRadius && !c->isReal() && !force) { /* LOG_TRACE("Sphere too far ("<<planeDist<<") from plane"); */ return false; }
- Vector3r planarPt=cogLine-planeDist*normal; // project sphere center to the facet plane
- Real normDotPt[3];
- Vector3r contactPt(Vector3r::Zero());
- for(int i=0; i<3; i++) normDotPt[i]=facet->ne[i].dot(planarPt-facet->vertices[i]);
- short w=(normDotPt[0]>0?1:0)+(normDotPt[1]>0?2:0)+(normDotPt[2]>0?4:0);
- //TRVAR4(planarPt,normDotPt[0],normDotPt[1],normDotPt[2]);
- //TRVAR2(normal,cogLine);
- //TRVAR3(facet->vertices[0],facet->vertices[1],facet->vertices[2]);
- switch(w){
- case 0: contactPt=planarPt; break; // inside triangle
- case 1: contactPt=getClosestSegmentPt(planarPt,facet->vertices[0],facet->vertices[1]); break; // +-- (n1)
- case 2: contactPt=getClosestSegmentPt(planarPt,facet->vertices[1],facet->vertices[2]); break; // -+- (n2)
- case 4: contactPt=getClosestSegmentPt(planarPt,facet->vertices[2],facet->vertices[0]); break; // --+ (n3)
- case 3: contactPt=facet->vertices[1]; break; // ++- (v1)
- case 5: contactPt=facet->vertices[0]; break; // +-+ (v0)
- case 6: contactPt=facet->vertices[2]; break; // -++ (v2)
- case 7: throw logic_error("Impossible triangle intersection?"); // +++ (impossible)
- default: throw logic_error("Nonsense intersection value!");
- }
- normal=cogLine-contactPt; // called normal, but it is no longer the facet's normal (for compat)
- //TRVAR3(normal,contactPt,sphereRadius);
- if(!c->isReal() && normal.squaredNorm()>sphereRadius*sphereRadius && !force) { /* LOG_TRACE("Sphere too far from closest point"); */ return false; } // fast test before sqrt
- Real norm=normal.norm(); normal/=norm; // normal is unit vector now
- Real penetrationDepth=sphereRadius-norm;
- #else
- /* This code was mostly copied from InteractingFacet2InteractinSphere4SpheresContactGeometry */
- // begin facet-local coordinates
- Vector3r contactLine=state1.ori.Conjugate()*(state2.pos+shift2-state1.pos);
- Vector3r normal=facet->normal;
- Real L=normal.Dot(contactLine); // height/depth of sphere's center from facet's plane
- if(L<0){normal*=-1; L*=-1;}
- if(L>sphereRadius && !c->isReal()) return false; // sphere too far away from the plane
-
- Vector3r contactPt=contactLine-L*normal; // projection of sphere's center to facet's plane (preliminary contact point)
- const Vector3r* edgeNormals=facet->ne; // array[3] of edge normals (in facet plane)
- int edgeMax=0; Real distMax=edgeNormals[0].Dot(contactPt);
- for(int i=1; i<3; i++){
- Real dist=edgeNormals[i].Dot(contactPt);
- if(distMax<dist){edgeMax=i; distMax=dist;}
- }
- //TRVAR2(distMax,edgeMax);
- // OK, what's the logic here? Copying from IF2IS4SCG…
- Real sphereRReduced=shrinkFactor*sphereRadius;
- Real inCircleR=facet->icr-sphereRReduced;
- Real penetrationDepth;
- if(inCircleR<0){inCircleR=facet->icr; sphereRReduced=0;}
- if(distMax<inCircleR){// contact with facet's surface
- penetrationDepth=sphereRadius-L;
- normal.normalize();
- } else { // contact with the edge
- contactPt+=edgeNormals[edgeMax]*(inCircleR-distMax);
- bool noVertexContact=false;
- //TRVAR3(edgeNormals[edgeMax],inCircleR,distMax);
- // contact with vertex no. edgeMax
- // FIXME: this is the original version, but why (edgeMax-1)%3? IN that case, edgeNormal to edgeMax would never be tried
- // if (contactPt.Dot(edgeNormals[ (edgeMax-1)%3])>inCircleR) contactPt=facet->vu[edgeMax]*(facet->vl[edgeMax]-sphereRReduced);
- if (contactPt.Dot(edgeNormals[ edgeMax ])>inCircleR) contactPt=facet->vu[edgeMax]*(facet->vl[edgeMax]-sphereRReduced);
- // contact with vertex no. edgeMax+1
- else if(contactPt.Dot(edgeNormals[edgeMax=(edgeMax+1)%3])>inCircleR) contactPt=facet->vu[edgeMax]*(facet->vl[edgeMax]-sphereRReduced);
- // contact with edge no. edgeMax
- else noVertexContact=true;
- normal=contactLine-contactPt;
- #ifdef FACET_TOPO
- if(noVertexContact && facet->edgeAdjIds[edgeMax]!=Body::ID_NONE){
- // find angle between our normal and the facet's normal (still local coords)
- Quaternionr q; q.Align(facet->normal,normal); AngleAxisr aa(q);
- assert(aa.angle()>=0 && aa.angle()<=Mathr::PI);
- if(edgeNormals[edgeMax].Dot(aa.axis())<0) aa.angle()*=-1.;
- bool negFace=normal.Dot(facet->normal)<0; // contact in on the negative facet's face
- Real halfAngle=(negFace?-1.:1.)*facet->edgeAdjHalfAngle[edgeMax];
- if(halfAngle<0 && aa.angle()>halfAngle) return false; // on concave boundary, and if in the other facet's sector, no contact
- // otherwise the contact will be created
- }
- #endif
- //TRVAR4(contactLine,contactPt,normal,normal.norm());
- //TRVAR3(se31.orientation*contactLine,se31.position+se31.orientation*contactPt,se31.orientation*normal);
- Real norm=normal.norm(); normal/=norm;
- penetrationDepth=sphereRadius-norm;
- //TRVAR1(penetrationDepth);
- }
- // end facet-local coordinates
- #endif
-
- if(penetrationDepth<0 && !c->isReal()) return false;
-
-
- shared_ptr<Dem3DofGeom_FacetSphere> fs;
- Vector3r normalGlob=state1.ori*normal;
- bool isNew=false;
- if(c->geom) fs=YADE_PTR_CAST<Dem3DofGeom_FacetSphere>(c->geom);
- else {
- // LOG_TRACE("Creating new Dem3DofGeom_FacetSphere");
- fs=shared_ptr<Dem3DofGeom_FacetSphere>(new Dem3DofGeom_FacetSphere());
- c->geom=fs;
- isNew=true;
- fs->effR2=sphereRadius-penetrationDepth;
- fs->refR1=-1; fs->refR2=sphereRadius;
- // postponed till below, to avoid numeric issues
- // see https://lists.launchpad.net/yade-dev/msg02794.html
- // since displacementN() is computed from fs->contactPoint,
- // it was returning something like +1e-16 at the very first step
- // when it was created ⇒ the constitutive law was erasing the
- // contact as soon as it was created.
- // fs->refLength=…
- fs->cp1pt=contactPt; // facet-local intial contact point
- fs->localFacetNormal=facet->normal;
- fs->cp2rel.setFromTwoVectors(Vector3r::UnitX(),state2.ori.conjugate()*(-normalGlob)); // initial sphere-local center-contactPt orientation WRT +x
- fs->cp2rel.normalize();
- }
- fs->se31=state1.se3; fs->se32=state2.se3; fs->se32.position+=shift2;
- fs->normal=normalGlob;
- fs->contactPoint=state2.pos+shift2+(-normalGlob)*(sphereRadius-penetrationDepth);
- // this refLength computation mimics what displacementN() does inside
- // displcementN will therefore return exactly zero at the step the contact
- // was created, which is what we want
- if(isNew) fs->refLength=(state2.pos+shift2-fs->contactPoint).norm();
- return true;
-}
-
-#ifdef YADE_OPENGL
-
- #include<yade/lib/opengl/OpenGLWrapper.hpp>
- #include<yade/lib/opengl/GLUtils.hpp>
-
- bool Gl1_Dem3DofGeom_FacetSphere::normal=false;
- bool Gl1_Dem3DofGeom_FacetSphere::rolledPoints=false;
- bool Gl1_Dem3DofGeom_FacetSphere::unrolledPoints=false;
- bool Gl1_Dem3DofGeom_FacetSphere::shear=false;
- bool Gl1_Dem3DofGeom_FacetSphere::shearLabel=false;
-
- void Gl1_Dem3DofGeom_FacetSphere::go(const shared_ptr<IGeom>& ig, const shared_ptr<Interaction>& ip, const shared_ptr<Body>& b1, const shared_ptr<Body>& b2, bool wireFrame){
- Dem3DofGeom_FacetSphere* fs = static_cast<Dem3DofGeom_FacetSphere*>(ig.get());
- const Se3r& se31=b1->state->se3,se32=b2->state->se3;
- const Vector3r& pos1=se31.position; const Vector3r& pos2=se32.position;
- const Quaternionr& ori1=se31.orientation; const Quaternionr& ori2=se32.orientation;
- const Vector3r& contPt=fs->contactPoint;
-
- if(normal){
- GLUtils::GLDrawArrow(contPt,contPt+fs->refLength*fs->normal); // normal of the contact
- }
- // sphere center to point on the sphere
- if(rolledPoints){
- //cerr<<pos1<<" "<<pos1+ori1*fs->cp1pt<<" "<<contPt<<endl;
- GLUtils::GLDrawLine(pos1+ori1*fs->cp1pt,contPt,Vector3r(0,.5,1));
- GLUtils::GLDrawLine(pos2,pos2+(ori2*fs->cp2rel*Vector3r::UnitX()*fs->effR2),Vector3r(0,1,.5));
- }
- // contact point to projected points
- if(unrolledPoints||shear){
- Vector3r ptTg1=fs->contPtInTgPlane1(), ptTg2=fs->contPtInTgPlane2();
- if(unrolledPoints){
- //TRVAR3(ptTg1,ptTg2,ss->normal)
- GLUtils::GLDrawLine(contPt,contPt+ptTg1,Vector3r(0,.5,1));
- GLUtils::GLDrawLine(contPt,contPt+ptTg2,Vector3r(0,1,.5)); GLUtils::GLDrawLine(pos2,contPt+ptTg2,Vector3r(0,1,.5));
- }
- if(shear){
- GLUtils::GLDrawLine(contPt+ptTg1,contPt+ptTg2,Vector3r(1,1,1));
- if(shearLabel) GLUtils::GLDrawNum(fs->displacementT().norm(),contPt,Vector3r(1,1,1));
- }
- }
- }
-
-#endif
-
=== removed file 'pkg/dem/Dem3DofGeom_FacetSphere.hpp'
--- pkg/dem/Dem3DofGeom_FacetSphere.hpp 2011-01-10 08:08:23 +0000
+++ pkg/dem/Dem3DofGeom_FacetSphere.hpp 1970-01-01 00:00:00 +0000
@@ -1,80 +0,0 @@
-#pragma once
-#include<yade/pkg/dem/DemXDofGeom.hpp>
-// for static roll/unroll functions in Dem3DofGeom_SphereSphere
-#include<yade/pkg/dem/Dem3DofGeom_SphereSphere.hpp>
-#include<yade/pkg/common/Sphere.hpp>
-#include<yade/pkg/common/Facet.hpp>
-
-class Dem3DofGeom_FacetSphere: public Dem3DofGeom{
- //! turn planePt in plane with normal0 around line passing through origin to plane with normal1
- static Vector3r turnPlanePt(const Vector3r& planePt, const Vector3r& normal0, const Vector3r& normal1){ Quaternionr q; q.setFromTwoVectors(normal0,normal1); return q*planePt; }
-
- Vector3r contPtInTgPlane1() const { return turnPlanePt(se31.position+se31.orientation*cp1pt-contactPoint,se31.orientation*localFacetNormal,normal); }
- Vector3r contPtInTgPlane2() const { return Dem3DofGeom_SphereSphere::unrollSpherePtToPlane(se32.orientation*cp2rel,effR2,-normal);}
-
- public:
- virtual ~Dem3DofGeom_FacetSphere();
- /******* API ********/
- virtual Real displacementN(){ return (se32.position-contactPoint).norm()-refLength;}
- virtual Vector3r displacementT(){ relocateContactPoints(); return contPtInTgPlane2()-contPtInTgPlane1(); }
- virtual Real slipToDisplacementTMax(Real displacementTMax);
- /***** end API ******/
-
- void setTgPlanePts(const Vector3r&, const Vector3r&);
- void relocateContactPoints(){ relocateContactPoints(contPtInTgPlane1(),contPtInTgPlane2()); }
- void relocateContactPoints(const Vector3r& p1, const Vector3r& p2);
- YADE_CLASS_BASE_DOC_ATTRS_CTOR(Dem3DofGeom_FacetSphere,Dem3DofGeom,"Class representing facet+sphere in contact which computes 3 degrees of freedom (normal and shear deformation).",
- ((Vector3r,cp1pt,,,"Reference contact point on the facet in facet-local coords."))
- ((Quaternionr,cp2rel,,,"Orientation between +x and the reference contact point (on the sphere) in sphere-local coords"))
- ((Vector3r,localFacetNormal,,,"Unit normal of the facet plane in facet-local coordinates"))
- ((Real,effR2,,,"Effective radius of sphere")),
- /*ctor*/ createIndex();
- );
- REGISTER_CLASS_INDEX(Dem3DofGeom_FacetSphere,Dem3DofGeom);
- DECLARE_LOGGER;
- friend class Gl1_Dem3DofGeom_FacetSphere;
- friend class Ig2_Facet_Sphere_Dem3DofGeom;
-};
-REGISTER_SERIALIZABLE(Dem3DofGeom_FacetSphere);
-
-#ifdef YADE_OPENGL
- #include<yade/pkg/common/GLDrawFunctors.hpp>
- class Gl1_Dem3DofGeom_FacetSphere:public GlIGeomFunctor{
- public:
- virtual void go(const shared_ptr<IGeom>&,const shared_ptr<Interaction>&,const shared_ptr<Body>&,const shared_ptr<Body>&,bool wireFrame);
- RENDERS(Dem3DofGeom_FacetSphere);
- YADE_CLASS_BASE_DOC_STATICATTRS(Gl1_Dem3DofGeom_FacetSphere,GlIGeomFunctor,"Render interaction of facet and sphere (represented by Dem3DofGeom_FacetSphere)",
- ((bool,normal,false,,"Render interaction normal"))
- ((bool,rolledPoints,false,,"Render points rolled on the sphere & facet (original contact point)"))
- ((bool,unrolledPoints,false,,"Render original contact points unrolled to the contact plane"))
- ((bool,shear,false,,"Render shear line in the contact plane"))
- ((bool,shearLabel,false,,"Render shear magnitude as number"))
- );
- };
- REGISTER_SERIALIZABLE(Gl1_Dem3DofGeom_FacetSphere);
-#endif
-
-#include<yade/pkg/common/Dispatching.hpp>
-class Ig2_Facet_Sphere_Dem3DofGeom:public IGeomFunctor{
- Vector3r getClosestSegmentPt(const Vector3r& P, const Vector3r& A, const Vector3r& B){
- // algo: http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/
- Vector3r BA=B-A;
- Real u=(P.dot(BA)-A.dot(BA))/(BA.squaredNorm());
- return A+min(1.,max(0.,u))*BA;
- }
- public:
- virtual bool go(const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c);
- virtual bool goReverse( const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c){
- c->swapOrder(); return go(cm2,cm1,state2,state1,-shift2,force,c);
- LOG_ERROR("!! goReverse maybe doesn't work in Ig2_Facet_Sphere_Dem3DofGeom. IGeomDispatcher should swap interaction members first and call go(...) afterwards.");
- }
-
- FUNCTOR2D(Facet,Sphere);
- DEFINE_FUNCTOR_ORDER_2D(Facet,Sphere);
- YADE_CLASS_BASE_DOC_ATTRS(Ig2_Facet_Sphere_Dem3DofGeom,IGeomFunctor,"Compute geometry of facet-sphere contact with normal and shear DOFs. As in all other Dem3DofGeom-related classes, total formulation of both shear and normal deformations is used. See :yref:`Dem3DofGeom_FacetSphere` for more information.",
- // unused: ((Real,shrinkFactor,0.,,"Reduce the facet's size, probably to avoid singularities at common facets' edges (?)"))
- );
- DECLARE_LOGGER;
-};
-REGISTER_SERIALIZABLE(Ig2_Facet_Sphere_Dem3DofGeom);
-
=== removed file 'pkg/dem/Dem3DofGeom_SphereSphere.cpp'
--- pkg/dem/Dem3DofGeom_SphereSphere.cpp 2010-12-13 12:11:43 +0000
+++ pkg/dem/Dem3DofGeom_SphereSphere.cpp 1970-01-01 00:00:00 +0000
@@ -1,196 +0,0 @@
-#include "Dem3DofGeom_SphereSphere.hpp"
-
-#include<yade/pkg/common/Sphere.hpp>
-#include<yade/core/Omega.hpp>
-YADE_PLUGIN((Dem3DofGeom_SphereSphere)
- #ifdef YADE_OPENGL
- (Gl1_Dem3DofGeom_SphereSphere)
- #endif
- (Ig2_Sphere_Sphere_Dem3DofGeom));
-
-
-Dem3DofGeom_SphereSphere::~Dem3DofGeom_SphereSphere(){}
-
-/*! Project point from sphere surface to tangent plane,
- * such that the angle of shortest arc from (1,0,0) pt on the sphere to the point itself is the same
- * as the angle of segment of the same length on the tangent plane.
- *
- * This function is (or should be) inverse of ScGeom::rollPlanePtToSphere.
- *
- * @param fromXtoPtOri gives orientation of the vector from sphere center to the sphere point from the global +x axis.
- * @param radius the distance from sphere center to the contact plane
- * @param planeNormal unit vector pointing away from the sphere center, determining plane orientation on which the projected point lies.
- * @returns The projected point coordinates (with origin at the contact point).
- */
-Vector3r Dem3DofGeom_SphereSphere::unrollSpherePtToPlane(const Quaternionr& fromXtoPtOri, const Real& radius, const Vector3r& planeNormal){
- Quaternionr normal2pt; normal2pt.setFromTwoVectors(planeNormal,fromXtoPtOri*Vector3r::UnitX());
- AngleAxisr aa(normal2pt);
- return (aa.angle()*radius) /* length */ *(aa.axis().cross(planeNormal)) /* direction: both are unit vectors */;
-}
-
-/*! Project point from tangent plane to the sphere.
- *
- * This function is (or should be) inverse of ScGeom::unrollSpherePtToPlane.
- *
- * @param planePt point on the tangent plane, with origin at the contact point (i.e. at sphere center + normal*radius)
- * @param radius sphere radius
- * @param planeNormal _unit_ vector pointing away from sphere center
- * @returns orientation that transforms +x axis to the vector between sphere center and point on the sphere that corresponds to planePt.
- *
- * @note It is not checked whether planePt relly lies on the tangent plane. If not, result will be incorrect.
- */
-Quaternionr Dem3DofGeom_SphereSphere::rollPlanePtToSphere(const Vector3r& planePt, const Real& radius, const Vector3r& planeNormal){
- if (planePt!=Vector3r::Zero()) {
- Quaternionr normal2pt;
- Vector3r axis=planeNormal.cross(planePt); axis.normalize();
- Real angle=planePt.norm()/radius;
- normal2pt=Quaternionr(AngleAxisr(angle,axis));
- Quaternionr ret; return ret.setFromTwoVectors(Vector3r::UnitX(),normal2pt*planeNormal);
- } else {
- Quaternionr ret; return ret.setFromTwoVectors(Vector3r::UnitX(),planeNormal);
- }
-}
-
-
-
-/* Set contact points on both spheres such that their projection is the one given
- * (should be on the plane passing through origin and oriented with normal; not checked!)
- */
-void Dem3DofGeom_SphereSphere::setTgPlanePts(Vector3r p1new, Vector3r p2new){
- cp1rel=ori1.conjugate()*rollPlanePtToSphere(p1new,effR1,normal);
- cp2rel=ori2.conjugate()*rollPlanePtToSphere(p2new,effR2,-normal);
-}
-
-
-
-/*! Perform slip of the projected contact points so that their distance becomes equal (or remains smaller) than the given one.
- * The slipped distance is returned.
- */
-Real Dem3DofGeom_SphereSphere::slipToDisplacementTMax(Real displacementTMax){
- // very close, reset shear
- if(displacementTMax<=0.){ setTgPlanePts(Vector3r(0,0,0),Vector3r(0,0,0)); return displacementTMax;}
- // otherwise
- Vector3r p1=contPtInTgPlane1(), p2=contPtInTgPlane2();
- Real currDistSq=(p2-p1).squaredNorm();
- if(currDistSq<pow(displacementTMax,2)) return 0; // close enough, no slip needed
- Vector3r diff=.5*(displacementTMax/sqrt(currDistSq)-1)*(p2-p1);
- setTgPlanePts(p1-diff,p2+diff);
- return 2*diff.norm();
-}
-
-
-/* Move contact point on both spheres in such way that their relative position (displacementT) is the same;
- * this should be done regularly to ensure that the angle doesn't go over π, since then quaternion would
- * flip axis and the point would project on other side of the tangent plane piece. */
-void Dem3DofGeom_SphereSphere::relocateContactPoints(){
- relocateContactPoints(contPtInTgPlane1(),contPtInTgPlane2());
-}
-
-/*! Like Dem3DofGeom_SphereSphere::relocateContactPoints(), but use already computed tangent plane points. */
-void Dem3DofGeom_SphereSphere::relocateContactPoints(const Vector3r& p1, const Vector3r& p2){
- Vector3r midPt=(effR1/(effR1+effR2))*(p1+p2); // proportionally to radii, so that angle would be the same
- if((p1.squaredNorm()>pow(effR1,2) || p2.squaredNorm()>pow(effR2,2)) && midPt.squaredNorm()>pow(min(effR1,effR2),2)){
- //cerr<<"RELOCATION with displacementT="<<displacementT(); // should be the same before and after relocation
- setTgPlanePts(p1-midPt,p2-midPt);
- //cerr<<" → "<<displacementT()<<endl;
- }
-}
-
-
-
-#ifdef YADE_OPENGL
- #include<yade/lib/opengl/OpenGLWrapper.hpp>
- #include<yade/lib/opengl/GLUtils.hpp>
- bool Gl1_Dem3DofGeom_SphereSphere::normal=false;
- bool Gl1_Dem3DofGeom_SphereSphere::rolledPoints=false;
- bool Gl1_Dem3DofGeom_SphereSphere::unrolledPoints=false;
- bool Gl1_Dem3DofGeom_SphereSphere::shear=false;
- bool Gl1_Dem3DofGeom_SphereSphere::shearLabel=false;
-
- void Gl1_Dem3DofGeom_SphereSphere::go(const shared_ptr<IGeom>& ig, const shared_ptr<Interaction>& ip, const shared_ptr<Body>& b1, const shared_ptr<Body>& b2, bool wireFrame){
- Dem3DofGeom_SphereSphere* ss = static_cast<Dem3DofGeom_SphereSphere*>(ig.get());
- //const Se3r& se31=b1->physicalParameters->dispSe3,se32=b2->physicalParameters->dispSe3;
- const Se3r& se31=b1->state->se3,se32=b2->state->se3;
- const Vector3r& pos1=se31.position,pos2=se32.position;
- Vector3r& contPt=ss->contactPoint;
-
- if(normal){
- GLUtils::GLDrawArrow(contPt,contPt+ss->normal*.5*ss->refLength); // normal of the contact
- }
- #if 0
- // never used, since bending/torsion not used
- //Vector3r contPt=se31.position+(ss->effR1/ss->refLength)*(se32.position-se31.position); // must be recalculated to not be unscaled if scaling displacements ...
- GLUtils::GLDrawLine(pos1,pos2,Vector3r(.5,.5,.5));
- Vector3r bend; Real tors;
- ss->bendingTorsionRel(bend,tors);
- GLUtils::GLDrawLine(contPt,contPt+10*ss->radius1*(bend+ss->normal*tors),Vector3r(1,0,0));
- #if 0
- GLUtils::GLDrawNum(bend[0],contPt-.2*ss->normal*ss->radius1,Vector3r(1,0,0));
- GLUtils::GLDrawNum(bend[1],contPt,Vector3r(0,1,0));
- GLUtils::GLDrawNum(bend[2],contPt+.2*ss->normal*ss->radius1,Vector3r(0,0,1));
- GLUtils::GLDrawNum(tors,contPt+.5*ss->normal*ss->radius2,Vector3r(1,1,0));
- #endif
- #endif
- // sphere center to point on the sphere
- if(rolledPoints){
- GLUtils::GLDrawLine(pos1,pos1+(ss->ori1*ss->cp1rel*Vector3r::UnitX()*ss->effR1),Vector3r(0,.5,1));
- GLUtils::GLDrawLine(pos2,pos2+(ss->ori2*ss->cp2rel*Vector3r::UnitX()*ss->effR2),Vector3r(0,1,.5));
- }
- //TRVAR4(pos1,ss->ori1,pos2,ss->ori2);
- //TRVAR2(ss->cp2rel,pos2+(ss->ori2*ss->cp2rel*Vector3r::UnitX()*ss->effR2));
- // contact point to projected points
- if(unrolledPoints||shear){
- Vector3r ptTg1=ss->contPtInTgPlane1(), ptTg2=ss->contPtInTgPlane2();
- if(unrolledPoints){
- //TRVAR3(ptTg1,ptTg2,ss->normal)
- GLUtils::GLDrawLine(contPt,contPt+ptTg1,Vector3r(0,.5,1)); GLUtils::GLDrawLine(pos1,contPt+ptTg1,Vector3r(0,.5,1));
- GLUtils::GLDrawLine(contPt,contPt+ptTg2,Vector3r(0,1,.5)); GLUtils::GLDrawLine(pos2,contPt+ptTg2,Vector3r(0,1,.5));
- }
- if(shear){
- GLUtils::GLDrawLine(contPt+ptTg1,contPt+ptTg2,Vector3r(1,1,1));
- if(shearLabel) GLUtils::GLDrawNum(ss->displacementT().norm(),contPt,Vector3r(1,1,1));
- }
- }
- }
-#endif
-
-CREATE_LOGGER(Ig2_Sphere_Sphere_Dem3DofGeom);
-
-bool Ig2_Sphere_Sphere_Dem3DofGeom::go(const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c){
- Sphere *s1=static_cast<Sphere*>(cm1.get()), *s2=static_cast<Sphere*>(cm2.get());
- Vector3r normal=(state2.pos+shift2)-state1.pos;
- Real penetrationDepthSq=pow((distFactor>0?distFactor:1.)*(s1->radius+s2->radius),2)-normal.squaredNorm();
- if (penetrationDepthSq<0 && !c->isReal() && !force){
- return false;
- }
-
- Real dist=normal.norm(); normal/=dist; /* normal is unit vector now */
- shared_ptr<Dem3DofGeom_SphereSphere> ss;
- if(c->geom) ss=YADE_PTR_CAST<Dem3DofGeom_SphereSphere>(c->geom);
- else {
- ss=shared_ptr<Dem3DofGeom_SphereSphere>(new Dem3DofGeom_SphereSphere());
- c->geom=ss;
-
- // constants
- if(distFactor>0) ss->refLength=dist;
- else ss->refLength=s1->radius+s2->radius;
- ss->refR1=s1->radius; ss->refR2=s2->radius;
-
- Real penetrationDepth=s1->radius+s2->radius-ss->refLength;
-
- if(scene->iter<=10){
- ss->effR1=s1->radius-.5*penetrationDepth; ss->effR2=s2->radius-.5*penetrationDepth;
- } else {ss->effR1=s1->radius; ss->effR2=s2->radius;}
-
- // for bending only: ss->initRelOri12=state1.ori.Conjugate()*state2.ori;
- // quasi-constants
- ss->cp1rel.setFromTwoVectors(Vector3r::UnitX(),state1.ori.conjugate()*normal);
- ss->cp2rel.setFromTwoVectors(Vector3r::UnitX(),state2.ori.conjugate()*(-normal));
- ss->cp1rel.normalize(); ss->cp2rel.normalize();
- }
- ss->normal=normal;
- ss->contactPoint=state1.pos+(ss->effR1-.5*(ss->refLength-dist))*ss->normal;
- ss->se31=state1.se3; ss->se32=state2.se3; ss->se32.position+=shift2;
- return true;
-}
-
=== removed file 'pkg/dem/Dem3DofGeom_SphereSphere.hpp'
--- pkg/dem/Dem3DofGeom_SphereSphere.hpp 2011-01-09 16:34:50 +0000
+++ pkg/dem/Dem3DofGeom_SphereSphere.hpp 1970-01-01 00:00:00 +0000
@@ -1,80 +0,0 @@
-#pragma once
-#include<yade/pkg/dem/DemXDofGeom.hpp>
-#include<yade/pkg/common/Sphere.hpp>
-
-class Dem3DofGeom_SphereSphere: public Dem3DofGeom{
- public:
- // auxiliary functions; the quaternion magic is all in here
- static Vector3r unrollSpherePtToPlane(const Quaternionr& fromXtoPtOri, const Real& radius, const Vector3r& normal);
- static Quaternionr rollPlanePtToSphere(const Vector3r& planePt, const Real& radius, const Vector3r& normal);
- private:
- Vector3r contPtInTgPlane1() const { return unrollSpherePtToPlane(ori1*cp1rel,effR1,normal); }
- Vector3r contPtInTgPlane2() const { return unrollSpherePtToPlane(ori2*cp2rel,effR2,-normal);}
- void setTgPlanePts(Vector3r p1new, Vector3r p2new);
- void relocateContactPoints();
- void relocateContactPoints(const Vector3r& tgPlanePt1, const Vector3r& tgPlanePt2);
- public:
- //! shorthands
- const Vector3r &pos1; const Quaternionr &ori1; const Vector3r &pos2; const Quaternionr &ori2;
- virtual ~Dem3DofGeom_SphereSphere();
-
- /********* API **********/
- Real displacementN(){ return (pos2-pos1).norm()-refLength; }
- Vector3r displacementT() {
- // enabling automatic relocation decreases overall simulation speed by about 3%; perhaps: bool largeStrains ... ?
- #if 1
- Vector3r p1=contPtInTgPlane1(), p2=contPtInTgPlane2(); relocateContactPoints(p1,p2); return p2-p1; // shear before relocation, but that is OK
- #else
- return contPtInTgPlane2()-contPtInTgPlane1();
- #endif
- }
- virtual Real slipToDisplacementTMax(Real displacementTMax);
- /********* end API ***********/
-
- YADE_CLASS_BASE_DOC_ATTRS_INIT_CTOR_PY(Dem3DofGeom_SphereSphere,Dem3DofGeom,"Class representing 2 spheres in contact which computes 3 degrees of freedom (normal and shear deformation).",
- ((Real,effR1,,,"Effective radius of sphere #1; can be smaller/larger than refR1 (the actual radius), but quasi-constant throughout interaction life"))
- ((Real,effR2,,,"Same as effR1, but for sphere #2."))
- ((Quaternionr,cp1rel,,,"Sphere's #1 relative orientation of the contact point with regards to sphere-local +x axis (quasi-constant)"))
- ((Quaternionr,cp2rel,,,"Same as cp1rel, but for sphere #2.")),
- /* extra initializers */ ((pos1,se31.position))((ori1,se31.orientation))((pos2,se32.position))((ori2,se32.orientation)),
- /*ctor*/ createIndex(); ,
- /*py*/
- );
- REGISTER_CLASS_INDEX(Dem3DofGeom_SphereSphere,Dem3DofGeom);
- friend class Gl1_Dem3DofGeom_SphereSphere;
- friend class Ig2_Sphere_Sphere_Dem3DofGeom;
-};
-REGISTER_SERIALIZABLE(Dem3DofGeom_SphereSphere);
-
-#ifdef YADE_OPENGL
- #include<yade/pkg/common/GLDrawFunctors.hpp>
- class Gl1_Dem3DofGeom_SphereSphere:public GlIGeomFunctor{
- public:
- virtual void go(const shared_ptr<IGeom>&,const shared_ptr<Interaction>&,const shared_ptr<Body>&,const shared_ptr<Body>&,bool wireFrame);
- RENDERS(Dem3DofGeom_SphereSphere);
- YADE_CLASS_BASE_DOC_STATICATTRS(Gl1_Dem3DofGeom_SphereSphere,GlIGeomFunctor,"Render interaction of 2 spheres (represented by Dem3DofGeom_SphereSphere)",
- ((bool,normal,false,,"Render interaction normal"))
- ((bool,rolledPoints,false,,"Render points rolled on the spheres (tracks the original contact point)"))
- ((bool,unrolledPoints,false,,"Render original contact points unrolled to the contact plane"))
- ((bool,shear,false,,"Render shear line in the contact plane"))
- ((bool,shearLabel,false,,"Render shear magnitude as number"))
- );
- };
- REGISTER_SERIALIZABLE(Gl1_Dem3DofGeom_SphereSphere);
-#endif
-
-#include<yade/pkg/common/Dispatching.hpp>
-class Ig2_Sphere_Sphere_Dem3DofGeom:public IGeomFunctor{
- public:
- virtual bool go(const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c);
- virtual bool goReverse( const shared_ptr<Shape>&, const shared_ptr<Shape>&, const State&, const State&, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>&){throw runtime_error("goReverse on symmetric functor should never be called!");}
- FUNCTOR2D(Sphere,Sphere);
- DEFINE_FUNCTOR_ORDER_2D(Sphere,Sphere);
- DECLARE_LOGGER;
- YADE_CLASS_BASE_DOC_ATTRS(Ig2_Sphere_Sphere_Dem3DofGeom,IGeomFunctor,
- "Functor handling contact of 2 spheres, producing Dem3DofGeom instance",
- ((Real,distFactor,-1,,"Factor of sphere radius such that sphere \"touch\" if their centers are not further than distFactor*(r1+r2); if negative, equilibrium distance is the sum of the sphere's radii."))
- );
-};
-REGISTER_SERIALIZABLE(Ig2_Sphere_Sphere_Dem3DofGeom);
-
=== removed file 'pkg/dem/Dem3DofGeom_WallSphere.cpp'
--- pkg/dem/Dem3DofGeom_WallSphere.cpp 2010-12-13 12:11:43 +0000
+++ pkg/dem/Dem3DofGeom_WallSphere.cpp 1970-01-01 00:00:00 +0000
@@ -1,117 +0,0 @@
-// © 2009 Václav Šmilauer <eudoxos@xxxxxxxx>
-#include<yade/pkg/dem/Dem3DofGeom_WallSphere.hpp>
-#include<yade/pkg/common/Sphere.hpp>
-#include<yade/pkg/common/Wall.hpp>
-YADE_PLUGIN((Dem3DofGeom_WallSphere)
- #ifdef YADE_OPENGL
- (Gl1_Dem3DofGeom_WallSphere)
- #endif
- (Ig2_Wall_Sphere_Dem3DofGeom));
-
-CREATE_LOGGER(Dem3DofGeom_WallSphere);
-Dem3DofGeom_WallSphere::~Dem3DofGeom_WallSphere(){}
-
-void Dem3DofGeom_WallSphere::setTgPlanePts(const Vector3r& p1new, const Vector3r& p2new){
- TRVAR3(cp1pt,cp2rel,contPtInTgPlane2()-contPtInTgPlane1());
- cp1pt=p1new+contactPoint-se31.position;
- cp2rel=se32.orientation.conjugate()*Dem3DofGeom_SphereSphere::rollPlanePtToSphere(p2new,effR2,-normal);
- TRVAR3(cp1pt,cp2rel,contPtInTgPlane2()-contPtInTgPlane1());
-}
-
-void Dem3DofGeom_WallSphere::relocateContactPoints(const Vector3r& p1, const Vector3r& p2){
- //TRVAR2(p2.norm(),effR2);
- if(p2.squaredNorm()>pow(effR2,2)){
- setTgPlanePts(Vector3r::Zero(),p2-p1);
- }
-}
-
-Real Dem3DofGeom_WallSphere::slipToDisplacementTMax(Real displacementTMax){
- //FIXME: not yet tested
- // negative or zero: reset shear
- if(displacementTMax<=0.){ setTgPlanePts(Vector3r(0,0,0),Vector3r(0,0,0)); return displacementTMax;}
- // otherwise
- Vector3r p1=contPtInTgPlane1(), p2=contPtInTgPlane2();
- Real currDistSq=(p2-p1).squaredNorm();
- if(currDistSq<pow(displacementTMax,2)) return 0; // close enough, no slip needed
- //Vector3r diff=.5*(sqrt(currDistSq)/displacementTMax-1)*(p2-p1); setTgPlanePts(p1+diff,p2-diff);
- Real scale=displacementTMax/sqrt(currDistSq); setTgPlanePts(scale*p1,scale*p2);
- return (displacementTMax/scale)*(1-scale);
-}
-
-CREATE_LOGGER(Ig2_Wall_Sphere_Dem3DofGeom);
-bool Ig2_Wall_Sphere_Dem3DofGeom::go(const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c){
- Wall* wall=static_cast<Wall*>(cm1.get());
- Real sphereRadius=static_cast<Sphere*>(cm2.get())->radius;
-
- Real dist=(state2.pos+shift2)[wall->axis]-state1.pos[wall->axis];
- if(!c->isReal() && abs(dist)>sphereRadius && !force){ /*LOG_DEBUG("dist="<<dist<<", returning false");*/ return false; } // wall and sphere too far from each other
-
- // contact point is sphere center projected onto the wall
- Vector3r contPt=state2.pos; contPt[wall->axis]=state1.pos[wall->axis];
- Vector3r normalGlob(0.,0.,0.);
- // wall interacting from both sides: normal depends on sphere's position
- assert(wall->sense==-1 || wall->sense==0 || wall->sense==1);
- if(wall->sense==0) normalGlob[wall->axis]=dist>0?1.:-1.;
- else normalGlob[wall->axis]=wall->sense==1?1.:-1;
-
- shared_ptr<Dem3DofGeom_WallSphere> ws;
- if(c->geom) ws=YADE_PTR_CAST<Dem3DofGeom_WallSphere>(c->geom);
- else {
- ws=shared_ptr<Dem3DofGeom_WallSphere>(new Dem3DofGeom_WallSphere());
- c->geom=ws;
- ws->effR2=abs(dist);
- ws->refR1=-1; ws->refR2=sphereRadius;
- ws->refLength=ws->effR2;
- ws->cp1pt=contPt-state1.pos; // initial contact point relative to wall position (orientation is global, since it is coincident with local for a wall)
- ws->cp2rel=Quaternionr::Identity();
- ws->cp2rel.setFromTwoVectors(Vector3r::UnitX(),state2.ori.conjugate()*(-normalGlob)); // initial sphere-local center-contactPt orientation WRT +x
- ws->cp2rel.normalize();
- //LOG_INFO(ws->cp1pt);
- }
- ws->se31=state1.se3; ws->se32=state2.se3; ws->se32.position+=shift2;
- ws->contactPoint=contPt;
- ws->normal=normalGlob;
- return true;
-}
-#ifdef YADE_OPENGL
-
- #include<yade/lib/opengl/OpenGLWrapper.hpp>
- #include<yade/lib/opengl/GLUtils.hpp>
-
- bool Gl1_Dem3DofGeom_WallSphere::normal=false;
- bool Gl1_Dem3DofGeom_WallSphere::rolledPoints=false;
- bool Gl1_Dem3DofGeom_WallSphere::unrolledPoints=false;
- bool Gl1_Dem3DofGeom_WallSphere::shear=false;
- bool Gl1_Dem3DofGeom_WallSphere::shearLabel=false;
-
- void Gl1_Dem3DofGeom_WallSphere::go(const shared_ptr<IGeom>& ig, const shared_ptr<Interaction>& ip, const shared_ptr<Body>& b1, const shared_ptr<Body>& b2, bool wireFrame){
- Dem3DofGeom_WallSphere* ws=static_cast<Dem3DofGeom_WallSphere*>(ig.get());
- const Se3r& se31=b1->state->se3,se32=b2->state->se3;
- const Vector3r& pos1=se31.position; const Vector3r& pos2=se32.position;
- //const Quaternionr& ori1=se31.orientation;
- const Quaternionr& ori2=se32.orientation;
- const Vector3r& contPt=ws->contactPoint;
-
- if(normal){
- GLUtils::GLDrawArrow(contPt,contPt+ws->refLength*ws->normal); // normal of the contact
- }
- // sphere center to point on the sphere
- if(rolledPoints){
- GLUtils::GLDrawLine(pos1+ws->cp1pt,contPt,Vector3r(0,.5,1));
- GLUtils::GLDrawLine(pos2,pos2+(ori2*ws->cp2rel*Vector3r::UnitX()*ws->effR2),Vector3r(0,1,.5));
- }
- // contact point to projected points
- if(unrolledPoints||shear){
- Vector3r ptTg1=ws->contPtInTgPlane1(), ptTg2=ws->contPtInTgPlane2();
- if(unrolledPoints){
- GLUtils::GLDrawLine(contPt,contPt+ptTg1,Vector3r(0,.5,1));
- GLUtils::GLDrawLine(contPt,contPt+ptTg2,Vector3r(0,1,.5)); GLUtils::GLDrawLine(pos2,contPt+ptTg2,Vector3r(0,1,.5));
- }
- if(shear){
- GLUtils::GLDrawLine(contPt+ptTg1,contPt+ptTg2,Vector3r(1,1,1));
- if(shearLabel) GLUtils::GLDrawNum(ws->displacementT().norm(),contPt,Vector3r(1,1,1));
- }
- }
- }
-
-#endif
=== removed file 'pkg/dem/Dem3DofGeom_WallSphere.hpp'
--- pkg/dem/Dem3DofGeom_WallSphere.hpp 2011-01-09 16:34:50 +0000
+++ pkg/dem/Dem3DofGeom_WallSphere.hpp 1970-01-01 00:00:00 +0000
@@ -1,70 +0,0 @@
-#pragma once
-#include<yade/pkg/dem/DemXDofGeom.hpp>
-// for static roll/unroll functions in Dem3DofGeom_SphereSphere
-#include<yade/pkg/dem/Dem3DofGeom_SphereSphere.hpp>
-#include<yade/pkg/common/Sphere.hpp>
-#include<yade/pkg/common/Wall.hpp>
-
-class Dem3DofGeom_WallSphere: public Dem3DofGeom{
-
- Vector3r contPtInTgPlane1() const { return se31.position+cp1pt-contactPoint; }
- Vector3r contPtInTgPlane2() const { return Dem3DofGeom_SphereSphere::unrollSpherePtToPlane(se32.orientation*cp2rel,effR2,-normal);}
-
- public:
- virtual ~Dem3DofGeom_WallSphere();
- /******* API ********/
- virtual Real displacementN(){ return (se32.position-contactPoint).norm()-refLength;}
- virtual Vector3r displacementT(){ relocateContactPoints(); return contPtInTgPlane2()-contPtInTgPlane1(); }
- virtual Real slipToDisplacementTMax(Real displacementTMax);
- /***** end API ******/
-
- void setTgPlanePts(const Vector3r&, const Vector3r&);
- void relocateContactPoints(){ relocateContactPoints(contPtInTgPlane1(),contPtInTgPlane2()); }
- void relocateContactPoints(const Vector3r& p1, const Vector3r& p2);
-
- YADE_CLASS_BASE_DOC_ATTRS_CTOR(Dem3DofGeom_WallSphere,Dem3DofGeom,"Representation of contact between wall and sphere, based on Dem3DofGeom.",
- ((Vector3r,cp1pt,,,"initial contact point on the wall, relative to the current contact point"))
- ((Quaternionr,cp2rel,,,"orientation between +x and the reference contact point (on the sphere) in sphere-local coords"))
- ((Real,effR2,,,"effective radius of sphere")),
- /*ctor*/ createIndex();
- );
- REGISTER_CLASS_INDEX(Dem3DofGeom_WallSphere,Dem3DofGeom);
- DECLARE_LOGGER;
- friend class Gl1_Dem3DofGeom_WallSphere;
- friend class Ig2_Wall_Sphere_Dem3DofGeom;
-};
-REGISTER_SERIALIZABLE(Dem3DofGeom_WallSphere);
-
-#ifdef YADE_OPENGL
- #include<yade/pkg/common/GLDrawFunctors.hpp>
- class Gl1_Dem3DofGeom_WallSphere:public GlIGeomFunctor{
- public:
- virtual void go(const shared_ptr<IGeom>&,const shared_ptr<Interaction>&,const shared_ptr<Body>&,const shared_ptr<Body>&,bool wireFrame);
- RENDERS(Dem3DofGeom_WallSphere);
- YADE_CLASS_BASE_DOC_STATICATTRS(Gl1_Dem3DofGeom_WallSphere,GlIGeomFunctor,"Render interaction of wall and sphere (represented by Dem3DofGeom_WallSphere)",
- ((bool,normal,false,,"Render interaction normal"))
- ((bool,rolledPoints,false,,"Render points rolled on the spheres (tracks the original contact point)"))
- ((bool,unrolledPoints,false,,"Render original contact points unrolled to the contact plane"))
- ((bool,shear,false,,"Render shear line in the contact plane"))
- ((bool,shearLabel,false,,"Render shear magnitude as number"))
- );
- };
- REGISTER_SERIALIZABLE(Gl1_Dem3DofGeom_WallSphere);
-#endif
-
-#include<yade/pkg/common/Dispatching.hpp>
-class Ig2_Wall_Sphere_Dem3DofGeom:public IGeomFunctor{
- public:
- virtual bool go(const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c);
- virtual bool goReverse( const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c){
- c->swapOrder(); return go(cm2,cm1,state2,state1,-shift2,force,c);
- LOG_ERROR("!! goReverse might not work in Ig2_Wall_Sphere_Dem3DofGeom. IGeomDispatcher should swap interaction members first and call go(...) afterwards.");
- }
-
- FUNCTOR2D(Wall,Sphere);
- DEFINE_FUNCTOR_ORDER_2D(Wall,Sphere);
- YADE_CLASS_BASE_DOC(Ig2_Wall_Sphere_Dem3DofGeom,IGeomFunctor,"Create/update contact of :yref:`Wall` and :yref:`Sphere` (:yref:`Dem3DofGeom_WallSphere` instance)");
- DECLARE_LOGGER;
-};
-REGISTER_SERIALIZABLE(Ig2_Wall_Sphere_Dem3DofGeom);
-
=== modified file 'pkg/dem/DemXDofGeom.cpp'
--- pkg/dem/DemXDofGeom.cpp 2010-10-13 16:23:08 +0000
+++ pkg/dem/DemXDofGeom.cpp 2013-08-29 10:30:31 +0000
@@ -1,5 +1,3 @@
#include"DemXDofGeom.hpp"
-YADE_PLUGIN((GenericSpheresContact)(Dem3DofGeom));
-Real Dem3DofGeom::displacementN(){throw;}
-Dem3DofGeom::~Dem3DofGeom(){}
+YADE_PLUGIN((GenericSpheresContact));
GenericSpheresContact::~GenericSpheresContact(){}
=== modified file 'pkg/dem/DemXDofGeom.hpp'
--- pkg/dem/DemXDofGeom.hpp 2011-01-24 14:45:35 +0000
+++ pkg/dem/DemXDofGeom.hpp 2013-08-29 10:30:31 +0000
@@ -2,12 +2,12 @@
#pragma once
#include<yade/core/IGeom.hpp>
-/*! Abstract class that unites ScGeom and Dem3DofGeom,
+/*! Abstract class that unites ScGeom and L3Geom,
created for the purposes of GlobalStiffnessTimeStepper.
It might be removed in the future. */
class GenericSpheresContact: public IGeom{
YADE_CLASS_BASE_DOC_ATTRS_CTOR(GenericSpheresContact,IGeom,
- "Class uniting :yref:`ScGeom` and :yref:`Dem3DofGeom`, for the purposes of :yref:`GlobalStiffnessTimeStepper`. (It might be removed inthe future). Do not use this class directly.",
+ "Class uniting :yref:`ScGeom` and :yref:`L3Geom`, for the purposes of :yref:`GlobalStiffnessTimeStepper`. (It might be removed inthe future). Do not use this class directly.",
((Vector3r,normal,,,"Unit vector oriented along the interaction, from particle #1, towards particle #2. |yupdate|"))
((Vector3r,contactPoint,,,"some reference point for the interaction (usually in the middle). |ycomp|"))
((Real,refR1,,,"Reference radius of particle #1. |ycomp|"))
@@ -19,45 +19,3 @@
virtual ~GenericSpheresContact(); // vtable
};
REGISTER_SERIALIZABLE(GenericSpheresContact);
-
-/*! Abstract base class for representing contact geometry of 2 elements that has 3 degrees of freedom:
- * normal (1 component) and shear (Vector3r, but in plane perpendicular to the normal)
- */
-class Dem3DofGeom: public GenericSpheresContact{
- public:
- virtual ~Dem3DofGeom();
-
- // API that needs to be implemented in derived classes
- virtual Real displacementN();
- virtual Vector3r displacementT(){throw;}
- virtual Real slipToDisplacementTMax(Real displacementTMax){throw;}; // plasticity
- // reference radii, for contact stiffness computation; set to negative for nonsense values
- // end API
-
- Real strainN(){
- //if(!logCompression)
- return displacementN()/refLength;
- //else{Real dn=displacementN(); }
- }
- Vector3r strainT(){return displacementT()/refLength;}
- Real slipToStrainTMax(Real strainTMax){return slipToDisplacementTMax(strainTMax*refLength)/refLength;}
-
- YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(Dem3DofGeom,GenericSpheresContact,
- "Abstract base class for representing contact geometry of 2 elements that has 3 degrees of freedom: normal (1 component) and shear (Vector3r, but in plane perpendicular to the normal).",
- ((Real,refLength,,,"some length used to convert displacements to strains. |ycomp|"))
- ((bool,logCompression,false,,"make strain go to -∞ for length going to zero (false by default)."))
- ((Se3r,se31,,,"Copy of body #1 se3 (needed to compute torque from the contact, strains etc). |yupdate|"))
- ((Se3r,se32,,,"Copy of body #2 se3. |yupdate|")),
- createIndex();,
- .def("displacementN",&Dem3DofGeom::displacementN)
- .def("displacementT",&Dem3DofGeom::displacementT)
- .def("strainN",&Dem3DofGeom::strainN)
- .def("strainT",&Dem3DofGeom::strainT)
- .def("slipToDisplacementTMax",&Dem3DofGeom::slipToDisplacementTMax)
- .def("slipToStrainTMax",&Dem3DofGeom::slipToStrainTMax)
- );
- REGISTER_CLASS_INDEX(Dem3DofGeom,IGeom);
-};
-REGISTER_SERIALIZABLE(Dem3DofGeom);
-
-
=== modified file 'pkg/dem/DomainLimiter.cpp'
--- pkg/dem/DomainLimiter.cpp 2013-03-07 18:28:01 +0000
+++ pkg/dem/DomainLimiter.cpp 2013-10-15 05:01:48 +0000
@@ -81,15 +81,14 @@
// test object types
GenericSpheresContact* gsc=dynamic_cast<GenericSpheresContact*>(I->geom.get());
ScGeom* scGeom=dynamic_cast<ScGeom*>(I->geom.get());
- Dem3DofGeom* d3dGeom=dynamic_cast<Dem3DofGeom*>(I->geom.get());
L3Geom* l3Geom=dynamic_cast<L3Geom*>(I->geom.get());
L6Geom* l6Geom=dynamic_cast<L6Geom*>(I->geom.get());
ScGeom6D* scGeom6d=dynamic_cast<ScGeom6D*>(I->geom.get());
bool hasRot=(l6Geom || scGeom6d);
//NormShearPhys* phys=dynamic_cast<NormShearPhys*>(I->phys.get()); //Disabled because of warning
if(!gsc) throw std::invalid_argument("LawTester: IGeom of "+strIds+" not a GenericSpheresContact.");
- if(!scGeom && !d3dGeom && !l3Geom) throw std::invalid_argument("LawTester: IGeom of "+strIds+" is neither ScGeom, nor Dem3DofGeom, nor L3Geom (or L6Geom).");
- assert(!((bool)scGeom && (bool)d3dGeom && (bool)l3Geom)); // nonsense
+ if(!scGeom && !l3Geom) throw std::invalid_argument("LawTester: IGeom of "+strIds+" is neither ScGeom, nor Dem3DofGeom, nor L3Geom (or L6Geom).");
+ assert(!((bool)scGeom && (bool)l3Geom)); // nonsense
// get body objects
State *state1=Body::byId(id1,scene)->state.get(), *state2=Body::byId(id2,scene)->state.get();
scene->forces.sync();
@@ -125,9 +124,7 @@
if(scGeom6d) uGeom.tail<3>()=-1.*Vector3r(scGeom6d->getTwist(),scGeom6d->getBending().dot(axY),scGeom6d->getBending().dot(axZ));
}
else{ // d3dGeom
- throw runtime_error("LawTester: Dem3DofGeom not yet supported.");
- // essentially copies code from ScGeom, which is not very nice indeed; oh well…
- // Vector3r vRel=(state2->vel+state2->angVel.cross(-gsc->refR2*gsc->normal))-(state1->vel+state1->angVel.cross(gsc->refR1*gsc->normal));
+ throw runtime_error("Geom type not yet supported.");
}
}
// update the transformation
@@ -248,11 +245,8 @@
// switch to local coordinates
glTranslatev(tester->contPt);
- #if EIGEN_WORLD_VERSION==2
- glMultMatrixd(Eigen::Transform3d(tester->trsf.transpose()).data());
- #elif EIGEN_WORLD_VERSION==3
- glMultMatrixd(Eigen::Affine3d(tester->trsf.transpose()).data());
- #endif
+ //glMultMatrixd(Eigen::Affine3d(tester->trsf.transpose()).data());
+ glMultMatrix(Eigen::Transform<Real,3,Eigen::Affine>(tester->trsf.transpose()).data());
glDisable(GL_LIGHTING);
=== modified file 'pkg/dem/FlowEngine.cpp'
--- pkg/dem/FlowEngine.cpp 2013-07-26 18:16:04 +0000
+++ pkg/dem/FlowEngine.cpp 2013-10-25 07:27:55 +0000
@@ -21,6 +21,10 @@
#include <boost/date_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
+#ifdef LINSOLV
+#include <cholmod.h>
+#endif
+
#include "FlowEngine.hpp"
CREATE_LOGGER ( FlowEngine );
@@ -83,7 +87,7 @@
Finite_vertices_iterator vertices_end = solver->T[solver->currentTes].Triangulation().finite_vertices_end();
for ( Finite_vertices_iterator V_it = solver->T[solver->currentTes].Triangulation().finite_vertices_begin(); V_it != vertices_end; V_it++ ) {
force = pressureForce ? Vector3r ( V_it->info().forces[0],V_it->info().forces[1],V_it->info().forces[2] ): Vector3r(0,0,0);
- if (viscousShear || shearLubrication){
+ if (shearLubrication || viscousShear){
force = force + solver->viscousShearForces[V_it->info().id()];
scene->forces.addTorque ( V_it->info().id(), solver->viscousShearTorques[V_it->info().id()]);
}
@@ -219,6 +223,20 @@
flow->x_min = 1000.0, flow->x_max = -10000.0, flow->y_min = 1000.0, flow->y_max = -10000.0, flow->z_min = 1000.0, flow->z_max = -10000.0;
}
+#ifdef LINSOLV
+template<class Solver>
+void FlowEngine::setForceMetis ( Solver& flow, bool force )
+{
+ if (force) {
+ flow->eSolver.cholmod().nmethods=1;
+ flow->eSolver.cholmod().method[0].ordering=CHOLMOD_METIS;
+ } else cholmod_defaults(&(flow->eSolver.cholmod()));
+}
+
+template<class Solver>
+bool FlowEngine::getForceMetis ( Solver& flow ) {return (flow->eSolver.cholmod().nmethods==1);}
+#endif
+
template<class Solver>
void FlowEngine::Build_Triangulation ( Solver& flow )
{
@@ -258,7 +276,7 @@
if ( !first && !multithread && (useSolver==0 || fluidBulkModulus>0)) flow->Interpolate ( flow->T[!flow->currentTes], flow->T[flow->currentTes] );
if ( WaveAction ) flow->ApplySinusoidalPressure ( flow->T[flow->currentTes].Triangulation(), sineMagnitude, sineAverage, 30 );
- if ( viscousShear || normalLubrication || shearLubrication) flow->computeEdgesSurfaces();
+ if (normalLubrication || shearLubrication || viscousShear) flow->computeEdgesSurfaces();
}
void FlowEngine::setPositionsBuffer(bool current)
@@ -377,7 +395,6 @@
typedef typename Solver::element_type Flow;
typedef typename Flow::Finite_vertices_iterator Finite_vertices_iterator;
typedef typename Solver::element_type Flow;
- typedef typename Flow::Finite_cells_iterator Finite_cells_iterator;
Finite_vertices_iterator vertices_end = flow->T[flow->currentTes].Triangulation().finite_vertices_end();
CGT::Vecteur Zero(0,0,0);
@@ -565,7 +582,7 @@
template<class Solver>
void FlowEngine::ComputeViscousForces ( Solver& flow )
{
- if (viscousShear || normalLubrication || shearLubrication){
+ if (normalLubrication || shearLubrication || viscousShear){
if ( Debug ) cout << "Application of viscous forces" << endl;
if ( Debug ) cout << "Number of edges = " << flow.Edge_ids.size() << endl;
for ( unsigned int k=0; k<flow.viscousShearForces.size(); k++ ) flow.viscousShearForces[k]=Vector3r::Zero();
@@ -576,6 +593,8 @@
typedef typename Solver::Tesselation Tesselation;
const Tesselation& Tes = flow.T[flow.currentTes];
+ flow.deltaShearVel.clear(); flow.normalV.clear(); flow.deltaNormVel.clear(); flow.surfaceDistance.clear(); flow.onlySpheresInteractions.clear(); flow.normalStressInteraction.clear(); flow.shearStressInteraction.clear();
+
for ( int i=0; i< ( int ) flow.Edge_ids.size(); i++ ) {
const int& id1 = flow.Edge_ids[i].first;
@@ -631,26 +650,35 @@
}
}
deltaShearV = deltaV - ( normal.dot ( deltaV ) ) *normal;
-
+ flow.deltaShearVel.push_back(deltaShearV);
+ flow.normalV.push_back(normal);
+ flow.surfaceDistance.push_back(max(surfaceDist, 0.) + eps*meanRad);
+
if (shearLubrication)
visc_f = flow.computeShearLubricationForce(deltaShearV,surfaceDist,i,eps,O1O2,meanRad);
else if (viscousShear)
visc_f = flow.computeViscousShearForce ( deltaShearV, i , Rh);
-
+
if (viscousShear || shearLubrication){
+
flow.viscousShearForces[id1]+=visc_f;
flow.viscousShearForces[id2]+=(-visc_f);
flow.viscousShearTorques[id1]+=O1C_vect.cross(visc_f);
flow.viscousShearTorques[id2]+=O2C_vect.cross(-visc_f);
+
/// Compute the viscous shear stress on each particle
if (viscousShearBodyStress){
flow.viscousBodyStress[id1] += visc_f * O1C_vect.transpose()/ (4.0/3.0 *3.14* pow(r1,3));
- flow.viscousBodyStress[id2] += (-visc_f) * O2C_vect.transpose()/ (4.0/3.0 *3.14* pow(r2,3));}
+ flow.viscousBodyStress[id2] += (-visc_f) * O2C_vect.transpose()/ (4.0/3.0 *3.14* pow(r2,3));
+ flow.shearStressInteraction.push_back(visc_f * O1O2_vect.transpose()/(4.0/3.0 *3.14* pow(r1,3)));
+ }
}
+
/// Compute the normal lubrication force applied on each particle
if (normalLubrication){
deltaNormV = normal.dot(deltaV);
+ flow.deltaNormVel.push_back(deltaNormV * normal);
lub_f = flow.computeNormalLubricationForce (deltaNormV, surfaceDist, i,eps,stiffness,scene->dt,meanRad)*normal;
flow.normLubForce[id1]+=lub_f;
flow.normLubForce[id2]+=(-lub_f);
@@ -658,9 +686,20 @@
/// Compute the normal lubrication stress on each particle
if (viscousNormalBodyStress){
flow.lubBodyStress[id1] += lub_f * O1C_vect.transpose()/ (4.0/3.0 *3.14* pow(r1,3));
- flow.lubBodyStress[id2] += (-lub_f) *O2C_vect.transpose() / (4.0/3.0 *3.14* pow(r2,3));}
+ flow.lubBodyStress[id2] += (-lub_f) *O2C_vect.transpose() / (4.0/3.0 *3.14* pow(r2,3));
+ flow.normalStressInteraction.push_back(lub_f * O1O2_vect.transpose()/(4.0/3.0 *3.14* pow(r1,3)));
+ }
}
-
+
+ if (create_file){
+ std::ofstream velocity_file("result_velocity.txt",ios::app);
+ velocity_file << i << "\t" << deltaNormV * normal[0] << "\t" << deltaNormV * normal[1] << "\t" << deltaNormV * normal[2] << "\t" << deltaShearV[0] << "\t" << deltaShearV[1] << "\t" << deltaShearV[2] << "\t" << normal[0] << "\t" << normal[1] << "\t" << normal[2] << "\t" << max(surfaceDist, 0.) + eps*meanRad << endl;
+ velocity_file.close(); }
+
+ if (display_force) cout<<"force tangentielle "<<visc_f<< " force normale "<< lub_f<<endl;
+ if (!hasFictious)
+ flow.onlySpheresInteractions.push_back(i);
+
}
}
}
@@ -718,7 +757,7 @@
assert (Tes.vertexHandles[id] != NULL);
const Tesselation::Vertex_Info& v_info = Tes.vertexHandles[id]->info();
force =(pressureForce) ? Vector3r ( ( v_info.forces ) [0],v_info.forces[1],v_info.forces[2] ) : Vector3r(0,0,0);
- if (viscousShear){
+ if (shearLubrication || viscousShear){
force = force +solver->viscousShearForces[v_info.id()];
scene->forces.addTorque ( v_info.id(), solver->viscousShearTorques[v_info.id()]);
}
@@ -1072,7 +1111,8 @@
// if ( !first && (useSolver==0 || fluidBulkModulus>0)) flow->Interpolate ( flow->T[!flow->currentTes], flow->T[flow->currentTes] );
if ( WaveAction ) flow->ApplySinusoidalPressure ( Tes.Triangulation(), sineMagnitude, sineAverage, 30 );
- if ( viscousShear || normalLubrication || shearLubrication) flow->computeEdgesSurfaces();
+
+ if (normalLubrication || shearLubrication || viscousShear) flow->computeEdgesSurfaces();
if ( Debug ) cout << endl << "end buildTri------" << endl << endl;
}
=== modified file 'pkg/dem/FlowEngine.hpp'
--- pkg/dem/FlowEngine.hpp 2013-07-26 18:16:04 +0000
+++ pkg/dem/FlowEngine.hpp 2013-10-25 07:27:55 +0000
@@ -59,6 +59,10 @@
int ReTrg;
int ellapsedIter;
TPL void initSolver (Solver& flow);
+ #ifdef LINSOLV
+ TPL void setForceMetis (Solver& flow, bool force);
+ TPL bool getForceMetis (Solver& flow);
+ #endif
TPL void Triangulate (Solver& flow);
TPL void AddBoundary (Solver& flow);
TPL void Build_Triangulation (double P_zero, Solver& flow);
@@ -91,6 +95,24 @@
return (flow->viscousBodyStress.size()>id_sph)?flow->viscousBodyStress[id_sph]:Matrix3r::Zero();}
TPL Matrix3r bodyNormalLubStress(unsigned int id_sph, Solver& flow) {
return (flow->lubBodyStress.size()>id_sph)?flow->lubBodyStress[id_sph]:Matrix3r::Zero();}
+ TPL Vector3r shearVelocity(unsigned int interaction, Solver& flow) {
+ return (flow->deltaShearVel[interaction]);}
+ TPL Vector3r normalVelocity(unsigned int interaction, Solver& flow) {
+ return (flow->deltaNormVel[interaction]);}
+ TPL Matrix3r normalStressInteraction(unsigned int interaction, Solver& flow) {
+ return (flow->normalStressInteraction[interaction]);}
+ TPL Matrix3r shearStressInteraction(unsigned int interaction, Solver& flow) {
+ return (flow->shearStressInteraction[interaction]);}
+ TPL Vector3r normalVect(unsigned int interaction, Solver& flow) {
+ return (flow->normalV[interaction]);}
+ TPL Real surfaceDistanceParticle(unsigned int interaction, Solver& flow) {
+ return (flow->surfaceDistance[interaction]);}
+ TPL Real edgeSize(Solver& flow) {
+ return (flow->Edge_ids.size());}
+ TPL Real OSI(Solver& flow) {
+ return (flow->onlySpheresInteractions.size());}
+ TPL int onlySpheresInteractions(unsigned int interaction, Solver& flow) {
+ return (flow->onlySpheresInteractions[interaction]);}
TPL python::list getConstrictions(bool all, Solver& flow) {
vector<Real> csd=flow->getConstrictions(); python::list pycsd;
for (unsigned int k=0;k<csd.size();k++) if ((all && csd[k]!=0) || csd[k]>0) pycsd.append(csd[k]); return pycsd;}
@@ -143,6 +165,13 @@
Matrix3r _bodyShearLubStress(unsigned int id_sph) {return bodyShearLubStress(id_sph,solver);}
Matrix3r _bodyNormalLubStress(unsigned int id_sph) {return bodyNormalLubStress(id_sph,solver);}
Vector3r _fluidForce(unsigned int id_sph) {return fluidForce(id_sph,solver);}
+ Vector3r _shearVelocity(unsigned int interaction) {return shearVelocity(interaction,solver);}
+ Vector3r _normalVelocity(unsigned int interaction) {return normalVelocity(interaction,solver);}
+ Matrix3r _normalStressInteraction(unsigned int interaction) {return normalStressInteraction(interaction,solver);}
+ Matrix3r _shearStressInteraction(unsigned int interaction) {return shearStressInteraction(interaction,solver);}
+ Vector3r _normalVect(unsigned int interaction) {return normalVect(interaction,solver);}
+ Real _surfaceDistanceParticle(unsigned int interaction) {return surfaceDistanceParticle(interaction,solver);}
+ int _onlySpheresInteractions(unsigned int interaction) {return onlySpheresInteractions(interaction,solver);}
void _imposeFlux(Vector3r pos, Real flux) {return imposeFlux(pos,flux,*solver);}
unsigned int _imposePressure(Vector3r pos, Real p) {return imposePressure(pos,p,solver);}
void _setImposedPressure(unsigned int cond, Real p) {setImposedPressure(cond,p,solver);}
@@ -155,6 +184,13 @@
#ifdef LINSOLV
void _exportMatrix(string filename) {exportMatrix(filename,solver);}
void _exportTriplets(string filename) {exportTriplets(filename,solver);}
+ void _setForceMetis (bool force) {setForceMetis(solver,force);}
+ bool _getForceMetis () {return getForceMetis (solver);}
+ void cholmodStats() {
+ cerr << cholmod_print_common("PFV Cholmod factorization",&(solver->eSolver.cholmod()))<<endl;
+ cerr << "cholmod method:" << solver->eSolver.cholmod().selected<<endl;
+ cerr << "METIS called:"<<solver->eSolver.cholmod().called_nd<<endl;}
+ bool metisUsed() {return bool(solver->eSolver.cholmod().called_nd);}
#endif
python::list _getConstrictions(bool all) {return getConstrictions(all,solver);}
python::list _getConstrictionsFull(bool all) {return getConstrictionsFull(all,solver);}
@@ -164,7 +200,7 @@
virtual void action();
virtual void backgroundAction();
- YADE_CLASS_BASE_DOC_ATTRS_DEPREC_INIT_CTOR_PY(FlowEngine,PartialEngine,"An engine to solve flow problem in saturated granular media.\n\n.. note::\n\t Multi-threading seems to work fine for Cholesky decomposition, but it fails for the solve phase in which -j1 is the fastest, here we specify thread numbers independently using :yref:`FlowEngine::numFactorizeThreads` and :yref:`FlowEngine::numSolveThreads`. These multhreading settings are only impacting the behaviour of openblas library and are relatively independant of :yref:`FlowEngine::multithread`. However, the settings have to be globally consistent. For instance, :yref:`FlowEngine::multithread`=True with multithread :yref:`FlowEngine::numSolveThreads`=:yref:`FlowEngine::numFactorizeThreads`=4 means that openblas will mobilize 8 processors at some point, if the system doesn't have so many procs. it will hurt performance.",
+ YADE_CLASS_BASE_DOC_ATTRS_DEPREC_INIT_CTOR_PY(FlowEngine,PartialEngine,"An engine to solve flow problem in saturated granular media. Model description can be found in [Chareyre2012a]_ and [Catalano2013a]_. See the example script FluidCouplingPFV/oedometer.py. More documentation to come.\n\n.. note::Multi-threading seems to work fine for Cholesky decomposition, but it fails for the solve phase in which -j1 is the fastest, here we specify thread numbers independently using :yref:`FlowEngine::numFactorizeThreads` and :yref:`FlowEngine::numSolveThreads`. These multhreading settings are only impacting the behaviour of openblas library and are relatively independant of :yref:`FlowEngine::multithread`. However, the settings have to be globally consistent. For instance, :yref:`multithread<FlowEngine::multithread>` =True with yref:`numFactorizeThreads<FlowEngine::numFactorizeThreads>` = yref:`numSolveThreads<FlowEngine::numSolveThreads>` = 4 implies that openblas will mobilize 8 processors at some point. If the system does not have so many procs. it will hurt performance.",
((bool,isActivated,true,,"Activates Flow Engine"))
((bool,first,true,,"Controls the initialization/update phases"))
((double, fluidBulkModulus, 0.,,"Bulk modulus of fluid (inverse of compressibility) K=-dP*V/dV [Pa]. Flow is compressible if fluidBulkModulus > 0, else incompressible."))
@@ -188,7 +224,7 @@
((bool,clampKValues,true,,"If true, clamp local permeabilities in [minKdivKmean,maxKdivKmean]*globalK. This clamping can avoid singular values in the permeability matrix and may reduce numerical errors in the solve phase. It will also hide junk values if they exist, or bias all values in very heterogeneous problems. So, use this with care."))
((Real,minKdivKmean,0.0001,,"define the min K value (see :yref:`FlowEngine::clampKValues`)"))
((Real,maxKdivKmean,100,,"define the max K value (see :yref:`FlowEngine::clampKValues`)"))
- ((double,permeabilityFactor,0.0,,"permability multiplier"))
+ ((double,permeabilityFactor,1.0,,"permability multiplier"))
((double,viscosity,1.0,,"viscosity of the fluid"))
((double,stiffness, 10000,,"equivalent contact stiffness used in the lubrication model"))
((int, useSolver, 0,, "Solver to use 0=G-Seidel, 1=Taucs, 2-Pardiso, 3-CHOLMOD"))
@@ -206,6 +242,8 @@
((int, ignoredBody,-1,,"Id of a sphere to exclude from the triangulation.)"))
((vector<int>, wallIds,vector<int>(6),,"body ids of the boundaries (default values are ok only if aabbWalls are appended before spheres, i.e. numbered 0,...,5)"))
((vector<bool>, boundaryUseMaxMin, vector<bool>(6,true),,"If true (default value) bounding sphere is added as function of max/min sphere coord, if false as function of yade wall position"))
+ ((bool, display_force, false,,"display the lubrication force applied on particles"))
+ ((bool, create_file, false,,"create file of velocities"))
((bool, viscousShear, false,,"Compute viscous shear terms as developped by Donia Marzougui (FIXME: ref.)"))
((bool, shearLubrication, false,,"Compute shear lubrication force as developped by Brule (FIXME: ref.) "))
((double, eps, 0.00001,,"roughness defined as a fraction of particles size, giving the minimum distance between particles in the lubrication model."))
@@ -217,6 +255,7 @@
#ifdef EIGENSPARSE_LIB
((int, numSolveThreads, 1,,"number of openblas threads in the solve phase."))
((int, numFactorizeThreads, 1,,"number of openblas threads in the factorization phase"))
+// ((bool, forceMetis, 0,,"If true, METIS is used for matrix preconditioning, else Cholmod is free to choose the best method (which may be METIS to, depending on the matrix). See ``nmethods`` in Cholmod documentation"))
#endif
,
/*deprec*/
@@ -240,7 +279,7 @@
.def("clearImposedPressure",&FlowEngine::_clearImposedPressure,"Clear the list of points with pressure imposed.")
.def("clearImposedFlux",&FlowEngine::_clearImposedFlux,"Clear the list of points with flux imposed.")
.def("getCellFlux",&FlowEngine::_getCellFlux,(python::arg("cond")),"Get influx in cell associated to an imposed P (indexed using 'cond').")
- .def("getBoundaryFlux",&FlowEngine::_getBoundaryFlux,(python::arg("boundary")),"Get total flux through boundary defined by its body id.\n\n.. note::\n\t The flux may be not zero even for no-flow condition. This artifact comes from cells which are incident to two or more boundaries (along the edges of the sample, typically). Such flux evaluation on impermeable boundary is just irrelevant, it does not imply that the boundary condition is not applied properly.")
+ .def("getBoundaryFlux",&FlowEngine::_getBoundaryFlux,(python::arg("boundary")),"Get total flux through boundary defined by its body id.\n\n.. note:: The flux may be not zero even for no-flow condition. This artifact comes from cells which are incident to two or more boundaries (along the edges of the sample, typically). Such flux evaluation on impermeable boundary is just irrelevant, it does not imply that the boundary condition is not applied properly.")
.def("getConstrictions",&FlowEngine::_getConstrictions,(python::arg("all")=true),"Get the list of constriction radii (inscribed circle) for all finite facets (if all==True) or all facets not incident to a virtual bounding sphere (if all==False). When all facets are returned, negative radii denote facet incident to one or more fictious spheres.")
.def("getConstrictionsFull",&FlowEngine::_getConstrictionsFull,(python::arg("all")=true),"Get the list of constrictions (inscribed circle) for all finite facets (if all==True), or all facets not incident to a fictious bounding sphere (if all==False). When all facets are returned, negative radii denote facet incident to one or more fictious spheres. The constrictions are returned in the format {{cell1,cell2}{rad,nx,ny,nz}}")
.def("saveVtk",&FlowEngine::saveVtk,"Save pressure field in vtk format.")
@@ -251,6 +290,13 @@
.def("normalLubForce",&FlowEngine::_normalLubForce,(python::arg("Id_sph")),"Return the normal lubrication force on sphere Id_sph.")
.def("bodyShearLubStress",&FlowEngine::_bodyShearLubStress,(python::arg("Id_sph")),"Return the shear lubrication stress on sphere Id_sph.")
.def("bodyNormalLubStress",&FlowEngine::_bodyNormalLubStress,(python::arg("Id_sph")),"Return the normal lubrication stress on sphere Id_sph.")
+ .def("shearVelocity",&FlowEngine::_shearVelocity,(python::arg("id_sph")),"Return the shear velocity of the interaction.")
+ .def("normalVelocity",&FlowEngine::_normalVelocity,(python::arg("id_sph")),"Return the normal velocity of the interaction.")
+ .def("normalVect",&FlowEngine::_normalVect,(python::arg("id_sph")),"Return the normal vector between particles.")
+ .def("surfaceDistanceParticle",&FlowEngine::_surfaceDistanceParticle,(python::arg("interaction")),"Return the distance between particles.")
+ .def("onlySpheresInteractions",&FlowEngine::_onlySpheresInteractions,(python::arg("interaction")),"Return the id of the interaction only between spheres.")
+
+
.def("pressureProfile",&FlowEngine::pressureProfile,(python::arg("wallUpY"),python::arg("wallDownY")),"Measure pore pressure in 6 equally-spaced points along the height of the sample")
.def("getPorePressure",&FlowEngine::getPorePressure,(python::arg("pos")),"Measure pore pressure in position pos[0],pos[1],pos[2]")
.def("averageSlicePressure",&FlowEngine::averageSlicePressure,(python::arg("posY")),"Measure slice-averaged pore pressure at height posY")
@@ -261,6 +307,9 @@
#ifdef LINSOLV
.def("exportMatrix",&FlowEngine::_exportMatrix,(python::arg("filename")="matrix"),"Export system matrix to a file with all entries (even zeros will displayed).")
.def("exportTriplets",&FlowEngine::_exportTriplets,(python::arg("filename")="triplets"),"Export system matrix to a file with only non-zero entries.")
+ .def("cholmodStats",&FlowEngine::cholmodStats,"get statistics of cholmod solver activity")
+ .def("metisUsed",&FlowEngine::metisUsed,"check wether metis lib is effectively used")
+ .add_property("forceMetis",&FlowEngine::_getForceMetis,&FlowEngine::_setForceMetis,"If true, METIS is used for matrix preconditioning, else Cholmod is free to choose the best method (which may be METIS to, depending on the matrix). See ``nmethods`` in Cholmod documentation")
#endif
)
DECLARE_LOGGER;
@@ -328,6 +377,15 @@
Vector3r _normalLubForce(unsigned int id_sph) {return normalLubForce(id_sph,solver);}
Matrix3r _bodyShearLubStress(unsigned int id_sph) {return bodyShearLubStress(id_sph,solver);}
Matrix3r _bodyNormalLubStress(unsigned int id_sph) {return bodyNormalLubStress(id_sph,solver);}
+ Matrix3r _normalStressInteraction(unsigned int interaction) {return normalStressInteraction(interaction,solver);}
+ Matrix3r _shearStressInteraction(unsigned int interaction) {return shearStressInteraction(interaction,solver);}
+ Vector3r _shearVelocity(unsigned int id_sph) {return shearVelocity(id_sph,solver);}
+ Vector3r _normalVelocity(unsigned int id_sph) {return normalVelocity(id_sph,solver);}
+ Vector3r _normalVect(unsigned int id_sph) {return normalVect(id_sph,solver);}
+ Real _surfaceDistanceParticle(unsigned int interaction) {return surfaceDistanceParticle(interaction,solver);}
+ int _onlySpheresInteractions(unsigned int interaction) {return onlySpheresInteractions(interaction,solver);}
+ Real _edgeSize() {return edgeSize(solver);}
+ Real _OSI() {return OSI(solver);}
Vector3r _fluidForce(unsigned int id_sph) {return fluidForce(id_sph, solver);}
void _imposeFlux(Vector3r pos, Real flux) {return imposeFlux(pos,flux,*solver);}
@@ -380,6 +438,15 @@
.def("normalLubForce",&PeriodicFlowEngine::_normalLubForce,(python::arg("Id_sph")),"Return the normal lubrication force on sphere Id_sph.")
.def("bodyShearLubStress",&PeriodicFlowEngine::_bodyShearLubStress,(python::arg("Id_sph")),"Return the shear lubrication stress on sphere Id_sph.")
.def("bodyNormalLubStress",&PeriodicFlowEngine::_bodyNormalLubStress,(python::arg("Id_sph")),"Return the normal lubrication stress on sphere Id_sph.")
+ .def("shearVelocity",&PeriodicFlowEngine::_shearVelocity,(python::arg("id_sph")),"Return the shear velocity of the interaction.")
+ .def("normalStressInteraction",&PeriodicFlowEngine::_normalStressInteraction,(python::arg("id_sph")),"Return the shear velocity of the interaction.")
+ .def("shearStressInteraction",&PeriodicFlowEngine::_shearStressInteraction,(python::arg("id_sph")),"Return the shear velocity of the interaction.")
+ .def("normalVelocity",&PeriodicFlowEngine::_normalVelocity,(python::arg("id_sph")),"Return the normal velocity of the interaction.")
+ .def("normalVect",&PeriodicFlowEngine::_normalVect,(python::arg("id_sph")),"Return the normal vector between particles.")
+ .def("surfaceDistanceParticle",&PeriodicFlowEngine::_surfaceDistanceParticle,(python::arg("interaction")),"Return the distance between particles.")
+ .def("onlySpheresInteractions",&PeriodicFlowEngine::_onlySpheresInteractions,(python::arg("interaction")),"Return the id of the interaction only between spheres.")
+ .def("edgeSize",&PeriodicFlowEngine::_edgeSize,"Return the number of interactions.")
+ .def("OSI",&PeriodicFlowEngine::_OSI,"Return the number of interactions only between spheres.")
// .def("imposeFlux",&FlowEngine::_imposeFlux,(python::arg("pos"),python::arg("p")),"Impose incoming flux in boundary cell of location 'pos'.")
.def("saveVtk",&PeriodicFlowEngine::saveVtk,"Save pressure field in vtk format.")
=== modified file 'pkg/dem/GlobalStiffnessTimeStepper.cpp'
--- pkg/dem/GlobalStiffnessTimeStepper.cpp 2013-05-24 16:48:19 +0000
+++ pkg/dem/GlobalStiffnessTimeStepper.cpp 2013-09-26 07:51:11 +0000
@@ -14,6 +14,7 @@
#include<yade/core/Scene.hpp>
#include<yade/core/Clump.hpp>
#include<yade/pkg/dem/Shop.hpp>
+#include<yade/pkg/dem/ViscoelasticPM.hpp>
CREATE_LOGGER(GlobalStiffnessTimeStepper);
YADE_PLUGIN((GlobalStiffnessTimeStepper));
@@ -25,13 +26,16 @@
State* sdec=body->state.get();
Vector3r& stiffness= stiffnesses[body->getId()];
Vector3r& Rstiffness=Rstiffnesses[body->getId()];
-
if(body->isClump()) {// if clump, we sum stifnesses of all members
const shared_ptr<Clump>& clump=YADE_PTR_CAST<Clump>(body->shape);
FOREACH(Clump::MemberMap::value_type& B, clump->members){
const shared_ptr<Body>& b = Body::byId(B.first,scene);
stiffness+=stiffnesses[b->getId()];
Rstiffness+=Rstiffnesses[b->getId()];
+ if (viscEl == true){
+ viscosities[body->getId()]+=viscosities[b->getId()];
+ Rviscosities[body->getId()]+=Rviscosities[b->getId()];
+ }
}
}
@@ -39,13 +43,15 @@
if (densityScaling) sdec->densityScaling = min(1.0001*sdec->densityScaling, timestepSafetyCoefficient*pow(defaultDt/targetDt,2.0));
return; // not possible to compute!
}
-
+
+ //Determine the elastic minimum eigenperiod (and if required determine also the viscous one separately and take the minimum of the two)
+
+ //Elastic
Real dtx, dty, dtz;
Real dt = max( max (stiffness.x(), stiffness.y()), stiffness.z() );
if (dt!=0) {
dt = sdec->mass/dt; computedSomething = true;}//dt = squared eigenperiod of translational motion
else dt = Mathr::MAX_REAL;
-
if (Rstiffness.x()!=0) {
dtx = sdec->inertia.x()/Rstiffness.x(); computedSomething = true;}//dtx = squared eigenperiod of rotational motion around x
else dtx = Mathr::MAX_REAL;
@@ -56,15 +62,43 @@
dtz = sdec->inertia.z()/Rstiffness.z(); computedSomething = true;}
else dtz = Mathr::MAX_REAL;
- Real Rdt = std::min( std::min (dtx, dty), dtz );//Rdt = smallest squared eigenperiod for rotational motions
+ Real Rdt = std::min( std::min (dtx, dty), dtz );//Rdt = smallest squared eigenperiod for elastic rotational motions
dt = 1.41044*timestepSafetyCoefficient*std::sqrt(std::min(dt,Rdt));//1.41044 = sqrt(2)
+
+ //Viscous
+ if (viscEl == true){
+ Vector3r& viscosity = viscosities[body->getId()];
+ Vector3r& Rviscosity = Rviscosities[body->getId()];
+ Real dtx_visc, dty_visc, dtz_visc;
+ Real dt_visc = max(max(viscosity.x(), viscosity.y()), viscosity.z() );
+ if (dt_visc!=0) {
+ dt_visc = sdec->mass/dt_visc; computedSomething = true;}//dt = eigenperiod of the viscous translational motion
+ else {dt_visc = Mathr::MAX_REAL;}
+
+ if (Rviscosity.x()!=0) {
+ dtx_visc = sdec->inertia.x()/Rviscosity.x(); computedSomething = true;}//dtx = eigenperiod of viscous rotational motion around x
+ else dtx_visc = Mathr::MAX_REAL;
+ if (Rviscosity.y()!=0) {
+ dty_visc = sdec->inertia.y()/Rviscosity.y(); computedSomething = true;}
+ else dty_visc = Mathr::MAX_REAL;
+ if (Rviscosity.z()!=0) {
+ dtz_visc = sdec->inertia.z()/Rviscosity.z(); computedSomething = true;}
+ else dtz_visc = Mathr::MAX_REAL;
+
+ Real Rdt_visc = std::min( std::min (dtx_visc, dty_visc), dtz_visc );//Rdt = smallest squared eigenperiod for viscous rotational motions
+ dt_visc = 2*timestepSafetyCoefficient*std::min(dt_visc,Rdt_visc);
+
+ //Take the minimum between the elastic and viscous minimum eigenperiod.
+ dt = std::min(dt,dt_visc);
+ }
+
//if there is a target dt, then we apply density scaling on the body, the inertia used in Newton will be mass*scaling, the weight is unmodified
if (densityScaling) {
sdec->densityScaling = min(sdec->densityScaling,timestepSafetyCoefficient*pow(dt /targetDt,2.0));
newDt=targetDt;
}
//else we update dt normaly
- else {newDt = std::min(dt,newDt);}
+ else {newDt = std::min(dt,newDt);}
}
bool GlobalStiffnessTimeStepper::isActivated()
@@ -97,7 +131,7 @@
computedOnce = true;}
else if (!computedOnce) scene->dt=defaultDt;
LOG_INFO("computed timestep " << newDt <<
- (scene->dt==newDt ? string(", appplied") :
+ (scene->dt==newDt ? string(", applied") :
string(", BUT timestep is ")+lexical_cast<string>(scene->dt))<<".");
}
@@ -107,15 +141,24 @@
if(size<rb->bodies->size()){
size=rb->bodies->size();
stiffnesses.resize(size); Rstiffnesses.resize(size);
+ if (viscEl == true){
+ viscosities.resize(size); Rviscosities.resize(size);
+ }
}
/* reset stored values */
memset(& stiffnesses[0],0,sizeof(Vector3r)*size);
memset(&Rstiffnesses[0],0,sizeof(Vector3r)*size);
+ if (viscEl == true){
+ memset(& viscosities[0],0,sizeof(Vector3r)*size);
+ memset(&Rviscosities[0],0,sizeof(Vector3r)*size);
+ }
+
FOREACH(const shared_ptr<Interaction>& contact, *rb->interactions){
if(!contact->isReal()) continue;
GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(contact->geom.get()); assert(geom);
NormShearPhys* phys=YADE_CAST<NormShearPhys*>(contact->phys.get()); assert(phys);
+
// all we need for getting stiffness
Vector3r& normal=geom->normal; Real& kn=phys->kn; Real& ks=phys->ks; Real& radius1=geom->refR1; Real& radius2=geom->refR2;
Real fn = (static_cast<NormShearPhys *> (contact->phys.get()))->normalForce.squaredNorm();
@@ -134,10 +177,35 @@
std::pow(normal.x(),2)+std::pow(normal.z(),2),
std::pow(normal.x(),2)+std::pow(normal.y(),2));
diag_Rstiffness *= ks;
+
+
//NOTE : contact laws with moments would be handled correctly by summing directly bending+twisting stiffness to diag_Rstiffness. The fact that there is no problem currently with e.g. cohesiveFrict law is probably because final computed dt is constrained by translational motion, not rotations.
stiffnesses [contact->getId1()]+=diag_stiffness;
Rstiffnesses[contact->getId1()]+=diag_Rstiffness*pow(radius1,2);
stiffnesses [contact->getId2()]+=diag_stiffness;
- Rstiffnesses[contact->getId2()]+=diag_Rstiffness*pow(radius2,2);
+ Rstiffnesses[contact->getId2()]+=diag_Rstiffness*pow(radius2,2);
+
+ //Same for the Viscous part, if required
+ if (viscEl == true){
+ ViscElPhys* viscPhys = YADE_CAST<ViscElPhys*>(contact->phys.get()); assert(viscPhys);
+ Real& cn=viscPhys->cn; Real& cs=viscPhys->cs;
+ //Diagonal terms of the translational viscous matrix
+ Vector3r diag_viscosity = Vector3r(std::pow(normal.x(),2),std::pow(normal.y(),2),std::pow(normal.z(),2));
+ diag_viscosity *= cn-cs;
+ diag_viscosity = diag_viscosity + Vector3r(1,1,1)*cs;
+ //diagonal terms of the rotational viscous matrix
+ Vector3r diag_Rviscosity =
+ Vector3r(std::pow(normal.y(),2)+std::pow(normal.z(),2),
+ std::pow(normal.x(),2)+std::pow(normal.z(),2),
+ std::pow(normal.x(),2)+std::pow(normal.y(),2));
+ diag_Rviscosity *= cs;
+
+ // Add the contact stiffness matrix to the two particles one
+ viscosities [contact->getId1()]+=diag_viscosity;
+ Rviscosities[contact->getId1()]+=diag_Rviscosity*pow(radius1,2);
+ viscosities [contact->getId2()]+=diag_viscosity;
+ Rviscosities[contact->getId2()]+=diag_Rviscosity*pow(radius2,2);
+ }
+
}
}
=== modified file 'pkg/dem/GlobalStiffnessTimeStepper.hpp'
--- pkg/dem/GlobalStiffnessTimeStepper.hpp 2013-04-09 14:14:48 +0000
+++ pkg/dem/GlobalStiffnessTimeStepper.hpp 2013-09-27 15:51:12 +0000
@@ -24,6 +24,8 @@
private :
vector<Vector3r> stiffnesses;
vector<Vector3r> Rstiffnesses;
+ vector<Vector3r> viscosities;
+ vector<Vector3r> Rviscosities;
void computeStiffnesses(Scene*);
Real newDt;
@@ -37,13 +39,14 @@
virtual void computeTimeStep(Scene*);
virtual bool isActivated();
YADE_CLASS_BASE_DOC_ATTRS_CTOR(
- GlobalStiffnessTimeStepper,TimeStepper,"An engine assigning the time-step as a fraction of the minimum eigen-period in the problem. The derivation is detailed in the chapter on `DEM formulation <https://www.yade-dem.org/doc/formulation.html#dem-simulations>`_",
+ GlobalStiffnessTimeStepper,TimeStepper,"An engine assigning the time-step as a fraction of the minimum eigen-period in the problem. The derivation is detailed in the chapter on `DEM formulation <https://www.yade-dem.org/doc/formulation.html#dem-simulations>`_. The viscEl option enables to evaluate the timestep in a similar way for the visco-elastic contact law :yref:`Law2_ScGeom_ViscElPhys_Basic`, more detail in :yref:`GlobalStiffnessTimestepper::viscEl`. ",
((Real,defaultDt,-1,,"used as the initial value of the timestep (especially useful in the first steps when no contact exist). If negative, it will be defined by :yref:`utils.PWaveTimeStep` * :yref:`GlobalStiffnessTimeStepper::timestepSafetyCoefficient`"))
((Real,maxDt,Mathr::MAX_REAL,,"if positive, used as max value of the timestep whatever the computed value"))
((Real,previousDt,1,,"last computed dt |yupdate|"))
((Real,timestepSafetyCoefficient,0.8,,"safety factor between the minimum eigen-period and the final assigned dt (less than 1))"))
((bool,densityScaling,false,,"|yupdate| don't modify this value if you don't plan to modify the scaling factor manually for some bodies. In most cases, it is enough to set :yref:`NewtonIntegrator::densityScaling` and let this one be adjusted automatically."))
- ((Real,targetDt,1,,"if :yref:`NewtonIntegrator::densityScaling` is active, this value will be used as the simulation timestep and the scaling will use this value of dt as the target value. The value of targetDt is arbitrary and should have no effect in the result in general. However if some bodies have imposed velocities, for instance, they will move more or less per each step depending on this value.")),
+ ((Real,targetDt,1,,"if :yref:`NewtonIntegrator::densityScaling` is active, this value will be used as the simulation timestep and the scaling will use this value of dt as the target value. The value of targetDt is arbitrary and should have no effect in the result in general. However if some bodies have imposed velocities, for instance, they will move more or less per each step depending on this value."))
+ ((bool,viscEl,false,,"To use with :yref:`ViscElPhys`. if True, evaluate separetly the minimum eigen-period in the problem considering only the elastic contribution on one hand (spring only), and only the viscous contribution on the other hand (dashpot only). Take then the minimum of the two and use the safety coefficient :yref:`GlobalStiffnessTimestepper::timestepSafetyCoefficient` to take into account the possible coupling between the two contribution.")),
computedOnce=false;)
DECLARE_LOGGER;
};
=== modified file 'pkg/dem/HertzMindlin.cpp'
--- pkg/dem/HertzMindlin.cpp 2012-02-16 16:05:15 +0000
+++ pkg/dem/HertzMindlin.cpp 2013-09-23 17:17:34 +0000
@@ -31,8 +31,8 @@
void Ip2_FrictMat_FrictMat_MindlinPhys::go(const shared_ptr<Material>& b1,const shared_ptr<Material>& b2, const shared_ptr<Interaction>& interaction){
if(interaction->phys) return; // no updates of an already existing contact necessary
- shared_ptr<MindlinPhys> mindlinPhys(new MindlinPhys());
- interaction->phys = mindlinPhys;
+ shared_ptr<MindlinPhys> contactPhysics(new MindlinPhys());
+ interaction->phys = contactPhysics;
FrictMat* mat1 = YADE_CAST<FrictMat*>(b1.get());
FrictMat* mat2 = YADE_CAST<FrictMat*>(b2.get());
@@ -64,20 +64,20 @@
Real Rmean = (Da+Db)/2.; // mean radius
Real Kno = 4./3.*E*sqrt(R); // coefficient for normal stiffness
Real Kso = 2*sqrt(4*R)*G/(2-V); // coefficient for shear stiffness
- Real frictionAngle = std::min(fa,fb);
+ Real frictionAngle = (!frictAngle) ? std::min(fa,fb) : (*frictAngle)(mat1->id,mat2->id,mat1->frictionAngle,mat2->frictionAngle);
Real Adhesion = 4.*Mathr::PI*R*gamma; // calculate adhesion force as predicted by DMT theory
/* pass values calculated from above to MindlinPhys */
- mindlinPhys->tangensOfFrictionAngle = std::tan(frictionAngle);
- //mindlinPhys->prevNormal = scg->normal; // used to compute relative rotation
- mindlinPhys->kno = Kno; // this is just a coeff
- mindlinPhys->kso = Kso; // this is just a coeff
- mindlinPhys->adhesionForce = Adhesion;
+ contactPhysics->tangensOfFrictionAngle = std::tan(frictionAngle);
+ //contactPhysics->prevNormal = scg->normal; // used to compute relative rotation
+ contactPhysics->kno = Kno; // this is just a coeff
+ contactPhysics->kso = Kso; // this is just a coeff
+ contactPhysics->adhesionForce = Adhesion;
- mindlinPhys->kr = krot;
- mindlinPhys->ktw = ktwist;
- mindlinPhys->maxBendPl = eta*Rmean; // does this make sense? why do we take Rmean?
+ contactPhysics->kr = krot;
+ contactPhysics->ktw = ktwist;
+ contactPhysics->maxBendPl = eta*Rmean; // does this make sense? why do we take Rmean?
/* compute viscous coefficients */
if(en && betan) throw std::invalid_argument("Ip2_FrictMat_FrictMat_MindlinPhys: only one of en, betan can be specified.");
@@ -86,13 +86,13 @@
// en or es specified, just compute alpha, otherwise alpha remains 0
if(en || es){
Real logE = log((*en)(mat1->id,mat2->id));
- mindlinPhys->alpha = -sqrt(5/6.)*2*logE/sqrt(pow(logE,2)+pow(Mathr::PI,2))*sqrt(2*E*sqrt(R)); // (see Tsuji, 1992)
+ contactPhysics->alpha = -sqrt(5/6.)*2*logE/sqrt(pow(logE,2)+pow(Mathr::PI,2))*sqrt(2*E*sqrt(R)); // (see Tsuji, 1992), also [Antypov2011] eq. 17
}
// betan specified, use that value directly; otherwise give zero
else{
- mindlinPhys->betan=betan ? (*betan)(mat1->id,mat2->id) : 0;
- mindlinPhys->betas=betas ? (*betas)(mat1->id,mat2->id) : mindlinPhys->betan;
+ contactPhysics->betan=betan ? (*betan)(mat1->id,mat2->id) : 0;
+ contactPhysics->betas=betas ? (*betas)(mat1->id,mat2->id) : contactPhysics->betan;
}
}
@@ -319,7 +319,7 @@
}
else if (useDamping){ // (see Tsuji, 1992)
Real mbar = (!b1->isDynamic() && b2->isDynamic()) ? de2->mass : ((!b2->isDynamic() && b1->isDynamic()) ? de1->mass : (de1->mass*de2->mass / (de1->mass + de2->mass))); // get equivalent mass if both bodies are dynamic, if not set it equal to the one of the dynamic body
- cn = phys->alpha*sqrt(mbar)*pow(uN,0.25); // normal viscous coefficient
+ cn = phys->alpha*sqrt(mbar)*pow(uN,0.25); // normal viscous coefficient, see also [Antypov2011] eq. 10
cs = cn; // same value for shear viscous coefficient
}
@@ -346,12 +346,32 @@
/**************************************/
// normal force must be updated here before we apply the Mohr-Coulomb criterion
- if (useDamping){ // get normal viscous component
+ if (useDamping){ // get normal viscous component
phys->normalViscous = cn*incidentVn;
- // add normal viscous component if damping is included
- phys->normalForce -= phys->normalViscous;
+ Vector3r normTemp = phys->normalForce - phys->normalViscous; // temporary normal force
+ // viscous force should not exceed the value of current normal force, i.e. no attraction force should be permitted if particles are non-adhesive
+ // if particles are adhesive, then fixed the viscous force at maximum equal to the adhesion force
+ // *** enforce normal force to zero if no adhesion is permitted ***
+ if (phys->adhesionForce==0.0 || !includeAdhesion){
+ if (normTemp.dot(scg->normal)<0.0){
+ phys->normalForce = Vector3r::Zero();
+ phys->normalViscous = phys->normalViscous + normTemp; // normal viscous force is such that the total applied force is null - it is necessary to compute energy correctly!
+ }
+ else{phys->normalForce -= phys->normalViscous;}
+ }
+ else if (includeAdhesion && phys->adhesionForce!=0.0){
+ // *** limit viscous component to the max adhesive force ***
+ if (normTemp.dot(scg->normal)<0.0 && (phys->normalViscous.norm() > phys->adhesionForce) ){
+ Real normVisc = phys->normalViscous.norm(); Vector3r normViscVector = phys->normalViscous/normVisc;
+ phys->normalViscous = phys->adhesionForce*normViscVector;
+ phys->normalForce -= phys->normalViscous;
+ }
+ // *** apply viscous component - in the presence of adhesion ***
+ else {phys->normalForce -= phys->normalViscous;}
+ }
if (calcEnergy) {normDampDissip += phys->normalViscous.dot(incidentVn*dt);} // calc dissipation of energy due to normal damping
}
+
/*************************************/
/* SHEAR DISPLACEMENT (elastic only) */
=== modified file 'pkg/dem/HertzMindlin.hpp'
--- pkg/dem/HertzMindlin.hpp 2013-05-29 11:54:22 +0000
+++ pkg/dem/HertzMindlin.hpp 2013-09-07 19:40:12 +0000
@@ -86,6 +86,7 @@
((shared_ptr<MatchMaker>,es,,,"Shear coefficient of restitution $e_s$."))
((shared_ptr<MatchMaker>,betan,,,"Normal viscous damping coefficient $\\beta_n$."))
((shared_ptr<MatchMaker>,betas,,,"Shear viscous damping coefficient $\\beta_s$."))
+ ((shared_ptr<MatchMaker>,frictAngle,,,"Instance of :yref:`MatchMaker` determining how to compute the friction angle of an interaction. If ``None``, minimum value is used."))
);
DECLARE_LOGGER;
};
=== added file 'pkg/dem/Ip2_ElastMat.cpp'
--- pkg/dem/Ip2_ElastMat.cpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/Ip2_ElastMat.cpp 2013-09-23 17:39:59 +0000
@@ -0,0 +1,64 @@
+#include "Ip2_ElastMat.hpp"
+
+#include <yade/pkg/common/NormShearPhys.hpp>
+#include <yade/pkg/common/NormShearPhys.hpp>
+#include <yade/pkg/dem/DemXDofGeom.hpp>
+
+YADE_PLUGIN((Ip2_ElastMat_ElastMat_NormPhys)(Ip2_ElastMat_ElastMat_NormShearPhys));
+
+
+void Ip2_ElastMat_ElastMat_NormPhys::go( const shared_ptr<Material>& b1
+ , const shared_ptr<Material>& b2
+ , const shared_ptr<Interaction>& interaction)
+{
+ if(interaction->phys) return;
+ const shared_ptr<ElastMat>& mat1 = YADE_PTR_CAST<ElastMat>(b1);
+ const shared_ptr<ElastMat>& mat2 = YADE_PTR_CAST<ElastMat>(b2);
+ Real Ea = mat1->young;
+ Real Eb = mat2->young;
+ interaction->phys = shared_ptr<NormPhys>(new NormPhys());
+ const shared_ptr<NormPhys>& phys = YADE_PTR_CAST<NormPhys>(interaction->phys);
+ Real Kn;
+ const GenericSpheresContact* geom=dynamic_cast<GenericSpheresContact*>(interaction->geom.get());
+ if (geom) {
+ Real Ra,Rb;//Vector3r normal;
+ Ra=geom->refR1>0?geom->refR1:geom->refR2;
+ Rb=geom->refR2>0?geom->refR2:geom->refR1;
+ //harmonic average of the two stiffnesses when (Ri.Ei/2) is the stiffness of a contact point on sphere "i"
+ Kn = 2*Ea*Ra*Eb*Rb/(Ea*Ra+Eb*Rb);
+ } else {
+ Kn = 2*Ea*Eb/(Ea+Eb);
+ }
+ phys->kn = Kn;
+};
+
+
+void Ip2_ElastMat_ElastMat_NormShearPhys::go( const shared_ptr<Material>& b1
+ , const shared_ptr<Material>& b2
+ , const shared_ptr<Interaction>& interaction)
+{
+ if(interaction->phys) return;
+ const shared_ptr<ElastMat>& mat1 = YADE_PTR_CAST<ElastMat>(b1);
+ const shared_ptr<ElastMat>& mat2 = YADE_PTR_CAST<ElastMat>(b2);
+ Real Ea = mat1->young;
+ Real Eb = mat2->young;
+ Real Va = mat1->poisson;
+ Real Vb = mat2->poisson;
+ interaction->phys = shared_ptr<NormShearPhys>(new NormShearPhys());
+ const shared_ptr<NormShearPhys>& phys = YADE_PTR_CAST<NormShearPhys>(interaction->phys);
+ Real Kn=0.0, Ks=0.0;
+ GenericSpheresContact* geom=dynamic_cast<GenericSpheresContact*>(interaction->geom.get());
+ if (geom) {
+ Real Ra,Rb;//Vector3r normal;
+ Ra=geom->refR1>0?geom->refR1:geom->refR2;
+ Rb=geom->refR2>0?geom->refR2:geom->refR1;
+ //harmonic average of the two stiffnesses when (Ri.Ei/2) is the stiffness of a contact point on sphere "i"
+ Kn = 2*Ea*Ra*Eb*Rb/(Ea*Ra+Eb*Rb);
+ Ks = 2*Ea*Ra*Va*Eb*Rb*Vb/(Ea*Ra*Va+Eb*Rb*Vb);
+ } else {
+ Kn = 2*Ea*Eb/(Ea+Eb);
+ Kn = 2*Ea*Va*Eb*Vb/(Ea*Va+Eb*Vb);
+ }
+ phys->kn = Kn;
+ phys->ks = Ks;
+};
=== added file 'pkg/dem/Ip2_ElastMat.hpp'
--- pkg/dem/Ip2_ElastMat.hpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/Ip2_ElastMat.hpp 2013-09-08 11:12:42 +0000
@@ -0,0 +1,24 @@
+
+#include<yade/pkg/common/Dispatching.hpp>
+#include<yade/pkg/common/MatchMaker.hpp>
+#include<yade/pkg/common/ElastMat.hpp>
+
+class Ip2_ElastMat_ElastMat_NormPhys: public IPhysFunctor{
+ public:
+ virtual void go(const shared_ptr<Material>& b1, const shared_ptr<Material>& b2, const shared_ptr<Interaction>& interaction);
+ FUNCTOR2D(ElastMat,ElastMat);
+ YADE_CLASS_BASE_DOC_ATTRS(Ip2_ElastMat_ElastMat_NormPhys,IPhysFunctor,"Create a :yref:`NormPhys` from two :yref:`ElastMats<ElastMat>`. TODO. EXPERIMENTAL",
+ );
+};
+REGISTER_SERIALIZABLE(Ip2_ElastMat_ElastMat_NormPhys);
+
+
+class Ip2_ElastMat_ElastMat_NormShearPhys: public IPhysFunctor{
+ public:
+ virtual void go(const shared_ptr<Material>& b1, const shared_ptr<Material>& b2, const shared_ptr<Interaction>& interaction);
+ FUNCTOR2D(ElastMat,ElastMat);
+ YADE_CLASS_BASE_DOC_ATTRS(Ip2_ElastMat_ElastMat_NormShearPhys,IPhysFunctor,"Create a :yref:`NormShearPhys` from two :yref:`ElastMats<ElastMat>`. TODO. EXPERIMENTAL",
+ );
+};
+REGISTER_SERIALIZABLE(Ip2_ElastMat_ElastMat_NormShearPhys);
+
=== added file 'pkg/dem/JointedCohesiveFrictionalPM.cpp'
--- pkg/dem/JointedCohesiveFrictionalPM.cpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/JointedCohesiveFrictionalPM.cpp 2013-11-05 10:52:25 +0000
@@ -0,0 +1,285 @@
+/* LucScholtes2010 */
+
+#include"JointedCohesiveFrictionalPM.hpp"
+#include<yade/core/Scene.hpp>
+#include<yade/pkg/dem/ScGeom.hpp>
+#include<yade/core/Omega.hpp>
+
+YADE_PLUGIN((JCFpmMat)(JCFpmState)(JCFpmPhys)(Ip2_JCFpmMat_JCFpmMat_JCFpmPhys)(Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM));
+
+
+/********************** Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM ****************************/
+CREATE_LOGGER(Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM);
+
+void Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM::go(shared_ptr<IGeom>& ig, shared_ptr<IPhys>& ip, Interaction* contact){
+
+ const int &id1 = contact->getId1();
+ const int &id2 = contact->getId2();
+ ScGeom* geom = static_cast<ScGeom*>(ig.get());
+ JCFpmPhys* phys = static_cast<JCFpmPhys*>(ip.get());
+
+ Body* b1 = Body::byId(id1,scene).get();
+ Body* b2 = Body::byId(id2,scene).get();
+
+ Real Dtensile=phys->FnMax/phys->kn;
+
+ string fileCracks = "cracks_"+Key+".txt";
+ /// Defines the interparticular distance used for computation
+ Real D = 0;
+
+ /*this is for setting the equilibrium distance between all cohesive elements in the first contact detection*/
+ if ( contact->isFresh(scene) ) {
+ phys->normalForce = Vector3r::Zero();
+ phys->shearForce = Vector3r::Zero();
+ if ((smoothJoint) && (phys->isOnJoint)) {
+ phys->jointNormal = geom->normal.dot(phys->jointNormal)*phys->jointNormal; //to set the joint normal colinear with the interaction normal
+ phys->jointNormal.normalize();
+ phys->initD = abs((b1->state->pos - b2->state->pos).dot(phys->jointNormal)); // to set the initial gap as the equilibrium gap
+ } else {
+ phys->initD = geom->penetrationDepth;
+ }
+ }
+
+ if ( smoothJoint && phys->isOnJoint ) {
+ if ( phys->more || ( phys->jointCumulativeSliding > (2*min(geom->radius1,geom->radius2)) ) ) {
+ scene->interactions->requestErase(contact->getId1(),contact->getId2()); return;
+ } else {
+ D = phys->initD - abs((b1->state->pos - b2->state->pos).dot(phys->jointNormal));
+ }
+ } else {
+ D = geom->penetrationDepth - phys->initD;
+ }
+
+ /* Determination of interaction */
+ if (D < 0) { //spheres do not touch
+ if ( !phys->isCohesive ) { scene->interactions->requestErase(contact->getId1(),contact->getId2()); return; }
+
+ if ( phys->isCohesive && (phys->FnMax>0) && (abs(D)>Dtensile) ) {
+
+ // update body state with the number of broken bonds
+ JCFpmState* st1=dynamic_cast<JCFpmState*>(b1->state.get());
+ JCFpmState* st2=dynamic_cast<JCFpmState*>(b2->state.get());
+ st1->tensBreak+=1;
+ st2->tensBreak+=1;
+ st1->tensBreakRel+=1.0/st1->noIniLinks;
+ st2->tensBreakRel+=1.0/st2->noIniLinks;
+
+ // create a text file to record properties of the broken bond (iteration, position, type (tensile), cross section and contact normal orientation)
+ if (recordCracks){
+ std::ofstream file (fileCracks.c_str(), !cracksFileExist ? std::ios::trunc : std::ios::app);
+ if(file.tellp()==0){ file <<"i p0 p1 p2 t s norm0 norm1 norm2"<<endl; }
+ Vector3r crackNormal=Vector3r::Zero();
+ if ((smoothJoint) && (phys->isOnJoint)) { crackNormal=phys->jointNormal; } else {crackNormal=geom->normal;}
+ file << lexical_cast<string> ( scene->iter )<<" "<< lexical_cast<string> ( geom->contactPoint[0] ) <<" "<< lexical_cast<string> ( geom->contactPoint[1] ) <<" "<< lexical_cast<string> ( geom->contactPoint[2] ) <<" "<< 0 <<" "<< lexical_cast<string> ( 0.5*(geom->radius1+geom->radius2) ) <<" "<< lexical_cast<string> ( crackNormal[0] ) <<" "<< lexical_cast<string> ( crackNormal[1] ) <<" "<< lexical_cast<string> ( crackNormal[2] ) << endl;
+ }
+ cracksFileExist=true;
+
+ // delete contact
+ scene->interactions->requestErase(contact->getId1(),contact->getId2()); return;
+ }
+ }
+
+ /* NormalForce */
+ Real Fn = 0;
+ Fn = phys->kn*D;
+
+ /* ShearForce */
+ Vector3r& shearForce = phys->shearForce;
+ Real jointSliding=0;
+
+ if ((smoothJoint) && (phys->isOnJoint)) {
+
+ /// incremental formulation (OK?)
+ Vector3r relativeVelocity = (b2->state->vel - b1->state->vel); // angVel are not taken into account as particles on joint don't rotate ????
+ Vector3r slidingVelocity = relativeVelocity - phys->jointNormal.dot(relativeVelocity)*phys->jointNormal;
+ Vector3r incrementalSliding = slidingVelocity*scene->dt;
+ shearForce -= phys->ks*incrementalSliding;
+
+ jointSliding = incrementalSliding.norm();
+ phys->jointCumulativeSliding += jointSliding;
+
+ } else {
+
+ shearForce = geom->rotate(phys->shearForce);
+ const Vector3r& incrementalShear = geom->shearIncrement();
+ shearForce -= phys->ks*incrementalShear;
+
+ }
+
+ /* Morh-Coulomb criterion */
+ Real maxFs = phys->FsMax + Fn*phys->tanFrictionAngle;
+ Real scalarShearForce = shearForce.norm();
+
+ if (scalarShearForce > maxFs) {
+ shearForce*=maxFs/scalarShearForce;
+ if ((smoothJoint) && (phys->isOnJoint)) {phys->dilation=phys->jointCumulativeSliding*phys->tanDilationAngle-D; phys->initD+=(jointSliding*phys->tanDilationAngle);}
+ // take into account shear cracking -> are those lines critical? -> TODO testing with and without
+ if ( phys->isCohesive ) {
+
+ // update body state with the number of broken bonds
+ JCFpmState* st1=dynamic_cast<JCFpmState*>(b1->state.get());
+ JCFpmState* st2=dynamic_cast<JCFpmState*>(b2->state.get());
+ st1->shearBreak+=1;
+ st2->shearBreak+=1;
+ st1->shearBreakRel+=1.0/st1->noIniLinks;
+ st2->shearBreakRel+=1.0/st2->noIniLinks;
+
+ // create a text file to record properties of the broken bond (iteration, position, type (shear), cross section and contact normal orientation)
+ if (recordCracks){
+ std::ofstream file (fileCracks.c_str(), !cracksFileExist ? std::ios::trunc : std::ios::app);
+ if(file.tellp()==0){ file <<"i p0 p1 p2 t s norm0 norm1 norm2"<<endl; }
+ Vector3r crackNormal=Vector3r::Zero();
+ if ((smoothJoint) && (phys->isOnJoint)) { crackNormal=phys->jointNormal; } else {crackNormal=geom->normal;}
+ file << lexical_cast<string> ( scene->iter )<<" "<< lexical_cast<string> ( geom->contactPoint[0] ) <<" "<< lexical_cast<string> ( geom->contactPoint[1] ) <<" "<< lexical_cast<string> ( geom->contactPoint[2] ) <<" "<< 1 <<" "<< lexical_cast<string> ( 0.5*(geom->radius1+geom->radius2) ) <<" "<< lexical_cast<string> ( crackNormal[0] ) <<" "<< lexical_cast<string> ( crackNormal[1] ) <<" "<< lexical_cast<string> ( crackNormal[2] ) << endl;
+ }
+ cracksFileExist=true;
+
+ // delete contact if in tension, set the contact properties to friction if in compression
+ if ( D < 0 ) {
+ scene->interactions->requestErase(contact->getId1(),contact->getId2()); return;
+ } else {
+ phys->FnMax = 0;
+ phys->FsMax = 0;
+ phys->isCohesive=false;
+ }
+ }
+ }
+
+ /* Apply forces */
+ if ((smoothJoint) && (phys->isOnJoint)) { phys->normalForce = Fn*phys->jointNormal; } else { phys->normalForce = Fn*geom->normal; }
+
+ Vector3r f = phys->normalForce + shearForce;
+
+ /// applyForceAtContactPoint computes torque also and, for now, we don't want rotation for particles on joint (some errors in calculation due to specific geometry)
+ //applyForceAtContactPoint(f, geom->contactPoint, I->getId2(), b2->state->pos, I->getId1(), b1->state->pos, scene);
+ scene->forces.addForce (id1,-f);
+ scene->forces.addForce (id2, f);
+
+ // simple solution to avoid torque computation for particles interacting on a smooth joint
+ if ( (phys->isOnJoint)&&(smoothJoint) ) return;
+
+ /// those lines are needed if rootBody->forces.addForce and rootBody->forces.addMoment are used instead of applyForceAtContactPoint -> NOTE need to check for accuracy!!!
+ scene->forces.addTorque(id1,(geom->radius1-0.5*geom->penetrationDepth)* geom->normal.cross(-f));
+ scene->forces.addTorque(id2,(geom->radius2-0.5*geom->penetrationDepth)* geom->normal.cross(-f));
+
+}
+
+CREATE_LOGGER(Ip2_JCFpmMat_JCFpmMat_JCFpmPhys);
+
+void Ip2_JCFpmMat_JCFpmMat_JCFpmPhys::go(const shared_ptr<Material>& b1, const shared_ptr<Material>& b2, const shared_ptr<Interaction>& interaction){
+
+ /* avoid updates of interaction if it already exists */
+ if( interaction->phys ) return;
+
+ ScGeom* geom=dynamic_cast<ScGeom*>(interaction->geom.get());
+ assert(geom);
+
+ const shared_ptr<JCFpmMat>& yade1 = YADE_PTR_CAST<JCFpmMat>(b1);
+ const shared_ptr<JCFpmMat>& yade2 = YADE_PTR_CAST<JCFpmMat>(b2);
+ JCFpmState* st1=dynamic_cast<JCFpmState*>(Body::byId(interaction->getId1(),scene)->state.get());
+ JCFpmState* st2=dynamic_cast<JCFpmState*>(Body::byId(interaction->getId2(),scene)->state.get());
+
+ shared_ptr<JCFpmPhys> contactPhysics(new JCFpmPhys());
+
+ /* From material properties */
+ Real E1 = yade1->young;
+ Real E2 = yade2->young;
+ Real v1 = yade1->poisson;
+ Real v2 = yade2->poisson;
+ Real f1 = yade1->frictionAngle;
+ Real f2 = yade2->frictionAngle;
+ Real SigT1 = yade1->tensileStrength;
+ Real SigT2 = yade2->tensileStrength;
+ Real Coh1 = yade1->cohesion;
+ Real Coh2 = yade2->cohesion;
+
+ /* From interaction geometry */
+ Real R1= geom->radius1;
+ Real R2= geom->radius2;
+ contactPhysics->crossSection = Mathr::PI*pow(min(R1,R2),2);
+
+ /* Pass values to JCFpmPhys. In case of a "jointed" interaction, the following values will be replaced by other ones later (in few if(){} blocks)*/
+
+ // frictional properties
+ contactPhysics->kn = 2.*E1*R1*E2*R2/(E1*R1+E2*R2);
+ contactPhysics->ks = 2.*E1*R1*v1*E2*R2*v2/(E1*R1*v1+E2*R2*v2);//alpha*contactPhysics->kn;
+ contactPhysics->tanFrictionAngle = std::tan(std::min(f1,f2));
+
+ // cohesive properties
+ ///to set if the contact is cohesive or not
+ if ((scene->iter < cohesiveTresholdIteration) && (std::min(SigT1,SigT2)>0 || std::min(Coh1,Coh2)>0) && (yade1->type == yade2->type)){
+ contactPhysics->isCohesive=true;
+ st1->noIniLinks++;
+ st2->noIniLinks++;
+ }
+
+ if ( contactPhysics->isCohesive ) {
+ contactPhysics->FnMax = std::min(SigT1,SigT2)*contactPhysics->crossSection;
+ contactPhysics->FsMax = std::min(Coh1,Coh2)*contactPhysics->crossSection;
+ }
+
+ /// +++ Jointed interactions ->NOTE: geom->normal is oriented from 1 to 2 / jointNormal from plane to sphere
+ if ( st1->onJoint && st2->onJoint )
+ {
+ if ( (((st1->jointNormal1.cross(st2->jointNormal1)).norm()<0.1) && (st1->jointNormal1.dot(st2->jointNormal1)<0)) || (((st1->jointNormal1.cross(st2->jointNormal2)).norm()<0.1) && (st1->jointNormal1.dot(st2->jointNormal2)<0)) || (((st1->jointNormal1.cross(st2->jointNormal3)).norm()<0.1) && (st1->jointNormal1.dot(st2->jointNormal3)<0)) )
+ {
+ contactPhysics->isOnJoint = true;
+ contactPhysics->jointNormal = st1->jointNormal1;
+ }
+ else if ( (((st1->jointNormal2.cross(st2->jointNormal1)).norm()<0.1) && (st1->jointNormal2.dot(st2->jointNormal1)<0)) || (((st1->jointNormal2.cross(st2->jointNormal2)).norm()<0.1) && (st1->jointNormal2.dot(st2->jointNormal2)<0)) || (((st1->jointNormal2.cross(st2->jointNormal3)).norm()<0.1) && (st1->jointNormal2.dot(st2->jointNormal3)<0)) )
+ {
+ contactPhysics->isOnJoint = true;
+ contactPhysics->jointNormal = st1->jointNormal2;
+ }
+ else if ( (((st1->jointNormal3.cross(st2->jointNormal1)).norm()<0.1) && (st1->jointNormal3.dot(st2->jointNormal1)<0)) || (((st1->jointNormal3.cross(st2->jointNormal2)).norm()<0.1) && (st1->jointNormal3.dot(st2->jointNormal2)<0)) || (((st1->jointNormal3.cross(st2->jointNormal3)).norm()<0.1) && (st1->jointNormal3.dot(st2->jointNormal3)<0)) )
+ {
+ contactPhysics->isOnJoint = true;
+ contactPhysics->jointNormal = st1->jointNormal3;
+ }
+ else if ( (st1->joint>3 || st2->joint>3) && ( ( ((st1->jointNormal1.cross(st2->jointNormal1)).norm()>0.1) && ((st1->jointNormal1.cross(st2->jointNormal2)).norm()>0.1) && ((st1->jointNormal1.cross(st2->jointNormal3)).norm()>0.1) ) || ( ((st1->jointNormal2.cross(st2->jointNormal1)).norm()>0.1) && ((st1->jointNormal2.cross(st2->jointNormal2)).norm()>0.1) && ((st1->jointNormal2.cross(st2->jointNormal3)).norm()>0.1) ) || ( ((st1->jointNormal3.cross(st2->jointNormal1)).norm()>0.1) && ((st1->jointNormal3.cross(st2->jointNormal2)).norm()>0.1) && ((st1->jointNormal3.cross(st2->jointNormal3)).norm()>0.1) ) ) ) { contactPhysics->isOnJoint = true; contactPhysics->more = true; contactPhysics->jointNormal = geom->normal; }
+ }
+
+ ///to specify joint properties
+ if ( contactPhysics->isOnJoint ) {
+ Real jf1 = yade1->jointFrictionAngle;
+ Real jf2 = yade2->jointFrictionAngle;
+ Real jkn1 = yade1->jointNormalStiffness;
+ Real jkn2 = yade2->jointNormalStiffness;
+ Real jks1 = yade1->jointShearStiffness;
+ Real jks2 = yade2->jointShearStiffness;
+ Real jdil1 = yade1->jointDilationAngle;
+ Real jdil2 = yade2->jointDilationAngle;
+ Real jcoh1 = yade1->jointCohesion;
+ Real jcoh2 = yade2->jointCohesion;
+ Real jSigT1 = yade1->jointTensileStrength;
+ Real jSigT2 = yade2->jointTensileStrength;
+
+ contactPhysics->tanFrictionAngle = std::tan(std::min(jf1,jf2));
+
+ //contactPhysics->kn = jointNormalStiffness*2.*R1*R2/(R1+R2); // very first expression from Luc
+ //contactPhysics->kn = (jkn1+jkn2)/2.0*2.*R1*R2/(R1+R2); // after putting jointNormalStiffness in material
+ contactPhysics->kn = ( jkn1*Mathr::PI*pow(R1,2) + jkn2*Mathr::PI*pow(R2,2) )/2.0; // for a size independant expression
+
+ //contactPhysics->ks = jointShearStiffness*2.*R1*R2/(R1+R2);
+ //contactPhysics->ks = (jks1+jks2)/2.0*2.*R1*R2/(R1+R2);
+ contactPhysics->ks = ( jks1*Mathr::PI*pow(R1,2) + jks2*Mathr::PI*pow(R2,2) )/2.0; // for a size independant expression
+
+ contactPhysics->tanDilationAngle = std::tan(std::min(jdil1,jdil2));
+
+ ///to set if the contact is cohesive or not
+ if ((scene->iter < cohesiveTresholdIteration) && (std::min(jcoh1,jcoh2)>0 || std::min(jSigT1,jSigT2)/2.0>0)) {
+ contactPhysics->isCohesive=true;
+ st1->noIniLinks++;
+ st2->noIniLinks++;
+ }
+ else { contactPhysics->isCohesive=false; contactPhysics->FnMax=0; contactPhysics->FsMax=0; }
+
+ if ( contactPhysics->isCohesive ) {
+ contactPhysics->FnMax = std::min(jSigT1,jSigT2)*contactPhysics->crossSection;
+ contactPhysics->FsMax = std::min(jcoh1,jcoh2)*contactPhysics->crossSection;
+ }
+ }
+ interaction->phys = contactPhysics;
+}
+
+JCFpmPhys::~JCFpmPhys(){}
=== added file 'pkg/dem/JointedCohesiveFrictionalPM.hpp'
--- pkg/dem/JointedCohesiveFrictionalPM.hpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/JointedCohesiveFrictionalPM.hpp 2013-11-05 10:52:25 +0000
@@ -0,0 +1,109 @@
+/* lucScholtes2010 */
+
+#pragma once
+
+#include<yade/pkg/common/ElastMat.hpp>
+#include<yade/pkg/common/Dispatching.hpp>
+#include<yade/pkg/common/NormShearPhys.hpp>
+#include<yade/pkg/dem/ScGeom.hpp>
+
+/** This class holds information associated with each body state*/
+class JCFpmState: public State {
+ YADE_CLASS_BASE_DOC_ATTRS_CTOR(JCFpmState,State,"JCFpm state information about each body.",
+ ((int,tensBreak,0,,"Number of tensile breakages. [-]"))
+ ((int,shearBreak,0,,"Number of shear breakages. [-]"))
+ ((int,noIniLinks,0,,"Number of initial cohesive interactions. [-]"))
+ ((Real,tensBreakRel,0,,"Relative number (in [0;1], compared with :yref:noIniLinks) of tensile breakages. [-]"))
+ ((Real,shearBreakRel,0,,"Relative number (in [0;1], compared with :yref:noIniLinks) of shear breakages. [-]"))
+ ((bool,onJoint,false,,"Identifies if the particle is on a joint surface."))
+ ((int,joint,0,,"Indicates the number of joint surfaces to which the particle belongs (0-> no joint, 1->1 joint, etc..). [-]"))
+ ((Vector3r,jointNormal1,Vector3r::Zero(),,"Specifies the normal direction to the joint plane 1. Rk: the ideal here would be to create a vector of vector wich size is defined by the joint integer (as much joint normals as joints). However, it needs to make the pushback function works with python since joint detection is done through a python script. lines 272 to 312 of cpp file should therefore be adapted. [-]"))
+ ((Vector3r,jointNormal2,Vector3r::Zero(),,"Specifies the normal direction to the joint plane 2. [-]"))
+ ((Vector3r,jointNormal3,Vector3r::Zero(),,"Specifies the normal direction to the joint plane 3. [-]"))
+ ,
+ createIndex();
+ );
+ REGISTER_CLASS_INDEX(JCFpmState,State);
+};
+REGISTER_SERIALIZABLE(JCFpmState);
+
+/** This class holds information associated with each body */
+class JCFpmMat: public FrictMat {
+ public:
+ virtual shared_ptr<State> newAssocState() const { return shared_ptr<State>(new JCFpmState); }
+ virtual bool stateTypeOk(State* s) const { return (bool)dynamic_cast<JCFpmState*>(s); }
+
+ YADE_CLASS_BASE_DOC_ATTRS_CTOR(JCFpmMat,FrictMat,"Possibly jointed, cohesive frictional material, for use with other JCFpm classes",
+ ((Real,cohesion,0.,,"Defines the maximum admissible tangential force in shear, for Fn=0, in the matrix (:yref:`FsMax<JCFpmPhys.FsMax>` = cohesion*:yref:`crossSection<JCFpmPhys.crossSection>`). [Pa]"))
+ ((Real,jointCohesion,0.,,"Defines the :yref:`maximum admissible tangential force in shear<JCFpmPhys.FsMax>`, for Fn=0, on the joint surface. [Pa]"))
+ ((Real,jointDilationAngle,0,,"Defines the dilatancy of the joint surface (only valid for :yref:`smooth contact logic<Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM.smoothJoint>`). [rad]"))
+ ((Real,jointFrictionAngle,-1,,"Defines Coulomb friction on the joint surface. [rad]"))
+ ((Real,jointNormalStiffness,0.,,"Defines the normal stiffness on the joint surface. [Pa/m]"))
+ ((Real,jointShearStiffness,0.,,"Defines the shear stiffness on the joint surface. [Pa/m]"))
+ ((Real,jointTensileStrength,0.,,"Defines the :yref:`maximum admissible normal force in traction<JCFpmPhys.FnMax>` on the joint surface. [Pa]"))
+ ((int,type,0,,"If particles of two different types interact, it will be with friction only (no cohesion).[-]"))
+ ((Real,tensileStrength,0.,,"Defines the maximum admissible normal force in traction in the matrix (:yref:`FnMax<JCFpmPhys.FnMax>` = tensileStrength*:yref:`crossSection<JCFpmPhys.crossSection>`). [Pa]"))
+ ,
+ createIndex();
+ );
+ REGISTER_CLASS_INDEX(JCFpmMat,FrictMat);
+};
+REGISTER_SERIALIZABLE(JCFpmMat);
+
+/** This class holds information associated with each interaction */
+class JCFpmPhys: public NormShearPhys {
+ public:
+ virtual ~JCFpmPhys();
+
+ YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(JCFpmPhys,NormShearPhys,"Representation of a single interaction of the JCFpm type, storage for relevant parameters",
+ ((Real,initD,0,,"equilibrium distance for interacting particles. Computed as the interparticular distance at first contact detection."))
+ ((bool,isCohesive,false,,"If false, particles interact in a frictional way. If true, particles are bonded regarding the given :yref:`cohesion<JCFpmMat.cohesion>` and :yref:`tensile strength<JCFpmMat.tensileStrength>` (or their jointed variants)."))
+ ((bool,more,false,,"specifies if the interaction is crossed by more than 3 joints. If true, interaction is deleted (temporary solution)."))
+ ((bool,isOnJoint,false,,"defined as true when both interacting particles are :yref:`on joint<JCFpmState.onJoint>` and are in opposite sides of the joint surface. In this case, mechanical parameters of the interaction are derived from the ''joint...'' material properties of the particles, and normal of the interaction is re-oriented (see also :yref:`Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM`)."))
+ ((Real,tanFrictionAngle,0,,"tangent of Coulomb friction angle for this interaction (auto. computed). [-]"))
+ ((Real,crossSection,0,,"crossSection=pi*Rmin^2. [m2]"))
+ ((Real,FnMax,0,,"computed from :yref:`tensile strength<JCFpmMat.tensileStrength>` (or joint variant) to define the maximum admissible normal force in traction. [N]"))
+ ((Real,FsMax,0,,"computed from :yref:`cohesion<JCFpmMat.cohesion>` (or jointCohesion) to define the maximum admissible tangential force in shear, for Fn=0. [N]"))
+ ((Vector3r,jointNormal,Vector3r::Zero(),,"normal direction to the joint, deduced from e.g. :yref:`<JCFpmState.jointNormal1>`."))
+ ((Real,jointCumulativeSliding,0,,"sliding distance for particles interacting on a joint. Used, when :yref:`<Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM.smoothJoint>` is true, to take into account dilatancy due to shearing. [-]"))
+ ((Real,tanDilationAngle,0,,"tangent of the angle defining the dilatancy of the joint surface (auto. computed from :yref:`JCFpmMat.jointDilationAngle`). [-]"))
+ ((Real,dilation,0,,"defines the normal displacement in the joint after sliding treshold. [m]"))
+ ,
+ createIndex();
+ ,
+ );
+ DECLARE_LOGGER;
+ REGISTER_CLASS_INDEX(JCFpmPhys,NormShearPhys);
+};
+REGISTER_SERIALIZABLE(JCFpmPhys);
+
+/** 2d functor creating InteractionPhysics (Ip2) taking JCFpmMat and JCFpmMat of 2 bodies, returning type JCFpmPhys */
+class Ip2_JCFpmMat_JCFpmMat_JCFpmPhys: public IPhysFunctor{
+ public:
+ virtual void go(const shared_ptr<Material>& pp1, const shared_ptr<Material>& pp2, const shared_ptr<Interaction>& interaction);
+
+ FUNCTOR2D(JCFpmMat,JCFpmMat);
+ DECLARE_LOGGER;
+
+ YADE_CLASS_BASE_DOC_ATTRS(Ip2_JCFpmMat_JCFpmMat_JCFpmPhys,IPhysFunctor,"Converts 2 :yref:`JCFpmMat` instances to one :yref:`JCFpmPhys` instance, with corresponding parameters.",
+ ((int,cohesiveTresholdIteration,1,,"should new contacts be cohesive? They will before this iter, they won't afterward."))
+ );
+
+};
+REGISTER_SERIALIZABLE(Ip2_JCFpmMat_JCFpmMat_JCFpmPhys);
+
+/** 2d functor creating the interaction law (Law2) based on SphereContactGeometry (ScGeom) and JCFpmPhys of 2 bodies, returning type JointedCohesiveFrictionalPM */
+class Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM: public LawFunctor{
+ public:
+ virtual void go(shared_ptr<IGeom>& _geom, shared_ptr<IPhys>& _phys, Interaction* I);
+ FUNCTOR2D(ScGeom,JCFpmPhys);
+
+ YADE_CLASS_BASE_DOC_ATTRS(Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM,LawFunctor,"Interaction law for cohesive frictional material, e.g. rock, possibly presenting joint surfaces. Joint surfaces can be defined in a preprocessing phase through .stl meshes (see ref for details of the procedure), and can be mechanically described with a smooth contact logic [Ivars2011]_ (implemented in Yade in [Scholtes2012]_).",
+ ((string,Key,"",,"string specifying the name of saved file 'cracks___.txt', when :yref:`recordCracks<Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM.recordCracks>` is true."))
+ ((bool,cracksFileExist,false,,"if true (and if :yref:`recordCracks<Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM.recordCracks>`), data are appended to an existing 'cracksKey' text file; otherwise its content is reset."))
+ ((bool,smoothJoint,false,,"if true, interactions of particles belonging to joint surface (:yref:`JCFpmPhys.isOnJoint`) are handled according to a smooth contact logic [Ivars2011]_, [Scholtes2012]_."))
+ ((bool,recordCracks,false,,"if true a text file cracksKey.txt (see :yref:`Key<Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM.Key>`) will be created. It contains : apparition iteration, position, type (tensile or shear), cross section and contact normal."))
+ );
+ DECLARE_LOGGER;
+};
+REGISTER_SERIALIZABLE(Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM);
=== modified file 'pkg/dem/L3Geom.cpp'
--- pkg/dem/L3Geom.cpp 2013-03-07 18:28:01 +0000
+++ pkg/dem/L3Geom.cpp 2013-08-23 15:21:20 +0000
@@ -297,7 +297,7 @@
if(!noSlip){
// plastic slip, if necessary; non-zero elastic limit only for compression
- Real maxFs=-min(0.,localF[0]*phys->tangensOfFrictionAngle); Eigen::Map<Vector2r> Fs(&localF[1]);
+ Real maxFs=-min((Real)0.,localF[0]*phys->tangensOfFrictionAngle); Eigen::Map<Vector2r> Fs(&localF[1]);
//cerr<<"u="<<geom->relU()<<", maxFs="<<maxFs<<", Fn="<<localF[0]<<", |Fs|="<<Fs.norm()<<", Fs="<<Fs<<endl;
if(Fs.squaredNorm()>maxFs*maxFs){
Real ratio=sqrt(maxFs*maxFs/Fs.squaredNorm());
@@ -338,17 +338,11 @@
const L3Geom& g(ig->cast<L3Geom>());
glTranslatev(g.contactPoint);
#ifdef L3_TRSF_QUATERNION
- #if EIGEN_WORLD_VERSION==2
- glMultMatrixd(Eigen::Transform3d(Matrix3r(g.trsf).transpose()).data());
- #elif EIGEN_WORLD_VERSION==3
- glMultMatrixd(Eigen::Affine3d(Matrix3r(g.trsf).transpose()).data());
- #endif
+ //glMultMatrixd(Eigen::Affine3d(Matrix3r(g.trsf).transpose()).data());
+ glMultMatrix(Eigen::Transform<Real,3,Eigen::Affine>(Matrix3r(g.trsf).transpose()).data());
#else
- #if EIGEN_WORLD_VERSION==2
- glMultMatrixd(Eigen::Transform3d(g.trsf.transpose()).data());
- #elif EIGEN_WORLD_VERSION==3
- glMultMatrixd(Eigen::Affine3d(g.trsf.transpose()).data());
- #endif
+ //glMultMatrixd(Eigen::Affine3d(g.trsf.transpose()).data());
+ glMultMatrix(Eigen::Transform<Real,3,Eigen::Affine>(g.trsf.transpose()).data());
#endif
Real rMin=g.refR1<=0?g.refR2:(g.refR2<=0?g.refR1:min(g.refR1,g.refR2));
if(axesWd>0){
=== modified file 'pkg/dem/L3Geom.hpp'
--- pkg/dem/L3Geom.hpp 2012-09-08 01:19:45 +0000
+++ pkg/dem/L3Geom.hpp 2013-08-23 15:21:20 +0000
@@ -154,7 +154,7 @@
#ifdef L3GEOM_SPHERESLIKE
struct Ig2_Facet_Sphere_L3Geom: public Ig2_Sphere_Sphere_L3Geom{
// get point on segment A..B closest to P; algo: http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/
- static Vector3r getClosestSegmentPt(const Vector3r& P, const Vector3r& A, const Vector3r& B){ Vector3r BA=B-A; Real u=(P.dot(BA)-A.dot(BA))/(BA.squaredNorm()); return A+min(1.,max(0.,u))*BA; }
+ static Vector3r getClosestSegmentPt(const Vector3r& P, const Vector3r& A, const Vector3r& B){ Vector3r BA=B-A; Real u=(P.dot(BA)-A.dot(BA))/(BA.squaredNorm()); return A+min((Real)1.,max((Real)0.,u))*BA; }
virtual bool go(const shared_ptr<Shape>& s1, const shared_ptr<Shape>& s2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& I);
YADE_CLASS_BASE_DOC(Ig2_Facet_Sphere_L3Geom,Ig2_Sphere_Sphere_L3Geom,"Incrementally compute :yref:`L3Geom` for contact between :yref:`Facet` and :yref:`Sphere`. Uses attributes of :yref:`Ig2_Sphere_Sphere_L3Geom`.");
FUNCTOR2D(Facet,Sphere);
=== modified file 'pkg/dem/LudingPM.cpp'
--- pkg/dem/LudingPM.cpp 2013-07-08 08:52:03 +0000
+++ pkg/dem/LudingPM.cpp 2013-10-11 19:22:48 +0000
@@ -56,17 +56,19 @@
phys->tangensOfFrictionAngle = std::tan(std::min(mat1->frictionAngle, mat2->frictionAngle));
phys->shearForce = Vector3r(0,0,0);
- phys->ThetMax = 0.0;
- phys->ThetNull = 0.0;
- phys->ThetPMax = phys->kp/(phys->kp-phys->k1)*phys->PhiF*2*a1*a2/(a1+a2); // [Luding2008], equation (7)
+ phys->DeltMax = 0.0;
+ phys->DeltNull = 0.0;
+ phys->DeltPMax = phys->kp/(phys->kp-phys->k1)*phys->PhiF*2*a1*a2/(a1+a2); // [Luding2008], equation (7)
// [Singh2013], equation (11)
-
+ phys->DeltPNull = phys->PhiF*2*a1*a2/(a1+a2); // [Singh2013], equation (12)
+ phys->DeltPrev = 0.0;
+ phys->DeltMin = 0.0;
interaction->phys = shared_ptr<LudingPhys>(phys);
}
Real Ip2_LudingMat_LudingMat_LudingPhys::reduced(Real a1, Real a2){
Real a = (a1?1/a1:0) + (a2?1/a2:0); a = a?1/a:0;
- return a;
+ return 2.0*a;
}
void Law2_ScGeom_LudingPhys_Basic::go(shared_ptr<IGeom>& _geom, shared_ptr<IPhys>& _phys, Interaction* I){
@@ -77,9 +79,9 @@
const int id1 = I->getId1();
const int id2 = I->getId2();
- const Real Theta = geom.penetrationDepth;
+ const Real Delt = geom.penetrationDepth;
- if (Theta<0) {
+ if (Delt<0) {
scene->interactions->requestErase(I);
return;
};
@@ -87,32 +89,53 @@
const BodyContainer& bodies = *scene->bodies;
-
-
Real forceHys = 0.0;
- if (phys.ThetMax/phys.ThetPMax >= 1.0) { // [Luding2008], equation (8)
+ if (phys.DeltMax/phys.DeltPMax >= 1.0) { // [Luding2008], equation (8)
phys.k2 = phys.kp; // [Singh2013], equation (10)
} else {
- phys.k2 = phys.k1 + (phys.kp - phys.k1)*phys.ThetMax/phys.ThetPMax;
- }
+ phys.k2 = phys.k1 + (phys.kp - phys.k1)*phys.DeltMax/phys.DeltPMax;
+ }
+
+ if (phys.k2>phys.kp) {
+ phys.k2 = phys.kp;
+ }
+
+ if (phys.k1>phys.k2) {
+ phys.k1 = phys.k2;
+ }
+
+ phys.DeltMin = (phys.k2- phys.k1)/(phys.k2 + phys.kc);
- if (Theta > phys.ThetMax) {
- phys.ThetMax = Theta;
- phys.ThetNull = (1.0 - phys.k1/phys.k2)*phys.ThetMax; // [Luding2008], equation over Fig 1
- // [Singh2013], equation (8)
- }
-
- Real k2DeltaTtmp = phys.k2*(Theta - phys.ThetNull);
- if ( k2DeltaTtmp >= phys.k1*Theta) { // [Luding2008], equation (6)
- forceHys = phys.k1*Theta; // [Singh2013], equation (6)
- } else if (k2DeltaTtmp > -phys.kc*Theta and k2DeltaTtmp < phys.k1*Theta) {
- forceHys = phys.k2*(Theta-phys.ThetNull);
- } else if (k2DeltaTtmp<=-phys.kc*Theta) {
- forceHys = -phys.kc*Theta;
- }
-
-
+ if (Delt > phys.DeltMax) {
+ phys.DeltMax = Delt;
+ phys.DeltNull = std::min((1.0 - phys.k1/phys.k2)*phys.DeltMax, phys.DeltPNull); // [Luding2008], equation over Fig 1
+ // [Singh2013], equation (8)
+ }
+
+ Real k2DeltTtmp = phys.k2*(Delt - phys.DeltNull); // [Luding2008], equation (6)
+ // [Singh2013], equation (6)
+
+ if ( k2DeltTtmp >= phys.k1*Delt) {
+ if (Delt<phys.DeltPMax){
+ forceHys = phys.k1*Delt;
+ } else {
+ forceHys = k2DeltTtmp;
+ }
+ } else if (k2DeltTtmp > -phys.kc*Delt and k2DeltTtmp < phys.k1*Delt) {
+ forceHys = k2DeltTtmp;
+ } else if (k2DeltTtmp<=-phys.kc*Delt) {
+ if ((Delt - phys.DeltPrev) < 0) {
+ forceHys = -phys.kc*Delt;
+ phys.DeltMax = Delt*(phys.k2 + phys.kc)/(phys.k2 - phys.k1); // [Singh2013], equation (9)
+ phys.DeltNull = std::min((1.0 - phys.k1/phys.k2)*phys.DeltMax, phys.DeltPNull); // [Luding2008], equation over Fig 1
+ // [Singh2013], equation (8)
+ } else {
+ forceHys = k2DeltTtmp;
+ }
+ }
+
+ phys.DeltPrev = Delt;
//===================================================================
//===================================================================
=== modified file 'pkg/dem/LudingPM.hpp'
--- pkg/dem/LudingPM.hpp 2013-07-08 08:52:03 +0000
+++ pkg/dem/LudingPM.hpp 2013-10-11 14:42:51 +0000
@@ -32,9 +32,12 @@
((Real,kp,NaN,,"Slope of unloading and reloading limit elastic branch"))
((Real,kc,NaN,,"Slope of irreversible, tensile adhesive branch"))
((Real,PhiF,NaN,,"Dimensionless plasticity depth"))
- ((Real,ThetMax,NaN,,"Maximum overlap between particles for a collision"))
- ((Real,ThetPMax,NaN,,"Maximum overlap between particles for the limit case"))
- ((Real,ThetNull,NaN,,"Force free overlap, plastic contact deformation"))
+ ((Real,DeltMin,NaN,,"MinimalDelta value of delta"))
+ ((Real,DeltMax,NaN,,"Maximum overlap between particles for a collision"))
+ ((Real,DeltPMax,NaN,,"Maximum overlap between particles for the limit case"))
+ ((Real,DeltNull,NaN,,"Force free overlap, plastic contact deformation"))
+ ((Real,DeltPNull,NaN,,"Max force free overlap, plastic contact deformation"))
+ ((Real,DeltPrev,NaN,,"Previous value of delta"))
((Real,G0,NaN,,"Viscous damping")),
createIndex();
)
=== modified file 'pkg/dem/NewtonIntegrator.hpp'
--- pkg/dem/NewtonIntegrator.hpp 2013-05-14 21:24:11 +0000
+++ pkg/dem/NewtonIntegrator.hpp 2013-09-26 09:03:43 +0000
@@ -61,7 +61,7 @@
virtual void action();
YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(NewtonIntegrator,GlobalEngine,"Engine integrating newtonian motion equations.",
((Real,damping,0.2,,"damping coefficient for Cundall's non viscous damping (see [Chareyre2005]_) [-]"))
- ((Vector3r,gravity,Vector3r::Zero(),,"Gravitational acceleration (effectifely replaces GravityEngine)."))
+ ((Vector3r,gravity,Vector3r::Zero(),,"Gravitational acceleration (effectively replaces GravityEngine)."))
((Real,maxVelocitySq,NaN,,"store square of max. velocity, for informative purposes; computed again at every step. |yupdate|"))
((bool,exactAsphericalRot,true,,"Enable more exact body rotation integrator for :yref:`aspherical bodies<Body.aspherical>` *only*, using formulation from [Allen1989]_, pg. 89."))
((Matrix3r,prevVelGrad,Matrix3r::Zero(),,"Store previous velocity gradient (:yref:`Cell::velGrad`) to track acceleration. |yupdate|"))
=== added file 'pkg/dem/Polyhedra.cpp'
--- pkg/dem/Polyhedra.cpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/Polyhedra.cpp 2013-10-17 11:59:10 +0000
@@ -0,0 +1,427 @@
+// © 2013 Jan Elias, http://www.fce.vutbr.cz/STM/elias.j/, elias.j@xxxxxxxxxxxx
+// https://www.vutbr.cz/www_base/gigadisk.php?i=95194aa9a
+
+#ifdef YADE_CGAL
+
+#include<yade/core/Interaction.hpp>
+#include<yade/core/Omega.hpp>
+#include<yade/core/Scene.hpp>
+#include<yade/core/State.hpp>
+#include<yade/pkg/common/ElastMat.hpp>
+#include<yade/pkg/common/Aabb.hpp>
+#include"Polyhedra.hpp"
+
+#define _USE_MATH_DEFINES
+
+YADE_PLUGIN(/* self-contained in hpp: */ (Polyhedra) (PolyhedraGeom) (Bo1_Polyhedra_Aabb) (PolyhedraPhys) (PolyhedraMat) (Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys) (PolyhedraVolumetricLaw)
+ /* some code in cpp (this file): */
+ #ifdef YADE_OPENGL
+ (Gl1_Polyhedra)
+ #endif
+ );
+
+//*********************************************************************************
+/* Polyhedra Constructor */
+
+void Polyhedra::Initialize(){
+
+ if (init) return;
+
+ bool isRandom = false;
+
+ //get vertices
+ int N = (int) v.size();
+ if (N==0) {
+ //generate randomly
+ while ((int) v.size()<4) GenerateRandomGeometry();
+ N = (int) v.size();
+ isRandom = true;
+ }
+
+ //compute convex hull of vertices
+ std::vector<CGALpoint> points;
+ points.resize(v.size());
+ for(int i=0;i<N;i++) {
+ points[i] = CGALpoint(v[i][0],v[i][1],v[i][2]);
+ }
+
+ CGAL::convex_hull_3(points.begin(), points.end(), P);
+
+ //connect triagular facets if possible
+ std::transform(P.facets_begin(), P.facets_end(), P.planes_begin(),Plane_equation());
+ P = Simplify(P, 1E-9);
+
+ //modify order of v according to CGAl polyhedron
+ int i = 0;
+ v.clear();
+ for (Polyhedron::Vertex_iterator vIter = P.vertices_begin(); vIter != P.vertices_end(); ++vIter, i++){
+ v.push_back(Vector3r(vIter->point().x(),vIter->point().y(),vIter->point().z()));
+ }
+
+ //list surface triangles for plotting
+ faceTri.clear();
+ std::transform(P.facets_begin(), P.facets_end(), P.planes_begin(),Plane_equation());
+ for (Polyhedron::Facet_iterator fIter = P.facets_begin(); fIter != P.facets_end(); fIter++){
+ Polyhedron::Halfedge_around_facet_circulator hfc0;
+ int n = fIter->facet_degree();
+ hfc0 = fIter->facet_begin();
+ int a = std::distance(P.vertices_begin(), hfc0->vertex());
+ for (int i=2; i<n; i++){
+ ++hfc0;
+ faceTri.push_back(a);
+ faceTri.push_back(std::distance(P.vertices_begin(), hfc0->vertex()));
+ faceTri.push_back(std::distance(P.vertices_begin(), hfc0->next()->vertex()));
+ }
+ }
+
+ //compute centroid and volume
+ P_volume_centroid(P, &volume, ¢roid);
+ //check vierd behavior of CGAL in tessalation
+ if(isRandom && volume*1.75<4./3.*3.14*size[0]/2.*size[1]/2.*size[2]/2.) {
+ v.clear();
+ seed = rand();
+ Initialize();
+ }
+ Vector3r translation((-1)*centroid);
+
+ //set centroid to be [0,0,0]
+ for(int i=0;i<N;i++) {
+ v[i] = v[i]-centroid;
+ }
+ if(isRandom) centroid = Vector3r::Zero();
+
+ Vector3r origin(0,0,0);
+
+ //move and rotate also the CGAL structure Polyhedron
+ Transformation t_trans(1.,0.,0.,translation[0],0.,1.,0.,translation[1],0.,0.,1.,translation[2],1.);
+ std::transform( P.points_begin(), P.points_end(), P.points_begin(), t_trans);
+
+ //compute inertia
+ double vtet;
+ Vector3r ctet;
+ Matrix3r Itet1, Itet2;
+ Matrix3r inertia_tensor(Matrix3r::Zero());
+ for(int i=0; i<(int) faceTri.size(); i+=3){
+ vtet = std::abs((origin-v[faceTri[i+2]]).dot((v[faceTri[i]]-v[faceTri[i+2]]).cross(v[faceTri[i+1]]-v[faceTri[i+2]]))/6.);
+ ctet = (origin+v[faceTri[i]]+v[faceTri[i+1]]+v[faceTri[i+2]]) / 4.;
+ Itet1 = TetraInertiaTensor(origin-ctet, v[faceTri[i]]-ctet, v[faceTri[i+1]]-ctet, v[faceTri[i+2]]-ctet);
+ ctet = ctet-origin;
+ Itet2<<
+ ctet[1]*ctet[1]+ctet[2]*ctet[2], -ctet[0]*ctet[1], -ctet[0]*ctet[2],
+ -ctet[0]*ctet[1], ctet[0]*ctet[0]+ctet[2]*ctet[2], -ctet[2]*ctet[1],
+ -ctet[0]*ctet[2], -ctet[2]*ctet[1], ctet[1]*ctet[1]+ctet[0]*ctet[0];
+ inertia_tensor = inertia_tensor + Itet1 + Itet2*vtet;
+ }
+
+ if(abs(inertia_tensor(0,1))+abs(inertia_tensor(0,2))+abs(inertia_tensor(1,2)) < 1E-13){
+ // no need to rotate, inertia already diagonal
+ orientation = Quaternionr::Identity();
+ inertia = Vector3r(inertia_tensor(0,0),inertia_tensor(1,1),inertia_tensor(2,2));
+ }else{
+ // calculate eigenvectors of I
+ Vector3r rot;
+ Matrix3r I_rot(Matrix3r::Zero()), I_new(Matrix3r::Zero());
+ matrixEigenDecomposition(inertia_tensor,I_rot,I_new);
+ // I_rot = eigenvectors of inertia_tensors in columns
+ // I_new = eigenvalues on diagonal
+ // set positove direction of vectors - otherwise reloading does not work
+ Matrix3r sign(Matrix3r::Zero());
+ double max_v_signed = I_rot(0,0);
+ double max_v = abs(I_rot(0,0));
+ if (max_v < abs(I_rot(1,0))) {max_v_signed = I_rot(1,0); max_v = abs(I_rot(1,0));}
+ if (max_v < abs(I_rot(2,0))) {max_v_signed = I_rot(2,0); max_v = abs(I_rot(2,0));}
+ sign(0,0) = max_v_signed/max_v;
+ max_v_signed = I_rot(0,1);
+ max_v = abs(I_rot(0,1));
+ if (max_v < abs(I_rot(1,1))) {max_v_signed = I_rot(1,1); max_v = abs(I_rot(1,1));}
+ if (max_v < abs(I_rot(2,1))) {max_v_signed = I_rot(2,1); max_v = abs(I_rot(2,1));}
+ sign(1,1) = max_v_signed/max_v;
+ sign(2,2) = 1.;
+ I_rot = I_rot*sign;
+ // force the eigenvectors to be right-hand oriented
+ Vector3r third = (I_rot.col(0)).cross(I_rot.col(1));
+ I_rot(0,2) = third[0];
+ I_rot(1,2) = third[1];
+ I_rot(2,2) = third[2];
+
+
+ inertia = Vector3r(I_new(0,0),I_new(1,1),I_new(2,2));
+ orientation = Quaternionr(I_rot);
+ //rotate the voronoi cell so that x - is maximal inertia axis and z - is minimal inertia axis
+ //orientation.normalize(); //not needed
+ for(int i=0; i< (int) v.size();i++) {
+ v[i] = orientation.conjugate()*v[i];
+ }
+
+ //rotate also the CGAL structure Polyhedron
+ Matrix3r rot_mat = (orientation.conjugate()).toRotationMatrix();
+ Transformation t_rot(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2),rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),1.);
+ std::transform( P.points_begin(), P.points_end(), P.points_begin(), t_rot);
+
+ }
+ //initialization done
+ init = 1;
+}
+
+//**************************************************************************
+/* Generator of randomly shaped polyhedron based on Voronoi tessellation*/
+
+void Polyhedra::GenerateRandomGeometry(){
+ srand(seed);
+
+ vector<CGALpoint> nuclei;
+ nuclei.push_back(CGALpoint(5.,5.,5.));
+ CGALpoint trial;
+ int iter = 0;
+ bool isOK;
+ //fill box 5x5x5 with randomly located nuclei with restricted minimal mutual distance 0.75 which gives approximate mean mutual distance 1;
+ double dist_min2 = 0.75*0.75;
+ while(iter<500){
+ isOK = true;
+ iter++;
+ trial = CGALpoint(double(rand())/RAND_MAX*5.+2.5,double(rand())/RAND_MAX*5.+2.5,double(rand())/RAND_MAX*5.+2.5);
+ for(int i=0;i< (int) nuclei.size();i++) {
+ isOK = pow(nuclei[i].x()-trial.x(),2)+pow(nuclei[i].y()-trial.y(),2)+pow(nuclei[i].z()-trial.z(),2) > dist_min2;
+ if (!isOK) break;
+ }
+
+ if(isOK){
+ iter = 0;
+ nuclei.push_back(trial);
+ }
+ }
+
+
+ //perform Voronoi tessellation
+ nuclei.erase(nuclei.begin());
+ Triangulation dt(nuclei.begin(), nuclei.end());
+ Triangulation::Vertex_handle zero_point = dt.insert(CGALpoint(5.,5.,5.));
+ v.clear();
+ std::vector<Triangulation::Cell_handle> ch_cells;
+ dt.incident_cells(zero_point,std::back_inserter(ch_cells));
+ for(std::vector<Triangulation::Cell_handle>::iterator ci = ch_cells.begin(); ci !=ch_cells.end(); ++ci){
+ v.push_back(FromCGALPoint(dt.dual(*ci))-Vector3r(5.,5.,5.));
+ }
+
+
+ //resize and rotate the voronoi cell
+ Quaternionr Rot(double(rand())/RAND_MAX,double(rand())/RAND_MAX,double(rand())/RAND_MAX,double(rand())/RAND_MAX);
+ Rot.normalize();
+ for(int i=0; i< (int) v.size();i++) {
+ v[i] = Rot*(Vector3r(v[i][0]*size[0],v[i][1]*size[1],v[i][2]*size[2]));
+ }
+
+ //to avoid patological cases (that should not be present, but CGAL works somehow unpredicable)
+ if (v.size() < 8) {
+ cout << "wrong " << v.size() << endl;
+ v.clear();
+ seed = rand();
+ GenerateRandomGeometry();
+ }
+}
+
+//****************************************************************************************
+/* Destructor */
+
+Polyhedra::~Polyhedra(){}
+
+//****************************************************************************************
+/* Destructor */
+
+PolyhedraGeom::~PolyhedraGeom(){}
+
+//****************************************************************************************
+/* AaBb overlap checker */
+
+void Bo1_Polyhedra_Aabb::go(const shared_ptr<Shape>& ig, shared_ptr<Bound>& bv, const Se3r& se3, const Body*){
+
+ Polyhedra* t=static_cast<Polyhedra*>(ig.get());
+ if (!t->IsInitialized()) t->Initialize();
+ if(!bv){ bv=shared_ptr<Bound>(new Aabb); }
+ Aabb* aabb=static_cast<Aabb*>(bv.get());
+ //Quaternionr invRot=se3.orientation.conjugate();
+ int N = (int) t->v.size();
+ Vector3r v_g, mincoords(0.,0.,0.), maxcoords(0.,0.,0.);
+ for(int i=0; i<N; i++) {
+ v_g=se3.orientation*t->v[i]; // vertices in global coordinates
+ mincoords = Vector3r(min(mincoords[0],v_g[0]),min(mincoords[1],v_g[1]),min(mincoords[2],v_g[2]));
+ maxcoords = Vector3r(max(maxcoords[0],v_g[0]),max(maxcoords[1],v_g[1]),max(maxcoords[2],v_g[2]));
+ }
+ aabb->min=se3.position+mincoords;
+ aabb->max=se3.position+maxcoords;
+}
+
+//**********************************************************************************
+/* Plotting */
+
+#ifdef YADE_OPENGL
+ #include<yade/lib/opengl/OpenGLWrapper.hpp>
+ void Gl1_Polyhedra::go(const shared_ptr<Shape>& cm, const shared_ptr<State>&,bool,const GLViewInfo&)
+ {
+ glMaterialv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,Vector3r(cm->color[0],cm->color[1],cm->color[2]));
+ glColor3v(cm->color);
+ Polyhedra* t=static_cast<Polyhedra*>(cm.get());
+ vector<int> faceTri = t->GetSurfaceTriangulation();
+
+ if (0) {
+ glDisable(GL_LIGHTING);
+ glBegin(GL_LINES);
+ #define __ONEWIRE(a,b) glVertex3v(t->v[a]);glVertex3v(t->v[b])
+ for(int tri=0; tri < (int) faceTri.size(); tri+=3) {__ONEWIRE(faceTri[tri],faceTri[tri+1]); __ONEWIRE(faceTri[tri],faceTri[tri+2]); __ONEWIRE(faceTri[tri+1],faceTri[tri+2]);}
+ #undef __ONEWIRE
+ glEnd();
+ }
+ else
+ {
+ Vector3r centroid = t->GetCentroid();
+ Vector3r faceCenter, n;
+ glDisable(GL_CULL_FACE); glEnable(GL_LIGHTING);
+ glBegin(GL_TRIANGLES);
+ #define __ONEFACE(a,b,c) n=(t->v[b]-t->v[a]).cross(t->v[c]-t->v[a]); n.normalize(); faceCenter=(t->v[a]+t->v[b]+t->v[c])/3.; if((faceCenter-centroid).dot(n)<0)n=-n; glNormal3v(n); glVertex3v(t->v[a]); glVertex3v(t->v[b]); glVertex3v(t->v[c]);
+ for(int tri=0; tri < (int) faceTri.size(); tri+=3) {__ONEFACE(faceTri[tri],faceTri[tri+1],faceTri[tri+2]);}
+ #undef __ONEFACE
+ glEnd();
+ }
+ }
+#endif
+
+//**********************************************************************************
+//!Precompute data needed for rotating tangent vectors attached to the interaction
+
+void PolyhedraGeom::precompute(const State& rbp1, const State& rbp2, const Scene* scene, const shared_ptr<Interaction>& c, const Vector3r&
+currentNormal, bool isNew, const Vector3r& shift2){
+
+ if(!isNew) {
+ orthonormal_axis = normal.cross(currentNormal);
+ Real angle = scene->dt*0.5*normal.dot(rbp1.angVel + rbp2.angVel);
+ twist_axis = angle*normal;}
+ else twist_axis=orthonormal_axis=Vector3r::Zero();
+ //Update contact normal
+ normal=currentNormal;
+ //Precompute shear increment
+ Vector3r c1x = (contactPoint - rbp1.pos);
+ Vector3r c2x = (contactPoint - rbp2.pos + shift2);
+ Vector3r relativeVelocity = (rbp2.vel+rbp2.angVel.cross(c2x)) - (rbp1.vel+rbp1.angVel.cross(c1x));
+ //keep the shear part only
+ relativeVelocity = relativeVelocity-normal.dot(relativeVelocity)*normal;
+ shearInc = relativeVelocity*scene->dt;
+}
+
+//**********************************************************************************
+Vector3r& PolyhedraGeom::rotate(Vector3r& shearForce) const {
+ // approximated rotations
+ shearForce -= shearForce.cross(orthonormal_axis);
+ shearForce -= shearForce.cross(twist_axis);
+ //NOTE : make sure it is in the tangent plane? It's never been done before. Is it not adding rounding errors at the same time in fact?...
+ shearForce -= normal.dot(shearForce)*normal;
+ return shearForce;
+}
+
+
+//**********************************************************************************
+/* Material law, physics */
+
+void Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys::go( const shared_ptr<Material>& b1
+ , const shared_ptr<Material>& b2
+ , const shared_ptr<Interaction>& interaction)
+{
+ if(interaction->phys) return;
+ const shared_ptr<PolyhedraMat>& mat1 = YADE_PTR_CAST<PolyhedraMat>(b1);
+ const shared_ptr<PolyhedraMat>& mat2 = YADE_PTR_CAST<PolyhedraMat>(b2);
+ interaction->phys = shared_ptr<PolyhedraPhys>(new PolyhedraPhys());
+ const shared_ptr<PolyhedraPhys>& contactPhysics = YADE_PTR_CAST<PolyhedraPhys>(interaction->phys);
+ Real Kna = mat1->Kn;
+ Real Knb = mat2->Kn;
+ Real Ksa = mat1->Ks;
+ Real Ksb = mat2->Ks;
+ Real frictionAngle = std::min(mat1->frictionAngle,mat2->frictionAngle);
+ contactPhysics->tangensOfFrictionAngle = std::tan(frictionAngle);
+ contactPhysics->kn = Kna*Knb/(Kna+Knb);
+ contactPhysics->ks = Ksa*Ksb/(Ksa+Ksb);
+};
+
+//**************************************************************************************
+#if 1
+Real PolyhedraVolumetricLaw::getPlasticDissipation() {return (Real) plasticDissipation;}
+void PolyhedraVolumetricLaw::initPlasticDissipation(Real initVal) {plasticDissipation.reset(); plasticDissipation+=initVal;}
+Real PolyhedraVolumetricLaw::elasticEnergy()
+{
+ Real energy=0;
+ FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
+ if(!I->isReal()) continue;
+ FrictPhys* phys = dynamic_cast<FrictPhys*>(I->phys.get());
+ if(phys) {
+ energy += 0.5*(phys->normalForce.squaredNorm()/phys->kn + phys->shearForce.squaredNorm()/phys->ks);}
+ }
+ return energy;
+}
+#endif
+
+
+//**************************************************************************************
+// Apply forces on polyhedrons in collision based on geometric configuration
+void PolyhedraVolumetricLaw::go(shared_ptr<IGeom>& ig, shared_ptr<IPhys>& ip, Interaction* I){
+
+ if (!I->geom) {I->phys=shared_ptr<IPhys>(); return;}
+ const shared_ptr<PolyhedraGeom>& contactGeom(dynamic_pointer_cast<PolyhedraGeom>(I->geom));
+ if(!contactGeom) {I->phys=shared_ptr<IPhys>(); return;}
+ const Body::id_t idA=I->getId1(), idB=I->getId2();
+ const shared_ptr<Body>& A=Body::byId(idA), B=Body::byId(idB);
+
+ PolyhedraPhys* phys = dynamic_cast<PolyhedraPhys*>(I->phys.get());
+
+ //erase the interaction when aAbB shows separation, otherwise keep it to be able to store previous separating plane for fast detection of separation
+ if (A->bound->min[0] >= B->bound->max[0] || B->bound->min[0] >= A->bound->max[0] || A->bound->min[1] >= B->bound->max[1] || B->bound->min[1] >= A->bound->max[1] || A->bound->min[2] >= B->bound->max[2] || B->bound->min[2] >= A->bound->max[2]) {
+ scene->interactions->requestErase(I);
+ return;
+ }
+
+ //zero penetration depth means no interaction force
+ if(!(contactGeom->penetrationVolume > 0) ) {I->phys=shared_ptr<IPhys>(); return;}
+ Vector3r normalForce=contactGeom->normal*contactGeom->penetrationVolume*phys->kn;
+
+ //shear force: in case the polyhdras are separated and come to contact again, one should not use the previous shear force
+ if (contactGeom->isShearNew)
+ shearForce = Vector3r::Zero();
+ else
+ shearForce = contactGeom->rotate(shearForce);
+
+ const Vector3r& shearDisp = contactGeom->shearInc;
+ shearForce -= phys->ks*shearDisp;
+
+ Real maxFs = normalForce.squaredNorm()*std::pow(phys->tangensOfFrictionAngle,2);
+
+ if (likely(!scene->trackEnergy && !traceEnergy)){//Update force but don't compute energy terms (see below))
+ // PFC3d SlipModel, is using friction angle. CoulombCriterion
+ if( shearForce.squaredNorm() > maxFs ){
+ Real ratio = sqrt(maxFs) / shearForce.norm();
+ shearForce *= ratio;}
+ } else {
+ //almost the same with additional Vector3r instatinated for energy tracing,
+ //duplicated block to make sure there is no cost for the instanciation of the vector when traceEnergy==false
+ if(shearForce.squaredNorm() > maxFs){
+ Real ratio = sqrt(maxFs) / shearForce.norm();
+ Vector3r trialForce=shearForce;//store prev force for definition of plastic slip
+ //define the plastic work input and increment the total plastic energy dissipated
+ shearForce *= ratio;
+ Real dissip=((1/phys->ks)*(trialForce-shearForce)).dot(shearForce);
+ if (traceEnergy) plasticDissipation += dissip;
+ else if(dissip>0) scene->energy->add(dissip,"plastDissip",plastDissipIx,false);
+ }
+ // compute elastic energy as well
+ scene->energy->add(0.5*(normalForce.squaredNorm()/phys->kn+shearForce.squaredNorm()/phys->ks),"elastPotential",elastPotentialIx,true);
+ }
+
+ Vector3r F = -normalForce-shearForce;
+ if (contactGeom->equivalentPenetrationDepth != contactGeom->equivalentPenetrationDepth) exit(1);
+ scene->forces.addForce (idA,F);
+ scene->forces.addForce (idB, -F);
+ scene->forces.addTorque(idA, -(A->state->pos-contactGeom->contactPoint).cross(F));
+ scene->forces.addTorque(idB, (B->state->pos-contactGeom->contactPoint).cross(F));
+
+ //needed to be able to acces interaction forces in other parts of yade
+ phys->normalForce = normalForce;
+ phys->shearForce = shearForce;
+}
+
+#endif // YADE_CGAL
=== added file 'pkg/dem/Polyhedra.hpp'
--- pkg/dem/Polyhedra.hpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/Polyhedra.hpp 2013-10-16 15:24:49 +0000
@@ -0,0 +1,274 @@
+// © 2013 Jan Elias, http://www.fce.vutbr.cz/STM/elias.j/, elias.j@xxxxxxxxxxxx
+// https://www.vutbr.cz/www_base/gigadisk.php?i=95194aa9a
+
+
+#pragma once
+
+#ifdef YADE_CGAL
+
+#include<vector>
+#include<yade/core/Shape.hpp>
+#include<yade/core/IGeom.hpp>
+#include<yade/core/GlobalEngine.hpp>
+#include<yade/core/Material.hpp>
+#include<yade/pkg/common/Aabb.hpp>
+#include<yade/pkg/common/Dispatching.hpp>
+#include<yade/pkg/dem/FrictPhys.hpp>
+#include<yade/pkg/common/Wall.hpp>
+#include<yade/pkg/common/Facet.hpp>
+#include<yade/lib/base/openmp-accu.hpp>
+
+#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
+#include <CGAL/Delaunay_triangulation_3.h>
+#include <CGAL/Triangulation_data_structure_3.h>
+#include <CGAL/Polyhedron_3.h>
+#include <CGAL/Polyhedron_items_with_id_3.h>
+#include <CGAL/convex_hull_3.h>
+#include <CGAL/Tetrahedron_3.h>
+#include <CGAL/linear_least_squares_fitting_3.h>
+
+#include<time.h>
+
+#define likely(x) __builtin_expect((x),1)
+#define unlikely(x) __builtin_expect((x),0)
+
+//CGAL definitions - does not work with another kernel!! Why???
+typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
+typedef CGAL::Polyhedron_3<K> Polyhedron;
+typedef CGAL::Delaunay_triangulation_3<K> Triangulation;
+typedef K::Point_3 CGALpoint;
+typedef K::Vector_3 CGALvector;
+typedef CGAL::Aff_transformation_3<K> Transformation;
+typedef K::Segment_3 Segment;
+typedef CGAL::Triangle_3<K> Triangle;
+typedef CGAL::Plane_3<K> Plane;
+typedef CGAL::Line_3<K> Line;
+typedef CGAL::Origin CGAL_ORIGIN;
+
+//**********************************************************************************
+class Polyhedra: public Shape{
+ public:
+ //constructor from Vertices
+ Polyhedra(std::vector<Vector3r> V) { createIndex(); v.resize(V.size()); for(int i=0;i<(int) V.size();i++) v[i]=V[i]; Initialize();} //contructor of "random" polyhedra
+ Polyhedra(Vector3r xsize, int xseed) { createIndex(); seed=xseed; size=xsize; v.clear(); Initialize();}
+ virtual ~Polyhedra();
+ Vector3r GetCentroid(){Initialize(); return centroid;}
+ Vector3r GetInertia(){Initialize(); return inertia;}
+ vector<int> GetSurfaceTriangulation(){Initialize(); return faceTri;}
+ void Initialize();
+ bool IsInitialized(){return init;}
+ std::vector<Vector3r> GetOriginalVertices();
+ double GetVolume(){Initialize(); return volume;}
+ Quaternionr GetOri(){Initialize(); return orientation;}
+ Polyhedron GetPolyhedron(){return P;};
+ void Clear(){v.clear(); P.clear(); init = 0; size = Vector3r(1.,1.,1.); faceTri.clear();};
+
+ protected:
+ //triangulation of facets for plotting
+ vector<int> faceTri;
+ //centroid = (0,0,0) for random Polyhedra
+ Vector3r centroid;
+ //CGAL structure Polyhedron
+ Polyhedron P;
+ //sign of performed initialization
+ bool init;
+ //centroid Volume
+ double volume;
+ //centroid inerta - diagonal of the tensor
+ Vector3r inertia;
+ //orientation, that provides diagonal inertia tensor
+ Quaternionr orientation;
+ void GenerateRandomGeometry();
+
+ YADE_CLASS_BASE_DOC_ATTRS_INIT_CTOR_PY(Polyhedra,Shape,"Polyhedral (convex) geometry.",
+ ((std::vector<Vector3r>,v,,,"Tetrahedron vertices in global coordinate system."))
+ ((int,seed, time(NULL),,"Seed for random generator."))
+ ((Vector3r, size, Vector3r(1.,1.,1.),,"Size of the grain in meters - x,y,z - before random rotation")),
+ /*init*/,
+ /*ctor*/
+ createIndex();
+ init = 0,
+ .def("Initialize",&Polyhedra::Initialize,"Initialization")
+ .def("GetVolume",&Polyhedra::GetVolume,"return polyhedra's volume")
+ .def("GetInertia",&Polyhedra::GetInertia,"return polyhedra's inertia tensor")
+ .def("GetOri",&Polyhedra::GetOri,"return polyhedra's orientation")
+ .def("GetCentroid",&Polyhedra::GetCentroid,"return polyhedra's centroid")
+ );
+ REGISTER_CLASS_INDEX(Polyhedra,Shape);
+};
+REGISTER_SERIALIZABLE(Polyhedra);
+
+
+//***************************************************************************
+/*! Collision configuration for Polyhedra and something.
+ * This is expressed as penetration volume properties: centroid, volume, depth ...
+ *
+ * Self-contained. */
+class PolyhedraGeom: public IGeom{
+ public:
+ virtual ~PolyhedraGeom();
+ //precompute data for shear evaluation
+ void precompute(const State& rbp1, const State& rbp2, const Scene* scene, const shared_ptr<Interaction>& c, const Vector3r& currentNormal, bool isNew, const Vector3r& shift2);
+ Vector3r& rotate(Vector3r& shearForce) const;
+ //sep_plane is a code storing plane, that previously separated two polyhedras. It is used for faster detection of non-overlap.
+ std::vector<int> sep_plane;
+ bool isShearNew;
+ protected:
+ YADE_CLASS_BASE_DOC_ATTRS_CTOR(PolyhedraGeom,IGeom,"Geometry of interaction between 2 :yref:`vector<Polyhedra>`, including volumetric characteristics",
+ ((Real,penetrationVolume,NaN,,"Volume of overlap [m³]"))
+ ((Real,equivalentCrossSection,NaN,,"Cross-section area of the overlap (perpendicular to the normal) - not used"))
+ ((Real,equivalentPenetrationDepth,NaN,,"volume / equivalentCrossSection - not used"))
+ ((Vector3r,contactPoint,Vector3r::Zero(),,"Contact point (global coords), centriod of the overlapping polyhedron"))
+ ((Vector3r,shearInc,Vector3r::Zero(),,"Shear displacement increment in the last step"))
+ ((Vector3r,normal,Vector3r::Zero(),,"Normal direction of the interaction"))
+ ((Vector3r,twist_axis,Vector3r::Zero(),,""))
+ ((Vector3r,orthonormal_axis,Vector3r::Zero(),,"")),
+ createIndex();
+ sep_plane.assign(3,0);
+ );
+ //FUNCTOR2D(Tetra,Tetra);
+ REGISTER_CLASS_INDEX(PolyhedraGeom,IGeom);
+};
+REGISTER_SERIALIZABLE(PolyhedraGeom);
+
+//***************************************************************************
+/*! Creates Aabb from Polyhedra.
+ *
+ * Self-contained. */
+class Bo1_Polyhedra_Aabb: public BoundFunctor{
+ public:
+ void go(const shared_ptr<Shape>& ig, shared_ptr<Bound>& bv, const Se3r& se3, const Body*);
+ FUNCTOR1D(Polyhedra);
+ YADE_CLASS_BASE_DOC(Bo1_Polyhedra_Aabb,BoundFunctor,"Create/update :yref:`Aabb` of a :yref:`Polyhedra`");
+};
+REGISTER_SERIALIZABLE(Bo1_Polyhedra_Aabb);
+
+//***************************************************************************
+#ifdef YADE_OPENGL
+ #include<yade/pkg/common/GLDrawFunctors.hpp>
+ /*! Draw Polyhedra using OpenGL */
+ class Gl1_Polyhedra: public GlShapeFunctor{
+ public:
+ virtual void go(const shared_ptr<Shape>&, const shared_ptr<State>&,bool,const GLViewInfo&);
+ YADE_CLASS_BASE_DOC(Gl1_Polyhedra,GlShapeFunctor,"Renders :yref:`Polyhedra` object");
+ RENDERS(Polyhedra);
+ };
+ REGISTER_SERIALIZABLE(Gl1_Polyhedra);
+#endif
+
+//***************************************************************************
+/*! Elastic material */
+class PolyhedraMat: public Material{
+ public:
+ PolyhedraMat(double N, double S, double F){Kn=N; Ks=S; frictionAngle=F;};
+ double GetStrength(){return strength;};
+ virtual ~PolyhedraMat(){};
+ YADE_CLASS_BASE_DOC_ATTRS_CTOR(PolyhedraMat,Material,"Elastic material with Coulomb friction.",
+ ((Real,Kn,1e8,,"Normal volumetric 'stiffness' (N/m3)."))
+ ((Real,Ks,1e5,,"Shear stiffness (N/m)."))
+ ((Real,frictionAngle,.5,,"Contact friction angle (in radians)."))
+ ((bool,IsSplitable,0,,"To be splitted ... or not"))
+ ((double,strength,100,,"Stress at whis polyhedra of volume 4/3*pi [mm] breaks.")),
+ /*ctor*/ createIndex();
+ );
+ REGISTER_CLASS_INDEX(PolyhedraMat,Material);
+};
+REGISTER_SERIALIZABLE(PolyhedraMat);
+
+//***************************************************************************
+class PolyhedraPhys: public IPhys{
+ public:
+ virtual ~PolyhedraPhys(){};
+ YADE_CLASS_BASE_DOC_ATTRS_CTOR(PolyhedraPhys,IPhys,"Simple elastic material with friction for volumetric constitutive laws",
+ ((Real,kn,0,,"Normal stiffness"))
+ ((Vector3r,normalForce,Vector3r::Zero(),,"Normal force after previous step (in global coordinates)."))
+ ((Real,ks,0,,"Shear stiffness"))
+ ((Vector3r,shearForce,Vector3r::Zero(),,"Shear force after previous step (in global coordinates)."))
+ ((Real,tangensOfFrictionAngle,NaN,,"tangens of angle of internal friction")),
+ /*ctor*/ createIndex();
+ );
+ REGISTER_CLASS_INDEX(PolyhedraPhys,IPhys);
+};
+REGISTER_SERIALIZABLE(PolyhedraPhys);
+
+//***************************************************************************
+class Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys: public IPhysFunctor{
+ public:
+ virtual void go(const shared_ptr<Material>& b1,
+ const shared_ptr<Material>& b2,
+ const shared_ptr<Interaction>& interaction);
+ FUNCTOR2D(PolyhedraMat,PolyhedraMat);
+ YADE_CLASS_BASE_DOC_ATTRS(Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys,IPhysFunctor,"",
+ );
+};
+REGISTER_SERIALIZABLE(Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys);
+
+//***************************************************************************
+/*! Calculate physical response based on penetration configuration given by TTetraGeom. */
+
+class PolyhedraVolumetricLaw: public LawFunctor{
+ OpenMPAccumulator<Real> plasticDissipation;
+ virtual void go(shared_ptr<IGeom>&, shared_ptr<IPhys>&, Interaction*);
+ Real elasticEnergy ();
+ Real getPlasticDissipation();
+ void initPlasticDissipation(Real initVal=0);
+ YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(PolyhedraVolumetricLaw,LawFunctor,"Calculate physical response of 2 :yref:`vector<Polyhedra>` in interaction, based on penetration configuration given by :yref:`PolyhedraGeom`.",
+ ((Vector3r,shearForce,Vector3r::Zero(),,"Shear force from last step"))
+ ((bool,traceEnergy,false,,"Define the total energy dissipated in plastic slips at all contacts. This will trace only plastic energy in this law, see O.trackEnergy for a more complete energies tracing"))
+ ((int,plastDissipIx,-1,(Attr::hidden|Attr::noSave),"Index for plastic dissipation (with O.trackEnergy)"))
+ ((int,elastPotentialIx,-1,(Attr::hidden|Attr::noSave),"Index for elastic potential energy (with O.trackEnergy)"))
+ ,,
+ .def("elasticEnergy",&PolyhedraVolumetricLaw::elasticEnergy,"Compute and return the total elastic energy in all \"FrictPhys\" contacts")
+ .def("plasticDissipation",&PolyhedraVolumetricLaw::getPlasticDissipation,"Total energy dissipated in plastic slips at all FrictPhys contacts. Computed only if :yref:`Law2_ScGeom_FrictPhys_CundallStrack::traceEnergy` is true.")
+ .def("initPlasticDissipation",&PolyhedraVolumetricLaw::initPlasticDissipation,"Initialize cummulated plastic dissipation to a value (0 by default).")
+ );
+ FUNCTOR2D(PolyhedraGeom,PolyhedraPhys);
+ DECLARE_LOGGER;
+};
+REGISTER_SERIALIZABLE(PolyhedraVolumetricLaw);
+
+
+//***************************************************************************
+//compute plane equation from three points on the facet
+struct Plane_equation {
+ template <class Facet>
+ typename Facet::Plane_3 operator()( Facet& f) {
+ typename Facet::Halfedge_handle h = f.halfedge();
+ typedef typename Facet::Plane_3 Plane;
+ return Plane( h->vertex()->point(),
+ h->next()->vertex()->point(),
+ h->next()->next()->vertex()->point());
+ }
+};
+//get Tetrahedron inertia
+Matrix3r TetraInertiaTensor(Vector3r av,Vector3r bv,Vector3r cv,Vector3r dv);
+//return intersection of two polyhedrons
+Polyhedron Polyhedron_Polyhedron_intersection(Polyhedron A, Polyhedron B, CGALpoint X, CGALpoint centroidA, CGALpoint centroidB, std::vector<int> &code);
+//return intersection of plane & polyhedron
+Polyhedron Polyhedron_Plane_intersection(Polyhedron A, Plane B, CGALpoint centroid, CGALpoint X);
+//return approximate intersection of sphere & polyhedron
+bool Sphere_Polyhedron_intersection(Polyhedron A, double r, CGALpoint C, CGALpoint centroid, double volume, CGALvector normal, double area);
+//return volume and centroid of polyhedra
+bool P_volume_centroid(Polyhedron P, double * volume, Vector3r * centroid);
+//CGAL - miniEigen communication
+Vector3r FromCGALPoint(CGALpoint A);
+Vector3r FromCGALVector(CGALvector A);
+CGALpoint ToCGALPoint(Vector3r A);
+CGALvector ToCGALVector(Vector3r A);
+//determination of intersection of two polyhedras
+bool do_intersect(Polyhedron A, Polyhedron B);
+bool do_intersect(Polyhedron A, Polyhedron B, std::vector<int> &sep_plane);
+//connect triagular facets if possible
+Polyhedron Simplify(Polyhedron P, double lim);
+//list of facets and edges
+void PrintPolyhedron(Polyhedron P);
+//normal by least square fitting of separating segments
+Vector3r FindNormal(Polyhedron Int, Polyhedron PA, Polyhedron PB);
+//calculate area of projection of polyhedron into the plane
+double CalculateProjectionArea(Polyhedron Int, CGALvector CGALnormal);
+//split polyhedron
+void SplitPolyhedra(const shared_ptr<Body>& body, Vector3r direction);
+//new polyhedra
+shared_ptr<Body> NewPolyhedra(vector<Vector3r> v, shared_ptr<Material> mat);
+
+#endif // YADE_CGAL
=== added file 'pkg/dem/Polyhedra_Ig2.cpp'
--- pkg/dem/Polyhedra_Ig2.cpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/Polyhedra_Ig2.cpp 2013-10-16 15:24:49 +0000
@@ -0,0 +1,305 @@
+// © 2013 Jan Elias, http://www.fce.vutbr.cz/STM/elias.j/, elias.j@xxxxxxxxxxxx
+// https://www.vutbr.cz/www_base/gigadisk.php?i=95194aa9a
+
+#ifdef YADE_CGAL
+
+#include"Polyhedra.hpp"
+#include"Polyhedra_Ig2.hpp"
+
+#define _USE_MATH_DEFINES
+
+YADE_PLUGIN(/* self-contained in hpp: */ (Ig2_Polyhedra_Polyhedra_PolyhedraGeom) (Ig2_Wall_Polyhedra_PolyhedraGeom) (Ig2_Facet_Polyhedra_PolyhedraGeom) );
+
+//**********************************************************************************
+/*! Create Polyhedra (collision geometry) from colliding Polyhedras. */
+
+bool Ig2_Polyhedra_Polyhedra_PolyhedraGeom::go(const shared_ptr<Shape>& cm1,const shared_ptr<Shape>& cm2,const State& state1,const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& interaction){
+
+ //get polyhedras
+ const Se3r& se31=state1.se3;
+ const Se3r& se32=state2.se3;
+ Polyhedra* A = static_cast<Polyhedra*>(cm1.get());
+ Polyhedra* B = static_cast<Polyhedra*>(cm2.get());
+
+ bool isNew = !interaction->geom;
+
+ //move and rotate 1st the CGAL structure Polyhedron
+ Matrix3r rot_mat = (se31.orientation).toRotationMatrix();
+ Vector3r trans_vec = se31.position;
+ Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PA = A->GetPolyhedron();
+ std::transform( PA.points_begin(), PA.points_end(), PA.points_begin(), t_rot_trans);
+
+
+ //move and rotate 2nd the CGAL structure Polyhedron
+ rot_mat = (se32.orientation).toRotationMatrix();
+ trans_vec = se32.position;
+ t_rot_trans = Transformation(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PB = B->GetPolyhedron();
+ std::transform( PB.points_begin(), PB.points_end(), PB.points_begin(), t_rot_trans);
+
+ shared_ptr<PolyhedraGeom> bang;
+ if (isNew) {
+ // new interaction
+ bang=shared_ptr<PolyhedraGeom>(new PolyhedraGeom());
+ bang->sep_plane.assign(3,0);
+ bang->contactPoint = Vector3r(0,0,0);
+ bang->isShearNew = true;
+ interaction->geom = bang;
+ }else{
+ // use data from old interaction
+ bang=YADE_PTR_CAST<PolyhedraGeom>(interaction->geom);
+ bang->isShearNew = bang->equivalentPenetrationDepth<=0;
+ }
+
+
+ //find intersection Polyhedra
+ Polyhedron Int;
+ Int = Polyhedron_Polyhedron_intersection(PA,PB,ToCGALPoint(bang->contactPoint),ToCGALPoint(se31.position),ToCGALPoint(se32.position), bang->sep_plane);
+
+ //volume and centroid of intersection
+ double volume;
+ Vector3r centroid;
+ P_volume_centroid(Int, &volume, ¢roid);
+ if(isnan(volume) || volume<=1E-25 || volume > min(A->GetVolume(),B->GetVolume())) {bang->equivalentPenetrationDepth=0; return true;}
+
+ //find normal direction
+ Vector3r normal = FindNormal(Int, PA, PB);
+ if((se32.position-centroid).dot(normal)<0) normal*=-1;
+
+ //calculate area of projection of Intersection into the normal plane
+ //double area = CalculateProjectionArea(Int, ToCGALVector(normal));
+ //if(isnan(area) || area<=1E-20) {bang->equivalentPenetrationDepth=0; return true;}
+ double area = volume/1E-8;
+
+ // store calculated stuff in bang; some is redundant
+ bang->equivalentCrossSection=area;
+ bang->contactPoint=centroid;
+ bang->penetrationVolume=volume;
+ bang->equivalentPenetrationDepth=volume/area;
+ bang->precompute(state1,state2,scene,interaction,normal,bang->isShearNew,shift2);
+ bang->normal=normal;
+
+ return true;
+}
+
+//**********************************************************************************
+/*! Create Polyhedra (collision geometry) from colliding Polyhedron and Wall. */
+
+bool Ig2_Wall_Polyhedra_PolyhedraGeom::go(const shared_ptr<Shape>& cm1,const shared_ptr<Shape>& cm2,const State& state1,const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& interaction){
+
+ const int& PA(cm1->cast<Wall>().axis);
+ const int& sense(cm1->cast<Wall>().sense);
+ const Se3r& se31=state1.se3; const Se3r& se32=state2.se3;
+ Polyhedra* B = static_cast<Polyhedra*>(cm2.get());
+
+ bool isNew = !interaction->geom;
+
+ //move and rotate also the CGAL structure Polyhedron
+ Matrix3r rot_mat = (se32.orientation).toRotationMatrix();
+ Vector3r trans_vec = se32.position;
+ Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PB = B->GetPolyhedron();
+ std::transform( PB.points_begin(), PB.points_end(), PB.points_begin(), t_rot_trans);
+ std::transform( PB.facets_begin(), PB.facets_end(), PB.planes_begin(),Plane_equation());
+
+ //move wall
+ Vector3r normal = Vector3r(0,0,0);
+ normal[PA] = sense;
+ CGALvector CGALnormal = CGALvector(normal[0],normal[1],normal[2]);
+ Plane A = Plane(CGALpoint(se31.position[0],se31.position[1],se31.position[2]),CGALvector(normal[0],normal[1],normal[2]));
+
+ shared_ptr<PolyhedraGeom> bang;
+ if (isNew) {
+ // new interaction
+ bang=shared_ptr<PolyhedraGeom>(new PolyhedraGeom());
+ bang->contactPoint = Vector3r(0,0,0);
+ bang->isShearNew = true;
+ interaction->geom = bang;
+ }else{
+ // use data from old interaction
+ bang=YADE_PTR_CAST<PolyhedraGeom>(interaction->geom);
+ bang->isShearNew = bang->equivalentPenetrationDepth<=0;
+ }
+
+ //find intersection Polyhedra
+ Polyhedron Int;
+ Int = Polyhedron_Plane_intersection(PB,A,ToCGALPoint(se32.position),ToCGALPoint(bang->contactPoint));
+
+ //volume and centroid of intersection
+ double volume;
+ Vector3r centroid;
+ P_volume_centroid(Int, &volume, ¢roid);
+ if(isnan(volume) || volume<=1E-25 || volume > B->GetVolume()) {bang->equivalentPenetrationDepth=0; return true;}
+
+ //calculate area of projection of Intersection into the normal plane
+ double area = volume/1E-8;
+ //double area = CalculateProjectionArea(Int, CGALnormal);
+ //if(isnan(area) || area<=1E-20) {bang->equivalentPenetrationDepth=0; return true;}
+
+ // store calculated stuff in bang; some is redundant
+ bang->equivalentCrossSection=area;
+ bang->contactPoint=centroid;
+ bang->penetrationVolume=volume;
+ bang->equivalentPenetrationDepth=volume/area;
+ bang->precompute(state1,state2,scene,interaction,normal,isNew,shift2);
+ bang->normal=FromCGALVector(CGALnormal);
+
+ return true;
+}
+
+//**********************************************************************************
+/*! Create Polyhedra (collision geometry) from colliding Polyhedron and Facet. */
+
+bool Ig2_Facet_Polyhedra_PolyhedraGeom::go(const shared_ptr<Shape>& cm1,const shared_ptr<Shape>& cm2,const State& state1,const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& interaction){
+
+
+ const Se3r& se31=state1.se3;
+ const Se3r& se32=state2.se3;
+ Facet* A = static_cast<Facet*>(cm1.get());
+ Polyhedra* B = static_cast<Polyhedra*>(cm2.get());
+
+ bool isNew = !interaction->geom;
+
+ //move and rotate 1st the CGAL structure Polyhedron
+ Matrix3r rot_mat = (se32.orientation).toRotationMatrix();
+ Vector3r trans_vec = se32.position;
+ Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PB = B->GetPolyhedron();
+ std::transform( PB.points_begin(), PB.points_end(), PB.points_begin(), t_rot_trans);
+
+ //move and rotate facet
+ vector<CGALpoint> v;
+ v.resize(6);
+ for (int i=0; i<3; i++) v[i] = ToCGALPoint(se31.orientation*A->vertices[i] + se31.position); // vertices in global coordinates
+
+ //determine
+ CGALpoint f_center((v[0].x()+v[1].x()+v[2].x())/3.,(v[0].y()+v[1].y()+v[2].y())/3.,(v[0].z()+v[1].z()+v[2].z())/3.);
+ CGALvector f_normal = CGAL::cross_product((v[1]-v[0]),(v[2]-v[0]));
+
+ //chage normal + change order of vertices to get outwarding facet normal for initial tetrahedron
+ if ((ToCGALPoint(se32.position)-f_center)*(f_normal) < 0) {
+ f_normal = (-1)*f_normal;
+ CGALpoint help;
+ help = v[0];
+ v[0]=v[1];
+ v[1]=help;
+ }
+
+ double f_area = sqrt(f_normal.squared_length());
+ for (int i=3; i<6; i++) v[i] = v[i-3]-f_normal/f_area*0.05*sqrt(f_area); // vertices in global coordinates
+
+ Polyhedron PA;
+ //use convex hull
+ //CGAL::convex_hull_3(v.begin(), v.end(), PA);
+
+ //construct polyhedron directly
+ Polyhedron::Halfedge_iterator hei = PA.make_tetrahedron(v[4],v[1],v[0],v[2]);
+ hei = PA.split_vertex(hei, hei->next()->opposite());
+ hei->vertex()->point() = v[3];
+ hei = PA.split_facet(hei->next()->next()->next(),hei->next());
+ hei = PA.split_vertex(hei, hei->next_on_vertex()->next_on_vertex());
+ hei->vertex()->point() = v[5];
+
+ shared_ptr<PolyhedraGeom> bang;
+ if (isNew) {
+ // new interaction
+ bang=shared_ptr<PolyhedraGeom>(new PolyhedraGeom());
+ bang->sep_plane.assign(3,0);
+ bang->contactPoint = Vector3r(0,0,0);
+ bang->isShearNew = true;
+ interaction->geom = bang;
+ }else{
+ // use data from old interaction
+ bang=YADE_PTR_CAST<PolyhedraGeom>(interaction->geom);
+ bang->isShearNew = bang->equivalentPenetrationDepth<=0;
+ }
+
+ //find intersection Polyhedra
+ Polyhedron Int;
+ Int = Polyhedron_Polyhedron_intersection(PA,PB,ToCGALPoint(bang->contactPoint),ToCGALPoint(se31.position),ToCGALPoint(se32.position), bang->sep_plane);
+
+ //volume and centroid of intersection
+ double volume;
+ Vector3r centroid;
+ P_volume_centroid(Int, &volume, ¢roid);
+ if(isnan(volume) || volume<=1E-25 || volume > B->GetVolume()) {bang->equivalentPenetrationDepth=0; return true;}
+
+ //find normal direction
+ Vector3r normal = FindNormal(Int, PA, PB);
+ if((se32.position-centroid).dot(normal)<0) normal*=-1;
+
+ //calculate area of projection of Intersection into the normal plane
+ double area = volume/1E-8;
+ //double area = CalculateProjectionArea(Int, ToCGALVector(normal));
+ //if(isnan(area) || area<=1E-20) {bang->equivalentPenetrationDepth=0; return true;}
+
+ // store calculated stuff in bang; some is redundant
+ bang->equivalentCrossSection=area;
+ bang->contactPoint=centroid;
+ bang->penetrationVolume=volume;
+ bang->equivalentPenetrationDepth=volume/area;
+ bang->precompute(state1,state2,scene,interaction,normal,bang->isShearNew,shift2);
+ bang->normal=normal;
+
+ return true;
+}
+
+//**********************************************************************************
+/*! Create Polyhedra (collision geometry) from colliding Polyhedron and Wall. */
+
+/*
+bool Ig2_Sphere_Polyhedra_PolyhedraGeom::go(const shared_ptr<Shape>& cm1,const shared_ptr<Shape>& cm2,const State& state1,const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& interaction){
+
+ const Se3r& se31=state1.se3;
+ const Se3r& se32=state2.se3;
+ Polyhedra* B = static_cast<Polyhedra*>(cm2.get());
+ const Real& r=cm1->cast<Sphere>().radius;
+
+ //cout << "Sphere x Polyhedra" << endl;
+
+ bool isNew = !interaction->geom;
+
+ //move and rotate 1st the CGAL structure Polyhedron
+ Matrix3r rot_mat = (se32.orientation).toRotationMatrix();
+ Vector3r trans_vec = se32.position;
+ Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PB = B->GetPolyhedron();
+ std::transform( PB.points_begin(), PB.points_end(), PB.points_begin(), t_rot_trans);
+ std::transform( PB.facets_begin(), PB.facets_end(), PB.planes_begin(),Plane_equation());
+
+ shared_ptr<PolyhedraGeom> bang;
+ if (isNew) {
+ // new interaction
+ bang=shared_ptr<PolyhedraGeom>(new PolyhedraGeom());
+ bang->isShearNew = true;
+ interaction->geom = bang;
+ }else{
+ // use data from old interaction
+ bang=YADE_PTR_CAST<PolyhedraGeom>(interaction->geom);
+ }
+
+ //volume and centroid of intersection
+ double volume, area;
+ CGALvector normalCGAL;
+ CGALpoint centroidCGAL=ToCGALPoint(se32.position);
+ Sphere_Polyhedron_intersection(PB, r, ToCGALPoint(se31.position), centroidCGAL, volume, normalCGAL, area);
+ if(isnan(volume) || volume<=1E-25 || volume > B->GetVolume()) {bang->equivalentPenetrationDepth=0; return true;}
+ Vector3r centroid = FromCGALPoint(centroidCGAL);
+ Vector3r normal = FromCGALVector(normalCGAL);
+
+
+
+ // store calculated stuff in bang; some is redundant
+ bang->equivalentCrossSection=area;
+ bang->contactPoint=centroid;
+ bang->penetrationVolume=volume;
+ bang->equivalentPenetrationDepth=volume/area;
+ bang->precompute(state1,state2,scene,interaction,normal,bang->isShearNew,shift2);
+ bang->normal=normal;
+
+ return true;
+}
+*/
+
+#endif // YADE_CGAL
=== added file 'pkg/dem/Polyhedra_Ig2.hpp'
--- pkg/dem/Polyhedra_Ig2.hpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/Polyhedra_Ig2.hpp 2013-10-16 15:24:49 +0000
@@ -0,0 +1,71 @@
+// © 2013 Jan Elias, http://www.fce.vutbr.cz/STM/elias.j/, elias.j@xxxxxxxxxxxx
+// https://www.vutbr.cz/www_base/gigadisk.php?i=95194aa9a
+
+#ifdef YADE_CGAL
+
+#include"Polyhedra.hpp"
+#include<yade/pkg/common/Sphere.hpp>
+
+//***************************************************************************
+/*! Create Polyhedra (collision geometry) from colliding Polyhedras. */
+class Ig2_Polyhedra_Polyhedra_PolyhedraGeom: public IGeomFunctor
+{
+ public:
+ virtual ~Ig2_Polyhedra_Polyhedra_PolyhedraGeom(){};
+ virtual bool go(const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c);
+ FUNCTOR2D(Polyhedra,Polyhedra);
+ DEFINE_FUNCTOR_ORDER_2D(Polyhedra,Polyhedra);
+ YADE_CLASS_BASE_DOC(Ig2_Polyhedra_Polyhedra_PolyhedraGeom,IGeomFunctor,"Create/update geometry of collision between 2 Polyhedras");
+ DECLARE_LOGGER;
+ private:
+};
+REGISTER_SERIALIZABLE(Ig2_Polyhedra_Polyhedra_PolyhedraGeom);
+
+//***************************************************************************
+/*! Create Polyhedra (collision geometry) from colliding Wall & Polyhedra. */
+class Ig2_Wall_Polyhedra_PolyhedraGeom: public IGeomFunctor
+{
+ public:
+ virtual ~Ig2_Wall_Polyhedra_PolyhedraGeom(){};
+ virtual bool go(const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c);
+ FUNCTOR2D(Wall,Polyhedra);
+ DEFINE_FUNCTOR_ORDER_2D(Wall,Polyhedra);
+ YADE_CLASS_BASE_DOC(Ig2_Wall_Polyhedra_PolyhedraGeom,IGeomFunctor,"Create/update geometry of collision between Wall and Polyhedra");
+ DECLARE_LOGGER;
+ private:
+};
+REGISTER_SERIALIZABLE(Ig2_Wall_Polyhedra_PolyhedraGeom);
+
+//***************************************************************************
+/*! Create Polyhedra (collision geometry) from colliding Facet & Polyhedra. */
+class Ig2_Facet_Polyhedra_PolyhedraGeom: public IGeomFunctor
+{
+ public:
+ virtual ~Ig2_Facet_Polyhedra_PolyhedraGeom(){};
+ virtual bool go(const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c);
+ FUNCTOR2D(Facet,Polyhedra);
+ DEFINE_FUNCTOR_ORDER_2D(Facet,Polyhedra);
+ YADE_CLASS_BASE_DOC(Ig2_Facet_Polyhedra_PolyhedraGeom,IGeomFunctor,"Create/update geometry of collision between Facet and Polyhedra");
+ DECLARE_LOGGER;
+ private:
+};
+REGISTER_SERIALIZABLE(Ig2_Facet_Polyhedra_PolyhedraGeom);
+
+//***************************************************************************
+/*! Create Polyhedra (collision geometry) from colliding Sphere & Polyhedra. */
+/*
+class Ig2_Sphere_Polyhedra_PolyhedraGeom: public IGeomFunctor
+{
+ public:
+ virtual ~Ig2_Sphere_Polyhedra_PolyhedraGeom(){};
+ virtual bool go(const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c);
+ FUNCTOR2D(Sphere,Polyhedra);
+ DEFINE_FUNCTOR_ORDER_2D(Sphere,Polyhedra);
+ YADE_CLASS_BASE_DOC(Ig2_Sphere_Polyhedra_PolyhedraGeom,IGeomFunctor,"Create/update geometry of collision between Sphere and Polyhedra");
+ DECLARE_LOGGER;
+ private:
+};
+REGISTER_SERIALIZABLE(Ig2_Sphere_Polyhedra_PolyhedraGeom);
+*/
+
+#endif // YADE_CGAL
=== added file 'pkg/dem/Polyhedra_splitter.cpp'
--- pkg/dem/Polyhedra_splitter.cpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/Polyhedra_splitter.cpp 2013-10-16 15:24:49 +0000
@@ -0,0 +1,109 @@
+// © 2013 Jan Elias, http://www.fce.vutbr.cz/STM/elias.j/, elias.j@xxxxxxxxxxxx
+// https://www.vutbr.cz/www_base/gigadisk.php?i=95194aa9a
+
+#ifdef YADE_CGAL
+
+#include<yade/pkg/dem/Polyhedra_splitter.hpp>
+
+
+YADE_PLUGIN((PolyhedraSplitter));
+CREATE_LOGGER(PolyhedraSplitter);
+
+//*********************************************************************************
+/* Evaluate tensorial stress estimation in polyhedras */
+
+void getStressForEachBody(vector<Matrix3r>& bStresses){
+ const shared_ptr<Scene>& scene=Omega::instance().getScene();
+ bStresses.resize(scene->bodies->size());
+ for (size_t k=0;k<scene->bodies->size();k++) bStresses[k]=Matrix3r::Zero();
+ FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
+ if(!I->isReal()) continue;
+ PolyhedraGeom* geom=YADE_CAST<PolyhedraGeom*>(I->geom.get());
+ PolyhedraPhys* phys=YADE_CAST<PolyhedraPhys*>(I->phys.get());
+ if(!geom || !phys) continue;
+ Vector3r f=phys->normalForce+phys->shearForce;
+ //Sum f_i*l_j for each contact of each particle
+ bStresses[I->getId1()] -=f*((geom->contactPoint-Body::byId(I->getId1(),scene)->state->pos).transpose());
+ bStresses[I->getId2()] +=f*((geom->contactPoint-Body::byId(I->getId2(),scene)->state->pos).transpose());
+
+
+ }
+}
+
+//*********************************************************************************
+/* Size dependent strength */
+
+double PolyhedraSplitter::getStrength(double volume, double strength){
+ //equvalent radius
+ double r_eq = pow(volume*3./4./Mathr::PI,1./3.);
+ //r should be in milimeters
+ return strength/(r_eq/1000.);
+}
+
+//*********************************************************************************
+/* Symmetrization of stress tensor */
+
+void PolyhedraSplitter::Symmetrize(Matrix3r & bStress){
+ bStress(0,1) = (bStress(0,1) + bStress(1,0))/2.;
+ bStress(0,2) = (bStress(0,2) + bStress(2,0))/2.;
+ bStress(1,2) = (bStress(1,2) + bStress(2,1))/2.;
+ bStress(1,0) = bStress(0,1);
+ bStress(2,0) = bStress(0,2);
+ bStress(2,1) = bStress(1,2);
+}
+
+//*********************************************************************************
+/* Split if stress exceed strength */
+
+void PolyhedraSplitter::action()
+{
+ const shared_ptr<Scene> _rb=shared_ptr<Scene>();
+ shared_ptr<Scene> rb=(_rb?_rb:Omega::instance().getScene());
+
+ vector<shared_ptr<Body> > bodies;
+ vector<Vector3r > directions;
+ vector<double > sigmas;
+
+
+
+ vector<Matrix3r> bStresses;
+ getStressForEachBody(bStresses);
+
+ int i = -1;
+ FOREACH(const shared_ptr<Body>& b, *rb->bodies){
+ i++;
+ if(!b || !b->material || !b->shape) continue;
+ shared_ptr<Polyhedra> p=dynamic_pointer_cast<Polyhedra>(b->shape);
+ shared_ptr<PolyhedraMat> m=dynamic_pointer_cast<PolyhedraMat>(b->material);
+
+ if(p && m->IsSplitable){
+ //not real strees, to get real one, it has to be divided by body volume
+ Matrix3r stress = bStresses[i];
+
+ //get eigenstresses
+ Symmetrize(stress);
+ Matrix3r I_vect(Matrix3r::Zero()), I_valu(Matrix3r::Zero());
+ matrixEigenDecomposition(stress,I_vect,I_valu);
+ int min_i = 0;
+ if (I_valu(min_i,min_i) > I_valu(1,1)) min_i = 1;
+ if (I_valu(min_i,min_i) > I_valu(2,2)) min_i = 2;
+ int max_i = 0;
+ if (I_valu(max_i,max_i) < I_valu(1,1)) max_i = 1;
+ if (I_valu(max_i,max_i) < I_valu(2,2)) max_i = 2;
+
+ //division of stress by volume
+ double comp_stress = I_valu(min_i,min_i)/p->GetVolume();
+ double tens_stress = I_valu(max_i,max_i)/p->GetVolume();
+ Vector3r dirC = I_vect.col(max_i);
+ Vector3r dirT = I_vect.col(min_i);
+ Vector3r dir = dirC.normalized() + dirT.normalized();;
+ double sigma_t = -comp_stress/2.+ tens_stress;
+ if (sigma_t > getStrength(p->GetVolume(),m->GetStrength())) {bodies.push_back(b); directions.push_back(dir); sigmas.push_back(sigma_t);};
+ }
+ }
+ for(int i=0; i<int(bodies.size()); i++){
+ SplitPolyhedra(bodies[i], directions[i]);
+ }
+}
+
+#endif // YADE_CGAL
=== added file 'pkg/dem/Polyhedra_splitter.hpp'
--- pkg/dem/Polyhedra_splitter.hpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/Polyhedra_splitter.hpp 2013-10-16 15:24:49 +0000
@@ -0,0 +1,33 @@
+// © 2013 Jan Elias, http://www.fce.vutbr.cz/STM/elias.j/, elias.j@xxxxxxxxxxxx
+// https://www.vutbr.cz/www_base/gigadisk.php?i=95194aa9a
+
+
+#pragma once
+
+#ifdef YADE_CGAL
+
+#include<yade/pkg/common/PeriodicEngines.hpp>
+#include<yade/core/Interaction.hpp>
+
+#include"Polyhedra.hpp"
+
+//*********************************************************************************
+/* Polyhedra Splitter */
+class PolyhedraSplitter : public PeriodicEngine{
+
+ public:
+ virtual void action();
+ double getStrength(double volume, double strength);
+ void Symmetrize(Matrix3r & bStress);
+
+ YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(PolyhedraSplitter,PeriodicEngine,"Engine that splits polyhedras.",
+ ,
+ /*ctor*/
+ ,/*py*/
+ );
+ DECLARE_LOGGER;
+};
+
+REGISTER_SERIALIZABLE(PolyhedraSplitter);
+
+#endif // YADE_CGAL
=== added file 'pkg/dem/Polyhedra_support.cpp'
--- pkg/dem/Polyhedra_support.cpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/Polyhedra_support.cpp 2013-10-16 15:24:49 +0000
@@ -0,0 +1,817 @@
+// © 2013 Jan Elias, http://www.fce.vutbr.cz/STM/elias.j/, elias.j@xxxxxxxxxxxx
+// https://www.vutbr.cz/www_base/gigadisk.php?i=95194aa9a
+
+#ifdef YADE_CGAL
+
+#include"Polyhedra.hpp"
+
+#define _USE_MATH_DEFINES
+
+
+//EMPRIRICAL CONSTANTS - ADJUST IF SEGMENTATION FAULT OCCUR, IT IS A PROBLEM OF CGAL. THESE ARE USED TO CHECK CGAL IMPUTS
+//DISTANCE_LIMIT controls numerical issues in calculating intersection. It should be small enough to neglect only extremely small overlaps, but large enough to prevent errors during computation of convex hull
+#define DISTANCE_LIMIT 1E-11 //-11
+//MERGE_PLANES_LIMIT - if two facets of two intersecting polyhedron differ less, then they are treated ose one only
+#define MERGE_PLANES_LIMIT 1E-18 //18
+//SIMPLIFY_LIMIT - if two facets of one polyhedron differ less, then they are joint into one facet
+#define SIMPLIFY_LIMIT 1E-20 //19
+//FIND_NORMAL_LIMIT - to determine which facet of intersection belongs to which polyhedron
+#define FIND_NORMAL_LIMIT 1E-40
+
+
+//**********************************************************************************
+//return volume and centroid of polyhedron
+
+bool P_volume_centroid(Polyhedron P, double * volume, Vector3r * centroid){
+
+
+ Vector3r basepoint = FromCGALPoint(P.vertices_begin()->point());
+ Vector3r A,B,C,D;
+ (*volume) = 0;
+ double vtet;
+ (*centroid) = Vector3r(0.,0.,0.);
+
+ //compute centroid and volume
+ for (Polyhedron::Facet_iterator fIter = P.facets_begin(); fIter != P.facets_end(); fIter++){
+ Polyhedron::Halfedge_around_facet_circulator hfc0;
+ hfc0 = fIter->facet_begin();
+ int n = fIter->facet_degree();
+ A = FromCGALPoint(hfc0->vertex()->point());
+ C = FromCGALPoint(hfc0->next()->vertex()->point());
+ for (int i=2; i<n; i++){
+ ++hfc0;
+ B = C;
+ C = FromCGALPoint(hfc0->next()->vertex()->point());
+ vtet = std::abs((basepoint-C).dot((A-C).cross(B-C)))/6.;
+ *volume += vtet;
+ *centroid = *centroid + (basepoint+A+B+C) / 4. * vtet;
+ }
+ }
+ *centroid = *centroid/(*volume);
+ return true;
+}
+
+//**********************************************************************************
+//STOLEN FROM TETRA body of Vaclav Smilauer
+
+/*! Calculates tetrahedron inertia relative to the origin (0,0,0), with unit density (scales linearly).
+
+See article F. Tonon, "Explicit Exact Formulas for the 3-D Tetrahedron Inertia Tensor in Terms of its Vertex Coordinates", http://www.scipub.org/fulltext/jms2/jms2118-11.pdf
+*/
+
+//Centroid MUST be [0,0,0]
+Matrix3r TetraInertiaTensor(Vector3r av,Vector3r bv,Vector3r cv,Vector3r dv){
+ #define x1 av[0]
+ #define y1 av[1]
+ #define z1 av[2]
+ #define x2 bv[0]
+ #define y2 bv[1]
+ #define z2 bv[2]
+ #define x3 cv[0]
+ #define y3 cv[1]
+ #define z3 cv[2]
+ #define x4 dv[0]
+ #define y4 dv[1]
+ #define z4 dv[2]
+
+ // Jacobian of transformation to the reference 4hedron
+ double detJ=(x2-x1)*(y3-y1)*(z4-z1)+(x3-x1)*(y4-y1)*(z2-z1)+(x4-x1)*(y2-y1)*(z3-z1)
+ -(x2-x1)*(y4-y1)*(z3-z1)-(x3-x1)*(y2-y1)*(z4-z1)-(x4-x1)*(y3-y1)*(z2-z1);
+ detJ=fabs(detJ);
+ double a=detJ*(y1*y1+y1*y2+y2*y2+y1*y3+y2*y3+
+ y3*y3+y1*y4+y2*y4+y3*y4+y4*y4+z1*z1+z1*z2+
+ z2*z2+z1*z3+z2*z3+z3*z3+z1*z4+z2*z4+z3*z4+z4*z4)/60.;
+ double b=detJ*(x1*x1+x1*x2+x2*x2+x1*x3+x2*x3+x3*x3+
+ x1*x4+x2*x4+x3*x4+x4*x4+z1*z1+z1*z2+z2*z2+z1*z3+
+ z2*z3+z3*z3+z1*z4+z2*z4+z3*z4+z4*z4)/60.;
+ double c=detJ*(x1*x1+x1*x2+x2*x2+x1*x3+x2*x3+x3*x3+x1*x4+
+ x2*x4+x3*x4+x4*x4+y1*y1+y1*y2+y2*y2+y1*y3+
+ y2*y3+y3*y3+y1*y4+y2*y4+y3*y4+y4*y4)/60.;
+ // a' in the article etc.
+ double a__=detJ*(2*y1*z1+y2*z1+y3*z1+y4*z1+y1*z2+
+ 2*y2*z2+y3*z2+y4*z2+y1*z3+y2*z3+2*y3*z3+
+ y4*z3+y1*z4+y2*z4+y3*z4+2*y4*z4)/120.;
+ double b__=detJ*(2*x1*z1+x2*z1+x3*z1+x4*z1+x1*z2+
+ 2*x2*z2+x3*z2+x4*z2+x1*z3+x2*z3+2*x3*z3+
+ x4*z3+x1*z4+x2*z4+x3*z4+2*x4*z4)/120.;
+ double c__=detJ*(2*x1*y1+x2*y1+x3*y1+x4*y1+x1*y2+
+ 2*x2*y2+x3*y2+x4*y2+x1*y3+x2*y3+2*x3*y3+
+ x4*y3+x1*y4+x2*y4+x3*y4+2*x4*y4)/120.;
+
+ Matrix3r ret; ret<<
+ a , -c__, -b__,
+ -c__, b , -a__,
+ -b__, -a__, c ;
+ return ret;
+
+ #undef x1
+ #undef y1
+ #undef z1
+ #undef x2
+ #undef y2
+ #undef z2
+ #undef x3
+ #undef y3
+ #undef z3
+ #undef x4
+ #undef y4
+ #undef z4
+}
+
+//**********************************************************************************
+//distace of point from a plane (squared) with sign
+double Oriented_squared_distance(Plane P, CGALpoint x){
+ double h = P.a()*x.x()+P.b()*x.y()+P.c()*x.z()+P.d();
+ return ((h>0.)-(h<0.))*pow(h,2)/(CGALvector(P.a(),P.b(),P.c())).squared_length();
+}
+
+//**********************************************************************************
+// test if point is inside polyhedra in strong sence, i.e. boundary location is not enough
+bool Is_inside_Polyhedron(Polyhedron P, CGALpoint inside){
+ Polyhedron::Plane_iterator pi;
+ for(pi=P.planes_begin(); pi!=P.planes_end(); ++pi){
+ if( ! pi->has_on_negative_side(inside)) return false;
+ }
+ return true;
+}
+
+//**********************************************************************************
+// test if point is inside polyhedra not closer than lim to its boundary
+bool Is_inside_Polyhedron(Polyhedron P, CGALpoint inside, double lim){
+ Polyhedron::Plane_iterator pi;
+ lim = pow(lim,2);
+ for(pi=P.planes_begin(); pi!=P.planes_end(); ++pi){
+ if(Oriented_squared_distance(*pi, inside)>-lim) return false;
+ }
+ return true;
+}
+
+//**********************************************************************************
+//test if two polyhedron intersect
+bool do_intersect(Polyhedron A, Polyhedron B){
+ std::vector<int> sep_plane;
+ sep_plane.assign(3,0);
+ return do_intersect(A, B, sep_plane);
+}
+
+//**********************************************************************************
+//test if two polyhedron intersect based on previous data
+bool do_intersect(Polyhedron A, Polyhedron B, std::vector<int> &sep_plane){
+
+
+ bool found;
+ //check previous separation plane
+ switch (sep_plane[0]){
+ case 1: //separation plane was previously determined as sep_plane[2]-th plane of A polyhedron
+ {
+ if(unlikely((unsigned) sep_plane[2]>=A.size_of_facets())) break;
+ Polyhedron::Facet_iterator fIter = A.facets_begin();
+ for (int i=0; i<sep_plane[2]; i++) ++fIter;
+ found = true;
+ for (Polyhedron::Vertex_iterator vIter = B.vertices_begin(); vIter != B.vertices_end(); ++vIter){
+ if (! fIter->plane().has_on_positive_side(vIter->point())) {found = false; break;};
+ }
+ if(found) return false;
+ }
+ break;
+ case 2: //separation plane was previously determined as sep_plane[2]-th plane of B polyhedron
+ {
+ if(unlikely((unsigned) sep_plane[2]>=B.size_of_facets())) break;
+ Polyhedron::Facet_iterator fIter = B.facets_begin();
+ for (int i=0; i<sep_plane[2]; i++) ++fIter;
+ found = true;
+ for (Polyhedron::Vertex_iterator vIter = A.vertices_begin(); vIter != A.vertices_end(); ++vIter){
+ if (! fIter->plane().has_on_positive_side(vIter->point())) {found = false; break;};
+ }
+ if(found) return false;
+ }
+ break;
+ case 3: //separation plane was previously given by sep_plane[1]-th and sep_plane[2]-th edge of A & B polyhedrons
+ {
+ if(unlikely((unsigned) sep_plane[1]>=A.size_of_halfedges()/2)) break;
+ if(unlikely((unsigned) sep_plane[2]>=B.size_of_halfedges()/2)) break;
+ Polyhedron::Edge_iterator eIter1 = A.edges_begin();
+ Polyhedron::Edge_iterator eIter2 = B.edges_begin();
+ for (int i=0; i<sep_plane[1]; i++) ++eIter1;
+ for (int i=0; i<sep_plane[2]; i++) ++eIter2;
+ found = true;
+ Plane X(eIter1->vertex()->point(),CGAL::cross_product((eIter1->vertex()->point() - eIter1->opposite()->vertex()->point()),(eIter2->vertex()->point() - eIter2->opposite()->vertex()->point())));
+ if (!X.has_on_positive_side(B.vertices_begin()->point())) X = X.opposite();
+
+ double lim = pow(DISTANCE_LIMIT,2);
+ for (Polyhedron::Vertex_iterator vIter = A.vertices_begin(); vIter != A.vertices_end(); ++vIter){
+ if (Oriented_squared_distance(X, vIter->point())>lim) {found = false; break;};
+ }
+ for (Polyhedron::Vertex_iterator vIter = B.vertices_begin(); vIter != B.vertices_end(); ++vIter){
+ if (! X.has_on_positive_side(vIter->point())) {found = false; break;};
+ }
+ if(found) return false;
+ }
+ break;
+ }
+
+ //regular test with no previous information about separating plane
+ //test all planes from A
+ int i = 0;
+ for (Polyhedron::Facet_iterator fIter = A.facets_begin(); fIter != A.facets_end(); fIter++, i++){
+ found = true;
+ for (Polyhedron::Vertex_iterator vIter = B.vertices_begin(); vIter != B.vertices_end(); ++vIter){
+ if (! fIter->plane().has_on_positive_side(vIter->point())) {found = false; break;};
+ }
+ if(found) {sep_plane[0] = 1; sep_plane[1] = 1; sep_plane[2] = i; return false;}
+ }
+ //test all planes from B
+ i = 0;
+ for (Polyhedron::Facet_iterator fIter = B.facets_begin(); fIter != B.facets_end(); fIter++, i++){
+ found = true;
+ for (Polyhedron::Vertex_iterator vIter = A.vertices_begin(); vIter != A.vertices_end(); ++vIter){
+ if (! fIter->plane().has_on_positive_side(vIter->point())) {found = false; break;};
+ }
+ if(found) {sep_plane[0] = 2; sep_plane[1] = 2; sep_plane[2] = i; return false;}
+ }
+ //test all pairs of edges from A & B
+ Plane X;
+ CGALvector vA;
+ double lim = pow(DISTANCE_LIMIT,2);
+ i = 0;
+ for (Polyhedron::Edge_iterator eIter1 = A.edges_begin(); eIter1 != A.edges_end(); ++eIter1, i++){
+ vA = eIter1->vertex()->point() - eIter1->opposite()->vertex()->point();
+ int j = 0;
+ for (Polyhedron::Edge_iterator eIter2 = B.edges_begin(); eIter2 != B.edges_end(); ++eIter2, j++){
+ found = true;
+ X = Plane(eIter1->vertex()->point(),CGAL::cross_product(vA,(eIter2->vertex()->point() - eIter2->opposite()->vertex()->point()) ));
+ if (!X.has_on_positive_side(B.vertices_begin()->point())) X = X.opposite();
+ for (Polyhedron::Vertex_iterator vIter = A.vertices_begin(); vIter != A.vertices_end(); ++vIter){
+ if (Oriented_squared_distance(X, vIter->point())>lim) {found = false; break;};
+ }
+ for (Polyhedron::Vertex_iterator vIter = B.vertices_begin(); vIter != B.vertices_end(); ++vIter){
+ if (! X.has_on_positive_side(vIter->point())) {found = false; break;};
+ }
+ if(found) {sep_plane[0] = 3; sep_plane[1] = i; sep_plane[2] = j; return false;}
+ }
+ }
+
+ sep_plane[0] = 0;
+ return true;
+}
+
+//**********************************************************************************
+//norm of difference between two planes
+double PlaneDifference(const Plane &a, const Plane &b){
+ double la = sqrt(pow(a.a(),2) + pow(a.b(),2) + pow(a.c(),2) + pow(a.d(),2));
+ double lb = sqrt(pow(b.a(),2) + pow(b.b(),2) + pow(b.c(),2) + pow(b.d(),2));
+ return pow(a.a()/la-b.a()/lb,2) + pow(a.b()/la-b.b()/lb,2) + pow(a.c()/la-b.c()/lb,2) + pow(a.d()/la-b.d()/lb,2);
+
+ //in case we do not care of the orientation
+ //return min(pow(a.a()/la-b.a()/lb,2) + pow(a.b()/la-b.b()/lb,2) + pow(a.c()/la-b.c()/lb,2) + pow(a.d()/la-b.d()/lb,2),pow(a.a()/la+b.a()/lb,2) + pow(a.b()/la+b.b()/lb,2) + pow(a.c()/la+b.c()/lb,2) + pow(a.d()/la+b.d()/lb,2));
+}
+
+//**********************************************************************************
+//connect triagular facets if possible
+Polyhedron Simplify(Polyhedron P, double limit){
+ bool elimination = true;
+ while(elimination){
+ elimination = false;
+ for (Polyhedron::Edge_iterator hei = P.edges_begin(); hei!=P.edges_end(); ++hei){
+ if (PlaneDifference(hei->facet()->plane(),hei->opposite()->facet()->plane()) < limit){
+ if (hei->vertex()->vertex_degree() < 3) hei = P.erase_center_vertex(hei);
+ else if(hei->opposite()->vertex()->vertex_degree() < 3) hei = P.erase_center_vertex(hei->opposite());
+ else hei = P.join_facet(hei);
+ elimination = true;
+ break;
+ }
+ }
+ }
+ if (P.size_of_facets() < 4) P.clear();
+ return P;
+}
+
+//**********************************************************************************
+//list of facets + edges
+void PrintPolyhedron(Polyhedron P){
+ Vector3r A,B,C;
+ cout << "*** faces ***" << endl;
+ for (Polyhedron::Facet_iterator fIter = P.facets_begin(); fIter!=P.facets_end(); ++fIter){
+ Polyhedron::Halfedge_around_facet_circulator hfc0;
+ hfc0 = fIter->facet_begin();
+ int n = fIter->facet_degree();
+ A = FromCGALPoint(hfc0->vertex()->point());
+ C = FromCGALPoint(hfc0->next()->vertex()->point());
+ for (int i=2; i<n; i++){
+ ++hfc0;
+ B = C;
+ C = FromCGALPoint(hfc0->next()->vertex()->point());
+ cout << A << " "<< B << " "<< C << endl;
+ }
+ }
+ cout << "*** edges ***" << endl;
+ for (Polyhedron::Edge_iterator hei = P.edges_begin(); hei!=P.edges_end(); ++hei){
+ cout << hei->vertex()->point() << " " << hei->opposite()->vertex()->point() << endl;
+ }
+}
+
+//**********************************************************************************
+//list of facets
+void PrintPolyhedronFacets(Polyhedron P){
+ Vector3r A,B,C;
+ for (Polyhedron::Facet_iterator fIter = P.facets_begin(); fIter!=P.facets_end(); ++fIter){
+ cout << "***" << endl;
+ Polyhedron::Halfedge_around_facet_circulator hfc0;
+ hfc0 = fIter->facet_begin();
+ int n = fIter->facet_degree();
+ for (int i = 0; i<n; ++hfc0, i++){
+ cout << hfc0->vertex()->point() << endl;
+ }
+ }
+}
+
+//**********************************************************************************
+//calculate intersection of three planes (exceptional cases not checked) - not in use, can be deleted
+CGALpoint PlaneIntersection( Plane a, Plane b, Plane c){
+ Matrix3r A;
+ A<<
+ a.a(),a.b(),a.c(),
+ b.a(),b.b(),b.c(),
+ c.a(),c.b(),c.c();
+ Vector3r x = A.inverse()*Vector3r(-a.d(),-b.d(),-c.d());
+ return CGALpoint(x.x(),x.y(),x.z());
+}
+
+//**********************************************************************************
+//solve system of 3x3 by Cramers rule
+Vector3r SolveLinSys3x3(Matrix3r A, Vector3r y){
+ //only system 3x3 by Cramers rule
+ double det = A(0,0)*A(1,1)*A(2,2)+A(0,1)*A(1,2)*A(2,0)+A(0,2)*A(1,0)*A(2,1)-A(0,2)*A(1,1)*A(2,0)-A(0,1)*A(1,0)*A(2,2)-A(0,0)*A(1,2)*A(2,1);
+ if (det == 0) {cerr << "error in linear solver" << endl; return Vector3r(0,0,0);}
+ return Vector3r((y(0)*A(1,1)*A(2,2)+A(0,1)*A(1,2)*y(2)+A(0,2)*y(1)*A(2,1)-A(0,2)*A(1,1)*y(2)-A(0,1)*y(1)*A(2,2)-y(0)*A(1,2)*A(2,1))/det, (A(0,0)*y(1)*A(2,2)+y(0)*A(1,2)*A(2,0)+A(0,2)*A(1,0)*y(2)-A(0,2)*y(1)*A(2,0)-y(0)*A(1,0)*A(2,2)-A(0,0)*A(1,2)*y(2))/det, (A(0,0)*A(1,1)*y(2)+A(0,1)*y(1)*A(2,0)+y(0)*A(1,0)*A(2,1)-y(0)*A(1,1)*A(2,0)-A(0,1)*A(1,0)*y(2)-A(0,0)*y(1)*A(2,1))/det);
+}
+
+//**********************************************************************************
+/*return convex hull of points
+critical point, because CGAL often returnes segmentation fault. The planes must be sufficiently "different". This is, however, checked elswhere by DISTANCE_LIMIT variable.
+*/
+Polyhedron ConvexHull(vector<CGALpoint> &planes){
+ Polyhedron Int;
+ //cout << "C"; cout.flush();
+ if (planes.size()>3) CGAL::convex_hull_3(planes.begin(), planes.end(), Int);
+ //cout << "-C"<< endl; cout.flush();
+ return Int;
+}
+
+//**********************************************************************************
+//determination of normal direction of intersection
+
+Vector3r FindNormal(Polyhedron Int, Polyhedron PA, Polyhedron PB){
+
+ //determine which plane is from which polyhedra
+ Polyhedron::Plane_iterator pi, pj;
+ std::transform( Int.facets_begin(), Int.facets_end(), Int.planes_begin(),Plane_equation());
+ std::transform( PA.facets_begin(), PA.facets_end(), PA.planes_begin(),Plane_equation());
+ std::transform( PB.facets_begin(), PB.facets_end(), PB.planes_begin(),Plane_equation());
+ vector<bool> from_A(Int.size_of_facets());
+ vector<double> minsA(Int.size_of_facets());
+ vector<double> minsB(Int.size_of_facets());
+ int i=0;
+ double minA, minB, k;
+ for(pi = Int.planes_begin(); pi!=Int.planes_end(); ++pi,i++){
+ minA = 1.;
+ minB = 1.;
+ for(pj=PA.planes_begin(); pj!=PA.planes_end(); ++pj){
+ k = PlaneDifference(*pi, *pj);
+ if (k<minA) {
+ minA = k;
+ minsA[i] = minA;
+ if (minA<FIND_NORMAL_LIMIT) {from_A[i] = true; break;} //already satisfactory
+ }
+
+ }
+ if (minA<FIND_NORMAL_LIMIT) continue;
+ for(pj=PB.planes_begin(); pj!=PB.planes_end(); ++pj){
+ k = PlaneDifference(*pi, *pj);
+
+ if (k<minB){
+ minB = k;
+ minsB[i] = minB;
+ if (minB<FIND_NORMAL_LIMIT || minB<minA) break; //already satisfactory
+ }
+ }
+ from_A[i] = ((minA < minB) ? true : false);
+ }
+ //check that not all belongs to A and not all belongs to B
+ if (*std::min_element(from_A.begin(),from_A.end())==1){
+ int loc = std::min_element(minsB.begin(),minsB.end()) - minsB.begin();
+ from_A[loc] = false;
+ }else if (*std::max_element(from_A.begin(),from_A.end())==0){
+ int loc = std::min_element(minsA.begin(),minsA.end()) - minsA.begin();
+ from_A[loc] = true;
+ }
+
+ //find intersecting segments
+ vector<Segment> segments;
+ int a,b;
+
+ for (Polyhedron::Edge_iterator hei = Int.edges_begin(); hei!=Int.edges_end(); ++hei){
+ a = std::distance(Int.facets_begin(), hei->facet());
+ b = std::distance(Int.facets_begin(), hei->opposite()->facet());
+ if ((from_A[a] && !from_A[b]) || (from_A[b] && !from_A[a])) segments.push_back(Segment(hei->vertex()->point(),hei->opposite()->vertex()->point()));
+ }
+
+ //find normal direction
+ Plane fit;
+ linear_least_squares_fitting_3(segments.begin(),segments.end(),fit,CGAL::Dimension_tag<1>());
+ CGALvector CGALnormal=fit.orthogonal_vector();
+ CGALnormal = CGALnormal/sqrt(CGALnormal.squared_length());
+ // reverse direction if projection of the (contact_point-centroid_of_B) vector onto the normal is negative (i.e. the normal points more towards B)
+
+ return FromCGALVector(CGALnormal);
+}
+
+//**********************************************************************************
+//not used
+double CalculateProjectionArea(Polyhedron Int, CGALvector CGALnormal){
+ //calculate area of projection of Intersection into the normal plane
+ double area = 0.;
+ double abs_size;
+ Polyhedron::Halfedge_around_facet_circulator hfc0;
+ CGALvector normal2;
+ double norm2;
+ std::transform( Int.facets_begin(), Int.facets_end(), Int.planes_begin(),Plane_equation());
+ for(Polyhedron::Facet_iterator fi = Int.facets_begin(); fi!= Int.facets_end(); ++fi){
+ assert(fi->is_triangle());
+ hfc0 = fi->facet_begin();
+ normal2 = fi->plane().orthogonal_vector();
+ norm2 = normal2.squared_length();
+ if(norm2 < 1E-20) continue;
+ abs_size = 0.5*sqrt((cross_product(CGALvector(hfc0->vertex()->point(),hfc0->next()->vertex()->point()),CGALvector(hfc0->vertex()->point(),hfc0->next()->next()->vertex()->point()))).squared_length());
+ // factor 0.5 because this procedure returnes doubles projected area
+ if (abs_size>0) area += 0.5*abs_size*abs(CGALnormal*normal2/sqrt(norm2));
+ }
+ return area;
+}
+
+//**********************************************************************************
+//prepare data for CGAL convex hull
+vector<Plane> MergePlanes(vector<Plane> planes1, vector<Plane> planes2, double limit){
+ vector<Plane> P = planes1;
+ bool add;
+ for(vector<Plane>::iterator i = planes2.begin(); i!=planes2.end(); ++i){
+ add = true;
+ for(vector<Plane>::iterator j = planes1.begin(); j!=planes1.end(); ++j){
+ if (PlaneDifference(*i,*j) < limit){
+ add = false;
+ break;
+ }
+ }
+ if (add) P.push_back(*i);
+ }
+ return P;
+}
+
+//**********************************************************************************
+//returnes intersecting polyhedron of polyhedron & plane (possibly empty)
+Polyhedron Polyhedron_Plane_intersection(Polyhedron A, Plane B, CGALpoint centroid, CGALpoint X){
+
+ Polyhedron Intersection;
+ CGALpoint inside;
+ vector<Plane> planes1, planes2;
+ vector<CGALpoint> dual_planes;
+ // test if do intersect, find some intersecting point
+ bool intersection_found = false;
+ double lim = pow(DISTANCE_LIMIT,2);
+ // test centroid of previous intersection
+ if(Is_inside_Polyhedron(A,X,DISTANCE_LIMIT) && Oriented_squared_distance(B,X)<=-lim) {
+ intersection_found = true;
+ inside = X;
+ // find new point by checking polyhedron vertices that lies on negative side of the plane
+ } else {
+ for(Polyhedron::Vertex_iterator vIter = A.vertices_begin(); vIter != A.vertices_end() && !intersection_found ; vIter++){
+ if(Oriented_squared_distance(B,vIter->point())<=-lim) {
+ intersection_found = true;
+ CGAL::Object result = CGAL::intersection(Line(vIter->point(),centroid), B);
+ if (const CGALpoint *ipoint = CGAL::object_cast<CGALpoint>(&result)) {
+ inside = vIter->point() + 0.5*CGALvector(vIter->point(),*ipoint);
+ }else{
+ cerr << "Error in line-plane intersection" << endl;
+ }
+ }
+ }
+ }
+ //no intersectiong point => no intersection polyhedron
+ if(!intersection_found) return Intersection;
+
+
+ //set the intersection point to origin
+ Transformation transl_back(CGAL::TRANSLATION, inside - CGALpoint(0.,0.,0.));
+ Transformation transl(CGAL::TRANSLATION, CGALpoint(0.,0.,0.)-inside);
+
+ std::transform( A.points_begin(), A.points_end(), A.points_begin(), transl);
+ B = transl.transform(B);
+
+ //dualize plane
+ planes1.push_back(B);
+
+ //dualize polyhedron
+ std::transform( A.facets_begin(), A.facets_end(), A.planes_begin(),Plane_equation());
+ for(Polyhedron::Plane_iterator pi =A.planes_begin(); pi!=A.planes_end(); ++pi) planes2.push_back(*pi);;
+
+ //merge planes
+ planes1 = MergePlanes(planes1,planes2, MERGE_PLANES_LIMIT);//MERGE_PLANES_LIMIT);
+ for(vector<Plane>::iterator pi =planes1.begin(); pi!= planes1.end(); ++pi) dual_planes.push_back(CGALpoint(-pi->a()/pi->d(),-pi->b()/pi->d(),-pi->c()/pi->d()));
+
+ //compute convex hull of it
+ Intersection = ConvexHull(dual_planes);
+ if (Intersection.empty()) {cout << "0" << endl; cout.flush(); return Intersection;}
+
+ //simplify
+ std::transform( Intersection.facets_begin(), Intersection.facets_end(), Intersection.planes_begin(),Plane_equation());
+ Intersection = Simplify(Intersection, SIMPLIFY_LIMIT);
+ std::transform( Intersection.facets_begin(), Intersection.facets_end(), Intersection.planes_begin(),Plane_equation());
+
+ //dualize again
+ dual_planes.clear();
+ for(Polyhedron::Plane_iterator pi =Intersection.planes_begin(); pi!=Intersection.planes_end(); ++pi) dual_planes.push_back(CGALpoint(-pi->a()/pi->d(),-pi->b()/pi->d(),-pi->c()/pi->d()));
+
+ //compute convex hull of it
+ Intersection = ConvexHull(dual_planes);
+
+ //return to original position
+ std::transform( Intersection.points_begin(), Intersection.points_end(), Intersection.points_begin(), transl_back);
+
+ if (Intersection.size_of_facets() < 4) Intersection.clear();
+ return Intersection;
+}
+
+//**********************************************************************************
+//returnes intersecting polyhedron of polyhedron & plane defined by diriction and point
+Polyhedron Polyhedron_Plane_intersection(Polyhedron A, Vector3r direction, Vector3r plane_point){
+ Plane B(ToCGALPoint(plane_point), ToCGALVector(direction));
+ CGALpoint X = ToCGALPoint(plane_point) - 1E-8*ToCGALVector(direction);
+ return Polyhedron_Plane_intersection(A, B, ToCGALPoint(plane_point), X);
+}
+
+//**********************************************************************************
+//returnes intersecting polyhedron of two polyhedrons (possibly empty)
+Polyhedron Polyhedron_Polyhedron_intersection(Polyhedron A, Polyhedron B, CGALpoint X, CGALpoint centroidA, CGALpoint centroidB, std::vector<int> &sep_plane){
+
+ Polyhedron Intersection;
+
+ vector<Plane> planes1, planes2;
+ vector<CGALpoint> dual_planes;
+ Polyhedron::Plane_iterator pi;
+ CGALpoint inside(0,0,0);
+
+ bool intersection_found = false;
+ std::transform( A.facets_begin(), A.facets_end(), A.planes_begin(),Plane_equation());
+ std::transform( B.facets_begin(), B.facets_end(), B.planes_begin(),Plane_equation());
+ Matrix3r Amatrix;
+ Vector3r y;
+ // test that X is really inside
+ if(Is_inside_Polyhedron(A,X,DISTANCE_LIMIT) && Is_inside_Polyhedron(B,X,DISTANCE_LIMIT)) {
+ intersection_found = true;
+ inside = X;
+ } else {
+ if (!do_intersect(A, B, sep_plane)) return Intersection;
+ //some intersection point
+ double dist_S, dist_T;
+ double lim2 = pow(DISTANCE_LIMIT,2);
+ CGALvector d1;
+ double factor = sqrt(DISTANCE_LIMIT * 1.5);
+ //test vertices A - not needed, edges are enough
+ for (Polyhedron::Vertex_iterator vIter = A.vertices_begin(); vIter != A.vertices_end() && !intersection_found; vIter++){
+ d1 = centroidA-vIter->point();
+ inside = vIter->point() + d1/sqrt(d1.squared_length())*DISTANCE_LIMIT*20.;
+ intersection_found = (Is_inside_Polyhedron(A,inside,DISTANCE_LIMIT) && Is_inside_Polyhedron(B,inside,DISTANCE_LIMIT));
+ }
+ //test vertices B - necessary
+ for (Polyhedron::Vertex_iterator vIter = B.vertices_begin(); vIter != B.vertices_end() && !intersection_found; vIter++){
+ d1 = centroidB-vIter->point();
+ inside = vIter->point() + d1/sqrt(d1.squared_length())*DISTANCE_LIMIT*20.;
+ intersection_found = (Is_inside_Polyhedron(A,inside,DISTANCE_LIMIT) && Is_inside_Polyhedron(B,inside,DISTANCE_LIMIT));
+ }
+
+ //test edges
+ for (Polyhedron::Edge_iterator eIter = A.edges_begin(); eIter != A.edges_end() && !intersection_found; eIter++){
+ for (Polyhedron::Facet_iterator fIter = B.facets_begin(); fIter != B.facets_end() && !intersection_found; fIter++){
+ dist_S = Oriented_squared_distance(fIter->plane(), eIter->vertex()->point());
+ dist_T = Oriented_squared_distance(fIter->plane(), eIter->opposite()->vertex()->point());
+ if (dist_S*dist_T >= 0 || abs(dist_S)<lim2 || abs(dist_T)<lim2) continue;
+ inside = eIter->vertex()->point() + (eIter->opposite()->vertex()->point()-eIter->vertex()->point())*sqrt(abs(dist_S))/(sqrt(abs(dist_S))+sqrt(abs(dist_T)));
+ // the fact that edge intersects the facet (not only its plane) is not explicitely checked, it sufices to check that the resulting point is inside both polyhedras
+ Plane p1 = fIter->plane();
+ Plane p2 = eIter->facet()->plane();
+ Plane p3 = eIter->opposite()->facet()->plane();
+ Amatrix << p1.a(),p1.b(),p1.c(),
+ p2.a(),p2.b(),p2.c(),
+ p3.a(),p3.b(),p3.c();
+ y = Vector3r(-p1.d()-factor*sqrt(pow(p1.a(),2)+pow(p1.b(),2)+pow(p1.c(),2)),-p2.d()-factor*sqrt(pow(p2.a(),2)+pow(p2.b(),2)+pow(p2.c(),2)),-p3.d()-factor*sqrt(pow(p3.a(),2)+pow(p3.b(),2)+pow(p3.c(),2)));
+ inside = ToCGALPoint(SolveLinSys3x3(Amatrix,y));
+ intersection_found = (Is_inside_Polyhedron(A,inside,DISTANCE_LIMIT) && Is_inside_Polyhedron (B,inside,DISTANCE_LIMIT));
+ }
+ }
+ }
+
+ //Polyhedrons do not intersect
+ if (!intersection_found) return Intersection;
+
+ //set the intersection point to origin
+ Transformation transl_back(CGAL::TRANSLATION, inside - CGALpoint(0.,0.,0.));
+ Transformation transl(CGAL::TRANSLATION, CGALpoint(0.,0.,0.)-inside);
+
+ std::transform( A.points_begin(), A.points_end(), A.points_begin(), transl);
+ std::transform( B.points_begin(), B.points_end(), B.points_begin(), transl);
+
+ //dualize polyhedrons
+ std::transform( A.facets_begin(), A.facets_end(), A.planes_begin(),Plane_equation());
+ std::transform( B.facets_begin(), B.facets_end(), B.planes_begin(),Plane_equation());
+ for(Polyhedron::Plane_iterator pi =A.planes_begin(); pi!=A.planes_end(); ++pi) planes1.push_back(*pi);
+ for(Polyhedron::Plane_iterator pi =B.planes_begin(); pi!=B.planes_end(); ++pi) planes2.push_back(*pi);
+
+
+ //merge planes
+ planes1 = MergePlanes(planes1,planes2, MERGE_PLANES_LIMIT);//MERGE_PLANES_LIMIT);
+ for(vector<Plane>::iterator pi =planes1.begin(); pi!= planes1.end(); ++pi) dual_planes.push_back(CGALpoint(-pi->a()/pi->d(),-pi->b()/pi->d(),-pi->c()/pi->d()));
+
+ //compute convex hull of it
+ Intersection = ConvexHull(dual_planes);
+ if (Intersection.empty()) {cout << "0" << endl; cout.flush(); return Intersection;};
+
+ //simplify
+ std::transform( Intersection.facets_begin(), Intersection.facets_end(), Intersection.planes_begin(),Plane_equation());
+ Intersection = Simplify(Intersection, SIMPLIFY_LIMIT);
+ std::transform( Intersection.facets_begin(), Intersection.facets_end(), Intersection.planes_begin(),Plane_equation());
+
+ //dualize again
+ dual_planes.clear();
+ for(Polyhedron::Plane_iterator pi =Intersection.planes_begin(); pi!=Intersection.planes_end(); ++pi) dual_planes.push_back(CGALpoint(-pi->a()/pi->d(),-pi->b()/pi->d(),-pi->c()/pi->d()));
+
+ //compute convex hull of it
+ Intersection = ConvexHull(dual_planes);
+ //return to original position
+ std::transform( Intersection.points_begin(), Intersection.points_end(), Intersection.points_begin(), transl_back);
+
+ if (Intersection.size_of_facets() < 4) Intersection.clear();
+ return Intersection;
+}
+
+//**********************************************************************************
+/*
+//returnes intersection of Sphere and polyhedron (possibly empty) - not finished
+bool Sphere_Polyhedron_intersection(Polyhedron A, double r, CGALpoint C, CGALpoint centroid, double volume, CGALvector normal, double area){
+ volume = 0;
+ area = 0;
+
+ double r2 = pow(r,2);
+ //check points
+ Polyhedron::Vertex_iterator closest_vert;
+ double dist2;
+ double min_dist2 = (C-centroid).squared_length();
+ //test vertices
+ for(Polyhedron::Vertex_iterator vi=A.vertices_begin(); vi!=A.vertices_end(); ++vi){
+ dist2 = (C-vi->point()).squared_length();
+ if(min_dist2 > dist2) {min_dist2 = dist2; closest_vert=vi;}
+ }
+ if(min_dist2 < r2){ //test if we are already inside the sphere
+
+ return true;
+ }
+
+ //test edges
+ CGALvector v1(C-closest_vert->point());
+ v1 = v1/sqrt(v1.squared_length());
+ Polyhedron::Halfedge_around_vertex_circulator closest_edge;
+ int i =0;
+ double cos_angle;
+ double max_cos_angle=-1;
+ CGALvector v2;
+ for(Polyhedron::Halfedge_around_vertex_circulator hfi=closest_vert->vertex_begin(); i<(int)closest_vert->degree(); ++hfi, i++){
+ v2 = hfi->opposite()->vertex()->point()-closest_vert->point();
+ v2 = v2/sqrt(v2.squared_length());
+ cos_angle = v1*v2;
+ if(cos_angle > max_cos_angle) closest_edge=hfi; max_cos_angle=cos_angle; //find the edge with minimum angle
+ }
+ if(max_cos_angle<=0) return false; //all edges goes with angle above or equal 90
+ double k = (C - closest_vert->point())*v2; //find closest point on the most descending edge (it must be on edge, otherwise opposite point would be already closer)
+ CGALpoint closest_point = closest_vert->point() + k*v2;
+ if((C-closest_point).squared_length()<r2){ //test if we are already inside the sphere
+
+ return true;
+ }
+
+
+ //check planes
+ v1 = (C-closest_point);
+ v1 = v1/sqrt(v1.squared_length());
+ v2 = closest_edge->vertex()->point()-closest_edge->opposite()->vertex()->point();
+ CGALvector v3 = closest_edge->facet()->plane().orthogonal_vector();
+ CGALvector v4 = closest_edge->opposite()->facet()->plane().orthogonal_vector();
+ v3 = CGAL::cross_product(v3,v2);
+ v4 = CGAL::cross_product(v4,v2*(-1));
+ Plane closest_plane;
+ if (v1*v3>=0 && v1*v4>=0) return false; // both planes running away
+ if (v1*v3<0) closest_plane = closest_edge->facet()->plane();
+ else closest_plane = closest_edge->opposite()->facet()->plane();
+ closest_point = closest_plane.projection(C);
+ if((C-closest_point).squared_length()<r2){ //test if we are already inside the sphere
+ double h = sqrt((C-closest_point).squared_length());
+ volume = 3.1415*pow(h,2)/3*(3*r-h);
+ return true;
+ }
+
+ return false;
+}
+*/
+
+//**********************************************************************************
+Vector3r FromCGALPoint(CGALpoint A){
+ return Vector3r(A.x(), A.y(), A.z());
+}
+
+//**********************************************************************************
+Vector3r FromCGALVector(CGALvector A){
+ return Vector3r(A.x(), A.y(), A.z());
+}
+
+//**********************************************************************************
+CGALpoint ToCGALPoint(Vector3r A){
+ return CGALpoint(A[0], A[1], A[2]);
+}
+
+//**********************************************************************************
+CGALvector ToCGALVector(Vector3r A){
+ return CGALvector(A[0], A[1], A[2]);
+}
+
+//**********************************************************************************
+//new polyhedra
+shared_ptr<Body> NewPolyhedra(vector<Vector3r> v, shared_ptr<Material> mat){
+ shared_ptr<Body> body(new Body);
+ body->material=mat;
+ body->shape=shared_ptr<Polyhedra>(new Polyhedra());
+ Polyhedra* A = static_cast<Polyhedra*>(body->shape.get());
+ A->v = v;
+ A->Initialize();
+ body->state->pos= A->GetCentroid();
+ body->state->mass=body->material->density*A->GetVolume();
+ body->state->inertia =A->GetInertia()*body->material->density;
+ body->state->ori = A->GetOri();
+ body->bound=shared_ptr<Aabb>(new Aabb);
+ body->setAspherical(true);
+ return body;
+}
+
+
+//**********************************************************************************
+//split polyhedra
+void SplitPolyhedra(const shared_ptr<Body>& body, Vector3r direction){
+
+ const Se3r& se3=body->state->se3;
+ Polyhedra* A = static_cast<Polyhedra*>(body->shape.get());
+ State* X = static_cast<State*>(body->state.get());
+
+ Vector3r OrigPos = X->pos;
+ Vector3r OrigVel = X->vel;
+ Vector3r OrigAngVel = X->angVel;
+
+ //move and rotate CGAL structure Polyhedron
+ Matrix3r rot_mat = (se3.orientation).toRotationMatrix();
+ Vector3r trans_vec = se3.position;
+ Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PA = A->GetPolyhedron();
+ std::transform( PA.points_begin(), PA.points_end(), PA.points_begin(), t_rot_trans);
+
+ //calculate splitted polyhedrons
+ Polyhedron S1 = Polyhedron_Plane_intersection(PA, direction, trans_vec);
+ Polyhedron S2 = Polyhedron_Plane_intersection(PA, (-1)*direction, trans_vec);
+
+
+ //replace original polyhedron
+ A->Clear();
+ for(Polyhedron::Vertex_iterator vi = S1.vertices_begin(); vi != S1.vertices_end(); vi++){ A->v.push_back(FromCGALPoint(vi->point()));};
+ A->Initialize();
+ X->pos = A->GetCentroid();
+ X->ori = A->GetOri();
+ X->mass=body->material->density*A->GetVolume();
+ X->refPos[0] = X->refPos[0]+1.;
+ X->inertia =A->GetInertia()*body->material->density;
+
+ //second polyhedron
+ vector<Vector3r> v2;
+ for(Polyhedron::Vertex_iterator vi = S2.vertices_begin(); vi != S2.vertices_end(); vi++){ v2.push_back(FromCGALPoint(vi->point()));};
+ shared_ptr<Body> BP = NewPolyhedra(v2, body->material);
+ //BP->shape->color = body->shape->color;
+ BP->shape->color = Vector3r(double(rand())/RAND_MAX,double(rand())/RAND_MAX,double(rand())/RAND_MAX);
+
+ //append to the rest
+ Scene* scene=Omega::instance().getScene().get();
+ //scene->bodies->erase(body->id);
+ scene->bodies->insert(BP);
+
+ //set proper state variables
+ X->vel = OrigVel + OrigAngVel.cross(X->pos-OrigPos);
+ BP->state->vel = OrigVel + OrigAngVel.cross(BP->state->pos-OrigPos);
+ X->angVel = OrigAngVel;
+ BP->state->angVel = OrigAngVel;
+
+}
+
+#endif // YADE_CGAL
=== removed file 'pkg/dem/Shop.cpp'
--- pkg/dem/Shop.cpp 2013-06-20 14:56:35 +0000
+++ pkg/dem/Shop.cpp 1970-01-01 00:00:00 +0000
@@ -1,935 +0,0 @@
-// 2007 © Václav Šmilauer <eudoxos@xxxxxxxx>
-#include"Shop.hpp"
-
-#include<limits>
-
-#include<boost/filesystem/convenience.hpp>
-#include<boost/tokenizer.hpp>
-#include<boost/tuple/tuple.hpp>
-
-#include<yade/core/Scene.hpp>
-#include<yade/core/Body.hpp>
-#include<yade/core/Interaction.hpp>
-
-#include<yade/pkg/common/Aabb.hpp>
-#include<yade/core/Clump.hpp>
-#include<yade/pkg/common/InsertionSortCollider.hpp>
-
-#include<yade/pkg/common/Box.hpp>
-#include<yade/pkg/common/Sphere.hpp>
-#include<yade/pkg/common/ElastMat.hpp>
-#include<yade/pkg/dem/ViscoelasticPM.hpp>
-#include<yade/pkg/dem/CapillaryPhys.hpp>
-
-#include<yade/pkg/common/Bo1_Sphere_Aabb.hpp>
-#include<yade/pkg/common/Bo1_Box_Aabb.hpp>
-#include<yade/pkg/dem/NewtonIntegrator.hpp>
-#include<yade/pkg/dem/Ig2_Sphere_Sphere_ScGeom.hpp>
-#include<yade/pkg/dem/Ig2_Box_Sphere_ScGeom.hpp>
-#include<yade/pkg/dem/Ip2_FrictMat_FrictMat_FrictPhys.hpp>
-
-#include<yade/pkg/common/ForceResetter.hpp>
-
-#include<yade/pkg/common/Dispatching.hpp>
-#include<yade/pkg/common/InteractionLoop.hpp>
-#include<yade/pkg/common/GravityEngines.hpp>
-
-#include<yade/pkg/dem/GlobalStiffnessTimeStepper.hpp>
-#include<yade/pkg/dem/ElasticContactLaw.hpp>
-
-#include<yade/pkg/dem/ScGeom.hpp>
-#include<yade/pkg/dem/FrictPhys.hpp>
-
-#include<yade/pkg/common/Grid.hpp>
-
-#include<yade/pkg/dem/Tetra.hpp>
-
-#ifdef YADE_OPENGL
- #include<yade/pkg/common/Gl1_NormPhys.hpp>
-#endif
-
-#include<boost/foreach.hpp>
-#ifndef FOREACH
- #define FOREACH BOOST_FOREACH
-#endif
-
-CREATE_LOGGER(Shop);
-
-/*! Flip periodic cell by given number of cells.
-
-Still broken, some interactions are missed. Should be checked.
-*/
-
-Matrix3r Shop::flipCell(const Matrix3r& _flip){
- Scene* scene=Omega::instance().getScene().get(); const shared_ptr<Cell>& cell(scene->cell); const Matrix3r& trsf(cell->trsf);
- Vector3r size=cell->getSize();
- Matrix3r flip;
- if(_flip==Matrix3r::Zero()){
- bool hasNonzero=false;
- for(int i=0; i<3; i++) for(int j=0; j<3; j++) {
- if(i==j){ flip(i,j)=0; continue; }
- flip(i,j)=-floor(.5+trsf(i,j)/(size[j]/size[i]));
- if(flip(i,j)!=0) hasNonzero=true;
- }
- if(!hasNonzero) {LOG_TRACE("No flip necessary."); return Matrix3r::Zero();}
- LOG_DEBUG("Computed flip matrix: upper "<<flip(0,1)<<","<<flip(0,2)<<","<<flip(1,2)<<"; lower "<<flip(1,0)<<","<<flip(2,0)<<","<<flip(2,1));
- } else {
- flip=_flip;
- }
-
- // current cell coords of bodies
- vector<Vector3i > oldCells; oldCells.resize(scene->bodies->size());
- FOREACH(const shared_ptr<Body>& b, *scene->bodies){
- if(!b) continue; cell->wrapShearedPt(b->state->pos,oldCells[b->getId()]);
- }
-
- // change cell trsf here
- Matrix3r trsfInc;
- for(int i=0; i<3; i++) for(int j=0; j<3; j++){
- if(i==j) { if(flip(i,j)!=0) LOG_WARN("Non-zero diagonal term at ["<<i<<","<<j<<"] is meaningless and will be ignored."); trsfInc(i,j)=0; continue; }
- // make sure non-diagonal entries are "integers"
- if(flip(i,j)!=double(int(flip(i,j)))) LOG_WARN("Flip matrix entry "<<flip(i,j)<<" at ["<<i<<","<<j<<"] not integer?! (will be rounded)");
- trsfInc(i,j)=int(flip(i,j))*size[j]/size[i];
- }
- TRWM3MAT(cell->trsf);
- TRWM3MAT(trsfInc);
- cell->trsf+=trsfInc;
- cell->postLoad(*cell);
-
- // new cell coords of bodies
- vector<Vector3i > newCells; newCells.resize(scene->bodies->size());
- FOREACH(const shared_ptr<Body>& b, *scene->bodies){
- if(!b) continue;
- cell->wrapShearedPt(b->state->pos,newCells[b->getId()]);
- }
-
- // remove all potential interactions
- scene->interactions->eraseNonReal();
- // adjust Interaction::cellDist for real interactions;
- FOREACH(const shared_ptr<Interaction>& i, *scene->interactions){
- Body::id_t id1=i->getId1(),id2=i->getId2();
- // this must be the same for both old and new interaction: cell2-cell1+cellDist
- // c₂-c₁+c₁₂=k; c'₂+c₁'+c₁₂'=k (new cell coords have ')
- // c₁₂'=(c₂-c₁+c₁₂)-(c₂'-c₁')
- i->cellDist=(oldCells[id2]-oldCells[id1]+i->cellDist)-(newCells[id2]-newCells[id1]);
- }
-
-
- // force reinitialization of the collider
- bool colliderFound=false;
- FOREACH(const shared_ptr<Engine>& e, scene->engines){
- Collider* c=dynamic_cast<Collider*>(e.get());
- if(c){ colliderFound=true; c->invalidatePersistentData(); }
- }
- if(!colliderFound) LOG_WARN("No collider found while flipping cell; continuing simulation might give garbage results.");
- return flip;
-}
-
-/* Apply force on contact point to 2 bodies; the force is oriented as it applies on the first body and is reversed on the second.
- */
-void Shop::applyForceAtContactPoint(const Vector3r& force, const Vector3r& contPt, Body::id_t id1, const Vector3r& pos1, Body::id_t id2, const Vector3r& pos2, Scene* scene){
- scene->forces.addForce(id1,force);
- scene->forces.addForce(id2,-force);
- scene->forces.addTorque(id1,(contPt-pos1).cross(force));
- scene->forces.addTorque(id2,-(contPt-pos2).cross(force));
-}
-
-
-/*! Compute sum of forces in the whole simulation and averages stiffness.
-
-Designed for being used with periodic cell, where diving the resulting components by
-areas of the cell will give average stress in that direction.
-
-Requires all .isReal() interaction to have phys deriving from NormShearPhys.
-*/
-Vector3r Shop::totalForceInVolume(Real& avgIsoStiffness, Scene* _rb){
- Scene* rb=_rb ? _rb : Omega::instance().getScene().get();
- Vector3r force(Vector3r::Zero()); Real stiff=0; long n=0;
- FOREACH(const shared_ptr<Interaction>&I, *rb->interactions){
- if(!I->isReal()) continue;
- NormShearPhys* nsi=YADE_CAST<NormShearPhys*>(I->phys.get());
- force+=Vector3r(abs(nsi->normalForce[0]+nsi->shearForce[0]),abs(nsi->normalForce[1]+nsi->shearForce[1]),abs(nsi->normalForce[2]+nsi->shearForce[2]));
- stiff+=(1/3.)*nsi->kn+(2/3.)*nsi->ks; // count kn in one direction and ks in the other two
- n++;
- }
- avgIsoStiffness= n>0 ? (1./n)*stiff : -1;
- return force;
-}
-
-Real Shop::unbalancedForce(bool useMaxForce, Scene* _rb){
- Scene* rb=_rb ? _rb : Omega::instance().getScene().get();
- rb->forces.sync();
- shared_ptr<NewtonIntegrator> newton;
- Vector3r gravity = Vector3r::Zero();
- FOREACH(shared_ptr<Engine>& e, rb->engines){ newton=dynamic_pointer_cast<NewtonIntegrator>(e); if(newton) {gravity=newton->gravity; break;} }
- // get maximum force on a body and sum of all forces (for averaging)
- Real sumF=0,maxF=0,currF; int nb=0;
- FOREACH(const shared_ptr<Body>& b, *rb->bodies){
- if(!b || b->isClumpMember() || !b->isDynamic()) continue;
- currF=(rb->forces.getForce(b->id)+b->state->mass*gravity).norm();
- if(b->isClump() && currF==0){ // this should not happen unless the function is called by an engine whose position in the loop is before Newton (with the exception of bodies which really have null force), because clumps forces are updated in Newton. Typical triaxial loops are using such ordering unfortunately (triaxEngine before Newton). So, here we make sure that they will get correct unbalance. In the future, it is better for optimality to check unbalancedF inside scripts at the end of loops, so that this "if" is never active.
- Vector3r f(rb->forces.getForce(b->id)),m(Vector3r::Zero());
- b->shape->cast<Clump>().addForceTorqueFromMembers(b->state.get(),rb,f,m);
- currF=(f+b->state->mass*gravity).norm();
- }
- maxF=max(currF,maxF); sumF+=currF; nb++;
- }
- Real meanF=sumF/nb;
- // get mean force on interactions
- sumF=0; nb=0;
- FOREACH(const shared_ptr<Interaction>& I, *rb->interactions){
- if(!I->isReal()) continue;
- shared_ptr<NormShearPhys> nsi=YADE_PTR_CAST<NormShearPhys>(I->phys); assert(nsi);
- sumF+=(nsi->normalForce+nsi->shearForce).norm(); nb++;
- }
- sumF/=nb;
- return (useMaxForce?maxF:meanF)/(sumF);
-}
-
-Real Shop::kineticEnergy(Scene* _scene, Body::id_t* maxId){
- Scene* scene=_scene ? _scene : Omega::instance().getScene().get();
- Real ret=0.;
- Real maxE=0; if(maxId) *maxId=Body::ID_NONE;
- FOREACH(const shared_ptr<Body>& b, *scene->bodies){
- if(!b || !b->isDynamic()) continue;
- const State* state(b->state.get());
- // ½(mv²+ωIω)
- Real E=0;
- if(scene->isPeriodic){
- /* Only take in account the fluctuation velocity, not the mean velocity of homothetic resize. */
- E=.5*state->mass*scene->cell->bodyFluctuationVel(state->pos,state->vel,scene->cell->velGrad).squaredNorm();
- } else {
- E=.5*(state->mass*state->vel.squaredNorm());
- }
- if(b->isAspherical()){
- Matrix3r T(state->ori);
- // the tensorial expression http://en.wikipedia.org/wiki/Moment_of_inertia#Moment_of_inertia_tensor
- // inertia tensor rotation from http://www.kwon3d.com/theory/moi/triten.html
- Matrix3r mI; mI<<state->inertia[0],0,0, 0,state->inertia[1],0, 0,0,state->inertia[2];
- E+=.5*state->angVel.transpose().dot((T.transpose()*mI*T)*state->angVel);
- }
- else { E+=0.5*state->angVel.dot(state->inertia.cwiseProduct(state->angVel));}
- if(maxId && E>maxE) { *maxId=b->getId(); maxE=E; }
- ret+=E;
- }
- return ret;
-}
-
-shared_ptr<FrictMat> Shop::defaultGranularMat(){
- shared_ptr<FrictMat> mat(new FrictMat);
- mat->density=2e3;
- mat->young=30e9;
- mat->poisson=.3;
- mat->frictionAngle=.5236; //30˚
- return mat;
-}
-
-/*! Create body - sphere. */
-shared_ptr<Body> Shop::sphere(Vector3r center, Real radius, shared_ptr<Material> mat){
- shared_ptr<Body> body(new Body);
- body->material=mat ? mat : static_pointer_cast<Material>(defaultGranularMat());
- body->state->pos=center;
- body->state->mass=4.0/3.0*Mathr::PI*radius*radius*radius*body->material->density;
- body->state->inertia=Vector3r(2.0/5.0*body->state->mass*radius*radius,2.0/5.0*body->state->mass*radius*radius,2.0/5.0*body->state->mass*radius*radius);
- body->bound=shared_ptr<Aabb>(new Aabb);
- body->shape=shared_ptr<Sphere>(new Sphere(radius));
- return body;
-}
-
-/*! Create body - box. */
-shared_ptr<Body> Shop::box(Vector3r center, Vector3r extents, shared_ptr<Material> mat){
- shared_ptr<Body> body(new Body);
- body->material=mat ? mat : static_pointer_cast<Material>(defaultGranularMat());
- body->state->pos=center;
- Real mass=8.0*extents[0]*extents[1]*extents[2]*body->material->density;
- body->state->mass=mass;
- body->state->inertia=Vector3r(mass*(4*extents[1]*extents[1]+4*extents[2]*extents[2])/12.,mass*(4*extents[0]*extents[0]+4*extents[2]*extents[2])/12.,mass*(4*extents[0]*extents[0]+4*extents[1]*extents[1])/12.);
- body->bound=shared_ptr<Aabb>(new Aabb);
- body->shape=shared_ptr<Box>(new Box(extents));
- return body;
-}
-
-/*! Create body - tetrahedron. */
-shared_ptr<Body> Shop::tetra(Vector3r v_global[4], shared_ptr<Material> mat){
- shared_ptr<Body> body(new Body);
- body->material=mat ? mat : static_pointer_cast<Material>(defaultGranularMat());
- Vector3r centroid=(v_global[0]+v_global[1]+v_global[2]+v_global[3])*.25;
- Vector3r v[4]; for(int i=0; i<4; i++) v[i]=v_global[i]-centroid;
- body->state->pos=centroid;
- body->state->mass=body->material->density*TetrahedronVolume(v);
- // inertia will be calculated below, by TetrahedronWithLocalAxesPrincipal
- body->bound=shared_ptr<Aabb>(new Aabb);
- body->shape=shared_ptr<Tetra>(new Tetra(v[0],v[1],v[2],v[3]));
- // make local axes coincident with principal axes
- TetrahedronWithLocalAxesPrincipal(body);
- return body;
-}
-
-
-void Shop::saveSpheresToFile(string fname){
- const shared_ptr<Scene>& scene=Omega::instance().getScene();
- ofstream f(fname.c_str());
- if(!f.good()) throw runtime_error("Unable to open file `"+fname+"'");
-
- FOREACH(shared_ptr<Body> b, *scene->bodies){
- if (!b->isDynamic()) continue;
- shared_ptr<Sphere> intSph=dynamic_pointer_cast<Sphere>(b->shape);
- if(!intSph) continue;
- const Vector3r& pos=b->state->pos;
- f<<pos[0]<<" "<<pos[1]<<" "<<pos[2]<<" "<<intSph->radius<<endl; // <<" "<<1<<" "<<1<<endl;
- }
- f.close();
-}
-
-Real Shop::getSpheresVolume(const shared_ptr<Scene>& _scene, int mask){
- const shared_ptr<Scene> scene=(_scene?_scene:Omega::instance().getScene());
- Real vol=0;
- FOREACH(shared_ptr<Body> b, *scene->bodies){
- if (!b || !b->isDynamic()) continue;
- Sphere* s=dynamic_cast<Sphere*>(b->shape.get());
- if((!s) or ((mask>0) and ((b->groupMask & mask)==0))) continue;
- vol += (4/3.)*Mathr::PI*pow(s->radius,3);
- }
- return vol;
-}
-
-Real Shop::getSpheresMass(const shared_ptr<Scene>& _scene, int mask){
- const shared_ptr<Scene> scene=(_scene?_scene:Omega::instance().getScene());
- Real mass=0;
- FOREACH(shared_ptr<Body> b, *scene->bodies){
- if (!b || !b->isDynamic()) continue;
- Sphere* s=dynamic_cast<Sphere*>(b->shape.get());
- if((!s) or ((mask>0) and ((b->groupMask & mask)==0))) continue;
- mass += b->state->mass;
- }
- return mass;
-}
-
-Real Shop::getPorosity(const shared_ptr<Scene>& _scene, Real _volume){
- const shared_ptr<Scene> scene=(_scene?_scene:Omega::instance().getScene());
- Real V;
- if(!scene->isPeriodic){
- if(_volume<=0) throw std::invalid_argument("utils.porosity must be given (positive) *volume* for aperiodic simulations.");
- V=_volume;
- } else {
- V=scene->cell->getVolume();
- }
- Real Vs=Shop::getSpheresVolume();
- return (V-Vs)/V;
-}
-
-Real Shop::getVoxelPorosity(const shared_ptr<Scene>& _scene, int _resolution, Vector3r _start,Vector3r _end){
- const shared_ptr<Scene> scene=(_scene?_scene:Omega::instance().getScene());
- if(_start==_end) throw std::invalid_argument("utils.voxelPorosity: cannot calculate porosity when start==end of the volume box.");
- if(_resolution<50) throw std::invalid_argument("utils.voxelPorosity: it doesn't make sense to calculate porosity with voxel resolution below 50.");
-
- // prepare the gird, it eats a lot of memory.
- // I am not optimizing for using bits. A single byte for each cell is used.
- std::vector<std::vector<std::vector<unsigned char> > > grid;
- int S(_resolution);
- grid.resize(S);
- for(int i=0 ; i<S ; ++i) {
- grid[i].resize(S);
- for(int j=0 ; j<S ; ++j) {
- grid[i][j].resize(S,0);
- }
- }
-
- Vector3r start(_start), size(_end - _start);
-
- FOREACH(shared_ptr<Body> bi, *scene->bodies){
- if((bi)->isClump()) continue;
- const shared_ptr<Body>& b = bi;
- if ( b->isDynamic() || b->isClumpMember() ) {
- const shared_ptr<Sphere>& sphere = YADE_PTR_CAST<Sphere> ( b->shape );
- Real r = sphere->radius;
- Real rr = r*r;
- Vector3r pos = b->state->se3.position;
- // we got sphere with radius r, at position pos.
- // and a box of size S, scaled to 'size'
- // mark cells that are iniside a sphere
- int ii(0),II(S),jj(0),JJ(S),kk(0),KK(S);
- // make sure to loop only in AABB of that sphere. No need to waste cycles outside of it.
- ii = std::max((int)((Real)(pos[0]-start[0]-r)*(Real)(S)/(Real)(size[0]))-1,0); II = std::min(ii+(int)((Real)(2.0*r)*(Real)(S)/(Real)(size[0]))+3,S);
- jj = std::max((int)((Real)(pos[1]-start[1]-r)*(Real)(S)/(Real)(size[1]))-1,0); JJ = std::min(jj+(int)((Real)(2.0*r)*(Real)(S)/(Real)(size[1]))+3,S);
- kk = std::max((int)((Real)(pos[2]-start[2]-r)*(Real)(S)/(Real)(size[2]))-1,0); KK = std::min(kk+(int)((Real)(2.0*r)*(Real)(S)/(Real)(size[2]))+3,S);
- for(int i=ii ; i<II ; ++i) {
- for(int j=jj ; j<JJ ; ++j) {
- for(int k=kk ; k<KK ; ++k) {
- Vector3r a(i,j,k);
- a = a/(Real)(S);
- Vector3r b( a[0]*size[0] , a[1]*size[1] , a[2]*size[2] );
- b = b+start;
- Vector3r c(0,0,0);
- c = pos-b;
- Real x = c[0];
- Real y = c[1];
- Real z = c[2];
- if(x*x+y*y+z*z < rr)
- grid[i][j][k]=1;
- }}}
- }
- }
-
- Real Vv=0;
- for(int i=0 ; i<S ; ++i ) {
- for(int j=0 ; j<S ; ++j ) {
- for(int k=0 ; k<S ; ++k ) {
- if(grid[i][j][k]==1)
- Vv += 1.0;
- }
- }
- }
-
- return ( std::pow(S,3) - Vv ) / std::pow(S,3);
-};
-
-vector<tuple<Vector3r,Real,int> > Shop::loadSpheresFromFile(const string& fname, Vector3r& minXYZ, Vector3r& maxXYZ, Vector3r* cellSize){
- if(!boost::filesystem::exists(fname)) {
- throw std::invalid_argument(string("File with spheres `")+fname+"' doesn't exist.");
- }
- vector<tuple<Vector3r,Real,int> > spheres;
- ifstream sphereFile(fname.c_str());
- if(!sphereFile.good()) throw std::runtime_error("File with spheres `"+fname+"' couldn't be opened.");
- Vector3r C;
- Real r=0;
- int clumpId=-1;
- string line;
- size_t lineNo=0;
- while(std::getline(sphereFile, line, '\n')){
- lineNo++;
- boost::tokenizer<boost::char_separator<char> > toks(line,boost::char_separator<char>(" \t"));
- vector<string> tokens; FOREACH(const string& s, toks) tokens.push_back(s);
- if(tokens.empty()) continue;
- if(tokens[0]=="##PERIODIC::"){
- if(tokens.size()!=4) throw std::invalid_argument(("Spheres file "+fname+":"+lexical_cast<string>(lineNo)+" contains ##PERIODIC::, but the line is malformed.").c_str());
- if(cellSize){ *cellSize=Vector3r(lexical_cast<Real>(tokens[1]),lexical_cast<Real>(tokens[2]),lexical_cast<Real>(tokens[3])); }
- continue;
- }
- if(tokens.size()!=5 and tokens.size()!=4) throw std::invalid_argument(("Line "+lexical_cast<string>(lineNo)+" in the spheres file "+fname+" has "+lexical_cast<string>(tokens.size())+" columns (must be 4 or 5).").c_str());
- C=Vector3r(lexical_cast<Real>(tokens[0]),lexical_cast<Real>(tokens[1]),lexical_cast<Real>(tokens[2]));
- r=lexical_cast<Real>(tokens[3]);
- for(int j=0; j<3; j++) { minXYZ[j]=(spheres.size()>0?min(C[j]-r,minXYZ[j]):C[j]-r); maxXYZ[j]=(spheres.size()>0?max(C[j]+r,maxXYZ[j]):C[j]+r);}
- if(tokens.size()==5)clumpId=lexical_cast<int>(tokens[4]);
- spheres.push_back(tuple<Vector3r,Real,int>(C,r,clumpId));
- }
- return spheres;
-}
-
-Real Shop::PWaveTimeStep(const shared_ptr<Scene> _rb){
- shared_ptr<Scene> rb=(_rb?_rb:Omega::instance().getScene());
- Real dt=std::numeric_limits<Real>::infinity();
- FOREACH(const shared_ptr<Body>& b, *rb->bodies){
- if(!b || !b->material || !b->shape) continue;
- shared_ptr<ElastMat> ebp=dynamic_pointer_cast<ElastMat>(b->material);
- shared_ptr<Sphere> s=dynamic_pointer_cast<Sphere>(b->shape);
- if(!ebp || !s) continue;
- Real density=b->state->mass/((4/3.)*Mathr::PI*pow(s->radius,3));
- dt=min(dt,s->radius/sqrt(ebp->young/density));
- }
- if (dt==std::numeric_limits<Real>::infinity()) {
- dt = 1.0;
- LOG_WARN("PWaveTimeStep has not found any suitable spherical body to calculate dt. dt is set to 1.0");
- }
- return dt;
-}
-
-/* Detremination of time step as according to Rayleigh wave speed of force propagation (see Thornton 2000, ref. MillerPursey_1955) */
-Real Shop::RayleighWaveTimeStep(const shared_ptr<Scene> _rb){
- shared_ptr<Scene> rb=(_rb?_rb:Omega::instance().getScene());
- Real dt=std::numeric_limits<Real>::infinity();
- FOREACH(const shared_ptr<Body>& b, *rb->bodies){
- if(!b || !b->material || !b->shape) continue;
-
- shared_ptr<ElastMat> ebp=dynamic_pointer_cast<ElastMat>(b->material);
- shared_ptr<Sphere> s=dynamic_pointer_cast<Sphere>(b->shape);
- if(!ebp || !s) continue;
-
- Real density=b->state->mass/((4/3.)*Mathr::PI*pow(s->radius,3));
- Real ShearModulus=ebp->young/(2.*(1+ebp->poisson));
- Real lambda=0.1631*ebp->poisson+0.876605;
- dt=min(dt,Mathr::PI*s->radius/lambda*sqrt(density/ShearModulus));
- }
- return dt;
-}
-
-/* Project 3d point into 2d using spiral projection along given axis;
- * the returned tuple is
- *
- * (height relative to the spiral, distance from axis, theta )
- *
- * dH_dTheta is the inclination of the spiral (height increase per radian),
- * theta0 is the angle for zero height (by given axis).
- */
-boost::tuple<Real,Real,Real> Shop::spiralProject(const Vector3r& pt, Real dH_dTheta, int axis, Real periodStart, Real theta0){
- int ax1=(axis+1)%3,ax2=(axis+2)%3;
- Real r=sqrt(pow(pt[ax1],2)+pow(pt[ax2],2));
- Real theta;
- if(r>Mathr::ZERO_TOLERANCE){
- theta=acos(pt[ax1]/r);
- if(pt[ax2]<0) theta=Mathr::TWO_PI-theta;
- }
- else theta=0;
- Real hRef=dH_dTheta*(theta-theta0);
- long period;
- if(isnan(periodStart)){
- Real h=Shop::periodicWrap(pt[axis]-hRef,hRef-Mathr::PI*dH_dTheta,hRef+Mathr::PI*dH_dTheta,&period);
- return boost::make_tuple(r,h,theta);
- }
- else{
- // Real hPeriodStart=(periodStart-theta0)*dH_dTheta;
- //TRVAR4(hPeriodStart,periodStart,theta0,theta);
- //Real h=Shop::periodicWrap(pt[axis]-hRef,hPeriodStart,hPeriodStart+2*Mathr::PI*dH_dTheta,&period);
- theta=Shop::periodicWrap(theta,periodStart,periodStart+2*Mathr::PI,&period);
- Real h=pt[axis]-hRef+period*2*Mathr::PI*dH_dTheta;
- //TRVAR3(pt[axis],pt[axis]-hRef,period);
- //TRVAR2(h,theta);
- return boost::make_tuple(r,h,theta);
- }
-}
-
-shared_ptr<Interaction> Shop::createExplicitInteraction(Body::id_t id1, Body::id_t id2, bool force){
- IGeomDispatcher* geomMeta=NULL;
- IPhysDispatcher* physMeta=NULL;
- shared_ptr<Scene> rb=Omega::instance().getScene();
- if(rb->interactions->find(Body::id_t(id1),Body::id_t(id2))!=0) throw runtime_error(string("Interaction #")+lexical_cast<string>(id1)+"+#"+lexical_cast<string>(id2)+" already exists.");
- FOREACH(const shared_ptr<Engine>& e, rb->engines){
- if(!geomMeta) { geomMeta=dynamic_cast<IGeomDispatcher*>(e.get()); if(geomMeta) continue; }
- if(!physMeta) { physMeta=dynamic_cast<IPhysDispatcher*>(e.get()); if(physMeta) continue; }
- InteractionLoop* id(dynamic_cast<InteractionLoop*>(e.get()));
- if(id){ geomMeta=id->geomDispatcher.get(); physMeta=id->physDispatcher.get(); }
- if(geomMeta&&physMeta){break;}
- }
- if(!geomMeta) throw runtime_error("No IGeomDispatcher in engines or inside InteractionLoop.");
- if(!physMeta) throw runtime_error("No IPhysDispatcher in engines or inside InteractionLoop.");
- shared_ptr<Body> b1=Body::byId(id1,rb), b2=Body::byId(id2,rb);
- if(!b1) throw runtime_error(("No body #"+lexical_cast<string>(id1)).c_str());
- if(!b2) throw runtime_error(("No body #"+lexical_cast<string>(id2)).c_str());
- shared_ptr<Interaction> i=geomMeta->explicitAction(b1,b2,/*force*/force);
- assert(force && i);
- if(!i) return i;
- physMeta->explicitAction(b1->material,b2->material,i);
- i->iterMadeReal=rb->iter;
- rb->interactions->insert(i);
- return i;
-}
-
-Vector3r Shop::inscribedCircleCenter(const Vector3r& v0, const Vector3r& v1, const Vector3r& v2)
-{
- return v0+((v2-v0)*(v1-v0).norm()+(v1-v0)*(v2-v0).norm())/((v1-v0).norm()+(v2-v1).norm()+(v0-v2).norm());
-}
-
-void Shop::getViscoelasticFromSpheresInteraction( Real tc, Real en, Real es, shared_ptr<ViscElMat> b)
-{
- b->kn = 1/tc/tc * ( Mathr::PI*Mathr::PI + pow(log(en),2) );
- b->cn = -2.0 /tc * log(en);
- b->ks = 2.0/7.0 /tc/tc * ( Mathr::PI*Mathr::PI + pow(log(es),2) );
- b->cs = -2.0/7.0 /tc * log(es);
-
- if (abs(b->cn) <= Mathr::ZERO_TOLERANCE ) b->cn=0;
- if (abs(b->cs) <= Mathr::ZERO_TOLERANCE ) b->cs=0;
-}
-
-/* This function is copied almost verbatim from scientific python, module Visualization, class ColorScale
- *
- */
-Vector3r Shop::scalarOnColorScale(Real x, Real xmin, Real xmax){
- Real xnorm=min((Real)1.,max((x-xmin)/(xmax-xmin),(Real)0.));
- if(xnorm<.25) return Vector3r(0,4.*xnorm,1);
- if(xnorm<.5) return Vector3r(0,1,1.-4.*(xnorm-.25));
- if(xnorm<.75) return Vector3r(4*(xnorm-.5),1.,0);
- return Vector3r(1,1-4*(xnorm-.75),0);
-}
-
-/* Wrap floating point number into interval (x0,x1〉such that it is shifted
- * by integral number of the interval range. If given, *period will hold
- * this number. The wrapped value is returned.
- */
-Real Shop::periodicWrap(Real x, Real x0, Real x1, long* period){
- Real xNorm=(x-x0)/(x1-x0);
- Real xxNorm=xNorm-floor(xNorm);
- if(period) *period=(long)floor(xNorm);
- return x0+xxNorm*(x1-x0);
-}
-
-void Shop::getStressForEachBody(vector<Shop::bodyState>& bodyStates){
- const shared_ptr<Scene>& scene=Omega::instance().getScene();
- bodyStates.resize(scene->bodies->size());
- FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
- Vector3r normalStress,shearStress;
- if(!I->isReal()) continue;
-
- //NormShearPhys + Dem3DofGeom
- const NormShearPhys* physNSP = YADE_CAST<NormShearPhys*>(I->phys.get());
- //Dem3DofGeom* geom=YADE_CAST<Dem3DofGeom*>(I->geom.get()); //For the moment only for Dem3DofGeom!!!
- // FIXME: slower, but does not crash
- Dem3DofGeom* geomDDG=dynamic_cast<Dem3DofGeom*>(I->geom.get()); //For the moment only for Dem3DofGeom!!!
-
- //FrictPhys + ScGeom
- const FrictPhys* physFP = YADE_CAST<FrictPhys*>(I->phys.get());
- ScGeom* geomScG=dynamic_cast<ScGeom*>(I->geom.get());
-
- const Body::id_t id1=I->getId1(), id2=I->getId2();
-
- if(((physNSP) and (geomDDG)) or ((physFP) and (geomScG))){
- if ((physNSP) and (geomDDG)) {
- Real minRad=(geomDDG->refR1<=0?geomDDG->refR2:(geomDDG->refR2<=0?geomDDG->refR1:min(geomDDG->refR1,geomDDG->refR2)));
- Real crossSection=Mathr::PI*pow(minRad,2);
-
- normalStress=((1./crossSection)*geomDDG->normal.dot(physNSP->normalForce))*geomDDG->normal;
- for(int i=0; i<3; i++){
- int ix1=(i+1)%3,ix2=(i+2)%3;
- shearStress[i]=geomDDG->normal[ix1]*physNSP->shearForce[ix1]+geomDDG->normal[ix2]*physNSP->shearForce[ix2];
- shearStress[i]/=crossSection;
- }
- } else if ((physFP) and (geomScG)) {
- Real minRad=(geomScG->radius1<=0?geomScG->radius2:(geomScG->radius2<=0?geomScG->radius1:min(geomScG->radius1,geomScG->radius2)));
- Real crossSection=Mathr::PI*pow(minRad,2);
-
- normalStress=((1./crossSection)*geomScG->normal.dot(physFP->normalForce))*geomScG->normal;
- for(int i=0; i<3; i++){
- int ix1=(i+1)%3,ix2=(i+2)%3;
- shearStress[i]=geomScG->normal[ix1]*physFP->shearForce[ix1]+geomScG->normal[ix2]*physFP->shearForce[ix2];
- shearStress[i]/=crossSection;
- }
- }
-
- bodyStates[id1].normStress+=normalStress;
- bodyStates[id2].normStress+=normalStress;
-
- bodyStates[id1].shearStress+=shearStress;
- bodyStates[id2].shearStress+=shearStress;
- }
- }
-}
-
-/* Return the stress tensor decomposed in 2 contributions, from normal and shear forces.
-The formulation follows the [Thornton2000]_ article
-"Numerical simulations of deviatoric shear deformation of granular media", eq (3) and (4)
- */
-py::tuple Shop::normalShearStressTensors(bool compressionPositive, bool splitNormalTensor, Real thresholdForce){
-
- //*** Stress tensor split into shear and normal contribution ***/
- Scene* scene=Omega::instance().getScene().get();
- if (!scene->isPeriodic){ throw runtime_error("Can't compute stress of periodic cell in aperiodic simulation."); }
- Matrix3r sigN(Matrix3r::Zero()),sigT(Matrix3r::Zero());
- //const Matrix3r& cellHsize(scene->cell->Hsize); //Disabled because of warning.
- FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
- if(!I->isReal()) continue;
- GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(I->geom.get());
- NormShearPhys* phys=YADE_CAST<NormShearPhys*>(I->phys.get());
- const Vector3r& n=geom->normal;
- // if compression has positive sign, we need to change sign for both normal and shear force
- // make copy to Fs since shearForce is used at multiple places (less efficient, but avoids confusion)
- Vector3r Fs=(compressionPositive?-1:1)*phys->shearForce;
- Real N=(compressionPositive?-1:1)*phys->normalForce.dot(n), T=Fs.norm();
- bool hasShear=(T>0);
- Vector3r t; if(hasShear) t=Fs/T;
- // Real R=(Body::byId(I->getId2(),scene)->state->pos+cellHsize*I->cellDist.cast<Real>()-Body::byId(I->getId1(),scene)->state->pos).norm();
- Real R=.5*(geom->refR1+geom->refR2);
- for(int i=0; i<3; i++) for(int j=i; j<3; j++){
- sigN(i,j)+=R*N*n[i]*n[j];
- if(hasShear) sigT(i,j)+=R*T*n[i]*t[j];
- }
- }
- Real vol=scene->cell->getVolume();
- sigN*=2/vol; sigT*=2/vol;
- // fill terms under the diagonal
- sigN(1,0)=sigN(0,1); sigN(2,0)=sigN(0,2); sigN(2,1)=sigN(1,2);
- sigT(1,0)=sigT(0,1); sigT(2,0)=sigT(0,2); sigT(2,1)=sigT(1,2);
-
- // *** Normal stress tensor split into two parts according to subnetworks of strong and weak forces (or other distinction if a threshold value for the force is assigned) ***/
- Real Fmean(0); Matrix3r f, fs, fw;
- fabricTensor(Fmean,f,fs,fw,false,compressionPositive,NaN);
- Matrix3r sigNStrong(Matrix3r::Zero()), sigNWeak(Matrix3r::Zero());
- FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
- if(!I->isReal()) continue;
- GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(I->geom.get());
- NormShearPhys* phys=YADE_CAST<NormShearPhys*>(I->phys.get());
- const Vector3r& n=geom->normal;
- Real N=(compressionPositive?-1:1)*phys->normalForce.dot(n);
- // Real R=(Body::byId(I->getId2(),scene)->state->pos+cellHsize*I->cellDist.cast<Real>()-Body::byId(I->getId1(),scene)->state->pos).norm();
- Real R=.5*(geom->refR1+geom->refR2);
- Real Fsplit=(!isnan(thresholdForce))?thresholdForce:Fmean;
- if (compressionPositive?(N<Fsplit):(N>Fsplit)){
- for(int i=0; i<3; i++) for(int j=i; j<3; j++){
- sigNStrong(i,j)+=R*N*n[i]*n[j];}
- }
- else{
- for(int i=0; i<3; i++) for(int j=i; j<3; j++){
- sigNWeak(i,j)+=R*N*n[i]*n[j];}
- }
- }
- sigNStrong*=2/vol; sigNWeak*=2/vol;
- // fill terms under the diagonal
- sigNStrong(1,0)=sigNStrong(0,1); sigNStrong(2,0)=sigNStrong(0,2); sigNStrong(2,1)=sigNStrong(1,2);
- sigNWeak(1,0)=sigNWeak(0,1); sigNWeak(2,0)=sigNWeak(0,2); sigNWeak(2,1)=sigNWeak(1,2);
-
- /// tensile forces are taken as positive!
- if (splitNormalTensor){return py::make_tuple(sigNStrong,sigNWeak);} // return strong-weak or tensile-compressive parts of the stress tensor (only normal part)
- return py::make_tuple(sigN,sigT); // return normal and shear components
-}
-
-/* Return the fabric tensor as according to [Satake1982]. */
-/* as side-effect, set Gl1_NormShear::strongWeakThresholdForce */
-void Shop::fabricTensor(Real& Fmean, Matrix3r& fabric, Matrix3r& fabricStrong, Matrix3r& fabricWeak, bool splitTensor, bool revertSign, Real thresholdForce){
- Scene* scene=Omega::instance().getScene().get();
- if (!scene->isPeriodic){ throw runtime_error("Can't compute fabric tensor of periodic cell in aperiodic simulation."); }
-
- // *** Fabric tensor ***/
- fabric=Matrix3r::Zero();
- int count=0; // number of interactions
- FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
- if(!I->isReal()) continue;
- GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(I->geom.get());
- const Vector3r& n=geom->normal;
- for(int i=0; i<3; i++) for(int j=i; j<3; j++){
- fabric(i,j)+=n[i]*n[j];
- }
- count++;
- }
- // fill terms under the diagonal
- fabric(1,0)=fabric(0,1); fabric(2,0)=fabric(0,2); fabric(2,1)=fabric(1,2);
- fabric/=count;
-
- // *** Average contact force ***/
- // calculate average contact force
- Fmean=0; // initialize
- FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
- if(!I->isReal()) continue;
- GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(I->geom.get());
- NormShearPhys* phys=YADE_CAST<NormShearPhys*>(I->phys.get());
- const Vector3r& n=geom->normal;
- Real f=(revertSign?-1:1)*phys->normalForce.dot(n);
- //Real f=phys->normalForce.norm();
- Fmean+=f;
- }
- Fmean/=count;
-
- #ifdef YADE_OPENGL
- Gl1_NormPhys::maxWeakFn=Fmean;
- #endif
-
- // *** Weak and strong fabric tensors ***/
- // evaluate two different parts of the fabric tensor
- // make distinction between strong and weak network of contact forces
- fabricStrong=Matrix3r::Zero();
- fabricWeak=Matrix3r::Zero();
- int nStrong(0), nWeak(0); // number of strong and weak contacts respectively
- if (!splitTensor & !isnan(thresholdForce)) {LOG_WARN("The bool splitTensor should be set to True if you specified a threshold value for the contact force, otherwise the function will return only the fabric tensor and not the two separate contributions.");}
- FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
- if(!I->isReal()) continue;
- GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(I->geom.get());
- NormShearPhys* phys=YADE_CAST<NormShearPhys*>(I->phys.get());
- const Vector3r& n=geom->normal;
- Real f=(revertSign?-1:1)*phys->normalForce.dot(n);
- // slipt the tensor according to the mean contact force or a threshold value if this is given
- Real Fsplit=(!isnan(thresholdForce))?thresholdForce:Fmean;
- if (revertSign?(f<Fsplit):(f>Fsplit)){ // reminder: forces are compared with their sign
- for(int i=0; i<3; i++) for(int j=i; j<3; j++){
- fabricStrong(i,j)+=n[i]*n[j];
- }
- nStrong++;
- }
- else{
- for(int i=0; i<3; i++) for(int j=i; j<3; j++){
- fabricWeak(i,j)+=n[i]*n[j];
- }
- nWeak++;
- }
- }
- // fill terms under the diagonal
- fabricStrong(1,0)=fabricStrong(0,1); fabricStrong(2,0)=fabricStrong(0,2); fabricStrong(2,1)=fabricStrong(1,2);
- fabricWeak(1,0)=fabricWeak(0,1); fabricWeak(2,0)=fabricWeak(0,2); fabricWeak(2,1)=fabricWeak(1,2);
- fabricStrong/=nStrong;
- fabricWeak/=nWeak;
-
- // *** Compute total fabric tensor from the two tensors above ***/
- Matrix3r fabricTot(Matrix3r::Zero());
- int q(0);
- if(!count==0){ // compute only if there are some interactions
- q=nStrong*1./count;
- fabricTot=(1-q)*fabricWeak+q*fabricStrong;
- }
-}
-
-py::tuple Shop::fabricTensor(bool splitTensor, bool revertSign, Real thresholdForce){
- Real Fmean; Matrix3r fabric, fabricStrong, fabricWeak;
- fabricTensor(Fmean,fabric,fabricStrong,fabricWeak,splitTensor,revertSign,thresholdForce);
- // returns fabric tensor or alternatively the two distinct contributions according to strong and weak subnetworks (or, if thresholdForce is specified, the distinction is made according to that value and not the mean one)
- if (!splitTensor){return py::make_tuple(fabric);}
- else{return py::make_tuple(fabricStrong,fabricWeak);}
-}
-
-Matrix3r Shop::getStress(Real volume){
- Scene* scene=Omega::instance().getScene().get();
- if (volume==0) volume = scene->isPeriodic?scene->cell->hSize.determinant():1;
- Matrix3r stressTensor = Matrix3r::Zero();
- const bool isPeriodic = scene->isPeriodic;
- FOREACH(const shared_ptr<Interaction>&I, *scene->interactions){
- if (!I->isReal()) continue;
- shared_ptr<Body> b1 = Body::byId(I->getId1(),scene);
- shared_ptr<Body> b2 = Body::byId(I->getId2(),scene);
- if (b1->shape->getClassIndex()==GridNode::getClassIndexStatic()) continue; //no need to check b2 because a GridNode can only be in interaction with an oher GridNode.
- NormShearPhys* nsi=YADE_CAST<NormShearPhys*> ( I->phys.get() );
- Vector3r branch=b1->state->pos -b2->state->pos;
- if (isPeriodic) branch-= scene->cell->hSize*I->cellDist.cast<Real>();
- stressTensor += (nsi->normalForce+nsi->shearForce)*branch.transpose();
- }
- return stressTensor/volume;
-}
-
-Matrix3r Shop::getCapillaryStress(Real volume){
- Scene* scene=Omega::instance().getScene().get();
- if (volume==0) volume = scene->isPeriodic?scene->cell->hSize.determinant():1;
- Matrix3r stressTensor = Matrix3r::Zero();
- const bool isPeriodic = scene->isPeriodic;
- FOREACH(const shared_ptr<Interaction>&I, *scene->interactions){
- if (!I->isReal()) continue;
- shared_ptr<Body> b1 = Body::byId(I->getId1(),scene);
- shared_ptr<Body> b2 = Body::byId(I->getId2(),scene);
- CapillaryPhys* nsi=YADE_CAST<CapillaryPhys*> ( I->phys.get() );
- Vector3r branch=b1->state->pos -b2->state->pos;
- if (isPeriodic) branch-= scene->cell->hSize*I->cellDist.cast<Real>();
- stressTensor += (nsi->fCap)*branch.transpose();
- }
- return stressTensor/volume;
-}
-
-/*
-Matrix3r Shop::stressTensorOfPeriodicCell(bool smallStrains){
- Scene* scene=Omega::instance().getScene().get();
- if (!scene->isPeriodic){ throw runtime_error("Can't compute stress of periodic cell in aperiodic simulation."); }
-// if (smallStrains){volume = scene->getVolume()cell->refSize[0]*scene->cell->refSize[1]*scene->cell->refSize[2];}
-// else volume = scene->cell->trsf.determinant()*scene->cell->refSize[0]*scene->cell->refSize[1]*scene->cell->refSize[2];
- // Using the function provided by Cell (BC)
- Real volume = scene->cell->getVolume();
- Matrix3r stress = Matrix3r::Zero();
- FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
- if(!I->isReal()) continue;
- Dem3DofGeom* geom=YADE_CAST<Dem3DofGeom*>(I->geom.get());
- NormShearPhys* phys=YADE_CAST<NormShearPhys*>(I->phys.get());
- Real l;
- if (smallStrains){l = geom->refLength;}
- else l=(geom->se31.position-geom->se32.position).norm();
- Vector3r& n=geom->normal;
- Vector3r& fT=phys->shearForce;
- Real fN=phys->normalForce.dot(n);
-
- stress += l*(fN*n*n.transpose() + .5*(fT*n.transpose() + n*fT.transpose()));
- }
- stress/=volume;
- return stress;
-}
-*/
-
-void Shop::getStressLWForEachBody(vector<Matrix3r>& bStresses){
- const shared_ptr<Scene>& scene=Omega::instance().getScene();
- bStresses.resize(scene->bodies->size());
- for (size_t k=0;k<scene->bodies->size();k++) bStresses[k]=Matrix3r::Zero();
- FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
- if(!I->isReal()) continue;
- GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(I->geom.get());
- NormShearPhys* phys=YADE_CAST<NormShearPhys*>(I->phys.get());
- Vector3r f=phys->normalForce+phys->shearForce;
- //Sum f_i*l_j/V for each contact of each particle
- bStresses[I->getId1()]-=(3.0/(4.0*Mathr::PI*pow(geom->refR1,3)))*f*((geom->contactPoint-Body::byId(I->getId1(),scene)->state->pos).transpose());
- if (!scene->isPeriodic)
- bStresses[I->getId2()]+=(3.0/(4.0*Mathr::PI*pow(geom->refR2,3)))*f*((geom->contactPoint- (Body::byId(I->getId2(),scene)->state->pos)).transpose());
- else
- bStresses[I->getId2()]+=(3.0/(4.0*Mathr::PI*pow(geom->refR2,3)))*f* ((geom->contactPoint- (Body::byId(I->getId2(),scene)->state->pos + (scene->cell->hSize*I->cellDist.cast<Real>()))).transpose());
- }
-}
-
-py::list Shop::getStressLWForEachBody(){
- py::list ret;
- vector<Matrix3r> bStresses;
- getStressLWForEachBody(bStresses);
- FOREACH(const Matrix3r& m, bStresses) ret.append(m);
- return ret;
-// return py::make_tuple(bStresses);
-}
-
-void Shop::calm(const shared_ptr<Scene>& _scene, int mask){
- const shared_ptr<Scene> scene=(_scene?_scene:Omega::instance().getScene());
- FOREACH(shared_ptr<Body> b, *scene->bodies){
- if (!b || !b->isDynamic()) continue;
- if(((mask>0) and ((b->groupMask & mask)==0))) continue;
- Sphere* s=dynamic_cast<Sphere*>(b->shape.get());
- if ( (s) or ( (!s) && (b->isClump()) ) ){
- b->state->vel=Vector3r::Zero();
- b->state->angVel=Vector3r::Zero();
- b->state->angMom=Vector3r::Zero();
- }
- }
-}
-
-py::list Shop::getBodyIdsContacts(Body::id_t bodyID) {
- py::list ret;
- if (bodyID < 0) {
- throw std::logic_error("BodyID should be a positive value!");
- }
-
- const shared_ptr<Scene>& scene=Omega::instance().getScene();
- const shared_ptr<Body>& b = Body::byId(bodyID,scene);
-
- for(Body::MapId2IntrT::iterator it=b->intrs.begin(),end=b->intrs.end(); it!=end; ++it) {
- ret.append((*it).first);
- }
- return ret;
-}
-
-void Shop::setContactFriction(Real angleRad){
- Scene* scene = Omega::instance().getScene().get();
- shared_ptr<BodyContainer>& bodies = scene->bodies;
- FOREACH(const shared_ptr<Body>& b,*scene->bodies){
- if(b->isClump()) continue;
- if (b->isDynamic())
- YADE_PTR_CAST<FrictMat> (b->material)->frictionAngle = angleRad;
- }
- FOREACH(const shared_ptr<Interaction>& ii, *scene->interactions){
- if (!ii->isReal()) continue;
- const shared_ptr<FrictMat>& sdec1 = YADE_PTR_CAST<FrictMat>((*bodies)[(Body::id_t) ((ii)->getId1())]->material);
- const shared_ptr<FrictMat>& sdec2 = YADE_PTR_CAST<FrictMat>((*bodies)[(Body::id_t) ((ii)->getId2())]->material);
- //FIXME - why dynamic_cast fails here?
- FrictPhys* contactPhysics = YADE_CAST<FrictPhys*>((ii)->phys.get());
- const Real& fa = sdec1->frictionAngle;
- const Real& fb = sdec2->frictionAngle;
- contactPhysics->tangensOfFrictionAngle = std::tan(std::min(fa,fb));
- }
-}
-
-void Shop::growParticles(Real multiplier, bool updateMass, bool dynamicOnly)
-{
- Scene* scene = Omega::instance().getScene().get();
- FOREACH(const shared_ptr<Body>& b,*scene->bodies){
- if (dynamicOnly && !b->isDynamic()) continue;
- int ci=b->shape->getClassIndex();
- if(b->isClump() || ci==GridNode::getClassIndexStatic() || ci==GridConnection::getClassIndexStatic()) continue;
- if (updateMass) {b->state->mass*=pow(multiplier,3); b->state->inertia*=pow(multiplier,5);}
- (YADE_CAST<Sphere*> (b->shape.get()))->radius *= multiplier;
- // Clump volume variation with homothetic displacement from its center
- if (b->isClumpMember()) b->state->pos += (multiplier-1) * (b->state->pos - Body::byId(b->clumpId, scene)->state->pos);
- }
- FOREACH(const shared_ptr<Body>& b,*scene->bodies){
- if(b->isClump()){
- Clump* clumpSt = YADE_CAST<Clump*>(b->shape.get());
- clumpSt->updateProperties(b);
- }
- }
- FOREACH(const shared_ptr<Interaction>& ii, *scene->interactions){
- int ci=(*(scene->bodies))[ii->getId1()]->shape->getClassIndex();
- if(ci==GridNode::getClassIndexStatic() || ci==GridConnection::getClassIndexStatic()) continue;
- if (ii->isReal()) {
- GenericSpheresContact* contact = YADE_CAST<GenericSpheresContact*>(ii->geom.get());
- if (!dynamicOnly || (*(scene->bodies))[ii->getId1()]->isDynamic())
- contact->refR1 = YADE_CAST<Sphere*>((* (scene->bodies))[ii->getId1()]->shape.get())->radius;
- if (!dynamicOnly || (*(scene->bodies))[ii->getId2()]->isDynamic())
- contact->refR2 = YADE_CAST<Sphere*>((* (scene->bodies))[ii->getId2()]->shape.get())->radius;
- const shared_ptr<FrictPhys>& contactPhysics = YADE_PTR_CAST<FrictPhys>(ii->phys);
- contactPhysics->kn*=multiplier; contactPhysics->ks*=multiplier;
- }
-
- }
-}
-
=== modified file 'pkg/dem/Shop.hpp'
--- pkg/dem/Shop.hpp 2013-05-30 20:19:43 +0000
+++ pkg/dem/Shop.hpp 2013-10-30 23:25:56 +0000
@@ -9,9 +9,9 @@
#include<boost/any.hpp>
#include<boost/lambda/lambda.hpp>
-#include<yade/lib/base/Math.hpp>
-#include<yade/lib/base/Logging.hpp>
-#include<yade/core/Body.hpp>
+#include "yade/lib/base/Math.hpp"
+#include "yade/lib/base/Logging.hpp"
+#include "yade/core/Body.hpp"
#include<boost/function.hpp>
@@ -84,6 +84,10 @@
//! Get unbalanced force of the whole simulation
static Real unbalancedForce(bool useMaxForce=false, Scene* _rb=NULL);
static Real kineticEnergy(Scene* _rb=NULL, Body::id_t* maxId=NULL);
+ //! get total momentum of current simulation
+ static Vector3r momentum();
+ //! get total angular momentum of current simulation
+ static Vector3r angularMomentum(Vector3r origin = Vector3r::Zero());
static Vector3r totalForceInVolume(Real& avgIsoStiffness, Scene *_rb=NULL);
@@ -123,8 +127,6 @@
static Matrix3r getStress(Real volume=0);
static Matrix3r getCapillaryStress(Real volume=0);
static Matrix3r stressTensorOfPeriodicCell() { LOG_WARN("Shop::stressTensorOfPeriodicCelli is DEPRECATED: use getStress instead"); return Shop::getStress(); }
- //! This version is restricted to periodic BCs and Dem3Dof
- static Matrix3r stressTensorOfPeriodicCell(bool smallStrains=true);
//! Compute overall ("macroscopic") stress of periodic cell, returning 2 tensors
//! (contribution of normal and shear forces)
static py::tuple normalShearStressTensors(bool compressionPositive=false, bool splitNormalTensor=false, Real thresholdForce=NaN);
@@ -143,7 +145,7 @@
static void setContactFriction(Real angleRad);
//! Homothetic change of sizes of spheres and clumps
- static void growParticles(Real multiplier, bool updateMass, bool dynamicOnly);
-
-
+ static void growParticles(Real multiplier, bool updateMass, bool dynamicOnly, unsigned int discretization, bool integrateInertia);
+ //! Change of size of a single sphere or a clump
+ static void growParticle(Body::id_t bodyID, Real multiplier, bool updateMass);
};
=== added file 'pkg/dem/Shop_01.cpp'
--- pkg/dem/Shop_01.cpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/Shop_01.cpp 2013-10-23 09:49:41 +0000
@@ -0,0 +1,455 @@
+// 2007 © Václav Šmilauer <eudoxos@xxxxxxxx>
+#include"Shop.hpp"
+
+#include<boost/filesystem/convenience.hpp>
+#include<boost/tokenizer.hpp>
+#include<boost/tuple/tuple.hpp>
+
+#include"yade/core/Scene.hpp"
+#include"yade/core/Body.hpp"
+#include"yade/core/Interaction.hpp"
+
+#include"yade/pkg/common/Aabb.hpp"
+#include"yade/core/Clump.hpp"
+#include"yade/pkg/common/InsertionSortCollider.hpp"
+
+#include"yade/pkg/common/Box.hpp"
+#include"yade/pkg/common/Sphere.hpp"
+#include"yade/pkg/common/ElastMat.hpp"
+#include"yade/pkg/dem/ViscoelasticPM.hpp"
+#include"yade/pkg/dem/CapillaryPhys.hpp"
+
+#include"yade/pkg/common/Bo1_Sphere_Aabb.hpp"
+#include"yade/pkg/common/Bo1_Box_Aabb.hpp"
+#include"yade/pkg/dem/NewtonIntegrator.hpp"
+#include"yade/pkg/dem/Ig2_Sphere_Sphere_ScGeom.hpp"
+#include"yade/pkg/dem/Ig2_Box_Sphere_ScGeom.hpp"
+#include"yade/pkg/dem/Ip2_FrictMat_FrictMat_FrictPhys.hpp"
+
+#include"yade/pkg/common/ForceResetter.hpp"
+
+#include"yade/pkg/common/Dispatching.hpp"
+#include"yade/pkg/common/InteractionLoop.hpp"
+#include"yade/pkg/common/GravityEngines.hpp"
+
+#include"yade/pkg/dem/GlobalStiffnessTimeStepper.hpp"
+#include"yade/pkg/dem/ElasticContactLaw.hpp"
+
+#include"yade/pkg/dem/ScGeom.hpp"
+#include"yade/pkg/dem/FrictPhys.hpp"
+
+#include"yade/pkg/common/Grid.hpp"
+
+#include"yade/pkg/dem/Tetra.hpp"
+
+#ifdef YADE_OPENGL
+ #include"yade/pkg/common/Gl1_NormPhys.hpp"
+#endif
+
+#include<boost/foreach.hpp>
+#ifndef FOREACH
+ #define FOREACH BOOST_FOREACH
+#endif
+
+CREATE_LOGGER(Shop);
+
+/*! Flip periodic cell by given number of cells.
+
+Still broken, some interactions are missed. Should be checked.
+*/
+
+Matrix3r Shop::flipCell(const Matrix3r& _flip){
+ Scene* scene=Omega::instance().getScene().get(); const shared_ptr<Cell>& cell(scene->cell); const Matrix3r& trsf(cell->trsf);
+ Vector3r size=cell->getSize();
+ Matrix3r flip;
+ if(_flip==Matrix3r::Zero()){
+ bool hasNonzero=false;
+ for(int i=0; i<3; i++) for(int j=0; j<3; j++) {
+ if(i==j){ flip(i,j)=0; continue; }
+ flip(i,j)=-floor(.5+trsf(i,j)/(size[j]/size[i]));
+ if(flip(i,j)!=0) hasNonzero=true;
+ }
+ if(!hasNonzero) {LOG_TRACE("No flip necessary."); return Matrix3r::Zero();}
+ LOG_DEBUG("Computed flip matrix: upper "<<flip(0,1)<<","<<flip(0,2)<<","<<flip(1,2)<<"; lower "<<flip(1,0)<<","<<flip(2,0)<<","<<flip(2,1));
+ } else {
+ flip=_flip;
+ }
+
+ // current cell coords of bodies
+ vector<Vector3i > oldCells; oldCells.resize(scene->bodies->size());
+ FOREACH(const shared_ptr<Body>& b, *scene->bodies){
+ if(!b) continue; cell->wrapShearedPt(b->state->pos,oldCells[b->getId()]);
+ }
+
+ // change cell trsf here
+ Matrix3r trsfInc;
+ for(int i=0; i<3; i++) for(int j=0; j<3; j++){
+ if(i==j) { if(flip(i,j)!=0) LOG_WARN("Non-zero diagonal term at ["<<i<<","<<j<<"] is meaningless and will be ignored."); trsfInc(i,j)=0; continue; }
+ // make sure non-diagonal entries are "integers"
+ if(flip(i,j)!=double(int(flip(i,j)))) LOG_WARN("Flip matrix entry "<<flip(i,j)<<" at ["<<i<<","<<j<<"] not integer?! (will be rounded)");
+ trsfInc(i,j)=int(flip(i,j))*size[j]/size[i];
+ }
+ TRWM3MAT(cell->trsf);
+ TRWM3MAT(trsfInc);
+ cell->trsf+=trsfInc;
+ cell->postLoad(*cell);
+
+ // new cell coords of bodies
+ vector<Vector3i > newCells; newCells.resize(scene->bodies->size());
+ FOREACH(const shared_ptr<Body>& b, *scene->bodies){
+ if(!b) continue;
+ cell->wrapShearedPt(b->state->pos,newCells[b->getId()]);
+ }
+
+ // remove all potential interactions
+ scene->interactions->eraseNonReal();
+ // adjust Interaction::cellDist for real interactions;
+ FOREACH(const shared_ptr<Interaction>& i, *scene->interactions){
+ Body::id_t id1=i->getId1(),id2=i->getId2();
+ // this must be the same for both old and new interaction: cell2-cell1+cellDist
+ // c₂-c₁+c₁₂=k; c'₂+c₁'+c₁₂'=k (new cell coords have ')
+ // c₁₂'=(c₂-c₁+c₁₂)-(c₂'-c₁')
+ i->cellDist=(oldCells[id2]-oldCells[id1]+i->cellDist)-(newCells[id2]-newCells[id1]);
+ }
+
+
+ // force reinitialization of the collider
+ bool colliderFound=false;
+ FOREACH(const shared_ptr<Engine>& e, scene->engines){
+ Collider* c=dynamic_cast<Collider*>(e.get());
+ if(c){ colliderFound=true; c->invalidatePersistentData(); }
+ }
+ if(!colliderFound) LOG_WARN("No collider found while flipping cell; continuing simulation might give garbage results.");
+ return flip;
+}
+
+/* Apply force on contact point to 2 bodies; the force is oriented as it applies on the first body and is reversed on the second.
+ */
+void Shop::applyForceAtContactPoint(const Vector3r& force, const Vector3r& contPt, Body::id_t id1, const Vector3r& pos1, Body::id_t id2, const Vector3r& pos2, Scene* scene){
+ scene->forces.addForce(id1,force);
+ scene->forces.addForce(id2,-force);
+ scene->forces.addTorque(id1,(contPt-pos1).cross(force));
+ scene->forces.addTorque(id2,-(contPt-pos2).cross(force));
+}
+
+
+/*! Compute sum of forces in the whole simulation and averages stiffness.
+
+Designed for being used with periodic cell, where diving the resulting components by
+areas of the cell will give average stress in that direction.
+
+Requires all .isReal() interaction to have phys deriving from NormShearPhys.
+*/
+Vector3r Shop::totalForceInVolume(Real& avgIsoStiffness, Scene* _rb){
+ Scene* rb=_rb ? _rb : Omega::instance().getScene().get();
+ Vector3r force(Vector3r::Zero()); Real stiff=0; long n=0;
+ FOREACH(const shared_ptr<Interaction>&I, *rb->interactions){
+ if(!I->isReal()) continue;
+ NormShearPhys* nsi=YADE_CAST<NormShearPhys*>(I->phys.get());
+ force+=Vector3r(abs(nsi->normalForce[0]+nsi->shearForce[0]),abs(nsi->normalForce[1]+nsi->shearForce[1]),abs(nsi->normalForce[2]+nsi->shearForce[2]));
+ stiff+=(1/3.)*nsi->kn+(2/3.)*nsi->ks; // count kn in one direction and ks in the other two
+ n++;
+ }
+ avgIsoStiffness= n>0 ? (1./n)*stiff : -1;
+ return force;
+}
+
+Real Shop::unbalancedForce(bool useMaxForce, Scene* _rb){
+ Scene* rb=_rb ? _rb : Omega::instance().getScene().get();
+ rb->forces.sync();
+ shared_ptr<NewtonIntegrator> newton;
+ Vector3r gravity = Vector3r::Zero();
+ FOREACH(shared_ptr<Engine>& e, rb->engines){ newton=dynamic_pointer_cast<NewtonIntegrator>(e); if(newton) {gravity=newton->gravity; break;} }
+ // get maximum force on a body and sum of all forces (for averaging)
+ Real sumF=0,maxF=0,currF; int nb=0;
+ FOREACH(const shared_ptr<Body>& b, *rb->bodies){
+ if(!b || b->isClumpMember() || !b->isDynamic()) continue;
+ currF=(rb->forces.getForce(b->id)+b->state->mass*gravity).norm();
+ if(b->isClump() && currF==0){ // this should not happen unless the function is called by an engine whose position in the loop is before Newton (with the exception of bodies which really have null force), because clumps forces are updated in Newton. Typical triaxial loops are using such ordering unfortunately (triaxEngine before Newton). So, here we make sure that they will get correct unbalance. In the future, it is better for optimality to check unbalancedF inside scripts at the end of loops, so that this "if" is never active.
+ Vector3r f(rb->forces.getForce(b->id)),m(Vector3r::Zero());
+ b->shape->cast<Clump>().addForceTorqueFromMembers(b->state.get(),rb,f,m);
+ currF=(f+b->state->mass*gravity).norm();
+ }
+ maxF=max(currF,maxF); sumF+=currF; nb++;
+ }
+ Real meanF=sumF/nb;
+ // get mean force on interactions
+ sumF=0; nb=0;
+ FOREACH(const shared_ptr<Interaction>& I, *rb->interactions){
+ if(!I->isReal()) continue;
+ shared_ptr<NormShearPhys> nsi=YADE_PTR_CAST<NormShearPhys>(I->phys); assert(nsi);
+ sumF+=(nsi->normalForce+nsi->shearForce).norm(); nb++;
+ }
+ sumF/=nb;
+ return (useMaxForce?maxF:meanF)/(sumF);
+}
+
+Real Shop::kineticEnergy(Scene* _scene, Body::id_t* maxId){
+ Scene* scene=_scene ? _scene : Omega::instance().getScene().get();
+ Real ret=0.;
+ Real maxE=0; if(maxId) *maxId=Body::ID_NONE;
+ FOREACH(const shared_ptr<Body>& b, *scene->bodies){
+ if(!b || !b->isDynamic()) continue;
+ const State* state(b->state.get());
+ // ½(mv²+ωIω)
+ Real E=0;
+ if(scene->isPeriodic){
+ /* Only take in account the fluctuation velocity, not the mean velocity of homothetic resize. */
+ E=.5*state->mass*scene->cell->bodyFluctuationVel(state->pos,state->vel,scene->cell->velGrad).squaredNorm();
+ } else {
+ E=.5*(state->mass*state->vel.squaredNorm());
+ }
+ if(b->isAspherical()){
+ Matrix3r T(state->ori);
+ // the tensorial expression http://en.wikipedia.org/wiki/Moment_of_inertia#Moment_of_inertia_tensor
+ // inertia tensor rotation from http://www.kwon3d.com/theory/moi/triten.html
+ Matrix3r mI; mI<<state->inertia[0],0,0, 0,state->inertia[1],0, 0,0,state->inertia[2];
+ //E+=.5*state->angVel.transpose().dot((T.transpose()*mI*T)*state->angVel);
+ E+=.5*state->angVel.transpose().dot((T*mI*T.transpose())*state->angVel);
+ }
+ else { E+=0.5*state->angVel.dot(state->inertia.cwiseProduct(state->angVel));}
+ if(maxId && E>maxE) { *maxId=b->getId(); maxE=E; }
+ ret+=E;
+ }
+ return ret;
+}
+
+Vector3r Shop::momentum() {
+ Vector3r ret = Vector3r::Zero();
+ Scene* scene=Omega::instance().getScene().get();
+ FOREACH(const shared_ptr<Body> b, *scene->bodies){
+ ret += b->state->mass * b->state->vel;
+ }
+ return ret;
+}
+
+Vector3r Shop::angularMomentum(Vector3r origin) {
+ Vector3r ret = Vector3r::Zero();
+ Scene* scene=Omega::instance().getScene().get();
+ Matrix3r T,Iloc;
+ FOREACH(const shared_ptr<Body> b, *scene->bodies){
+ ret += (b->state->pos-origin).cross(b->state->mass * b->state->vel);
+ ret += b->state->angMom;
+ }
+ return ret;
+}
+
+
+shared_ptr<FrictMat> Shop::defaultGranularMat(){
+ shared_ptr<FrictMat> mat(new FrictMat);
+ mat->density=2e3;
+ mat->young=30e9;
+ mat->poisson=.3;
+ mat->frictionAngle=.5236; //30˚
+ return mat;
+}
+
+/*! Create body - sphere. */
+shared_ptr<Body> Shop::sphere(Vector3r center, Real radius, shared_ptr<Material> mat){
+ shared_ptr<Body> body(new Body);
+ body->material=mat ? mat : static_pointer_cast<Material>(defaultGranularMat());
+ body->state->pos=center;
+ body->state->mass=4.0/3.0*Mathr::PI*radius*radius*radius*body->material->density;
+ body->state->inertia=Vector3r(2.0/5.0*body->state->mass*radius*radius,2.0/5.0*body->state->mass*radius*radius,2.0/5.0*body->state->mass*radius*radius);
+ body->bound=shared_ptr<Aabb>(new Aabb);
+ body->shape=shared_ptr<Sphere>(new Sphere(radius));
+ return body;
+}
+
+/*! Create body - box. */
+shared_ptr<Body> Shop::box(Vector3r center, Vector3r extents, shared_ptr<Material> mat){
+ shared_ptr<Body> body(new Body);
+ body->material=mat ? mat : static_pointer_cast<Material>(defaultGranularMat());
+ body->state->pos=center;
+ Real mass=8.0*extents[0]*extents[1]*extents[2]*body->material->density;
+ body->state->mass=mass;
+ body->state->inertia=Vector3r(mass*(4*extents[1]*extents[1]+4*extents[2]*extents[2])/12.,mass*(4*extents[0]*extents[0]+4*extents[2]*extents[2])/12.,mass*(4*extents[0]*extents[0]+4*extents[1]*extents[1])/12.);
+ body->bound=shared_ptr<Aabb>(new Aabb);
+ body->shape=shared_ptr<Box>(new Box(extents));
+ return body;
+}
+
+/*! Create body - tetrahedron. */
+shared_ptr<Body> Shop::tetra(Vector3r v_global[4], shared_ptr<Material> mat){
+ shared_ptr<Body> body(new Body);
+ body->material=mat ? mat : static_pointer_cast<Material>(defaultGranularMat());
+ Vector3r centroid=(v_global[0]+v_global[1]+v_global[2]+v_global[3])*.25;
+ Vector3r v[4]; for(int i=0; i<4; i++) v[i]=v_global[i]-centroid;
+ body->state->pos=centroid;
+ body->state->mass=body->material->density*TetrahedronVolume(v);
+ // inertia will be calculated below, by TetrahedronWithLocalAxesPrincipal
+ body->bound=shared_ptr<Aabb>(new Aabb);
+ body->shape=shared_ptr<Tetra>(new Tetra(v[0],v[1],v[2],v[3]));
+ // make local axes coincident with principal axes
+ TetrahedronWithLocalAxesPrincipal(body);
+ return body;
+}
+
+
+void Shop::saveSpheresToFile(string fname){
+ const shared_ptr<Scene>& scene=Omega::instance().getScene();
+ ofstream f(fname.c_str());
+ if(!f.good()) throw runtime_error("Unable to open file `"+fname+"'");
+
+ FOREACH(shared_ptr<Body> b, *scene->bodies){
+ if (!b->isDynamic()) continue;
+ shared_ptr<Sphere> intSph=dynamic_pointer_cast<Sphere>(b->shape);
+ if(!intSph) continue;
+ const Vector3r& pos=b->state->pos;
+ f<<pos[0]<<" "<<pos[1]<<" "<<pos[2]<<" "<<intSph->radius<<endl; // <<" "<<1<<" "<<1<<endl;
+ }
+ f.close();
+}
+
+Real Shop::getSpheresVolume(const shared_ptr<Scene>& _scene, int mask){
+ const shared_ptr<Scene> scene=(_scene?_scene:Omega::instance().getScene());
+ Real vol=0;
+ FOREACH(shared_ptr<Body> b, *scene->bodies){
+ if (!b || !b->isDynamic()) continue;
+ Sphere* s=dynamic_cast<Sphere*>(b->shape.get());
+ if((!s) or ((mask>0) and ((b->groupMask & mask)==0))) continue;
+ vol += (4/3.)*Mathr::PI*pow(s->radius,3);
+ }
+ return vol;
+}
+
+Real Shop::getSpheresMass(const shared_ptr<Scene>& _scene, int mask){
+ const shared_ptr<Scene> scene=(_scene?_scene:Omega::instance().getScene());
+ Real mass=0;
+ FOREACH(shared_ptr<Body> b, *scene->bodies){
+ if (!b || !b->isDynamic()) continue;
+ Sphere* s=dynamic_cast<Sphere*>(b->shape.get());
+ if((!s) or ((mask>0) and ((b->groupMask & mask)==0))) continue;
+ mass += b->state->mass;
+ }
+ return mass;
+}
+
+Real Shop::getPorosity(const shared_ptr<Scene>& _scene, Real _volume){
+ const shared_ptr<Scene> scene=(_scene?_scene:Omega::instance().getScene());
+ Real V;
+ if(!scene->isPeriodic){
+ if(_volume<=0) throw std::invalid_argument("utils.porosity must be given (positive) *volume* for aperiodic simulations.");
+ V=_volume;
+ } else {
+ V=scene->cell->getVolume();
+ }
+ Real Vs=Shop::getSpheresVolume();
+ return (V-Vs)/V;
+}
+
+Real Shop::getVoxelPorosity(const shared_ptr<Scene>& _scene, int _resolution, Vector3r _start,Vector3r _end){
+ const shared_ptr<Scene> scene=(_scene?_scene:Omega::instance().getScene());
+ if(_start==_end) throw std::invalid_argument("utils.voxelPorosity: cannot calculate porosity when start==end of the volume box.");
+ if(_resolution<50) throw std::invalid_argument("utils.voxelPorosity: it doesn't make sense to calculate porosity with voxel resolution below 50.");
+
+ // prepare the gird, it eats a lot of memory.
+ // I am not optimizing for using bits. A single byte for each cell is used.
+ std::vector<std::vector<std::vector<unsigned char> > > grid;
+ int S(_resolution);
+ grid.resize(S);
+ for(int i=0 ; i<S ; ++i) {
+ grid[i].resize(S);
+ for(int j=0 ; j<S ; ++j) {
+ grid[i][j].resize(S,0);
+ }
+ }
+
+ Vector3r start(_start), size(_end - _start);
+
+ FOREACH(shared_ptr<Body> bi, *scene->bodies){
+ if((bi)->isClump()) continue;
+ const shared_ptr<Body>& b = bi;
+ if ( b->isDynamic() || b->isClumpMember() ) {
+ const shared_ptr<Sphere>& sphere = YADE_PTR_CAST<Sphere> ( b->shape );
+ Real r = sphere->radius;
+ Real rr = r*r;
+ Vector3r pos = b->state->se3.position;
+ // we got sphere with radius r, at position pos.
+ // and a box of size S, scaled to 'size'
+ // mark cells that are iniside a sphere
+ int ii(0),II(S),jj(0),JJ(S),kk(0),KK(S);
+ // make sure to loop only in AABB of that sphere. No need to waste cycles outside of it.
+ ii = std::max((int)((Real)(pos[0]-start[0]-r)*(Real)(S)/(Real)(size[0]))-1,0); II = std::min(ii+(int)((Real)(2.0*r)*(Real)(S)/(Real)(size[0]))+3,S);
+ jj = std::max((int)((Real)(pos[1]-start[1]-r)*(Real)(S)/(Real)(size[1]))-1,0); JJ = std::min(jj+(int)((Real)(2.0*r)*(Real)(S)/(Real)(size[1]))+3,S);
+ kk = std::max((int)((Real)(pos[2]-start[2]-r)*(Real)(S)/(Real)(size[2]))-1,0); KK = std::min(kk+(int)((Real)(2.0*r)*(Real)(S)/(Real)(size[2]))+3,S);
+ for(int i=ii ; i<II ; ++i) {
+ for(int j=jj ; j<JJ ; ++j) {
+ for(int k=kk ; k<KK ; ++k) {
+ Vector3r a(i,j,k);
+ a = a/(Real)(S);
+ Vector3r b( a[0]*size[0] , a[1]*size[1] , a[2]*size[2] );
+ b = b+start;
+ Vector3r c(0,0,0);
+ c = pos-b;
+ Real x = c[0];
+ Real y = c[1];
+ Real z = c[2];
+ if(x*x+y*y+z*z < rr)
+ grid[i][j][k]=1;
+ }}}
+ }
+ }
+
+ Real Vv=0;
+ for(int i=0 ; i<S ; ++i ) {
+ for(int j=0 ; j<S ; ++j ) {
+ for(int k=0 ; k<S ; ++k ) {
+ if(grid[i][j][k]==1)
+ Vv += 1.0;
+ }
+ }
+ }
+
+ return ( std::pow(S,3) - Vv ) / std::pow(S,3);
+};
+
+vector<tuple<Vector3r,Real,int> > Shop::loadSpheresFromFile(const string& fname, Vector3r& minXYZ, Vector3r& maxXYZ, Vector3r* cellSize){
+ if(!boost::filesystem::exists(fname)) {
+ throw std::invalid_argument(string("File with spheres `")+fname+"' doesn't exist.");
+ }
+ vector<tuple<Vector3r,Real,int> > spheres;
+ ifstream sphereFile(fname.c_str());
+ if(!sphereFile.good()) throw std::runtime_error("File with spheres `"+fname+"' couldn't be opened.");
+ Vector3r C;
+ Real r=0;
+ int clumpId=-1;
+ string line;
+ size_t lineNo=0;
+ while(std::getline(sphereFile, line, '\n')){
+ lineNo++;
+ boost::tokenizer<boost::char_separator<char> > toks(line,boost::char_separator<char>(" \t"));
+ vector<string> tokens; FOREACH(const string& s, toks) tokens.push_back(s);
+ if(tokens.empty()) continue;
+ if(tokens[0]=="##PERIODIC::"){
+ if(tokens.size()!=4) throw std::invalid_argument(("Spheres file "+fname+":"+lexical_cast<string>(lineNo)+" contains ##PERIODIC::, but the line is malformed.").c_str());
+ if(cellSize){ *cellSize=Vector3r(lexical_cast<Real>(tokens[1]),lexical_cast<Real>(tokens[2]),lexical_cast<Real>(tokens[3])); }
+ continue;
+ }
+ if(tokens.size()!=5 and tokens.size()!=4) throw std::invalid_argument(("Line "+lexical_cast<string>(lineNo)+" in the spheres file "+fname+" has "+lexical_cast<string>(tokens.size())+" columns (must be 4 or 5).").c_str());
+ C=Vector3r(lexical_cast<Real>(tokens[0]),lexical_cast<Real>(tokens[1]),lexical_cast<Real>(tokens[2]));
+ r=lexical_cast<Real>(tokens[3]);
+ for(int j=0; j<3; j++) { minXYZ[j]=(spheres.size()>0?min(C[j]-r,minXYZ[j]):C[j]-r); maxXYZ[j]=(spheres.size()>0?max(C[j]+r,maxXYZ[j]):C[j]+r);}
+ if(tokens.size()==5)clumpId=lexical_cast<int>(tokens[4]);
+ spheres.push_back(tuple<Vector3r,Real,int>(C,r,clumpId));
+ }
+ return spheres;
+}
+
+Real Shop::PWaveTimeStep(const shared_ptr<Scene> _rb){
+ shared_ptr<Scene> rb=(_rb?_rb:Omega::instance().getScene());
+ Real dt=std::numeric_limits<Real>::infinity();
+ FOREACH(const shared_ptr<Body>& b, *rb->bodies){
+ if(!b || !b->material || !b->shape) continue;
+ shared_ptr<ElastMat> ebp=dynamic_pointer_cast<ElastMat>(b->material);
+ shared_ptr<Sphere> s=dynamic_pointer_cast<Sphere>(b->shape);
+ if(!ebp || !s) continue;
+ Real density=b->state->mass/((4/3.)*Mathr::PI*pow(s->radius,3));
+ dt=min(dt,s->radius/sqrt(ebp->young/density));
+ }
+ if (dt==std::numeric_limits<Real>::infinity()) {
+ dt = 1.0;
+ LOG_WARN("PWaveTimeStep has not found any suitable spherical body to calculate dt. dt is set to 1.0");
+ }
+ return dt;
+}
=== added file 'pkg/dem/Shop_02.cpp'
--- pkg/dem/Shop_02.cpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/Shop_02.cpp 2013-10-30 23:25:56 +0000
@@ -0,0 +1,526 @@
+// 2007 © Václav Šmilauer <eudoxos@xxxxxxxx>
+#include"Shop.hpp"
+
+#include<limits>
+
+#include<boost/filesystem/convenience.hpp>
+#include<boost/tokenizer.hpp>
+#include<boost/tuple/tuple.hpp>
+
+#include<yade/core/Scene.hpp>
+#include<yade/core/Body.hpp>
+#include<yade/core/Interaction.hpp>
+
+#include<yade/pkg/common/Aabb.hpp>
+#include<yade/core/Clump.hpp>
+#include<yade/pkg/common/InsertionSortCollider.hpp>
+
+#include<yade/pkg/common/Box.hpp>
+#include<yade/pkg/common/Sphere.hpp>
+#include<yade/pkg/common/ElastMat.hpp>
+#include<yade/pkg/dem/ViscoelasticPM.hpp>
+#include<yade/pkg/dem/CapillaryPhys.hpp>
+
+#include<yade/pkg/common/Bo1_Sphere_Aabb.hpp>
+#include<yade/pkg/common/Bo1_Box_Aabb.hpp>
+#include<yade/pkg/dem/NewtonIntegrator.hpp>
+#include<yade/pkg/dem/Ig2_Sphere_Sphere_ScGeom.hpp>
+#include<yade/pkg/dem/Ig2_Box_Sphere_ScGeom.hpp>
+#include<yade/pkg/dem/Ip2_FrictMat_FrictMat_FrictPhys.hpp>
+
+#include<yade/pkg/common/ForceResetter.hpp>
+
+#include<yade/pkg/common/Dispatching.hpp>
+#include<yade/pkg/common/InteractionLoop.hpp>
+#include<yade/pkg/common/GravityEngines.hpp>
+
+#include<yade/pkg/dem/GlobalStiffnessTimeStepper.hpp>
+#include<yade/pkg/dem/ElasticContactLaw.hpp>
+
+#include<yade/pkg/dem/ScGeom.hpp>
+#include<yade/pkg/dem/FrictPhys.hpp>
+
+#include<yade/pkg/common/Grid.hpp>
+
+#include<yade/pkg/dem/Tetra.hpp>
+
+#ifdef YADE_OPENGL
+ #include<yade/pkg/common/Gl1_NormPhys.hpp>
+#endif
+
+#include<boost/foreach.hpp>
+#ifndef FOREACH
+ #define FOREACH BOOST_FOREACH
+#endif
+
+CREATE_LOGGER(Shop);
+
+/*! Flip periodic cell by given number of cells.
+
+Still broken, some interactions are missed. Should be checked.
+*/
+
+/* Detremination of time step as according to Rayleigh wave speed of force propagation (see Thornton 2000, ref. MillerPursey_1955) */
+Real Shop::RayleighWaveTimeStep(const shared_ptr<Scene> _rb){
+ shared_ptr<Scene> rb=(_rb?_rb:Omega::instance().getScene());
+ Real dt=std::numeric_limits<Real>::infinity();
+ FOREACH(const shared_ptr<Body>& b, *rb->bodies){
+ if(!b || !b->material || !b->shape) continue;
+
+ shared_ptr<ElastMat> ebp=dynamic_pointer_cast<ElastMat>(b->material);
+ shared_ptr<Sphere> s=dynamic_pointer_cast<Sphere>(b->shape);
+ if(!ebp || !s) continue;
+
+ Real density=b->state->mass/((4/3.)*Mathr::PI*pow(s->radius,3));
+ Real ShearModulus=ebp->young/(2.*(1+ebp->poisson));
+ Real lambda=0.1631*ebp->poisson+0.876605;
+ dt=min(dt,Mathr::PI*s->radius/lambda*sqrt(density/ShearModulus));
+ }
+ return dt;
+}
+
+/* Project 3d point into 2d using spiral projection along given axis;
+ * the returned tuple is
+ *
+ * (height relative to the spiral, distance from axis, theta )
+ *
+ * dH_dTheta is the inclination of the spiral (height increase per radian),
+ * theta0 is the angle for zero height (by given axis).
+ */
+boost::tuple<Real,Real,Real> Shop::spiralProject(const Vector3r& pt, Real dH_dTheta, int axis, Real periodStart, Real theta0){
+ int ax1=(axis+1)%3,ax2=(axis+2)%3;
+ Real r=sqrt(pow(pt[ax1],2)+pow(pt[ax2],2));
+ Real theta;
+ if(r>Mathr::ZERO_TOLERANCE){
+ theta=acos(pt[ax1]/r);
+ if(pt[ax2]<0) theta=Mathr::TWO_PI-theta;
+ }
+ else theta=0;
+ Real hRef=dH_dTheta*(theta-theta0);
+ long period;
+ if(isnan(periodStart)){
+ Real h=Shop::periodicWrap(pt[axis]-hRef,hRef-Mathr::PI*dH_dTheta,hRef+Mathr::PI*dH_dTheta,&period);
+ return boost::make_tuple(r,h,theta);
+ }
+ else{
+ // Real hPeriodStart=(periodStart-theta0)*dH_dTheta;
+ //TRVAR4(hPeriodStart,periodStart,theta0,theta);
+ //Real h=Shop::periodicWrap(pt[axis]-hRef,hPeriodStart,hPeriodStart+2*Mathr::PI*dH_dTheta,&period);
+ theta=Shop::periodicWrap(theta,periodStart,periodStart+2*Mathr::PI,&period);
+ Real h=pt[axis]-hRef+period*2*Mathr::PI*dH_dTheta;
+ //TRVAR3(pt[axis],pt[axis]-hRef,period);
+ //TRVAR2(h,theta);
+ return boost::make_tuple(r,h,theta);
+ }
+}
+
+shared_ptr<Interaction> Shop::createExplicitInteraction(Body::id_t id1, Body::id_t id2, bool force){
+ IGeomDispatcher* geomMeta=NULL;
+ IPhysDispatcher* physMeta=NULL;
+ shared_ptr<Scene> rb=Omega::instance().getScene();
+ if(rb->interactions->find(Body::id_t(id1),Body::id_t(id2))!=0) throw runtime_error(string("Interaction #")+lexical_cast<string>(id1)+"+#"+lexical_cast<string>(id2)+" already exists.");
+ FOREACH(const shared_ptr<Engine>& e, rb->engines){
+ if(!geomMeta) { geomMeta=dynamic_cast<IGeomDispatcher*>(e.get()); if(geomMeta) continue; }
+ if(!physMeta) { physMeta=dynamic_cast<IPhysDispatcher*>(e.get()); if(physMeta) continue; }
+ InteractionLoop* id(dynamic_cast<InteractionLoop*>(e.get()));
+ if(id){ geomMeta=id->geomDispatcher.get(); physMeta=id->physDispatcher.get(); }
+ if(geomMeta&&physMeta){break;}
+ }
+ if(!geomMeta) throw runtime_error("No IGeomDispatcher in engines or inside InteractionLoop.");
+ if(!physMeta) throw runtime_error("No IPhysDispatcher in engines or inside InteractionLoop.");
+ shared_ptr<Body> b1=Body::byId(id1,rb), b2=Body::byId(id2,rb);
+ if(!b1) throw runtime_error(("No body #"+lexical_cast<string>(id1)).c_str());
+ if(!b2) throw runtime_error(("No body #"+lexical_cast<string>(id2)).c_str());
+ shared_ptr<Interaction> i=geomMeta->explicitAction(b1,b2,/*force*/force);
+ assert(force && i);
+ if(!i) return i;
+ physMeta->explicitAction(b1->material,b2->material,i);
+ i->iterMadeReal=rb->iter;
+ rb->interactions->insert(i);
+ return i;
+}
+
+Vector3r Shop::inscribedCircleCenter(const Vector3r& v0, const Vector3r& v1, const Vector3r& v2)
+{
+ return v0+((v2-v0)*(v1-v0).norm()+(v1-v0)*(v2-v0).norm())/((v1-v0).norm()+(v2-v1).norm()+(v0-v2).norm());
+}
+
+void Shop::getViscoelasticFromSpheresInteraction( Real tc, Real en, Real es, shared_ptr<ViscElMat> b)
+{
+ b->kn = 1/tc/tc * ( Mathr::PI*Mathr::PI + pow(log(en),2) );
+ b->cn = -2.0 /tc * log(en);
+ b->ks = 2.0/7.0 /tc/tc * ( Mathr::PI*Mathr::PI + pow(log(es),2) );
+ b->cs = -2.0/7.0 /tc * log(es);
+
+ if (abs(b->cn) <= Mathr::ZERO_TOLERANCE ) b->cn=0;
+ if (abs(b->cs) <= Mathr::ZERO_TOLERANCE ) b->cs=0;
+}
+
+/* This function is copied almost verbatim from scientific python, module Visualization, class ColorScale
+ *
+ */
+Vector3r Shop::scalarOnColorScale(Real x, Real xmin, Real xmax){
+ Real xnorm=min((Real)1.,max((x-xmin)/(xmax-xmin),(Real)0.));
+ if(xnorm<.25) return Vector3r(0,4.*xnorm,1);
+ if(xnorm<.5) return Vector3r(0,1,1.-4.*(xnorm-.25));
+ if(xnorm<.75) return Vector3r(4*(xnorm-.5),1.,0);
+ return Vector3r(1,1-4*(xnorm-.75),0);
+}
+
+/* Wrap floating point number into interval (x0,x1〉such that it is shifted
+ * by integral number of the interval range. If given, *period will hold
+ * this number. The wrapped value is returned.
+ */
+Real Shop::periodicWrap(Real x, Real x0, Real x1, long* period){
+ Real xNorm=(x-x0)/(x1-x0);
+ Real xxNorm=xNorm-floor(xNorm);
+ if(period) *period=(long)floor(xNorm);
+ return x0+xxNorm*(x1-x0);
+}
+
+void Shop::getStressForEachBody(vector<Shop::bodyState>& bodyStates){
+ const shared_ptr<Scene>& scene=Omega::instance().getScene();
+ bodyStates.resize(scene->bodies->size());
+ FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
+ Vector3r normalStress,shearStress;
+ if(!I->isReal()) continue;
+
+ const FrictPhys* physFP = YADE_CAST<FrictPhys*>(I->phys.get());
+ ScGeom* geomScG=YADE_CAST<ScGeom*>(I->geom.get());
+
+ const Body::id_t id1=I->getId1(), id2=I->getId2();
+
+ if((physFP) and (geomScG)){
+ Real minRad=(geomScG->radius1<=0?geomScG->radius2:(geomScG->radius2<=0?geomScG->radius1:min(geomScG->radius1,geomScG->radius2)));
+ Real crossSection=Mathr::PI*pow(minRad,2);
+
+ normalStress=((1./crossSection)*geomScG->normal.dot(physFP->normalForce))*geomScG->normal;
+ for(int i=0; i<3; i++){
+ int ix1=(i+1)%3,ix2=(i+2)%3;
+ shearStress[i]=geomScG->normal[ix1]*physFP->shearForce[ix1]+geomScG->normal[ix2]*physFP->shearForce[ix2];
+ shearStress[i]/=crossSection;
+ }
+ bodyStates[id1].normStress+=normalStress;
+ bodyStates[id2].normStress+=normalStress;
+ bodyStates[id1].shearStress+=shearStress;
+ bodyStates[id2].shearStress+=shearStress;
+ }
+ }
+}
+
+/* Return the stress tensor decomposed in 2 contributions, from normal and shear forces.
+The formulation follows the [Thornton2000]_ article
+"Numerical simulations of deviatoric shear deformation of granular media", eq (3) and (4)
+ */
+py::tuple Shop::normalShearStressTensors(bool compressionPositive, bool splitNormalTensor, Real thresholdForce){
+
+ //*** Stress tensor split into shear and normal contribution ***/
+ Scene* scene=Omega::instance().getScene().get();
+ if (!scene->isPeriodic){ throw runtime_error("Can't compute stress of periodic cell in aperiodic simulation."); }
+ Matrix3r sigN(Matrix3r::Zero()),sigT(Matrix3r::Zero());
+ //const Matrix3r& cellHsize(scene->cell->Hsize); //Disabled because of warning.
+ FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
+ if(!I->isReal()) continue;
+ GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(I->geom.get());
+ NormShearPhys* phys=YADE_CAST<NormShearPhys*>(I->phys.get());
+ const Vector3r& n=geom->normal;
+ // if compression has positive sign, we need to change sign for both normal and shear force
+ // make copy to Fs since shearForce is used at multiple places (less efficient, but avoids confusion)
+ Vector3r Fs=(compressionPositive?-1:1)*phys->shearForce;
+ Real N=(compressionPositive?-1:1)*phys->normalForce.dot(n), T=Fs.norm();
+ bool hasShear=(T>0);
+ Vector3r t; if(hasShear) t=Fs/T;
+ // Real R=(Body::byId(I->getId2(),scene)->state->pos+cellHsize*I->cellDist.cast<Real>()-Body::byId(I->getId1(),scene)->state->pos).norm();
+ Real R=.5*(geom->refR1+geom->refR2);
+ for(int i=0; i<3; i++) for(int j=i; j<3; j++){
+ sigN(i,j)+=R*N*n[i]*n[j];
+ if(hasShear) sigT(i,j)+=R*T*n[i]*t[j];
+ }
+ }
+ Real vol=scene->cell->getVolume();
+ sigN*=2/vol; sigT*=2/vol;
+ // fill terms under the diagonal
+ sigN(1,0)=sigN(0,1); sigN(2,0)=sigN(0,2); sigN(2,1)=sigN(1,2);
+ sigT(1,0)=sigT(0,1); sigT(2,0)=sigT(0,2); sigT(2,1)=sigT(1,2);
+
+ // *** Normal stress tensor split into two parts according to subnetworks of strong and weak forces (or other distinction if a threshold value for the force is assigned) ***/
+ Real Fmean(0); Matrix3r f, fs, fw;
+ fabricTensor(Fmean,f,fs,fw,false,compressionPositive,NaN);
+ Matrix3r sigNStrong(Matrix3r::Zero()), sigNWeak(Matrix3r::Zero());
+ FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
+ if(!I->isReal()) continue;
+ GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(I->geom.get());
+ NormShearPhys* phys=YADE_CAST<NormShearPhys*>(I->phys.get());
+ const Vector3r& n=geom->normal;
+ Real N=(compressionPositive?-1:1)*phys->normalForce.dot(n);
+ // Real R=(Body::byId(I->getId2(),scene)->state->pos+cellHsize*I->cellDist.cast<Real>()-Body::byId(I->getId1(),scene)->state->pos).norm();
+ Real R=.5*(geom->refR1+geom->refR2);
+ Real Fsplit=(!isnan(thresholdForce))?thresholdForce:Fmean;
+ if (compressionPositive?(N<Fsplit):(N>Fsplit)){
+ for(int i=0; i<3; i++) for(int j=i; j<3; j++){
+ sigNStrong(i,j)+=R*N*n[i]*n[j];}
+ }
+ else{
+ for(int i=0; i<3; i++) for(int j=i; j<3; j++){
+ sigNWeak(i,j)+=R*N*n[i]*n[j];}
+ }
+ }
+ sigNStrong*=2/vol; sigNWeak*=2/vol;
+ // fill terms under the diagonal
+ sigNStrong(1,0)=sigNStrong(0,1); sigNStrong(2,0)=sigNStrong(0,2); sigNStrong(2,1)=sigNStrong(1,2);
+ sigNWeak(1,0)=sigNWeak(0,1); sigNWeak(2,0)=sigNWeak(0,2); sigNWeak(2,1)=sigNWeak(1,2);
+
+ /// tensile forces are taken as positive!
+ if (splitNormalTensor){return py::make_tuple(sigNStrong,sigNWeak);} // return strong-weak or tensile-compressive parts of the stress tensor (only normal part)
+ return py::make_tuple(sigN,sigT); // return normal and shear components
+}
+
+/* Return the fabric tensor as according to [Satake1982]. */
+/* as side-effect, set Gl1_NormShear::strongWeakThresholdForce */
+void Shop::fabricTensor(Real& Fmean, Matrix3r& fabric, Matrix3r& fabricStrong, Matrix3r& fabricWeak, bool splitTensor, bool revertSign, Real thresholdForce){
+ Scene* scene=Omega::instance().getScene().get();
+ if (!scene->isPeriodic){ throw runtime_error("Can't compute fabric tensor of periodic cell in aperiodic simulation."); }
+
+ // *** Fabric tensor ***/
+ fabric=Matrix3r::Zero();
+ int count=0; // number of interactions
+ FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
+ if(!I->isReal()) continue;
+ GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(I->geom.get());
+ const Vector3r& n=geom->normal;
+ for(int i=0; i<3; i++) for(int j=i; j<3; j++){
+ fabric(i,j)+=n[i]*n[j];
+ }
+ count++;
+ }
+ // fill terms under the diagonal
+ fabric(1,0)=fabric(0,1); fabric(2,0)=fabric(0,2); fabric(2,1)=fabric(1,2);
+ fabric/=count;
+
+ // *** Average contact force ***/
+ // calculate average contact force
+ Fmean=0; // initialize
+ FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
+ if(!I->isReal()) continue;
+ GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(I->geom.get());
+ NormShearPhys* phys=YADE_CAST<NormShearPhys*>(I->phys.get());
+ const Vector3r& n=geom->normal;
+ Real f=(revertSign?-1:1)*phys->normalForce.dot(n);
+ //Real f=phys->normalForce.norm();
+ Fmean+=f;
+ }
+ Fmean/=count;
+
+ #ifdef YADE_OPENGL
+ Gl1_NormPhys::maxWeakFn=Fmean;
+ #endif
+
+ // *** Weak and strong fabric tensors ***/
+ // evaluate two different parts of the fabric tensor
+ // make distinction between strong and weak network of contact forces
+ fabricStrong=Matrix3r::Zero();
+ fabricWeak=Matrix3r::Zero();
+ int nStrong(0), nWeak(0); // number of strong and weak contacts respectively
+ if (!splitTensor & !isnan(thresholdForce)) {LOG_WARN("The bool splitTensor should be set to True if you specified a threshold value for the contact force, otherwise the function will return only the fabric tensor and not the two separate contributions.");}
+ FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
+ if(!I->isReal()) continue;
+ GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(I->geom.get());
+ NormShearPhys* phys=YADE_CAST<NormShearPhys*>(I->phys.get());
+ const Vector3r& n=geom->normal;
+ Real f=(revertSign?-1:1)*phys->normalForce.dot(n);
+ // slipt the tensor according to the mean contact force or a threshold value if this is given
+ Real Fsplit=(!isnan(thresholdForce))?thresholdForce:Fmean;
+ if (revertSign?(f<Fsplit):(f>Fsplit)){ // reminder: forces are compared with their sign
+ for(int i=0; i<3; i++) for(int j=i; j<3; j++){
+ fabricStrong(i,j)+=n[i]*n[j];
+ }
+ nStrong++;
+ }
+ else{
+ for(int i=0; i<3; i++) for(int j=i; j<3; j++){
+ fabricWeak(i,j)+=n[i]*n[j];
+ }
+ nWeak++;
+ }
+ }
+ // fill terms under the diagonal
+ fabricStrong(1,0)=fabricStrong(0,1); fabricStrong(2,0)=fabricStrong(0,2); fabricStrong(2,1)=fabricStrong(1,2);
+ fabricWeak(1,0)=fabricWeak(0,1); fabricWeak(2,0)=fabricWeak(0,2); fabricWeak(2,1)=fabricWeak(1,2);
+ fabricStrong/=nStrong;
+ fabricWeak/=nWeak;
+
+ // *** Compute total fabric tensor from the two tensors above ***/
+ Matrix3r fabricTot(Matrix3r::Zero());
+ int q(0);
+ if(!count==0){ // compute only if there are some interactions
+ q=nStrong*1./count;
+ fabricTot=(1-q)*fabricWeak+q*fabricStrong;
+ }
+}
+
+py::tuple Shop::fabricTensor(bool splitTensor, bool revertSign, Real thresholdForce){
+ Real Fmean; Matrix3r fabric, fabricStrong, fabricWeak;
+ fabricTensor(Fmean,fabric,fabricStrong,fabricWeak,splitTensor,revertSign,thresholdForce);
+ // returns fabric tensor or alternatively the two distinct contributions according to strong and weak subnetworks (or, if thresholdForce is specified, the distinction is made according to that value and not the mean one)
+ if (!splitTensor){return py::make_tuple(fabric);}
+ else{return py::make_tuple(fabricStrong,fabricWeak);}
+}
+
+Matrix3r Shop::getStress(Real volume){
+ Scene* scene=Omega::instance().getScene().get();
+ if (volume==0) volume = scene->isPeriodic?scene->cell->hSize.determinant():1;
+ Matrix3r stressTensor = Matrix3r::Zero();
+ const bool isPeriodic = scene->isPeriodic;
+ FOREACH(const shared_ptr<Interaction>&I, *scene->interactions){
+ if (!I->isReal()) continue;
+ shared_ptr<Body> b1 = Body::byId(I->getId1(),scene);
+ shared_ptr<Body> b2 = Body::byId(I->getId2(),scene);
+ if (b1->shape->getClassIndex()==GridNode::getClassIndexStatic()) continue; //no need to check b2 because a GridNode can only be in interaction with an oher GridNode.
+ NormShearPhys* nsi=YADE_CAST<NormShearPhys*> ( I->phys.get() );
+ Vector3r branch=b1->state->pos -b2->state->pos;
+ if (isPeriodic) branch-= scene->cell->hSize*I->cellDist.cast<Real>();
+ stressTensor += (nsi->normalForce+nsi->shearForce)*branch.transpose();
+ }
+ return stressTensor/volume;
+}
+
+Matrix3r Shop::getCapillaryStress(Real volume){
+ Scene* scene=Omega::instance().getScene().get();
+ if (volume==0) volume = scene->isPeriodic?scene->cell->hSize.determinant():1;
+ Matrix3r stressTensor = Matrix3r::Zero();
+ const bool isPeriodic = scene->isPeriodic;
+ FOREACH(const shared_ptr<Interaction>&I, *scene->interactions){
+ if (!I->isReal()) continue;
+ shared_ptr<Body> b1 = Body::byId(I->getId1(),scene);
+ shared_ptr<Body> b2 = Body::byId(I->getId2(),scene);
+ CapillaryPhys* nsi=YADE_CAST<CapillaryPhys*> ( I->phys.get() );
+ Vector3r branch=b1->state->pos -b2->state->pos;
+ if (isPeriodic) branch-= scene->cell->hSize*I->cellDist.cast<Real>();
+ stressTensor += (nsi->fCap)*branch.transpose();
+ }
+ return stressTensor/volume;
+}
+
+void Shop::getStressLWForEachBody(vector<Matrix3r>& bStresses){
+ const shared_ptr<Scene>& scene=Omega::instance().getScene();
+ bStresses.resize(scene->bodies->size());
+ for (size_t k=0;k<scene->bodies->size();k++) bStresses[k]=Matrix3r::Zero();
+ FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
+ if(!I->isReal()) continue;
+ GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(I->geom.get());
+ NormShearPhys* phys=YADE_CAST<NormShearPhys*>(I->phys.get());
+ Vector3r f=phys->normalForce+phys->shearForce;
+ //Sum f_i*l_j/V for each contact of each particle
+ bStresses[I->getId1()]-=(3.0/(4.0*Mathr::PI*pow(geom->refR1,3)))*f*((geom->contactPoint-Body::byId(I->getId1(),scene)->state->pos).transpose());
+ if (!scene->isPeriodic)
+ bStresses[I->getId2()]+=(3.0/(4.0*Mathr::PI*pow(geom->refR2,3)))*f*((geom->contactPoint- (Body::byId(I->getId2(),scene)->state->pos)).transpose());
+ else
+ bStresses[I->getId2()]+=(3.0/(4.0*Mathr::PI*pow(geom->refR2,3)))*f* ((geom->contactPoint- (Body::byId(I->getId2(),scene)->state->pos + (scene->cell->hSize*I->cellDist.cast<Real>()))).transpose());
+ }
+}
+
+py::list Shop::getStressLWForEachBody(){
+ py::list ret;
+ vector<Matrix3r> bStresses;
+ getStressLWForEachBody(bStresses);
+ FOREACH(const Matrix3r& m, bStresses) ret.append(m);
+ return ret;
+// return py::make_tuple(bStresses);
+}
+
+void Shop::calm(const shared_ptr<Scene>& _scene, int mask){
+ const shared_ptr<Scene> scene=(_scene?_scene:Omega::instance().getScene());
+ FOREACH(shared_ptr<Body> b, *scene->bodies){
+ if (!b || !b->isDynamic()) continue;
+ if(((mask>0) and ((b->groupMask & mask)==0))) continue;
+ Sphere* s=dynamic_cast<Sphere*>(b->shape.get());
+ if ( (s) or ( (!s) && (b->isClump()) ) ){
+ b->state->vel=Vector3r::Zero();
+ b->state->angVel=Vector3r::Zero();
+ b->state->angMom=Vector3r::Zero();
+ }
+ }
+}
+
+py::list Shop::getBodyIdsContacts(Body::id_t bodyID) {
+ py::list ret;
+ if (bodyID < 0) {
+ throw std::logic_error("BodyID should be a positive value!");
+ }
+
+ const shared_ptr<Scene>& scene=Omega::instance().getScene();
+ const shared_ptr<Body>& b = Body::byId(bodyID,scene);
+
+ for(Body::MapId2IntrT::iterator it=b->intrs.begin(),end=b->intrs.end(); it!=end; ++it) {
+ ret.append((*it).first);
+ }
+ return ret;
+}
+
+void Shop::setContactFriction(Real angleRad){
+ Scene* scene = Omega::instance().getScene().get();
+ shared_ptr<BodyContainer>& bodies = scene->bodies;
+ FOREACH(const shared_ptr<Body>& b,*scene->bodies){
+ if(b->isClump()) continue;
+ if (b->isDynamic())
+ YADE_PTR_CAST<FrictMat> (b->material)->frictionAngle = angleRad;
+ }
+ FOREACH(const shared_ptr<Interaction>& ii, *scene->interactions){
+ if (!ii->isReal()) continue;
+ const shared_ptr<FrictMat>& sdec1 = YADE_PTR_CAST<FrictMat>((*bodies)[(Body::id_t) ((ii)->getId1())]->material);
+ const shared_ptr<FrictMat>& sdec2 = YADE_PTR_CAST<FrictMat>((*bodies)[(Body::id_t) ((ii)->getId2())]->material);
+ //FIXME - why dynamic_cast fails here?
+ FrictPhys* contactPhysics = YADE_CAST<FrictPhys*>((ii)->phys.get());
+ const Real& fa = sdec1->frictionAngle;
+ const Real& fb = sdec2->frictionAngle;
+ contactPhysics->tangensOfFrictionAngle = std::tan(std::min(fa,fb));
+ }
+}
+
+void Shop::growParticles(Real multiplier, bool updateMass, bool dynamicOnly, unsigned int discretization, bool integrateInertia)
+{
+ Scene* scene = Omega::instance().getScene().get();
+ FOREACH(const shared_ptr<Body>& b,*scene->bodies){
+ if (dynamicOnly && !b->isDynamic()) continue;
+ int ci=b->shape->getClassIndex();
+ if(b->isClump() || ci==GridNode::getClassIndexStatic() || ci==GridConnection::getClassIndexStatic()) continue;
+ if (updateMass) {b->state->mass*=pow(multiplier,3); b->state->inertia*=pow(multiplier,5);}
+ (YADE_CAST<Sphere*> (b->shape.get()))->radius *= multiplier;
+ // Clump volume variation with homothetic displacement from its center
+ if (b->isClumpMember()) b->state->pos += (multiplier-1) * (b->state->pos - Body::byId(b->clumpId, scene)->state->pos);
+ }
+ FOREACH(const shared_ptr<Body>& b,*scene->bodies){
+ if(b->isClump()){
+ Clump* clumpSt = YADE_CAST<Clump*>(b->shape.get());
+ clumpSt->updateProperties(b, discretization, integrateInertia);
+ }
+ }
+ FOREACH(const shared_ptr<Interaction>& ii, *scene->interactions){
+ int ci=(*(scene->bodies))[ii->getId1()]->shape->getClassIndex();
+ if(ci==GridNode::getClassIndexStatic() || ci==GridConnection::getClassIndexStatic()) continue;
+ if (ii->isReal()) {
+ GenericSpheresContact* contact = YADE_CAST<GenericSpheresContact*>(ii->geom.get());
+ if (!dynamicOnly || (*(scene->bodies))[ii->getId1()]->isDynamic())
+ contact->refR1 = YADE_CAST<Sphere*>((* (scene->bodies))[ii->getId1()]->shape.get())->radius;
+ if (!dynamicOnly || (*(scene->bodies))[ii->getId2()]->isDynamic())
+ contact->refR2 = YADE_CAST<Sphere*>((* (scene->bodies))[ii->getId2()]->shape.get())->radius;
+ const shared_ptr<FrictPhys>& contactPhysics = YADE_PTR_CAST<FrictPhys>(ii->phys);
+ contactPhysics->kn*=multiplier; contactPhysics->ks*=multiplier;
+ }
+
+ }
+}
+
+void Shop::growParticle(Body::id_t bodyID, Real multiplier, bool updateMass)
+{
+ const shared_ptr<Body>& b = Body::byId(bodyID);
+ Real& rad = YADE_CAST<Sphere*>(b->shape.get())->radius;
+ rad *= multiplier;
+ if (updateMass) {b->state->mass*=pow(multiplier,3); b->state->inertia*=pow(multiplier,5);}
+ for(Body::MapId2IntrT::iterator it=b->intrs.begin(),end=b->intrs.end(); it!=end; ++it) { //Iterate over all bodie's interactions
+ if(!(*it).second->isReal()) continue;
+ GenericSpheresContact* contact = YADE_CAST<GenericSpheresContact*>((*it).second->geom.get());
+ if (bodyID==it->second->getId1()) contact->refR1 = rad;
+ else contact->refR2 = rad;
+ }
+}
=== modified file 'pkg/dem/SpherePack.cpp'
--- pkg/dem/SpherePack.cpp 2013-05-23 14:20:26 +0000
+++ pkg/dem/SpherePack.cpp 2013-09-23 17:39:59 +0000
@@ -207,10 +207,10 @@
if (mode!=RDIST_RMEAN) {
//if rMean is not imposed, then we call makeCloud recursively, scaling the PSD down until the target num is obtained
Real nextPoro = porosity+(1-porosity)/10.;
- LOG_WARN("Exceeded "<<maxTry<<" tries to insert non-overlapping sphere to packing. Only "<<i<<" spheres was added, although you requested "<<num<<". Trying again with porosity "<<nextPoro<<". The size distribution is being scaled down");
+ LOG_WARN("Exceeded "<<maxTry<<" tries to insert non-overlapping sphere to packing. Only "<<i<<" spheres were added, although you requested "<<num<<". Trying again with porosity "<<nextPoro<<". The size distribution is being scaled down");
pack.clear();
return makeCloud(mn, mx, -1., rRelFuzz, num, periodic, nextPoro, psdSizes, psdCumm, distributeMass,seed,hSizeFound?hSize:Matrix3r::Zero());}
- else LOG_WARN("Exceeded "<<maxTry<<" tries to insert non-overlapping sphere to packing. Only "<<i<<" spheres was added, although you requested "<<num<<".");
+ else LOG_WARN("Exceeded "<<maxTry<<" tries to insert non-overlapping sphere to packing. Only "<<i<<" spheres were added, although you requested "<<num<<".");
}
return i;}
}
@@ -270,7 +270,7 @@
int bin=int(bins*(2*s.r-minD)/(maxD-minD)); bin=min(bin,bins-1); // to make sure
if (mass) hist[bin]+=pow(s.r,3)/vol; else hist[bin]+=1./N;
}
- for(int i=0; i<bins; i++) cumm[i+1]=min(1.,cumm[i]+hist[i]); // cumm[i+1] is OK, cumm.size()==bins+1
+ for(int i=0; i<bins; i++) cumm[i+1]=min((Real)1.,cumm[i]+hist[i]); // cumm[i+1] is OK, cumm.size()==bins+1
return py::make_tuple(edges,cumm);
}
@@ -365,7 +365,7 @@
if(!overlap) { pack.push_back(Sph(c,r)); break; }
}
if (t==maxTry) {
- if(numbers[ii]>0) LOG_WARN("Exceeded "<<maxTry<<" tries to insert non-overlapping sphere to packing. Only "<<i<<" spheres was added, although you requested "<<numbers[ii]<<" with radius "<<radii[ii]);
+ if(numbers[ii]>0) LOG_WARN("Exceeded "<<maxTry<<" tries to insert non-overlapping sphere to packing. Only "<<i<<" spheres were added, although you requested "<<numbers[ii]<<" with radius "<<radii[ii]);
return i;
}
}
@@ -410,7 +410,7 @@
for(size_t j=0; j<packSize; j++){ if(pow(pack[j].r+r,2) >= (pack[j].c-c).squaredNorm()) { overlap=true; break; } }
} else {
for(size_t j=0; j<packSize; j++){
- Vector3r dr;
+ Vector3r dr=Vector3r::Zero();
for(int axis=0; axis<2; axis++) dr[axis]=min(cellWrapRel(c[axis],pack[j].c[axis],pack[j].c[axis]+size[axis]),cellWrapRel(pack[j].c[axis],c[axis],c[axis]+size[axis]));
if(pow(pack[j].r+r,2)>= dr.squaredNorm()){ overlap=true; break; }
}
@@ -418,7 +418,7 @@
if(!overlap) { pack.push_back(Sph(c,r)); break; }
}
if (t==maxTry) {
- if(numbers[ii]>0) LOG_WARN("Exceeded "<<maxTry<<" tries to insert non-overlapping sphere to packing. Only "<<i<<" spheres was added, although you requested "<<numbers[ii]<<" with radius "<<radii[ii]);
+ if(numbers[ii]>0) LOG_WARN("Exceeded "<<maxTry<<" tries to insert non-overlapping sphere to packing. Only "<<i<<" spheres were added, although you requested "<<numbers[ii]<<" with radius "<<radii[ii]);
return i;
}
}
=== modified file 'pkg/dem/TesselationWrapper.hpp'
--- pkg/dem/TesselationWrapper.hpp 2013-03-05 19:00:17 +0000
+++ pkg/dem/TesselationWrapper.hpp 2013-10-31 00:03:24 +0000
@@ -113,7 +113,7 @@
Finite_edges_iterator facet_it;
MicroMacroAnalyser mma;
- YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(TesselationWrapper,GlobalEngine,"Handle the triangulation of spheres in a scene, build tesselation on request, and give access to computed quantities: currently volume, porosity and local deformation for each sphere. The calculation of microstrain is using a definition similar to the one of [Calvetti1997]_ or [Bagi2006]_\n\nSee example usage in script example/tesselationWrapper.py.\n\nBelow is an output of the :yref:`defToVtk<TesselationWrapper::defToVtk>` function visualized with paraview (in this case Yade's TesselationWrapper was used to process experimental data obtained on sand by Edward Ando at Grenoble University, 3SR lab.)\n\n.. figure:: fig/localstrain.*",
+ YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(TesselationWrapper,GlobalEngine,"Handle the triangulation of spheres in a scene, build tesselation on request, and give access to computed quantities (see also the `dedicated section in user manual <https://yade-dem.org/doc/user.html#micro-stress-and-micro-strain>`_). The calculation of microstrain is explained in [Catalano2013a]_ \n\nSee example usage in script example/tesselationWrapper/tesselationWrapper.py.\n\nBelow is an output of the :yref:`defToVtk<TesselationWrapper::defToVtk>` function visualized with paraview (in this case Yade's TesselationWrapper was used to process experimental data obtained on sand by Edward Ando at Grenoble University, 3SR lab.)\n\n.. figure:: fig/localstrain.*",
((unsigned int,n_spheres,0,,"|ycomp|"))
,/*ctor*/
Tes = new Tesselation;
@@ -126,11 +126,11 @@
,/*py*/
.def("triangulate",&TesselationWrapper::insertSceneSpheres,(python::arg("reset")=true),"triangulate spheres of the packing")
.def("setState",&TesselationWrapper::setState,(python::arg("state")=0),"Make the current state of the simulation the initial (0) or final (1) configuration for the definition of displacement increments, use only state=0 if you just want to get volmumes and porosity.")
- .def("loadState",&TesselationWrapper::loadState,(python::arg("stateNumber")=0),"Load a file with positions to define state 0 or 1.")
+ .def("loadState",&TesselationWrapper::loadState,(python::arg("inputFile")="state",python::arg("state")=0,python::arg("bz2")=true),"Load a file with positions to define state 0 or 1.")
.def("saveState",&TesselationWrapper::saveState,(python::arg("outputFile")="state",python::arg("state")=0,python::arg("bz2")=true),"Save a file with positions, can be later reloaded in order to define state 0 or 1.")
.def("volume",&TesselationWrapper::Volume,(python::arg("id")=0),"Returns the volume of Voronoi's cell of a sphere.")
.def("defToVtk",&TesselationWrapper::defToVtk,(python::arg("outputFile")="def.vtk"),"Write local deformations in vtk format from states 0 and 1.")
- .def("defToVtkFromStates",&TesselationWrapper::defToVtkFromStates,(python::arg("input1")="state1",python::arg("input2")="state2",python::arg("outputFile")="def.vtk",python::arg("bz2")=false),"Write local deformations in vtk format from state files (since the file format is very special, consider using defToVtkFromPositions if the input files were not generated by TesselationWrapper).")
+ .def("defToVtkFromStates",&TesselationWrapper::defToVtkFromStates,(python::arg("input1")="state1",python::arg("input2")="state2",python::arg("outputFile")="def.vtk",python::arg("bz2")=true),"Write local deformations in vtk format from state files (since the file format is very special, consider using defToVtkFromPositions if the input files were not generated by TesselationWrapper).")
.def("defToVtkFromPositions",&TesselationWrapper::defToVtkFromPositions,(python::arg("input1")="pos1",python::arg("input2")="pos2",python::arg("outputFile")="def.vtk",python::arg("bz2")=false),"Write local deformations in vtk format from positions files (one sphere per line, with x,y,z,rad separated by spaces).")
.def("computeVolumes",&TesselationWrapper::ComputeVolumes,"Compute volumes of all Voronoi's cells.")
.def("getVolPoroDef",&TesselationWrapper::getVolPoroDef,(python::arg("deformation")=false),"Return a table with per-sphere computed quantities. Include deformations on the increment defined by states 0 and 1 if deformation=True (make sure to define states 0 and 1 consistently).")
=== modified file 'pkg/dem/Tetra.cpp'
--- pkg/dem/Tetra.cpp 2012-02-16 16:05:15 +0000
+++ pkg/dem/Tetra.cpp 2013-10-04 12:04:43 +0000
@@ -1,9 +1,27 @@
// © 2007 Václav Šmilauer <eudoxos@xxxxxxxx>
+// © 2013 Jan Stránský <jan.stransky@xxxxxxxxxxx>
#include"Tetra.hpp"
-YADE_PLUGIN(/* self-contained in hpp: */ (Tetra) (TTetraGeom) (Bo1_Tetra_Aabb)
- /* some code in cpp (this file): */ (TetraVolumetricLaw) (Ig2_Tetra_Tetra_TTetraGeom)
+#include<yade/core/Interaction.hpp>
+#include<yade/core/Omega.hpp>
+#include<yade/core/Scene.hpp>
+#include<yade/core/State.hpp>
+#include<yade/pkg/common/ElastMat.hpp>
+
+#include<yade/pkg/common/Aabb.hpp>
+
+#ifdef YADE_CGAL
+ #include <CGAL/intersections.h>
+#endif
+
+YADE_PLUGIN(/* self-contained in hpp: */ (Tetra) (TTetraGeom) (TTetraSimpleGeom) (Bo1_Tetra_Aabb)
+ /* some code in cpp (this file): */ (TetraVolumetricLaw)
+ (Ig2_Tetra_Tetra_TTetraGeom)
+ #ifdef YADE_CGAL
+ (Ig2_Tetra_Tetra_TTetraSimpleGeom)
+ (Law2_TTetraSimpleGeom_NormPhys_Simple)
+ #endif
#ifdef YADE_OPENGL
(Gl1_Tetra)
#endif
@@ -11,14 +29,9 @@
Tetra::~Tetra(){}
TTetraGeom::~TTetraGeom(){}
-
-#include<yade/core/Interaction.hpp>
-#include<yade/core/Omega.hpp>
-#include<yade/core/Scene.hpp>
-#include<yade/core/State.hpp>
-#include<yade/pkg/common/ElastMat.hpp>
-
-#include<yade/pkg/common/Aabb.hpp>
+TTetraSimpleGeom::~TTetraSimpleGeom(){}
+
+
void Bo1_Tetra_Aabb::go(const shared_ptr<Shape>& ig, shared_ptr<Bound>& bv, const Se3r& se3, const Body*){
Tetra* t=static_cast<Tetra*>(ig.get());
@@ -32,6 +45,566 @@
#undef __VOP
}
+
+
+
+#ifdef YADE_CGAL
+const int Ig2_Tetra_Tetra_TTetraSimpleGeom::psMap[4][3] = { // segments of point
+ {0,2,3},
+ {0,1,4},
+ {1,2,5},
+ {3,4,5}
+};
+
+const int Ig2_Tetra_Tetra_TTetraSimpleGeom::ptMap[4][3] = { // triangles of point
+ {0,1,3},
+ {0,1,2},
+ {1,2,3},
+ {0,2,3}
+};
+
+const int Ig2_Tetra_Tetra_TTetraSimpleGeom::stMap[6][2] = { // triangles of segments
+ {0,1},
+ {1,2},
+ {1,3},
+ {0,3},
+ {0,2},
+ {2,3}
+};
+
+const int Ig2_Tetra_Tetra_TTetraSimpleGeom::ppsMap[4][4] = { // point-point pair to segment
+ {-1, 0, 2, 3},
+ { 0,-1, 1, 4},
+ { 2, 1,-1, 5},
+ { 3, 4, 5,-1}
+};
+
+const int Ig2_Tetra_Tetra_TTetraSimpleGeom::tsMap[4][3] = { // segmnts of triangle
+ {0,3,4},
+ {0,1,2},
+ {1,4,5},
+ {2,3,5}
+};
+
+const int Ig2_Tetra_Tetra_TTetraSimpleGeom::sstMap[6][6] = { // segment-segment pair to triangle
+ {-1, 1, 1, 0, 0,-1},
+ { 1,-1, 1,-1, 2, 2},
+ { 1, 1,-1, 3,-1, 3},
+ { 0,-1, 3,-1, 0, 3},
+ { 0, 2,-1, 0,-1, 2},
+ {-1, 2, 3, 3, 2,-1}
+};
+
+bool Ig2_Tetra_Tetra_TTetraSimpleGeom::checkVertexToTriangleCase(
+ const Triangle tA[4],
+ const Point pB[4],
+ const Segment sB[6],
+ Vector3r& normal,
+ Vector3r& contactPoint,
+ Real& penetrationVolume)
+{
+ for (int i=0; i<4; i++) { // loop over triangles 1
+ const Triangle& t = tA[i]; // choose triangle 1
+ for (int j=0; j<4; j++) { // loop over vertices 2
+ const Point& p = pB[j]; // choose vertex 2
+ // choose edges posessing p
+ const Segment& sa = sB[psMap[j][0]];
+ const Segment& sb = sB[psMap[j][1]];
+ const Segment& sc = sB[psMap[j][2]];
+ if ( !(do_intersect(t,sa) && do_intersect(t,sb) && do_intersect(t,sc)) ) { continue; }// if all edges intersect with t
+ // evaluate the points
+ CGAL::Object oa = intersection(t,sa);
+ const Point* pa = CGAL::object_cast<Point>(&oa);
+ CGAL::Object ob = intersection(t,sb);
+ const Point* pb = CGAL::object_cast<Point>(&ob);
+ CGAL::Object oc = intersection(t,sc);
+ const Point* pc = CGAL::object_cast<Point>(&oc);
+ if ( !(pa && pb && pc) ) { continue; } // check that the intrsection really exists
+ Vector_3 n = CGAL::normal(t[0],t[1],t[2]); // normal of triangle
+ for (int k=0; k<3; k++) {
+ normal[k] = n[k]; // sets normal of contact = nornal of triangle
+ // contact point is center of mass of overlaping tetrahedron
+ contactPoint[k] = .25*(p[k]+pa->operator[](k)+pb->operator[](k)+pc->operator[](k));
+ }
+ normal.normalize();
+ const Point* v[4] = {&p,pa,pb,pc};
+ penetrationVolume = TetrahedronVolume(v);
+ Real vol = TetrahedronVolume(pB);
+ if (penetrationVolume > .5*vol) { penetrationVolume = vol-penetrationVolume; }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Ig2_Tetra_Tetra_TTetraSimpleGeom::checkEdgeToEdgeCase(
+ const Segment sA[6],
+ const Segment sB[6],
+ const Triangle tA[4],
+ const Triangle tB[4],
+ Vector3r& normal,
+ Vector3r& contactPoint,
+ Real& penetrationVolume)
+{
+ for (int i=0; i<6; i++) {
+ const Segment& sa = sA[i];
+ const Triangle& ta0 = tA[stMap[i][0]];
+ const Triangle& ta1 = tA[stMap[i][1]];
+ for (int j=0; j<6; j++) {
+ const Segment sb = sB[j];
+ if ( !(do_intersect(sb,ta0) && do_intersect(sb,ta1) ) ) { continue; }
+ const Triangle tb0 = tB[stMap[j][0]];
+ const Triangle tb1 = tB[stMap[j][1]];
+ if ( !(do_intersect(sa,tb0) && do_intersect(sa,tb1) ) ) { continue; }
+ CGAL::Object osb1 = intersection(sb,ta0);
+ CGAL::Object osb2 = intersection(sb,ta1);
+ CGAL::Object osa1 = intersection(sa,tb0);
+ CGAL::Object osa2 = intersection(sa,tb1);
+ const Point* psb1 = CGAL::object_cast<Point>(&osb1);
+ const Point* psb2 = CGAL::object_cast<Point>(&osb2);
+ const Point* psa1 = CGAL::object_cast<Point>(&osa1);
+ const Point* psa2 = CGAL::object_cast<Point>(&osa2);
+ if ( !(psb1 && psb2 && psa1 && psa2) ) { continue; }
+ Vector_3 n = CGAL::cross_product(sa.to_vector(),sb.to_vector());
+ Vector3r nApprox;
+ for (int k=0; k<3; k++) {
+ normal[k] = n[k];
+ #define OP(p) p->operator[](k)
+ nApprox[k] = .5*(OP(psa1)+OP(psa2)) - .5*(OP(psb1)+OP(psb2));
+ contactPoint[k] = .25*(OP(psa1)+OP(psa2)+OP(psb1)+OP(psb2));
+ #undef OP
+ }
+ if ( nApprox.dot(normal) < 0 ) { normal *= (Real)-1.; }
+ normal.normalize();
+ const Point* p[4] = {psb1,psb2,psa1,psa2};
+ penetrationVolume = TetrahedronVolume(p);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Ig2_Tetra_Tetra_TTetraSimpleGeom::checkEdgeToTriangleCase1( // edge smaller than triangle
+ const Triangle tA[4],
+ const Segment sB[6],
+ const Point pB[6],
+ Vector3r& normal,
+ Vector3r& contactPoint,
+ Real& penetrationVolume)
+{
+ for (int i=0; i<4; i++) {
+ const Triangle& ta = tA[i];
+ int ni = 0;
+ for (int j=0; j<6; j++) {
+ const Segment& s = sB[j];
+ if ( do_intersect(ta,s) ) { ni++; }
+ }
+ if ( ni != 4 ) { continue; }
+ Vector_3 n = CGAL::normal(ta[0],ta[1],ta[2]);
+ for (int j=0; j<3; j++) {
+ const Point& p1 = pB[j];
+ if ( Vector_3(ta[0],p1)*n > 0.) { continue; }
+ const Segment& s10 = sB[psMap[j][0]];
+ const Segment& s11 = sB[psMap[j][1]];
+ const Segment& s12 = sB[psMap[j][2]];
+ bool b10 = do_intersect(ta,s10);
+ bool b11 = do_intersect(ta,s11);
+ bool b12 = do_intersect(ta,s12);
+ if ( !((b10 && b11) || (b11 && b12) || (b12 && b10) ) ) { continue; }
+ for (int k=j+1; k<4; k++) {
+ const Point& p2 = pB[k];
+ if ( Vector_3(ta[0],p2)*n > 0. ) { continue; }
+ const Segment& s20 = sB[psMap[k][0]];
+ const Segment& s21 = sB[psMap[k][1]];
+ const Segment& s22 = sB[psMap[k][2]];
+ bool b20 = do_intersect(ta,s20);
+ bool b21 = do_intersect(ta,s21);
+ bool b22 = do_intersect(ta,s22);
+ if ( !((b20 && b21) || (b21 && b22) || (b22 && b20) ) ) { continue; }
+ int l,m;
+ for (l=0; l<3; l++) {
+ if (l!=j && l!=k) { break; }
+ }
+ for (m=l+1; m<4; m++) {
+ if (m!=j && m!=k) { break; }
+ }
+ const Segment& s13 = sB[ppsMap[j][l]];
+ const Segment& s14 = sB[ppsMap[j][m]];
+ const Segment& s23 = sB[ppsMap[k][l]];
+ const Segment& s24 = sB[ppsMap[k][m]];
+ CGAL::Object o13 = intersection(ta,s13);
+ CGAL::Object o14 = intersection(ta,s14);
+ CGAL::Object o23 = intersection(ta,s23);
+ CGAL::Object o24 = intersection(ta,s24);
+ const Point* ps13 = CGAL::object_cast<Point>(&o13);
+ const Point* ps14 = CGAL::object_cast<Point>(&o14);
+ const Point* ps23 = CGAL::object_cast<Point>(&o23);
+ const Point* ps24 = CGAL::object_cast<Point>(&o24);
+ if ( !(ps13 && ps14 && ps23 && ps24) ) { continue; }
+ const Point* pp1[4] = {&p1,&p2,ps13,ps14};
+ const Point* pp2[4] = {&p2,ps23,ps24,ps14};
+ const Point* pp3[4] = {&p2,ps23,ps13,ps14};
+ Real v1 = TetrahedronVolume(pp1);
+ Real v2 = TetrahedronVolume(pp2);
+ Real v3 = TetrahedronVolume(pp3);
+ Vector3r cg1,cg2,cg3;
+ for (l=0; l<3; l++) {
+ normal[l] = n[l];
+ #define OP(p) p->operator[](l)
+ cg1[l] = .25*(p1[l]+p2[l]+OP(ps13)+OP(ps14));
+ cg2[l] = .25*(p2[l]+OP(ps23)+OP(ps24)+OP(ps14));
+ cg3[l] = .25*(p2[l]+OP(ps23)+OP(ps13)+OP(ps14));
+ #undef OP
+ }
+ penetrationVolume = v1 + v2 + v3;
+ contactPoint = (v1*cg1 + v2*cg2 + v3*cg3) / penetrationVolume;
+ normal.normalize();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool Ig2_Tetra_Tetra_TTetraSimpleGeom::checkEdgeToTriangleCase2( // edge larger than triangle
+ const Triangle tA[4],
+ const Triangle tB[4],
+ const Segment sA[6],
+ const Segment sB[6],
+ Vector3r& normal,
+ Vector3r& contactPoint,
+ Real& penetrationVolume)
+{
+ for (int i=0; i<6; i++) {
+ const Segment& sb = sB[i];
+ int ni = 0;
+ for (int j=0; j<4; j++) {
+ const Triangle& t = tA[j];
+ if ( do_intersect(t,sb) ) { ni++; }
+ }
+ if ( ni != 2 ) { continue; }
+ for (int j=0; j<3; j++) {
+ const Triangle& ta1 = tA[j];
+ if ( !(do_intersect(ta1,sb) ) ) { continue; }
+ for (int k=j+1; k<4; k++) {
+ const Triangle& ta2 = tA[k];
+ if ( !(do_intersect(ta2,sb) ) ) { continue; }
+ const Triangle& tb1 = tB[stMap[i][0]];
+ const Triangle& tb2 = tB[stMap[i][1]];
+ const Segment& sa1a = sA[tsMap[j][0]];
+ const Segment& sa1b = sA[tsMap[j][1]];
+ const Segment& sa1c = sA[tsMap[j][2]];
+ bool b1a = do_intersect(sa1a,tb1) && do_intersect(sa1a,tb2);
+ bool b1b = do_intersect(sa1b,tb1) && do_intersect(sa1b,tb2);
+ bool b1c = do_intersect(sa1c,tb1) && do_intersect(sa1c,tb2);
+ if ( !(b1a || b1b || b1c) ) { continue; }
+ const Segment& sa2a = sA[tsMap[k][0]];
+ const Segment& sa2b = sA[tsMap[k][1]];
+ const Segment& sa2c = sA[tsMap[k][2]];
+ bool b2a = do_intersect(sa2a,tb1) && do_intersect(sa2a,tb2);
+ bool b2b = do_intersect(sa2b,tb1) && do_intersect(sa2b,tb2);
+ bool b2c = do_intersect(sa2c,tb1) && do_intersect(sa2c,tb2);
+ if ( !(b2a || b2b || b2c) ) { continue; }
+ int l = b1a? tsMap[j][0] : b1b? tsMap[j][1] : tsMap[j][2];
+ int m = b2a? tsMap[k][0] : b2b? tsMap[k][1] : tsMap[k][2];
+ if (sstMap[l][m] == -1) { continue; }
+ const Segment& sa1 = sA[l];
+ const Segment& sa2 = sA[m];
+ const Triangle& taN = tA[sstMap[l][m]];
+ CGAL::Object o1 = intersection(sb,ta1);
+ CGAL::Object o2 = intersection(sb,ta2);
+ CGAL::Object o11 = intersection(sa1,tb1);
+ CGAL::Object o12 = intersection(sa1,tb2);
+ CGAL::Object o21 = intersection(sa2,tb1);
+ CGAL::Object o22 = intersection(sa2,tb2);
+ const Point* p1 = CGAL::object_cast<Point>(&o1);
+ const Point* p2 = CGAL::object_cast<Point>(&o2);
+ const Point* p11 = CGAL::object_cast<Point>(&o11);
+ const Point* p12 = CGAL::object_cast<Point>(&o12);
+ const Point* p21 = CGAL::object_cast<Point>(&o21);
+ const Point* p22 = CGAL::object_cast<Point>(&o22);
+ if ( !(p1 && p2 && p11 && p12 && p21 && p22) ) { continue; }
+ const Point* pp1[4] = {p1,p2,p11,p12};
+ const Point* pp2[4] = {p2,p21,p22,p12};
+ const Point* pp3[4] = {p2,p21,p11,p12};
+ Real v1 = TetrahedronVolume(pp1);
+ Real v2 = TetrahedronVolume(pp2);
+ Real v3 = TetrahedronVolume(pp3);
+ Vector3r cg1,cg2,cg3;
+ Vector_3 n = CGAL::normal(taN[0],taN[1],taN[2]);
+ for (int l=0; l<3; l++) {
+ normal[l] = n[l];
+ #define OP(p) p->operator[](l)
+ cg1[l] = .25*(OP(p1)+OP(p2)+OP(p11)+OP(p12));
+ cg2[l] = .25*(OP(p2)+OP(p21)+OP(p22)+OP(p12));
+ cg3[l] = .25*(OP(p2)+OP(p21)+OP(p11)+OP(p12));
+ #undef OP
+ }
+ penetrationVolume = v1 + v2 + v3;
+ contactPoint = (v1*cg1 + v2*cg2 + v3*cg3) / penetrationVolume;
+ normal.normalize();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool Ig2_Tetra_Tetra_TTetraSimpleGeom::checkVertexToEdgeCase(
+ const Point pA[4],
+ const Segment sA[6],
+ const Triangle tA[4],
+ const Segment sB[6],
+ const Triangle tB[4],
+ Vector3r& normal,
+ Vector3r& contactPoint,
+ Real& penetrationVolume)
+{
+ for (int i=0; i<4; i++) {
+ const Point& pa = pA[i];
+ if ( Vector_3(tB[0][0],pa)*CGAL::normal(tB[0][0],tB[0][1],tB[0][2]) > 0. ) { continue; }
+ if ( Vector_3(tB[1][0],pa)*CGAL::normal(tB[1][0],tB[1][1],tB[1][2]) > 0. ) { continue; }
+ if ( Vector_3(tB[2][0],pa)*CGAL::normal(tB[2][0],tB[2][1],tB[2][2]) > 0. ) { continue; }
+ if ( Vector_3(tB[3][0],pa)*CGAL::normal(tB[3][0],tB[3][1],tB[3][2]) > 0. ) { continue; }
+ const Segment& sa1 = sA[psMap[i][0]];
+ const Segment& sa2 = sA[psMap[i][1]];
+ const Segment& sa3 = sA[psMap[i][2]];
+ for (int j=0; j<6; j++) {
+ const Segment& sb = sB[j];
+ const Triangle& tb1 = tB[stMap[j][0]];
+ const Triangle& tb2 = tB[stMap[j][1]];
+ const Triangle& ta1 = tA[ptMap[i][0]];
+ const Triangle& ta2 = tA[ptMap[i][1]];
+ const Triangle& ta3 = tA[ptMap[i][2]];
+ bool bsa1tb1 = do_intersect(sa1,tb1);
+ bool bsa1tb2 = do_intersect(sa1,tb2);
+ bool bsa2tb1 = do_intersect(sa2,tb1);
+ bool bsa2tb2 = do_intersect(sa2,tb2);
+ bool bsa3tb1 = do_intersect(sa3,tb1);
+ bool bsa3tb2 = do_intersect(sa3,tb2);
+ bool bsbta1 = do_intersect(sb,ta1);
+ bool bsbta2 = do_intersect(sb,ta2);
+ bool bsbta3 = do_intersect(sb,ta3);
+ if ( !( (bsa1tb1 || bsa1tb2) && (bsa2tb1 || bsa2tb2) && (bsa3tb1 || bsa3tb2) && ((bsbta1 && bsbta2) || (bsbta2 && bsbta3) || (bsbta3 && bsbta1)) ) ) { continue; }
+ CGAL::Object oa1 = intersection(sa1,bsa1tb1? tb1 : tb2);
+ CGAL::Object oa2 = intersection(sa2,bsa2tb1? tb1 : tb2);
+ CGAL::Object oa3 = intersection(sa3,bsa3tb1? tb1 : tb2);
+ CGAL::Object ob1 = intersection(sb, (bsbta1 && bsbta2)? ta1 : (bsbta2 && bsbta3)? ta2 : ta3);
+ CGAL::Object ob2 = intersection(sb, (bsbta1 && bsbta2)? ta2 : (bsbta2 && bsbta3)? ta3 : ta1);
+ const Point* pa1 = CGAL::object_cast<Point>(&oa1);
+ const Point* pa2 = CGAL::object_cast<Point>(&oa2);
+ const Point* pa3 = CGAL::object_cast<Point>(&oa3);
+ const Point* pb1 = CGAL::object_cast<Point>(&ob1);
+ const Point* pb2 = CGAL::object_cast<Point>(&ob2);
+ if ( !(pa1 && pa2 && pa3 && pb1 && pb2) ) { continue; }
+ Segment sa(*pa1,*pa2);
+ Real d1 = sqrt(CGAL::squared_distance(sa,*pb1));
+ Real d2 = sqrt(CGAL::squared_distance(sa,*pb2));
+ const Point* ppb1 = d1<d2? pb1 : pb2;
+ const Point* ppb2 = d1<d2? pb2 : pb1;
+ const Point* pp1[4] = {&pa,pa1,pa2,pa3};
+ const Point* pp2[4] = {pa1,pa2,pa3,ppb2};
+ const Point* pp3[4] = {pa1,pa2,ppb1,ppb2};
+ Real v1 = TetrahedronVolume(pp1);
+ Real v2 = TetrahedronVolume(pp2);
+ Real v3 = TetrahedronVolume(pp3);
+ Vector3r cg1,cg2,cg3;
+ Vector_3 n(pa,sb.supporting_line().projection(pa));
+ for (int l=0; l<3; l++) {
+ normal[l] = n[l];
+ #define OP(p) p->operator[](l)
+ cg1[l] = .25*(pa[l]+OP(pa1)+OP(pa2)+OP(pa3));
+ cg2[l] = .25*(OP(pa1)+OP(pa2)+OP(pa3)+OP(ppb2));
+ cg3[l] = .25*(OP(pa1)+OP(pa2)+OP(ppb1)+OP(ppb2));
+ #undef OP
+ }
+ penetrationVolume = v1 + v2 + v3;
+ contactPoint = (v1*cg1 + v2*cg2 + v3*cg3) / penetrationVolume;
+ normal.normalize();
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Ig2_Tetra_Tetra_TTetraSimpleGeom::checkVertexToVertexCase(
+ const Point pA[4],
+ const Point pB[4],
+ const Segment sA[6],
+ const Triangle tA[4],
+ const Triangle tB[4],
+ Vector3r& normal,
+ Vector3r& contactPoint,
+ Real& penetrationVolume)
+{
+ for (int i=0; i<4; i++) {
+ const Point& pa = pA[i];
+ if ( Vector_3(tB[0][0],pa)*CGAL::normal(tB[0][0],tB[0][1],tB[0][2]) > 0. ) { continue; }
+ if ( Vector_3(tB[1][0],pa)*CGAL::normal(tB[1][0],tB[1][1],tB[1][2]) > 0. ) { continue; }
+ if ( Vector_3(tB[2][0],pa)*CGAL::normal(tB[2][0],tB[2][1],tB[2][2]) > 0. ) { continue; }
+ if ( Vector_3(tB[3][0],pa)*CGAL::normal(tB[3][0],tB[3][1],tB[3][2]) > 0. ) { continue; }
+ const Segment& sa1 = sA[psMap[i][0]];
+ const Segment& sa2 = sA[psMap[i][1]];
+ const Segment& sa3 = sA[psMap[i][2]];
+ for (int j=0; j<4; j++) {
+ const Point& pb = pB[j];
+ if ( Vector_3(tA[0][0],pb)*CGAL::normal(tA[0][0],tA[0][1],tA[0][2]) > 0. ) { continue; }
+ if ( Vector_3(tA[1][0],pb)*CGAL::normal(tA[1][0],tA[1][1],tA[1][2]) > 0. ) { continue; }
+ if ( Vector_3(tA[2][0],pb)*CGAL::normal(tA[2][0],tA[2][1],tA[2][2]) > 0. ) { continue; }
+ if ( Vector_3(tA[3][0],pb)*CGAL::normal(tA[3][0],tB[3][1],tB[3][2]) > 0. ) { continue; }
+ const Triangle& tb1 = tB[ptMap[j][0]];
+ const Triangle& tb2 = tB[ptMap[j][1]];
+ const Triangle& tb3 = tB[ptMap[j][2]];
+ bool b11 = do_intersect(sa1,tb1);
+ bool b12 = do_intersect(sa1,tb2);
+ bool b13 = do_intersect(sa1,tb3);
+ bool b21 = do_intersect(sa2,tb1);
+ bool b22 = do_intersect(sa2,tb2);
+ bool b23 = do_intersect(sa2,tb3);
+ bool b31 = do_intersect(sa3,tb1);
+ bool b32 = do_intersect(sa3,tb2);
+ bool b33 = do_intersect(sa3,tb3);
+ if ( !(b11 || b12 || b13) && (b21 || b22 || b23) && (b31 || b32 || b33) ) { continue; }
+ CGAL::Object o1 = intersection(sa1, b11? tb1: b12? tb2 : tb3);
+ CGAL::Object o2 = intersection(sa2, b21? tb1: b22? tb2 : tb3);
+ CGAL::Object o3 = intersection(sa3, b31? tb1: b32? tb2 : tb3);
+ const Point* p1 = CGAL::object_cast<Point>(&o1);
+ const Point* p2 = CGAL::object_cast<Point>(&o2);
+ const Point* p3 = CGAL::object_cast<Point>(&o3);
+ if ( !(p1 && p2 && p3) ) { continue; }
+ const Point* pp1[4] = {&pa,p1,p2,p3};
+ const Point* pp2[4] = {&pb,p2,p3,p3};
+ Real v1 = TetrahedronVolume(pp1);
+ Real v2 = TetrahedronVolume(pp2);
+ Vector3r cg1,cg2;
+ Vector_3 n(pa,pb);
+ for (int l=0; l<3; l++) {
+ normal[l] = n[l];
+ #define OP(p) p->operator[](l)
+ cg1[l] = .25*(pa[l]+OP(p1)+OP(p2)+OP(p3));
+ cg2[l] = .25*(pb[l]+OP(p1)+OP(p2)+OP(p3));
+ #undef OP
+ }
+ penetrationVolume = v1 + v2;
+ contactPoint = (v1*cg1 + v2*cg2) / penetrationVolume;
+ normal.normalize();
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Ig2_Tetra_Tetra_TTetraSimpleGeom::go(
+ const shared_ptr<Shape>& cm1,
+ const shared_ptr<Shape>& cm2,
+ const State& state1,
+ const State& state2,
+ const Vector3r& shift2,
+ const bool& force,
+ const shared_ptr<Interaction>& interaction)
+{
+ const Se3r& se31=state1.se3; const Se3r& se32=state2.se3;
+ Tetra* shape1 = static_cast<Tetra*>(cm1.get());
+ Tetra* shape2 = static_cast<Tetra*>(cm2.get());
+
+ Point p1[4], p2[4];
+ Vector3r vTemp;
+ // vertices in global coordinates
+ for (int i=0; i<4; i++) {
+ vTemp = se31.position + se31.orientation*shape1->v[i];
+ p1[i] = Point(vTemp[0],vTemp[1],vTemp[2]);
+ vTemp = se32.position + se32.orientation*shape2->v[i];
+ p2[i] = Point(vTemp[0],vTemp[1],vTemp[2]);
+ }
+
+ // Faces (CGAL triangles) of each tetra
+ #define T(p,i,j,k) Triangle(p[i],p[j],p[k])
+ const Triangle t1[4] = {T(p1,0,1,3),T(p1,0,2,1),T(p1,1,2,3),T(p1,0,3,2)};
+ const Triangle t2[4] = {T(p2,0,1,3),T(p2,0,2,1),T(p2,1,2,3),T(p2,0,3,2)};
+ #undef T
+ // Edges (CGAL segments) of each tetra
+ #define S(p,i,j) Segment(p[i],p[j])
+ const Segment s1[6] = {S(p1,0,1),S(p1,1,2),S(p1,0,2),S(p1,0,3),S(p1,1,3),S(p1,2,3)};
+ const Segment s2[6] = {S(p2,0,1),S(p2,1,2),S(p2,0,2),S(p2,0,3),S(p2,1,3),S(p2,2,3)};
+ #undef S
+
+ Vector3r n;
+ Vector3r cp;
+ Real V;
+ int flag;
+
+ # define SET_GEOM_AND_RETURN_TRUE \
+ shared_ptr<TTetraSimpleGeom> geom; \
+ if (!interaction->geom) geom=shared_ptr<TTetraSimpleGeom>(new TTetraSimpleGeom()); \
+ else geom=YADE_PTR_CAST<TTetraSimpleGeom>(interaction->geom); \
+ interaction->geom=geom; \
+ geom->normal = n; \
+ geom->contactPoint = cp; \
+ geom->penetrationVolume = V; \
+ geom->flag = flag; \
+ return true;
+
+
+ if (checkVertexToTriangleCase(t1,p2,s2,n,cp,V)) {
+ flag = 1;
+ SET_GEOM_AND_RETURN_TRUE
+ }
+ if (checkVertexToTriangleCase(t2,p1,s1,n,cp,V)) {
+ n *= -1.;
+ flag = 2;
+ SET_GEOM_AND_RETURN_TRUE
+ }
+ if (checkEdgeToEdgeCase(s1,s2,t1,t2,n,cp,V)) {
+ flag = 3;
+ SET_GEOM_AND_RETURN_TRUE
+ }
+ if (checkEdgeToTriangleCase1(t1,s2,p2,n,cp,V)) {
+ flag = 4;
+ SET_GEOM_AND_RETURN_TRUE
+ }
+ if (checkEdgeToTriangleCase1(t2,s1,p1,n,cp,V)) {
+ n *= -1.;
+ flag = 5;
+ SET_GEOM_AND_RETURN_TRUE
+ }
+ if (checkEdgeToTriangleCase2(t1,t2,s1,s2,n,cp,V)) {
+ flag = 6;
+ SET_GEOM_AND_RETURN_TRUE
+ }
+ if (checkEdgeToTriangleCase2(t2,t1,s2,s1,n,cp,V)) {
+ n *= -1.;
+ flag = 7;
+ SET_GEOM_AND_RETURN_TRUE
+ }
+ if (checkVertexToEdgeCase(p1,s1,t1,s2,t2,n,cp,V)) {
+ n *= -1.;
+ flag = 8;
+ SET_GEOM_AND_RETURN_TRUE
+ }
+ if (checkVertexToEdgeCase(p2,s2,t2,s1,t1,n,cp,V)) {
+ flag = 9;
+ SET_GEOM_AND_RETURN_TRUE
+ }
+
+ #undef SET_GEOM_AND_RETURN_TRUE
+
+
+ if (interaction->geom) {
+ TTetraSimpleGeom* geom = static_cast<TTetraSimpleGeom*>(interaction->geom.get());
+ geom->penetrationVolume = (Real)-1.;
+ geom->flag = 0;
+ return true;
+ }
+ return false;
+}
+#endif
+
+
+
+
+
+
+
CREATE_LOGGER(Ig2_Tetra_Tetra_TTetraGeom);
/*! Calculate configuration of Tetra - Tetra intersection.
@@ -107,7 +680,9 @@
tA=Tetra(Vector3r(0,0,0),Vector3r(1,0,0),Vector3r(0,1,0),Vector3r(0,0,1));
#endif
list<Tetra> tAB=Tetra2TetraIntersection(tA,tB);
- if(tAB.size()==0) { /* LOG_DEBUG("No intersection."); */ return false;} //no intersecting volume
+ if (!interaction->isReal() && !force) {
+ if(tAB.size()==0) { /* LOG_DEBUG("No intersection."); */ return false;} //no intersecting volume
+ }
Real V(0); // volume of intersection (cummulative)
Vector3r Sg(0,0,0); // static moment of intersection
@@ -186,6 +761,8 @@
Real maxPenetrationDepthB=sqrt(6*(IB(ix,ix)+IB(ixx,ixx)-IB(ixxx,ixxx))/V);
TRVAR2(maxPenetrationDepthA,maxPenetrationDepthB);
+ //normal = se32.position - se31.position; normal.normalize();
+
/* store calculated stuff in bang; some is redundant */
bang->normal=normal;
bang->equivalentCrossSection=equivalentCrossSection;
@@ -367,6 +944,7 @@
return(ret); // prevent warning
}
+
CREATE_LOGGER(TetraVolumetricLaw);
/*! Apply forces on tetrahedra in collision based on geometric configuration provided by Ig2_Tetra_Tetra_TTetraGeom.
@@ -413,12 +991,13 @@
#ifdef YADE_OPENGL
#include<yade/lib/opengl/OpenGLWrapper.hpp>
- void Gl1_Tetra::go(const shared_ptr<Shape>& cm, const shared_ptr<State>&,bool,const GLViewInfo&)
+ bool Gl1_Tetra::wire;
+ void Gl1_Tetra::go(const shared_ptr<Shape>& cm, const shared_ptr<State>&,bool wire2,const GLViewInfo&)
{
glMaterialv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,Vector3r(cm->color[0],cm->color[1],cm->color[2]));
glColor3v(cm->color);
Tetra* t=static_cast<Tetra*>(cm.get());
- if (0) { // wireframe, as for Tetrahedron
+ if (wire && wire2) { // wireframe, as for Tetrahedron
glDisable(GL_LIGHTING);
glBegin(GL_LINES);
#define __ONEWIRE(a,b) glVertex3v(t->v[a]);glVertex3v(t->v[b])
@@ -432,10 +1011,10 @@
glDisable(GL_CULL_FACE); glEnable(GL_LIGHTING);
glBegin(GL_TRIANGLES);
#define __ONEFACE(a,b,c) n=(t->v[b]-t->v[a]).cross(t->v[c]-t->v[a]); n.normalize(); faceCenter=(t->v[a]+t->v[b]+t->v[c])/3.; if((faceCenter-center).dot(n)<0)n=-n; glNormal3v(n); glVertex3v(t->v[a]); glVertex3v(t->v[b]); glVertex3v(t->v[c]);
- __ONEFACE(3,0,1);
- __ONEFACE(0,1,2);
+ __ONEFACE(0,2,1);
+ __ONEFACE(0,1,3);
__ONEFACE(1,2,3);
- __ONEFACE(2,3,0);
+ __ONEFACE(0,3,2);
#undef __ONEFACE
glEnd();
}
@@ -599,6 +1178,51 @@
}
-Real TetrahedronVolume(const Vector3r v[4]) { return fabs((Vector3r(v[3])-Vector3r(v[0])).dot((Vector3r(v[3])-Vector3r(v[1])).cross(Vector3r(v[3])-Vector3r(v[2]))))/6.; }
-Real TetrahedronVolume(const vector<Vector3r>& v) { return fabs(Vector3r(v[1]-v[0]).dot(Vector3r(v[2]-v[0]).cross(v[3]-v[0])))/6.; }
+
+
+Real TetrahedronSignedVolume(const Vector3r v[4]) { return (Vector3r(v[3])-Vector3r(v[0])).dot((Vector3r(v[3])-Vector3r(v[1])).cross(Vector3r(v[3])-Vector3r(v[2])))/6.; }
+Real TetrahedronVolume(const Vector3r v[4]) { return fabs(TetrahedronSignedVolume(v)); }
+Real TetrahedronSignedVolume(const vector<Vector3r>& v) { return Vector3r(v[1]-v[0]).dot(Vector3r(v[2]-v[0]).cross(v[3]-v[0]))/6.; }
+Real TetrahedronVolume(const vector<Vector3r>& v) { return fabs(TetrahedronSignedVolume(v)); }
+#ifdef YADE_CGAL
+Real TetrahedronVolume(const CGAL::Point_3<CGAL::Cartesian<Real> >* v[4]) {
+ Vector3r vv[4];
+ for (int i=0; i<4; i++) {
+ for (int j=0; j<3; j++) {
+ vv[i][j] = v[i]->operator[](j);
+ }
+ }
+ return TetrahedronVolume(vv);
+}
+Real TetrahedronVolume(const CGAL::Point_3<CGAL::Cartesian<Real> > v[4]) {
+ Vector3r vv[4];
+ for (int i=0; i<4; i++) {
+ for (int j=0; j<3; j++) {
+ vv[i][j] = v[i][j];
+ }
+ }
+ return TetrahedronVolume(vv);
+}
+#endif
+
+
+
+#ifdef YADE_CGAL
+void Law2_TTetraSimpleGeom_NormPhys_Simple::go(shared_ptr<IGeom>& ig, shared_ptr<IPhys>& ip, Interaction* contact){
+ int id1 = contact->getId1(), id2 = contact->getId2();
+ TTetraSimpleGeom* geom= static_cast<TTetraSimpleGeom*>(ig.get());
+ NormPhys* phys = static_cast<NormPhys*>(ip.get());
+ if ( geom->flag == 0 || geom->penetrationVolume <= 0. ) {
+ scene->interactions->requestErase(contact);
+ return;
+ }
+ Real& un=geom->penetrationVolume;
+ phys->normalForce=phys->kn*std::max(un,(Real) 0)*geom->normal;
+
+ State* de1 = Body::byId(id1,scene)->state.get();
+ State* de2 = Body::byId(id2,scene)->state.get();
+ applyForceAtContactPoint(-phys->normalForce, geom->contactPoint, id1, de1->se3.position, id2, de2->se3.position);
+ // TODO periodic
+}
+#endif
=== modified file 'pkg/dem/Tetra.hpp'
--- pkg/dem/Tetra.hpp 2011-01-09 16:34:50 +0000
+++ pkg/dem/Tetra.hpp 2013-09-11 21:43:09 +0000
@@ -1,4 +1,5 @@
// © 2007 Václav Šmilauer <eudoxos@xxxxxxxx>
+// © 2013 Jan Stránský <jan.stransky@xxxxxxxxxxx>
#pragma once
@@ -10,6 +11,12 @@
#include<yade/pkg/common/Aabb.hpp>
#include<yade/pkg/common/Dispatching.hpp>
+#include<yade/pkg/common/NormShearPhys.hpp>
+
+#ifdef YADE_CGAL
+ #include <CGAL/Cartesian.h>
+ //#include <CGAL/intersections.h>
+#endif
/* Our mold of tetrahedron: just 4 vertices.
@@ -19,16 +26,16 @@
public:
Tetra(Vector3r v0, Vector3r v1, Vector3r v2, Vector3r v3) { createIndex(); v.resize(4); v[0]=v0; v[1]=v1; v[2]=v2; v[3]=v3; }
virtual ~Tetra();
- protected:
- YADE_CLASS_BASE_DOC_ATTRS_CTOR(Tetra,Shape,"Tetrahedron geometry.",
- ((std::vector<Vector3r>,v,std::vector<Vector3r>(4),,"Tetrahedron vertices in global coordinate system.")),
- /*ctor*/createIndex();
- );
- REGISTER_CLASS_INDEX(Tetra,Shape);
+ YADE_CLASS_BASE_DOC_ATTRS_CTOR(Tetra,Shape,"Tetrahedron geometry.",
+ ((std::vector<Vector3r>,v,std::vector<Vector3r>(4),,"Tetrahedron vertices (in local coordinate system).")),
+ /*ctor*/createIndex();
+ );
+ REGISTER_CLASS_INDEX(Tetra,Shape);
};
REGISTER_SERIALIZABLE(Tetra);
+
/*! Collision configuration for Tetra and something.
* This is expressed as penetration volume properties: centroid, volume, orientation of principal axes, inertia.
*
@@ -36,22 +43,37 @@
class TTetraGeom: public IGeom{
public:
virtual ~TTetraGeom();
- protected:
- YADE_CLASS_BASE_DOC_ATTRS_CTOR(TTetraGeom,IGeom,"Geometry of interaction between 2 :yref:`tetrahedra<Tetra>`, including volumetric characteristics",
- ((Real,penetrationVolume,NaN,,"Volume of overlap [m³]"))
- ((Real,equivalentCrossSection,NaN,,"Cross-section of the overlap (perpendicular to the axis of least inertia"))
- ((Real,maxPenetrationDepthA,NaN,,"??"))
- ((Real,maxPenetrationDepthB,NaN,,"??"))
- ((Real,equivalentPenetrationDepth,NaN,,"??"))
- ((Vector3r,contactPoint,,,"Contact point (global coords)"))
- ((Vector3r,normal,,,"Normal of the interaction, directed in the sense of least inertia of the overlap volume")),
- createIndex();
- );
- //FUNCTOR2D(Tetra,Tetra);
- REGISTER_CLASS_INDEX(TTetraGeom,IGeom);
+ YADE_CLASS_BASE_DOC_ATTRS_CTOR(TTetraGeom,IGeom,"Geometry of interaction between 2 :yref:`tetrahedra<Tetra>`, including volumetric characteristics",
+ ((Real,penetrationVolume,NaN,,"Volume of overlap [m³]"))
+ ((Real,equivalentCrossSection,NaN,,"Cross-section of the overlap (perpendicular to the axis of least inertia"))
+ ((Real,maxPenetrationDepthA,NaN,,"??"))
+ ((Real,maxPenetrationDepthB,NaN,,"??"))
+ ((Real,equivalentPenetrationDepth,NaN,,"??"))
+ ((Vector3r,contactPoint,,,"Contact point (global coords)"))
+ ((Vector3r,normal,,,"Normal of the interaction, directed in the sense of least inertia of the overlap volume")),
+ createIndex();
+ );
+ REGISTER_CLASS_INDEX(TTetraGeom,IGeom);
};
REGISTER_SERIALIZABLE(TTetraGeom);
+
+class TTetraSimpleGeom: public IGeom{
+ public:
+ virtual ~TTetraSimpleGeom();
+ YADE_CLASS_BASE_DOC_ATTRS_CTOR(TTetraSimpleGeom,IGeom,"EXPERIMENTAL. Geometry of interaction between 2 :yref:`tetrahedra<Tetra>`",
+ ((Real,penetrationVolume,NaN,,"Volume of overlap [m³]"))
+ ((Vector3r,contactPoint,,,"Contact point (global coords)"))
+ ((Vector3r,normal,,,"Normal of the interaction TODO"))
+ ((int,flag,0,,"TODO")),
+ createIndex();
+ );
+ REGISTER_CLASS_INDEX(TTetraSimpleGeom,IGeom);
+};
+REGISTER_SERIALIZABLE(TTetraSimpleGeom);
+
+
+
/*! Creates Aabb from Tetra.
*
* Self-contained. */
@@ -69,7 +91,9 @@
class Gl1_Tetra: public GlShapeFunctor{
public:
virtual void go(const shared_ptr<Shape>&, const shared_ptr<State>&,bool,const GLViewInfo&);
- YADE_CLASS_BASE_DOC(Gl1_Tetra,GlShapeFunctor,"Renders :yref:`Tetra` object");
+ YADE_CLASS_BASE_DOC_STATICATTRS(Gl1_Tetra,GlShapeFunctor,"Renders :yref:`Tetra` object",
+ ((bool,wire,true,,"TODO"))
+ );
RENDERS(Tetra);
};
REGISTER_SERIALIZABLE(Gl1_Tetra);
@@ -108,11 +132,67 @@
REGISTER_SERIALIZABLE(Ig2_Tetra_Tetra_TTetraGeom);
+
+
+#ifdef YADE_CGAL
+class Ig2_Tetra_Tetra_TTetraSimpleGeom: public IGeomFunctor
+{
+ protected:
+ typedef CGAL::Cartesian<Real> K;
+ typedef K::Point_3 Point;
+ typedef CGAL::Tetrahedron_3<K> CGALTetra;
+ typedef CGAL::Segment_3<K> Segment;
+ typedef CGAL::Triangle_3<K> Triangle;
+ typedef CGAL::Vector_3<K> Vector_3;
+ bool checkVertexToTriangleCase(const Triangle tA[4], const Point pB[4], const Segment sB[6], Vector3r& normal, Vector3r& contactPoint, Real& penetrationVolume);
+ bool checkEdgeToEdgeCase(const Segment sA[6], const Segment sB[6], const Triangle tA[4], const Triangle tB[4], Vector3r& normal, Vector3r& contactPoint, Real& penetrationVolume);
+ bool checkEdgeToTriangleCase1(const Triangle tA[4], const Segment sB[6], const Point pB[4], Vector3r& normal, Vector3r& contactPoint, Real& penetrationVolume);
+ bool checkEdgeToTriangleCase2(const Triangle tA[4], const Triangle tB[4], const Segment sA[6], const Segment sB[6], Vector3r& normal, Vector3r& contactPoint, Real& penetrationVolume);
+ bool checkVertexToEdgeCase(const Point pA[4], const Segment sA[6], const Triangle tA[4], const Segment sB[6], const Triangle tB[4], Vector3r& normal, Vector3r& contactPoint, Real& penetrationVolume);
+ bool checkVertexToVertexCase(const Point pA[4], const Point pB[4], const Segment sA[6], const Triangle tA[4], const Triangle tB[4], Vector3r& normal, Vector3r& contactPoint, Real& penetrationVolume);
+ static const int psMap[4][3];
+ static const int ptMap[4][3];
+ static const int stMap[6][2];
+ static const int tsMap[4][3];
+ static const int ppsMap[4][4];
+ static const int sstMap[6][6];
+ public:
+ virtual bool go(const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c);
+ virtual bool goReverse( const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c){ throw std::logic_error("Ig2_Tetra_Tetra_TTetraSimpleGeom::goReverse called, but the functor is symmetric."); }
+ FUNCTOR2D(Tetra,Tetra);
+ DEFINE_FUNCTOR_ORDER_2D(Tetra,Tetra);
+ YADE_CLASS_BASE_DOC(Ig2_Tetra_Tetra_TTetraSimpleGeom,IGeomFunctor,"EXPERIMANTAL. Create/update geometry of collision between 2 :yref:`tetrahedra<Tetra>` (:yref:`TTetraSimpleGeom` instance)");
+ DECLARE_LOGGER;
+};
+REGISTER_SERIALIZABLE(Ig2_Tetra_Tetra_TTetraSimpleGeom);
+
+
+
+
+class Law2_TTetraSimpleGeom_NormPhys_Simple: public LawFunctor{
+ public:
+ virtual void go(shared_ptr<IGeom>& _geom, shared_ptr<IPhys>& _phys, Interaction* I);
+ YADE_CLASS_BASE_DOC(Law2_TTetraSimpleGeom_NormPhys_Simple,LawFunctor,"EXPERIMENTAL. TODO");
+ FUNCTOR2D(TTetraSimpleGeom,NormPhys);
+ DECLARE_LOGGER;
+};
+REGISTER_SERIALIZABLE(Law2_TTetraSimpleGeom_NormPhys_Simple);
+#endif
+
+
+
+
// Miscillaneous functions
//! Tetrahedron's volume.
/// http://en.wikipedia.org/wiki/Tetrahedron#Surface_area_and_volume
+Real TetrahedronSignedVolume(const Vector3r v[4]);
Real TetrahedronVolume(const Vector3r v[4]);
+Real TetrahedronSignedVolume(const vector<Vector3r>& v);
Real TetrahedronVolume(const vector<Vector3r>& v);
+#ifdef YADE_CGAL
+Real TetrahedronVolume(const CGAL::Point_3<CGAL::Cartesian<Real> >* v[4]);
+Real TetrahedronVolume(const CGAL::Point_3<CGAL::Cartesian<Real> > v[4]);
+#endif
Matrix3r TetrahedronInertiaTensor(const vector<Vector3r>& v);
//Matrix3r TetrahedronInertiaTensor(const Vector3r v[4]);
Matrix3r TetrahedronCentralInertiaTensor(const vector<Vector3r>& v);
=== modified file 'pkg/dem/TriaxialTest.hpp'
--- pkg/dem/TriaxialTest.hpp 2012-09-08 01:19:45 +0000
+++ pkg/dem/TriaxialTest.hpp 2013-09-19 08:50:04 +0000
@@ -28,7 +28,7 @@
Essential engines :
- 1/ The TrixialCompressionEngine is used for controlling the state of the sample and simulating loading paths. TrixialCompressionEngine inherits from TriaxialStressController, which can compute stress- strain-like quantities in the packing and maintain a constant level of stress at each boundary. TriaxialCompressionEngine has few more members in order to impose constant strain rate and control the transition between isotropic compression and triaxial test.
+ 1/ The TriaxialCompressionEngine is used for controlling the state of the sample and simulating loading paths. TriaxialCompressionEngine inherits from TriaxialStressController, which can compute stress- strain-like quantities in the packing and maintain a constant level of stress at each boundary. TriaxialCompressionEngine has few more members in order to impose constant strain rate and control the transition between isotropic compression and triaxial test.
2/ The class TriaxialStateRecorder is used to write to a file the history of stresses and strains.
=== modified file 'pkg/dem/VTKRecorder.cpp'
--- pkg/dem/VTKRecorder.cpp 2013-07-23 10:53:20 +0000
+++ pkg/dem/VTKRecorder.cpp 2013-11-04 14:55:45 +0000
@@ -16,6 +16,7 @@
#include<vtkTriangle.h>
#include<vtkLine.h>
#include<vtkQuad.h>
+#include<vtkHexahedron.h>
#ifdef YADE_VTK_MULTIBLOCK
#include<vtkXMLMultiBlockDataWriter.h>
#include<vtkMultiBlockDataSet.h>
@@ -27,6 +28,7 @@
#include<yade/pkg/common/Box.hpp>
#include<yade/pkg/dem/ConcretePM.hpp>
#include<yade/pkg/dem/WirePM.hpp>
+#include<yade/pkg/dem/JointedCohesiveFrictionalPM.hpp>
#include<yade/pkg/dem/Shop.hpp>
@@ -49,6 +51,7 @@
recActive[REC_CLUMPID]=true;
recActive[REC_MATERIALID]=true;
recActive[REC_STRESS]=true;
+ if (scene->isPeriodic) { recActive[REC_PERICELL]=true; }
}
else if(rec=="spheres") recActive[REC_SPHERES]=true;
else if(rec=="velocity") recActive[REC_VELOCITY]=true;
@@ -64,11 +67,17 @@
else if((rec=="clumpids") || (rec=="clumpId")) recActive[REC_CLUMPID]=true;
else if(rec=="materialId") recActive[REC_MATERIALID]=true;
else if(rec=="stress") recActive[REC_STRESS]=true;
- else LOG_ERROR("Unknown recorder named `"<<rec<<"' (supported are: all, spheres, velocity, facets, boxes, color, stress, cpm, wpm, intr, id, clumpId, materialId). Ignored.");
+ else if(rec=="jcfpm") recActive[REC_JCFPM]=true;
+ else if(rec=="cracks") recActive[REC_CRACKS]=true;
+ else if(rec=="pericell" && scene->isPeriodic) recActive[REC_PERICELL]=true;
+ else LOG_ERROR("Unknown recorder named `"<<rec<<"' (supported are: all, spheres, velocity, facets, boxes, color, stress, cpm, wpm, intr, id, clumpId, materialId, jcfpm, cracks, pericell). Ignored.");
}
// cpm needs interactions
if(recActive[REC_CPM]) recActive[REC_INTR]=true;
+ // jcfpm needs interactions
+ if(recActive[REC_JCFPM]) recActive[REC_INTR]=true;
+
// wpm needs interactions
if(recActive[REC_WPM]) recActive[REC_INTR]=true;
@@ -189,6 +198,10 @@
intrAbsForceT->SetNumberOfComponents(3);
intrAbsForceT->SetName("absForceT");
+ // pericell
+ vtkSmartPointer<vtkPoints> pericellPoints = vtkSmartPointer<vtkPoints>::New();
+ vtkSmartPointer<vtkCellArray> pericellHexa = vtkSmartPointer<vtkCellArray>::New();
+
// extras for CPM
if(recActive[REC_CPM]){ CpmStateUpdater csu; csu.update(scene); }
vtkSmartPointer<vtkFloatArray> cpmDamage = vtkSmartPointer<vtkFloatArray>::New();
@@ -198,6 +211,52 @@
cpmStress->SetNumberOfComponents(9);
cpmStress->SetName("cpmStress");
+ // extras for JCFpm
+ vtkSmartPointer<vtkFloatArray> damage = vtkSmartPointer<vtkFloatArray>::New();
+ damage->SetNumberOfComponents(1);;
+ damage->SetName("damage");
+ vtkSmartPointer<vtkFloatArray> damageRel = vtkSmartPointer<vtkFloatArray>::New();
+ damageRel->SetNumberOfComponents(1);;
+ damageRel->SetName("damageRel");
+ vtkSmartPointer<vtkFloatArray> intrIsCohesive = vtkSmartPointer<vtkFloatArray>::New();
+ intrIsCohesive->SetNumberOfComponents(1);
+ intrIsCohesive->SetName("isCohesive");
+ vtkSmartPointer<vtkFloatArray> intrIsOnJoint = vtkSmartPointer<vtkFloatArray>::New();
+ intrIsOnJoint->SetNumberOfComponents(1);
+ intrIsOnJoint->SetName("isOnJoint");
+
+ // extras for cracks
+ vtkSmartPointer<vtkPoints> crackPos = vtkSmartPointer<vtkPoints>::New();
+ vtkSmartPointer<vtkCellArray> crackCells = vtkSmartPointer<vtkCellArray>::New();
+ vtkSmartPointer<vtkFloatArray> iter = vtkSmartPointer<vtkFloatArray>::New();
+ iter->SetNumberOfComponents(1);
+ iter->SetName("iter");
+ vtkSmartPointer<vtkFloatArray> crackType = vtkSmartPointer<vtkFloatArray>::New();
+ crackType->SetNumberOfComponents(1);
+ crackType->SetName("crackType");
+ vtkSmartPointer<vtkFloatArray> crackSize = vtkSmartPointer<vtkFloatArray>::New();
+ crackSize->SetNumberOfComponents(1);
+ crackSize->SetName("crackSize");
+ vtkSmartPointer<vtkFloatArray> crackNorm = vtkSmartPointer<vtkFloatArray>::New();
+ crackNorm->SetNumberOfComponents(3);
+ crackNorm->SetName("crackNorm");
+
+ // the same for newly created cracks
+// vtkSmartPointer<vtkPoints> crackPosNew = vtkSmartPointer<vtkPoints>::New();
+// vtkSmartPointer<vtkCellArray> crackCellsNew = vtkSmartPointer<vtkCellArray>::New();
+// vtkSmartPointer<vtkFloatArray> iterNew = vtkSmartPointer<vtkFloatArray>::New();
+// iterNew->SetNumberOfComponents(1);
+// iterNew->SetName("iter");
+// vtkSmartPointer<vtkFloatArray> crackTypeNew = vtkSmartPointer<vtkFloatArray>::New();
+// crackTypeNew->SetNumberOfComponents(1);
+// crackTypeNew->SetName("crackType");
+// vtkSmartPointer<vtkFloatArray> crackSizeNew = vtkSmartPointer<vtkFloatArray>::New();
+// crackSizeNew->SetNumberOfComponents(1);
+// crackSizeNew->SetName("crackSize");
+// vtkSmartPointer<vtkFloatArray> crackNormNew = vtkSmartPointer<vtkFloatArray>::New();
+// crackNormNew->SetNumberOfComponents(3);
+// crackNormNew->SetName("crackNorm");
+
// extras for WireMatPM
vtkSmartPointer<vtkFloatArray> wpmNormalForce = vtkSmartPointer<vtkFloatArray>::New();
wpmNormalForce->SetNumberOfComponents(1);
@@ -283,6 +342,13 @@
wpmLimitFactor->InsertNextValue(NaN);
}
}
+ else if (recActive[REC_JCFPM]){
+ const JCFpmPhys* jcfpmphys = YADE_CAST<JCFpmPhys*>(I->phys.get());
+ intrIsCohesive->InsertNextValue(jcfpmphys->isCohesive);
+ intrIsOnJoint->InsertNextValue(jcfpmphys->isOnJoint);
+ intrForceN->InsertNextValue(fn);
+ }
+
else {
intrForceN->InsertNextValue(fn);
}
@@ -344,6 +410,11 @@
cpmStress->InsertNextTupleValue(s);
}
+ if (recActive[REC_JCFPM]){
+ damage->InsertNextValue(YADE_PTR_CAST<JCFpmState>(b->state)->tensBreak + YADE_PTR_CAST<JCFpmState>(b->state)->shearBreak);
+ damageRel->InsertNextValue(YADE_PTR_CAST<JCFpmState>(b->state)->tensBreakRel + YADE_PTR_CAST<JCFpmState>(b->state)->shearBreakRel);
+ }
+
if (recActive[REC_MATERIALID]) spheresMaterialId->InsertNextValue(b->material->id);
continue;
}
@@ -433,6 +504,32 @@
}
}
+ if (recActive[REC_PERICELL]) {
+ const Matrix3r& hSize = scene->cell->hSize;
+ Vector3r v0 = hSize*Vector3r(0,0,1);
+ Vector3r v1 = hSize*Vector3r(0,1,1);
+ Vector3r v2 = hSize*Vector3r(1,1,1);
+ Vector3r v3 = hSize*Vector3r(1,0,1);
+ Vector3r v4 = hSize*Vector3r(0,0,0);
+ Vector3r v5 = hSize*Vector3r(0,1,0);
+ Vector3r v6 = hSize*Vector3r(1,1,0);
+ Vector3r v7 = hSize*Vector3r(1,0,0);
+ pericellPoints->InsertNextPoint(v0[0],v0[1],v0[2]);
+ pericellPoints->InsertNextPoint(v1[0],v1[1],v1[2]);
+ pericellPoints->InsertNextPoint(v2[0],v2[1],v2[2]);
+ pericellPoints->InsertNextPoint(v3[0],v3[1],v3[2]);
+ pericellPoints->InsertNextPoint(v4[0],v4[1],v4[2]);
+ pericellPoints->InsertNextPoint(v5[0],v5[1],v5[2]);
+ pericellPoints->InsertNextPoint(v6[0],v6[1],v6[2]);
+ pericellPoints->InsertNextPoint(v7[0],v7[1],v7[2]);
+ vtkSmartPointer<vtkHexahedron> h = vtkSmartPointer<vtkHexahedron>::New();
+ vtkIdList* l = h->GetPointIds();
+ for (int i=0; i<8; i++) {
+ l->SetId(i,i);
+ }
+ pericellHexa->InsertNextCell(h);
+ }
+
vtkSmartPointer<vtkDataCompressor> compressor;
if(compress) compressor=vtkSmartPointer<vtkZLibDataCompressor>::New();
@@ -463,8 +560,11 @@
spheresUg->GetPointData()->AddArray(cpmStress);
}
+ if (recActive[REC_JCFPM]) {
+ spheresUg->GetPointData()->AddArray(damage);
+ }
if (recActive[REC_MATERIALID]) spheresUg->GetPointData()->AddArray(spheresMaterialId);
-
+
#ifdef YADE_VTK_MULTIBLOCK
if(!multiblock)
#endif
@@ -474,7 +574,11 @@
if(ascii) writer->SetDataModeToAscii();
string fn=fileName+"spheres."+lexical_cast<string>(scene->iter)+".vtu";
writer->SetFileName(fn.c_str());
- writer->SetInput(spheresUg);
+ #ifdef YADE_VTK6
+ writer->SetInputData(spheresUg);
+ #else
+ writer->SetInput(spheresUg);
+ #endif
writer->Write();
}
}
@@ -498,7 +602,11 @@
if(ascii) writer->SetDataModeToAscii();
string fn=fileName+"facets."+lexical_cast<string>(scene->iter)+".vtu";
writer->SetFileName(fn.c_str());
- writer->SetInput(facetsUg);
+ #ifdef YADE_VTK6
+ writer->SetInputData(facetsUg);
+ #else
+ writer->SetInput(facetsUg);
+ #endif
writer->Write();
}
}
@@ -522,7 +630,11 @@
if(ascii) writer->SetDataModeToAscii();
string fn=fileName+"boxes."+lexical_cast<string>(scene->iter)+".vtu";
writer->SetFileName(fn.c_str());
- writer->SetInput(boxesUg);
+ #ifdef YADE_VTK6
+ writer->SetInputData(boxesUg);
+ #else
+ writer->SetInput(boxesUg);
+ #endif
writer->Write();
}
}
@@ -532,6 +644,10 @@
intrPd->SetLines(intrCells);
intrPd->GetCellData()->AddArray(intrForceN);
intrPd->GetCellData()->AddArray(intrAbsForceT);
+ if (recActive[REC_JCFPM]) {
+ intrPd->GetCellData()->AddArray(intrIsCohesive);
+ intrPd->GetCellData()->AddArray(intrIsOnJoint);
+ }
if (recActive[REC_WPM]){
intrPd->GetCellData()->AddArray(wpmNormalForce);
intrPd->GetCellData()->AddArray(wpmLimitFactor);
@@ -545,10 +661,109 @@
if(ascii) writer->SetDataModeToAscii();
string fn=fileName+"intrs."+lexical_cast<string>(scene->iter)+".vtp";
writer->SetFileName(fn.c_str());
- writer->SetInput(intrPd);
- writer->Write();
- }
- }
+ #ifdef YADE_VTK6
+ writer->SetInputData(intrPd);
+ #else
+ writer->SetInput(intrPd);
+ #endif
+ writer->Write();
+ }
+ }
+ vtkSmartPointer<vtkUnstructuredGrid> pericellUg = vtkSmartPointer<vtkUnstructuredGrid>::New();
+ if (recActive[REC_PERICELL]){
+ pericellUg->SetPoints(pericellPoints);
+ pericellUg->SetCells(12,pericellHexa);
+ #ifdef YADE_VTK_MULTIBLOCK
+ if(!multiblock)
+ #endif
+ {
+ vtkSmartPointer<vtkXMLUnstructuredGridWriter> writer = vtkSmartPointer<vtkXMLUnstructuredGridWriter>::New();
+ if(compress) writer->SetCompressor(compressor);
+ if(ascii) writer->SetDataModeToAscii();
+ string fn=fileName+"pericell."+lexical_cast<string>(scene->iter)+".vtu";
+ writer->SetFileName(fn.c_str());
+ #ifdef YADE_VTK6
+ writer->SetInputData(pericellUg);
+ #else
+ writer->SetInput(pericellUg);
+ #endif
+ writer->Write();
+ }
+ }
+
+ if (recActive[REC_CRACKS]) {
+ string fileCracks = "cracks_"+Key+".txt";
+ std::ifstream file (fileCracks.c_str(),std::ios::in);
+ vtkSmartPointer<vtkUnstructuredGrid> crackUg = vtkSmartPointer<vtkUnstructuredGrid>::New();
+ vtkSmartPointer<vtkUnstructuredGrid> crackUgNew = vtkSmartPointer<vtkUnstructuredGrid>::New();
+
+ if(file){
+ while ( !file.eof() ){
+ std::string line;
+ Real i,p0,p1,p2,t,s,n0,n1,n2;
+ while ( std::getline(file, line)/* writes into string "line", a line of file "file". To go along diff. lines*/ )
+ {
+ file >> i >> p0 >> p1 >> p2 >> t >> s >> n0 >> n1 >> n2;
+ vtkIdType pid[1];
+ pid[0] = crackPos->InsertNextPoint(p0, p1, p2);
+ crackCells->InsertNextCell(1,pid);
+ crackType->InsertNextValue(t);
+ crackSize->InsertNextValue(s);
+ iter->InsertNextValue(i);
+ float n[3] = { n0, n1, n2 };
+ crackNorm->InsertNextTupleValue(n);
+ // Then, taking care only of newly created cracks :
+// if (i > scene->iter - iterPeriod)
+// {
+// pid[0] = crackPosNew->InsertNextPoint(p0, p1, p2);
+// crackCellsNew->InsertNextCell(1,pid);
+// crackTypeNew->InsertNextValue(t);
+// crackSizeNew->InsertNextValue(s);
+// iterNew->InsertNextValue(i);
+// crackNormNew->InsertNextTupleValue(n);
+// }
+ }
+ }
+ file.close();
+ }
+
+ crackUg->SetPoints(crackPos);
+ crackUg->SetCells(VTK_VERTEX, crackCells);
+ crackUg->GetPointData()->AddArray(iter);
+ crackUg->GetPointData()->AddArray(crackType);
+ crackUg->GetPointData()->AddArray(crackSize);
+ crackUg->GetPointData()->AddArray(crackNorm); //orientation of 2D glyphs does not match this direction (some work to do in order to have the good orientation)
+
+ vtkSmartPointer<vtkXMLUnstructuredGridWriter> writer = vtkSmartPointer<vtkXMLUnstructuredGridWriter>::New();
+ if(compress) writer->SetCompressor(compressor);
+ if(ascii) writer->SetDataModeToAscii();
+ string fn=fileName+"cracks."+lexical_cast<string>(scene->iter)+".vtu";
+ writer->SetFileName(fn.c_str());
+ #ifdef YADE_VTK6
+ writer->SetInputData(crackUg);
+ #else
+ writer->SetInput(crackUg);
+ #endif
+ writer->Write();
+
+ // Same operations, for newly created cracks :
+// crackUgNew->SetPoints(crackPosNew);
+// crackUgNew->SetCells(VTK_VERTEX, crackCellsNew);
+// crackUgNew->GetPointData()->AddArray(iterNew);
+// crackUgNew->GetPointData()->AddArray(crackTypeNew);
+// crackUgNew->GetPointData()->AddArray(crackSizeNew);
+// crackUgNew->GetPointData()->AddArray(crackNormNew); //same remark about the orientation...
+//
+// fn=fileName+"newcracks."+lexical_cast<string>(scene->iter)+".vtu";
+// writer->SetFileName(fn.c_str());
+// #ifdef YADE_VTK6
+// writer->SetInputData(crackUgNew);
+// #else
+// writer->SetInput(crackUgNew);
+// #endif
+// writer->Write();
+ }
+
#ifdef YADE_VTK_MULTIBLOCK
if(multiblock){
vtkSmartPointer<vtkMultiBlockDataSet> multiblockDataset = vtkSmartPointer<vtkMultiBlockDataSet>::New();
@@ -556,11 +771,16 @@
if(recActive[REC_SPHERES]) multiblockDataset->SetBlock(i++,spheresUg);
if(recActive[REC_FACETS]) multiblockDataset->SetBlock(i++,facetsUg);
if(recActive[REC_INTR]) multiblockDataset->SetBlock(i++,intrPd);
+ if(recActive[REC_PERICELL]) multiblockDataset->SetBlock(i++,pericellUg);
vtkSmartPointer<vtkXMLMultiBlockDataWriter> writer = vtkSmartPointer<vtkXMLMultiBlockDataWriter>::New();
if(ascii) writer->SetDataModeToAscii();
string fn=fileName+lexical_cast<string>(scene->iter)+".vtm";
writer->SetFileName(fn.c_str());
- writer->SetInput(multiblockDataset);
+ #ifdef YADE_VTK6
+ writer->SetInputData(multiblockDataset);
+ #else
+ writer->SetInput(multiblockDataset);
+ #endif
writer->Write();
}
#endif
=== modified file 'pkg/dem/VTKRecorder.hpp'
--- pkg/dem/VTKRecorder.hpp 2013-04-24 19:49:00 +0000
+++ pkg/dem/VTKRecorder.hpp 2013-11-05 10:52:25 +0000
@@ -11,7 +11,7 @@
class VTKRecorder: public PeriodicEngine {
public:
- enum {REC_SPHERES=0,REC_FACETS,REC_BOXES,REC_COLORS,REC_MASS,REC_CPM,REC_INTR,REC_VELOCITY,REC_ID,REC_CLUMPID,REC_SENTINEL,REC_MATERIALID,REC_STRESS,REC_MASK,REC_RPM,REC_WPM};
+ enum {REC_SPHERES=0,REC_FACETS,REC_BOXES,REC_COLORS,REC_MASS,REC_CPM,REC_INTR,REC_VELOCITY,REC_ID,REC_CLUMPID,REC_SENTINEL,REC_MATERIALID,REC_STRESS,REC_MASK,REC_RPM,REC_JCFPM,REC_CRACKS,REC_WPM,REC_PERICELL};
virtual void action();
void addWallVTK (vtkSmartPointer<vtkQuad>& boxes, vtkSmartPointer<vtkPoints>& boxesPos, Vector3r& W1, Vector3r& W2, Vector3r& W3, Vector3r& W4);
YADE_CLASS_BASE_DOC_ATTRS_CTOR(VTKRecorder,PeriodicEngine,"Engine recording snapshots of simulation into series of \\*.vtu files, readable by VTK-based postprocessing programs such as Paraview. Both bodies (spheres and facets) and interactions can be recorded, with various vector/scalar quantities that are defined on them.\n\n:yref:`PeriodicEngine.initRun` is initialized to ``True`` automatically.",
@@ -23,7 +23,8 @@
((bool,multiblock,false,,"Use multi-block (``.vtm``) files to store data, rather than separate ``.vtu`` files."))
#endif
((string,fileName,"",,"Base file name; it will be appended with {spheres,intrs,facets}-243100.vtu (unless *multiblock* is ``True``) depending on active recorders and step number (243100 in this case). It can contain slashes, but the directory must exist already."))
- ((vector<string>,recorders,vector<string>(1,string("all")),,"List of active recorders (as strings). ``all`` (the default value) enables all base and generic recorders.\n\n.. admonition:: Base recorders\n\n\tBase recorders save the geometry (unstructured grids) on which other data is defined. They are implicitly activated by many of the other recorders. Each of them creates a new file (or a block, if :yref:`multiblock <VTKRecorder.multiblock>` is set).\n\n\t``spheres``\n\t\tSaves positions and radii (``radii``) of :yref:`spherical<Sphere>` particles.\n\t``facets``\n\t\tSave :yref:`facets<Facet>` positions (vertices).\n\t``boxes``\n\t\tSave :yref:`boxes<Box>` positions (edges).\n\t``intr``\n\t\tStore interactions as lines between nodes at respective particles positions. Additionally stores magnitude of normal (``forceN``) and shear (``absForceT``) forces on interactions (the :yref:`geom<Interaction.geom> must be of type :yref:`NormShearPhys`). \n\n.. admonition:: Generic recorders\n\n\tGeneric recorders do not depend on specific model being used and save commonly useful data.\n\n\t``id``\n\t\tSaves id's (field ``id``) of spheres; active only if ``spheres`` is active.\n\t``mass``\n\t\tSaves masses (field ``mass``) of spheres; active only if ``spheres`` is active.\n\t``clumpId``\n\t\tSaves id's of clumps to which each sphere belongs (field ``clumpId``); active only if ``spheres`` is active.\n\t``colors``\n\t\tSaves colors of :yref:`spheres<Sphere>` and of :yref:`facets<Facet>` (field ``color``); only active if ``spheres`` or ``facets`` are activated.\n\t``mask``\n\t\tSaves groupMasks of :yref:`spheres<Sphere>` and of :yref:`facets<Facet>` (field ``mask``); only active if ``spheres`` or ``facets`` are activated.\n\t``materialId``\n\t\tSaves materialID of :yref:`spheres<Sphere>` and of :yref:`facets<Facet>`; only active if ``spheres`` or ``facets`` are activated.\n\t``velocity``\n\t\tSaves linear and angular velocities of spherical particles as Vector3 and length(fields ``linVelVec``, ``linVelLen`` and ``angVelVec``, ``angVelLen`` respectively``); only effective with ``spheres``.\n\t``stress``\n\t\tSaves stresses of :yref:`spheres<Sphere>` and of :yref:`facets<Facet>` as Vector3 and length; only active if ``spheres`` or ``facets`` are activated.\n\n.. admonition:: Specific recorders\n\n\tThe following should only be activated in appropriate cases, otherwise crashes can occur due to violation of type presuppositions.\n\n\t``cpm``\n\t\tSaves data pertaining to the :yref:`concrete model<Law2_ScGeom_CpmPhys_Cpm>`: ``cpmDamage`` (normalized residual strength averaged on particle), ``cpmStress`` (stress on particle); ``intr`` is activated automatically by ``cpm``\n\t``rpm``\n\t\tSaves data pertaining to the :yref:`rock particle model<Law2_Dem3DofGeom_RockPMPhys_Rpm>`: ``rpmSpecNum`` shows different pieces of separated stones, only ids. ``rpmSpecMass`` shows masses of separated stones.\n\t``wpm``\n\t\tSaves data pertaining to the :yref:`wire particle model<Law2_ScGeom_WirePhys_WirePM>`: ``wpmForceNFactor`` shows the loading factor for the wire, e.g. normal force divided by threshold normal force.\n\n"))
+ ((vector<string>,recorders,vector<string>(1,string("all")),,"List of active recorders (as strings). ``all`` (the default value) enables all base and generic recorders.\n\n.. admonition:: Base recorders\n\n\tBase recorders save the geometry (unstructured grids) on which other data is defined. They are implicitly activated by many of the other recorders. Each of them creates a new file (or a block, if :yref:`multiblock <VTKRecorder.multiblock>` is set).\n\n\t``spheres``\n\t\tSaves positions and radii (``radii``) of :yref:`spherical<Sphere>` particles.\n\t``facets``\n\t\tSave :yref:`facets<Facet>` positions (vertices).\n\t``boxes``\n\t\tSave :yref:`boxes<Box>` positions (edges).\n\t``intr``\n\t\tStore interactions as lines between nodes at respective particles positions. Additionally stores magnitude of normal (``forceN``) and shear (``absForceT``) forces on interactions (the :yref:`geom<Interaction.geom> must be of type :yref:`NormShearPhys`). \n\n.. admonition:: Generic recorders\n\n\tGeneric recorders do not depend on specific model being used and save commonly useful data.\n\n\t``id``\n\t\tSaves id's (field ``id``) of spheres; active only if ``spheres`` is active.\n\t``mass``\n\t\tSaves masses (field ``mass``) of spheres; active only if ``spheres`` is active.\n\t``clumpId``\n\t\tSaves id's of clumps to which each sphere belongs (field ``clumpId``); active only if ``spheres`` is active.\n\t``colors``\n\t\tSaves colors of :yref:`spheres<Sphere>` and of :yref:`facets<Facet>` (field ``color``); only active if ``spheres`` or ``facets`` are activated.\n\t``mask``\n\t\tSaves groupMasks of :yref:`spheres<Sphere>` and of :yref:`facets<Facet>` (field ``mask``); only active if ``spheres`` or ``facets`` are activated.\n\t``materialId``\n\t\tSaves materialID of :yref:`spheres<Sphere>` and of :yref:`facets<Facet>`; only active if ``spheres`` or ``facets`` are activated.\n\t``velocity``\n\t\tSaves linear and angular velocities of spherical particles as Vector3 and length(fields ``linVelVec``, ``linVelLen`` and ``angVelVec``, ``angVelLen`` respectively``); only effective with ``spheres``.\n\t``stress``\n\t\tSaves stresses of :yref:`spheres<Sphere>` and of :yref:`facets<Facet>` as Vector3 and length; only active if ``spheres`` or ``facets`` are activated.\n\n.. admonition:: Specific recorders\n\n\tThe following should only be activated in appropriate cases, otherwise crashes can occur due to violation of type presuppositions.\n\n\t``cpm``\n\t\tSaves data pertaining to the :yref:`concrete model<Law2_ScGeom_CpmPhys_Cpm>`: ``cpmDamage`` (normalized residual strength averaged on particle), ``cpmStress`` (stress on particle); ``intr`` is activated automatically by ``cpm``\n\t``wpm``\n\t\tSaves data pertaining to the :yref:`wire particle model<Law2_ScGeom_WirePhys_WirePM>`: ``wpmForceNFactor`` shows the loading factor for the wire, e.g. normal force divided by threshold normal force.\n\t``jcfpm``\n\t\tSaves data pertaining to the :yref:`rock (smooth)-jointed model<Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM>`: ``damage`` is defined by :yref:`JCFpmState.tensBreak` + :yref:`JCFpmState.shearBreak`; ``intr`` is activated automatically by ``jcfpm``, and :yref:`on joint<JCFpmPhys.isOnJoint>` or :yref:`cohesive<JCFpmPhys.isCohesive>` interactions can be vizualized.\n\t``cracks``\n\t\tSaves other data pertaining to the :yref:`rock model<Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM>`: ``cracks`` shows locations where cohesive bonds failed during the simulation, with their types (0/1 for tensile/shear breakages), their sizes (0.5*(R1+R2)), and their normal directions. The :yref:`corresponding attribute<Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM.recordCracks>` has to be activated, and Key attributes have to be consistent.\n\n"))
+ ((string,Key,"",,"Necessary if :yref:`recorders<VTKRecorder.recorders>` contains 'cracks'. A string specifying the name of file 'cracks___.txt' that is considered in this case (see :yref:`corresponding attribute<Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM.Key>`)."))
((int,mask,0,,"If mask defined, only bodies with corresponding groupMask will be exported. If 0, all bodies will be exported.")),
/*ctor*/
initRun=true;
=== modified file 'pkg/dem/ViscoelasticCapillarPM.cpp'
--- pkg/dem/ViscoelasticCapillarPM.cpp 2013-06-12 14:35:50 +0000
+++ pkg/dem/ViscoelasticCapillarPM.cpp 2013-08-28 12:00:42 +0000
@@ -50,7 +50,7 @@
*/
Real R = phys.R;
- Real s = -geom.penetrationDepth/2.0;
+ Real s = -geom.penetrationDepth;
Real Vb = phys.Vb;
Real VbS = Vb/(R*R*R);
@@ -79,7 +79,7 @@
(0.03345 + 0.04543*Th1 - 0.09056*Th2) * log(VbS) +
(0.0018574 + 0.004456*Th1 - 0.006257*Th2) *log(VbS)*log(VbS);
- Real sPl = s/sqrt(Vb/R);
+ Real sPl = (s/2.0)/sqrt(Vb/R);
Real lnFS = f1 - f2*exp(f3*log(sPl) + f4*log(sPl)*log(sPl));
Real FS = exp(lnFS);
=== modified file 'pkg/dem/ViscoelasticPM.cpp'
--- pkg/dem/ViscoelasticPM.cpp 2013-06-12 14:35:50 +0000
+++ pkg/dem/ViscoelasticPM.cpp 2013-08-28 13:31:12 +0000
@@ -132,7 +132,6 @@
if (not(phys.liqBridgeCreated) and phys.Capillar) {
phys.liqBridgeCreated = true;
- phys.sCrit = (1+0.5*phys.theta)*(pow(phys.Vb,1/3.0) + 0.1*pow(phys.Vb,2.0/3.0)); // [Willett2000], equation (15), use the full-length e.g 2*Sc
Sphere* s1=dynamic_cast<Sphere*>(bodies[id1]->shape.get());
Sphere* s2=dynamic_cast<Sphere*>(bodies[id2]->shape.get());
if (s1 and s2) {
@@ -142,6 +141,11 @@
} else {
phys.R = s2->radius;
}
+
+ const Real Vstar = phys.Vb/(phys.R*phys.R*phys.R);
+ const Real Sstar = (1+0.5*phys.theta)*(pow(Vstar,1/3.0) + 0.1*pow(Vstar,2.0/3.0)); // [Willett2000], equation (15), use the full-length e.g 2*Sc
+
+ phys.sCrit = Sstar*phys.R;
}
Vector3r& shearForce = phys.shearForce;
=== modified file 'pkg/dem/ViscoelasticPM.hpp'
--- pkg/dem/ViscoelasticPM.hpp 2013-06-12 14:35:49 +0000
+++ pkg/dem/ViscoelasticPM.hpp 2013-08-28 12:00:42 +0000
@@ -79,6 +79,6 @@
private:
Real calculateCapillarForce(const ScGeom& geom, ViscElPhys& phys);
FUNCTOR2D(ScGeom,ViscElPhys);
- YADE_CLASS_BASE_DOC(Law2_ScGeom_ViscElPhys_Basic,LawFunctor,"Linear viscoelastic model operating on :yref:`ScGeom` and :yref:`ViscElPhys`.");
+ YADE_CLASS_BASE_DOC(Law2_ScGeom_ViscElPhys_Basic,LawFunctor,"Linear viscoelastic model operating on :yref:`ScGeom` and :yref:`ViscElPhys`. The model is mostly based on the paper for For details see Pournin [Pournin2001]_ .");
};
REGISTER_SERIALIZABLE(Law2_ScGeom_ViscElPhys_Basic);
=== modified file 'pkg/dem/WirePM.hpp'
--- pkg/dem/WirePM.hpp 2013-06-25 04:00:41 +0000
+++ pkg/dem/WirePM.hpp 2013-08-14 08:01:55 +0000
@@ -51,10 +51,12 @@
((Real,diameter,0.0027,,"Diameter of the single wire in [m] (the diameter is used to compute the cross-section area of the wire)."))
((unsigned int,type,0,,"Three different types are considered:\n\n"
"== ===============================================================\n"
- "type=0 Corresponds to Bertrand's approach (see [Bertrand2008]_): only one stress-strain curve is used\n"
- "type=1 New approach: two stress-strain curves (see [Thoeni2013]_)\n"
- "type=2 New approach: two stress-strain curves with changed initial stiffness and horizontal shift (shift is random if $\\text{seed}\\geq0$, for more details see [Thoeni2013]_)\n"
- "== ==============================================================="))
+ "0 Corresponds to Bertrand's approach (see [Bertrand2008]_): only one stress-strain curve is used\n"
+ "1 New approach: two separate stress-strain curves can be used (see [Thoeni2013]_)\n"
+ "2 New approach with stochastically distorted contact model: two separate stress-strain curves with changed initial stiffness and horizontal shift (shift is random if $\\text{seed}\\geq0$, for more details see [Thoeni2013]_)\n"
+ "== ===============================================================\n\n"
+ "By default the type is 0."
+ ))
((vector<Vector2r>,strainStressValues,,Attr::triggerPostLoad,"Piecewise linear definition of the stress-strain curve by set of points (strain[-]>0,stress[Pa]>0) for one single wire. Tension only is considered and the point (0,0) is not needed! NOTE: Vector needs to be initialized!"))
((vector<Vector2r>,strainStressValuesDT,,Attr::triggerPostLoad,"Piecewise linear definition of the stress-strain curve by set of points (strain[-]>0,stress[Pa]>0) for the double twist. Tension only is considered and the point (0,0) is not needed! If this value is given the calculation will be based on two different stress-strain curves without considering the parameter introduced by [Bertrand2008]_ (see [Thoeni2013]_)."))
((bool,isDoubleTwist,false,,"Type of the mesh. If true two particles of the same material which body ids differ by one will be considered as double-twisted interaction."))
=== modified file 'py/CMakeLists.txt'
--- py/CMakeLists.txt 2013-07-01 18:57:33 +0000
+++ py/CMakeLists.txt 2013-10-15 16:14:46 +0000
@@ -30,17 +30,18 @@
SET_TARGET_PROPERTIES(WeightedAverage2d PROPERTIES PREFIX "")
INSTALL(TARGETS WeightedAverage2d DESTINATION "${YADE_PY_PATH}/yade/")
-ADD_LIBRARY(_eudoxos SHARED "${CMAKE_CURRENT_SOURCE_DIR}/_eudoxos.cpp")
-SET_TARGET_PROPERTIES(_eudoxos PROPERTIES PREFIX "")
-TARGET_LINK_LIBRARIES(_eudoxos yade)
-INSTALL(TARGETS _eudoxos DESTINATION "${YADE_PY_PATH}/yade/")
-
ADD_LIBRARY(_utils SHARED "${CMAKE_CURRENT_SOURCE_DIR}/_utils.cpp")
SET_TARGET_PROPERTIES(_utils PROPERTIES PREFIX "")
TARGET_LINK_LIBRARIES(_utils yade)
INSTALL(TARGETS _utils DESTINATION "${YADE_PY_PATH}/yade/")
+ADD_LIBRARY(_polyhedra_utils SHARED "${CMAKE_CURRENT_SOURCE_DIR}/_polyhedra_utils.cpp")
+SET_TARGET_PROPERTIES(_polyhedra_utils PROPERTIES PREFIX "")
+TARGET_LINK_LIBRARIES(_polyhedra_utils)
+INSTALL(TARGETS _polyhedra_utils DESTINATION "${YADE_PY_PATH}/yade/")
+
+
ADD_LIBRARY(_packPredicates SHARED "${CMAKE_CURRENT_SOURCE_DIR}/pack/_packPredicates.cpp")
SET_TARGET_PROPERTIES(_packPredicates PROPERTIES PREFIX "")
TARGET_LINK_LIBRARIES(_packPredicates yade)
=== removed file 'py/_eudoxos.cpp'
--- py/_eudoxos.cpp 2013-03-07 18:28:01 +0000
+++ py/_eudoxos.cpp 1970-01-01 00:00:00 +0000
@@ -1,335 +0,0 @@
-#if 0
- #include<yade/lib/pyutil/numpy.hpp>
-#endif
-
-#include<yade/pkg/dem/ConcretePM.hpp>
-#include<boost/python.hpp>
-#include<boost/python/object.hpp>
-#include<boost/version.hpp>
-#include<yade/pkg/dem/Shop.hpp>
-#include<yade/pkg/dem/DemXDofGeom.hpp>
-
-#ifdef YADE_VTK
- #pragma GCC diagnostic ignored "-Wdeprecated"
- #include<vtkPointLocator.h>
- #include<vtkIdList.h>
- #include<vtkUnstructuredGrid.h>
- #include<vtkPoints.h>
- #pragma GCC diagnostic warning "-Wdeprecated"
-#endif
-
-namespace py = boost::python;
-using namespace std;
-
-#ifdef YADE_CPM_FULL_MODEL_AVAILABLE
- #include"../../brefcom-mm.hh"
-#endif
-
-# if 0
-Real elasticEnergyDensityInAABB(python::tuple Aabb){
- Vector3r bbMin=tuple2vec(python::extract<python::tuple>(Aabb[0])()), bbMax=tuple2vec(python::extract<python::tuple>(Aabb[1])()); Vector3r box=bbMax-bbMin;
- shared_ptr<Scene> rb=Omega::instance().getScene();
- Real E=0;
- FOREACH(const shared_ptr<Interaction>&i, *rb->interactions){
- if(!i->phys) continue;
- shared_ptr<CpmPhys> bc=dynamic_pointer_cast<CpmPhys>(i->phys); if(!bc) continue;
- const shared_ptr<Body>& b1=Body::byId(i->getId1(),rb), b2=Body::byId(i->getId2(),rb);
- bool isIn1=isInBB(b1->state->pos,bbMin,bbMax), isIn2=isInBB(b2->state->pos,bbMin,bbMax);
- if(!isIn1 && !isIn2) continue;
- Real weight=1.;
- if((!isIn1 && isIn2) || (isIn1 && !isIn2)){
- //shared_ptr<Body> bIn=isIn1?b1:b2, bOut=isIn2?b2:b1;
- Vector3r vIn=(isIn1?b1:b2)->state->pos, vOut=(isIn2?b1:b2)->state->pos;
- #define _WEIGHT_COMPONENT(axis) if(vOut[axis]<bbMin[axis]) weight=min(weight,abs((vOut[axis]-bbMin[axis])/(vOut[axis]-vIn[axis]))); else if(vOut[axis]>bbMax[axis]) weight=min(weight,abs((vOut[axis]-bbMax[axis])/(vOut[axis]-vIn[axis])));
- _WEIGHT_COMPONENT(0); _WEIGHT_COMPONENT(1); _WEIGHT_COMPONENT(2);
- assert(weight>=0 && weight<=1);
- }
- E+=.5*bc->E*bc->crossSection*pow(bc->epsN,2)+.5*bc->G*bc->crossSection*pow(bc->epsT.norm(),2);
- }
- return E/(box[0]*box[1]*box[2]);
-}
-#endif
-
-/*! Set velocity of all dynamic particles so that if their motion were unconstrained,
- * axis given by axisPoint and axisDirection would be reached in timeToAxis
- * (or, point at distance subtractDist from axis would be reached).
- *
- * The code is analogous to AxialGravityEngine and is intended to give initial motion
- * to particles subject to axial compaction to speed up the process. */
-void velocityTowardsAxis(const Vector3r& axisPoint, const Vector3r& axisDirection, Real timeToAxis, Real subtractDist=0., Real perturbation=0.1){
- FOREACH(const shared_ptr<Body>&b, *(Omega::instance().getScene()->bodies)){
- if(b->state->blockedDOFs==State::DOF_ALL) continue;
- const Vector3r& x0=b->state->pos;
- const Vector3r& x1=axisPoint;
- const Vector3r x2=axisPoint+axisDirection;
- Vector3r closestAxisPoint=(x2-x1) * /* t */ (-(x1-x0).dot(x2-x1))/((x2-x1).squaredNorm());
- Vector3r toAxis=closestAxisPoint-x0;
- if(subtractDist>0) toAxis*=(toAxis.norm()-subtractDist)/toAxis.norm();
- b->state->vel=toAxis/timeToAxis;
- Vector3r ppDiff=perturbation*(1./sqrt(3.))*Vector3r(Mathr::UnitRandom(),Mathr::UnitRandom(),Mathr::UnitRandom())*b->state->vel.norm();
- b->state->vel+=ppDiff;
- }
-}
-BOOST_PYTHON_FUNCTION_OVERLOADS(velocityTowardsAxis_overloads,velocityTowardsAxis,3,5);
-
-void particleConfinement(){
- CpmStateUpdater csu; csu.update();
-}
-
-// makes linker error out with monolithic build..
-#if 0
-python::dict testNumpy(){
- Scene* scene=Omega::instance().getScene().get();
- int dim1[]={scene->bodies->size()};
- int dim2[]={scene->bodies->size(),3};
- numpy_boost<Real,1> mass(dim1);
- numpy_boost<Real,2> vel(dim2);
- FOREACH(const shared_ptr<Body>& b, *scene->bodies){
- if(!b) continue;
- mass[b->getId()]=b->state->mass;
- VECTOR3R_TO_NUMPY(vel[b->getId()],b->state->vel);
- }
- py::dict ret;
- ret["mass"]=mass;
- ret["vel"]=vel;
- return ret;
-}
-#endif
-
-#if 0
-/* Compute stress tensor on each particle */
-void particleMacroStress(void){
- Scene* scene=Omega::instance().getScene().get();
- // find interactions of each particle
- std::vector<std::list<Body::id_t> > bIntr(scene->bodies->size());
- FOREACH(const shared_ptr<Interaction>& i, *scene->interactions){
- if(!i->isReal) continue;
- // only contacts between 2 spheres
- Sphere* s1=dynamic_cast<Sphere*>(Body::byId(i->getId1(),scene)->shape.get())
- Sphere* s2=dynamic_cast<Sphere*>(Body::byId(i->getId2(),scene)->shape.get())
- if(!s1 || !s2) continue;
- bIntr[i.getId1()].push_back(i.getId2());
- bIntr[i.getId2()].push_back(i.getId1());
- }
- for(Body::id_t id1=0; id1<(Body::id_t)bIntr.size(); id1++){
- if(bIntr[id1].size()==0) continue;
- Matrix3r ss(Matrix3r::Zero()); // stress tensor on current particle
- FOREACH(Body::id_t id2, bIntr[id1]){
- const shared_ptr<Interaction> i=scene->interactions->find(id1,id2);
- assert(i);
- Dem3DofGeom* geom=YADE_CAST<Dem3DofGeom*>(i->geom);
- CpmPhys* phys=YADE_CAST<CpmPhys*>(i->phys);
- Real d=(geom->se31->pos-geom->se32->pos).norm(); // current contact length
- const Vector3r& n=geom->normal;
- const Real& A=phys->crossSection;
- const Vector3r& sigmaT=phys->sigmaT;
- const Real& sigmaN=phys->sigmaN;
- for(int i=0; i<3; i++) for(int j=0;j<3; j++){
- ss[i][j]=d*A*(sigmaN*n[i]*n[j]+.5*(sigmaT[i]*n[j]+sigmaT[j]*n[i]));
- }
- }
- // divide ss by V of the particle
- // FIXME: for now, just use 2*(4/3)*π*r³ (.5 porosity)
- ss*=1/(2*(4/3)*Mathr::PI*);
- }
-}
-#endif
-#include<yade/lib/smoothing/WeightedAverage2d.hpp>
-/* Fastly locate interactions within given distance from a point in 2d (projected to plane) */
-struct HelixInteractionLocator2d{
- struct FlatInteraction{ Real r,h,theta; shared_ptr<Interaction> i; FlatInteraction(Real _r, Real _h, Real _theta, const shared_ptr<Interaction>& _i): r(_r), h(_h), theta(_theta), i(_i){}; };
- shared_ptr<GridContainer<FlatInteraction> > grid;
- Real thetaSpan;
- int axis;
- HelixInteractionLocator2d(Real dH_dTheta, int _axis, Real periodStart, Real theta0, Real thetaMin, Real thetaMax): axis(_axis){
- Scene* scene=Omega::instance().getScene().get();
- Real inf=std::numeric_limits<Real>::infinity();
- Vector2r lo=Vector2r(inf,inf), hi(-inf,-inf);
- Real minD0(inf),maxD0(-inf);
- Real minTheta(inf), maxTheta(-inf);
- std::list<FlatInteraction> intrs;
- // first pass: find extrema for positions and interaction lengths, build interaction list
- FOREACH(const shared_ptr<Interaction>& i, *scene->interactions){
- Dem3DofGeom* ge=dynamic_cast<Dem3DofGeom*>(i->geom.get());
- CpmPhys* ph=dynamic_cast<CpmPhys*>(i->phys.get());
- if(!ge || !ph) continue;
- Real r,h,theta;
- boost::tie(r,h,theta)=Shop::spiralProject(ge->contactPoint,dH_dTheta,axis,periodStart,theta0);
- if(!isnan(thetaMin) && theta<thetaMin) continue;
- if(!isnan(thetaMax) && theta>thetaMax) continue;
- lo=lo.cwiseMin(Vector2r(r,h)); hi=hi.cwiseMax(Vector2r(r,h));
- minD0=min(minD0,ge->refLength); maxD0=max(maxD0,ge->refLength);
- minTheta=min(minTheta,theta); maxTheta=max(maxTheta,theta);
- intrs.push_back(FlatInteraction(r,h,theta,i));
- }
- // create grid, put interactions inside
- Vector2i nCells=Vector2i(max(10,(int)((hi[0]-lo[0])/(2*minD0))),max(10,(int)((hi[1]-lo[1])/(2*minD0))));
- Vector2r hair=1e-2*Vector2r((hi[0]-lo[0])/nCells[0],(hi[1]-lo[1])/nCells[1]); // avoid rounding issue on boundary, enlarge by 1/100 cell size on each side
- grid=shared_ptr<GridContainer<FlatInteraction> >(new GridContainer<FlatInteraction>(lo-hair,hi+hair,nCells));
- FOREACH(const FlatInteraction& fi, intrs){
- grid->add(fi,Vector2r(fi.r,fi.h));
- }
- thetaSpan=maxTheta-minTheta;
- }
- py::list intrsAroundPt(const Vector2r& pt, Real radius){
- py::list ret;
- FOREACH(const Vector2i& v, grid->circleFilter(pt,radius)){
- FOREACH(const FlatInteraction& fi, grid->grid[v[0]][v[1]]){
- if((pow(fi.r-pt[0],2)+pow(fi.h-pt[1],2))>radius*radius) continue; // too far
- ret.append(fi.i);
- }
- }
- return ret;
- }
- // return macroscopic stress around interactions that project around given point and their average omega
- // stresses are rotated around axis back by theta angle
- python::tuple macroAroundPt(const Vector2r& pt, Real radius){
- Matrix3r ss(Matrix3r::Zero());
- Real omegaCumm=0, kappaCumm=0; int nIntr=0;
- FOREACH(const Vector2i& v, grid->circleFilter(pt,radius)){
- FOREACH(const FlatInteraction& fi, grid->grid[v[0]][v[1]]){
- if((pow(fi.r-pt[0],2)+pow(fi.h-pt[1],2))>radius*radius) continue; // too far
- Dem3DofGeom* geom=YADE_CAST<Dem3DofGeom*>(fi.i->geom.get());
- CpmPhys* phys=YADE_CAST<CpmPhys*>(fi.i->phys.get());
- // transformation matrix, to rotate to the plane
- Vector3r ax(Vector3r::Zero()); ax[axis]=1.;
- Quaternionr q(AngleAxisr(fi.theta,ax)); q=q.conjugate();
- Matrix3r TT=q.toRotationMatrix();
- //
- Real d=(geom->se31.position-geom->se32.position).norm(); // current contact length
- const Vector3r& n=TT*geom->normal;
- const Real& A=phys->crossSection;
- const Vector3r& sigmaT=TT*phys->sigmaT;
- const Real& sigmaN=phys->sigmaN;
- for(int i=0; i<3; i++) for(int j=0;j<3; j++){
- ss(i,j)+=d*A*(sigmaN*n[i]*n[j]+.5*(sigmaT[i]*n[j]+sigmaT[j]*n[i]));
- }
- omegaCumm+=phys->omega; kappaCumm+=phys->kappaD;
- nIntr++;
- }
- }
- // divide by approx spatial volume over which we averaged:
- // spiral cylinder with two half-spherical caps at ends
- ss*=1/((4/3.)*Mathr::PI*pow(radius,3)+Mathr::PI*pow(radius,2)*(thetaSpan*pt[0]-2*radius));
- return python::make_tuple(nIntr,ss,omegaCumm/nIntr,kappaCumm/nIntr);
- }
- Vector2r getLo(){ return grid->getLo(); }
- Vector2r getHi(){ return grid->getHi(); }
-
-};
-
-#ifdef YADE_VTK
-
-/* Fastly locate interactions within given distance from a given point. See python docs for details. */
-class InteractionLocator{
- // object doing the work, see http://www.vtk.org/doc/release/5.2/html/a01048.html
- vtkPointLocator *locator;
- // used by locator
- vtkUnstructuredGrid* grid;
- vtkPoints* points;
- // maps vtk's id to Interaction objects
- vector<shared_ptr<Interaction> > intrs;
- // axis-aligned bounds of all interactions
- Vector3r mn,mx;
- // count of interactions we hold
- int cnt;
- public:
- InteractionLocator(){
- // traverse all real interactions in the current simulation
- // add them to points, create grid with those points
- // store shared_ptr's to interactions in intrs separately
- Scene* scene=Omega::instance().getScene().get();
- locator=vtkPointLocator::New();
- points=vtkPoints::New();
- int count=0;
- FOREACH(const shared_ptr<Interaction>& i, *scene->interactions){
- if(!i->isReal()) continue;
- Dem3DofGeom* ge=dynamic_cast<Dem3DofGeom*>(i->geom.get());
- if(!ge) continue;
- const Vector3r& cp(ge->contactPoint);
- int id=points->InsertNextPoint(cp[0],cp[1],cp[2]);
- if(intrs.size()<=(size_t)id){intrs.resize(id+1000);}
- intrs[id]=i;
- count++;
- }
- if(count==0) throw std::runtime_error("Zero interactions when constructing InteractionLocator!");
- double bb[6];
- points->ComputeBounds(); points->GetBounds(bb);
- mn=Vector3r(bb[0],bb[2],bb[4]); mx=Vector3r(bb[1],bb[3],bb[5]);
- cnt=points->GetNumberOfPoints();
-
- grid=vtkUnstructuredGrid::New();
- grid->SetPoints(points);
- locator->SetDataSet(grid);
- locator->BuildLocator();
- }
- // cleanup
- ~InteractionLocator(){ locator->Delete(); points->Delete(); grid->Delete(); }
-
- py::list intrsAroundPt(const Vector3r& pt, Real radius){
- vtkIdList *ids=vtkIdList::New();
- locator->FindPointsWithinRadius(radius,(const double*)(&pt),ids);
- int numIds=ids->GetNumberOfIds();
- py::list ret;
- for(int i=0; i<numIds; i++){
- // LOG_TRACE("Add "<<i<<"th id "<<ids->GetId(i));
- ret.append(intrs[ids->GetId(i)]);
- }
- return ret;
- }
- python::tuple macroAroundPt(const Vector3r& pt, Real radius, Real forceVolume=-1){
- Matrix3r ss(Matrix3r::Zero());
- vtkIdList *ids=vtkIdList::New();
- locator->FindPointsWithinRadius(radius,(const double*)(&pt),ids);
- int numIds=ids->GetNumberOfIds();
- Real omegaCumm=0, kappaCumm=0;
- for(int k=0; k<numIds; k++){
- const shared_ptr<Interaction>& I(intrs[ids->GetId(k)]);
- Dem3DofGeom* geom=YADE_CAST<Dem3DofGeom*>(I->geom.get());
- CpmPhys* phys=YADE_CAST<CpmPhys*>(I->phys.get());
- Real d=(geom->se31.position-geom->se32.position).norm(); // current contact length
- const Vector3r& n=geom->normal;
- const Real& A=phys->crossSection;
- const Vector3r& sigmaT=phys->sigmaT;
- const Real& sigmaN=phys->sigmaN;
- for(int i=0; i<3; i++) for(int j=0;j<3; j++){
- ss(i,j)+=d*A*(sigmaN*n[i]*n[j]+.5*(sigmaT[i]*n[j]+sigmaT[j]*n[i]));
- }
- omegaCumm+=phys->omega; kappaCumm+=phys->kappaD;
- }
- Real volume=(forceVolume>0?forceVolume:(4/3.)*Mathr::PI*pow(radius,3));
- ss*=1/volume;
- return py::make_tuple(ss,omegaCumm/numIds,kappaCumm/numIds);
- }
- py::tuple getBounds(){ return py::make_tuple(mn,mx);}
- int getCnt(){ return cnt; }
-};
-#endif
-
-BOOST_PYTHON_MODULE(_eudoxos){
- YADE_SET_DOCSTRING_OPTS;
- py::def("velocityTowardsAxis",velocityTowardsAxis,velocityTowardsAxis_overloads(py::args("axisPoint","axisDirection","timeToAxis","subtractDist","perturbation")));
- // def("spiralSphereStresses2d",spiralSphereStresses2d,(python::arg("dH_dTheta"),python::arg("axis")=2));
- py::def("particleConfinement",particleConfinement);
- #if 0
- import_array();
- py::def("testNumpy",testNumpy);
- #endif
-#ifdef YADE_VTK
- py::class_<InteractionLocator>("InteractionLocator","Locate all (real) interactions in space by their :yref:`contact point<Dem3DofGeom::contactPoint>`. When constructed, all real interactions are spatially indexed (uses `vtkPointLocator <http://www.vtk.org/doc/release/5.4/html/a01247.html>`_ internally). Use instance methods to use those data. \n\n.. note::\n\tData might become inconsistent with real simulation state if simulation is being run between creation of this object and spatial queries.")
- .def("intrsAroundPt",&InteractionLocator::intrsAroundPt,((python::arg("point"),python::arg("maxDist"))),"Return list of real interactions that are not further than *maxDist* from *point*.")
- .def("macroAroundPt",&InteractionLocator::macroAroundPt,((python::arg("point"),python::arg("maxDist"),python::arg("forceVolume")=-1)),"Return tuple of averaged stress tensor (as Matrix3), average omega and average kappa values. *forceVolume* can be used (if positive) rather than the sphere (with *maxDist* radius) volume for the computation. (This is useful if *point* and *maxDist* encompass empty space that you want to avoid.)")
- .add_property("bounds",&InteractionLocator::getBounds,"Return coordinates of lower and uppoer corner of axis-aligned abounding box of all interactions")
- .add_property("count",&InteractionLocator::getCnt,"Number of interactions held")
- ;
-#endif
- py::class_<HelixInteractionLocator2d>("HelixInteractionLocator2d",
- "Locate all real interactions in 2d plane (reduced by spiral projection from 3d, using ``Shop::spiralProject``, which is the same as :yref:`yade.utils.spiralProject`) using their :yref:`contact points<Dem3DofGeom::contactPoint>`. \n\n.. note::\n\tDo not run simulation while using this object.",
- python::init<Real,int,Real,Real,Real,Real>((python::arg("dH_dTheta"),python::arg("axis")=0,python::arg("periodStart")=NaN,python::arg("theta0")=0,python::arg("thetaMin")=NaN,python::arg("thetaMax")=NaN),":param float dH_dTheta: Spiral inclination, i.e. height increase per 1 radian turn;\n:param int axis: axis of rotation (0=x,1=y,2=z)\n:param float theta: spiral angle at zero height (theta intercept)\n:param float thetaMin: only interactions with $\\theta$≥\\ *thetaMin* will be considered (NaN to deactivate)\n:param float thetaMax: only interactions with $\\theta$≤\\ *thetaMax* will be considered (NaN to deactivate)\n\nSee :yref:`yade.utils.spiralProject`.")
- )
- .def("intrsAroundPt",&HelixInteractionLocator2d::intrsAroundPt,(python::arg("pt2d"),python::arg("radius")),"Return list of interaction objects that are not further from *pt2d* than *radius* in the projection plane")
- .def("macroAroundPt",&HelixInteractionLocator2d::macroAroundPt,(python::arg("pt2d"),python::arg("radius")),"Compute macroscopic stress around given point; the interaction ($n$ and $\\sigma^T$ are rotated to the projection plane by $\\theta$ (as given by :yref:`yade.utils.spiralProject`) first, but no skew is applied). The formula used is\n\n.. math::\n\n \\sigma_{ij}=\\frac{1}{V}\\sum_{IJ}d^{IJ}A^{IJ}\\left[\\sigma^{N,IJ}n_i^{IJ}n_j^{IJ}+\\frac{1}{2}\\left(\\sigma_i^{T,IJ}n_j^{IJ}+\\sigma_j^{T,IJ}n_i^{IJ}\\right)\\right]\n\nwhere the sum is taken over volume $V$ containing interactions $IJ$ between spheres $I$ and $J$;\n\n* $i$, $j$ indices denote Cartesian components of vectors and tensors,\n* $d^{IJ}$ is current distance between spheres $I$ and $J$,\n* $A^{IJ}$ is area of contact $IJ$,\n* $n$ is ($\\theta$-rotated) interaction normal (unit vector pointing from center of $I$ to the center of $J$)\n* $\\sigma^{N,IJ}$ is normal stress (as scalar) in contact $IJ$,\n* $\\sigma^{T,IJ}$ is shear stress in contact $IJ$ in global coordinates and $\\theta$-rotated. \n\nAdditionally, computes average of :yref:`CpmPhys.omega` ($\\bar\\omega$) and :yref:`CpmPhys.kappaD` ($\\bar\\kappa_D$). *N* is the number of interactions in the volume given.\n\n:return: tuple of (*N*, $\\tens{\\sigma}$, $\\bar\\omega$, $\\bar\\kappa_D$).\n")
- .add_property("lo",&HelixInteractionLocator2d::getLo,"Return lower corner of the rectangle containing all interactions.")
- .add_property("hi",&HelixInteractionLocator2d::getHi,"Return upper corner of the rectangle containing all interactions.");
-
-}
=== modified file 'py/_extraDocs.py'
--- py/_extraDocs.py 2012-09-08 01:19:45 +0000
+++ py/_extraDocs.py 2013-09-19 08:50:04 +0000
@@ -46,7 +46,7 @@
.. note:: *Axis conventions.* Boundaries perpendicular to the *x* axis are called "left" and "right", *y* corresponds to "top" and "bottom", and axis *z* to "front" and "back". In the default loading path, strain rate is assigned along *y*, and constant stresses are assigned on *x* and *z*.
**Essential engines**
- #. The :yref:`TrixaialCompressionEngine` is used for controlling the state of the sample and simulating loading paths. :yref:`TriaxialCompressionEngine` inherits from :yref:`TriaxialStressController`, which computes stress- and strain-like quantities in the packing and maintain a constant level of stress at each boundary. :yref:`TriaxialCompressionEngine` has few more members in order to impose constant strain rate and control the transition between isotropic compression and triaxial test. Transitions are defined by changing some flags of the :yref:`TriaxialStressController`, switching from/to imposed strain rate to/from imposed stress.
+ #. The :yref:`TriaxialCompressionEngine` is used for controlling the state of the sample and simulating loading paths. :yref:`TriaxialCompressionEngine` inherits from :yref:`TriaxialStressController`, which computes stress- and strain-like quantities in the packing and maintain a constant level of stress at each boundary. :yref:`TriaxialCompressionEngine` has few more members in order to impose constant strain rate and control the transition between isotropic compression and triaxial test. Transitions are defined by changing some flags of the :yref:`TriaxialStressController`, switching from/to imposed strain rate to/from imposed stress.
#. The class :yref:`TriaxialStateRecorder` is used to write to a file the history of stresses and strains.
#. :yref:`TriaxialTest` is using :yref:`GlobalStiffnessTimeStepper` to compute an appropriate $\Dt$ for the numerical scheme.
@@ -198,7 +198,6 @@
:yref:`L6Geom` 6 full
:yref:`ScGeom` 3 emulate local coordinate system
:yref:`ScGeom6D` 6 emulate local coordinate system
-:yref:`Dem3DofGeom` 3 *not supported*
=================== ===== ==================================
Depending on :yref:`IGeom`, 3 ($u_x$, $u_y$, $u_z$) or 6 ($u_x$, $u_y$, $u_z$, $\phi_x$, $\phi_y$, $\phi_z$) degrees of freedom (DoFs) are controlled with LawTester, by prescribing linear and angular velocities of both particles in contact. All DoFs controlled with LawTester are orthogonal (fully decoupled) and are controlled independently.
=== added file 'py/_polyhedra_utils.cpp'
--- py/_polyhedra_utils.cpp 1970-01-01 00:00:00 +0000
+++ py/_polyhedra_utils.cpp 2013-10-15 16:14:46 +0000
@@ -0,0 +1,374 @@
+// © 2013 Jan Elias, http://www.fce.vutbr.cz/STM/elias.j/, elias.j@xxxxxxxxxxxx
+// https://www.vutbr.cz/www_base/gigadisk.php?i=95194aa9a
+
+#ifdef YADE_CGAL
+
+#include"yade/pkg/dem/Polyhedra.hpp"
+
+#include<boost/python.hpp>
+#include <boost/python/module.hpp>
+#include<yade/core/Scene.hpp>
+#include<yade/core/Omega.hpp>
+#include<yade/pkg/common/Sphere.hpp>
+#include<yade/pkg/common/ElastMat.hpp>
+#include<yade/lib/pyutil/doc_opts.hpp>
+#include<cmath>
+
+#include<numpy/ndarrayobject.h>
+
+using namespace std;
+namespace py = boost::python;
+
+
+//**********************************************************************************
+//print polyhedron in basic position
+void PrintPolyhedra(const shared_ptr<Shape>& shape){
+ Polyhedra* A = static_cast<Polyhedra*>(shape.get());
+ Polyhedron PA = A->GetPolyhedron();
+ A->Initialize();
+ PrintPolyhedron(PA);
+}
+
+//**********************************************************************************
+//print polyhedron in actual position
+void PrintPolyhedraActualPos(const shared_ptr<Shape>& cm1,const State& state1){
+ const Se3r& se3=state1.se3;
+ Polyhedra* A = static_cast<Polyhedra*>(cm1.get());
+ A->Initialize();
+
+ //move and rotate CGAL structure Polyhedron
+ Matrix3r rot_mat = (se3.orientation).toRotationMatrix();
+ Vector3r trans_vec = se3.position;
+ Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PA = A->GetPolyhedron();
+ std::transform( PA.points_begin(), PA.points_end(), PA.points_begin(), t_rot_trans);
+
+ PrintPolyhedron(PA);
+}
+
+
+//**********************************************************************************
+//test of polyhedron intersection callable from python shell
+bool do_Polyhedras_Intersect(const shared_ptr<Shape>& cm1,const shared_ptr<Shape>& cm2,const State& state1,const State& state2){
+
+ const Se3r& se31=state1.se3;
+ const Se3r& se32=state2.se3;
+ Polyhedra* A = static_cast<Polyhedra*>(cm1.get());
+ Polyhedra* B = static_cast<Polyhedra*>(cm2.get());
+
+ //move and rotate 1st the CGAL structure Polyhedron
+ Matrix3r rot_mat = (se31.orientation).toRotationMatrix();
+ Vector3r trans_vec = se31.position;
+ Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PA = A->GetPolyhedron();
+ std::transform( PA.points_begin(), PA.points_end(), PA.points_begin(), t_rot_trans);
+
+ //move and rotate 2st the CGAL structure Polyhedron
+ rot_mat = (se32.orientation).toRotationMatrix();
+ trans_vec = se32.position;
+ t_rot_trans = Transformation(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PB = B->GetPolyhedron();
+ std::transform( PB.points_begin(), PB.points_end(), PB.points_begin(), t_rot_trans);
+
+ //calculate plane equations
+ std::transform( PA.facets_begin(), PA.facets_end(), PA.planes_begin(),Plane_equation());
+ std::transform( PB.facets_begin(), PB.facets_end(), PB.planes_begin(),Plane_equation());
+
+
+ //call test
+ return do_intersect(PA,PB);
+}
+
+//**********************************************************************************
+//determination of critical time step for polyhedrons & spheres (just rough estimation)
+Real PWaveTimeStep(){
+ const shared_ptr<Scene> _rb=shared_ptr<Scene>();
+ shared_ptr<Scene> rb=(_rb?_rb:Omega::instance().getScene());
+ Real dt=std::numeric_limits<Real>::infinity();
+ FOREACH(const shared_ptr<Body>& b, *rb->bodies){
+ if(!b || !b->material || !b->shape) continue;
+ shared_ptr<Sphere> s=dynamic_pointer_cast<Sphere>(b->shape);
+ shared_ptr<Polyhedra> p=dynamic_pointer_cast<Polyhedra>(b->shape);
+ if(!s && !p) continue;
+ if(!p){
+ //spheres
+ shared_ptr<ElastMat> ebp=dynamic_pointer_cast<ElastMat>(b->material);
+ if(!ebp) continue;
+ Real density=b->state->mass/((4./3.)*Mathr::PI*pow(s->radius,3));
+ dt=min(dt,s->radius/sqrt(ebp->young/density));
+ }else{
+ //polyhedrons
+ shared_ptr<PolyhedraMat> ebp=dynamic_pointer_cast<PolyhedraMat>(b->material);
+ if(!ebp) continue;
+ Real density=b->state->mass/p->GetVolume();
+ //get equivalent radius and use same equation as for sphere
+ Real equi_radius=pow(p->GetVolume()/((4./3.)*Mathr::PI),1./3.);
+ //dt=min(dt,equi_radius/sqrt(ebp->Kn*equi_radius/density));
+ dt=min(dt,equi_radius/sqrt(ebp->Kn/equi_radius/density));
+ }
+ }
+ if (dt==std::numeric_limits<Real>::infinity()) {
+ dt = 1.0;
+ LOG_WARN("PWaveTimeStep has not found any suitable spherical or polyhedral body to calculate dt. dt is set to 1.0");
+ }
+ return dt;
+}
+
+//**********************************************************************************
+//returns approximate sieve size of polyhedron
+Real SieveSize(const shared_ptr<Shape>& cm1){
+ Polyhedra* A = static_cast<Polyhedra*>(cm1.get());
+
+ double phi = M_PI/4.;
+ double x,y;
+ double minx = 0, maxx = 0, miny = 0, maxy = 0;
+
+ for (vector<Vector3r>::iterator i=A->v.begin(); i!=A->v.end(); ++i){
+ x = cos(phi)*(*i)[1] + sin(phi)*(*i)[2];
+ y = -sin(phi)*(*i)[1] + cos(phi)*(*i)[2];
+ minx = min(minx,x);
+ maxx = max(maxx,x);
+ miny = min(miny,y);
+ maxy = max(maxy,y);
+ }
+
+ return max(maxx-minx, maxy-miny);
+}
+
+
+//**********************************************************************************
+//returns approximate size of polyhedron
+Vector3r SizeOfPolyhedra(const shared_ptr<Shape>& cm1){
+ Polyhedra* A = static_cast<Polyhedra*>(cm1.get());
+
+ double minx = 0, maxx = 0, miny = 0, maxy = 0, minz = 0, maxz = 0;
+
+ for (vector<Vector3r>::iterator i=A->v.begin(); i!=A->v.end(); ++i){
+ minx = min(minx,(*i)[0]);
+ maxx = max(maxx,(*i)[0]);
+ miny = min(miny,(*i)[1]);
+ maxy = max(maxy,(*i)[1]);
+ minz = min(minz,(*i)[2]);
+ maxz = max(maxz,(*i)[2]);
+ }
+
+ return Vector3r(maxx-minx, maxy-miny, maxz-minz);
+}
+
+//**********************************************************************************
+//save sieve curve points into a file
+void SieveCurve(){
+ const shared_ptr<Scene> _rb=shared_ptr<Scene>();
+ shared_ptr<Scene> rb=(_rb?_rb:Omega::instance().getScene());
+ std::vector< std::pair<double,double> > sieve_volume;
+ double total_volume = 0;
+ FOREACH(const shared_ptr<Body>& b, *rb->bodies){
+ if(!b || !b->shape) continue;
+ shared_ptr<Polyhedra> p=dynamic_pointer_cast<Polyhedra>(b->shape);
+ if(p){
+ sieve_volume.push_back(std::pair<double,double>(SieveSize(p),p->GetVolume()));
+ total_volume += p->GetVolume();
+ }
+ }
+
+ std::sort(sieve_volume.begin(), sieve_volume.end()) ;
+ double cumul_vol = 0;
+
+ ofstream myfile;
+ myfile.open ("sieve_curve.dat");
+ for(std::vector< std::pair<double,double> > :: iterator i = sieve_volume.begin(); i != sieve_volume.end(); ++i) {
+ cumul_vol += i->second/total_volume;
+ myfile << i->first << "\t" << cumul_vol << endl;
+ }
+ myfile.close();
+}
+
+//**********************************************************************************
+//save size of polyhedrons into a file
+void SizeRatio(){
+ const shared_ptr<Scene> _rb=shared_ptr<Scene>();
+ shared_ptr<Scene> rb=(_rb?_rb:Omega::instance().getScene());
+ ofstream myfile;
+ myfile.open ("sizes.dat");
+ FOREACH(const shared_ptr<Body>& b, *rb->bodies){
+ if(!b || !b->shape) continue;
+ shared_ptr<Polyhedra> p=dynamic_pointer_cast<Polyhedra>(b->shape);
+ if(p){
+ myfile << SizeOfPolyhedra(p) << endl;
+ }
+ }
+ myfile.close();
+}
+
+//**********************************************************************************
+//returns max coordinates
+Vector3r MaxCoord(const shared_ptr<Shape>& cm1,const State& state1){
+ const Se3r& se3=state1.se3;
+ Polyhedra* A = static_cast<Polyhedra*>(cm1.get());
+
+ //move and rotate CGAL structure Polyhedron
+ Matrix3r rot_mat = (se3.orientation).toRotationMatrix();
+ Vector3r trans_vec = se3.position;
+ Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PA = A->GetPolyhedron();
+ std::transform( PA.points_begin(), PA.points_end(), PA.points_begin(), t_rot_trans);
+
+ Vector3r maxccord = trans_vec;
+ for(Polyhedron::Vertex_iterator vi = PA.vertices_begin(); vi != PA.vertices_end(); ++vi){
+ if (vi->point()[0]>maxccord[0]) maxccord[0]=vi->point()[0];
+ if (vi->point()[1]>maxccord[1]) maxccord[1]=vi->point()[1];
+ if (vi->point()[2]>maxccord[2]) maxccord[2]=vi->point()[2];
+ }
+
+ return maxccord;
+}
+
+//**********************************************************************************
+//returns min coordinates
+Vector3r MinCoord(const shared_ptr<Shape>& cm1,const State& state1){
+ const Se3r& se3=state1.se3;
+ Polyhedra* A = static_cast<Polyhedra*>(cm1.get());
+
+ //move and rotate CGAL structure Polyhedron
+ Matrix3r rot_mat = (se3.orientation).toRotationMatrix();
+ Vector3r trans_vec = se3.position;
+ Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PA = A->GetPolyhedron();
+ std::transform( PA.points_begin(), PA.points_end(), PA.points_begin(), t_rot_trans);
+
+ Vector3r minccord = trans_vec;
+ for(Polyhedron::Vertex_iterator vi = PA.vertices_begin(); vi != PA.vertices_end(); ++vi){
+ if (vi->point()[0]<minccord[0]) minccord[0]=vi->point()[0];
+ if (vi->point()[1]<minccord[1]) minccord[1]=vi->point()[1];
+ if (vi->point()[2]<minccord[2]) minccord[2]=vi->point()[2];
+ }
+
+ return minccord;
+}
+
+
+//**********************************************************************************
+//generate "packing" of non-overlapping polyhedrons
+vector<Vector3r> fillBox_cpp(Vector3r minCoord, Vector3r maxCoord, Vector3r sizemin, Vector3r sizemax, Vector3r ratio, int seed, shared_ptr<Material> mat){
+ vector<Vector3r> v;
+ Polyhedra trialP;
+ Polyhedron trial, trial_moved;
+ srand(seed);
+ int it = 0;
+ vector<Polyhedron> polyhedrons;
+ vector<vector<Vector3r> > vv;
+ Vector3r position;
+ bool intersection;
+ int count = 0;
+
+ bool fixed_ratio = 0;
+ if (ratio[0] > 0 && ratio[1] > 0 && ratio[2]>0){
+ fixed_ratio = 1;
+ sizemax[0] = min(min(sizemax[0]/ratio[0], sizemax[1]/ratio[1]), sizemax[2]/ratio[2]);
+ sizemin[0] = max(max(sizemin[0]/ratio[0], sizemin[1]/ratio[1]), sizemin[2]/ratio[2]);
+ }
+
+ //it - number of trials to make packing possibly more/less dense
+ Vector3r random_size;
+ while (it<1000){
+ it = it+1;
+ if (it == 1){
+ trialP.Clear();
+ trialP.seed = rand();
+ if(fixed_ratio) trialP.size = (rand()*(sizemax[0]-sizemin[0])/RAND_MAX + sizemin[0])*ratio;
+ else trialP.size = Vector3r(rand()*(sizemax[0]-sizemin[0]),rand()*(sizemax[1]-sizemin[1]),rand()*(sizemax[2]-sizemin[2]))/RAND_MAX + sizemin;
+ trialP.Initialize();
+ trial = trialP.GetPolyhedron();
+ Matrix3r rot_mat = (trialP.GetOri()).toRotationMatrix();
+ Transformation t_rot(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2),rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),1.);
+ std::transform( trial.points_begin(), trial.points_end(), trial.points_begin(), t_rot);
+ }
+ position = Vector3r(rand()*(maxCoord[0]-minCoord[0]),rand()*(maxCoord[1]-minCoord[1]),rand()*(maxCoord[2]-minCoord[2]))/RAND_MAX + minCoord;
+
+ //move CGAL structure Polyhedron
+ Transformation transl(CGAL::TRANSLATION, ToCGALVector(position));
+ trial_moved = trial;
+ std::transform( trial_moved.points_begin(), trial_moved.points_end(), trial_moved.points_begin(), transl);
+ //calculate plane equations
+ std::transform( trial_moved.facets_begin(), trial_moved.facets_end(), trial_moved.planes_begin(),Plane_equation());
+
+ intersection = false;
+ //call test with boundary
+ for(Polyhedron::Vertex_iterator vi = trial_moved.vertices_begin(); (vi != trial_moved.vertices_end()) && (!intersection); vi++){
+ intersection = (vi->point().x()<minCoord[0]) || (vi->point().x()>maxCoord[0]) || (vi->point().y()<minCoord[1]) || (vi->point().y()>maxCoord[1]) || (vi->point().z()<minCoord[2]) || (vi->point().z()>maxCoord[2]);
+ }
+ //call test with other polyhedrons
+ for(vector<Polyhedron>::iterator a = polyhedrons.begin(); (a != polyhedrons.end()) && (!intersection); a++){
+ intersection = do_intersect(*a,trial_moved);
+ if (intersection) break;
+ }
+ if (!intersection){
+ polyhedrons.push_back(trial_moved);
+ v.clear();
+ for(Polyhedron::Vertex_iterator vi = trial_moved.vertices_begin(); vi != trial_moved.vertices_end(); vi++){
+ v.push_back(FromCGALPoint(vi->point()));
+ }
+ vv.push_back(v);
+ it = 0;
+ count ++;
+
+ }
+ }
+ cout << "generated " << count << " polyhedrons"<< endl;
+
+ //can't be used - no information about material
+ Scene* scene=Omega::instance().getScene().get();
+ for(vector<vector<Vector3r> >::iterator p=vv.begin(); p!=vv.end(); ++p){
+ shared_ptr<Body> BP = NewPolyhedra(*p, mat);
+ BP->shape->color = Vector3r(double(rand())/RAND_MAX,double(rand())/RAND_MAX,double(rand())/RAND_MAX);
+ scene->bodies->insert(BP);
+ }
+ return v;
+}
+
+//**********************************************************************************
+//split polyhedra
+void Split(const shared_ptr<Body> body, Vector3r direction){
+ SplitPolyhedra(body, direction);
+}
+
+//**********************************************************************************
+//distace of point from a plane (squared) with sign
+double Oriented_squared_distance2(Plane P, CGALpoint x){
+ double h = P.a()*x.x()+P.b()*x.y()+P.c()*x.z()+P.d();
+ return ((h>0.)-(h<0.))*pow(h,2)/(CGALvector(P.a(),P.b(),P.c())).squared_length();
+}
+
+//**********************************************************************************
+bool convexHull(vector<Vector3r> points){
+ vector<CGALpoint> pointsCGAL;
+ for(int i=0;i<(int) points.size() ;i++) {
+ pointsCGAL.push_back(ToCGALPoint(points[i]));
+ }
+ Polyhedron P;
+ CGAL::convex_hull_3(pointsCGAL.begin(), pointsCGAL.end(), P);
+ return true;
+}
+
+BOOST_PYTHON_MODULE(_polyhedra_utils){
+ // http://numpy.scipy.org/numpydoc/numpy-13.html mentions this must be done in module init, otherwise we will crash
+ import_array();
+
+ YADE_SET_DOCSTRING_OPTS;
+
+ py::def("PrintPolyhedra",PrintPolyhedra,"Print list of vertices sorted according to polyhedrons facets.");
+ py::def("PrintPolyhedraActualPos",PrintPolyhedraActualPos,"Print list of vertices sorted according to polyhedrons facets.");
+ py::def("PWaveTimeStep",PWaveTimeStep,"Get timestep accoring to the velocity of P-Wave propagation; computed from sphere radii, rigidities and masses.");
+ py::def("do_Polyhedras_Intersect",do_Polyhedras_Intersect,"check polyhedras intersection");
+ py::def("fillBox_cpp",fillBox_cpp,"Generate non-overlaping polyhedrons in box");
+ py::def("MinCoord",MinCoord,"returns min coordinates");
+ py::def("MaxCoord",MaxCoord,"returns max coordinates");
+ py::def("SieveSize",SieveSize,"returns approximate sieve size of polyhedron");
+ py::def("SieveCurve",SieveCurve,"save sieve curve coordinates into file");
+ py::def("SizeOfPolyhedra",SizeOfPolyhedra,"returns max, middle an min size in perpendicular directions");
+ py::def("SizeRatio",SizeRatio,"save sizes of polyhedra into file");
+ py::def("convexHull",convexHull,"....");
+ py::def("Split",Split,"split polyhedron perpendicularly to given direction direction");
+}
+
+#endif // YADE_CGAL
=== modified file 'py/_utils.cpp'
--- py/_utils.cpp 2013-05-31 13:27:13 +0000
+++ py/_utils.cpp 2013-11-13 07:37:51 +0000
@@ -7,6 +7,7 @@
#include<yade/pkg/dem/ScGeom.hpp>
#include<yade/pkg/dem/DemXDofGeom.hpp>
#include<yade/pkg/common/Facet.hpp>
+#include<yade/pkg/dem/Tetra.hpp>
#include<yade/pkg/common/Sphere.hpp>
#include<yade/pkg/common/NormShearPhys.hpp>
#include<yade/lib/computational-geometry/Hull2d.hpp>
@@ -75,32 +76,33 @@
Real PWaveTimeStep(){return Shop::PWaveTimeStep();};
Real RayleighWaveTimeStep(){return Shop::RayleighWaveTimeStep();};
-Real elasticEnergyInAABB(py::tuple Aabb){
- Vector3r bbMin=py::extract<Vector3r>(Aabb[0])(), bbMax=py::extract<Vector3r>(Aabb[1])();
- shared_ptr<Scene> rb=Omega::instance().getScene();
- Real E=0;
- FOREACH(const shared_ptr<Interaction>&i, *rb->interactions){
- if(!i->phys) continue;
- shared_ptr<NormShearPhys> bc=dynamic_pointer_cast<NormShearPhys>(i->phys); if(!bc) continue;
- shared_ptr<Dem3DofGeom> geom=dynamic_pointer_cast<Dem3DofGeom>(i->geom); if(!geom){LOG_ERROR("NormShearPhys contact doesn't have Dem3DofGeom associated?!"); continue;}
- const shared_ptr<Body>& b1=Body::byId(i->getId1(),rb), b2=Body::byId(i->getId2(),rb);
- bool isIn1=isInBB(b1->state->pos,bbMin,bbMax), isIn2=isInBB(b2->state->pos,bbMin,bbMax);
- if(!isIn1 && !isIn2) continue;
- LOG_DEBUG("Interaction #"<<i->getId1()<<"--#"<<i->getId2());
- Real weight=1.;
- if((!isIn1 && isIn2) || (isIn1 && !isIn2)){
- //shared_ptr<Body> bIn=isIn1?b1:b2, bOut=isIn2?b2:b1;
- Vector3r vIn=(isIn1?b1:b2)->state->pos, vOut=(isIn2?b1:b2)->state->pos;
- #define _WEIGHT_COMPONENT(axis) if(vOut[axis]<bbMin[axis]) weight=min(weight,abs((vOut[axis]-bbMin[axis])/(vOut[axis]-vIn[axis]))); else if(vOut[axis]>bbMax[axis]) weight=min(weight,abs((vOut[axis]-bbMax[axis])/(vOut[axis]-vIn[axis])));
- _WEIGHT_COMPONENT(0); _WEIGHT_COMPONENT(1); _WEIGHT_COMPONENT(2);
- assert(weight>=0 && weight<=1);
- //cerr<<"Interaction crosses Aabb boundary, weight is "<<weight<<endl;
- //LOG_DEBUG("Interaction crosses Aabb boundary, weight is "<<weight);
- } else {assert(isIn1 && isIn2); /* cerr<<"Interaction inside, weight is "<<weight<<endl;*/ /*LOG_DEBUG("Interaction inside, weight is "<<weight);*/}
- E+=geom->refLength*weight*(.5*bc->kn*pow(geom->strainN(),2)+.5*bc->ks*pow(geom->strainT().norm(),2));
- }
- return E;
-}
+// FIXME: rewrite for ScGeom or remove
+// Real elasticEnergyInAABB(py::tuple Aabb){
+// Vector3r bbMin=py::extract<Vector3r>(Aabb[0])(), bbMax=py::extract<Vector3r>(Aabb[1])();
+// shared_ptr<Scene> rb=Omega::instance().getScene();
+// Real E=0;
+// FOREACH(const shared_ptr<Interaction>&i, *rb->interactions){
+// if(!i->phys) continue;
+// shared_ptr<NormShearPhys> bc=dynamic_pointer_cast<NormShearPhys>(i->phys); if(!bc) continue;
+// shared_ptr<Dem3DofGeom> geom=dynamic_pointer_cast<Dem3DofGeom>(i->geom); if(!geom){LOG_ERROR("NormShearPhys contact doesn't have Dem3DofGeom associated?!"); continue;}
+// const shared_ptr<Body>& b1=Body::byId(i->getId1(),rb), b2=Body::byId(i->getId2(),rb);
+// bool isIn1=isInBB(b1->state->pos,bbMin,bbMax), isIn2=isInBB(b2->state->pos,bbMin,bbMax);
+// if(!isIn1 && !isIn2) continue;
+// LOG_DEBUG("Interaction #"<<i->getId1()<<"--#"<<i->getId2());
+// Real weight=1.;
+// if((!isIn1 && isIn2) || (isIn1 && !isIn2)){
+// //shared_ptr<Body> bIn=isIn1?b1:b2, bOut=isIn2?b2:b1;
+// Vector3r vIn=(isIn1?b1:b2)->state->pos, vOut=(isIn2?b1:b2)->state->pos;
+// #define _WEIGHT_COMPONENT(axis) if(vOut[axis]<bbMin[axis]) weight=min(weight,abs((vOut[axis]-bbMin[axis])/(vOut[axis]-vIn[axis]))); else if(vOut[axis]>bbMax[axis]) weight=min(weight,abs((vOut[axis]-bbMax[axis])/(vOut[axis]-vIn[axis])));
+// _WEIGHT_COMPONENT(0); _WEIGHT_COMPONENT(1); _WEIGHT_COMPONENT(2);
+// assert(weight>=0 && weight<=1);
+// //cerr<<"Interaction crosses Aabb boundary, weight is "<<weight<<endl;
+// //LOG_DEBUG("Interaction crosses Aabb boundary, weight is "<<weight);
+// } else {assert(isIn1 && isIn2); /* cerr<<"Interaction inside, weight is "<<weight<<endl;*/ /*LOG_DEBUG("Interaction inside, weight is "<<weight);*/}
+// E+=geom->refLength*weight*(.5*bc->kn*pow(geom->strainN(),2)+.5*bc->ks*pow(geom->strainT().norm(),2));
+// }
+// return E;
+// }
/* return histogram ([bin1Min,bin2Min,…],[value1,value2,…]) from projections of interactions
* to the plane perpendicular to axis∈[0,1,2]; the number of bins can be specified and they cover
@@ -362,9 +364,12 @@
NormShearPhys* nsi=dynamic_cast<NormShearPhys*>(I->phys.get());
if(!nsi) continue;
Vector3r pos1,pos2;
+ /*
Dem3DofGeom* d3dg=dynamic_cast<Dem3DofGeom*>(I->geom.get()); // Dem3DofGeom has copy of se3 in itself, otherwise we have to look up the bodies
if(d3dg){ pos1=d3dg->se31.position; pos2=d3dg->se32.position; }
else{ pos1=Body::byId(I->getId1(),scene)->state->pos; pos2=Body::byId(I->getId2(),scene)->state->pos; }
+ */
+ pos1=Body::byId(I->getId1(),scene)->state->pos; pos2=Body::byId(I->getId2(),scene)->state->pos;
Real dot1=(pos1-planePt).dot(normal), dot2=(pos2-planePt).dot(normal);
if(dot1*dot2>0) continue; // both (centers of) bodies are on the same side of the plane=> this interaction has to be disregarded
// if pt1 is on the negative plane side, d3dg->normal.Dot(normal)>0, the force is well oriented;
@@ -507,7 +512,7 @@
py::def("setRefSe3",setRefSe3,"Set reference :yref:`positions<State::refPos>` and :yref:`orientations<State::refOri>` of all :yref:`bodies<Body>` equal to their current :yref:`positions<State::pos>` and :yref:`orientations<State::ori>`.");
py::def("interactionAnglesHistogram",interactionAnglesHistogram,interactionAnglesHistogram_overloads(py::args("axis","mask","bins","aabb")));
py::def("bodyNumInteractionsHistogram",bodyNumInteractionsHistogram,bodyNumInteractionsHistogram_overloads(py::args("aabb")));
- py::def("elasticEnergy",elasticEnergyInAABB);
+// py::def("elasticEnergy",elasticEnergyInAABB);
py::def("inscribedCircleCenter",inscribedCircleCenter,(py::arg("v1"),py::arg("v2"),py::arg("v3")),"Return center of inscribed circle for triangle given by its vertices *v1*, *v2*, *v3*.");
py::def("unbalancedForce",&Shop__unbalancedForce,(py::args("useMaxForce")=false),"Compute the ratio of mean (or maximum, if *useMaxForce*) summary force on bodies and mean force magnitude on interactions. For perfectly static equilibrium, summary force on all bodies is zero (since forces from interactions cancel out and induce no acceleration of particles); this ratio will tend to zero as simulation stabilizes, though zero is never reached because of finite precision computation. Sufficiently small value can be e.g. 1e-2 or smaller, depending on how much equilibrium it should be.");
py::def("kineticEnergy",Shop__kineticEnergy,(py::args("findMaxId")=false),"Compute overall kinetic energy of the simulation as\n\n.. math:: \\sum\\frac{1}{2}\\left(m_i\\vec{v}_i^2+\\vec{\\omega}(\\mat{I}\\vec{\\omega}^T)\\right).\n\nFor :yref:`aspherical<Body.aspherical>` bodies, the inertia tensor $\\mat{I}$ is transformed to global frame, before multiplied by $\\vec{\\omega}$, therefore the value should be accurate.\n");
@@ -526,7 +531,7 @@
py::def("wireNone",wireNone,"Set :yref:`Shape::wire` on all bodies to False, rendering them as solids.");
py::def("wireNoSpheres",wireNoSpheres,"Set :yref:`Shape::wire` to True on non-spherical bodies (:yref:`Facets<Facet>`, :yref:`Walls<Wall>`).");
py::def("flipCell",&Shop::flipCell,(py::arg("flip")=Matrix3r(Matrix3r::Zero())),"Flip periodic cell so that angles between $R^3$ axes and transformed axes are as small as possible. This function relies on the fact that periodic cell defines by repetition or its corners regular grid of points in $R^3$; however, all cells generating identical grid are equivalent and can be flipped one over another. This necessiatates adjustment of :yref:`Interaction.cellDist` for interactions that cross boundary and didn't before (or vice versa), and re-initialization of collider. The *flip* argument can be used to specify desired flip: integers, each column for one axis; if zero matrix, best fit (minimizing the angles) is computed automatically.\n\nIn c++, this function is accessible as ``Shop::flipCell``.\n\n.. warning:: This function is currently broken and should not be used.");
- py::def("getViscoelasticFromSpheresInteraction",getViscoelasticFromSpheresInteraction,(py::arg("tc"),py::arg("en"),py::arg("es")),"Compute viscoelastic interaction parameters from analytical solution of a pair spheres collision problem:\n\n.. math:: k_n=\\frac{m}{t_c^2}\\left(\\pi^2+(\\ln e_n)^2\\right) \\\\ c_n=-\\frac{2m}{t_c} \\\\ k_t=\\frac{2}{7}\\frac{m}{t_c^2}\\left(\\pi^2+(\\ln e_t)^2\\right) \\\\ c_t=-\\frac{2}{7}\\frac{m}{t_c}\\ln e_t \n\n\nwhere $k_n$, $c_n$ are normal elastic and viscous coefficients and $k_t$, $c_t$ shear elastic and viscous coefficients. For details see [Pournin2001]_.\n\n:param float m: sphere mass $m$\n:param float tc: collision time $t_c$\n:param float en: normal restitution coefficient $e_n$\n:param float es: tangential restitution coefficient $e_s$\n:return: dictionary with keys ``kn`` (the value of $k_n$), ``cn`` ($c_n$), ``kt`` ($k_t$), ``ct`` ($c_t$).");
+ py::def("getViscoelasticFromSpheresInteraction",getViscoelasticFromSpheresInteraction,(py::arg("tc"),py::arg("en"),py::arg("es")),"Compute viscoelastic interaction parameters from analytical solution of a pair spheres collision problem:\n\n.. math:: k_n=\\frac{m}{t_c^2}\\left(\\pi^2+(\\ln e_n)^2\\right) \\\\ c_n=-\\frac{2m}{t_c}\\ln e_n \\\\ k_t=\\frac{2}{7}\\frac{m}{t_c^2}\\left(\\pi^2+(\\ln e_t)^2\\right) \\\\ c_t=-\\frac{2}{7}\\frac{m}{t_c}\\ln e_t \n\n\nwhere $k_n$, $c_n$ are normal elastic and viscous coefficients and $k_t$, $c_t$ shear elastic and viscous coefficients. For details see [Pournin2001]_.\n\n:param float m: sphere mass $m$\n:param float tc: collision time $t_c$\n:param float en: normal restitution coefficient $e_n$\n:param float es: tangential restitution coefficient $e_s$\n:return: dictionary with keys ``kn`` (the value of $k_n$), ``cn`` ($c_n$), ``kt`` ($k_t$), ``ct`` ($c_t$).");
py::def("stressTensorOfPeriodicCell",Shop::getStress,(py::args("volume")=0),"Deprecated, use utils.getStress instead |ydeprecated|");
//py::def("stressTensorOfPeriodicCell",Shop__stressTensorOfPeriodicCell,(py::args("smallStrains")=false),"Compute overall (macroscopic) stress of periodic cell using equation published in [Kuhl2001]_:\n\n.. math:: \\vec{\\sigma}=\\frac{1}{V}\\sum_cl^c[\\vec{N}^cf_N^c+\\vec{T}^{cT}\\cdot\\vec{f}^c_T],\n\nwhere $V$ is volume of the cell, $l^c$ length of interaction $c$, $f^c_N$ normal force and $\\vec{f}^c_T$ shear force. Sumed are values over all interactions $c$. $\\vec{N}^c$ and $\\vec{T}^{cT}$ are projection tensors (see the original publication for more details):\n\n.. math:: \\vec{N}=\\vec{n}\\otimes\\vec{n}\\rightarrow N_{ij}=n_in_j\n\n.. math:: \\vec{T}^T=\\vec{I}_{sym}\\cdot\\vec{n}-\\vec{n}\\otimes\\vec{n}\\otimes\\vec{n}\\rightarrow T^T_{ijk}=\\frac{1}{2}(\\delta_{ik}\\delta_{jl}+\\delta_{il}\\delta_{jk})n_l-n_in_jn_k\n\n.. math:: \\vec{T}^T\\cdot\\vec{f}_T\\equiv T^T_{ijk}f_k=(\\delta_{ik}n_j/2+\\delta_{jk}n_i/2-n_in_jn_k)f_k=n_jf_i/2+n_if_j/2-n_in_jn_kf_k,\n\nwhere $n$ is unit vector oriented along the interaction (:yref:`normal<GenericSpheresContact::normal>`) and $\\delta$ is Kronecker's delta. As $\\vec{n}$ and $\\vec{f}_T$ are perpendicular (therfore $n_if_i=0$) we can write\n\n.. math:: \\sigma_{ij}=\\frac{1}{V}\\sum l[n_in_jf_N+n_jf^T_i/2+n_if^T_j/2]\n\n:param bool smallStrains: if false (large strains), real values of volume and interaction lengths are computed. If true, only :yref:`refLength<Dem3DofGeom::refLength>` of interactions and initial volume are computed (can save some time).\n\n:return: macroscopic stress tensor as Matrix3");
py::def("normalShearStressTensors",Shop__normalShearStressTensors,(py::args("compressionPositive")=false,py::args("splitNormalTensor")=false,py::args("thresholdForce")=NaN),"Compute overall stress tensor of the periodic cell decomposed in 2 parts, one contributed by normal forces, the other by shear forces. The formulation can be found in [Thornton2000]_, eq. (3):\n\n.. math:: \\tens{\\sigma}_{ij}=\\frac{2}{V}\\sum R N \\vec{n}_i \\vec{n}_j+\\frac{2}{V}\\sum R T \\vec{n}_i\\vec{t}_j\n\nwhere $V$ is the cell volume, $R$ is \"contact radius\" (in our implementation, current distance between particle centroids), $\\vec{n}$ is the normal vector, $\\vec{t}$ is a vector perpendicular to $\\vec{n}$, $N$ and $T$ are norms of normal and shear forces.\n\n:param bool splitNormalTensor: if true the function returns normal stress tensor split into two parts according to the two subnetworks of strong an weak forces.\n\n:param Real thresholdForce: threshold value according to which the normal stress tensor can be split (e.g. a zero value would make distinction between tensile and compressive forces).");
@@ -540,7 +545,15 @@
py::def("calm",Shop__calm,(py::arg("mask")=-1),"Set translational and rotational velocities of all bodies to zero.");
py::def("setNewVerticesOfFacet",setNewVerticesOfFacet,(py::arg("b"),py::arg("v1"),py::arg("v2"),py::arg("v3")),"Sets new vertices (in global coordinates) to given facet.");
py::def("setContactFriction",Shop::setContactFriction,py::arg("angleRad"),"Modify the friction angle (in radians) inside the material classes and existing contacts. The friction for non-dynamic bodies is not modified.");
- py::def("growParticles",Shop::growParticles,(py::args("multiplier"), py::args("updateMass")=true, py::args("dynamicOnly")=true), "Change the size of spheres and sphere clumps by the multiplier. If updateMass=True, then the mass is updated. dynamicOnly=True is mandatory in many cases since in current implementation the function would crash on the non-spherical and non-dynamic bodies (e.g. facets, boxes, etc.)");
+ py::def("growParticles",Shop::growParticles,(py::args("multiplier"), py::args("updateMass")=true, py::args("dynamicOnly")=true, py::args("discretization")=15, py::args("integrateInertia")=true), "Change the size of spheres and sphere clumps by the multiplier. If updateMass=True, then the mass is updated. dynamicOnly=True is mandatory in many cases since in current implementation the function would crash on the non-spherical and non-dynamic bodies (e.g. facets, boxes, etc.). For clumps the masses and inertias are adapted automatically (for details of inertia tensor integration scheme see :yref:`clump()<BodyContainer.clump>`).");
+ py::def("growParticle",Shop::growParticle,(py::args("bodyID"),py::args("multiplier"), py::args("updateMass")=true), "Change the size of a single sphere (to be implemented: single clump). If updateMass=True, then the mass is updated.");
py::def("intrsOfEachBody",intrsOfEachBody,"returns list of lists of interactions of each body");
py::def("numIntrsOfEachBody",numIntrsOfEachBody,"returns list of number of interactions of each body");
+ py::def("TetrahedronSignedVolume",static_cast<Real (*)(const vector<Vector3r>&)>(&TetrahedronSignedVolume),"TODO");
+ py::def("TetrahedronVolume",static_cast<Real (*)(const vector<Vector3r>&)>(&TetrahedronVolume),"TODO");
+ py::def("TetrahedronInertiaTensor",TetrahedronInertiaTensor,"TODO");
+ py::def("TetrahedronCentralInertiaTensor",TetrahedronCentralInertiaTensor,"TODO");
+ py::def("TetrahedronWithLocalAxesPrincipal",TetrahedronWithLocalAxesPrincipal,"TODO");
+ py::def("momentum",Shop::momentum,"TODO");
+ py::def("angularMomentum",Shop::angularMomentum,(py::args("origin")=Vector3r(Vector3r::Zero())),"TODO");
}
=== removed file 'py/eudoxos.py'
--- py/eudoxos.py 2013-04-24 19:49:00 +0000
+++ py/eudoxos.py 1970-01-01 00:00:00 +0000
@@ -1,311 +0,0 @@
-# encoding: utf-8
-# 2008 © Václav Šmilauer <eudoxos@xxxxxxxx>
-#
-# I doubt there functions will be useful for anyone besides me.
-#
-"""Miscillaneous functions that are not believed to be generally usable,
-therefore kept in my "private" module here.
-
-They comprise notably oofem export and various CPM-related functions.
-"""
-
-from yade.wrapper import *
-from math import *
-from yade._eudoxos import * ## c++ implementations
-
-
-class IntrSmooth3d():
- r"""Return spatially weigted gaussian average of arbitrary quantity defined on interactions.
-
- At construction time, all real interactions are put inside spatial grid, permitting fast search for
- points in neighbourhood defined by distance.
-
- Parameters for the distribution are standard deviation :math:`\sigma` and relative cutoff distance
- *relThreshold* (3 by default) which will discard points farther than *relThreshold* :math:`\times \sigma`.
-
- Given central point :math:`p_0`, points are weighted by gaussian function
-
- .. math::
-
- \rho(p_0,p)=\frac{1}{\sigma\sqrt{2\pi}}\exp\left(\frac{-||p_0-p||^2}{2\sigma^2}\right)
-
- To get the averaged value, simply call the instance, passing central point and callable object which received interaction object and returns the desired quantity:
-
- >>> O.reset()
- >>> from yade import utils
- >>> O.bodies.append([utils.sphere((0,0,0),1),utils.sphere((0,0,1.9),1)])
- [0, 1]
- >>> O.engines=[InteractionLoop([Ig2_Sphere_Sphere_ScGeom(),],[Ip2_FrictMat_FrictMat_FrictPhys()],[])]
- >>> utils.createInteraction(0,1) #doctest: +ELLIPSIS
- <Interaction instance at 0x...>
-
- >> is3d=IntrSmooth3d(0.003)
- >> is3d((0,0,0),lambda i: i.phys.normalForce)
- Vector3(0,0,0)
-
- """
- def __init__(self,stDev):
- self.locator=InteractionLocator()
- self.stDev=stDev
- self.relThreshold=3.
- self.sqrt2pi=sqrt(2*pi)
- import yade.config
- if not 'vtk' in yade.config.features: raise RuntimeError("IntrSmooth3d is only function with VTK-enabled builds.")
- def _ptpt2weight(self,pt0,pt1):
- distSq=(pt0-pt1).SquaredLength()
- return (1./(self.stDev*self.sqrt2pi))*exp(-distSq/(2*self.stDev*self.stDev))
- def bounds(self): return self.locator.bounds()
- def count(self): return self.locator.count()
- def __call__(self,pt,extr):
- intrs=self.locator.intrsAroundPt(pt,self.stDev*self.relThreshold)
- if len(intrs)==0: return float('nan')
- weights,val=0.,0.
- for i in intrs:
- weight=self._ptpt2weight(pt,i.geom.contactPoint)
- val+=weight*extr(i)
- weights+=weight
- #print i,weight,extr(i)
- return val/weights
-
-
-def estimateStress(strain,cutoff=0.):
- """Use summed stored energy in contacts to compute macroscopic stress over the same volume, provided known strain."""
- # E=(1/2)σεAl # global stored energy
- # σ=EE/(.5εAl)=EE/(.5εV)
- from yade import utils
- dim=utils.aabbDim(cutoff,centers=False)
- return utils.elasticEnergy(utils.aabbExtrema(cutoff))/(.5*strain*dim[0]*dim[1]*dim[2])
-
-def estimatePoissonYoung(principalAxis,stress=0,plot=False,cutoff=0.):
- """Estimate Poisson's ration given the "principal" axis of straining.
- For every base direction, homogenized strain is computed
- (slope in linear regression on discrete function particle coordinate →
- → particle displacement in the same direction as returned by
- utils.coordsAndDisplacements) and, (if axis '0' is the strained
- axis) the poisson's ratio is given as -½(ε₁+ε₂)/ε₀.
-
- Young's modulus is computed as σ/ε₀; if stress σ is not given (default 0),
- the result is 0.
-
- cutoff, if > 0., will take only smaller part (centered) or the specimen into account
- """
- dd=[] # storage for linear regression parameters
- import pylab,numpy
- try:
- import stats
- except ImportError:
- raise ImportError("Unable to import stats; install the python-stats package.")
- from yade import utils
- if cutoff>0: cut=utils.fractionalBox(fraction=1-cutoff)
- for axis in [0,1,2]:
- if cutoff>0:
- w,dw=utils.coordsAndDisplacements(axis,Aabb=cut)
- else:
- w,dw=utils.coordsAndDisplacements(axis)
- l,ll=stats.linregress(w,dw)[0:2] # use only tangent and section
- dd.append((l,ll,min(w),max(w)))
- if plot: pylab.plot(w,dw,'.',label=r'$\Delta %s(%s)$'%('xyz'[axis],'xyz'[axis]))
- if plot:
- for axis in [0,1,2]:
- dist=dd[axis][-1]-dd[axis][-2]
- c=numpy.linspace(dd[axis][-2]-.2*dist,dd[axis][-1]+.2*dist)
- d=[dd[axis][0]*cc+dd[axis][1] for cc in c]
- pylab.plot(c,d,label=r'$\widehat{\Delta %s}(%s)$'%('xyz'[axis],'xyz'[axis]))
- pylab.legend(loc='upper left')
- pylab.xlabel(r'$x,\;y,\;z$')
- pylab.ylabel(r'$\Delta x,\;\Delta y,\; \Delta z$')
- pylab.show()
- otherAxes=(principalAxis+1)%3,(principalAxis+2)%3
- avgTransHomogenizedStrain=.5*(dd[otherAxes[0]][0]+dd[otherAxes[1]][0])
- principalHomogenizedStrain=dd[principalAxis][0]
- return -avgTransHomogenizedStrain/principalHomogenizedStrain,stress/principalHomogenizedStrain
-
-
-def oofemTextExport(fName):
- """Export simulation data in text format
-
- The format is line-oriented as follows::
-
- E G # elastic material parameters
- epsCrackOnset relDuctility xiShear transStrainCoeff # tensile parameters; epsFr=epsCrackOnset*relDuctility
- cohesionT tanPhi # shear parameters
- number_of_spheres number_of_links
- id x y z r boundary # spheres; boundary: -1 negative, 0 none, 1 positive
- …
- id1 id2 cp_x cp_y cp_z A # interactions; cp = contact point; A = cross-section
-
- """
- material,bodies,interactions=[],[],[]
-
- f=open(fName,'w') # fail early on I/O problem
-
- ph=O.interactions.nth(0).phys # some params are the same everywhere
- material.append("%g %g"%(ph.E,ph.G))
- material.append("%g %g %g %g"%(ph.epsCrackOnset,ph.epsFracture,1e50,0.0))
- material.append("%g %g"%(ph.undamagedCohesion,ph.tanFrictionAngle))
-
- # need strainer for getting bodies in positive/negative boundary
- strainers=[e for e in O.engines if e.name=='UniaxialStrainer']
- if len(strainers)>0: strainer=strainers[0]
- else: strainer=None
-
- for b in O.bodies:
- if not b.shape or b.shape.name!='Sphere': continue
- if strainer and b.id in strainer.negIds: boundary=-1
- elif strainer and b.id in strainer.posIds: boundary=1
- else: boundary=0
- bodies.append("%d %g %g %g %g %d"%(b.id,b.state.pos[0],b.state.pos[1],b.state.pos[2],b.shape.radius,boundary))
-
- for i in O.interactions:
- cp=i.geom.contactPoint
- interactions.append("%d %d %g %g %g %g"%(i.id1,i.id2,cp[0],cp[1],cp[2],i.phys.crossSection))
-
- f.write('\n'.join(material+["%d %d"%(len(bodies),len(interactions))]+bodies+interactions))
- f.close()
-
-def oofemPrescribedDisplacementsExport(fileName):
- f=open(fileName,'w')
- f.write(fileName+'.out\n'+'''All Yade displacements prescribed as boundary conditions
-NonLinearStatic nsteps 2 contextOutputStep 1 rtolv 1.e-2 stiffMode 2 maxiter 50 controllmode 1 nmodules 0
-domain 3dshell
-OutputManager tstep_all dofman_all element_all
-''')
- inters=[i for i in O.interactions if (i.geom and i.phys)]
- f.write("ndofman %d nelem %d ncrosssect 1 nmat 1 nbc %d nic 0 nltf 1 nbarrier 0\n"%(len(O.bodies),len(inters),len(O.bodies)*6))
- bcMax=0; bcMap={}
- for b in O.bodies:
- mf=' '.join([str(a) for a in list(O.actions.f(b.id))+list(O.actions.m(b.id))])
- f.write("## #%d: forces %s\n"%(b.id+1,mf))
- f.write("Particle %d coords 3 %.15e %.15e %.15e rad %g"%(b.id+1,b.phys['se3'][0],b.phys['se3'][1],b.phys['se3'][2],b.mold['radius']))
- bcMap[b.id]=tuple([bcMax+i for i in [1,2,3,4,5,6]])
- bcMax+=6
- f.write(' bc '+' '.join([str(i) for i in bcMap[b.id]])+'\n')
- for j,i in enumerate(inters):
- epsTNorm=sqrt(sum([e**2 for e in i.phys['epsT']]))
- epsT='epsT [ %g %g %g ] %g'%(i.phys['epsT'][0],i.phys['epsT'][1],i.phys['epsT'][2],epsTNorm)
- en=i.phys['epsN']; n=i.geom['normal']
- epsN='epsN [ %g %g %g ] %g'%(en*n[0],en*n[1],en*n[2],en)
- Fn='Fn [ %g %g %g ] %g'%(i.phys['normalForce'][0],i.phys['normalForce'][1],i.phys['normalForce'][2],i.phys['Fn'])
- Fs='Fs [ %lf %lf %lf ] %lf'%(i.phys['shearForce'][0],i.phys['shearForce'][1],i.phys['shearForce'][2],sqrt(sum([fs**2 for fs in i.phys['shearForce']])))
- f.write('## #%d #%d: %s %s %s %s\n'%(i.id1+1,i.id2+1,epsN,epsT,Fn,Fs))
- f.write('CohSur3d %d nodes 2 %d %d mat 1 crossSect 1 area %g\n'%(j+1,i.id1+1,i.id2+1,i.phys['crossSection']))
- # crosssection
- f.write("SimpleCS 1 thick 1.0 width 1.0\n")
- # material
- ph=inters[0].phys
- f.write("CohInt 1 kn %g ks %g e0 %g ef %g c 0. ksi %g coh %g tanphi %g d 1.0 conf 0.0 maxdist 0.0\n"%(ph['E'],ph['G'],ph['epsCrackOnset'],ph['epsFracture'],ph['xiShear'],ph['undamagedCohesion'],ph['tanFrictionAngle']))
- # boundary conditions
- for b in O.bodies:
- displ=b.phys.displ; rot=b.phys.rot
- dofs=[displ[0],displ[1],displ[2],rot[0],rot[1],rot[2]]
- f.write('# particle %d\n'%b.id)
- for dof in range(6):
- f.write('BoundaryCondition %d loadTimeFunction 1 prescribedvalue %.15e\n'%(bcMap[b.id][dof],dofs[dof]))
- #f.write('PiecewiseLinFunction 1 npoints 3 t 3 0. 10. 1000. f(t) 3 0. 10. 1000.\n')
- f.write('ConstantFunction 1 f(t) 1.0\n')
-
-
-
-
-def oofemDirectExport(fileBase,title=None,negIds=[],posIds=[]):
- from yade.wrapper import Omega
- material,bodies,interactions=[],[],[]
- o=Omega()
- strainers=[e for e in o.engines if e.name=='UniaxialStrainer']
- if len(strainers)>0:
- strainer=strainers[0]
- posIds,negIds=strainer.posIds,strainer.negIds
- else: strainer=None
- f=open(fileBase+'.in','w')
- # header
- f.write(fileBase+'.out\n')
- f.write((title if title else 'Yade simulation for '+fileBase)+'\n')
- f.write("NonLinearStatic nsteps 2 contextOutputStep 1 rtolv 1.e-2 stiffMode 2 maxiter 50 controllmode 1 nmodules 0\n")
- f.write("domain 3dShell\n")
- f.write("OutputManager tstep_all dofman_all element_all\n")
- inters=[i for i in o.interactions if (i.geom and i.phys)]
- f.write("ndofman %d nelem %d ncrosssect 1 nmat 1 nbc 2 nic 0 nltf 1 nbarrier 0\n"%(len(o.bodies)+2,len(inters)))
- # elements
- f.write("Node 1 coords 3 0.0 0.0 0.0 bc 6 1 1 1 1 1 1\n")
- f.write("Node 2 coords 3 0.0 0.0 0.0 bc 6 1 2 1 1 1 1\n")
- for b in o.bodies:
- f.write("Particle %d coords 3 %g %g %g rad %g"%(b.id+3,b.state.refPos[0],b.state.refPos[1],b.state.refPos[2],b.shape.radius))
- if b.id in negIds: f.write(" dofType 6 1 1 1 1 1 1 masterMask 6 0 1 0 0 0 0 ")
- elif b.id in posIds: f.write(" dofType 6 1 1 1 1 1 1 masterMask 6 0 2 0 0 0 0 0")
- f.write('\n')
- j=1
- for i in inters:
- f.write('CohSur3d %d nodes 2 %d %d mat 1 crossSect 1 area %g\n'%(j,i.id1+3,i.id2+3,i.phys.crossSection))
- j+=1
- # crosssection
- f.write("SimpleCS 1 thick 1.0 width 1.0\n")
- # material
- ph=inters[0].phys
- f.write("CohInt 1 kn %g ks %g e0 %g ef %g c 0. ksi %g coh %g tanphi %g damchartime %g damrateexp %g plchartime %g plrateexp %g d 1.0\n"%(ph.E,ph.G,ph.epsCrackOnset,ph.epsFracture,0.0,ph.undamagedCohesion,ph.tanFrictionAngle,ph.dmgTau,ph.dmgRateExp,ph.plTau,ph.plRateExp))
- # boundary conditions
- f.write('BoundaryCondition 1 loadTimeFunction 1 prescribedvalue 0.0\n')
- f.write('BoundaryCondition 2 loadTimeFunction 1 prescribedvalue 1.e-4\n')
- f.write('PiecewiseLinFunction 1 npoints 3 t 3 0. 10. 1000. f(t) 3 0. 10. 1000.\n')
-
-
-def displacementsInteractionsExport(fName):
- f=open(fName,'w')
- print "Writing body displacements and interaction strains."
- o=Omega()
- for b in o.bodies:
- x0,y0,z0=b.phys['refSe3'][0:3]; x,y,z=b.phys.pos
- rx,ry,rz,rr=b.phys['se3'][3:7]
- f.write('%d xyz [ %g %g %g ] dxyz [ %g %g %g ] rxyz [ %g %g %g ] \n'%(b.id,x0,y0,z0,x-x0,y-y0,z-z0,rr*rx,rr*ry,rr*rz))
- f.write('\n')
- for i in o.interactions:
- if not i['isReal']:continue
- epsTNorm=sqrt(sum([e**2 for e in i.phys['epsT']]))
- epsT='epsT [ %g %g %g ] %g'%(i.phys['epsT'][0],i.phys['epsT'][1],i.phys['epsT'][2],epsTNorm)
- en=i.phys['epsN']; n=i.geom['normal']
- epsN='epsN [ %g %g %g ] %g'%(en*n[0],en*n[1],en*n[2],en)
- Fn='Fn [ %g %g %g ] %g'%(i.phys['normalForce'][0],i.phys['normalForce'][1],i.phys['normalForce'][2],i.phys['Fn'])
- Fs='Fs [ %lf %lf %lf ] %lf'%(i.phys['shearForce'][0],i.phys['shearForce'][1],i.phys['shearForce'][2],sqrt(sum([fs**2 for fs in i.phys['shearForce']])))
- f.write('%d %d %s %s %s %s\n'%(i.id1,i.id2,epsN,epsT,Fn,Fs))
- # f.write('%d %d %g %g %g %g %g\n'%(i.id1,i.id2,i.phys['epsN'],i.phys['epsT'][0],i.phys['epsT'][1],i.phys['epsT'][2],epsTNorm))
- f.close()
-
-
-
-
-
-def eliminateJumps(eps,sigma,numSteep=10,gapWidth=5,movWd=40):
- from matplotlib.mlab import movavg
- from numpy import diff,abs
- import numpy
- # get histogram of 'derivatives'
- ds=abs(diff(sigma))
- n,bins=numpy.histogram(ds)
- i=1; sum=0
- # numSteep steepest pieces will be discarded
- while sum<numSteep:
- #print n[-i],bins[-i]
- sum+=n[-i]; i+=1
- #print n[-i],bins[-i]
- threshold=bins[-i]
- # old algo: replace with nan's
- ##rEps,rSigma=eps[:],sigma[:]; nan=float('nan')
- ##indices=where(ds>threshold)[0]
- ##for i in indices:
- ## for ii in range(max(0,i-gapWidth),min(len(rEps),i+gapWidth+1)): rEps[ii]=rSigma[ii]=nan
-
- ## doesn't work with older numpy (?)
- # indices1=where(ds>threshold)[0]
- indices1=[]
- for i in range(len(ds)):
- if ds[i]>threshold: indices1.append(i)
- indices=[]
- for i in indices1:
- for ii in range(i-gapWidth,i+gapWidth+1): indices.append(ii)
- #print indices1, indices
- rEps=[eps[i] for i in range(0,len(eps)) if i not in indices]
- rSigma=[sigma[i] for i in range(0,len(sigma)) if i not in indices]
- # apply moving average to the result
- rSigma=movavg(rSigma,movWd)
- rEps=rEps[movWd/2:-movWd/2+1]
- return rEps,rSigma.flatten().tolist()
-
=== modified file 'py/export.py'
--- py/export.py 2013-07-10 09:16:15 +0000
+++ py/export.py 2013-11-04 14:20:24 +0000
@@ -26,23 +26,53 @@
count=0
- out.write('#format ' + format + '\n')
- if (comment):
- out.write('# ' + comment + '\n')
+ output = ''
+ outputVel=''
+ if (format<>'liggghts_in'):
+ output = '#format ' + format + '\n'
+ if (comment):
+ output += '# ' + comment + '\n'
+
+ minCoord= Vector3.Zero
+ maxCoord= Vector3.Zero
+ maskNumber = []
+
for b in O.bodies:
try:
if (isinstance(b.shape,Sphere) and ((mask<0) or ((mask&b.mask)>0))):
if (format=='x_y_z_r'):
- out.write('%g\t%g\t%g\t%g\n'%(b.state.pos[0],b.state.pos[1],b.state.pos[2],b.shape.radius))
+ output+=('%g\t%g\t%g\t%g\n'%(b.state.pos[0],b.state.pos[1],b.state.pos[2],b.shape.radius))
elif (format=='x_y_z_r_matId'):
- out.write('%g\t%g\t%g\t%g\t%d\n'%(b.state.pos[0],b.state.pos[1],b.state.pos[2],b.shape.radius,b.material.id))
+ output+=('%g\t%g\t%g\t%g\t%d\n'%(b.state.pos[0],b.state.pos[1],b.state.pos[2],b.shape.radius,b.material.id))
elif (format=='id_x_y_z_r_matId'):
- out.write('%d\t%g\t%g\t%g\t%g\t%d\n'%(b.id,b.state.pos[0],b.state.pos[1],b.state.pos[2],b.shape.radius,b.material.id))
+ output+=('%d\t%g\t%g\t%g\t%g\t%d\n'%(b.id,b.state.pos[0],b.state.pos[1],b.state.pos[2],b.shape.radius,b.material.id))
+ elif (format=='jointedPM'):
+ output+=('%g\t%g\t%g\t%g\t%g\t%g\t%g\t%g\t%g\t%g\t%g\t%g\n'%(b.id,b.state.onJoint,b.state.joint,b.state.jointNormal1[0],b.state.jointNormal1[1],b.state.jointNormal1[2],b.state.jointNormal2[0],b.state.jointNormal2[1],b.state.jointNormal2[2],b.state.jointNormal3[0],b.state.jointNormal3[1],b.state.jointNormal3[2]))
+ elif (format=='liggghts_in'):
+ output+=('%g %g %g %g %g %g %g\n'%(count+1,b.mask,b.shape.radius,b.material.density,b.state.pos[0],b.state.pos[1],b.state.pos[2]))
+ outputVel+=('%g %g %g %g %g %g %g\n'%(count+1,b.state.vel[0],b.state.vel[1],b.state.vel[2],b.state.angVel[0],b.state.angVel[1],b.state.angVel[2]))
else:
raise RuntimeError("Please, specify a correct format output!");
count+=1
+ if (count==1):
+ minCoord = b.state.pos - Vector3(b.shape.radius,b.shape.radius,b.shape.radius)
+ maxCoord = b.state.pos + Vector3(b.shape.radius,b.shape.radius,b.shape.radius)
+ else:
+ minCoord = Vector3(min(minCoord[0], b.state.pos[0]-b.shape.radius),min(minCoord[1], b.state.pos[1]-b.shape.radius),min(minCoord[2], b.state.pos[2]-b.shape.radius))
+ maxCoord = Vector3(max(maxCoord[0], b.state.pos[0]+b.shape.radius),max(maxCoord[1], b.state.pos[1]+b.shape.radius),max(minCoord[2], b.state.pos[2]+b.shape.radius))
+ if b.mask not in maskNumber:
+ maskNumber.append(b.mask)
except AttributeError:
pass
+
+ if (format=='liggghts_in'):
+ outputHeader = 'LIGGGHTS Description\n\n'
+ outputHeader += '%d atoms\n%d atom types\n\n'%(count,len(maskNumber))
+ outputHeader += '%g %g xlo xhi\n%g %g ylo yhi\n%g %g zlo zhi\n\n'%(minCoord[0],maxCoord[0],minCoord[1],maxCoord[1],minCoord[2],maxCoord[2])
+
+
+ output=outputHeader + 'Atoms\n\n' + output + '\nVelocities\n\n' + outputVel
+ out.write(output)
out.close()
return count
@@ -180,7 +210,7 @@
#VTKExporter===============================================================
class VTKExporter:
- """Class for exporting data to VTK Simple Legacy File (for example if, for some reason, you are not able to use VTKRecorder). Export of spheres and facets is supported.
+ """Class for exporting data to VTK Simple Legacy File (for example if, for some reason, you are not able to use VTKRecorder). Export of spheres, facets and interactions is supported.
USAGE:
create object vtkExporter = VTKExporter('baseFileName'),
@@ -197,7 +227,7 @@
self.intrsSnapCount = startSnap
self.baseName = baseName
- def exportSpheres(self,ids='all',what=[],comment="comment",numLabel=None):
+ def exportSpheres(self,ids='all',what=[],comment="comment",numLabel=None,useRef=False):
"""exports spheres (positions and radius) and defined properties.
:param ids: if "all", then export all spheres, otherwise only spheres from integer list
@@ -206,6 +236,7 @@
:type what: [tuple(2)]
:param string comment: comment to add to vtk file
:param int numLabel: number of file (e.g. time step), if unspecified, the last used value + 1 will be used
+ :param bool useRef: if False (default), use current position of the spheres for export, use reference position otherwise
"""
allIds = False
if ids=='all':
@@ -220,10 +251,11 @@
continue
bodies.append(b)
n = len(bodies)
- outFile = open(self.baseName+'-spheres-%04d'%self.spheresSnapCount+'.vtk', 'w')
+ fName = self.baseName+'-spheres-%04d'%(numLabel if numLabel else self.spheresSnapCount)+'.vtk'
+ outFile = open(fName, 'w')
outFile.write("# vtk DataFile Version 3.0.\n%s\nASCII\n\nDATASET POLYDATA\nPOINTS %d double\n"%(comment,n))
for b in bodies:
- pos = b.state.pos
+ pos = b.state.refPos if useRef else b.state.pos if not O.periodic else O.cell.wrap(b.state.pos)
outFile.write("%g %g %g\n"%(pos[0],pos[1],pos[2]))
outFile.write("\nPOINT_DATA %d\nSCALARS radius double 1\nLOOKUP_TABLE default\n"%(n))
for b in bodies:
@@ -271,7 +303,8 @@
continue
bodies.append(b)
n = len(bodies)
- outFile = open(self.baseName+'-facets-%04d'%self.facetsSnapCount+'.vtk', 'w')
+ fName = self.baseName+'-facets-%04d'%(numLabel if numLabel else self.facetsSnapCount)+'.vtk'
+ outFile = open(fName, 'w')
outFile.write("# vtk DataFile Version 3.0.\n%s\nASCII\n\nDATASET POLYDATA\nPOINTS %d double\n"%(comment,3*n))
for b in bodies:
p = b.state.pos
@@ -352,7 +385,8 @@
nodes[e[0]] = pt1
nodes[e[1]] = pt2
nodes[e[2]] = pt3
- outFile = open(self.baseName+'-facets-%04d'%self.facetsSnapCount+'.vtk', 'w')
+ fName = self.baseName+'-facets-%04d'%(numLabel if numLabel else self.facetsSnapCount)+'.vtk'
+ outFile = open(fName, 'w')
outFile.write("# vtk DataFile Version 3.0.\n%s\nASCII\n\nDATASET POLYDATA\nPOINTS %d double\n"%(comment,len(nodes)))
gg = 0
for n in nodes:
@@ -381,41 +415,35 @@
outFile.close()
self.facetsSnapCount += 1
- def exportInteractions(self,ids='all',what=[],comment="comment",numLabel=None):
+ def exportInteractions(self,ids='all',what=[],verticesWhat=[],comment="comment",numLabel=None):
"""exports interactions and defined properties.
- :param ids: if "all", then export all interactions, otherwise only interactions from (integer,integer) list
- :type ids: [(int,int)] | "all"
+ :param ids: if "all", then export all spheres, otherwise only spheres from integer list
+ :type ids: [int] | "all"
:param what: what to export. parameter is list of couple (name,command). Name is string under which it is save to vtk, command is string to evaluate. Node that the interactions are labeled as i in this function. Scalar, vector and tensor variables are supported. For example, to export stiffness difference from certain value (1e9) (named as dStiff) you should write: ... what=[('dStiff','i.phys.kn-1e9'), ...
:type what: [tuple(2)]
:param string comment: comment to add to vtk file
:param int numLabel: number of file (e.g. time step), if unspecified, the last used value + 1 will be used
"""
- allIds = False
if ids=='all':
- intrs=O.interactions
- allIds = True
- else:
- intrs = []
- for j,k in ids:
- i = O.interactions[j,k]
- if not i: continue
- intrs.append(i)
- n = len([i for i in intrs if i.isReal])
- ids = [None for j in xrange(n)]
- outFile = open(self.baseName+'-intrs-%04d'%self.intrsSnapCount+'.vtk', 'w')
+ ids = [(i.id1,i.id2) for i in O.interactions]
+ intrs = [(2*j-1,2*j) for j in xrange(len(ids))]
+ n = len(intrs)
+ fName = self.baseName+'-intrs-%04d'%(numLabel if numLabel else self.intrsSnapCount)+'.vtk'
+ outFile = open(fName, 'w')
outFile.write("# vtk DataFile Version 3.0.\n%s\nASCII\n\nDATASET POLYDATA\nPOINTS %d double\n"%(comment,2*n))
- for j,i in enumerate(intrs):
- if not i.isReal: continue
- pos = O.bodies[i.id1].state.pos
- outFile.write("%g %g %g\n"%(pos[0],pos[1],pos[2]))
- pos = O.bodies[i.id2].state.pos + i.cellDist
- outFile.write("%g %g %g\n"%(pos[0],pos[1],pos[2]))
- ids[j] = (2*j-1,2*j)
+ for ii,jj in ids:
+ i = O.interactions[ii,jj]
+ pos = O.bodies[ii].state.pos
+ outFile.write("%g %g %g\n"%(pos[0],pos[1],pos[2]))
+ pos = O.bodies[jj].state.pos + (O.cell.hSize*i.cellDist if O.periodic else Vector3.Zero)
+ outFile.write("%g %g %g\n"%(pos[0],pos[1],pos[2]))
outFile.write("LINES %d %d\n"%(n,3*n))
for j,i in enumerate(intrs):
- outFile.write("2 %d %d\n"%(ids[j][0]+1,ids[j][1]+1))
+ outFile.write("2 %d %d\n"%(i[0]+1,i[1]+1))
outFile.write("\nCELL_DATA %d\n"%(n))
+ for i in O.interactions:
+ if i.isReal: break
for name,command in what:
test = eval(command)
if isinstance(test,Matrix3):
@@ -432,13 +460,99 @@
# outFile.write("%g %g %g\n"%(v[0],v[1],v[2]))
elif isinstance(test,(int,float)):
outFile.write("\nSCALARS %s double 1\nLOOKUP_TABLE default\n"%(name))
- for i in intrs:
+ for ii,jj in ids:
+ i = O.interactions[ii,jj]
outFile.write("%g\n"%(eval(command)))
else:
print 'WARNING: export.VTKExporter.exportInteractions: wrong `what` parameter, vtk output might be corrupted'
+ if verticesWhat:
+ outFile.write("\nPOINT_DATA %d\n"%(2*n))
+ b = b1 = b2 = O.bodies[0]
+ for vWhat in verticesWhat:
+ lw = len(vWhat)
+ if lw == 2:
+ name,command = vWhat
+ test = eval(command)
+ elif lw == 3:
+ name,command1,command2 = vWhat
+ test = eval(command1)
+ if isinstance(test,Matrix3):
+ outFile.write("\nTENSORS %s double\n"%(name))
+ for ii,jj in ids:
+ i = O.interactions[ii,jj]
+ b1 = O.bodies[ii]
+ b2 = O.bodies[jj]
+ if lw==2:
+ for b in (b1,b2):
+ t = eval(command)
+ outFile.write("%g %g %g\n%g %g %g\n%g %g %g\n\n"%(t[0,0],t[0,1],t[0,2],t[1,0],t[1,1],t[1,2],t[2,0],t[2,1],t[2,2]))
+ elif lw==3:
+ t1 = eval(command1)
+ t2 = eval(command2)
+ outFile.write("%g %g %g\n%g %g %g\n%g %g %g\n\n"%(t1[0,0],t1[0,1],t1[0,2],t1[1,0],t1[1,1],t1[1,2],t1[2,0],t1[2,1],t1[2,2]))
+ outFile.write("%g %g %g\n%g %g %g\n%g %g %g\n\n"%(t2[0,0],t2[0,1],t2[0,2],t2[1,0],t2[1,1],t2[1,2],t2[2,0],t2[2,1],t2[2,2]))
+ elif isinstance(test,Vector3):
+ outFile.write("\nVECTORS %s double\n"%(name))
+ for ii,jj in ids:
+ i = O.interactions[ii,jj]
+ b1 = O.bodies[ii]
+ b2 = O.bodies[jj]
+ if lw==2:
+ for b in (b1,b2):
+ v = eval(command)
+ outFile.write("%g %g %g\n"%(v[0],v[1],v[2]))
+ elif lw==3:
+ v1 = eval(command1)
+ v2 = eval(command2)
+ outFile.write("%g %g %g\n"%(v1[0],v1[1],v1[2]))
+ outFile.write("%g %g %g\n"%(v2[0],v2[1],v2[2]))
+ elif isinstance(test,(int,float)):
+ outFile.write("\nSCALARS %s double 1\nLOOKUP_TABLE default\n"%(name))
+ for ii,jj in ids:
+ i = O.interactions[ii,jj]
+ b1 = O.bodies[ii]
+ b2 = O.bodies[jj]
+ if lw==2:
+ for b in (b1,b2):
+ outFile.write("%g\n"%(eval(command)))
+ elif lw==3:
+ outFile.write("%g\n"%(eval(command1)))
+ outFile.write("%g\n"%(eval(command2)))
+ else:
+ print 'WARNING: export.VTKExporter.exportInteractions: wrong `what` parameter, vtk output might be corrupted'
outFile.close()
self.intrsSnapCount += 1
+ def exportPeriodicCell(self,comment="comment",numLabel=None):
+ """exports spheres (positions and radius) and defined properties.
+
+ :param string comment: comment to add to vtk file
+ :param int numLabel: number of file (e.g. time step), if unspecified, the last used value + 1 will be used
+ """
+ if not O.periodic:
+ print 'WARNING: export.VTKExporter.exportPeriodicCell: scene is not periodic, no export...'
+ return
+ hSize = O.cell.hSize
+ fName = self.baseName+'-periCell-%04d'%(numLabel if numLabel else self.intrsSnapCount)+'.vtk'
+ outFile = open(fName, 'w')
+ outFile.write("# vtk DataFile Version 3.0.\n%s\nASCII\n\nDATASET UNSTRUCTURED_GRID\nPOINTS 8 double\n"%(comment))
+ vertices = [
+ hSize*Vector3(0,0,1),
+ hSize*Vector3(0,1,1),
+ hSize*Vector3(1,1,1),
+ hSize*Vector3(1,0,1),
+ hSize*Vector3(0,0,0),
+ hSize*Vector3(0,1,0),
+ hSize*Vector3(1,1,0),
+ hSize*Vector3(1,0,0),
+ ]
+ for v in vertices:
+ outFile.write('%g %g %g\n'%(v[0],v[1],v[2]))
+ outFile.write('\nCELLS 1 9\n')
+ outFile.write('8 0 1 2 3 4 5 6 7\n')
+ outFile.write('\nCELL_TYPES 1\n12\n')
+ outFile.close()
+
#gmshGeoExport===============================================================
=== modified file 'py/pack/_packSpheres.cpp'
--- py/pack/_packSpheres.cpp 2013-01-22 14:15:20 +0000
+++ py/pack/_packSpheres.cpp 2013-08-14 08:01:55 +0000
@@ -16,7 +16,7 @@
.def("save",&SpherePack::toFile,(python::arg("fileName")),"Save packing to external text file (will be overwritten).")
.def("fromSimulation",&SpherePack::fromSimulation,"Make packing corresponding to the current simulation. Discards current data.")
.def("makeCloud",&SpherePack::makeCloud,(python::arg("minCorner")=Vector3r(Vector3r::Zero()),python::arg("maxCorner")=Vector3r(Vector3r::Zero()),python::arg("rMean")=-1,python::arg("rRelFuzz")=0,python::arg("num")=-1,python::arg("periodic")=false,python::arg("porosity")=0.65,python::arg("psdSizes")=vector<Real>(),python::arg("psdCumm")=vector<Real>(),python::arg("distributeMass")=false,python::arg("seed")=0,python::arg("hSize")=Matrix3r(Matrix3r::Zero())),"Create random loose packing enclosed in a parallelepiped (also works in 2D if minCorner[k]=maxCorner[k] for one coordinate)."
- "\nSphere radius distribution can be specified using one of the following ways:\n\n#. *rMean*, *rRelFuzz* and *num* gives uniform radius distribution in *rMean*×(1 ± *rRelFuzz*). Less than *num* spheres can be generated if it is too high.\n#. *rRelFuzz*, *num* and (optional) *porosity*, which estimates mean radius so that *porosity* is attained at the end. *rMean* must be less than 0 (default). *porosity* is only an initial guess for the generation algorithm, which will retry with higher porosity until the prescibed *num* is obtained.\n#. *psdSizes* and *psdCumm*, two arrays specifying points of the `particle size distribution <http://en.wikipedia.org/wiki/Particle_size_distribution>`__ function. As many spheres as possible are generated.\n#. *psdSizes*, *psdCumm*, *num*, and (optional) *porosity*, like above but if *num* is not obtained, *psdSizes* will be scaled down uniformly, until *num* is obtained (see :yref:`appliedPsdScaling<yade._packSpheres.SpherePack.appliedPsdScaling>`).\n\nBy default (with ``distributeMass==False``), the distribution is applied to particle radii. The usual sense of \"particle size distribution\" is the distribution of *mass fraction* (rather than particle count); this can be achieved with ``distributeMass=True``."
+ "\nSphere radius distribution can be specified using one of the following ways:\n\n#. *rMean*, *rRelFuzz* and *num* gives uniform radius distribution in *rMean×(1 ± rRelFuzz)*. Less than *num* spheres can be generated if it is too high.\n#. *rRelFuzz*, *num* and (optional) *porosity*, which estimates mean radius so that *porosity* is attained at the end. *rMean* must be less than 0 (default). *porosity* is only an initial guess for the generation algorithm, which will retry with higher porosity until the prescibed *num* is obtained.\n#. *psdSizes* and *psdCumm*, two arrays specifying points of the `particle size distribution <http://en.wikipedia.org/wiki/Particle_size_distribution>`__ function. As many spheres as possible are generated.\n#. *psdSizes*, *psdCumm*, *num*, and (optional) *porosity*, like above but if *num* is not obtained, *psdSizes* will be scaled down uniformly, until *num* is obtained (see :yref:`appliedPsdScaling<yade._packSpheres.SpherePack.appliedPsdScaling>`).\n\nBy default (with ``distributeMass==False``), the distribution is applied to particle radii. The usual sense of \"particle size distribution\" is the distribution of *mass fraction* (rather than particle count); this can be achieved with ``distributeMass=True``."
"\n\nIf *num* is defined, then sizes generation is deterministic, giving the best fit of target distribution. It enables spheres placement in descending size order, thus giving lower porosity than the random generation."
"\n\n:param Vector3 minCorner: lower corner of an axis-aligned box\n:param Vector3 maxCorner: upper corner of an axis-aligned box\n:param Matrix3 hSize: base vectors of a generalized box (arbitrary parallelepiped, typically :yref:`Cell::hSize`), superseeds minCorner and maxCorner if defined. For periodic boundaries only.\n:param float rMean: mean radius or spheres\n:param float rRelFuzz: dispersion of radius relative to rMean\n:param int num: number of spheres to be generated. If negavite (default), generate as many as possible with stochastic sizes, ending after a fixed number of tries to place the sphere in space, else generate exactly *num* spheres with deterministic size distribution.\n:param bool periodic: whether the packing to be generated should be periodic\n:param float porosity: initial guess for the iterative generation procedure (if *num*>1). The algorithm will be retrying until the number of generated spheres is *num*. The first iteration tries with the provided porosity, but next iterations increase it if necessary (hence an initialy high porosity can speed-up the algorithm). If *psdSizes* is not defined, *rRelFuzz* ($z$) and *num* ($N$) are used so that the porosity given ($\\rho$) is approximately achieved at the end of generation, $r_m=\\sqrt[3]{\\frac{V(1-\\rho)}{\\frac{4}{3}\\pi(1+z^2)N}}$. The default is $\\rho$=0.5. The optimal value depends on *rRelFuzz* or *psdSizes*.\n:param psdSizes: sieve sizes (particle diameters) when particle size distribution (PSD) is specified\n:param psdCumm: cummulative fractions of particle sizes given by *psdSizes*; must be the same length as *psdSizes* and should be non-decreasing\n:param bool distributeMass: if ``True``, given distribution will be used to distribute sphere's mass rather than radius of them.\n:param seed: number used to initialize the random number generator.\n:returns: number of created spheres, which can be lower than *num* depending on the method used.\n")
.def("psd",&SpherePack::psd,(python::arg("bins")=50,python::arg("mass")=true),"Return `particle size distribution <http://en.wikipedia.org/wiki/Particle_size_distribution>`__ of the packing.\n:param int bins: number of bins between minimum and maximum diameter\n:param mass: Compute relative mass rather than relative particle count for each bin. Corresponds to :yref:`distributeMass parameter for makeCloud<yade.pack.SpherePack.makeCloud>`.\n:returns: tuple of ``(cumm,edges)``, where ``cumm`` are cummulative fractions for respective diameters and ``edges`` are those diameter values. Dimension of both arrays is equal to ``bins+1``.")
=== modified file 'py/pack/pack.py'
--- py/pack/pack.py 2013-03-08 21:26:47 +0000
+++ py/pack/pack.py 2013-09-19 08:50:04 +0000
@@ -286,7 +286,8 @@
return ret
def filterSpherePack(predicate,spherePack,returnSpherePack=None,**kw):
- """Using given SpherePack instance, return spheres the satisfy predicate.
+ """Using given SpherePack instance, return spheres that satisfy predicate.
+ It returns either a :yref:`yade._packSpheres.SpherePack` (if returnSpherePack) or a list.
The packing will be recentered to match the predicate and warning is given if the predicate
is larger than the packing."""
if returnSpherePack==None:
@@ -386,7 +387,7 @@
:param predicate: solid-defining predicate for which we generate packing
:param spheresInCell: if given, the packing will be periodic, with given number of spheres in the periodic cell.
:param radius: mean radius of spheres
- :param rRelFuzz: relative fuzz of the radius -- e.g. radius=10, rRelFuzz=.2, then spheres will have radii 10 ± (10*.2)).
+ :param rRelFuzz: relative fuzz of the radius -- e.g. radius=10, rRelFuzz=.2, then spheres will have radii 10 ± (10*.2)), with an uniform distribution.
0 by default, meaning all spheres will have exactly the same radius.
:param cropLayers: (aperiodic only) how many layers of spheres will be added to the computed dimension of the box so that there no
(or not so much, at least) boundary effects at the boundaries of the predicate.
@@ -398,9 +399,9 @@
least number spheres will be loaded and returned.
:param useOBB: effective only if a inGtsSurface predicate is given. If true (not default), oriented bounding box will be
computed first; it can reduce substantially number of spheres for the triaxial compression (like 10× depending on
- how much asymmetric the body is), see scripts/test/gts-triax-pack-obb.py.
- :param memoDbg: show packigns that are considered and reasons why they are rejected/accepted
- :param returnSpherePack: see :yref:`filterSpherePack`
+ how much asymmetric the body is), see examples/gts-horse/gts-random-pack-obb.py
+ :param memoDbg: show packings that are considered and reasons why they are rejected/accepted
+ :param returnSpherePack: see the corresponding argument in :yref:`yade.pack.filterSpherePack`
:return: SpherePack object with spheres, filtered by the predicate.
"""
=== added file 'py/polyhedra_utils.py'
--- py/polyhedra_utils.py 1970-01-01 00:00:00 +0000
+++ py/polyhedra_utils.py 2013-10-15 16:14:46 +0000
@@ -0,0 +1,102 @@
+# 2013 Jan Elias, http://www.fce.vutbr.cz/STM/elias.j/, elias.j@xxxxxxxxxxxx
+# https://www.vutbr.cz/www_base/gigadisk.php?i=95194aa9a
+
+"""
+Auxiliary functions for polyhedra
+"""
+
+
+import math,random,doctest,geom,numpy
+from yade import Vector3
+from yade.wrapper import *
+#from miniEigen import *
+try: # use psyco if available
+ import psyco
+ psyco.full()
+except ImportError: pass
+
+
+# c++ implementations for performance reasons
+from yade._polyhedra_utils import *
+
+#**********************************************************************************
+def randomColor(seed=None):
+ random.seed(seed);
+ #Return random Vector3 with each component in interval 0...1 (uniform distribution)
+ return Vector3(random.random(),random.random(),random.random())
+
+#**********************************************************************************
+#create polyhedra, one can specify vertices directly, or leave it empty for random shape
+def polyhedra(material,size=Vector3(1,1,1),seed=None,v=[],mask=1,fixed=False, color=[-1,-1,-1]):
+ """create polyhedra, one can specify vertices directly, or leave it empty for random shape.
+
+ :param Material material: material of new body
+ :param Vector3 size: size of new body (see Polyhedra docs)
+ :param float seed: seed for random operations
+ :param [Vector3] v: list of body vertices (see Polyhedra docs)
+ """
+ b=Body()
+ random.seed(seed);
+ b.aspherical = True
+ if len(v)>0:
+ b.shape = Polyhedra(v=v)
+ else:
+ b.shape = Polyhedra(size = size, seed=random.randint(0,1E6))
+ if color[0] == -1:
+ b.shape.color = randomColor(seed=random.randint(0,1E6))
+ else:
+ b.shape.color = color
+ b.mat = material
+ b.state.mass = b.mat.density*b.shape.GetVolume()
+ b.state.inertia = b.shape.GetInertia()*b.mat.density
+ b.state.ori = b.shape.GetOri()
+ b.state.pos = b.shape.GetCentroid()
+ b.mask=mask
+ if fixed:
+ b.state.blockedDOFs = 'xyzXYZ'
+ return b
+
+#**********************************************************************************
+#creates polyhedra having N vertices and resembling sphere
+def polyhedralBall(radius, N, material, center,mask=1):
+ """creates polyhedra having N vertices and resembling sphere
+
+ :param float radius: ball radius
+ :param int N: number of vertices
+ :param Material material: material of new body
+ :param Vector3 center: center of the new body
+ """
+ pts = []
+
+ inc = math.pi * (3. - math.sqrt(5.))
+ off = 2. / float(N)
+ for k in range(0, N):
+ y = k * off - 1. + (off / 2.)
+ r = math.sqrt(1. - y*y)
+ phi = k * inc
+ pts.append([math.cos(phi)*r*radius, y*radius, math.sin(phi)*r*radius])
+
+ ball = polyhedra(material,v=pts)
+ ball.state.pos = center
+ return ball
+
+#**********************************************************************************
+#fill box [mincoord, maxcoord] by non-overlaping polyhedrons with random geometry and sizes within the range (uniformly distributed)
+def fillBox(mincoord, maxcoord,material,sizemin=[1,1,1],sizemax=[1,1,1],ratio=[0,0,0],seed=None,mask=1):
+ """fill box [mincoord, maxcoord] by non-overlaping polyhedrons with random geometry and sizes within the range (uniformly distributed)
+ :param Vector3 mincoord: first corner
+ :param Vector3 maxcoord: second corner
+ :param Vector3 sizemin: minimal size of bodies
+ :param Vector3 sizemax: maximal size of bodies
+ :param Vector3 ratio: scaling ratio
+ :param float seed: random seed
+ """
+ random.seed(seed);
+ v = fillBox_cpp(mincoord, maxcoord, sizemin,sizemax, ratio, random.randint(0,1E6), material)
+ #lastnan = -1
+ #for i in range(0,len(v)):
+ # if(math.isnan(v[i][0])):
+ # O.bodies.append(polyhedra(material,seed=random.randint(0,1E6),v=v[lastnan+1:i],mask=1,fixed=False))
+ # lastnan = i
+
+
=== modified file 'py/system.py'
--- py/system.py 2012-06-28 19:34:29 +0000
+++ py/system.py 2013-08-29 10:30:31 +0000
@@ -35,8 +35,6 @@
'ElasticMat':'ElastMat', # Sun Jan 10 09:53:15 2010, vaclav@flux
'ElasticContactInteraction':'FrictPhys', # Sun Jan 10 09:57:59 2010, vaclav@flux
'ef2_Spheres_Elastic_ElasticLaw':'Law2_ScGeom_FrictPhys_Basic', # Sun Jan 10 09:59:42 2010, vaclav@flux
- 'Law2_Dem3Dof_Elastic_Elastic':'Law2_Dem3Dof_FrictPhys_Basic', # Sun Jan 10 10:00:25 2010, vaclav@flux
- 'Law2_Dem3Dof_FrictPhys_Basic':'Law2_Dem3DofGeom_FrictPhys_Basic', # Sun Jan 10 10:01:27 2010, vaclav@flux
'Ip2_FrictMat_FrictMat_NormShearPhys':'Ip2_FrictMat_FrictMat_FrictPhys', # Sun Jan 10 10:07:40 2010, vaclav@flux
'GLDrawCpmPhys':'Gl1_CpmPhys', # Sat Feb 6 14:46:08 2010, vaclav@flux
'RockJointLawRelationships':'Ip2_2xCohFrictMat_NormalInelasticityPhys', # Mon Feb 8 11:17:59 2010, jduriez@c1solimara-l
=== modified file 'py/tests/__init__.py'
--- py/tests/__init__.py 2012-04-13 16:27:00 +0000
+++ py/tests/__init__.py 2013-08-29 10:30:31 +0000
@@ -7,8 +7,8 @@
allTests=['wrapper','core','pbc','clump','cohesive-chain']
# all yade modules (ugly...)
-import yade.eudoxos,yade.export,yade.linterpolation,yade.pack,yade.plot,yade.post2d,yade.timing,yade.utils,yade.ymport,yade.geom
-allModules=(yade.eudoxos,yade.export,yade.linterpolation,yade.pack,yade.plot,yade.post2d,yade.timing,yade.utils,yade.ymport,yade.geom)
+import yade.export,yade.linterpolation,yade.pack,yade.plot,yade.post2d,yade.timing,yade.utils,yade.ymport,yade.geom
+allModules=(yade.export,yade.linterpolation,yade.pack,yade.plot,yade.post2d,yade.timing,yade.utils,yade.ymport,yade.geom)
try:
import yade.qt
allModules+=(yade.qt,)
=== modified file 'py/utils.py'
--- py/utils.py 2013-03-21 13:38:27 +0000
+++ py/utils.py 2013-10-15 17:11:37 +0000
@@ -270,7 +270,7 @@
b.mask=0 #avoid contact detection with the nodes. Manual interaction will be set for them in "gridConnection" below.
return b
-def gridConnection(id1,id2,radius,wire=False,color=None,highlight=False,material=-1,mask="none",cellDist=None):
+def gridConnection(id1,id2,radius,wire=False,color=None,highlight=False,material=-1,mask=1,cellDist=None):
b=Body()
b.shape=GridConnection(radius=radius,color=color if color else randomColor(),wire=wire,highlight=highlight)
sph1=O.bodies[id1] ; sph2=O.bodies[id2]
@@ -348,6 +348,57 @@
b.chain=chain
return b
+def tetraPoly(vertices,strictCheck=True,dynamic=True,fixed=False,wire=True,color=None,highlight=False,noBound=False,material=-1,mask=1,chain=-1):
+ """Create tetrahedron (actually simple Polyhedra) with given parameters.
+
+ :param [Vector3,Vector3,Vector3,Vector3] vertices: coordinates of vertices in the global coordinate system.
+
+ See :yref:`yade.utils.sphere`'s documentation for meaning of other parameters."""
+ b=Body()
+ b.shape = Polyhedra(v=vertices,color=color if color else randomColor(),wire=wire,highlight=highlight)
+ volume = b.shape.GetVolume()
+ inertia = b.shape.GetInertia()
+ center = b.shape.GetCentroid()
+ _commonBodySetup(b,volume,inertia,material,noBound=noBound,pos=center,fixed=fixed)
+ b.aspherical=False # mass and inertia are 0 anyway; fell free to change to ``True`` if needed
+ b.state.ori = b.shape.GetOri()
+ b.mask=mask
+ b.chain=chain
+ return b
+
+def tetra(vertices,strictCheck=True,dynamic=True,fixed=False,wire=True,color=None,highlight=False,noBound=False,material=-1,mask=1,chain=-1):
+ """Create tetrahedron with given parameters.
+
+ :param [Vector3,Vector3,Vector3,Vector3] vertices: coordinates of vertices in the global coordinate system.
+ :param bool strictCheck: checks vertices order, raise RuntimeError for negative volume
+
+ See :yref:`yade.utils.sphere`'s documentation for meaning of other parameters."""
+ b=Body()
+ center = .25*sum(vertices,Vector3.Zero)
+ volume = TetrahedronSignedVolume(vertices)
+ if volume < 0:
+ if strictCheck:
+ raise RuntimeError, "tetra: wrong order of vertices"
+ temp = vertices[3]
+ vertices[3] = vertices[2]
+ vertices[2] = temp
+ volume = TetrahedronSignedVolume(vertices)
+ assert(volume>0)
+ b.shape = Tetra(v=vertices,color=color if color else randomColor(),wire=wire,highlight=highlight)
+ # modifies pos, ori and inertia
+ ori = TetrahedronWithLocalAxesPrincipal(b)
+ _commonBodySetup(b,volume,b.state.inertia,material,noBound=noBound,pos=center,fixed=fixed)
+ b.state.ori = b.state.refOri = ori
+ b.aspherical = True
+ b.mask = mask
+ b.chain = chain
+ return b
+
+
+
+
+
+
#def setNewVerticesOfFacet(b,vertices):
# center = inscribedCircleCenter(vertices[0],vertices[1],vertices[2])
# vertices = Vector3(vertices[0])-center,Vector3(vertices[1])-center,Vector3(vertices[2])-center
@@ -466,6 +517,7 @@
pylab.title('Number of interactions histogram, average %g (cutoff=%g)'%(avgNumInteractions(cutoff),cutoff))
pylab.xlabel('Number of interactions')
pylab.ylabel('Body count')
+ pylab.ion()
pylab.show()
def plotDirections(aabb=(),mask=0,bins=20,numHist=True,noShow=False):
@@ -493,7 +545,9 @@
pylab.axvline(x=avg,linewidth=3,color='r')
pylab.ylabel('Body count')
if noShow: return pylab.gcf()
- else: pylab.show()
+ else:
+ pylab.ion()
+ pylab.show()
def encodeVideoFromFrames(*args,**kw):
@@ -545,7 +599,7 @@
l=_procStatus('VmData'); ll=l.split(); assert(ll[2]=='kB')
return int(ll[1])
-def uniaxialTestFeatures(filename=None,areaSections=10,axis=-1,**kw):
+def uniaxialTestFeatures(filename=None,areaSections=10,axis=-1,distFactor=2.2,**kw):
"""Get some data about the current packing useful for uniaxial test:
#. Find the dimensions that is the longest (uniaxial loading axis)
@@ -571,7 +625,7 @@
assert(axis in (0,1,2))
import numpy
areas=[approxSectionArea(coord,axis) for coord in numpy.linspace(mm[axis],mx[axis],num=10)[1:-1]]
- negIds,posIds=negPosExtremeIds(axis=axis,distFactor=2.2)
+ negIds,posIds=negPosExtremeIds(axis=axis,distFactor=distFactor)
return {'negIds':negIds,'posIds':posIds,'axis':axis,'area':min(areas)}
def voxelPorosityTriaxial(triax,resolution=200,offset=0):
@@ -855,7 +909,7 @@
:param int mask: :yref:`Body.mask` for the body
:return:
* binsSizes: list of bin's sizes
- * binsProc: how much material (in procents) are in the bin, cumulative
+ * binsProc: how much material (in percents) are in the bin, cumulative
* binsSumCum: how much material (in units) are in the bin, cumulative
binsSizes, binsProc, binsSumCum
@@ -876,13 +930,12 @@
binsMass = numpy.zeros(bins)
binsNumbers = numpy.zeros(bins)
-
for b in O.bodies:
if (isinstance(b.shape,Sphere) and ((mask<0) or ((b.mask&mask)<>0))):
d=2*b.shape.radius
basketId = int(math.floor( (d-minD) / deltaBinD ) )
- if (d == maxD): basketId = bins-1 #If the diametr equals the maximal diameter, put the particle into the last bin
+ if (d == maxD): basketId = bins-1 #If the diameter equals the maximal diameter, put the particle into the last bin
binsMass[basketId] = binsMass[basketId] + b.state.mass #Put masses into the bin
binsNumbers[basketId] = binsNumbers[basketId] + 1 #Put numbers into the bin
@@ -924,3 +977,105 @@
self.numCM = len(relRadii)
self.relRadii = relRadii
self.relPositions = relPositions
+
+class UnstructuredGrid:
+ """EXPERIMENTAL.
+ Class representing triangulated FEM-like unstructured grid. It is used for transfereing data from ad to YADE and external FEM program. The main purpose of this class is to store information about individual grid vertices/nodes coords (since facets stores only coordinates of vertices in local coords) and to avaluate and/or apply nodal forces from contact forces (from actual contact force and contact point the force is distributed to nodes using linear approximation).
+
+ TODO rewrite to C++
+ TODO better docs
+
+ :param dict vertices: dict of {internal vertex label:vertex}, e.g. {5:(0,0,0),22:(0,1,0),23:(1,0,0)}
+ :param dict connectivityTable: dict of {internal element label:[indices of vertices]}, e.g. {88:[5,22,23]}
+ """
+ def __init__(self,vertices={},connectivityTable={},**kw):
+ self.vertices = vertices
+ self.connectivityTable = connectivityTable
+ self.forces = dict( (i,Vector3(0,0,0)) for i in vertices)
+ self.build(**kw)
+ def setup(self,vertices,connectivityTable,toSimulation=False,bodies=None,**kw):
+ """Sets new information to receiver
+
+ :param dict vertices: see constructor for explanation
+ :param dict connectivityTable: see constructor for explanation
+ :param bool toSimulation: if new information should be inserted to Yade simulation (create new bodies or not)
+ :param [[int]]|None bodies: list of list of bodies indices to be appended as clumps (thus no contact detection is done within one body)
+ """
+ self.vertices = dict( (i,v) for i,v in vertices.iteritems())
+ self.connectivityTable = dict( (i,e) for i,e in connectivityTable.iteritems())
+ self.build(**kw)
+ if toSimulation:
+ self.toSimulation(bodies)
+ def build(self,**kw):
+ self.elements = {}
+ for i,c in self.connectivityTable.iteritems():
+ if len(c) == 3:
+ b = facet([self.vertices[j] for j in c],**kw)
+ elif len(c) == 4:
+ b = tetra([self.vertices[j] for j in c],**kw)
+ else:
+ raise RuntimeError, "Unsupported cell shape (should be triangle or tetrahedron)"
+ self.elements[i] = b
+ def resetForces(self):
+ for i in self.vertices:
+ self.forces[i] = Vector3(0,0,0)
+ def getForcesOfNodes(self):
+ """Computes forces for each vertex/node. The nodal force is computed from contact force and contact point using linear approximation
+ """
+ self.resetForces()
+ for i,e in self.elements.iteritems():
+ ie = self.connectivityTable[i]
+ for i in e.intrs():
+ fn = i.phys.normalForce
+ fs = i.phys.shearForce if hasattr(i.phys,"shearForce") else Vector3.Zero
+ f = (fn+fs) * (-1 if e.id == i.id1 else +1 if e.id == i.id2 else 'ERROR')
+ cp = i.geom.contactPoint
+ if isinstance(e.shape,Facet):
+ v0,v1,v2 = [Vector3(self.vertices[j]) for j in ie]
+ w0 = ((cp-v1).cross(v2-v1)).norm()
+ w1 = ((cp-v2).cross(v0-v2)).norm()
+ w2 = ((cp-v0).cross(v1-v0)).norm()
+ ww = w0+w1+w2
+ self.forces[ie[0]] += f*w0/ww
+ self.forces[ie[1]] += f*w1/ww
+ self.forces[ie[2]] += f*w2/ww
+ elif isinstance(e.shape,Tetra):
+ v0,v1,v2,v3 = [Vector3(self.vertices[j]) for j in ie]
+ w0 = TetrahedronVolume((cp,v1,v2,v3))
+ w1 = TetrahedronVolume((cp,v2,v3,v0))
+ w2 = TetrahedronVolume((cp,v3,v0,v1))
+ w3 = TetrahedronVolume((cp,v0,v1,v2))
+ ww = w0+w1+w2+w3
+ self.forces[ie[0]] += f*w0/ww
+ self.forces[ie[1]] += f*w1/ww
+ self.forces[ie[2]] += f*w2/ww
+ self.forces[ie[3]] += f*w3/ww
+ return self.forces
+ def setPositionsOfNodes(self,newPoss):
+ """Sets new position of nodes and also updates all elements in the simulation
+
+ :param [Vector3] newPoss: list of new positions
+ """
+ for i in self.vertices:
+ self.vertices[i] = newPoss[i]
+ self.updateElements()
+ def updateElements(self):
+ """Updates positions of all elements in the simulation
+ """
+ for i,c in self.connectivityTable.iteritems():
+ e = self.elements[i]
+ e.state.pos = Vector3(0,0,0)
+ e.state.ori = Quaternion((1,0,0),0)
+ if isinstance(e.shape,Facet):
+ e.shape.vertices = [self.vertices[j] for j in c]
+ elif isinstance(e.shape,Tetra):
+ e.shape.v = [self.vertices[j] for j in c]
+ def toSimulation(self,bodies=None):
+ """Insert all elements to Yade simulation
+ """
+ if bodies:
+ e = self.elements.values()
+ for b in bodies:
+ O.bodies.appendClumped([e[i] for i in b])
+ else:
+ O.bodies.append(self.elements.values())
=== modified file 'py/wrapper/yadeWrapper.cpp'
--- py/wrapper/yadeWrapper.cpp 2013-07-23 10:01:46 +0000
+++ py/wrapper/yadeWrapper.cpp 2013-10-04 13:41:43 +0000
@@ -133,7 +133,7 @@
#endif
vector<Body::id_t> ret; FOREACH(shared_ptr<Body>& b, bb){ret.push_back(append(b));} return ret;
}
- Body::id_t clump(vector<Body::id_t> ids){
+ Body::id_t clump(vector<Body::id_t> ids, unsigned int discretization, bool integrateInertia){
// create and add clump itself
Scene* scene(Omega::instance().getScene().get());
shared_ptr<Body> clumpBody=shared_ptr<Body>(new Body());
@@ -149,39 +149,40 @@
};
FOREACH(Body::id_t id, ids) Clump::add(clumpBody,Body::byId(id,scene));
- Clump::updateProperties(clumpBody);
+ Clump::updateProperties(clumpBody, discretization, integrateInertia);
return clumpBody->getId();
}
- python::tuple appendClump(vector<shared_ptr<Body> > bb){
+ python::tuple appendClump(vector<shared_ptr<Body> > bb, unsigned int discretization, bool integrateInertia){
// append constituent particles
vector<Body::id_t> ids(appendList(bb));
// clump them together (the clump fcn) and return
- return python::make_tuple(clump(ids),ids);
+ return python::make_tuple(clump(ids, discretization, integrateInertia),ids);
}
- void addToClump(Body::id_t bid, Body::id_t cid){
+ void addToClump(vector<Body::id_t> bids, Body::id_t cid, unsigned int discretization, bool integrateInertia){
Scene* scene(Omega::instance().getScene().get()); // get scene
- shared_ptr<Body> bp = Body::byId(bid,scene); // get body pointer
shared_ptr<Body> clp = Body::byId(cid,scene); // get clump pointer
checkClump(clp);
- if (bp->isClump()){
- if (bp == clp) {PyErr_Warn(PyExc_UserWarning,("Warning: Body "+lexical_cast<string>(bid)+" and clump "+lexical_cast<string>(cid)+" are the same bodies. Body was not added.").c_str()); return;}
- Clump::add(clp,bp);//add clump bid to clump cid
- Clump::updateProperties(clp);
- proxee->erase(bid);//erase old clump
- return;
- }
- else if (bp->isClumpMember()){
- Body::id_t bpClumpId = bp->clumpId;
- shared_ptr<Body> bpClumpPointer = Body::byId(bpClumpId,scene);
- if (bpClumpPointer == clp) {PyErr_Warn(PyExc_UserWarning,("Warning: Body "+lexical_cast<string>(bid)+" is already a clump member of clump "+lexical_cast<string>(cid)+". Body was not added.").c_str()); return;}
- Clump::add(clp,bpClumpPointer);//add clump bpClumpId to clump cid
- Clump::updateProperties(clp);
- proxee->erase(bpClumpId);//erase old clump
- return;
- }
- else {Clump::add(clp,bp); Clump::updateProperties(clp);}// bp must be a standalone!
+ vector<Body::id_t> eraseList;
+ FOREACH(Body::id_t bid, bids) {
+ shared_ptr<Body> bp = Body::byId(bid,scene); // get body pointer
+ if (bp->isClump()){
+ if (bp == clp) {PyErr_Warn(PyExc_UserWarning,("Warning: Body "+lexical_cast<string>(bid)+" and clump "+lexical_cast<string>(cid)+" are the same bodies. Body was not added.").c_str()); return;}
+ Clump::add(clp,bp);//add clump bid to clump cid
+ eraseList.push_back(bid);
+ }
+ else if (bp->isClumpMember()){
+ Body::id_t bpClumpId = bp->clumpId;
+ shared_ptr<Body> bpClumpPointer = Body::byId(bpClumpId,scene);
+ if (bpClumpPointer == clp) {PyErr_Warn(PyExc_UserWarning,("Warning: Body "+lexical_cast<string>(bid)+" is already a clump member of clump "+lexical_cast<string>(cid)+". Body was not added.").c_str()); return;}
+ Clump::add(clp,bpClumpPointer);//add clump bpClumpId to clump cid
+ eraseList.push_back(bpClumpId);
+ }
+ else Clump::add(clp,bp);// bp must be a standalone!
+ }
+ Clump::updateProperties(clp, discretization, integrateInertia);
+ FOREACH(Body::id_t bid, eraseList) proxee->erase(bid);//erase old clumps
}
- void releaseFromClump(Body::id_t bid, Body::id_t cid){
+ void releaseFromClump(Body::id_t bid, Body::id_t cid, unsigned int discretization, bool integrateInertia){
Scene* scene(Omega::instance().getScene().get()); // get scene
shared_ptr<Body> bp = Body::byId(bid,scene); // get body pointer
shared_ptr<Body> clp = Body::byId(cid,scene); // get clump pointer
@@ -193,11 +194,11 @@
std::map<Body::id_t,Se3r>& members = clump->members;
if (members.size() == 2) {PyErr_Warn(PyExc_UserWarning,("Warning: Body "+lexical_cast<string>(bid)+" not released from clump "+lexical_cast<string>(cid)+", because number of clump members would get < 2!").c_str()); return;}
Clump::del(clp,bp);//release bid from cid
- Clump::updateProperties(clp);
+ Clump::updateProperties(clp, discretization, integrateInertia);
} else { PyErr_Warn(PyExc_UserWarning,("Warning: Body "+lexical_cast<string>(bid)+" must be a clump member of clump "+lexical_cast<string>(cid)+". Body was not released.").c_str()); return;}
} else { PyErr_Warn(PyExc_UserWarning,("Warning: Body "+lexical_cast<string>(bid)+" is not a clump member. Body was not released.").c_str()); return;}
}
- python::list replaceByClumps(python::list ctList, vector<Real> amounts){
+ python::list replaceByClumps(python::list ctList, vector<Real> amounts, unsigned int discretization, bool integrateInertia){
python::list ret;
Real checkSum = 0.0;
FOREACH(Real amount, amounts) {
@@ -336,7 +337,7 @@
LOG_DEBUG("New body (sphere) "<<newSphere->id<<" added.");
idsTmp[jj] = newSphere->id;
}
- Body::id_t newClumpId = clump(idsTmp);
+ Body::id_t newClumpId = clump(idsTmp, discretization, integrateInertia);
ret.append(python::make_tuple(newClumpId,idsTmp));
erase(b->id);
}
@@ -471,16 +472,20 @@
public:
pyForceContainer(shared_ptr<Scene> _scene): scene(_scene) { }
void checkId(long id){ if(id<0 || (size_t)id>=scene->bodies->size()){ PyErr_SetString(PyExc_IndexError, "Body id out of range."); python::throw_error_already_set(); /* never reached */ throw; } }
- Vector3r force_get(long id){ checkId(id); return scene->forces.getForceSingle(id); /* scene->forces.sync(); return scene->forces.getForce(id); */}
- Vector3r torque_get(long id){ checkId(id); return scene->forces.getTorqueSingle(id); }
- Vector3r move_get(long id){ checkId(id); return scene->forces.getMoveSingle(id); }
- Vector3r rot_get(long id){ checkId(id); return scene->forces.getRotSingle(id); }
- void force_add(long id, const Vector3r& f){ checkId(id); scene->forces.addForce (id,f); }
- void torque_add(long id, const Vector3r& t){ checkId(id); scene->forces.addTorque(id,t);}
+ Vector3r force_get(long id, bool sync){ checkId(id); if (!sync) return scene->forces.getForceSingle(id); scene->forces.sync(); return scene->forces.getForce(id);}
+ Vector3r torque_get(long id, bool sync){ checkId(id); if (!sync) return scene->forces.getTorqueSingle(id); scene->forces.sync(); return scene->forces.getTorque(id);}
+ Vector3r move_get(long id){ checkId(id); return scene->forces.getMoveSingle(id); }
+ Vector3r rot_get(long id){ checkId(id); return scene->forces.getRotSingle(id); }
+ void force_add(long id, const Vector3r& f, bool permanent){ checkId(id); if (!permanent) scene->forces.addForce (id,f); else scene->forces.addPermForce (id,f); }
+ void torque_add(long id, const Vector3r& t, bool permanent){ checkId(id); if (!permanent) scene->forces.addTorque(id,t); else scene->forces.addPermTorque(id,t);}
void move_add(long id, const Vector3r& t){ checkId(id); scene->forces.addMove(id,t);}
void rot_add(long id, const Vector3r& t){ checkId(id); scene->forces.addRot(id,t);}
+ Vector3r permForce_get(long id){ checkId(id); return scene->forces.getPermForce(id);}
+ Vector3r permTorque_get(long id){ checkId(id); return scene->forces.getPermTorque(id);}
+ void reset(bool resetAll) {scene->forces.reset(scene->iter,resetAll);}
long syncCount_get(){ return scene->forces.syncCount;}
void syncCount_set(long count){ scene->forces.syncCount=count;}
+ bool getPermForceUsed() {return scene->forces.getPermForceUsed();}
};
class pyMaterialContainer{
@@ -855,12 +860,12 @@
.def("__iter__",&pyBodyContainer::pyIter)
.def("append",&pyBodyContainer::append,"Append one Body instance, return its id.")
.def("append",&pyBodyContainer::appendList,"Append list of Body instance, return list of ids")
- .def("appendClumped",&pyBodyContainer::appendClump,"Append given list of bodies as a clump (rigid aggregate); returns a tuple of ``(clumpId,[memberId1,memberId2,...])``. Clump masses and inertia are adapted automatically. If clump members are overlapping this is done by integration/summation over mass points using a regular grid of cells. For non-overlapping members inertia of the clump is the sum of inertias from members.")
- .def("clump",&pyBodyContainer::clump,"Clump given bodies together (creating a rigid aggregate); returns ``clumpId``. Clump masses and inertia are adapted automatically (see :yref:`appendClumped()<BodyContainer.appendClumped>`).")
- .def("addToClump",&pyBodyContainer::addToClump,"Add body b to an existing clump c. c must be clump and b may not be a clump member of c.\n\nSee **/examples/clumps/addToClump-example.py** for an example script.\n\n.. note:: If b is a clump itself, then all members will be added to c and b will be deleted. If b is a clump member of clump d, then all members from d will be added to c and d will be deleted. If you need to add just clump member b, :yref:`release<BodyContainer.releaseFromClump>` this member from d first.")
- .def("releaseFromClump",&pyBodyContainer::releaseFromClump,"Release body b from clump c. b must be a clump member of c.\n\nSee **/examples/clumps/releaseFromClump-example.py** for an example script.\n\n.. note:: If c contains only 2 members b will not be released and a warning will appear. In this case clump c should be :yref:`erased<BodyContainer.erase>`.")
- .def("replaceByClumps",&pyBodyContainer::replaceByClumps,"Replace spheres by clumps using a list of clump templates and a list of amounts; returns a list of tuples: ``[(clumpId1,[memberId1,memberId2,...]),(clumpId2,[memberId1,memberId2,...]),...]``. A new clump will have the same volume as the sphere, that was replaced (clump volume/mass/inertia is accounting for overlaps assuming that there are only pair overlaps, to adapt masses of clumps with multiple overlaps use :yref:`adaptClumpMasses()<BodyContainer.adaptClumpMasses>`). \n\n\t *O.bodies.replaceByClumps( [utils.clumpTemplate([1,1],[.5,.5])] , [.9] ) #will replace 90 % of all standalone spheres by 'dyads'*\n\nSee **/examples/clumps/replaceByClumps-example.py** for an example script.")
- .def("getRoundness",&pyBodyContainer::getRoundness,"Returns roundness coefficient RC = R2/R1. R1 is the theoretical radius of a sphere, with same volume as clump. R2 is the minimum radius of a sphere, that imbeds clump. If just spheres are present RC = 1. If clumps are present 0 < RC < 1. Bodies can be excluded from the calculation by giving a list of ids: *O.bodies.getRoundness([ids])*.\n\nSee **/examples/clumps/replaceByClumps-example.py** for an example script.")
+ .def("appendClumped",&pyBodyContainer::appendClump,(python::arg("discretization")=15,python::arg("integrateInertia")=true),"Append given list of bodies as a clump (rigid aggregate); returns a tuple of ``(clumpId,[memberId1,memberId2,...])``. Clump masses and inertia are adapted automatically (for details see :yref:`clump()<BodyContainer.clump>`).")
+ .def("clump",&pyBodyContainer::clump,(python::arg("discretization")=15,python::arg("integrateInertia")=true),"Clump given bodies together (creating a rigid aggregate); returns ``clumpId``. Clump masses and inertia are adapted automatically (default with integrateInertia=True). If clump members are overlapping this is done by integration/summation over mass points using a regular grid of cells (number of grid cells in one direction is defined as $R_{min}/discretization$, where $R_{min}$ is minimum clump member radius). For non-overlapping members inertia of the clump is the sum of inertias from members. If integrateInertia=False sum of inertias from members is used (faster, but inaccurate).")
+ .def("addToClump",&pyBodyContainer::addToClump,(python::arg("discretization")=15,python::arg("integrateInertia")=true),"Add body b (or a list of bodies) to an existing clump c. c must be clump and b may not be a clump member of c. Clump masses and inertia are adapted automatically (for details see :yref:`clump()<BodyContainer.clump>`).\n\nSee :ysrc:`examples/clumps/addToClump-example.py` for an example script.\n\n.. note:: If b is a clump itself, then all members will be added to c and b will be deleted. If b is a clump member of clump d, then all members from d will be added to c and d will be deleted. If you need to add just clump member b, :yref:`release<BodyContainer.releaseFromClump>` this member from d first.")
+ .def("releaseFromClump",&pyBodyContainer::releaseFromClump,(python::arg("discretization")=15,python::arg("integrateInertia")=true),"Release body b from clump c. b must be a clump member of c. Clump masses and inertia are adapted automatically (for details see :yref:`clump()<BodyContainer.clump>`).\n\nSee :ysrc:`examples/clumps/releaseFromClump-example.py` for an example script.\n\n.. note:: If c contains only 2 members b will not be released and a warning will appear. In this case clump c should be :yref:`erased<BodyContainer.erase>`.")
+ .def("replaceByClumps",&pyBodyContainer::replaceByClumps,(python::arg("discretization")=15,python::arg("integrateInertia")=true),"Replace spheres by clumps using a list of clump templates and a list of amounts; returns a list of tuples: ``[(clumpId1,[memberId1,memberId2,...]),(clumpId2,[memberId1,memberId2,...]),...]``. A new clump will have the same volume as the sphere, that was replaced. Clump masses and inertia are adapted automatically (for details see :yref:`clump()<BodyContainer.clump>`). \n\n\t *O.bodies.replaceByClumps( [utils.clumpTemplate([1,1],[.5,.5])] , [.9] ) #will replace 90 % of all standalone spheres by 'dyads'*\n\nSee :ysrc:`examples/clumps/replaceByClumps-example.py` for an example script.")
+ .def("getRoundness",&pyBodyContainer::getRoundness,(python::arg("excludeList")=python::list()),"Returns roundness coefficient RC = R2/R1. R1 is the theoretical radius of a sphere, with same volume as clump. R2 is the minimum radius of a sphere, that imbeds clump. If just spheres are present RC = 1. If clumps are present 0 < RC < 1. Bodies can be excluded from the calculation by giving a list of ids: *O.bodies.getRoundness([ids])*.\n\nSee :ysrc:`examples/clumps/replaceByClumps-example.py` for an example script.")
.def("clear", &pyBodyContainer::clear,"Remove all bodies (interactions not checked)")
.def("erase", &pyBodyContainer::erase,"Erase body with the given id; all interaction will be deleted by InteractionLoop in the next step.")
.def("replace",&pyBodyContainer::replace);
@@ -884,15 +889,19 @@
.def("next",&pyInteractionIterator::pyNext);
python::class_<pyForceContainer>("ForceContainer",python::init<pyForceContainer&>())
- .def("f",&pyForceContainer::force_get,(python::arg("id")),"Force applied on body.")
- .def("t",&pyForceContainer::torque_get,(python::arg("id")),"Torque applied on body.")
- .def("m",&pyForceContainer::torque_get,(python::arg("id")),"Deprecated alias for t (torque).")
+ .def("f",&pyForceContainer::force_get,(python::arg("id"),python::arg("sync")=false),"Force applied on body. For clumps in openMP, synchronize the force container with sync=True, else the value will be wrong.")
+ .def("t",&pyForceContainer::torque_get,(python::arg("id"),python::arg("sync")=false),"Torque applied on body. For clumps in openMP, synchronize the force container with sync=True, else the value will be wrong.")
+ .def("m",&pyForceContainer::torque_get,(python::arg("id"),python::arg("sync")=false),"Deprecated alias for t (torque).")
.def("move",&pyForceContainer::move_get,(python::arg("id")),"Displacement applied on body.")
.def("rot",&pyForceContainer::rot_get,(python::arg("id")),"Rotation applied on body.")
- .def("addF",&pyForceContainer::force_add,(python::arg("id"),python::arg("f")),"Apply force on body (accumulates).")
- .def("addT",&pyForceContainer::torque_add,(python::arg("id"),python::arg("t")),"Apply torque on body (accumulates).")
+ .def("addF",&pyForceContainer::force_add,(python::arg("id"),python::arg("f"),python::arg("permanent")=false),"Apply force on body (accumulates).\n\n # If permanent=false (default), the force applies for one iteration, then it is reset by ForceResetter. \n # If permanent=true, it persists over iterations, until it is overwritten by another call to addF(id,f,True) or removed by reset(resetAll=True). The permanent force on a body can be checked with permF(id).")
+ .def("addT",&pyForceContainer::torque_add,(python::arg("id"),python::arg("t"),python::arg("permanent")=false),"Apply torque on body (accumulates). \n\n # If permanent=false (default), the torque applies for one iteration, then it is reset by ForceResetter. \n # If permanent=true, it persists over iterations, until it is overwritten by another call to addT(id,f,True) or removed by reset(resetAll=True). The permanent torque on a body can be checked with permT(id).")
+ .def("permF",&pyForceContainer::permForce_get,(python::arg("id")),"read the value of permanent force on body (set with setPermF()).")
+ .def("permT",&pyForceContainer::permTorque_get,(python::arg("id")),"read the value of permanent torque on body (set with setPermT()).")
.def("addMove",&pyForceContainer::move_add,(python::arg("id"),python::arg("m")),"Apply displacement on body (accumulates).")
.def("addRot",&pyForceContainer::rot_add,(python::arg("id"),python::arg("r")),"Apply rotation on body (accumulates).")
+ .def("reset",&pyForceContainer::reset,(python::arg("resetAll")=true),"Reset the force container, including user defined permanent forces/torques. resetAll=False will keep permanent forces/torques unchanged.")
+ .def("getPermForceUsed",&pyForceContainer::getPermForceUsed,"Check wether permanent forces are present.")
.add_property("syncCount",&pyForceContainer::syncCount_get,&pyForceContainer::syncCount_set,"Number of synchronizations of ForceContainer (cummulative); if significantly higher than number of steps, there might be unnecessary syncs hurting performance.")
;
=== modified file 'py/ymport.py'
--- py/ymport.py 2013-04-24 16:19:28 +0000
+++ py/ymport.py 2013-08-14 08:59:42 +0000
@@ -337,12 +337,11 @@
:param float scale: factor scales the given data.
:param \*\*kw: (unused keyword arguments) is passed to :yref:`yade.utils.facet`
:param bool returnConnectivityTable: if True, apart from facets returns also nodes (list of (x,y,z) nodes coordinates) and elements (list of (id1,id2,id3) element nodes ids). If False (default), returns only facets
-
- Example: :ysrc:`examples/test/unv-read/unvRead.py`."""
+ """
nodes,elems = [],[]
f = open(fileName)
for line in f:
- if line.startswith('134'): # read nodes coordinates
+ if line.startswith('134,'): # read nodes coordinates
ls = line.split(',')
v = Vector3(
float(ls[1])*scale + shift[0],
@@ -350,9 +349,9 @@
float(ls[3])*scale + shift[2]
)
nodes.append(v)
- if line.startswith('136'): # read elements
+ if line.startswith('136,'): # read elements
ls = line.split(',')
- i1,i2,i3 = int(ls[3])/2, int(ls[4])/2, int(ls[5])/2 # the numbering of nodes is 1,3,5,7,..., hense this int(ls[*])/2
+ i1,i2,i3 = int(ls[3])/2, int(ls[4])/2, int(ls[5])/2 # the numbering of nodes is 1,3,5,7,..., hence this int(ls[*])/2
elems.append( (i1,i2,i3) )
facets = [utils.facet( ( nodes[e[0]], nodes[e[1]], nodes[e[2]] ), **kw) for e in elems]
if returnConnectivityTable:
=== added file 'scripts/checks-and-tests/checks/DEM-PFV-check.py'
--- scripts/checks-and-tests/checks/DEM-PFV-check.py 1970-01-01 00:00:00 +0000
+++ scripts/checks-and-tests/checks/DEM-PFV-check.py 2013-08-30 10:33:17 +0000
@@ -0,0 +1,146 @@
+# -*- coding: utf-8 -*-
+# Here, we are testing bulk modulus, then permeability, then the consolidation of a specimen.
+# the test is based on examples/FluidCouplingPFV/oedometer.py, only slightly simplified and using less particles
+
+try: FlowEngine
+except NameError:
+ print "skip DEM-PFV check, FlowEngine not available"
+else:
+
+ errors=0
+ tolerance=0.01
+
+ from yade import pack
+ num_spheres=100# number of spheres
+ young=1e6
+ compFricDegree = 3 # initial contact friction during the confining phase
+ finalFricDegree = 30 # contact friction during the deviatoric loading
+ mn,mx=Vector3(0,0,0),Vector3(1,1,1) # corners of the initial packing
+
+ O.materials.append(FrictMat(young=young,poisson=0.5,frictionAngle=radians(compFricDegree),density=2600,label='spheres'))
+ O.materials.append(FrictMat(young=young,poisson=0.5,frictionAngle=0,density=0,label='walls'))
+ walls=aabbWalls([mn,mx],thickness=0,material='walls')
+ wallIds=O.bodies.append(walls)
+
+ sp=pack.SpherePack()
+ #sp.makeCloud(mn,mx,-1,0.3333,num_spheres,False, 0.95,seed=1) #"seed" is not enough for portable determinism it seems, let us use a data file
+ sp.load(checksPath+'/data/100spheres')
+
+ sp.toSimulation(material='spheres')
+
+ triax=TriaxialStressController(
+ maxMultiplier=1.+2e4/young, # spheres growing factor (fast growth)
+ finalMaxMultiplier=1.+2e3/young, # spheres growing factor (slow growth)
+ thickness = 0,
+ stressMask = 7,
+ max_vel = 0.005,
+ internalCompaction=True, # If true the confining pressure is generated by growing particles
+ )
+
+ newton=NewtonIntegrator(damping=0.2)
+
+ O.engines=[
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Sphere_Aabb(),Bo1_Box_Aabb()]),
+ InteractionLoop(
+ [Ig2_Sphere_Sphere_ScGeom(),Ig2_Box_Sphere_ScGeom()],
+ [Ip2_FrictMat_FrictMat_FrictPhys()],
+ [Law2_ScGeom_FrictPhys_CundallStrack()],label="iloop"
+ ),
+ FlowEngine(dead=1,label="flow"),#introduced as a dead engine for the moment, see 2nd section
+ GlobalStiffnessTimeStepper(active=1,timeStepUpdateInterval=100,timestepSafetyCoefficient=0.8),
+ triax,
+ newton
+ ]
+
+ triax.goal1=triax.goal2=triax.goal3=10000
+
+ while 1:
+ O.run(200, True)
+ unb=unbalancedForce()
+ if unb<0.01 and abs(10000-triax.meanStress)/10000<0.01: break
+
+ setContactFriction(radians(finalFricDegree))
+
+ ## ______________ Oedometer section _________________
+
+ #A. Check bulk modulus of the dry material from load/unload cycles
+ triax.stressMask=2
+ triax.goal1=triax.goal3=0
+
+ triax.internalCompaction=False
+ triax.wall_bottom_activated=False
+ triax.goal2=11000; O.run(2000,1)
+ triax.goal2=10000; O.run(2000,1)
+ triax.goal2=11000; O.run(2000,1)
+ e22=triax.strain[1]
+ triax.goal2=10000; O.run(2000,1)
+
+ e22=e22-triax.strain[1]
+ modulus = 1000./abs(e22)
+
+ target=249064.586653
+ if abs((modulus-target)/target)>tolerance :
+ print "DEM-PFV: difference in bulk modulus:", modulus, "vs. target ",target
+ errors+=1
+
+ #B. Activate flow engine and set boundary conditions in order to get permeability
+ flow.dead=0
+ flow.defTolerance=0.3
+ flow.meshUpdateInterval=200
+ flow.useSolver=3
+ flow.viscosity=10
+ flow.bndCondIsPressure=[0,0,1,1,0,0]
+ flow.bndCondValue=[0,0,1,0,0,0]
+ flow.boundaryUseMaxMin=[0,0,0,0,0,0]
+ O.dt=0.1e-3
+ O.dynDt=False
+
+ O.run(1,1)
+ Qin = flow.getBoundaryFlux(2)
+ Qout = flow.getBoundaryFlux(3)
+ permeability = abs(Qin)/1.e-4 #size is one, we compute K=V/∇H
+
+ if abs(Qin+Qout)>1e-15 :
+ print "DEM-PFV: unbalanced Qin vs. Qout"
+ errors+=1
+
+ target=0.0408678245942
+ if abs((permeability-target)/target)>tolerance :
+ print "DEM-PFV: difference in permeability:",permeability," vs. target ",target
+ errors+=1
+
+ #C. now the oedometer test, drained at the top, impermeable at the bottom plate
+ flow.bndCondIsPressure=[0,0,0,1,0,0]
+ flow.bndCondValue=[0,0,0,0,0,0]
+ newton.damping=0
+
+ zeroTime=O.time
+ zeroe22 = triax.strain[1]
+ triax.goal2=11000
+
+ O.timingEnabled=1
+ from yade import timing
+ O.run(3000,1)
+
+ target=637.268936033
+ if abs((flow.getPorePressure((0.5,0.1,0.5))-target)/target)>tolerance :
+ print "DEM-PFV: difference in final pressure:",flow.getPorePressure((0.5,0.1,0.5))," vs. target ",target
+ errors+=1
+ target=0.00260892345196
+ if abs((triax.strain[1]-zeroe22-target)/target)>tolerance :
+ print "DEM-PFV: difference in final deformation",triax.strain[1]-zeroe22," vs. target ",target
+ errors+=1
+
+ if (float(flow.execTime)/float(sum([e.execTime for e in O.engines])))>0.6 :
+ print "DEM-PFV: More than 60\% of cpu time in FlowEngine (",100.*(float(flow.execTime)/float(sum([e.execTime for e in O.engines]))) ,"%). Should not happen with efficient libraries (check blas/lapack/cholmod implementations)"
+ errors+=1
+
+ flow.forceMetis=True
+ O.run(201,1)
+ if not flow.metisUsed():
+ print "DEM-PFV: Metis is not used during cholmod's reordering although explicitely enabled, something wrong with libraries"
+ errors+=1
+
+ if (errors):
+ resultStatus +=1 #Test is failed
\ No newline at end of file
=== modified file 'scripts/checks-and-tests/checks/README'
--- scripts/checks-and-tests/checks/README 2012-03-13 09:51:32 +0000
+++ scripts/checks-and-tests/checks/README 2013-08-29 10:30:31 +0000
@@ -15,3 +15,5 @@
7. Users are encouraged to add their own scripts into the scripts/test/checks/ folder. Discussion of some specific checktests design in users question is welcome.
8. A check test should never need more than a few seconds to run. If your typical script needs more, try and reduce the number of element or the number of steps.
+
+9. Failures are reported via a global variable "resultStatus", which should be ONLY INCREMENTED in a checkTests, never assigned to (resultStatus+=1 is ok, resultStatus=1 is bad), else the script would erase the history of the checkTests coming before it.
=== modified file 'scripts/checks-and-tests/checks/checkGravity.py'
--- scripts/checks-and-tests/checks/checkGravity.py 2012-03-13 09:51:32 +0000
+++ scripts/checks-and-tests/checks/checkGravity.py 2013-08-29 10:30:31 +0000
@@ -69,8 +69,6 @@
def getCurrentVel(inVel=0):
t = O.time+O.dt
return inVel + g*t
-
-resultStatus=0
def warningMessagePos(inVel, y_pos, y_pos_need):
print "The body with the initial velocity %.3f, has an y-position %.3f, but it should be %.3f" % (inVel, y_pos, y_pos_need)
=== modified file 'scripts/checks-and-tests/checks/checkList.py'
--- scripts/checks-and-tests/checks/checkList.py 2012-03-13 09:51:32 +0000
+++ scripts/checks-and-tests/checks/checkList.py 2013-09-09 07:37:01 +0000
@@ -4,23 +4,31 @@
scriptsToRun=os.listdir(checksPath)
resultStatus = 0
+nFailed=0
+
+skipScripts = ['checkList.py', 'DEM-PFV-check.py']
+
for script in scriptsToRun:
- if (script[len(script)-3:]==".py" and not(script=="checkList.py")):
+ if (script[len(script)-3:]==".py" and script not in skipScripts):
try:
print "###################################"
print "running: ",script
execfile(checksPath+"/"+script)
- if (resultStatus):
+ if (resultStatus>nFailed):
print "Status: FAILURE!!!"
+ nFailed=resultStatus
else:
print "Status: success"
print "___________________________________"
except:
print script," failure"
O.reset()
+ elif (script in skipScripts):
+ print "###################################"
+ print "Skipping %s, because it is in SkipScripts"%script
-if (resultStatus):
- print "Some tests are failed!"
+if (resultStatus>0):
+ print resultStatus, " tests are failed"
sys.exit(1)
else:
sys.exit(0)
=== modified file 'scripts/checks-and-tests/checks/checkTestDummy.py'
--- scripts/checks-and-tests/checks/checkTestDummy.py 2012-04-13 16:27:00 +0000
+++ scripts/checks-and-tests/checks/checkTestDummy.py 2013-08-29 10:30:31 +0000
@@ -5,3 +5,10 @@
import math,os,sys
print 'checkTest mechanism'
+#Typical structure of a checkTest:
+
+#do something and get a result...
+
+if 0: #put a condition on the result here, is it the expected result? else:
+ print "Dummy failed (we know it will not happen here, you get the idea)."
+ resultStatus+=1
=== modified file 'scripts/checks-and-tests/checks/checkTestTriax.py'
--- scripts/checks-and-tests/checks/checkTestTriax.py 2012-04-13 16:27:00 +0000
+++ scripts/checks-and-tests/checks/checkTestTriax.py 2013-08-29 10:30:31 +0000
@@ -24,6 +24,9 @@
if abs((O.engines[4].stress(1)[0]-50058.7)/50058.7)>tolerance :
print "Triaxial checkTest: difference on confining stress"
errors+=1
+
+if (errors):
+ resultStatus +=1 #Test is failed
if (errors):
resultStatus +=1 #Test is failed
=== modified file 'scripts/checks-and-tests/checks/checkWirePM.py'
--- scripts/checks-and-tests/checks/checkWirePM.py 2013-06-25 03:07:32 +0000
+++ scripts/checks-and-tests/checks/checkWirePM.py 2013-08-30 06:17:12 +0000
@@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
-
+# 2011 © Klaus Thoeni <klaus.thoeni@xxxxxxxxx>
# Check test version for WirePM tensile test
tolerance=0.01
-resultStatus=0
errors=0
#### define parameters for the net
@@ -120,8 +119,7 @@
## critical time step proposed by Bertrand
kn = 16115042 # stiffness of single wire from code
O.dt = 0.2*sqrt(particleMass/(2.*kn))
-
-O.run(350000,True)
+O.run(30000,True)
Fn = 0.
for i in posIds:
@@ -134,11 +132,15 @@
un = O.bodies[O.bodies[posIds[0]].id].state.pos[1] - O.bodies[O.bodies[posIds[0]].id].state.refPos[1]
-if abs((un-0.04)/0.04)>tolerance :
+if abs((un-0.0034)/0.034)>tolerance :
print "WirePM checkTest: difference on peak displacement"
+ print "Reference value:",0.034
+ print "Calculated value:",un
errors+=1
-if abs((Fn-33626.44)/33626.44)>tolerance :
+if abs((Fn-6458.9)/6458.9)>tolerance :
print "WirePM checkTest: difference on peak Force"
+ print "Reference value:",6458.9
+ print "Calculated value:",Fn
errors+=1
if (errors):
=== added file 'scripts/checks-and-tests/checks/data/100spheres'
--- scripts/checks-and-tests/checks/data/100spheres 1970-01-01 00:00:00 +0000
+++ scripts/checks-and-tests/checks/data/100spheres 2013-08-30 10:33:17 +0000
@@ -0,0 +1,100 @@
+0.0632457 0.137506 0.588536 0.063226 -1
+0.842337 0.909075 0.228734 0.0629123 -1
+0.513101 0.410777 0.29259 0.0625985 -1
+0.713178 0.140678 0.552867 0.0622848 -1
+0.572038 0.771198 0.580526 0.061971 -1
+0.510268 0.830189 0.934033 0.0616573 -1
+0.698458 0.909364 0.321995 0.0613435 -1
+0.435077 0.850735 0.634324 0.0610297 -1
+0.88327 0.425009 0.331716 0.060716 -1
+0.513094 0.408062 0.754777 0.0604022 -1
+0.666411 0.538952 0.581302 0.0600885 -1
+0.142211 0.596396 0.440358 0.0597747 -1
+0.740399 0.122601 0.228504 0.059461 -1
+0.256304 0.747201 0.133182 0.0591472 -1
+0.218839 0.602966 0.569829 0.0588335 -1
+0.711303 0.335885 0.446001 0.0585197 -1
+0.568087 0.203046 0.675411 0.058206 -1
+0.35397 0.460902 0.125286 0.0578922 -1
+0.171544 0.212005 0.448482 0.0575785 -1
+0.569966 0.654172 0.139978 0.0572647 -1
+0.116057 0.794807 0.402336 0.056951 -1
+0.225881 0.2033 0.906605 0.0566372 -1
+0.935517 0.157095 0.828888 0.0563234 -1
+0.608556 0.653312 0.60127 0.0560097 -1
+0.577936 0.18979 0.282304 0.0556959 -1
+0.765827 0.582739 0.852717 0.0553822 -1
+0.296862 0.231217 0.29576 0.0550684 -1
+0.407717 0.14018 0.631867 0.0547547 -1
+0.133618 0.899989 0.536212 0.0544409 -1
+0.0921193 0.544563 0.70964 0.0541272 -1
+0.486208 0.46921 0.0602879 0.0538134 -1
+0.252228 0.23762 0.563835 0.0534997 -1
+0.155961 0.593446 0.205225 0.0531859 -1
+0.677064 0.271099 0.647271 0.0528722 -1
+0.725058 0.404587 0.790763 0.0525584 -1
+0.337957 0.79412 0.52915 0.0522447 -1
+0.762235 0.905443 0.869685 0.0519309 -1
+0.754353 0.763991 0.564623 0.0516172 -1
+0.869227 0.32951 0.763365 0.0513034 -1
+0.250542 0.453809 0.602187 0.0509896 -1
+0.323428 0.875137 0.101187 0.0506759 -1
+0.262607 0.771509 0.44276 0.0503621 -1
+0.935384 0.602415 0.0959998 0.0500484 -1
+0.807628 0.265685 0.547061 0.0497346 -1
+0.210523 0.519291 0.802076 0.0494209 -1
+0.346678 0.445988 0.335549 0.0491071 -1
+0.712145 0.375483 0.90935 0.0487934 -1
+0.079367 0.0931514 0.842839 0.0484796 -1
+0.56555 0.93538 0.10315 0.0481659 -1
+0.109913 0.781804 0.155589 0.0478521 -1
+0.933627 0.325613 0.251893 0.0475384 -1
+0.607802 0.937669 0.730514 0.0472246 -1
+0.21714 0.876959 0.650905 0.0469109 -1
+0.713226 0.866026 0.720087 0.0465971 -1
+0.205985 0.336471 0.560601 0.0462833 -1
+0.404262 0.27439 0.467811 0.0459696 -1
+0.399282 0.21219 0.5422 0.0456558 -1
+0.279491 0.743819 0.600524 0.0453421 -1
+0.790314 0.223836 0.440482 0.0450283 -1
+0.173372 0.349905 0.6542 0.0447146 -1
+0.872236 0.77503 0.319853 0.0444008 -1
+0.950422 0.316509 0.681236 0.0440871 -1
+0.875169 0.816942 0.480635 0.0437733 -1
+0.10681 0.139044 0.218576 0.0434596 -1
+0.44486 0.492578 0.399139 0.0431458 -1
+0.0584047 0.166868 0.316999 0.0428321 -1
+0.270556 0.626276 0.464675 0.0425183 -1
+0.814508 0.681643 0.904557 0.0422046 -1
+0.231417 0.205926 0.208882 0.0418908 -1
+0.919379 0.403174 0.713806 0.041577 -1
+0.171969 0.774404 0.697855 0.0412633 -1
+0.23728 0.475321 0.91767 0.0409495 -1
+0.372958 0.577225 0.0409206 0.0406358 -1
+0.926633 0.934036 0.646285 0.040322 -1
+0.716763 0.904977 0.424815 0.0400083 -1
+0.588615 0.886954 0.871796 0.0396945 -1
+0.146662 0.3448 0.369151 0.0393808 -1
+0.319354 0.476513 0.661693 0.039067 -1
+0.156809 0.453677 0.546265 0.0387533 -1
+0.416026 0.40884 0.60921 0.0384395 -1
+0.0818604 0.387212 0.662613 0.0381258 -1
+0.92477 0.0546337 0.437419 0.037812 -1
+0.532074 0.304338 0.905312 0.0374983 -1
+0.581168 0.387114 0.578045 0.0371845 -1
+0.492878 0.335594 0.662791 0.0368708 -1
+0.192548 0.76341 0.544556 0.036557 -1
+0.865145 0.877785 0.710656 0.0362432 -1
+0.17073 0.719191 0.256069 0.0359295 -1
+0.0826056 0.232443 0.719743 0.0356157 -1
+0.769743 0.431122 0.159319 0.035302 -1
+0.218126 0.38882 0.909338 0.0349882 -1
+0.382319 0.603201 0.318078 0.0346745 -1
+0.800503 0.477491 0.763354 0.0343607 -1
+0.909841 0.522721 0.399759 0.034047 -1
+0.722825 0.660194 0.668187 0.0337332 -1
+0.394776 0.40613 0.713393 0.0334195 -1
+0.0397222 0.0600967 0.278554 0.0331057 -1
+0.583692 0.935074 0.93591 0.032792 -1
+0.201732 0.607963 0.0696597 0.0324782 -1
+0.165494 0.421522 0.84153 0.0321645 -1
=== modified file 'scripts/erskine3.py' (properties changed: +x to -x)
=== modified file 'scripts/hackett.py' (properties changed: +x to -x)
=== modified file 'scripts/linkdeps.py' (properties changed: +x to -x)
=== added directory 'scripts/ppa'
=== added file 'scripts/ppa/createtar.py'
--- scripts/ppa/createtar.py 1970-01-01 00:00:00 +0000
+++ scripts/ppa/createtar.py 2013-11-18 20:26:55 +0000
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+import argparse, os, git, shutil
+
+jobsNumber = 4
+
+parser = argparse.ArgumentParser(description='Process some integers.')
+parser.add_argument("g", help="git debian-directory")
+parser.add_argument("p", help="direcrory, where will be created pbuilder tarballs")
+parser.add_argument("u", help="git upstream-direcrory")
+parser.add_argument("-u","--update", help="update tarballs, if they are exist", action='store_true')
+args = parser.parse_args()
+
+gitdebdir = args.g
+gitupsdir = args.u
+pbdir = args.p
+
+if not(os.path.isdir(gitdebdir)):
+ raise RuntimeError('Git-debian-directory does not exists')
+
+if not(os.path.isdir(gitupsdir)):
+ raise RuntimeError('Git-upstream-directory does not exists')
+
+if not(os.path.isdir(pbdir)):
+ print ('Pbuilder-directory does not exists. Creating')
+ os.mkdir(pbdir)
+
+repodeb = git.Repo(gitdebdir)
+repoups = git.Repo(gitupsdir)
+assert repodeb.bare == False
+assert repoups.bare == False
+
+if (repodeb.is_dirty()):
+ raise RuntimeError('Git-debian-repo has an uncommitted changes. Exiting.')
+
+if (repoups.is_dirty()):
+ raise RuntimeError('Git-upstream-repo has an uncommitted changes. Exiting.')
+
+for branch in repodeb.branches:
+ branchstr = str(branch)
+ if (branchstr<>'master'):
+ print "Switching to branch %s"%(branch)
+ repodeb.git.checkout(branch)
+ infile = open(gitdebdir+"/pbuilder")
+ lines = infile.readlines()
+ mirror = lines[0].strip()
+ components = lines[1].strip()
+ archs = lines[2].split()
+ keyringuse = lines[3].strip()
+ infile.close()
+ for a in archs:
+ tarball = "%s/%s_%s.tgz"%(pbdir, branchstr, a.strip())
+ if not(os.path.isfile(tarball)):
+ createPbTar = ('sudo pbuilder --create --distribution %s --mirror %s --components "%s" --architecture %s --debootstrapopts "--keyring=%s" --basetgz %s'%
+ (branchstr, mirror, components, a, keyringuse, tarball))
+ print "Creating tarball %s"%(tarball)
+ os.system(createPbTar)
+ else:
+ print "Tarball %s exists"%(tarball)
+ if (args.update):
+ print "Updating %s as requested" %(tarball)
+ updatePbTar = ('sudo pbuilder --update --basetgz %s'%(tarball))
+ os.system(updatePbTar)
+
+ # Creating dir for building
+ builddirup="%s_%s/"%(branchstr, a)
+ builddirdeb="%s/build/"%(builddirup)
+ builddirres="%s/result/"%(builddirup)
+ shutil.rmtree(builddirdeb,ignore_errors=True)
+ shutil.copytree(gitupsdir, builddirdeb )
+ shutil.rmtree(builddirdeb+".git")
+ # Get package version
+ versiondebian = repoups.git.describe()
+
+ # Get package name
+ infilepname = open(gitdebdir+"/changelog"); sourcePackName = infilepname.readlines()[0].split()[0]
+ print sourcePackName
+
+ os.system('cd %s; apack %s_%s.orig.tar.xz build'%(builddirup,sourcePackName,versiondebian))
+ shutil.copytree(gitdebdir, builddirdeb+"/debian")
+
+ os.system('sed -i.bak -e s/VERSION/%s/ -e s/DISTRIBUTION/%s/ %s/debian/changelog'%(versiondebian,branch,builddirdeb))
+ os.system('cd %s; dpkg-source -b -I build'%(builddirup))
+ os.mkdir(builddirres)
+ print "Building package %s_%s"%(sourcePackName, versiondebian)
+ buildPackage = ('sudo pbuilder --build --architecture %s --basetgz %s --logfile %s/pbuilder.log --debbuildopts "-j%d" --buildresult %s %s/*.dsc'%
+ (a, tarball, builddirup, jobsNumber, builddirres, builddirup))
+ print buildPackage
+ os.system(buildPackage)
+ shutil.rmtree(builddirdeb)
+ exit(0)
+
=== modified file 'scripts/py2wiki.py' (properties changed: +x to -x)
=== modified file 'scripts/rename-class.py' (properties changed: +x to -x)
=== removed file 'yade.kdev4'
--- yade.kdev4 2010-07-01 19:22:26 +0000
+++ yade.kdev4 1970-01-01 00:00:00 +0000
@@ -1,3 +0,0 @@
-[Project]
-Manager=KDevCustomMakeManager
-Name=yade