← Back to team overview

yade-dev team mailing list archive

[Branch ~yade-dev/yade/trunk] Rev 2542: 1. Allow O.engines to be modified inside the loop from python

 

------------------------------------------------------------
revno: 2542
committer: Václav Šmilauer <eudoxos@xxxxxxxx>
branch nick: trunk
timestamp: Fri 2010-11-12 10:48:58 +0100
message:
  1. Allow O.engines to be modified inside the loop from python 
  2. Fix include dir creation (thanks, buildbot)
  3. Fix Ip2_FrictMat_FrictMat_FrictPhys failure (inversed conditional)
  4. Add tests for substepping, modification of O.engines and Engine.dead
  5. Remove O.initializers
modified:
  SConstruct
  core/EnergyTracker.hpp
  core/Scene.cpp
  core/Scene.hpp
  pkg/common/GravityEngines.cpp
  pkg/common/GravityEngines.hpp
  pkg/dem/CapillaryTriaxialTest.cpp
  pkg/dem/Ip2_FrictMat_FrictMat_FrictPhys.cpp
  pkg/dem/NewtonIntegrator.cpp
  pkg/dem/NewtonIntegrator.hpp
  pkg/dem/STLImporterTest.cpp
  pkg/dem/Shop.cpp
  pkg/dem/Shop.hpp
  py/tests/omega.py
  py/wrapper/yadeWrapper.cpp
  scripts/test/energy.py


--
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
=== modified file 'SConstruct'
--- SConstruct	2010-11-12 08:03:16 +0000
+++ SConstruct	2010-11-12 09:48:58 +0000
@@ -465,7 +465,7 @@
 if not env.GetOption('clean'):
 	# how to make that executed automatically??! For now, run always.
 	#env.AddPreAction(installAlias,installHeaders)
-	from os.path import join,split,isabs,isdir,exists,lexists,islink,isfile,sep
+	from os.path import join,split,isabs,isdir,exists,lexists,islink,isfile,sep,dirname
 	#installHeaders() # install to buildDir always
 	#if 0: # do not install headers
 	#	installHeaders(env.subst('$PREFIX')) # install to $PREFIX if specifically requested: like "scons /usr/local/include"
@@ -476,18 +476,13 @@
 			shutil.rmtree(link)
 		if not exists(link):
 			if lexists(link): os.remove(link) # remove dangling symlink
+			d=os.path.dirname(link)
+			if not exists(d): os.makedirs(d)
 			os.symlink(relpath(link,target),link)
 
 	yadeInc=join(buildDir,'include/yade')
 	mkSymlink(yadeInc,'.')
-	#if not os.path.exists(yadeInc): os.makedirs(yadeInc)
 	import glob
-	# old compat paths
-	#for p in ['core','extra']+glob.glob('lib/*')+glob.glob('pkg/*')+glob.glob('gui'):
-	#	link=yadeInc+'/'+p.replace('/','-')
-	#	if os.path.isdir(p) and not os.path.exists(link):
-	#		if lexists(link): os.remove(link) # dangling symlink
-	#		os.symlink(relpath(link,p),link)
 	boostDir=buildDir+'/include/boost'
 	if not exists(boostDir): os.makedirs(boostDir)
 	if not env['haveForeach']:

=== modified file 'core/EnergyTracker.hpp'
--- core/EnergyTracker.hpp	2010-11-07 11:46:20 +0000
+++ core/EnergyTracker.hpp	2010-11-12 09:48:58 +0000
@@ -43,6 +43,7 @@
 		int id=-1; set(val,name,id);
 	}
 	void clear(){ energies.clear(); names.clear(); resetStep.clear();}
+	Real total(){ Real ret=0; size_t sz=energies.size(); for(size_t id=0; id<sz; id++) ret+=energies.get(id); return ret; }
 
 	py::list keys_py(){ py::list ret; FOREACH(pairStringInt p, names) ret.append(p.first); return ret; }
 	void resetResettables(){ size_t sz=energies.size(); for(size_t id=0; id<sz; id++){ if(resetStep[id]) energies.reset(id); } }
