Skip to content
Permalink
Browse files

Add new DetectSteadyState

See examples/steady-state.py for an example
  • Loading branch information...
eudoxos committed Aug 23, 2016
1 parent fff3bd1 commit 8223678ae8667c7675edd87be767e7f2d525b801
Showing with 181 additions and 6 deletions.
  1. +62 −0 examples/steady-state.py
  2. +1 −1 pkg/dem/Inlet.cpp
  3. +8 −4 pkg/dem/Inlet.hpp
  4. +5 −1 pkg/dem/Outlet.hpp
  5. +66 −0 pkg/dem/SteadyState.cpp
  6. +39 −0 pkg/dem/SteadyState.hpp
@@ -0,0 +1,62 @@
import woo.core, woo.dem
from minieigen import *
woo.master.usesApi=10103

# material we will use for both boundary and particles
m0=woo.dem.FrictMat(density=3000,young=1e5)
# period for running engines which only run sometimes
per=100
# inlet is producing particles
inlet=woo.dem.BoxInlet(
box=((-.03,-.03,.1),(.03,.03,.15)), # volume of inlet
massRate=.5, # mass rate at which particles are being generated
materials=[m0],
generator=woo.dem.PsdSphereGenerator(psdPts=[(.005,0),(.01,1)]),
stepPeriod=per,
glColor=.9,
)
# outlet defines domain where particles will be deleted
outlet=woo.dem.BoxOutlet(
box=((-.06,-.06,-0.01),(.06,.06,.15)),
inside=False,
stepPeriod=per, # delete what is outside
currRateSmooth=.5, # smoothing factor to make fluctuations smaller (smoothed value is consumed by DetectSteadyState)
glColor=.5,
)
# detector of steady state
steady=woo.dem.DetectSteadyState(
inlets=[inlet], # one inlet here, but could be more (sum of fluxes is influx)
outlets=[outlet], # one outlet here, but could be more (sum of fluxes is efflux)
relFlow=1., # wait for 1.*efflux >= influx, before entering 'trans' stage
hookTrans='print("hookTrans")', # do this when 'trans' is entered
waitTrans=.5, # stay .5s in 'trans' stage, before entering steady stage
hookSteady='print("hookSteady")', # do this when 'stead' is entered
waitSteady=1, # stay 1s in 'steady' stage
hookDone='S.stop(); engine.dead; print("hookDone, stopping")', # do this when 'done' is entered (steady finished)
stepPeriod=per, # run only every *per* steps, just like inlets and outlets
rateSmooth=.9, # if defined, apply extra smoothing to influx and efflux values (not really necessary)
label='steady',
)
# scene object
S=woo.master.scene=woo.core.Scene(
fields=[woo.dem.DemField(gravity=(0,0,-10),
# cylinder with open top (cup-shaped) to hold some particles
par=woo.triangulated.cylinder(Vector3(0,0,0),Vector3(0,0,.05),.05,mat=m0,capA=True,capB=False)
)],
# usual engine setup, adding inlet, outlet, steady detector and plotting
engines=woo.dem.DemField.minimalEngines(damping=.3)+[
inlet,outlet,steady,woo.core.PyRunner(per,'S.plot.autoData()')
]
)
# what to plot
S.plot.plots={'t=S.time':('influx=S.lab.steady.influx','efflux=S.lab.steady.efflux',None,'stageNum=S.lab.steady.stageNum')}
# color particles by velocity
S.gl.demField.colorBy='velocity'
S.gl.demField.colorRange.mnmx=(0,.5)
# save for reloading
S.saveTmp()
# let's go
import woo.qt
woo.qt.View()
S.run()
S.plot.plot()
@@ -17,7 +17,7 @@

WOO_PLUGIN(dem,(Inlet)(ParticleGenerator)(MinMaxSphereGenerator)(ParticleShooter)(AlignedMinMaxShooter)(RandomInlet)(BoxInlet)(BoxInlet2d)(CylinderInlet)(ArcInlet)(ArcShooter)(SpatialBias)(AxialBias)(PsdAxialBias)(LayeredAxialBias)(NonuniformAxisPlacementBias));

