Author: eudoxos Date: 2009-08-04 08:38:03 +0200 (Tue, 04 Aug 2009) New Revision: 1915
Modified: trunk/gui/py/PythonUI_rc.py trunk/py/yadeWrapper/yadeWrapper.cpp trunk/scripts/simple-scene-parallel.py Log: 1. All different yade types are real python types now. They have their properties defined at startup. One can say O.bodies[0].shape.diffuseColor instead of O.bodies[0].shape['diffuseColor'] now. It also works much better with ipython tab-completion. It is not sure whether we will be able ever to deprecate the old object['attribute'] syntax. 2. Wrap ParallelEngine in python, and finish Dispatcher wraps as well. Modified: trunk/gui/py/PythonUI_rc.py =================================================================== --- trunk/gui/py/PythonUI_rc.py 2009-08-03 19:56:37 UTC (rev 1914) +++ trunk/gui/py/PythonUI_rc.py 2009-08-04 06:38:03 UTC (rev 1915) @@ -21,28 +21,58 @@ for bb in ret: ret2|=listChildClassesRecursive(bb) return ret | ret2 + # not sure whether __builtins__ is the right place? -_dd=__builtins__.__dict__ -_allClasses=set(listChildClassesRecursive('Serializable')) +_proxyNamespace=__builtins__.__dict__ +# all classes that we want to handle at this point +_allSerializables=set(listChildClassesRecursive('Serializable')) +# classes that cannot be instantiated in python directly, and will have no properties generated for them +_noPropsClasses=set(['InteractionContainer','BodyContainer','EngineUnit','Engine','MetaEngine']) +# We call some classes differently in python than in c++ +_classTranslations={'FileGenerator':'Preprocessor'} +# classes that have special wrappers; only the most-bottom ones +_pyRootClasses=set([ + 'StandAloneEngine','DeusExMachina','GeometricalModel','InteractingGeometry','PhysicalParameters','BoundingVolume','InteractingGeometry','InteractionPhysics','FileGenerator', + 'BoundingVolumeEngineUnit','GeometricalModelEngineUnit','InteractingGeometryEngineUnit','InteractionGeometryEngineUnit','InteractionPhysicsEngineUnit','PhysicalParametersEngineUnit','PhysicalActionDamperUnit','PhysicalActionApplierUnit','ConstitutiveLaw']) +# classes for which proxies were already created _proxiedClasses=set() -_classTranslations={'FileGenerator':'Preprocessor'} -for root in [ - 'StandAloneEngine','DeusExMachina','GeometricalModel','InteractingGeometry','PhysicalParameters','BoundingVolume','InteractingGeometry','InteractionPhysics','FileGenerator', - # functors - 'BoundingVolumeEngineUnit','GeometricalModelEngineUnit','InteractingGeometryEngineUnit','InteractionGeometryEngineUnit','InteractionPhysicsEngineUnit','PhysicalParametersEngineUnit','PhysicalActionDamperUnit','PhysicalActionApplierUnit','ConstitutiveLaw' - ]: - root2=root if (root not in _classTranslations.keys()) else _classTranslations[root] +# create types for all classes that yade defines: first those deriving from some root classes +for root0 in _pyRootClasses: + root=root0 if (root0 not in _classTranslations.keys()) else _classTranslations[root0] + rootType=yade.wrapper.__dict__[root] for p in listChildClassesRecursive(root): - #class argStorage: - # def __init__(self,_root,_class): self._root,self._class=_root,_class - # def __call__(self,*args): return yade.wrapper.__dict__[self._root](self._class,*args) - #if root=='MetaEngine': _dd[p]=argStorage(root2,p) - _dd[p]=lambda __r_=root2,__p_=p,**kw : yade.wrapper.__dict__[__r_](__p_,**kw) # eval(root2)(p,kw) + _proxyNamespace[p]=type(p,(rootType,),{'__init__': lambda self,__subType_=p,*args,**kw: super(type(self),self).__init__(__subType_,*args,**kw)}) _proxiedClasses.add(p) -#_dd['Generic']=yade.wrapper.Serializable -for p in _allClasses-_proxiedClasses: # wrap classes that don't derive from any specific root class, just from Serializable - _dd[p]=lambda __p_=p,**kw: yade.wrapper.Serializable(__p_,**kw) +# create types for classes that derive just from Serializable +for p in _allSerializables-_proxiedClasses-_pyRootClasses: + _proxyNamespace[p]=type(p,(Serializable,),{'__init__': lambda self,__subType_=p,*args,**kw: super(type(self),self).__init__(__subType_,*args,**kw)}) +# create class properties for yade serializable attributes, i.e. access object['attribute'] as object.attribute +for c0 in _allSerializables-_noPropsClasses: + c=c0 if (c0 not in _classTranslations.keys()) else _classTranslations[c0] + cls=eval(c) # ugly: create instance; better lookup some namespace (builtins? globals?) + for k in cls().keys(): # must be instantiated so that attributes can be retrieved + setattr(cls,k,property(lambda self,__k_=k:self.__getitem__(__k_),lambda self,val,__k_=k:self.__setitem__(__k_,val))) + + + +### deprecated code, may be removed in 2010 ;-) +if 0: + for root0 in _pyRootClasses: + root=root0 if (root0 not in _classTranslations.keys()) else _classTranslations[root0] + for p in listChildClassesRecursive(root): + #class argStorage: + # def __init__(self,_root,_class): self._root,self._class=_root,_class + # def __call__(self,*args): return yade.wrapper.__dict__[self._root](self._class,*args) + #if root=='MetaEngine': _proxyNamespace[p]=argStorage(root2,p) + _proxyNamespace[p]=lambda __r_=root,__p_=p,**kw : yade.wrapper.__dict__[__r_](__p_,**kw) # eval(root2)(p,kw) + _proxiedClasses.add(p) + # wrap classes that don't derive from any specific root class, have no proxy and need it + for p in _allSerializables-_proxiedClasses-pyRootClasses: + _proxyNamespace[p]=lambda __p_=p,**kw: yade.wrapper.Serializable(__p_,**kw) + + for p in _allSerializables-_proxiedClasses-pyRootClasses: + _proxyNamespace[p]=lambda __p_=p,**kw: yade.wrapper.Serializable(__p_,**kw) ### end wrappers #### HANDLE RENAMED CLASSES #### @@ -63,8 +93,8 @@ def __init__(self,_old,_new): self.old,self.new=_old,_new def __call__(self,*args,**kw): import warnings; warnings.warn("Class `%s' was renamed to `%s', update your code!"%(self.old,self.new),DeprecationWarning,stacklevel=2); - return _dd[self.new](*args,**kw) - _dd[oldName]=warnWrap(oldName,renamed[oldName]) + return _proxyNamespace[self.new](*args,**kw) + _proxyNamespace[oldName]=warnWrap(oldName,renamed[oldName]) Modified: trunk/py/yadeWrapper/yadeWrapper.cpp =================================================================== --- trunk/py/yadeWrapper/yadeWrapper.cpp 2009-08-03 19:56:37 UTC (rev 1914) +++ trunk/py/yadeWrapper/yadeWrapper.cpp 2009-08-04 06:38:03 UTC (rev 1915) @@ -123,39 +123,6 @@ #endif -#if 0 -BASIC_PY_PROXY(pyGeneric,Serializable); -python::list anyEngines_get(const vector<shared_ptr<Engine> >&); -void anyEngines_set(vector<shared_ptr<Engine> >&, python::object); - -BASIC_PY_PROXY_HEAD(pyParallelEngine,ParallelEngine) - pyParallelEngine(python::list slaves){init("ParallelEngine"); slaves_set(slaves);} - void slaves_set(python::list slaves){ - shared_ptr<ParallelEngine> me=dynamic_pointer_cast<ParallelEngine>(proxee); if(!me) throw runtime_error("Proxied class not a ParallelEngine. (WTF?)"); - int len=python::len(slaves); - me->slaves=ParallelEngine::slaveContainer(); // empty the container - for(int i=0; i<len; i++){ - python::extract<python::list> grpMaybe(slaves[i]); - python::list grpList; - if(grpMaybe.check()){ grpList=grpMaybe(); } - else{ /* we got a standalone thing; let's wrap it in list */ grpList.append(slaves[i]); } - vector<shared_ptr<Engine> > grpVec; - anyEngines_set(grpVec,grpList); - me->slaves.push_back(grpVec); - } - } - python::list slaves_get(void){ - shared_ptr<ParallelEngine> me=dynamic_pointer_cast<ParallelEngine>(proxee); if(!me) throw runtime_error("Proxied class not a ParallelEngine. (WTF?)"); - python::list ret; - FOREACH(vector<shared_ptr<Engine > >& grp, me->slaves){ - python::list rret=anyEngines_get(grp); - if(python::len(rret)==1){ ret.append(rret[0]); } else ret.append(rret); - } - return ret; - } -BASIC_PY_PROXY_TAIL; -#endif - class pyBodyContainer{ public: const shared_ptr<BodyContainer> proxee; @@ -491,8 +458,6 @@ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(STLImporter_import_overloads,py_import,1,3); - - /***************************************************************************** ********** New helper functions */ void Serializable_updateAttrs(const shared_ptr<Serializable>& self, const python::dict& d){ @@ -509,7 +474,6 @@ } return ret; } - std::string Serializable_pyStr(const shared_ptr<Serializable>& self) { return "<"+self->getClassName()+" instance at "+lexical_cast<string>(self.get())+">"; } @@ -548,8 +512,14 @@ FOREACH(shared_ptr<functorT> functor,functors) instance->add(functor); return instance; } - // FIXME: this one as well +template<typename DispatcherT, typename functorT> +std::vector<shared_ptr<functorT> > Dispatcher_functors_get(shared_ptr<DispatcherT> self){ + std::vector<shared_ptr<functorT> > ret; + FOREACH(const shared_ptr<EngineUnit>& functor, self->functorArguments){ shared_ptr<functorT> functorRightType(dynamic_pointer_cast<functorT>(functor)); if(!functorRightType) throw logic_error("Internal error: Dispatcher of type "+self->getClassName()+" did not contain EngineUnit of the required type "+typeid(functorT).name()+"?"); ret.push_back(functorRightType); } + return ret; +} +// FIXME: and this one as well shared_ptr<InteractionDispatchers> InteractionDispatchers_ctor_lists(const std::vector<shared_ptr<InteractionGeometryEngineUnit> >& gff, const std::vector<shared_ptr<InteractionPhysicsEngineUnit> >& pff, const std::vector<shared_ptr<ConstitutiveLaw> >& cff){ shared_ptr<InteractionDispatchers> instance(new InteractionDispatchers); FOREACH(shared_ptr<InteractionGeometryEngineUnit> gf, gff) instance->geomDispatcher->add(gf); @@ -557,6 +527,29 @@ FOREACH(shared_ptr<ConstitutiveLaw> cf, cff) instance->constLawDispatcher->add(cf); return instance; } +// ParallelEngine +void ParallelEngine_slaves_set(shared_ptr<ParallelEngine> self, const python::list& slaves){ + int len=python::len(slaves); + self->slaves=ParallelEngine::slaveContainer(); // empty the container + for(int i=0; i<len; i++){ + python::extract<std::vector<shared_ptr<Engine> > > serialGroup(slaves[i]); + if (serialGroup.check()){ self->slaves.push_back(serialGroup()); continue; } + python::extract<shared_ptr<Engine> > serialAlone(slaves[i]); + if (serialAlone.check()){ vector<shared_ptr<Engine> > aloneWrap; aloneWrap.push_back(serialAlone()); self->slaves.push_back(aloneWrap); continue; } + PyErr_SetString(PyExc_TypeError,"List elements must be either\n (a) sequences of engines to be executed one after another\n(b) alone engines."); + python::throw_error_already_set(); + } +} +python::list ParallelEngine_slaves_get(shared_ptr<ParallelEngine> self){ + python::list ret; + FOREACH(vector<shared_ptr<Engine > >& grp, self->slaves){ + if(grp.size()==1) ret.append(python::object(grp[0])); + else ret.append(python::object(grp)); + } + return ret; +} +shared_ptr<ParallelEngine> ParallelEngine_ctor_list(const python::list& slaves){ shared_ptr<ParallelEngine> instance(new ParallelEngine); ParallelEngine_slaves_set(instance,slaves); return instance; } + // injected methods Vector3r PhysicalParameters_displ_get(const shared_ptr<PhysicalParameters>& pp){return pp->se3.position-pp->refSe3.position;} Vector3r PhysicalParameters_rot_get (const shared_ptr<PhysicalParameters>& pp){Quaternionr relRot=pp->refSe3.orientation.Conjugate()*pp->se3.orientation; Vector3r axis; Real angle; relRot.ToAxisAngle(axis,angle); return axis*angle; } @@ -666,7 +659,7 @@ python::class_<pyBexContainer>("BexContainer",python::init<pyBexContainer&>()) .def("f",&pyBexContainer::force_get) .def("t",&pyBexContainer::torque_get) - .def("m",&pyBexContainer::torque_get) // for compatibility with ActionContainer + .def("m",&pyBexContainer::torque_get) .def("move",&pyBexContainer::move_get) .def("rot",&pyBexContainer::rot_get) .def("addF",&pyBexContainer::force_add) @@ -674,73 +667,6 @@ .def("addMove",&pyBexContainer::move_add) .def("addRot",&pyBexContainer::rot_add); -// keep for a while for reference, to make sure everything is wrapped as it was before -#if 0 - BASIC_PY_PROXY_WRAPPER(pyStandAloneEngine,"StandAloneEngine") - TIMING_PROPS(pyStandAloneEngine); - BASIC_PY_PROXY_WRAPPER(pyMetaEngine,"MetaEngine") - .add_property("functors",&pyMetaEngine::functors_get,&pyMetaEngine::functors_set) - TIMING_PROPS(pyMetaEngine) - .def(python::init<string,python::list>()); - BASIC_PY_PROXY_WRAPPER(pyParallelEngine,"ParallelEngine") - .add_property("slaves",&pyParallelEngine::slaves_get,&pyParallelEngine::slaves_set) - .def(python::init<python::list>()); - BASIC_PY_PROXY_WRAPPER(pyDeusExMachina,"DeusExMachina") - TIMING_PROPS(pyDeusExMachina); - BASIC_PY_PROXY_WRAPPER(pyInteractionDispatchers,"InteractionDispatchers") - .def(python::init<python::list,python::list,python::list>()) - .add_property("geomDispatcher",&pyInteractionDispatchers::geomDispatcher_get) - .add_property("physDispatcher",&pyInteractionDispatchers::physDispatcher_get) - .add_property("constLawDispatcher",&pyInteractionDispatchers::constLawDispatcher_get) - TIMING_PROPS(pyInteractionDispatchers); - BASIC_PY_PROXY_WRAPPER(pyEngineUnit,"EngineUnit") - .add_property("timingDeltas",&pyEngineUnit::timingDeltas_get) - .add_property("bases",&pyEngineUnit::bases_get); - - #undef TIMING_PROPS - - BASIC_PY_PROXY_WRAPPER(pyGeometricalModel,"GeometricalModel"); - BASIC_PY_PROXY_WRAPPER(pyInteractingGeometry,"InteractingGeometry"); - BASIC_PY_PROXY_WRAPPER(pyPhysicalParameters,"PhysicalParameters") - .add_property("blockedDOFs",&pyPhysicalParameters::blockedDOFs_get,&pyPhysicalParameters::blockedDOFs_set) - .add_property("pos",&pyPhysicalParameters::pos_get,&pyPhysicalParameters::pos_set) - .add_property("ori",&pyPhysicalParameters::ori_get,&pyPhysicalParameters::ori_set) - .add_property("refPos",&pyPhysicalParameters::refPos_get,&pyPhysicalParameters::refPos_set) - .add_property("displ",&pyPhysicalParameters::displ_get) - .add_property("rot",&pyPhysicalParameters::rot_get) - ; - BASIC_PY_PROXY_WRAPPER(pyBoundingVolume,"BoundingVolume") - .add_property("min",&pyBoundingVolume::min_get) - .add_property("max",&pyBoundingVolume::max_get); - BASIC_PY_PROXY_WRAPPER(pyInteractionGeometry,"InteractionGeometry"); - BASIC_PY_PROXY_WRAPPER(pyInteractionPhysics,"InteractionPhysics"); - - BASIC_PY_PROXY_WRAPPER(pyGeneric,"Generic"); - - BASIC_PY_PROXY_WRAPPER(pyBody,"Body") - .add_property("shape",&pyBody::shape_get,&pyBody::shape_set) - .add_property("mold",&pyBody::mold_get,&pyBody::mold_set) - .add_property("bound",&pyBody::bound_get,&pyBody::bound_set) - .add_property("phys",&pyBody::phys_get,&pyBody::phys_set) - .add_property("dynamic",&pyBody::dynamic_get,&pyBody::dynamic_set) - .add_property("id",&pyBody::id_get) - .add_property("mask",&pyBody::mask_get,&pyBody::mask_set) - .add_property("isStandalone",&pyBody::isStandalone) - .add_property("isClumpMember",&pyBody::isClumpMember) - .add_property("isClump",&pyBody::isClump); - - BASIC_PY_PROXY_WRAPPER(pyInteraction,"Interaction") - .add_property("phys",&pyInteraction::phys_get,&pyInteraction::phys_set) - .add_property("geom",&pyInteraction::geom_get,&pyInteraction::geom_set) - .add_property("id1",&pyInteraction::id1_get) - .add_property("id2",&pyInteraction::id2_get) - .add_property("isReal",&pyInteraction::isReal_get); - - BASIC_PY_PROXY_WRAPPER(pyFileGenerator,"Preprocessor") - .def("generate",&pyFileGenerator::generate) - .def("load",&pyFileGenerator::load); -#endif - python::class_<pySTLImporter>("STLImporter") .def("open",&pySTLImporter::open) .add_property("number_of_facets",&pySTLImporter::number_of_facets) @@ -750,13 +676,13 @@ ////////////////////////////////////////////////////////////// ///////////// proxyless wrappers - /* TODO: bases for functors; functors for dispatchers; ParallelEngine (?) */ - python::class_<Serializable, shared_ptr<Serializable>, noncopyable >("Serializable") - .add_property("name",&Serializable::getClassName).def("__str__",&Serializable_pyStr).def("postProcessAttributes",&Serializable::postProcessAttributes) - .def("dict",&Serializable::pyDict).def("__getitem__",&Serializable::pyGetAttr).def("__setitem__",&Serializable::pySetAttr).def("has_key",&Serializable::pyHasKey).def("keys",&Serializable::pyKeys). - def("updateAttrs",&Serializable_updateAttrs).def("updateExistingAttrs",&Serializable_updateExistingAttrs) + .add_property("name",&Serializable::getClassName).def("__str__",&Serializable_pyStr).def("__repr__",&Serializable_pyStr).def("postProcessAttributes",&Serializable::postProcessAttributes) + .def("dict",&Serializable::pyDict).def("__getitem__",&Serializable::pyGetAttr).def("__setitem__",&Serializable::pySetAttr).def("has_key",&Serializable::pyHasKey).def("keys",&Serializable::pyKeys) + .def("updateAttrs",&Serializable_updateAttrs).def("updateExistingAttrs",&Serializable_updateExistingAttrs) .def("__init__",python::raw_constructor(Serializable_ctor_kwAttrs<Serializable>)) + // aliases for __getitem__ and __setitem__, but they are used by the property generator code and can be useful if we deprecate the object['attr'] type of access + .def("_prop_get",&Serializable::pyGetAttr).def("_prop_set",&Serializable::pySetAttr) ; python::class_<Engine, shared_ptr<Engine>, python::bases<Serializable>, noncopyable >("Engine",python::no_init) .add_property("execTime",&Engine_timingInfo_nsec_get,&Engine_timingInfo_nsec_set) @@ -770,8 +696,16 @@ python::class_<MetaEngine, shared_ptr<MetaEngine>, python::bases<Engine>, noncopyable>("MetaEngine",python::no_init); python::class_<TimingDeltas, shared_ptr<TimingDeltas>, noncopyable >("TimingDeltas").add_property("data",&TimingDeltas_pyData).def("reset",&TimingDeltas::reset); - python::class_<InteractionDispatchers,shared_ptr<InteractionDispatchers>, python::bases<Engine>, noncopyable >("InteractionDispatchers").def("__init__",python::make_constructor(InteractionDispatchers_ctor_lists)); - #define EXPOSE_DISPATCHER(DispatcherT,functorT) python::class_<DispatcherT, shared_ptr<DispatcherT>, python::bases<MetaEngine>, noncopyable >(#DispatcherT).def("__init__",python::make_constructor(Dispatcher_ctor_list<DispatcherT,functorT>)); + python::class_<InteractionDispatchers,shared_ptr<InteractionDispatchers>, python::bases<Engine>, noncopyable >("InteractionDispatchers") + .def("__init__",python::make_constructor(InteractionDispatchers_ctor_lists)) + .def_readonly("geomDispatcher",&InteractionDispatchers::geomDispatcher) + .def_readonly("physDispatcher",&InteractionDispatchers::physDispatcher) + .def_readonly("constLawDispatcher",&InteractionDispatchers::constLawDispatcher); + python::class_<ParallelEngine,shared_ptr<ParallelEngine>, python::bases<Engine>, noncopyable>("ParallelEngine") + .def("__init__",python::make_constructor(ParallelEngine_ctor_list)) + .add_property("slaves",&ParallelEngine_slaves_get,&ParallelEngine_slaves_set); + + #define EXPOSE_DISPATCHER(DispatcherT,functorT) python::class_<DispatcherT, shared_ptr<DispatcherT>, python::bases<MetaEngine>, noncopyable >(#DispatcherT).def("__init__",python::make_constructor(Dispatcher_ctor_list<DispatcherT,functorT>)).add_property("functors",&Dispatcher_functors_get<DispatcherT,functorT>); EXPOSE_DISPATCHER(BoundingVolumeMetaEngine,BoundingVolumeEngineUnit) EXPOSE_DISPATCHER(GeometricalModelMetaEngine,GeometricalModelEngineUnit) EXPOSE_DISPATCHER(InteractingGeometryMetaEngine,InteractingGeometryEngineUnit) @@ -794,7 +728,6 @@ EXPOSE_FUNCTOR(PhysicalActionApplierUnit) EXPOSE_FUNCTOR(ConstitutiveLaw) #undef EXPOSE_FUNCTOR - #define EXPOSE_CXX_CLASS_RENAMED(cxxName,pyName) python::class_<cxxName,shared_ptr<cxxName>, python::bases<Serializable>, noncopyable>(#pyName).def("__init__",python::raw_constructor(Serializable_ctor_kwAttrs<cxxName>)) #define EXPOSE_CXX_CLASS(className) EXPOSE_CXX_CLASS_RENAMED(className,className) @@ -812,7 +745,9 @@ .add_property("isClump",&Body::isClump); EXPOSE_CXX_CLASS(InteractingGeometry); EXPOSE_CXX_CLASS(GeometricalModel); - EXPOSE_CXX_CLASS(BoundingVolume); + EXPOSE_CXX_CLASS(BoundingVolume) + .def_readonly("min",&BoundingVolume::min) + .def_readonly("max",&BoundingVolume::max); EXPOSE_CXX_CLASS(PhysicalParameters) .add_property("blockedDOFs",&PhysicalParameters::blockedDOFs_vec_get,&PhysicalParameters::blockedDOFs_vec_set) .add_property("pos",&PhysicalParameters_pos_get,&PhysicalParameters_pos_set) Modified: trunk/scripts/simple-scene-parallel.py =================================================================== --- trunk/scripts/simple-scene-parallel.py 2009-08-03 19:56:37 UTC (rev 1914) +++ trunk/scripts/simple-scene-parallel.py 2009-08-04 06:38:03 UTC (rev 1915) @@ -25,11 +25,10 @@ # implementation of openMP in gcc: http://gcc.gnu.org/projects/gomp/ # ParallelEngine([ + # BexResetter will run in parallel with the second group of BoundingVolumeMEtaEngine+PersistentSAPCollider BexResetter(), - [ - BoundingVolumeMetaEngine([InteractingSphere2AABB(),InteractingBox2AABB(),MetaInteractingGeometry2AABB()]), - InsertionSortCollider(), - ] + # Engines within the group will be run serially, however + [BoundingVolumeMetaEngine([InteractingSphere2AABB(),InteractingBox2AABB(),MetaInteractingGeometry2AABB()]), PersistentSAPCollider(),] ]), InteractionGeometryMetaEngine([InteractingSphere2InteractingSphere4SpheresContactGeometry(),InteractingBox2InteractingSphere4SpheresContactGeometry()]), InteractionPhysicsMetaEngine([SimpleElasticRelationships()]), @@ -44,5 +43,5 @@ O.bodies.append(utils.box(center=[0,0,0],extents=[.5,.5,.5],dynamic=False,color=[1,0,0],young=30e9,poisson=.3,density=2400)) O.bodies.append(utils.sphere([0,0,2],1,color=[0,1,0],young=30e9,poisson=.3,density=2400)) O.dt=.5*utils.PWaveTimeStep() - +O.saveTmp() #o.run(100000); o.wait(); print o.iter/o.realtime,"iterations/sec" _______________________________________________ Mailing list: https://launchpad.net/~yade-dev Post to : yade-...@lists.launchpad.net Unsubscribe : https://launchpad.net/~yade-dev More help : https://help.launchpad.net/ListHelp _______________________________________________ yade-dev mailing list yade-dev@lists.berlios.de https://lists.berlios.de/mailman/listinfo/yade-dev