@@ -60,6 +61,7 @@
 			.def("__setitem__",&EnergyTracker::setItem_py,"Set energy value for given name (will create a non-resettable item, if it does not exist yet).")
 			.def("clear",&EnergyTracker::clear,"Clear all stored values.")
 			.def("keys",&EnergyTracker::keys_py,"Return defined energies.")
+			.def("total",&EnergyTracker::total,"Return sum of all energies.")
 	)
 };
 REGISTER_SERIALIZABLE(EnergyTracker);

=== modified file 'core/Scene.cpp'
--- core/Scene.cpp	2010-11-07 11:46:20 +0000
+++ core/Scene.cpp	2010-11-12 09:48:58 +0000
@@ -67,13 +67,19 @@
 
 
 void Scene::moveToNextTimeStep(){
-	if(needsInitializers){
+	if(runInternalConsistencyChecks){
+		runInternalConsistencyChecks=false;
 		checkStateTypes();
-		FOREACH(shared_ptr<Engine> e, initializers){ e->scene=this; if(e->dead || !e->isActivated()) continue; e->action(); } 
-		forces.resize(bodies->size());
-		needsInitializers=false;
+		forces.resize(bodies->size()); // optimization, not necessary
+	}
+	// substepping or not, update engines from _nextEngines, if defined
+	if(!_nextEngines.empty() && subStep<0){
+		engines=_nextEngines;
+		_nextEngines.clear();
 	}
 	if(!subStepping && subStep<0){
+		/* set substep to 0 during the loop, so that engines/nextEngines handler know whether we are inside the loop currently */
+		subStep=0;
 		// ** 1. ** prologue
 		if(isPeriodic) cell->integrateAndUpdate(dt);
 		//forces.reset(); // uncomment if ForceResetter is removed
@@ -89,6 +95,7 @@
 		// ** 3. ** epilogue
 		iter++;
 		time+=dt;
+		subStep=-1;
 	} else {
 		/* IMPORTANT: take care to copy EXACTLY the same sequence as is in the block above !! */
 		if(TimingInfo::enabled){ TimingInfo::enabled=false; LOG_INFO("O.timingEnabled disabled, since O.subStepping is used."); }

=== modified file 'core/Scene.hpp'
--- core/Scene.hpp	2010-10-29 10:12:44 +0000
+++ core/Scene.hpp	2010-11-12 09:48:58 +0000
@@ -73,12 +73,12 @@
 		#endif
 		((bool,isPeriodic,false,Attr::readonly,"Whether periodic boundary conditions are active."))
 		((bool,trackEnergy,false,Attr::readonly,"Whether energies are being traced."))
-		((bool,needsInitializers,true,Attr::readonly,"Whether initializers will be run before the first step."))
+		((bool,runInternalConsistencyChecks,true,Attr::hidden,"Run internal consistency check, right before the very first simulation 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::hidden,"Engines sequence in the simulation."))
-		((vector<shared_ptr<Engine> >,initializers,,Attr::hidden,"Engines that will be run only once, before the first step."))
+		((vector<shared_ptr<Engine> >,_nextEngines,,Attr::hidden,"Engines to be used from the next step on; is returned transparently by O.engines if in the middle of the loop (controlled by subStep>=0)."))
 		((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."))
 		((shared_ptr<EnergyTracker>,energy,new EnergyTracker,Attr::hidden,"Energy values, if energy tracking is enabled."))

=== modified file 'pkg/common/GravityEngines.cpp'
--- pkg/common/GravityEngines.cpp	2010-11-07 11:46:20 +0000
+++ pkg/common/GravityEngines.cpp	2010-11-12 09:48:58 +0000
@@ -27,6 +27,7 @@
 		// skip clumps, only apply forces on their constituents
 		if(!b || b->isClump()) continue;
 		scene->forces.addForce(b->getId(),gravity*b->state->mass);
+		if(scene->trackEnergy) scene->energy->add(gravity.dot(b->state->pos)*b->state->mass,"gravPot",gravPotIx,/*reset at ever step*/true);
 	}
 }
 

=== modified file 'pkg/common/GravityEngines.hpp'
--- pkg/common/GravityEngines.hpp	2010-11-07 11:46:20 +0000
+++ pkg/common/GravityEngines.hpp	2010-11-12 09:48:58 +0000
@@ -14,6 +14,7 @@
 		virtual void action();
 	YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(GravityEngine,FieldApplier,"Engine applying constant acceleration to all bodies.",
 		((Vector3r,gravity,Vector3r::Zero(),,"Acceleration [kgms⁻²]"))
+		((int,gravPotIx,-1,(Attr::noSave|Attr::hidden),"Index for gravPot energy"))
 		,/*ctor*/,/*py*/
 	);
 };

=== modified file 'pkg/dem/CapillaryTriaxialTest.cpp'
--- pkg/dem/CapillaryTriaxialTest.cpp	2010-11-12 08:03:16 +0000
+++ pkg/dem/CapillaryTriaxialTest.cpp	2010-11-12 09:48:58 +0000
@@ -414,9 +414,6 @@
 	//	scene->engines.push_back(orientationIntegrator);
 	//scene->engines.push_back(triaxialstressController);
 	
-	scene->initializers.clear();
-	scene->initializers.push_back(boundDispatcher);
-	
 }
 
 

=== modified file 'pkg/dem/Ip2_FrictMat_FrictMat_FrictPhys.cpp'
--- pkg/dem/Ip2_FrictMat_FrictMat_FrictPhys.cpp	2010-11-12 08:03:16 +0000
+++ pkg/dem/Ip2_FrictMat_FrictMat_FrictPhys.cpp	2010-11-12 09:48:58 +0000
@@ -41,7 +41,7 @@
 	//same for shear stiffness
 	Real Ks = 2*Ea*Ra*Va*Eb*Rb*Vb/(Ea*Ra*Va+Eb*Rb*Va);
 	
-	contactPhysics->frictionAngle = frictAngle ? std::min(mat1->frictionAngle,mat2->frictionAngle) : (*frictAngle)(mat1->id,mat2->id,mat1->frictionAngle,mat2->frictionAngle);
+	contactPhysics->frictionAngle = (!frictAngle) ? std::min(mat1->frictionAngle,mat2->frictionAngle) : (*frictAngle)(mat1->id,mat2->id,mat1->frictionAngle,mat2->frictionAngle);
 	contactPhysics->tangensOfFrictionAngle = std::tan(contactPhysics->frictionAngle); 
 	contactPhysics->prevNormal = normal;
 	contactPhysics->kn = Kn;

=== modified file 'pkg/dem/NewtonIntegrator.cpp'
--- pkg/dem/NewtonIntegrator.cpp	2010-11-07 11:46:20 +0000
+++ pkg/dem/NewtonIntegrator.cpp	2010-11-12 09:48:58 +0000
@@ -10,6 +10,7 @@
 #include<yade/core/Scene.hpp>
 #include<yade/pkg/dem/Clump.hpp>
 #include<yade/pkg/common/VelocityBins.hpp>
+#include<yade/pkg/dem/Shop.hpp>
 #include<yade/lib/base/Math.hpp>
 		
 YADE_PLUGIN((NewtonIntegrator));
@@ -180,6 +181,7 @@
 				Clump::moveMembers(b,scene);
 			}
 			saveMaximaVelocity(scene,id,state);
+			// if(scene->trackEnergy && (b->isStandalone() || b->isClumpMember())) scene->energy->add(Shop::kineticEnergy_singleParticle(scene,b),"kin",kinEnergyIx,/*non-incremental*/true);
 
 			// process callbacks
 			for(size_t i=0; i<callbacksSize; i++){

=== modified file 'pkg/dem/NewtonIntegrator.hpp'
--- pkg/dem/NewtonIntegrator.hpp	2010-11-07 11:46:20 +0000
+++ pkg/dem/NewtonIntegrator.hpp	2010-11-12 09:48:58 +0000
@@ -56,6 +56,7 @@
 		((vector<shared_ptr<BodyCallback> >,callbacks,,,"List (std::vector in c++) of :yref:`BodyCallbacks<BodyCallback>` which will be called for each body as it is being processed."))
 		((Vector3r,prevCellSize,Vector3r(NaN,NaN,NaN),Attr::hidden,"cell size from previous step, used to detect change and find max velocity"))
 		((bool,warnNoForceReset,true,,"Warn when forces were not resetted in this step by :yref:`ForceResetter`; this mostly points to :yref:`ForceResetter` being forgotten incidentally and should be disabled only with a good reason."))
+		((int,kinEnergyIx,-1,(Attr::hidden|Attr::noSave),"Index for kinetic energy in scene->energies."))
 		,
 		/*ctor*/
 			#ifdef YADE_OPENMP

=== modified file 'pkg/dem/STLImporterTest.cpp'
--- pkg/dem/STLImporterTest.cpp	2010-11-07 11:46:20 +0000
+++ pkg/dem/STLImporterTest.cpp	2010-11-12 09:48:58 +0000
@@ -235,8 +235,6 @@
 	scene->engines.push_back(orientationIntegrator);
 	scene->engines.push_back(kinematic);
  	
-	scene->initializers.clear();
-	scene->initializers.push_back(boundDispatcher);
 }
 
 

=== modified file 'pkg/dem/Shop.cpp'
--- pkg/dem/Shop.cpp	2010-11-07 11:46:20 +0000
+++ pkg/dem/Shop.cpp	2010-11-12 09:48:58 +0000
@@ -180,31 +180,34 @@
 	return (useMaxForce?maxF:meanF)/(sumF);
 }
 
+Real Shop::kineticEnergy_singleParticle(Scene* scene,const shared_ptr<Body>& b){
+	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).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+=state->angVel.dot(state->inertia.cwise()*state->angVel);}
+	return E;
+}
+
 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;