WOO_IMPL__CLASS_BASE_DOC_ATTRS(woo_dem_Inlet__CLASS_BASE_DOC_ATTRS);
WOO_IMPL__CLASS_BASE_DOC_ATTRS_PY(woo_dem_Inlet__CLASS_BASE_DOC_ATTRS_PY);
WOO_IMPL__CLASS_BASE_DOC_ATTRS_PY(woo_dem_ParticleGenerator__CLASS_BASE_DOC_ATTRS_PY);
WOO_IMPL__CLASS_BASE_DOC_ATTRS(woo_dem_MinMaxSphereGenerator_CLASS_BASE_DOC_ATTRS);
WOO_IMPL__CLASS_BASE_DOC(woo_dem_ParticleShooter__CLASS_BASE_DOC);
@@ -2,6 +2,8 @@
#include<woo/pkg/dem/Particle.hpp>
#include<boost/range/numeric.hpp>
#include<boost/range/algorithm/fill.hpp>
#include<woo/lib/pyutil/converters.hpp>



#ifdef WOO_OPENGL
@@ -23,8 +25,8 @@ struct Inlet: public PeriodicEngine{
#ifdef WOO_OPENGL
void renderMassAndRate(const Vector3r& pos);
#endif
#define woo_dem_Inlet__CLASS_BASE_DOC_ATTRS \
Inlet,PeriodicEngine,ClassTrait().doc("Inlet generating new particles. This is an abstract base class which in itself does not generate anything, but provides some unified interface to derived classes.").section("Inlets & Outlets","TODO",{"ParticleGenerator","SpatialBias","ParticleShooter","Outlet"}), \
#define woo_dem_Inlet__CLASS_BASE_DOC_ATTRS_PY \
Inlet,PeriodicEngine,ClassTrait().doc("Inlet generating new particles. This is an abstract base class which in itself does not generate anything, but provides some unified interface to derived classes.").section("Inlets & Outlets","TODO",{"ParticleGenerator","SpatialBias","ParticleShooter","Outlet","DetectSteadyState"}), \
((int,mask,((void)":obj:`DemField.defaultInletMask`",DemField::defaultInletMask),,":obj:`~woo.dem.Particle.mask` for new particles.")) \
((Real,maxMass,-1,AttrTrait<>().massUnit(),"Mass at which the engine will not produce any particles (inactive if not positive)")) \
((long,maxNum,-1,,"Number of generated particles after which no more will be produced (inactive if not positive)")) \
@@ -34,9 +36,11 @@ struct Inlet: public PeriodicEngine{
((Real,currRate,NaN,AttrTrait<>().readonly().massRateUnit(),"Current value of mass flow rate")) \
((bool,zeroRateAtStop,true,,"When the generator stops (mass/number of particles reached, ...), set :obj:`currRate` to zero immediately")) \
((Real,currRateSmooth,1,AttrTrait<>().range(Vector2r(0,1)),"Smoothing factor for currRate ∈〈0,1〉")) \
((Real,glColor,0,AttrTrait<>().noGui(),"Color for rendering (nan disables rendering)"))
((Real,glColor,0,AttrTrait<>().noGui(),"Color for rendering (nan disables rendering)")) \
, /*py*/ ; woo::converters_cxxVector_pyList_2way<shared_ptr<Inlet>>(); // converter needed for DetectSteadyState


WOO_DECL__CLASS_BASE_DOC_ATTRS(woo_dem_Inlet__CLASS_BASE_DOC_ATTRS);
WOO_DECL__CLASS_BASE_DOC_ATTRS_PY(woo_dem_Inlet__CLASS_BASE_DOC_ATTRS_PY);
};
WOO_REGISTER_OBJECT(Inlet);

@@ -1,6 +1,8 @@
#pragma once
#include<woo/core/Engine.hpp>
#include<woo/pkg/dem/Particle.hpp>
#include<woo/lib/pyutil/converters.hpp>


#ifdef WOO_OPENGL
#include<woo/lib/opengl/GLUtils.hpp>
@@ -44,7 +46,9 @@ struct Outlet: public PeriodicEngine{
.def("clear",&Outlet::pyClear,"Clear information about saved particles (particle list, if saved, mass and number, rDivR0)") \
.def("diamMass",&Outlet::pyDiamMass,(py::arg("zipped")=false),"With *zipped*, return list of (diameter, mass); without *zipped*, return tuple of 2 arrays, diameters and masses.") \
.def("diamMassTime",&Outlet::pyDiamMassTime,(py::arg("zipped")=false),"With *zipped*, return list of (diameter, mass, time); without *zipped*, return tuple of 3 arrays: diameters, masses, times.") \
.def("massOfDiam",&Outlet::pyMassOfDiam,(py::arg("min")=0,py::arg("max")=Inf),"Return mass of particles of which diameters are between *min* and *max*.")
.def("massOfDiam",&Outlet::pyMassOfDiam,(py::arg("min")=0,py::arg("max")=Inf),"Return mass of particles of which diameters are between *min* and *max*."); \
woo::converters_cxxVector_pyList_2way<shared_ptr<Outlet>>(); // converter needed for DetectSteadyState


WOO_DECL__CLASS_BASE_DOC_ATTRS_PY(woo_dem_Outlet__CLASS_BASE_DOC_ATTRS_PY);
};
@@ -0,0 +1,66 @@
#include<woo/pkg/dem/SteadyState.hpp>

WOO_PLUGIN(dem,(DetectSteadyState));
WOO_IMPL__CLASS_BASE_DOC_ATTRS_PY(woo_dem_DetectSteadyState__CLASS_BASE_DOC_ATTRS_PY);
WOO_IMPL_LOGGER(DetectSteadyState);

void DetectSteadyState::run(){
// compute smoothed flows in whatever the stage is
Real sumInflux=0, sumEfflux=0;
for(const auto& i: inlets) sumInflux+=i->currRate;
for(const auto& o: outlets) sumEfflux+=o->currRate;
if(stepPrev<0 || isnan(influx) || isnan(rateSmooth)) influx=sumInflux;
else influx=(1-rateSmooth)*influx+rateSmooth*sumInflux;
if(stepPrev<0 || isnan(efflux) || isnan(rateSmooth)) efflux=sumEfflux;
else efflux=(1-rateSmooth)*efflux+rateSmooth*sumEfflux;
// if run for the very first time, set time
if(stepPrev<0) stageEntered=scene->time;
switch(stage){
case STAGE_INIT: {
if(scene->time-stageEntered<=waitInit) return;
LOG_INFO("Entering 'flow' stage at "<<scene->time);
stage=STAGE_FLOW;
stageEntered=scene->time;
if(!hookFlow.empty()){
LOG_DEBUG("Executing hookFlow: "<<hookFlow);
runPy("DetectSteadyState.hookFlow",hookFlow);
}
break;
}
case STAGE_FLOW: {
if(efflux<influx*relFlow) return;
LOG_INFO("Entering 'trans' stage at "<<scene->time);
stage=STAGE_TRANS;
stageEntered=scene->time;
if(!hookTrans.empty()){
LOG_DEBUG("Executing hookTrans: "<<hookTrans);
runPy("DetectSteadyState.hookTrans",hookTrans);
}
break;
}
case STAGE_TRANS: {
if(scene->time-stageEntered<=waitTrans) return;
LOG_INFO("Entering 'steady' stage at "<<scene->time);
stage=STAGE_STEADY;
stageEntered=scene->time;
if(!hookSteady.empty()){
LOG_DEBUG("Executing hookSteady: "<<hookSteady);
runPy("DetectSteadyState.hookSteady",hookSteady);
}
break;
}
case STAGE_STEADY: {
if(scene->time-stageEntered<=waitSteady) return;
LOG_INFO("Entering 'done' stage at "<<scene->time);
stage=STAGE_DONE;
stageEntered=scene->time;
if(!hookDone.empty()){
LOG_DEBUG("Executing hookDone: "<<hookDone);
runPy("DetectSteadyState.hookDone",hookDone);
}
break;
}
case STAGE_DONE: return;
default: throw std::logic_error("DetectSteadyState.stage: invalid value "+to_string(stage)+" (please report bug).");
}
}
@@ -0,0 +1,39 @@
#pragma once

#include<woo/pkg/dem/Inlet.hpp>
#include<woo/pkg/dem/Outlet.hpp>

struct DetectSteadyState: public PeriodicEngine{
WOO_DECL_LOGGER;
enum{STAGE_INIT=0,STAGE_FLOW,STAGE_TRANS,STAGE_STEADY,STAGE_DONE};
void run() override;
#define woo_dem_DetectSteadyState__CLASS_BASE_DOC_ATTRS_PY \
DetectSteadyState,PeriodicEngine,ClassTrait().doc("Detect steady state from summary flows of relevant inlets (total influx) and outlets (total efflux), plus waiting times in-between." \
"The detection is done is several stages:\n\n" \
"1. (init) wait for :obj:`waitInit` before doing anything else;\n" \
"2. (flow) execute :obj:`hookFlow`, check if :math:`\\sum_i o_i \\geq \\alpha \\sum_j i_j` (:math:`\\alpha` is :obj:`relFlow`); if true, proto-steady state was reached and we proceed;\n" \
"3. (trans) execute :obj:`hookTrans`, then wait for :obj:`waitTrans`, then proceed to the next stage;\n" \
"4. (steady) executed :obj:`hookSteady`, then wait for :obj:`waitSteady`, then proceed;\n" \
"5. (done) execute :obj:`hookDone` and do nothing more.\n\n"), \
((Real,waitInit,0,AttrTrait<>().timeUnit(),"Time to wait in ``init`` stage, before moving to ``flow``.")) \
((Real,relFlow,1.,AttrTrait<>().timeUnit(),"Relative flow used for comparing influx and efflux in the ``flow`` stage.")) \
((Real,waitTrans,0,AttrTrait<>().timeUnit(),"Time to wait in ``trans`` stage, before moving to ``steady``.")) \
((Real,waitSteady,0,AttrTrait<>().timeUnit(),"Time to wait in the steady stage, before moving to ``done``.")) \
((string,hookFlow,"",,"Hook executed when the ``flow`` stage is entered.")) \
((string,hookTrans,"",,"Hook executed when the ``trans`` stage is entered.")) \
((string,hookSteady,"",,"Hook executed when the ``steady`` stage is entered.")) \
((string,hookDone,"e.dead=True",,"Hook executed when the ``done`` stage is entered.")) \
((Real,rateSmooth,1,AttrTrait<>().range(Vector2r(0,1)),"Smoothing factor for rates ∈〈0,1〉")) \
((int,stage,STAGE_INIT,AttrTrait<Attr::namedEnum>().readonly().namedEnum({{STAGE_INIT,{"init"}},{STAGE_FLOW,{"flow"}},{STAGE_TRANS,{"trans"}},{STAGE_STEADY,{"steady"}},{STAGE_DONE,{"done"}}}),"Stage in which we currently are.")) \
((Real,stageEntered,0.,AttrTrait<>().readonly().timeUnit(),"Time when the current stage was entered.")) \
((vector<shared_ptr<Inlet>>,inlets,,,"Inlets of which rates are used to compute summary influx.")) \
((vector<shared_ptr<Outlet>>,outlets,,,"Inlets of which rates are used to compute summary efflux.")) \
((Real,influx,NaN,AttrTrait<>().readonly(),"Smoothed summary influx value.")) \
((Real,efflux,NaN,AttrTrait<>().readonly(),"Smoothed summary efflux value.")) \
,/*py*/ .def_readonly("stageNum",&DetectSteadyState::stage,"Return the current :obj:`stage` as numerical value (useful for plotting).");

WOO_DECL__CLASS_BASE_DOC_ATTRS_PY(woo_dem_DetectSteadyState__CLASS_BASE_DOC_ATTRS_PY);
};
WOO_REGISTER_OBJECT(DetectSteadyState);


0 comments on commit 8223678

Please sign in to comment.
You can’t perform that action at this time.