← Back to team overview

yade-dev team mailing list archive

[svn] r1758 - in trunk: extra/usct gui/py

 

Author: eudoxos
Date: 2009-04-23 15:20:50 +0200 (Thu, 23 Apr 2009)
New Revision: 1758

Modified:
   trunk/extra/usct/UniaxialStrainControlledTest.cpp
   trunk/extra/usct/UniaxialStrainControlledTest.hpp
   trunk/gui/py/utils.py
   trunk/gui/py/yade-multi
Log:
1. Remove cruft from UniaxialStrainer, add the ability to set initial velocities to suppress inertia at the simulation beginning
2. Jobs in yade-multi can be automatically named after variables suffixed with !
3. yade-multi now shows and updates automatically some statistic on localhost:9080 using http



Modified: trunk/extra/usct/UniaxialStrainControlledTest.cpp
===================================================================
--- trunk/extra/usct/UniaxialStrainControlledTest.cpp	2009-04-22 06:57:45 UTC (rev 1757)
+++ trunk/extra/usct/UniaxialStrainControlledTest.cpp	2009-04-23 13:20:50 UTC (rev 1758)
@@ -20,7 +20,7 @@
 	assert(posIds.size()>0);
 	assert(negIds.size()>0);
 	posCoords.clear(); negCoords.clear();