-	const bool isPeriodic=scene->isPeriodic;
 	FOREACH(const shared_ptr<Body>& b, *scene->bodies){
 		if(!b || !b->isDynamic()) continue;
-		const State* state(b->state.get());
-		// ½(mv²+ωIω)
-		Vector3r vel=b->state->vel;
-		if(isPeriodic){
-			/* Only take in account the fluctuation velocity, not the mean velocity of homothetic resize. */
-			vel=scene->cell->bodyFluctuationVel(state->pos,vel);
-			// create function in Cell that will compute velocity compensations etc for us
-			// since it might be used in more places than just here (code audit needed)
-		}
-		Real E=.5*(state->mass*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+=state->angVel.dot(state->inertia.cwise()*state->angVel);}
+		Real E=Shop::kineticEnergy_singleParticle(scene,b);
 		if(maxId && E>maxE) { *maxId=b->getId(); maxE=E; }
 		ret+=E;
 	}
@@ -261,64 +264,6 @@
 
 }
 
-/*! Create root body. */
-shared_ptr<Scene> Shop::scene(){
-	return shared_ptr<Scene>(new Scene);
-}
-
-
-/*! Assign default set of actors (initializers and engines) to an existing Scene.
- */
-void Shop::rootBodyActors(shared_ptr<Scene> scene){
-	// initializers	
-	scene->initializers.clear();
-
-	shared_ptr<BoundDispatcher> boundDispatcher	= shared_ptr<BoundDispatcher>(new BoundDispatcher);
-	boundDispatcher->add(new Bo1_Sphere_Aabb);
-	boundDispatcher->add(new Bo1_Box_Aabb);
-	boundDispatcher->add(new Bo1_Tetra_Aabb);
-	scene->initializers.push_back(boundDispatcher);
-
-	//engines
-	scene->engines.clear();
-
-	if(getDefault<int>("param_timeStepUpdateInterval")>0){
-		shared_ptr<GlobalStiffnessTimeStepper> sdecTimeStepper(new GlobalStiffnessTimeStepper);
-		sdecTimeStepper->timeStepUpdateInterval=getDefault<int>("param_timeStepUpdateInterval");
-		sdecTimeStepper->timeStepUpdateInterval=300;
-		scene->engines.push_back(sdecTimeStepper);
-	}
-
-	scene->engines.push_back(shared_ptr<Engine>(new ForceResetter));
-
-	scene->engines.push_back(boundDispatcher);
-
-	scene->engines.push_back(shared_ptr<Engine>(new InsertionSortCollider));
-
-	shared_ptr<IGeomDispatcher> interactionGeometryDispatcher(new IGeomDispatcher);
-	interactionGeometryDispatcher->add(new Ig2_Sphere_Sphere_ScGeom);
-	interactionGeometryDispatcher->add(new Ig2_Box_Sphere_ScGeom);
-	interactionGeometryDispatcher->add(new Ig2_Tetra_Tetra_TTetraGeom);
-	scene->engines.push_back(interactionGeometryDispatcher);
-
-	shared_ptr<IPhysDispatcher> interactionPhysicsDispatcher(new IPhysDispatcher);
-	interactionPhysicsDispatcher->add(new Ip2_FrictMat_FrictMat_FrictPhys);
-	scene->engines.push_back(interactionPhysicsDispatcher);
-		
-	shared_ptr<ElasticContactLaw> constitutiveLaw(new ElasticContactLaw);
-	scene->engines.push_back(constitutiveLaw);
-
-	if(getDefault<Vector3r>("param_gravity")!=Vector3r(0,0,0)){
-		shared_ptr<GravityEngine> gravityCondition(new GravityEngine);
-		gravityCondition->gravity=getDefault<Vector3r>("param_gravity");
-		scene->engines.push_back(gravityCondition);
-	}
-	
-	shared_ptr<NewtonIntegrator> newton(new NewtonIntegrator);
-	newton->damping=max(getDefault<double>("param_damping"),0.);
-	scene->engines.push_back(newton);
-}
-
 
 shared_ptr<FrictMat> Shop::defaultGranularMat(){
 	shared_ptr<FrictMat> mat(new FrictMat);

=== modified file 'pkg/dem/Shop.hpp'
--- pkg/dem/Shop.hpp	2010-11-07 11:46:20 +0000
+++ pkg/dem/Shop.hpp	2010-11-12 09:48:58 +0000
@@ -51,13 +51,6 @@
 			ensureInit(); //cerr<<"Shop: Setting `"<<key<<"'="<<value<<" (type `"<<typeid(valType).name()<<"')."<<endl;
 			defaults[key]=boost::any(value);}
 
