Commit 882a674e authored by Chiara Modenese's avatar Chiara Modenese
Browse files

1. Add scripts/test/force-network-video.py

2. Rename attribute flags (Attr::readonly, Attr::triggerPostLoad etc)
3. Detect whether display is available and store it in yade.runtime.hasDisplay (false by default); that restores the behavior of yade.qt, which raises ImportError at no display, and makes yade.plot consistent with tha variable as well
4. Fix total running time for batches
5. Do not send 0 refresh for finished job files (refreshes as frequently as possible, whereas no refresh was desired)
6. Debian package depends on mencoder now
7. qt.SnapshotEngine open the view automatically if needed (not very reliable, though)
8. Add scripts/test/force-network-video.py
9. Fixes in the OpenGLRenderer that avoid crashes; drawWithNames should draw shapes in the same place as draw witout names, making the selection consistent with what is seen (periodic boundaries etc)
10. Move {ScGeom,Dem3DofGeom}::contactPoint to GenericSpheresContact
11. Initial (not yet functional) implementation of the partial slip solution for Hertz-Mindlin (Mindlin-Deresiewitz)
parent fc13bc84
......@@ -63,17 +63,17 @@ class Body: public Serializable{
friend class BodyContainer;
YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(Body,Serializable,"A particle, basic element of simulation; interacts with other bodies.",
((Body::id_t,id,Body::ID_NONE,Attr::pyReadonly,"Unique id of this body."))
((Body::id_t,id,Body::ID_NONE,Attr::readonly,"Unique id of this body."))
((int,groupMask,1,,"Bitmask for determining interactions."))
((int,flags,FLAG_DYNAMIC|FLAG_BOUNDED,Attr::pyReadonly,"Bits of various body-related flags. *Do not access directly*. In c++, use isDynamic/setDynamic, isBounded/setBounded. In python, use :yref:`Body.dynamic` and :yref:`Body.bounded`."))
((int,flags,FLAG_DYNAMIC|FLAG_BOUNDED,Attr::readonly,"Bits of various body-related flags. *Do not access directly*. In c++, use isDynamic/setDynamic, isBounded/setBounded. In python, use :yref:`Body.dynamic` and :yref:`Body.bounded`."))
((shared_ptr<Material>,material,,,":yref:`Material` instance associated with this body."))
((shared_ptr<State>,state,new State,,"Physical :yref:`state<State>`."))
((shared_ptr<Shape>,shape,,,"Geometrical :yref:`Shape`."))
((shared_ptr<Bound>,bound,,,":yref:`Bound`, approximating volume for the purposes of collision detection."))
((int,clumpId,Body::ID_NONE,Attr::pyReadonly,"Id of clump this body makes part of; invalid number if not part of clump; see :yref:`Body::isStandalone`, :yref:`Body::isClump`, :yref:`Body::isClumpMember` properties. \n\n This property is not meant to be modified directly from Python, use :yref:`O.bodies.appendClumped<BodyContainer.appendClumped>` instead.")),
((int,clumpId,Body::ID_NONE,Attr::readonly,"Id of clump this body makes part of; invalid number if not part of clump; see :yref:`Body::isStandalone`, :yref:`Body::isClump`, :yref:`Body::isClumpMember` properties. \n\n This property is not meant to be modified directly from Python, use :yref:`O.bodies.appendClumped<BodyContainer.appendClumped>` instead.")),
/* ctor */,
/* py */
//
......
......@@ -23,8 +23,8 @@ class Bound: public Serializable, public Indexable{
public:
YADE_CLASS_BASE_DOC_ATTRS_DEPREC_INIT_CTOR_PY(Bound,Serializable,"Object bounding part of space taken by associated body; might be larger, used to optimalize collision detection",
((Vector3r,color,Vector3r(1,1,1),,"Color for rendering this object"))
((Vector3r,min,Vector3r(NaN,NaN,NaN),(Attr::noSave | Attr::pyReadonly),"Lower corner of box containing this bound (and the :yref:`Body` as well)"))
((Vector3r,max,Vector3r(NaN,NaN,NaN),(Attr::noSave | Attr::pyReadonly),"Lower corner of box containing this bound (and the :yref:`Body` as well)"))
((Vector3r,min,Vector3r(NaN,NaN,NaN),(Attr::noSave | Attr::readonly),"Lower corner of box containing this bound (and the :yref:`Body` as well)"))
((Vector3r,max,Vector3r(NaN,NaN,NaN),(Attr::noSave | Attr::readonly),"Lower corner of box containing this bound (and the :yref:`Body` as well)"))
,
/*deprec*/ ((diffuseColor,color,"For consistency with Shape.color")),
/* init */,
......
......@@ -94,22 +94,18 @@ class Cell: public Serializable{
void postLoad(Cell&){ integrateAndUpdate(0); }
// to resolve overloads
Vector3r wrapShearedPt_py(const Vector3r& pt) const { return wrapShearedPt(pt);}
Vector3r wrapPt_py(const Vector3r& pt) const { return wrapPt(pt);}
YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(Cell,Serializable,"Parameters of periodic boundary conditions. Only applies if O.isPeriodic==True.",
/* TODO: remove the overrides, add Attr::pyCallPostLoad */
((Vector3r,refSize,Vector3r(1,1,1),,"[will be overridden below]"))
((Matrix3r,trsf,Matrix3r::Identity(),,"[will be overridden below]"))
((Vector3r,refSize,Vector3r(1,1,1),Attr::triggerPostLoad,"Reference size of the cell."))
((Matrix3r,trsf,Matrix3r::Identity(),Attr::triggerPostLoad,"Current transformation matrix of the cell."))
((Matrix3r,velGrad,Matrix3r::Zero(),,"Velocity gradient of the transformation; used in NewtonIntegrator."))
((Matrix3r,Hsize,Matrix3r::Zero(),,"The current period size (one column per box edge) |yupdate|")),
((Matrix3r,Hsize,Matrix3r::Zero(),Attr::readonly,"The current cell size (one column per box edge), computed from *refSize* and *trsf* |yupdate|")),
/*ctor*/ integrateAndUpdate(0),
/*py*/
.def_readonly("size",&Cell::getSize_copy,"Current size of the cell, i.e. lengths of 3 cell lateral vectors after applying current trsf. Update automatically at every step.")
/* accessors that ensure cache coherence */
.add_property("refSize",&Cell::getRefSize,&Cell::setRefSize,"Reference size of the cell.")
.add_property("trsf",&Cell::getTrsf,&Cell::setTrsf,"Transformation matrix of the cell.")
.add_property("volume",&Cell::getVolume,"Current volume of the cell.")
// debugging only
.def("wrap",&Cell::wrapShearedPt_py,"Transform an arbitrary point into a point in the reference cell")
......
......@@ -61,8 +61,8 @@ class Interaction : public Serializable
void init();
YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(Interaction,Serializable,"Interaction between pair of bodies.",
((Body::id_t,id1,0,Attr::pyReadonly,":yref:`Id<Body::id>` of the first body in this interaction."))
((Body::id_t,id2,0,Attr::pyReadonly,":yref:`Id<Body::id>` of the first body in this interaction."))
((Body::id_t,id1,0,Attr::readonly,":yref:`Id<Body::id>` of the first body in this interaction."))
((Body::id_t,id2,0,Attr::readonly,":yref:`Id<Body::id>` of the first body in this interaction."))
((long,iterMadeReal,-1,,"Step number at which the interaction was fully (in the sense of interactionGeometry and interactionPhysics) created. (Should be touched only by :yref:`InteractionPhysicsDispatcher` and :yref:`InteractionDispatchers`, therefore they are made friends of Interaction"))
((shared_ptr<InteractionGeometry>,interactionGeometry,,,"Geometry part of the interaction."))
((shared_ptr<InteractionPhysics>,interactionPhysics,,,"Physical (material) part of the interaction."))
......
......@@ -35,7 +35,7 @@ class Material: public Serializable, public Indexable{
static const shared_ptr<Material> byLabel(const std::string& label, shared_ptr<Scene> scene) {return byLabel(label,scene.get());}
YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(Material,Serializable,"Material properties of a :yref:`body<Body>`.",
((int,id,((void)"not shared",-1),Attr::pyReadonly,"Numeric id of this material; is non-negative only if this Material is shared (i.e. in O.materials), -1 otherwise. This value is set automatically when the material is inserted to the simulation via :yref:`O.materials.append<MaterialContainer.append>`. (This id was necessary since before boost::serialization was used, shared pointers were not tracked properly; it might disappear in the future)"))
((int,id,((void)"not shared",-1),Attr::readonly,"Numeric id of this material; is non-negative only if this Material is shared (i.e. in O.materials), -1 otherwise. This value is set automatically when the material is inserted to the simulation via :yref:`O.materials.append<MaterialContainer.append>`. (This id was necessary since before boost::serialization was used, shared pointers were not tracked properly; it might disappear in the future)"))
((string,label,,,"Textual identifier for this material; can be used for shared materials lookup in :yref:`MaterialContainer`."))
((Real,density,1000,,"Density of the material [kg/m³]")),
/* ctor */,
......
......@@ -59,34 +59,34 @@ class Scene: public Serializable{
YADE_CLASS_BASE_DOC_ATTRS_CTOR(Scene,Serializable,"Object comprising the whole simulation.",
#ifdef YADE_GROUP_RELATION_DATA
((shared_ptr<GroupRelationData>,grpRelationData,,Attr::pyHidden,"Assigns float value to all possible combinations of body group that interact."))
((shared_ptr<GroupRelationData>,grpRelationData,,Attr::hidden,"Assigns float value to all possible combinations of body group that interact."))
#endif
((Real,dt,1e-8,,"Current timestep for integration."))
((long,iter,0,Attr::pyReadonly,"Current iteration (computational step) number"))
((long,iter,0,Attr::readonly,"Current iteration (computational step) number"))
((bool,subStepping,false,,"Whether we currently advance by one engine in every step (rather than by single run through all engines)."))
((int,subStep,-1,Attr::pyReadonly,"Number of sub-step; not to be changed directly. -1 means to run loop prologue (cell integration), 0…n-1 runs respective engines (n is number of engines), n runs epilogue (increment step number and time."))
((Real,time,0,Attr::pyReadonly,"Simulation time (virtual time) [s]"))
((int,subStep,-1,Attr::readonly,"Number of sub-step; not to be changed directly. -1 means to run loop prologue (cell integration), 0…n-1 runs respective engines (n is number of engines), n runs epilogue (increment step number and time."))
((Real,time,0,Attr::readonly,"Simulation time (virtual time) [s]"))
((long,stopAtIter,0,,"Iteration after which to stop the simulation."))
#if 0
// not yet implemented
((Real,stopAtTime,0,,"Time at which to stop the simulation"))
((Real,stopAtRealTime,0,,"Time at which to stop the simulation"))
#endif
((bool,isPeriodic,false,Attr::pyReadonly,"Whether periodic boundary conditions are active."))
((bool,needsInitializers,true,Attr::pyReadonly,"Whether initializers will be run before the first step."))
((bool,isPeriodic,false,Attr::readonly,"Whether periodic boundary conditions are active."))
((bool,needsInitializers,true,Attr::readonly,"Whether initializers will be run before the first step."))
((Body::id_t,selectedBody,-1,,"Id of body that is selected by the user"))
((list<string>,tags,,,"Arbitrary key=value associations (tags like mp3 tags: author, date, version, description etc.)"))
((vector<shared_ptr<Engine> >,engines,,Attr::pyHidden,"Engines sequence in the simulation."))
((vector<shared_ptr<Engine> >,initializers,,Attr::pyHidden,"Engines that will be run only once, before the first step."))
((shared_ptr<BodyContainer>,bodies,new BodyContainer,Attr::pyHidden,"Bodies contained in the scene."))
((shared_ptr<InteractionContainer>,interactions,new InteractionContainer,Attr::pyHidden,"All interactions between bodies."))
((vector<shared_ptr<Material> >,materials,,Attr::pyHidden,"Container of shared materials. Add elements using Scene::addMaterial, not directly. Do NOT remove elements from here unless you know what you are doing!"))
((shared_ptr<Bound>,bound,,Attr::pyHidden,"Bounding box of the scene (only used for rendering and initialized if needed)."))
((shared_ptr<Cell>,cell,new Cell,Attr::pyHidden,"Information on periodicity; only should be used if Scene::isPeriodic."))
((vector<shared_ptr<Serializable> >,miscParams,,Attr::pyHidden,"Store for arbitrary Serializable objects; will set static parameters during deserialization (primarily for GLDraw functors which otherwise have no attribute access)"))
((vector<shared_ptr<DisplayParameters> >,dispParams,,Attr::pyHidden,"'hash maps' of display parameters (since yade::serialization had no support for maps, emulate it via vector of strings in format key=value)"))
((vector<shared_ptr<Engine> >,engines,,Attr::hidden,"Engines sequence in the simulation."))
((vector<shared_ptr<Engine> >,initializers,,Attr::hidden,"Engines that will be run only once, before the first step."))
((shared_ptr<BodyContainer>,bodies,new BodyContainer,Attr::hidden,"Bodies contained in the scene."))
((shared_ptr<InteractionContainer>,interactions,new InteractionContainer,Attr::hidden,"All interactions between bodies."))
((vector<shared_ptr<Material> >,materials,,Attr::hidden,"Container of shared materials. Add elements using Scene::addMaterial, not directly. Do NOT remove elements from here unless you know what you are doing!"))
((shared_ptr<Bound>,bound,,Attr::hidden,"Bounding box of the scene (only used for rendering and initialized if needed)."))
((shared_ptr<Cell>,cell,new Cell,Attr::hidden,"Information on periodicity; only should be used if Scene::isPeriodic."))
((vector<shared_ptr<Serializable> >,miscParams,,Attr::hidden,"Store for arbitrary Serializable objects; will set static parameters during deserialization (primarily for GLDraw functors which otherwise have no attribute access)"))
((vector<shared_ptr<DisplayParameters> >,dispParams,,Attr::hidden,"'hash maps' of display parameters (since yade::serialization had no support for maps, emulate it via vector of strings in format key=value)"))
,
/*ctor*/ fillDefaultTags();
......
......@@ -84,6 +84,7 @@ import yade.config
import yade.wrapper
import yade.log
import yade.system
import yade.runtime
# continue option processing
......@@ -109,13 +110,13 @@ yade.runtime.opts=opts
from yade import *
from math import *
def userSession(qt3=False,qt4=False):
def userSession(qt4=False):
# prepare nice namespace for users
import yade
import sys
if len(sys.argv)>0:
arg0=sys.argv[0]
if qt3 or qt4: yade.qt.Controller();
if qt4: yade.qt.Controller();
if sum(bool(arg0.endswith(ext)) for ext in ('.xml','.xml.bz2','.xml.gz','.yade','.yade.gz','.yade.bz2','.bin','.bin.gz','.bin.bz2'))>0:
if len(sys.argv)>1: raise RuntimeError('Extra arguments to saved simulation to run: '+' '.join(sys.argv[1:]))
sys.stderr.write("Running simulation "+arg0+'\n')
......@@ -146,7 +147,7 @@ def userSession(qt3=False,qt4=False):
ipshell=IPShellEmbed(
#exit_msg='Bye.',
banner='[[ ^L clears screen, ^U kills line. '+
', '.join((['F12 controller','F11 3d view','F10 both','F9 generator'] if (qt3 or qt4) else [])+['F8 plot'])+'. ]]',
', '.join((['F12 controller','F11 3d view','F10 both','F9 generator'] if (qt4) else [])+['F8 plot'])+'. ]]',
rc_override=dict( # ipython options, see e.g. http://www.cv.nrao.edu/~rreid/casa/tips/ipy_user_conf.py
prompt_in1='Yade [\#]: ',
prompt_in2=' .\D.: ',
......@@ -160,7 +161,7 @@ def userSession(qt3=False,qt4=False):
# only with the gui
# the escape codes might not work on non-linux terminals.
]
+(['"\e[24~": "\C-Uyade.qt.Controller();\C-M"','"\e[23~": "\C-Uyade.qt.View();\C-M"','"\e[21~": "\C-Uyade.qt.Controller(), yade.qt.View();\C-M"','"\e[20~": "\C-Uyade.qt.Generator();\C-M"'] if (qt3 or qt4) else []) #F12,F11,F10,F9
+(['"\e[24~": "\C-Uyade.qt.Controller();\C-M"','"\e[23~": "\C-Uyade.qt.View();\C-M"','"\e[21~": "\C-Uyade.qt.Controller(), yade.qt.View();\C-M"','"\e[20~": "\C-Uyade.qt.Generator();\C-M"'] if (qt4) else []) #F12,F11,F10,F9
+['"\e[19~": "\C-Uimport yade.plot; yade.plot.plot();\C-M"', #F8
'"\e[A": history-search-backward', '"\e[B": history-search-forward', # incremental history forward/backward
]
......@@ -174,7 +175,7 @@ def userSession(qt3=False,qt4=False):
## run userSession in a way corresponding to the features we use:
gui=None
if 'qt3' in features: gui='qt3'
yade.runtime.hasDisplay=False # this is the default initialized in the module, anyway
if 'qt4' in features: gui='qt4'
if opts.nogui: gui=None
if gui:
......@@ -183,7 +184,9 @@ if gui:
# we however want to handle this gracefully, therefore
# we test the connection with bare xlib first, which merely raises DisplayError
try:
Xlib.display._BaseDisplay(); # contrary to display.Display, _BaseDisplay does not check for extensions and that avoids spurious message "Xlib.protocol.request.QueryExtension" (bug?)
# contrary to display.Display, _BaseDisplay does not check for extensions and that avoids spurious message "Xlib.protocol.request.QueryExtension" (bug?)
Xlib.display._BaseDisplay();
yade.runtime.hasDisplay=True
except Xlib.error.DisplayError:
gui=None
......@@ -194,11 +197,9 @@ yade.remote.runServers()
if gui==None:
userSession()
elif gui=='qt3':
# raises exception if unable to open the display, but that should have been handled above already
import yade.qt
userSession(qt3=True)
elif gui=='qt4':
## we already tested that DISPLAY is available and can be opened
## otherwise Qt4 might crash at this point
import PyQt4
from PyQt4 import QtGui
from PyQt4.QtCore import *
......
......@@ -99,13 +99,19 @@ finished: %s
return ret
def t2hhmmss(dt): return '%02d:%02d:%02d'%(dt//3600,(dt%3600)//60,(dt%60))
def totalRunningTime():
tt0,tt1=[j.started for j in jobs if j.started],[j.finished for j in jobs if j.finished]+[time.time()]
# it is safe to suppose that
if len(tt0)==0: return 0 # no job has been started at all
return max(tt1)-min(tt0)
def globalHtmlStats():
t0=min([j.started for j in jobs if j.started!=None])
unfinished=len([j for j in jobs if j.status!='DONE'])
usedSlots=sum([j.nSlots for j in jobs if j.status=='RUNNING'])
global maxJobs
if unfinished:
ret='<p>Running for %s, since %s.</p>'%(t2hhmmss(time.time()-t0),time.ctime(t0))
ret='<p>Running for %s, since %s.</p>'%(t2hhmmss(totalRunningTime()),time.ctime(t0))
else:
failed=len([j for j in jobs if j.exitStatus!=0])
lastFinished=max([j.finished for j in jobs])
......@@ -171,8 +177,10 @@ class HttpStatsServer(BaseHTTPRequestHandler):
f=open(fileName)
self.sendHttp(f.read(),contentType=contentType,**headers)
def sendHttp(self,data,contentType,**headers):
"Send file over http, using appropriate content-type. Headers are converted to strings. The *refresh* header is handled specially: if the value is 0, it is not sent at all."
self.send_response(200)
self.send_header('Content-type',contentType)
if 'refresh' in headers and headers['refresh']==0: del headers['refresh']
for h in headers: self.send_header(h,str(headers[h]))
self.end_headers()
self.wfile.write(data)
......@@ -357,7 +365,7 @@ if opts.plotAlwaysUpdateTime>0:
# OK, go now
if not dryRun: runJobs(jobs,maxJobs)
print 'All jobs finished, total time ',t2hhmmss(sum([j.finished-j.started for j in jobs if j.started is not None]))
print 'All jobs finished, total time ',t2hhmmss(totalRunningTime())
plots=[]
for j in jobs:
......
......@@ -7,7 +7,7 @@ Standards-Version: 3.7.2
Package: yade@_VERSION@
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, python-numpy, ipython, python-matplotlib, python-tk, python-qt4, python-xlib
Depends: ${shlibs:Depends}, ${misc:Depends}, python-numpy, ipython, python-matplotlib, python-tk, python-qt4, python-xlib, mencoder
Description: Platform for dynamical modeling.
Yet Another Dynamic Engine. etc.
.
......@@ -15,7 +15,7 @@ Description: Platform for dynamical modeling.
Package: yade@_VERSION@-dbg
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, python-numpy, ipython, gdb, python-matplotlib, python-tk, python-qt4, python-xlib
Depends: ${shlibs:Depends}, ${misc:Depends}, python-numpy, ipython, gdb, python-matplotlib, python-tk, python-qt4, python-xlib, mencoder
Description: Platform for dynamical modeling.
Yet Another Dynamic Engine. etc.
.
......
......@@ -328,6 +328,15 @@ Thanks.
doi = {10.1007/3-540-44424-6_3},
}
@article{Thornton1991,
title={Impact of elastic spheres with and without adhesion},
author={Colin Thornton and K. K. Yin},
year={1991},
journal={Powder technology},
volume={65},
pages={153--166},
doi={10.1016/0032-5910(91)80178-L}
}
@article{Thornton2000,
title={Numerical simulations of deviatoric shear deformation of granular media},
......
......@@ -754,12 +754,12 @@ Expected parameters are indicated by macro name components separated with unders
By default, an attribute will be serialized and will be read-write from python. There is a number of flags that can be passed as the 4th argument (empty by default) to change that:
* ``Attr::noSave`` avoids serialization of the attribute (while still keeping its accessibility from Python)
* ``Attr::pyReadonly`` makes the attribute read-only from Python
* ``Attr::pyCallPostLoad`` will call ``postLoad`` function to handle attribute change after its value is set from Python; this is to ensure consistency of other precomputed data which depend on this value (such as ``Cell.trsf`` and such)
* ``Attr::pyHidden`` will not expose the attribute to Python at all
* ``Attr::pyNoResize`` will not permit changing size of the array from Python [not yet used]
* ``Attr::readonly`` makes the attribute read-only from Python
* ``Attr::triggerPostLoad`` will trigger call to ``postLoad`` function to handle attribute change after its value is set from Python; this is to ensure consistency of other precomputed data which depend on this value (such as ``Cell.trsf`` and such)
* ``Attr::hidden`` will not expose the attribute to Python at all
* ``Attr::noResize`` will not permit changing size of the array from Python [not yet used]
Flags can be combined as usual using bitwise disjunction ``|`` (such as ``Attr::noSave | Attr::pyReadonly``), though in such case the value should be parenthesized to avoid a warning with some compilers (g++ specifically), i.e. ``(Attr::noSave | Attr::pyReadonly)``.
Flags can be combined as usual using bitwise disjunction ``|`` (such as ``Attr::noSave | Attr::readonly``), though in such case the value should be parenthesized to avoid a warning with some compilers (g++ specifically), i.e. ``(Attr::noSave | Attr::readonly)``.
Currently, the flags logic handled at runtime; that means that even for attributes with ``Attr::noSave``, their serialization template must be defined (although it will never be used). In the future, the implementation might be template-based, avoiding this necessity.
......
......@@ -91,7 +91,7 @@ O.engines=[
GravityEngine(gravity=(1e-2,1e-2,-1000)),
NewtonIntegrator(damping=.1,exactAsphericalRot=True),
RotationEngine(
subscribedBodies=rotateIDs,
ids=rotateIDs,
angularVelocity=10.0,
rotationAxis=[0,0,1],
rotateAroundZero=1,
......
......@@ -41,24 +41,33 @@ YADE_PLUGIN((SnapshotEngine));
CREATE_LOGGER(SnapshotEngine);
void SnapshotEngine::action(){
shared_ptr<GLViewer> glv;
const int viewNo=0;
if(!OpenGLManager::self || ((size_t)viewNo>=OpenGLManager::self->views.size()) || !(glv=OpenGLManager::self->views[viewNo])){
if(!ignoreErrors) throw invalid_argument("View #"+lexical_cast<string>(viewNo)+" doesn't exist.");
return;
if(!OpenGLManager::self) throw logic_error("No OpenGLManager instance?!");
if(OpenGLManager::self->views.size()==0){
int viewNo=OpenGLManager::self->waitForNewView(deadTimeout);
if(viewNo<0){
if(!ignoreErrors) throw runtime_error("SnapshotEngine: Timeout waiting for new 3d view.");
else {
LOG_WARN("Making myself Engine::dead, as I can not live without a 3d view (timeout)."); dead=true; return;
}
}
}
const shared_ptr<GLViewer>& glv=OpenGLManager::self->views[0];
ostringstream fss; fss<<fileBase<<setw(5)<<setfill('0')<<counter++<<"."<<boost::algorithm::to_lower_copy(format);
LOG_DEBUG("GL view #"<<viewNo<<" → "<<fss.str())
LOG_DEBUG("GL view → "<<fss.str())
glv->setSnapshotFormat(QString(format.c_str()));
glv->nextFrameSnapshotFilename=fss.str();
// wait for the renderer to save the frame (will happen at next postDraw)
timespec t1,t2; t1.tv_sec=0; t1.tv_nsec=10000000; /* 10 ms */
long waiting=0;
while(!glv->nextFrameSnapshotFilename.empty()){
nanosleep(&t1,&t2);
if(((++waiting) % 1000)==0) LOG_WARN("Already waiting "<<waiting/100<<"s for snapshot to be saved. Something went wrong?");
nanosleep(&t1,&t2); waiting++;
if(((waiting) % 1000)==0) LOG_WARN("Already waiting "<<waiting/100<<"s for snapshot to be saved. Something went wrong?");
if(waiting/100.>deadTimeout){
if(ignoreErrors){ LOG_WARN("Timeout waiting for snapshot to be saved, making byself Engine::dead"); dead=true; return; }
else throw runtime_error("SnapshotEngine: Timeout waiting for snapshot to be saved.");
}
}
savedSnapshots.push_back(fss.str());
snapshots.push_back(fss.str());
usleep((long)(msecSleep*1000));
}
......@@ -493,8 +502,11 @@ void GLViewer::draw()
}
void GLViewer::drawWithNames(){
cerr<<"[selection redraw]";
qglviewer::Vec vd=camera()->viewDirection(); renderer->viewDirection=Vector3r(vd[0],vd[1],vd[2]);
if(Omega::instance().getScene() && Omega::instance().getScene()->bodies->size()<renderer->selectBodyLimit) renderer->renderWithNames(Omega::instance().getScene());
const shared_ptr<Scene> scene(Omega::instance().getScene());
//
if(scene && (scene->bodies->size()<renderer->selectBodyLimit)) renderer->renderShape(true);
}
// new object selected.
......
......@@ -26,10 +26,10 @@ class SnapshotEngine: public PeriodicEngine{
((string,format,"PNG",,"Format of snapshots (one of JPEG, PNG, EPS, PS, PPM, BMP) `QGLViewer documentation <http://www.libqglviewer.com/refManual/classQGLViewer.html#abbb1add55632dced395e2f1b78ef491c>`_. File extension will be lowercased *format*. Validity of format is not checked."))
((string,fileBase,"",,"Basename for snapshots"))
((int,counter,0,,"Number that will be appended to fileBase when the next snapshot is saved (incremented at every save). |yupdate|"))
// ((int,viewNo,((void)"primary view",0),"The GLView number that we save."))
((bool,ignoreErrors,true,,"Silently return if selected view doesn't exist"))
((vector<string>,savedSnapshots,,,"Files that have been created so far"))
((bool,ignoreErrors,true,,"Only report errors instead of throwing exceptions, in case of timeouts."))
((vector<string>,snapshots,,,"Files that have been created so far"))
((int,msecSleep,0,,"number of msec to sleep after snapshot (to prevent 3d hw problems) [ms]"))
((Real,deadTimeout,3,,"Timeout for 3d operations (opening new view, saving snapshot); after timing out, throw exception (or only report error if *ignoreErrors*) and make myself :yref:`dead<Engine.dead>`. [s]"))
);
DECLARE_LOGGER;
};
......
......@@ -59,3 +59,18 @@ void OpenGLManager::centerAllViews(){
void OpenGLManager::startTimerSlot(){
startTimer(50);
}
int OpenGLManager::waitForNewView(float timeout,bool center){
size_t origViewCount=views.size();
emitCreateView();
float t=0;
while(views.size()!=origViewCount+1){
usleep(50000); t+=.05;
// wait at most 5 secs
if(t>=timeout) {
LOG_ERROR("Timeout waiting for the new view to open, giving up."); return -1;
}
}
if(center)(*views.rbegin())->centerScene();
return (*views.rbegin())->viewId;
}
......@@ -23,6 +23,9 @@ class OpenGLManager: public QObject{
void emitCreateView(){ emit createView(); }
void emitStartTimer(){ emit startTimerSignal(); }
void emitCloseView(int id){ emit closeView(id); }
// create a new view and wait for it to become available; return the view number
// if timout (in seconds) elapses without the view to come up, reports error and returns -1
int waitForNewView(float timeout=5., bool center=true);
signals:
void createView();
void resizeView(int id, int wd, int ht);
......
......@@ -348,7 +348,7 @@ class SerializableEditor(QFrame):
if Klass:
widget=Klass(self,getter=getter,setter=setter)
widget.setFocusPolicy(Qt.StrongFocus)
if (entry.flags & AttrFlags.pyReadonly): widget.setEnabled(False)
if (entry.flags & AttrFlags.readonly): widget.setEnabled(False)
return widget
# sequences
if entry.T.__class__==tuple:
......
......@@ -74,7 +74,11 @@ class pyGLViewer{
};
// ask to create a new view and wait till it exists
pyGLViewer createView(){ size_t origViewNo=OpenGLManager::self->views.size(); OpenGLManager::self->emitCreateView(); while(OpenGLManager::self->views.size()!=origViewNo+1) usleep(50000); return pyGLViewer((*OpenGLManager::self->views.rbegin())->viewId); }
pyGLViewer createView(){
int id=OpenGLManager::self->waitForNewView();
if(id<0) throw std::runtime_error("Unable to open new 3d view.");
return pyGLViewer((*OpenGLManager::self->views.rbegin())->viewId);
}
py::list getAllViews(){ py::list ret; FOREACH(const shared_ptr<GLViewer>& v, OpenGLManager::self->views){ if(v) ret.append(pyGLViewer(v->viewId)); } return ret; };
void centerViews(void){ OpenGLManager::self->centerAllViews(); }
......
# 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.")
from PyQt4.QtGui import *
from PyQt4 import QtCore
......
......@@ -49,7 +49,7 @@ using namespace std;
namespace yade{
namespace Attr{
// keep in sync with py/wrapper/yadeWrapper.cpp !
enum flags { noSave=1, pyReadonly=2, pyCallPostLoad=4, pyHidden=8, pyNoResize=16, };
enum flags { noSave=1, readonly=2, triggerPostLoad=4, hidden=8, noResize=16, };
};
};
using namespace yade;
......@@ -103,14 +103,14 @@ void make_setter_postLoad(C& instance, const T& val){ instance.*A=val; /* cerr<<
#define _DEF_READWRITE_BY_VALUE_STATIC(thisClass,attr,doc) _DEF_READWRITE_BY_VALUE(thisClass,attr,doc)
// the conditional yade::py_wrap_ref should be eliminated by compiler at compile-time, as it depends only on types, not their values
// most of this could be written with templates, including flags (ints can be template args)
#define _DEF_READWRITE_CUSTOM(thisClass,attr) if(!(_ATTR_FLG(attr) & yade::Attr::pyHidden)){ bool _ro(_ATTR_FLG(attr) & Attr::pyReadonly), _post(_ATTR_FLG(attr) & Attr::pyCallPostLoad), _ref(yade::py_wrap_ref<typeof(thisClass::_ATTR_NAM(attr))>::value); std::string docStr(_ATTR_DOC(attr)); docStr+=" :yattrflags:`"+boost::lexical_cast<string>(_ATTR_FLG(attr))+"` "; \
#define _DEF_READWRITE_CUSTOM(thisClass,attr) if(!(_ATTR_FLG(attr) & yade::Attr::hidden)){ bool _ro(_ATTR_FLG(attr) & Attr::readonly), _post(_ATTR_FLG(attr) & Attr::triggerPostLoad), _ref(yade::py_wrap_ref<typeof(thisClass::_ATTR_NAM(attr))>::value); std::string docStr(_ATTR_DOC(attr)); docStr+=" :yattrflags:`"+boost::lexical_cast<string>(_ATTR_FLG(attr))+"` "; \
if ( _ref && !_ro && !_post) _classObj.def_readwrite(_ATTR_NAM_STR(attr),&thisClass::_ATTR_NAM(attr),docStr.c_str()); \
else if ( _ref && !_ro && _post) _classObj.add_property(_ATTR_NAM_STR(attr),boost::python::make_getter(&thisClass::_ATTR_NAM(attr)),make_setter_postLoad<thisClass,typeof(thisClass::_ATTR_NAM(attr)),&thisClass::_ATTR_NAM(attr)>,docStr.c_str()); \
else if ( _ref && _ro) _classObj.def_readonly(_ATTR_NAM_STR(attr),&thisClass::_ATTR_NAM(attr),docStr.c_str()); \
else if (!_ref && !_ro && !_post) _classObj._DEF_READWRITE_BY_VALUE(thisClass,_ATTR_NAM(attr),docStr.c_str()); \
else if (!_ref && !_ro && _post) _classObj._DEF_READWRITE_BY_VALUE_POSTLOAD(thisClass,_ATTR_NAM(attr),docStr.c_str()); \
else if (!_ref && _ro) _classObj._DEF_READONLY_BY_VALUE(thisClass,_ATTR_NAM(attr),docStr.c_str()); \
if(_ro && _post) std::cerr<<"WARN: " BOOST_PP_STRINGIZE(thisClass) "::" _ATTR_NAM_STR(attr) " with the yade::Attr::pyReadonly flag also uselessly sets yade::Attr::pyCallPostLoad."<<std::endl; \
if(_ro && _post) std::cerr<<"WARN: " BOOST_PP_STRINGIZE(thisClass) "::" _ATTR_NAM_STR(attr) " with the yade::Attr::readonly flag also uselessly sets yade::Attr::triggerPostLoad."<<std::endl; \
}
#define _DEF_READWRITE_CUSTOM_STATIC(thisClass,attr,doc) { /* if(yade::py_wrap_ref<typeof(thisClass::attr)>::value)*/ _classObj.def_readwrite(BOOST_PP_STRINGIZE(attr),&thisClass::attr,doc); /* else _classObj._DEF_READWRITE_BY_VALUE_STATIC(thisClass,attr,doc);*/ }
......
......@@ -36,7 +36,7 @@ class Facet : public Shape {
void postLoad(Facet&);
YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(Facet,Shape,"Facet (triangular particle) geometry.",
((vector<Vector3r>,vertices,vector<Vector3r>(3),(Attr::pyCallPostLoad | Attr::pyNoResize),"Vertex positions in local coordinates."))
((vector<Vector3r>,vertices,vector<Vector3r>(3),(Attr::triggerPostLoad | Attr::noResize),"Vertex positions in local coordinates."))
#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]"))
((vector<Real>,edgeAdjHalfAngle,vector<Real>(3,0),,"half angle between normals of this facet and the adjacent facet [experimental]"))
......
......@@ -19,9 +19,9 @@ class InteractionDispatchers: public GlobalEngine {
virtual void pyHandleCustomCtorArgs(python::tuple& t, python::dict& d);
virtual void action();
YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(InteractionDispatchers,GlobalEngine,"Unified dispatcher for handling interaction loop at every step, for parallel performance reasons.\n\n.. admonition:: Special constructor\n\n\tConstructs from 3 lists of :yref:`Ig2<InteractionGeometryFunctor>`, :yref:`Ip2<InteractionPhysicsFunctor>`, :yref:`Law<LawFunctor>` functors respectively; they will be passed to interal dispatchers, which you might retrieve. (NOT YET DONE: Optionally, list of :yref:`IntrCallbacks<IntrCallback>` can be provided as fourth argument.)",
((shared_ptr<InteractionGeometryDispatcher>,geomDispatcher,new InteractionGeometryDispatcher,Attr::pyReadonly,":yref:`InteractionGeometryDispatcher` object that is used for dispatch."))
((shared_ptr<InteractionPhysicsDispatcher>,physDispatcher,new InteractionPhysicsDispatcher,Attr::pyReadonly,":yref:`InteractionPhysicsDispatcher` object used for dispatch."))
((shared_ptr<LawDispatcher>,lawDispatcher,new LawDispatcher,Attr::pyReadonly,":yref:`LawDispatcher` object used for dispatch."))
((shared_ptr<InteractionGeometryDispatcher>,geomDispatcher,new InteractionGeometryDispatcher,Attr::readonly,":yref:`InteractionGeometryDispatcher` object that is used for dispatch."))
((shared_ptr<InteractionPhysicsDispatcher>,physDispatcher,new InteractionPhysicsDispatcher,Attr::readonly,":yref:`InteractionPhysicsDispatcher` object used for dispatch."))
((shared_ptr<LawDispatcher>,lawDispatcher,new LawDispatcher,Attr::readonly,":yref:`LawDispatcher` object used for dispatch."))
((vector<shared_ptr<IntrCallback> >,callbacks,,,":yref:`Callbacks<IntrCallback>` which will be called for every :yref:`Interaction`, if activated."))
,
/*ctor*/ alreadyWarnedNoCollider=false;
......
......@@ -38,7 +38,7 @@ class Collider: public GlobalEngine {
YADE_CLASS_BASE_DOC_ATTRS(Collider,GlobalEngine,"Abstract class for finding spatial collisions between bodies. \n\n.. admonition:: Special constructor\n\n\tDerived colliders (unless they override ``pyHandleCustomCtorArgs``) can be given list of :yref:`BoundFunctors <BoundFunctor>` which is used to initialize the internal :yref:`boundDispatcher <Collider.boundDispatcher>` instance.",
((shared_ptr<BoundDispatcher>,boundDispatcher,new BoundDispatcher,Attr::pyReadonly,":yref:`BoundDispatcher` object that is used for creating :yref:`bounds <Body.bound>` on collider's request as necessary."))
((shared_ptr<BoundDispatcher>,boundDispatcher,new BoundDispatcher,Attr::readonly,":yref:`BoundDispatcher` object that is used for creating :yref:`bounds <Body.bound>` on collider's request as necessary."))
);
};
REGISTER_SERIALIZABLE(Collider);
......
......@@ -207,7 +207,7 @@ class InsertionSortCollider: public Collider{
((Real,binOverlap,0.8,,"Relative bins hysteresis, to avoid moving body back and forth if its velocity is around the border value. (Passed to VelocityBins)"))
((Real,maxRefRelStep,.3,,"(Passed to VelocityBins)"))
((int,histInterval,100,,"How often to show velocity bins graphically, if debug logging is enabled for VelocityBins."))
((int,numReinit,0,Attr::pyReadonly,"Cummulative number of bound array re-initialization."))
((int,numReinit,0,Attr::readonly,"Cummulative number of bound array re-initialization."))
,
/* ctor */
#ifdef ISC_TIMING
......
......@@ -20,32 +20,44 @@ void Gl1_NormPhys::go(const shared_ptr<InteractionPhysics>& ip, const shared_ptr
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<InteractionGeometry> ig(i->interactionGeometry); if(!ig) return; // changed meanwhile?
GenericSpheresContact* gsc=YADE_CAST<GenericSpheresContact*>(ig.get());
//if(!gsc) cerr<<"Gl1_NormPhys: InteractionGeometry is not a GenericSpheresContact, but a "<<ig->getClassName()<<endl;
Real fnNorm=np->normalForce.dot(gsc->normal);
GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(ig.get());
//if(!geom) cerr<<"Gl1_NormPhys: InteractionGeometry 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);
maxFn=max(fnNorm,maxFn);
Real realMaxRadius;
if(maxRadius<0){
if(gsc->refR1>0) refRadius=min(gsc->refR1,refRadius);
if(gsc->refR2>0) refRadius=min(gsc->refR2,refRadius);
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=realMaxRadius*(fnNorm/maxFn); // use logarithmic scale here?
Vector3r color=Shop::scalarOnColorScale(fnNorm*fnSign,-maxFn,maxFn);
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();
# 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;