yade-dev team mailing list archive
-
yade-dev team
-
Mailing list archive
-
Message #02221
[Branch ~yade-dev/yade/trunk] Rev 1783: 1. New yade.post2d module for 2d post-processing (raw/smooth scalar/vector field plots), with doc...
------------------------------------------------------------
revno: 1783
committer: Václav Šmilauer <vaclav@flux>
branch nick: trunk
timestamp: Fri 2009-11-13 12:27:22 +0100
message:
1. New yade.post2d module for 2d post-processing (raw/smooth scalar/vector field plots), with docs and examples
2. Add epydoc brief documentation to all python modules
3. Change constructor of GaussAverage in python to take always the relThreshold parameter
added:
examples/concrete/uniax-post.py
py/post2d.py
modified:
examples/concrete/uniax.py
gui/py/PythonTCPServer.py
gui/py/ipython.py
gui/py/runtime.py
gui/qt3/qt.py
lib/smoothing/WeightedAverage2d.hpp
py/SConscript
py/WeightedAverage2d.cpp
py/_packObb.cpp
py/_packPredicates.cpp
py/_packSpheres.cpp
py/eudoxos.py
py/log.cpp
py/pack.py
py/tests/__init__.py
py/tests/wrapper.py
py/timing.py
py/utils.py
py/yadeWrapper/yadeWrapper.cpp
--
lp:yade
https://code.launchpad.net/~yade-dev/yade/trunk
Your team Yade developers is subscribed to branch lp:yade.
To unsubscribe from this branch go to https://code.launchpad.net/~yade-dev/yade/trunk/+edit-subscription.
=== added file 'examples/concrete/uniax-post.py'
--- examples/concrete/uniax-post.py 1970-01-01 00:00:00 +0000
+++ examples/concrete/uniax-post.py 2009-11-13 11:27:22 +0000
@@ -0,0 +1,38 @@
+#
+# demonstration of the yade.post2d module (see its documentation for details)
+#
+from yade import post2d
+import pylab # the matlab-like interface of matplotlib
+
+# run uniax.py to get this file
+O.load('/tmp/uniax-tension.xml.bz2')
+
+# flattener that project to the xz plane
+flattener=post2d.AxisFlatten(useRef=False,axis=1)
+# return scalar given a Body instance
+extractDmg=lambda b: b.phys['normDmg']
+# will call flattener.planar implicitly
+# the same as: extractVelocity=lambda b: flattener.planar(b,b.phys['velocity'])
+extractVelocity=lambda b: b.phys['velocity']
+
+# create new figure
+pylab.figure()
+# plot raw damage
+post2d.plot(post2d.data(extractDmg,flattener))
+
+# plot smooth damage into new figure
+pylab.figure(); ax,map=post2d.plot(post2d.data(extractDmg,flattener,stDev=2e-3))
+
+# show color scale
+pylab.colorbar(map,orientation='horizontal')
+
+# raw velocity (vector field) plot
+pylab.figure(); post2d.plot(post2d.data(extractVelocity,flattener))
+
+# smooth velocity plot; data are sampled at regular grid
+pylab.figure(); ax,map=post2d.plot(post2d.data(extractVelocity,flattener,stDev=1e-3))
+# save last (current) figure to file
+pylab.gcf().savefig('/tmp/foo.png')
+
+# show the figures
+pylab.show()
=== modified file 'examples/concrete/uniax.py'
--- examples/concrete/uniax.py 2009-11-04 21:54:10 +0000
+++ examples/concrete/uniax.py 2009-11-13 11:27:22 +0000
@@ -136,6 +136,7 @@
if abs(sigma[-1]/extremum)<minMaxRatio or abs(strainer['strain'])>5e-3:
if mode=='tension' and doModes & 2: # only if compression is enabled
mode='compression'
+ O.save('/tmp/uniax-tension.xml.bz2')
print "Damaged, switching to compression... "; O.pause()
# important! initTest must be launched in a separate thread;
# otherwise O.load would wait for the iteration to finish,
=== modified file 'gui/py/PythonTCPServer.py'
--- gui/py/PythonTCPServer.py 2009-10-22 19:32:22 +0000
+++ gui/py/PythonTCPServer.py 2009-11-13 11:27:22 +0000
@@ -1,3 +1,11 @@
+# encoding: utf-8
+# 2008-2009 © Václav Šmilauer <eudoxos@xxxxxxxx>
+"""
+Remote connections to yade: authenticated python command-line over telnet and anonymous socket for getting some read-only information about current simulation.
+
+These classes are used internally in gui/py/PythonUI_rc.py and are not intended for direct use.
+"""
+
import SocketServer
import sys,time
@@ -5,7 +13,7 @@
class InfoSocketProvider(SocketServer.BaseRequestHandler):
"""Class providing dictionary of important simulation information,
- without authenticating the access"""
+ without authenticating the access."""
def handle(self):
import pickle, os
O=Omega()
@@ -14,6 +22,11 @@
class PythonConsoleSocketEmulator(SocketServer.BaseRequestHandler):
+ """Class emulating python command-line over a socket connection.
+
+ The connection is authenticated by requiring a cookie.
+ Only connections from localhost (127.0.0.*) are allowed.
+ """
def setup(self):
if not self.client_address[0].startswith('127.0.0'):
print "TCP Connection from non-127.0.0.* address %s rejected"%self.client_address[0]
@@ -72,6 +85,7 @@
self.request.send('\nBye ' + str(self.client_address) + '\n')
class GenericTCPServer:
+ "Base class for socket server, handling port allocation, initial logging and thead backgrounding."
def __init__(self,handler,title,cookie=True,minPort=9000,host='',maxPort=65536,background=True):
import socket, random
self.port=-1
=== modified file 'gui/py/ipython.py'
--- gui/py/ipython.py 2008-09-21 10:47:46 +0000
+++ gui/py/ipython.py 2009-11-13 11:27:22 +0000
@@ -4,6 +4,8 @@
#
# Therefore, it is execfile'd at ipython startup
#
+"""Yade-specific command-line completion hooks for in ipython (experimental)"""
+
def yade_completers(self, event):
# this should parse the incomplete line and return only the object that pertains to this ['
# it will work fine in cases like b['clum<TAB> if b is a Body, but not in b['id']+b2['id
=== modified file 'gui/py/runtime.py'
--- gui/py/runtime.py 2008-05-01 06:29:44 +0000
+++ gui/py/runtime.py 2009-11-13 11:27:22 +0000
@@ -1,1 +1,2 @@
# this module is populated at initialization from the c++ part of PythonUI
+"""Runtime variables, populated at yade startup."""
=== modified file 'gui/qt3/qt.py'
--- gui/qt3/qt.py 2009-08-05 09:09:52 +0000
+++ gui/qt3/qt.py 2009-11-13 11:27:22 +0000
@@ -1,3 +1,7 @@
+# encoding: utf-8
+# 2008 © Václav Šmilauer <eudoxos@xxxxxxxx>
+"""Access/manipulation of the qt3-based yade gui."""
+
# import module parts in c++
from _qt import *
=== modified file 'lib/smoothing/WeightedAverage2d.hpp'
--- lib/smoothing/WeightedAverage2d.hpp 2009-10-11 11:22:25 +0000
+++ lib/smoothing/WeightedAverage2d.hpp 2009-11-13 11:27:22 +0000
@@ -52,7 +52,7 @@
const Vector2i& getSize() const{ return nCells;}
// add new element to the right cell
- void add(const T& t, Vector2r xy){Vector2i cxy=xy2cell(xy); grid[cxy[0]][cxy[1]].push_back(t);}
+ void add(const T& t, Vector2r xy){bool inGrid; Vector2i cxy=xy2cell(xy,&inGrid); if(!inGrid){ if(cxy[0]<0) cxy[0]=0; if(cxy[0]>=nCells[0]) cxy[0]=nCells[0]-1; if(cxy[1]<0) cxy[1]=0; if(cxy[1]>=nCells[1]) cxy[1]=nCells[1]-1; } grid[cxy[0]][cxy[1]].push_back(t);}
/* Filters: return list of cells, based on some spatial criterion: rectangle, ellipse, circle (add more if you need that) */
vector<Vector2i> rectangleFilter(Vector2r bbLo, Vector2r bbHi) const {
@@ -161,9 +161,10 @@
struct Poly2d{vector<Vector2r> vertices; bool inclusive;};
vector<Poly2d> clips;
public:
- pyGaussAverage(python::tuple lo, python::tuple hi, python::tuple nCells, Real stDev){
+ pyGaussAverage(python::tuple lo, python::tuple hi, python::tuple nCells, Real stDev, Real relThreshold=3.){
shared_ptr<GridContainer<Scalar2d> > g(new GridContainer<Scalar2d>(tuple2vec2r(lo),tuple2vec2r(hi),tuple2vec2i(nCells)));
sgda=shared_ptr<SGDA_Scalar2d>(new SGDA_Scalar2d(g,stDev));
+ sgda->relThreshold=relThreshold;
}
bool pointInsidePolygon(const Vector2r&,const vector<Vector2r>&);
bool ptIsClipped(const Vector2r& pt){
=== modified file 'py/SConscript'
--- py/SConscript 2009-10-04 14:37:41 +0000
+++ py/SConscript 2009-11-13 11:27:22 +0000
@@ -28,6 +28,7 @@
env.File('timing.py'),
env.File('pack.py'),
env.File('export.py'),
+ env.File('post2d.py'),
env.SharedLibrary('wrapper',['yadeWrapper/yadeWrapper.cpp'],SHLIBPREFIX='',LIBS=linkPlugins(['Shop','BoundingVolumeMetaEngine','GeometricalModelMetaEngine','InteractingGeometryMetaEngine','InteractionGeometryMetaEngine','InteractionPhysicsMetaEngine','PhysicalParametersMetaEngine','ConstitutiveLawDispatcher','InteractionDispatchers','ParallelEngine','Clump','STLImporter',])
),
env.SharedLibrary('_customConverters',['yadeWrapper/customConverters.cpp'],SHLIBPREFIX='',LIBS=env['LIBS']+linkPlugins(Split("BoundingVolumeEngineUnit GeometricalModelEngineUnit InteractingGeometryEngineUnit InteractionGeometryEngineUnit InteractionPhysicsEngineUnit PhysicalParametersEngineUnit PhysicalActionDamperUnit PhysicalActionApplierUnit ConstitutiveLaw")))
=== modified file 'py/WeightedAverage2d.cpp'
--- py/WeightedAverage2d.cpp 2009-10-11 11:22:25 +0000
+++ py/WeightedAverage2d.cpp 2009-11-13 11:27:22 +0000
@@ -16,7 +16,8 @@
BOOST_PYTHON_MODULE(WeightedAverage2d)
{
- boost::python::class_<pyGaussAverage>("GaussAverage",python::init<python::tuple,python::tuple,python::tuple,Real>())
+ boost::python::scope().attr("__doc__")="Smoothing (2d gauss-weighted average) for postprocessing scalars in 2d.";
+ boost::python::class_<pyGaussAverage>("GaussAverage",python::init<python::tuple,python::tuple,python::tuple,Real,Real>(python::args("min","max","nCells","stDev","relThreshold"),"Create empty container for data, which can be added using add and later retrieved using avg."))
.def("add",&pyGaussAverage::addPt)
.def("avg",&pyGaussAverage::avg)
.add_property("stDev",&pyGaussAverage::stDev_get,&pyGaussAverage::stDev_set)
=== modified file 'py/_packObb.cpp'
--- py/_packObb.cpp 2009-07-15 15:10:16 +0000
+++ py/_packObb.cpp 2009-11-13 11:27:22 +0000
@@ -71,6 +71,7 @@
}
BOOST_PYTHON_MODULE(_packObb){
+ python::scope().attr("__doc__")="Computation of oriented bounding box for cloud of points.";
python::def("cloudBestFitOBB",bestFitOBB_py,"Return (Vector3 center, Vector3 halfSize, Quaternion orientation) of\nbest-fit oriented bounding-box for given tuple of points\n(uses brute-force velome minimization, do not use for very large clouds).");
};
=== modified file 'py/_packPredicates.cpp'
--- py/_packPredicates.cpp 2009-08-20 09:38:22 +0000
+++ py/_packPredicates.cpp 2009-11-13 11:27:22 +0000
@@ -322,7 +322,7 @@
#endif
BOOST_PYTHON_MODULE(_packPredicates){
-
+ python::scope().attr("__doc__")="Spatial predicates for volumes (defined analytically or by triangulation).";
// base predicate class
python::class_<PredicateWrap,/* necessary, as methods are pure virtual*/ boost::noncopyable>("Predicate")
.def("__call__",python::pure_virtual(&Predicate::operator()))
=== modified file 'py/_packSpheres.cpp'
--- py/_packSpheres.cpp 2009-08-22 15:05:20 +0000
+++ py/_packSpheres.cpp 2009-11-13 11:27:22 +0000
@@ -3,6 +3,7 @@
#include<yade/pkg-dem/SpherePack.hpp>
BOOST_PYTHON_MODULE(_packSpheres){
+ python::scope().attr("__doc__")="Creation, manipulation, IO for generic sphere packings.";
python::class_<SpherePack>("SpherePack","Set of spheres as centers and radii",python::init<python::optional<python::list> >(python::args("list"),"Empty constructor, optionally taking list [ ((cx,cy,cz),r), ⦠] for initial data." ))
.def("add",&SpherePack::add,"Add single sphere to packing, given center as 3-tuple and radius")
.def("toList",&SpherePack::toList,"Return packing data as python list.")
=== modified file 'py/eudoxos.py'
--- py/eudoxos.py 2009-07-07 10:54:14 +0000
+++ py/eudoxos.py 2009-11-13 11:27:22 +0000
@@ -3,6 +3,12 @@
#
# 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
=== modified file 'py/log.cpp'
--- py/log.cpp 2009-08-20 09:38:22 +0000
+++ py/log.cpp 2009-11-13 11:27:22 +0000
@@ -45,6 +45,7 @@
#endif
BOOST_PYTHON_MODULE(log){
+ python::scope().attr("__doc__") = "Acess and manipulation of log4cxx loggers.";
python::def("setLevel",logSetLevel,"Set minimum severity level (constants TRACE,DEBUG,INFO,WARN,ERROR,FATAL) for given logger\nleading 'yade.' will be appended automatically to the logger name; if logger is '', the root logger 'yade' will be operated on.");
python::scope().attr("TRACE")=(int)ll_TRACE;
python::scope().attr("DEBUG")=(int)ll_DEBUG;
=== modified file 'py/pack.py'
--- py/pack.py 2009-09-19 19:41:52 +0000
+++ py/pack.py 2009-11-13 11:27:22 +0000
@@ -1,5 +1,18 @@
# encoding: utf-8
-#
+# 2009 © Václav Šmilauer <eudoxos@xxxxxxxx>
+"""
+Creating packings and filling volumes defined by boundary representation or constructive solid geometry.
+
+For examples, see
+ - scripts/test/gts-horse.py
+ - scripts/test/gts-operators.py
+ - scripts/test/gts-random-pack-obb.py
+ - scripts/test/gts-random-pack.py
+ - scripts/test/pack-cloud.py
+ - scripts/test/pack-predicates.py
+ - scripts/test/regular-sphere-pack.py
+"""
+
import itertools,warnings
from numpy import arange
from math import sqrt
=== added file 'py/post2d.py'
--- py/post2d.py 1970-01-01 00:00:00 +0000
+++ py/post2d.py 2009-11-13 11:27:22 +0000
@@ -0,0 +1,254 @@
+# encoding: utf-8
+# 2009 © Václav Šmilauer <eudoxos@xxxxxxxx>
+"""
+Module for 2d postprocessing, containing classes to project points from 3d to 2d in various ways,
+providing basic but flexible framework for extracting arbitrary scalar values from bodies and plotting the
+results. There are 2 basic components: flatteners and extractors.
+
+Flatteners
+==========
+Instance of classes that convert 3d (model) coordinates to 2d (plot) coordinates. Their interface is
+defined by the Flatten class (__call__, planar, normal).
+
+Extractors
+==========
+Callable objects returning scalar or vector value, given a body object. If a 3d vector is returned,
+Flattener.planar is called, which should return only in-plane components of the vector.
+
+Example
+=======
+This example can be found in examples/concrete/uniax-post.py ::
+
+ from yade import post2d
+ import pylab # the matlab-like interface of matplotlib
+
+ O.load('/tmp/uniax-tension.xml.bz2')
+
+ # flattener that project to the xz plane
+ flattener=post2d.AxisFlatten(useRef=False,axis=1)
+ # return scalar given a Body instance
+ extractDmg=lambda b: b.phys['normDmg']
+ # will call flattener.planar implicitly
+ # the same as: extractVelocity=lambda b: flattener.planar(b,b.phys['velocity'])
+ extractVelocity=lambda b: b.phys['velocity']
+
+ # create new figure
+ pylab.figure()
+ # plot raw damage
+ post2d.plot(post2d.data(extractDmg,flattener))
+
+ # plot smooth damage into new figure
+ pylab.figure(); ax,map=post2d.plot(post2d.data(extractDmg,flattener,stDev=2e-3))
+ # show color scale
+ pylab.colorbar(map,orientation='horizontal')
+
+ # raw velocity (vector field) plot
+ pylab.figure(); post2d.plot(post2d.data(extractVelocity,flattener))
+
+ # smooth velocity plot; data are sampled at regular grid
+ pylab.figure(); ax,map=post2d.plot(post2d.data(extractVelocity,flattener,stDev=1e-3))
+ # save last (current) figure to file
+ pylab.gcf().savefig('/tmp/foo.png')
+
+ # show the figures
+ pylab.show()
+
+"""
+
+class Flatten:
+ """Abstract class for converting 3d point into 2d. Used by post2d.data2d."""
+ def __init__(self): pass
+ def __call__(self,b):
+ """Given a Body instance, should return either 2d coordinates as a 2-tuple, or None if the Body should be discarded."""
+ pass
+ def planar(self,pos,vec):
+ "Given position and vector value, project the vector value to the flat plane and return its 2 in-plane components."
+ def normal(self,pos,vec):
+ "Given position and vector value, return lenght of the vector normal to the flat plane."
+
+class SpiralFlatten(Flatten):
+ """Class converting 3d point to 2d based on projection from spiral.
+ The y-axis in the projection corresponds to the rotation axis"""
+ def __init__(self,useRef,thetaRange,dH_dTheta,axis=2,periodStart=0):
+ """@param useRef: use reference positions rather than actual positions
+ @param thetaRange: (thetaMin,thetaMax) tuple; bodies outside this range will be discarded
+ @param dH_dTheta: inclination of the spiral (per radian)
+ @param axis: axis of rotation of the spiral
+ @param periodStart: height of the spiral for zero angle
+ """
+ self.useRef,self.thetaRange,self.dH_dTheta,self.axis,self.periodStart=useRef,thetaRange,dH_dTheta,axis,periodStart
+ self.ax1,self.ax2=(axis+1)%3,(axis+2)%3
+ def _getPos(self,b):
+ return b.phys.refPos if self.useRef else b.phys.pos
+ def __call__(self,b):
+ import yade.utils
+ xy,theta=yade.utils.spiralProject(_getPos(b),self.dH_dTheta,self.axis,self.periodStart)
+ if theta<thetaRange[0] or theta>thetaRange[1]: return None
+ return xy
+ def planar(self,b,vec):
+ from math import sqrt
+ pos=_getPos(b)
+ pos[self.axis]=0; pos.Normalize()
+ return pos.Dot(vec),vec[axis]
+ def normal(self,pos,vec):
+ ax=Vector3(0,0,0); ax[axis]=1; pos=_getPos(b)
+ circum=ax.Cross(pos); circum.Normalize()
+ return circum.Dot(vec)
+
+
+class CylinderFlatten(Flatten):
+ """Class for converting 3d point to 2d based on projection from circle.
+ The y-axis in the projection corresponds to the rotation axis; the x-axis is distance form the axis.
+ """
+ def __init__(self,useRef,axis=2):
+ """@param useRef: use reference positions rather than actual positions.
+ @param axis of the cylinder (0, 1 or 2)
+ """
+ if axis not in (0,1,2): raise IndexError("axis must be one of 0,1,2 (not %d)"%axis)
+ self.useRef,self.axis=useRef,axis
+ def _getPos(self,b):
+ return b.phys.refPos if self.useRef else b.phys.pos
+ def __call__(self,b):
+ p=_getPos(b)
+ pp=(p[(self.axis+1)%3],p[(self.axis+2)%3])
+ import math.sqrt
+ return math.sqrt(pp[0]**2+pp[2]**2),p[self.axis]
+ def planar(self,b,vec):
+ pos=_getPos(b)
+ from math import sqrt
+ pos[self.axis]=0; pos.Normalize()
+ return pos.Dot(vec),vec[axis]
+ def normal(self,b,vec):
+ pos=_getPos(b)
+ ax=Vector3(0,0,0); ax[axis]=1
+ circum=ax.Cross(pos); circum.Normalize()
+ return circum.Dot(vec)
+
+
+class AxisFlatten(Flatten):
+ def __init__(self,useRef,axis=2):
+ """@param useRef: use reference positions rather than actual positions.
+ @param axis: axis normal to the plane (0, 1 or 2); the return value will be simply position with this component dropped."""
+ if axis not in (0,1,2): raise IndexError("axis must be one of 0,1,2 (not %d)"%axis)
+ self.useRef,self.axis=useRef,axis
+ self.ax1,self.ax2=(self.axis+1)%3,(self.axis+2)%3
+ def __call__(self,b):
+ p=b.phys.refPos if self.useRef else b.phys.pos
+ return (p[self.ax1],p[self.ax2])
+ def planar(self,pos,vec):
+ return vec[self.ax1],vec[self.ax2]
+ def normal(self,pos,vec):
+ return vec[self.axis]
+
+def data(extractor,flattener,onlyDynamic=True,stDev=None,relThreshold=3.,div=(50,50),margin=(0,0)):
+ """Filter all bodies (spheres only), project them to 2d and extract required scalar value;
+ return either discrete array of positions and values, or smoothed data, depending on whether the stDev
+ value is specified.
+
+ @param extractor: callable object that receives Body instance; it should return scalar, a 2-tuple (vector fields) or None (to skip that body)
+ @param flattener: callable object that receives Body instance and returns its 2d coordinates or None (to skip that body)
+ @param onlyDynamic: skip all non-dynamic bodies
+ @param stDev: standard deviation for averaging, enables smoothing; None (default) means raw mode.
+ @param relThreshold: threshold for the gaussian weight function relative to stDev (smooth mode only)
+ @param div: 2-tuple specifying number of cells for the gaussian grid (smooth mode only)
+ @param margin: 2-tuple specifying margin around bounding box for data (smooth mode only)
+ @return: Dictionary always containing keys 'type' (one of 'rawScalar','rawVector','smoothScalar','smoothVector', depending on value of smooth and on return value from extractor), 'x', 'y', 'bbox'.
+
+ Raw data further contains 'radii'.
+
+ Scalar fields contain 'val' (value from extractor), vector fields have 'valX' and 'valY' (2 components returned by the extractor).
+ """
+ from miniWm3Wrap import Vector3
+ xx,yy,dd1,dd2,rr=[],[],[],[],[]
+ nDim=0
+ for b in O.bodies:
+ if onlyDynamic and not b.dynamic: continue
+ if b.shape.name!='Sphere': continue
+ xy,d=flattener(b),extractor(b)
+ if xy==None or d==None: continue
+ if nDim==0: nDim=1 if isinstance(d,float) else 2
+ if nDim==1: dd1.append(d);
+ elif len(d)==2:
+ dd1.append(d[0]); dd2.append(d[1])
+ elif len(d)==3:
+ d1,d2=flattener.planar(b,Vector3(d))
+ dd1.append(d1); dd2.append(d2)
+ else:
+ raise RuntimeError("Extractor must return float or 2 or 3 (not %d) floats"%nDim)
+ xx.append(xy[0]); yy.append(xy[1]); rr.append(b.shape['radius'])
+ if stDev==None:
+ bbox=(min(xx),min(yy)),(max(xx),max(yy))
+ if nDim==1: return {'type':'rawScalar','x':xx,'y':yy,'val':dd1,'radii':rr,'bbox':bbox}
+ else: return {'type':'rawVector','x':xx,'y':yy,'valX':dd1,'valY':dd2,'radii':rr,'bbox':bbox}
+
+ from yade.WeightedAverage2d import GaussAverage
+ import numpy
+ lo,hi=(min(xx),min(yy)),(max(xx),max(yy))
+ llo=lo[0]-margin[0],lo[1]-margin[1]; hhi=hi[0]+margin[0],hi[1]+margin[1]
+ ga=GaussAverage(llo,hhi,div,stDev,relThreshold)
+ ga2=GaussAverage(llo,hhi,div,stDev,relThreshold)
+ for i in range(0,len(xx)):
+ ga.add(dd1[i],(xx[i],yy[i]))
+ if nDim>1: ga2.add(dd2[i],(xx[i],yy[i]))
+ step=[(hhi[i]-llo[i])/float(div[i]) for i in [0,1]]
+ xxx,yyy=[numpy.arange(llo[i]+.5*step[i],hhi[i],step[i]) for i in [0,1]]
+ ddd=numpy.zeros((len(yyy),len(xxx)),float)
+ ddd2=numpy.zeros((len(yyy),len(xxx)),float)
+ for cx in range(0,div[0]):
+ for cy in range(0,div[1]):
+ ddd[cy,cx]=float(ga.avg((xxx[cx],yyy[cy])))
+ if nDim>1: ddd2[cy,cx]=float(ga2.avg((xxx[cx],yyy[cy])))
+ if nDim==1: return {'type':'smoothScalar','x':xxx,'y':yyy,'val':ddd,'bbox':(llo,hhi)}
+ else: return {'type':'smoothVector','x':xxx,'y':yyy,'valX':ddd,'valY':ddd2,'bbox':(llo,hhi)}
+
+def plot(data,axes=None,alpha=.5,clabel=True,**kw):
+ """Given output from post2d.data, plot the scalar as discrete or smooth plot.
+
+ For raw discrete data, plot filled circles with radii of particles, colored by the scalar value.
+
+ For smooth discrete data, plot image with optional contours and contour labels.
+
+ For vector data (raw or smooth), plot quiver (vector field), with arrows colored by the magnitude.
+
+ @param axes: matplotlib.axes instance to plot to; if None, will be created from scratch
+ @param data: return value from post2d.data
+ @param clabel: show contour labels (smooth mode only)
+ @return: tuple of (axes,mappable); mappable can be used in further calls to pylab.colorbar
+ """
+ import pylab,math
+ if not axes: axes=pylab.gca()
+ if data['type']=='rawScalar':
+ from matplotlib.patches import Circle
+ import matplotlib.collections,numpy
+ patches=[]
+ for x,y,d,r in zip(data['x'],data['y'],data['val'],data['radii']):
+ patches.append(Circle(xy=(x,y),radius=r))
+ coll=matplotlib.collections.PatchCollection(patches,linewidths=0.,**kw)
+ coll.set_array(numpy.array(data['val']))
+ bb=coll.get_datalim(coll.get_transform())
+ axes.add_collection(coll)
+ axes.set_xlim(bb.xmin,bb.xmax); axes.set_ylim(bb.ymin,bb.ymax)
+ axes.grid(True); axes.set_aspect('equal')
+ return axes,coll
+ elif data['type']=='smoothScalar':
+ loHi=data['bbox']
+ img=axes.imshow(data['val'],extent=(loHi[0][0],loHi[1][0],loHi[0][1],loHi[1][1]),origin='lower',aspect='equal',**kw)
+ ct=axes.contour(data['x'],data['y'],data['val'],colors='k',origin='lower',extend='both')
+ axes.update_datalim(loHi)
+ if clabel: axes.clabel(ct,inline=1,fontsize=10)
+ axes.set_xlim(loHi[0][0],loHi[1][0]); axes.set_ylim(loHi[0][1],loHi[1][1])
+ axes.grid(True); axes.set_aspect('equal')
+ return axes,img
+ elif data['type'] in ('rawVector','smoothVector'):
+ import numpy
+ loHi=data['bbox']
+ valX,valY=numpy.array(data['valX']),numpy.array(data['valY']) # rawVector data are plain python lists
+ scalars=numpy.sqrt(valX**2+valY**2)
+ # numpy.sqrt computes element-wise sqrt
+ quiv=axes.quiver(data['x'],data['y'],data['valX'],data['valY'],scalars,**kw)
+ #axes.update_datalim(loHi)
+ axes.set_xlim(loHi[0][0],loHi[1][0]); axes.set_ylim(loHi[0][1],loHi[1][1])
+ axes.grid(True); axes.set_aspect('equal')
+ return axes,quiv
+
+
=== modified file 'py/tests/__init__.py'
--- py/tests/__init__.py 2009-08-05 07:32:18 +0000
+++ py/tests/__init__.py 2009-11-13 11:27:22 +0000
@@ -1,3 +1,5 @@
+# 2009 © Václav Šmilauer <eudoxos@xxxxxxxx>
+"""All defined functionality tests for yade."""
import unittest
# add any new test suites to the list here, so that they are picked up by testAll
=== modified file 'py/tests/wrapper.py'
--- py/tests/wrapper.py 2009-09-19 19:41:52 +0000
+++ py/tests/wrapper.py 2009-11-13 11:27:22 +0000
@@ -1,13 +1,16 @@
+# 2009 © Václav Šmilauer <eudoxos@xxxxxxxx>
+
+"""
+This test module covers python/c++ transitions, for both classes deriving from Serializable,
+but also for other classes that we wrap (like Wm3).
+"""
+
import unittest
from yade.wrapper import *
from miniWm3Wrap import *
from yade._customConverters import *
from math import *
-"""
-This test module covers python/c++ transitions, for both classes deriving from Serializable,
-but also for other classes that we wrap (like Wm3).
-"""
# copied from PythonUI_rc, should be in some common place (utils? runtime?)
def listChildClassesRecursive(base):
=== modified file 'py/timing.py'
--- py/timing.py 2009-07-07 10:54:14 +0000
+++ py/timing.py 2009-11-13 11:27:22 +0000
@@ -1,4 +1,10 @@
# encoding: utf-8
+# 2008 © Václav Šmilauer <eudoxos@xxxxxxxx>
+"""Functions for accessing timing information stored in engines and functors.
+See https://yade.hmg.inpg.fr/index.php/Speed_profiling_using_TimingInfo_and_TimingDeltas_classes
+for more details on usage."""
+
+
def _resetEngine(e):
if e.timingDeltas: e.timingDeltas.reset()
if e.__class__.__name__=='EngineUnit': return
@@ -9,6 +15,7 @@
e.execTime,e.execCount=0,0
def reset():
+ "Zero all timing data."
for e in O.engines: _resetEngine(e)
_statCols={'label':40,'count':20,'time':20,'relTime':20}
@@ -58,6 +65,28 @@
return lines
def stats():
+ """Print summary table of timing information from engines and functors. Absolute times as well as percentages are given. Sample output::
+
+ Name Count Time Rel. time
+ -------------------------------------------------------------------------------------------------------
+ PhysicalActionContainerReseter 400 9449μs 0.01%
+ BoundingVolumeMetaEngine 400 1171770μs 1.15%
+ PersistentSAPCollider 400 9433093μs 9.24%
+ InteractionGeometryMetaEngine 400 15177607μs 14.87%
+ InteractionPhysicsMetaEngine 400 9518738μs 9.33%
+ ConstitutiveLawDispatcher 400 64810867μs 63.49%
+ ef2_Spheres_Brefcom_BrefcomLaw
+ setup 4926145 7649131μs 15.25%
+ geom 4926145 23216292μs 46.28%
+ material 4926145 8595686μs 17.14%
+ rest 4926145 10700007μs 21.33%
+ TOTAL 50161117μs 100.00%
+ "damper" 400 1866816μs 1.83%
+ "strainer" 400 21589μs 0.02%
+ "plotDataCollector" 160 64284μs 0.06%
+ "damageChecker" 9 3272μs 0.00%
+ TOTAL 102077490μs 100.00%
+ """
print 'Name'.ljust(_statCols['label'])+' '+'Count'.rjust(_statCols['count'])+' '+'Time'.rjust(_statCols['time'])+' '+'Rel. time'.rjust(_statCols['relTime'])
print '-'*(sum([_statCols[k] for k in _statCols])+len(_statCols)-1)
_engines_stats(O.engines,sum([e.execTime for e in O.engines]),0)
=== modified file 'py/utils.py'
--- py/utils.py 2009-11-12 20:40:12 +0000
+++ py/utils.py 2009-11-13 11:27:22 +0000
@@ -2,7 +2,12 @@
#
# utility functions for yade
#
-# 2008 © Václav Šmilauer <eudoxos@xxxxxxxx>
+# 2008-2009 © Václav Šmilauer <eudoxos@xxxxxxxx>
+
+"""Heap of functions that don't (yet) fit anywhere else.
+
+Devs: please DO NOT ADD more functions here, it is getting too crowded!
+"""
import math,random
from yade.wrapper import *
@@ -533,8 +538,7 @@
return {'negIds':negIds,'posIds':posIds,'axis':axis,'area':min(areas)}
def NormalRestitution2DampingRate(en):
- """Compute the normal damping rate as a function of the normal coefficient of restitution.
- """
+ """Compute the normal damping rate as a function of the normal coefficient of restitution. """
if en == 0.0: return 0.999999999
if en == 1.0: return 0.0
from math import sqrt,log,pi
=== modified file 'py/yadeWrapper/yadeWrapper.cpp'
--- py/yadeWrapper/yadeWrapper.cpp 2009-10-21 15:22:14 +0000
+++ py/yadeWrapper/yadeWrapper.cpp 2009-11-13 11:27:22 +0000
@@ -634,6 +634,7 @@
BOOST_PYTHON_MODULE(wrapper)
{
+ python::scope().attr("__doc__")="Wrapper for c++ internals of yade.";
python::class_<pyOmega>("Omega")
.add_property("iter",&pyOmega::iter,"Get current step number")
.add_property("stopAtIter",&pyOmega::stopAtIter_get,&pyOmega::stopAtIter_set,"Get/set number of iteration after which the simulation will stop.")