-		//! creates empty scene along with its parameters (bound etc.)
-		static shared_ptr<Scene> scene();
-		/*! creates engines and initilizers within given Scene: elastic contact law, with gravity, timestepper and damping.
-		 *
-		 * All parameters are take from Shop::defaults, which are user-settable.
-		 */
-		static void rootBodyActors(shared_ptr<Scene>);
 		//! create default sphere, along with its bound etc. 
 		static shared_ptr<Body> sphere(Vector3r center, Real radius, shared_ptr<Material> mat);
 		//! create default box with everything needed
@@ -101,6 +94,8 @@
 		//! 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);
+		static Real kineticEnergy_singleParticle(Scene* scene, const shared_ptr<Body>& b);
+
 		static Vector3r totalForceInVolume(Real& avgIsoStiffness, Scene *_rb=NULL);
 
 

=== modified file 'py/tests/omega.py'
--- py/tests/omega.py	2010-11-06 11:33:09 +0000
+++ py/tests/omega.py	2010-11-12 09:48:58 +0000
@@ -15,9 +15,52 @@
 ## TODO tests
 class TestInteractions(unittest.TestCase): pass
 class TestForce(unittest.TestCase): pass
-class TestEngines(unittest.TestCase): pass 
 class TestTags(unittest.TestCase): pass 
 