-	FOREACH(body_id_t id,posIds){ const shared_ptr<Body>& b=Body::byId(id); posCoords.push_back(b->physicalParameters->se3.position[axis]);
+	FOREACH(body_id_t id,posIds){ const shared_ptr<Body>& b=Body::byId(id,rootBody); posCoords.push_back(b->physicalParameters->se3.position[axis]);
 		if(blockDisplacements && blockRotations) b->isDynamic=false;
 		else{
 			shared_ptr<PhysicalParameters> &pp=b->physicalParameters;
@@ -28,7 +28,7 @@
 			if(blockRotations) pp->blockedDOFs|=PhysicalParameters::DOF_RXRYRZ;
 		}
 	}
-	FOREACH(body_id_t id,negIds){ const shared_ptr<Body>& b=Body::byId(id); negCoords.push_back(b->physicalParameters->se3.position[axis]);
+	FOREACH(body_id_t id,negIds){ const shared_ptr<Body>& b=Body::byId(id,rootBody); negCoords.push_back(b->physicalParameters->se3.position[axis]);
 		if(blockDisplacements && blockRotations) b->isDynamic=false;
 		else{
 			shared_ptr<PhysicalParameters> &pp=b->physicalParameters;
@@ -49,11 +49,43 @@
 	assert(originalLength>0 && !isnan(originalLength));
 
 	assert(!isnan(strainRate) || !isnan(absSpeed));
-	if(strainRate==0){ strainRate=absSpeed/originalLength; LOG_INFO("Computed new strainRate "<<strainRate); }
+	if(!isnan(std::numeric_limits<Real>::quiet_NaN())){ LOG_FATAL("NaN's are not properly supported (compiled, with -ffast-math?), which is required."); throw; }
 
-	initAccelTime_s=initAccelTime>=0 ? initAccelTime : Omega::instance().getTimeStep()*(-initAccelTime);
-	LOG_INFO("Strain speed will be "<<absSpeed<<", strain rate "<<strainRate<<", will be reached after "<<initAccelTime_s<<"s ("<<initAccelTime_s/Omega::instance().getTimeStep()<<" steps).");
+	if(isnan(strainRate)){ strainRate=absSpeed/originalLength; LOG_INFO("Computed new strainRate "<<strainRate); }
+	else {absSpeed=strainRate*originalLength;}
 
+	if(!setSpeeds){
+		initAccelTime_s=initAccelTime>=0 ? initAccelTime : Omega::instance().getTimeStep()*(-initAccelTime);
+		LOG_INFO("Strain speed will be "<<absSpeed<<", strain rate "<<strainRate<<", will be reached after "<<initAccelTime_s<<"s ("<<initAccelTime_s/Omega::instance().getTimeStep()<<" steps).");
+	} else {
+		/* set speed such that it is linear on the strained axis; transversal speed is not set, which can perhaps create some problems.
+			Note: all bodies in the simulation will have their speed set, since there is no way to tell which ones are part of the specimen
+			and which are not.
+
+			Speeds will be linearly interpolated beween axis positions p0,p1 and velocities v0,v1.
+		*/
+		initAccelTime_s=0;
+		LOG_INFO("Strain speed will be "<<absSpeed<<", strain rate "<<strainRate<<"; velocities will be set directly at the beginning.");
+		Real p0=axisCoord(negIds[0]), p1=axisCoord(posIds[0]); // limit positions
+		Real v0,v1; // speeds at p0, p1
+		switch(asymmetry){
+			case -1: v0=-absSpeed; v1=0; break;
+			case  0: v0=-absSpeed/2; v1=absSpeed/2; break;
+			case  1: v0=0; v1=absSpeed; break;
+			default: LOG_FATAL("Unknown asymmetry value "<<asymmetry<<" (should be -1,0,1)"); throw;
+		}
+		assert(p1>p0);
+		FOREACH(const shared_ptr<Body>& b, *rootBody->bodies){
+			// skip bodies on the boundary, since those will have their positions updated directly
+			if(std::find(posIds.begin(),posIds.end(),b->id)!=posIds.end() || std::find(negIds.begin(),negIds.end(),b->id)!=negIds.end()) { continue; }
+			Real p=axisCoord(b->id);
+			Real pNormalized=(p-p0)/(p1-p0);
+			YADE_CAST<ParticleParameters*>(b->physicalParameters.get())->velocity[axis]=pNormalized*(v1-v0)+v0;
+		}
+	}
+	stressUpdateInterval=max(1,(int)(2e-5/(abs(strainRate)*Omega::instance().getTimeStep())));
+	LOG_INFO("Stress will be updated every "<<stressUpdateInterval<<" steps.");
+
 	/* if we have default (<0) crossSectionArea, try to get it from root's AABB;
 	 * this will not work if there are foreign bodies in the simulation,
 	 * in which case you must give the value yourself as engine attribute.
@@ -75,13 +107,10 @@
 		}
 	}
 	assert(crossSectionArea>0);
-	prepareRecStream();
-#if 0
-	setupTransStrainSensors();
-#endif
 }
 
-void UniaxialStrainer::applyCondition(MetaBody* rootBody){
+void UniaxialStrainer::applyCondition(MetaBody* _rootBody){
+	rootBody=_rootBody;
 	if(needsInit) init();
 	// postconditions for initParams
 	assert(posIds.size()==posCoords.size() && negIds.size()==negCoords.size() && originalLength>0 && crossSectionArea>0);
@@ -117,19 +146,18 @@
 
 	Real axialLength=axisCoord(posIds[0])-axisCoord(negIds[0]);
 	strain=axialLength/originalLength-1;
-	if(Omega::instance().getCurrentIteration()%400==0) TRVAR5(dAX,axialLength,originalLength,currentStrainRate,strain);
 
 	// reverse if we're over the limit strain
 	if(notYetReversed && limitStrain!=0 && ((currentStrainRate>0 && strain>limitStrain) || (currentStrainRate<0 && strain<limitStrain))) { currentStrainRate*=-1; notYetReversed=false; LOG_INFO("Reversed strain rate to "<<currentStrainRate); }
 
-	if(Omega::instance().getCurrentIteration()%10==0) {
-		computeAxialForce(rootBody);
+	// update forces and stresses
+	if(Omega::instance().getCurrentIteration()%stressUpdateInterval==0) {
+		computeAxialForce();
 		avgStress=(sumPosForces+sumNegForces)/(2*crossSectionArea); // average nominal stress
-		if(!recordFile.empty() && recStream.good()) recStream<<Omega::instance().getCurrentIteration()<<" "<<strain<<" "<<avgStress<<endl; // <<" "<<avgTransStrain<<endl;
 	}
 }
 
-void UniaxialStrainer::computeAxialForce(MetaBody* rootBody){
+void UniaxialStrainer::computeAxialForce(){
 	sumPosForces=sumNegForces=0;
 	rootBody->bex.sync();
 	FOREACH(body_id_t id, negIds) sumNegForces+=rootBody->bex.getForce(id)[axis];
@@ -220,7 +248,6 @@
 #include<yade/pkg-common/LeapFrogPositionIntegrator.hpp>
 #include<yade/pkg-common/LeapFrogOrientationIntegrator.hpp>
 #include<yade/pkg-common/PersistentSAPCollider.hpp>
-#include<yade/pkg-dem/PositionOrientationRecorder.hpp>
 #include<yade/pkg-dem/GlobalStiffnessTimeStepper.hpp>
 #include<yade/pkg-common/PhysicalActionDamper.hpp>
 #include<yade/pkg-common/CundallNonViscousDamping.hpp>

Modified: trunk/extra/usct/UniaxialStrainControlledTest.hpp
===================================================================
--- trunk/extra/usct/UniaxialStrainControlledTest.hpp	2009-04-22 06:57:45 UTC (rev 1757)
+++ trunk/extra/usct/UniaxialStrainControlledTest.hpp	2009-04-23 13:20:50 UTC (rev 1758)
@@ -46,13 +46,12 @@
  */
 class UniaxialStrainer: public DeusExMachina {
 	private:
+		MetaBody* rootBody;
 		bool idInVector(body_id_t id, const vector<body_id_t>& V){for(size_t i=0; i<V.size(); i++){ if(V[i]==id) return true; }	return false; }  // unused now
-		void computeAxialForce(MetaBody* rootBody);
+		void computeAxialForce();
 
-		ofstream recStream;
-		string recordFile;
 		bool needsInit;
-		Real& axisCoord(body_id_t id){ return Body::byId(id)->physicalParameters->se3.position[axis]; };
+		Real& axisCoord(body_id_t id){ return Body::byId(id,rootBody)->physicalParameters->se3.position[axis]; };
 		void init();
 	public:
 		virtual bool isActivated(){return active;}
@@ -71,6 +70,7 @@
 		Real sumPosForces,sumNegForces;
 		//! crossSection perpendicular to he strained axis, computed from AABB of MetaBody
 		Real crossSectionArea;		//! Apply strain along x (0), y (1) or z(2) axis
+		//! The axis which is strained (0,1,2 for x,y,z)
 		int axis;
 		//! If 0, straining is symmetric for negIds and posIds; for 1 (or -1), only posIds are strained and negIds don't move (or vice versa)
 		int asymmetry;
@@ -84,6 +84,10 @@
 		long idleIterations;
 		//! Time for strain reaching the requested value (linear interpolation). If negative, the time is dt*(-initAccelTime), where dt is  the timestep at the first iteration.
 		Real initAccelTime, initAccelTime_s /* value always in s, computed from initAccelTime */;
+		//! should we set speeds at the beginning directly, instead of increasing strain rate progressively?
+		bool setSpeeds;
+		//! how often to update forces (initialized automatically)
+		int stressUpdateInterval;
 
 		/** bodies on which straining will be applied (on the positive and negative side of axis) */
 		vector<body_id_t> posIds, negIds;
@@ -110,7 +114,7 @@
 		Real strain, avgStress;
 
 		virtual void applyCondition(MetaBody* rootBody);
-		UniaxialStrainer(){axis=2; asymmetry=0; currentStrainRate=0; originalLength=-1; limitStrain=0; notYetReversed=true; crossSectionArea=-1; needsInit=true; /* sensorsPusher=shared_ptr<UniaxialStrainSensorPusher>(); */ recordFile=""; strain=avgStress=/*avgTransStrain=*/0; blockRotations=false; blockDisplacements=false;  strainRate=0; absSpeed=stopStrain=numeric_limits<Real>::quiet_NaN(); active=true; idleIterations=0; initAccelTime=-200;};
+		UniaxialStrainer(){axis=2; asymmetry=0; currentStrainRate=0; originalLength=-1; limitStrain=0; notYetReversed=true; crossSectionArea=-1; needsInit=true; strain=avgStress=0; blockRotations=false; blockDisplacements=false; setSpeeds=false; strainRate=absSpeed=stopStrain=numeric_limits<Real>::quiet_NaN(); active=true; idleIterations=0; initAccelTime=-200;};
 		virtual ~UniaxialStrainer(){};
 		REGISTER_ATTRIBUTES(DeusExMachina,
 				(strainRate) 
@@ -128,14 +132,12 @@
 				(limitStrain) 
 				(notYetReversed) 
 				(crossSectionArea) 
-				(recordFile) 
 				(strain) 
 				(avgStress) 
 				(blockDisplacements) 
 				(blockRotations) 
+				(setSpeeds)
 		);
-		void prepareRecStream(void){ if(!recordFile.empty()) recStream.open(recordFile.c_str()); }
-		void postProcessAttributes(bool deserializing){ if(deserializing) prepareRecStream(); } 	
 	REGISTER_CLASS_AND_BASE(UniaxialStrainer,DeusExMachina);
 	DECLARE_LOGGER;
 };

Modified: trunk/gui/py/utils.py
===================================================================
--- trunk/gui/py/utils.py	2009-04-22 06:57:45 UTC (rev 1757)
+++ trunk/gui/py/utils.py	2009-04-23 13:20:50 UTC (rev 1758)
@@ -347,21 +347,30 @@
 		o.tags['line']='l!'
 	else:
 		if not tableFileLine: tableFileLine=os.environ['PARAM_TABLE']
-		tableFile,tableLine=tableFileLine.split(':')
+		env=tableFileLine.split(':')
+		tableDesc=None
+		tableFile,tableLine=env[0],env[1]
+		if len(env)>2: tableDesc=env[3]
 		o.tags['line']='l'+tableLine
 		ll=[l.split('#')[0] for l in ['']+open(tableFile).readlines()]; names=ll[1].split(); values=ll[int(tableLine)].split()
 		assert(len(names)==len(values))
+		if 'description' in names: O.tags['description']=values[names.index('description')]
+		else:
+			bangCols=[i for i,h in enumerate(names) if h[-1]=='!']
+			if len(bangCols)==0: bangCols=range(len(headings))
+			for i in range(len(names)):
+				if names[i][-1]=='!': names[i]=names[i][:-1] # strip trailing !
+			O.tags['description']=','.join( names[col]+'='+('%g'%values[col] if isinstance(values[col],float) else str(values[col])) for col in bangCols)
 		for i in range(len(names)):
-			if names[i]=='description': o.tags['description']=values[i]
-			else:
-				if names[i] not in kw.keys():
-					if (not unknownOk) and names[i][0]!='!': raise NameError("Parameter `%s' has no default value assigned"%names[i])
-				else: kw.pop(names[i])
-				if names[i][0]!='!':
-					exec('__builtin__.%s=%s'%(names[i],values[i])); tagsParams+=['%s=%s'%(names[i],values[i])]; dictParams[names[i]]=values[i]
+			if names[i]=='description': continue
+			if names[i] not in kw.keys():
+				if (not unknownOk) and names[i][0]!='!': raise NameError("Parameter `%s' has no default value assigned"%names[i])
+			else: kw.pop(names[i])
+			if names[i][0]!='!':
+				exec('%s=%s'%(names[i],values[i])) in __builtins__; tagsParams+=['%s=%s'%(names[i],values[i])]; dictParams[names[i]]=values[i]
 	defaults=[]
 	for k in kw.keys():
-		exec("__builtin__.%s=%s"%(k,repr(kw[k])))
+		exec("%s=%s"%(k,repr(kw[k]))) in __builtins__
 		defaults+=["%s=%s"%(k,kw[k])]; dictDefaults[k]=kw[k]
 	o.tags['defaultParams']=",".join(defaults)
 	o.tags['params']=",".join(tagsParams)

Modified: trunk/gui/py/yade-multi
===================================================================
--- trunk/gui/py/yade-multi	2009-04-22 06:57:45 UTC (rev 1757)
+++ trunk/gui/py/yade-multi	2009-04-23 13:20:50 UTC (rev 1758)
@@ -23,9 +23,48 @@
 finished: %s
 """%(self.id,self.exitStatus,'OK' if self.exitStatus==0 else 'FAILED',self.duration,self.command,time.asctime(time.localtime(self.started)),time.asctime(time.localtime(self.finished))));
 		log.close()
+	def t2hhmmss(self,dt): return '%02d:%02d:%02d'%(dt//3600,(dt%3600)//60,(dt%60))
+	def htmlStats(self):
+		ret='<tr>'
+		ret+='<td>%s</td>'%self.id
+		if self.status=='PENDING': ret+='<td bgcolor="grey">(pending)</td>'
+		elif self.status=='RUNNING': ret+='<td bgcolor="yellow">%s</td>'%self.t2hhmmss(time.time()-self.started)
+		elif self.status=='DONE': ret+='<td bgcolor="%s">%s</td>'%('green' if self.exitStatus==0 else 'red',self.duration)
+		ret+='<td>%s</td>'%self.command
+		ret+='<td>%d</td>'%self.nSlots
+		ret+='</tr>'
+		return ret
 
+def globalHtmlStats():
+	ret='<h3>Jobs</h3>'
+	nFailed=len([j for j in jobs if j.status=='DONE' and j.exitStatus!=0])
+	ret+='<p><b>%d</b> total, <b>%d</b> <span style="background-color:yellow">running</span>, <b>%d</b> <span style="background-color:green">done</span>%s</p>'%(len(jobs),len([j for j in jobs if j.status=='RUNNING']), len([j for j in jobs if j.status=='DONE']),' (<b>%d <span style="background-color:red"><b>failed</b></span>)'%nFailed if nFailed>0 else '')
+	return ret
 
+from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
+import socket
+class HttpStatsServer(BaseHTTPRequestHandler):
+	def do_GET(self):
+		self.send_response(200)
+		self.send_header('Content-type','text/html')
+		self.send_header('refresh','5')
+		self.end_headers()
+		self.wfile.write('<HTML><TITLE>Yade-multi overview</TITLE><BODY>')
+		self.wfile.write(globalHtmlStats())
+		self.wfile.write('<TABLE border=1><tr><th>id</th><th>status</th><th>command</th><th>slots</th></tr>')
+		for j in jobs:
+			self.wfile.write(j.htmlStats())
+		self.wfile.write('</TABLE></BODY></HTML>')
+		return
+	def log_request(self,req): pass
+def runHttpStatsServer():
+	try:
+		server=HTTPServer(('',9080),HttpStatsServer)
+		import thread; thread.start_new_thread(server.serve_forever,())
+	except socket.error:
+		print "WARN: Port 9080 occupied, not starting HTTP stats server"
 
+
 def runJob(job):
 	job.status='RUNNING'
 	job.started=time.time();
@@ -33,7 +72,7 @@
 	job.exitStatus=os.system(job.command)
 	job.finished=time.time()
 	dt=job.finished-job.started;
-	job.duration='%02d:%02d:%02d'%(dt//3600,(dt%3600)//60,(dt%60))
+	job.duration=job.t2hhmmss(dt)
 	strStatus='done   ' if job.exitStatus==0 else 'FAILED '
 	job.status='DONE'
 	print "#%d (%s%s) %s (exit status %d), duration %s, log %s"%(job.num,job.id,'' if job.nSlots==1 else '/%d'%job.nSlots,strStatus,job.exitStatus,job.duration,job.log)
@@ -117,8 +156,20 @@
 	print "Will use lines ",', '.join([str(i)+' (%s)'%idStrings[i] for i in useLines])+'.'
 	#print idStrings
 except ValueError:
-	idColumn=None
-	idStrings=None
+	# try to get idStrings from columns with trailing !
+	bangCols=[i for i,h in enumerate(headings) if h[-1]=='!']
+	if len(bangCols)==0:
+		print 'WARN: no headings with trailing !, will use all of them for description'
+		bangCols=range(len(headings))
+	for i in range(len(headings)):
+		if headings[i][-1]=='!': headings[i]=headings[i][:-1]
+	idStrings={}
+	for i in useLines:
+		newIdBase=','.join( headings[col]+'='+('%g'%values[i][col] if isinstance(values[i][col],float) else str(values[i][col])) for col in bangCols)
+		newId=newIdBase; j=1
+		while newId in idStrings.values():
+			newId=newIdBase+'_%d_'%j; j+=1
+		idStrings[i]=newId
 	print "Will use lines ",', '.join([str(i) for i in useLines])+'.'
 
 
@@ -140,6 +191,8 @@
 for job in jobs:
 	print '   #%d (%s%s):'%(job.num,job.id,'' if job.nSlots==1 else '/%d'%job.nSlots),job.command
 
+runHttpStatsServer()
+
 # OK, go now
 if not dryRun: runJobs(jobs,maxJobs)