Motion integration is performed by the
Leapfrog engine and described in detail in Motion integration. Usually, each node moves according to force, which results in acceleration, velocity change and position change; nodes rotate according to torque, which results in angular acceleration, angular velocity change and orientation change.
It is sometimes useful to prescribe some other kind of motion – e.g. force, velocity or trajectory.
Motion integration is performed on
nodes, which are usually attached to
particles. There are also nodes not attached to any particle but used by some engines (e.g.
Conveyor); those nodes, if added S.dem.nodes (usually via
S.dem.nodesAppend), will move just the same. This will be shown below.
Blocking degrees of freedom (DoFs) is done via the
fixed parameter to routines creating particles (
Sphere.make and friends). This parameter simply sets
Facet are by default
fixed (they will not move, even if force is applied to them); in contrast to that, particles like
Capsule are by default not fixed.
DemData.blocked is in itself more versatile; it can block forces/torques along individual axes; translations are noted as
xyz (lowercase) and rotations
XYZ (uppercase). For instance
to prevent particle from rotating, set
for 2d simulation in the \(xy\)-plane, set
zXY(prevent out-of-plane translation and only allow rotation along the axis perpendicular to \(xy\) plane).
blocked does not make the node necessarily unmovable. Since forces/torques along that axis are ignored, it only means that
angular velocity will be constant. Those are initially zero, but if you set them to a non-zero value, the node will be moving.
Woo: s=Sphere.make((0,0,1),.1) # see what is blocked on the sphere Woo: s.blocked Out: '' Woo: s.blocked='XYZ' # did it really change? Woo: s.blocked Out: 'XYZ' Woo: w=Wall.make(0,axis=2) # see what is blocked on the wall by default Woo: w.blocked Out: 'xyzXYZ' # Particle.blocked is only a shorthand for particles with a single node # it really means this: Woo: w.nodes.dem.blocked Out: 'xyzXYZ' # DemData is what carries all DEM-related properties (velocity, mass, inertia and such) Woo: w.nodes.dem Out: <DemData @ 0x30f43b0> # let's dump it to see what's inside there Woo: w.nodes.dem.dumps(format='expr') Out: '##woo-expression##\n#: import woo.dem\nwoo.dem.DemData(\n\tvel=(0, 0, 0),\n\tangVel=(0, 0, 0),\n\tmass=0,\n\tinertia=(0, 0, 0),\n\tforce=(0, 0, 0),\n\ttorque=(0, 0, 0),\n\tangMom=(nan, nan, nan),\n\tflags=63,\n\tlinIx=-1,\n\timpose=None\n)\n'
A more versatile option is to use the
DemData.impose to assign any object deriving from
woo.dem.Impose. What is imposed in each step is either force or velocity, though those are not constrained to global coordinate axes, and are computed by some c++ routine.
Impose instances can be shared by many nodes.
This example shows the
woo.dem.AlignedHarmonicOscillations imposition, which is applied to all cylinders with different parameters:
import woo from woo import utils,pack from woo.dem import * from woo.core import * from minieigen import * woo.master.scene=S=Scene(fields=[DemField(gravity=(0,0,-10))]) mat=utils.defaultMaterial() sp=pack.SpherePack() sp.makeCloud((4,4,4),(14,14,14),.4,rRelFuzz=.5) sp.toSimulation(S,mat=mat) S.dem.par.add([utils.wall(1,axis=2,sense=0,mat=mat,glAB=((-10,-1),(20,11))),]) S.periodic=True S.cell.setBox(20,20,20) S.engines=DemField.minimalEngines(damping=.4) S.dtSafety=.5 S.dt=.5*utils.pWaveDt() # to compute oscillation freqs below # create cylinders for i,x in enumerate([-2,0,2,4,6,8,10.5,12,14]): c=InfCylinder.make((x,0,3),radius=.8,axis=1,mat=mat,glAB=(-1,11)) c.angVel=(0,2.*(i+1),0) # each of cylinders will move haronically along global x and z axes (not y) c.impose=AlignedHarmonicOscillations(freqs=(1./(10000.*S.dt),float('nan'),1/(((i%3)+3)*1000.*S.dt)),amps=(.3*(i%2+1),0,.4*(i%4+1))) S.dem.par.add(c) S.saveTmp() try: from woo import gl gl.Gl1_Wall.div=10 gl.Gl1_InfCylinder.wire=True except ImportError: pass
woo.dem.InterpolatedMotion was used in this example to move the bottle (using Video, internally
woo.qt.SnapshotEngine); since we needed to move the whole bottle as an aggregate, it was added as a clump (
The same simulation visualized in Paraview using the
from woo.core import * from woo.dem import * import woo, math from minieigen import * # use the same material for both capsules and boundaries, for simplicity capsMat=wallMat=woo.dem.FrictMat(ktDivKn=.2,tanPhi=.2,density=1000,young=1e7) # create the scene object, set the master scene S=woo.master.scene=Scene(fields=[DemField(gravity=(0,0,-10))]) # add the bottom plane (wall) S.dem.par.add(Wall.make(0,axis=2,sense=1,mat=wallMat,color=0,glAB=((-.2,-.2),(.2,.2)))) # create bottle mesh from the STL bottle=woo.utils.importSTL('pill-bottle.coarse2.stl',mat=wallMat,scale=0.001,shift=(.056,.027,-0.01),ori=Quaternion((1,0,0),math.pi/2.),color=-.25) # create node which will serve as "handle" to move the bottle S.lab.botNode=Node(pos=(0,0,.04),dem=ClumpData(blocked='xyzXYZ')) # add bottle as clump; # center is the centroid normally, but the mesh has no mass, thus reference point must be given S.dem.par.addClumped(bottle,centralNode=S.lab.botNode) S.dtSafety=.5 import woo.gl woo.gl.Renderer.engines=False woo.gl.Renderer.allowFast=False # particle factory, with many parameters # when the factory finishes, it will call the pillsDone function, defined below factory=CylinderInlet(stepPeriod=100,node=Node(pos=(0,0,.17),ori=Quaternion((0,1,0),math.pi/2.)),radius=.018,height=.05,generator=PharmaCapsuleGenerator(),materials=[capsMat],massRate=0,maxMass=.12,label='feed',attemptPar=100,color=-1,doneHook='pillsDone(S)') # add factory (and optional Paraview export) to engines S.engines=DemField.minimalEngines(damping=.3)+[ factory, ## comment out to enable export for Paraview: VtkExport(out='/tmp/bottle.',ascii=True,stepPeriod=500) ] # save the scene S.saveTmp() def pillsDone(S): # once the bottle is full, wait for another 0.2s #then start moving the bottle by interpolating prescribed positions and orientations S.lab.botNode.dem.impose=InterpolatedMotion(t0=S.time+0.2,poss=[S.lab.botNode.pos,(0,.05,.08),(0,.05,.09),(0,.04,.13)],oris=[Quaternion.Identity,((1,0,0),.666*math.pi),((1,0,0),.85*math.pi),((1,0,0),.9*math.pi)],times=[0,.3,.5,1.6])
woo.dem.CircularOrbit and how to use it on meshes (like mixer blades), be those clumped or not.
Report issues or inclarities to github.