+class TestEngines(unittest.TestCase):
+	def setUp(self): O.reset()
+	def testSubstepping(self):
+		'Engines: substepping'
+		O.engines=[ForceResetter(),PyRunner(initRun=True,iterPeriod=1,command='pass'),GravityEngine()]
+		# value outside the loop
+		self.assert_(O.subStep==-1)
+		# O.subStep is meaningful when substepping
+		O.subStepping=True
+		O.step(); self.assert_(O.subStep==0)
+		O.step(); self.assert_(O.subStep==1)
+		# when substepping is turned off in the middle of the loop, the next step finishes the loop
+		O.subStepping=False
+		O.step(); self.assert_(O.subStep==-1)
+		print 'substep is',O.subStep
+		# subStep==0 inside the loop without substepping
+		O.engines=[PyRunner(initRun=True,iterPeriod=1,command='if O.subStep!=0: raise RuntimeError("O.subStep!=0 inside the loop with O.subStepping==False!")')]
+		O.step()
+	def testEnginesModificationInsideLoop(self):
+		'Engines: can be modified inside the loop transparently.'
+		O.engines=[
+			PyRunner(initRun=True,iterPeriod=1,command='from yade import *; O.engines=[ForceResetter(),GravityEngine(),NewtonIntegrator()]'), # change engines here
+			ForceResetter() # useless engine
+		]
+		O.subStepping=True
+		# run prologue and the first engine, which modifies O.engines
+		O.step(); O.step(); self.assert_(O.subStep==1)
+		self.assert_(len(O.engines)==3) # gives modified engine sequence transparently
+		self.assert_(len(O._nextEngines)==3)
+		self.assert_(len(O._currEngines)==2)
+		O.step(); O.step(); # run the 2nd ForceResetter, and epilogue
+		self.assert_(O.subStep==-1)
+		# start the next step, nextEngines should replace engines automatically
+		O.step()
+		self.assert_(O.subStep==0)
+		self.assert_(len(O._nextEngines)==0)
+		self.assert_(len(O.engines)==3)
+		self.assert_(len(O._currEngines)==3)
+	def testDead(self):
+		'Engines: dead engines are not run'
+		O.engines=[PyRunner(dead=True,initRun=True,iterPeriod=1,command='pass')]
+		O.step(); self.assert_(O.engines[0].nDone==0)
+
+
 class TestIO(unittest.TestCase):
 	def testSaveAllClasses(self):
 		'I/O: All classes can be saved and loaded with boost::serialization'
@@ -35,7 +78,6 @@
 		self.assert_(len(failed)==0,'Failed classes were: '+' '.join(failed))
 
 
-
 class TestCell(unittest.TestCase):
 	def setUp(self):
 		O.reset(); O.periodic=True

=== modified file 'py/wrapper/yadeWrapper.cpp'
--- py/wrapper/yadeWrapper.cpp	2010-11-07 11:46:20 +0000
+++ py/wrapper/yadeWrapper.cpp	2010-11-12 09:48:58 +0000
@@ -422,12 +422,17 @@
 		}
 	}
 
-	vector<shared_ptr<Engine> > engines_get(void){assertScene(); return OMEGA.getScene()->engines;}
-	void engines_set(const vector<shared_ptr<Engine> >& egs){assertScene(); OMEGA.getScene()->engines.clear(); FOREACH(const shared_ptr<Engine>& e, egs) OMEGA.getScene()->engines.push_back(e); mapLabeledEntitiesToVariables(); }
-	vector<shared_ptr<Engine> > initializers_get(void){assertScene(); return OMEGA.getScene()->initializers;}
-	void initializers_set(const vector<shared_ptr<Engine> >& egs){assertScene(); OMEGA.getScene()->initializers.clear(); FOREACH(const shared_ptr<Engine>& e, egs) OMEGA.getScene()->initializers.push_back(e); mapLabeledEntitiesToVariables(); OMEGA.getScene()->needsInitializers=true; }
+	vector<shared_ptr<Engine> > engines_get(void){assertScene(); Scene* scene=OMEGA.getScene().get(); return scene->_nextEngines.empty()?scene->engines:scene->_nextEngines;}
+	void engines_set(const vector<shared_ptr<Engine> >& egs){
+		assertScene(); Scene* scene=OMEGA.getScene().get();
+		if(scene->subStep<0) scene->engines=egs; // not inside the engine loop right now, ok to update directly
+		else scene->_nextEngines=egs; // inside the engine loop, update _nextEngines; O.engines picks that up automatically, and Scene::moveToNextTimestep will put them in place of engines at the start of the next loop
+		mapLabeledEntitiesToVariables();
+	}
+	// raw access to engines/_nextEngines, for debugging
+	vector<shared_ptr<Engine> > currEngines_get(){ return OMEGA.getScene()->engines; }
+	vector<shared_ptr<Engine> > nextEngines_get(){ return OMEGA.getScene()->_nextEngines; }
 
-	
 	pyBodyContainer bodies_get(void){assertScene(); return pyBodyContainer(OMEGA.getScene()->bodies); }
 	pyInteractionContainer interactions_get(void){assertScene(); return pyInteractionContainer(OMEGA.getScene()->interactions); }
 	
@@ -518,7 +523,7 @@
 
 	python::class_<pyOmega>("Omega")
 		.add_property("iter",&pyOmega::iter,"Get current step number")
-		.add_property("subStep",&pyOmega::subStep,"Get the current subStep number (only meaningful if O.subStepping==True)")
+		.add_property("subStep",&pyOmega::subStep,"Get the current subStep number (only meaningful if O.subStepping==True); -1 when outside the loop, otherwise either 0 (O.subStepping==False) or number of engine to be run (O.subStepping==True)")
 		.add_property("subStepping",&pyOmega::subStepping_get,&pyOmega::subStepping_set,"Get/set whether subStepping is active.")
 		.add_property("stopAtIter",&pyOmega::stopAtIter_get,&pyOmega::stopAtIter_set,"Get/set number of iteration after which the simulation will stop.")
 		.add_property("time",&pyOmega::time,"Return virtual (model world) time of the simulation.")
@@ -548,8 +553,9 @@
 		.def("plugins",&pyOmega::plugins_get,"Return list of all plugins registered in the class factory.")
 		.def("_sceneObj",&pyOmega::scene_get,"Return the :yref:`scene <Scene>` object. Debugging only, all (or most) :yref:`Scene` functionality is proxies through :yref:`Omega`.")
 		.add_property("engines",&pyOmega::engines_get,&pyOmega::engines_set,"List of engines in the simulation (Scene::engines).")
+		.add_property("_currEngines",&pyOmega::currEngines_get,"Currently running engines; debugging only!")
+		.add_property("_nextEngines",&pyOmega::nextEngines_get,"Engines for the next step, if different from the current ones, otherwise empty; debugging only!")
 		.add_property("miscParams",&pyOmega::miscParams_get,&pyOmega::miscParams_set,"MiscParams in the simulation (Scene::mistParams), usually used to save serializables that don't fit anywhere else, like GL functors")
-		.add_property("initializers",&pyOmega::initializers_get,&pyOmega::initializers_set,"List of initializers (Scene::initializers).")
 		.add_property("bodies",&pyOmega::bodies_get,"Bodies in the current simulation (container supporting index access by id and iteration)")
 		.add_property("interactions",&pyOmega::interactions_get,"Interactions in the current simulation (container supporting index acces by either (id1,id2) or interactionNumber and iteration)")
 		.add_property("materials",&pyOmega::materials_get,"Shared materials; they can be accessed by id or by label")

=== modified file 'scripts/test/energy.py'
--- scripts/test/energy.py	2010-10-29 10:12:44 +0000
+++ scripts/test/energy.py	2010-11-12 09:48:58 +0000
@@ -12,10 +12,12 @@
 	PyRunner(iterPeriod=10,command='addPlotData()')
 ]
 O.trackEnergy=True
-
-def addPlotData(): plot.addData(i=O.iter,**O.energy)
-
-plot.plots={'i':('elastPotential','plastDissip')}
+O.step() # to have energy categories
+
+def addPlotData():
+	plot.addData(i=O.iter,total=O.energy.total(),**O.energy)
+
+plot.plots={'i':['total',]+O.energy.keys()}
 plot.plot(subPlots=True)
 
 O.dt=utils.PWaveTimeStep()