SimpleSceneCpp

From Yade

This preprocessor, once compiled, can be run from the Preprocessor menu. It will appear as SimpleScene.

Please note that comments are written for the python version SimpleScenePython and at some places are not completely pertinent.

The class is declared in the header:

<source lang="cpp">

  1. pragma once
  2. include<yade/core/FileGenerator.hpp>
  3. include<yade/core/PhysicalAction.hpp>
  4. include<yade/extra/Shop.hpp>


class SimpleScene: public FileGenerator { public: SimpleScene(){}; ~SimpleScene (){}; virtual bool generate(); protected : void registerAttributes(){ FileGenerator::registerAttributes(); } REGISTER_CLASS_NAME(SimpleScene); REGISTER_BASE_CLASS_NAME(FileGenerator); DECLARE_LOGGER; }; REGISTER_SERIALIZABLE(SimpleScene,false); </source>

Now for the working code:

<source lang="cpp">

  1. include"SimpleScene.hpp"
  2. include<yade/extra/Shop.hpp>
  3. include<yade/pkg-dem/BodyMacroParameters.hpp>
  4. include<yade/pkg-common/InteractingSphere2AABB.hpp>
  5. include<yade/pkg-common/InteractingBox2AABB.hpp>
  6. include<yade/pkg-common/MetaInteractingGeometry.hpp>
  7. include<yade/pkg-common/MetaInteractingGeometry2AABB.hpp>
  8. include<yade/pkg-common/PhysicalActionContainerReseter.hpp>
  9. include<yade/pkg-common/PhysicalActionContainerInitializer.hpp>
  10. include<yade/pkg-common/PhysicalParametersMetaEngine.hpp>
  11. include<yade/pkg-common/AABB.hpp>
  12. include<yade/pkg-common/Box.hpp>
  13. include<yade/pkg-common/Sphere.hpp>
  14. include<yade/pkg-common/InteractingBox.hpp>
  15. include<yade/pkg-common/Momentum.hpp>
  16. include<yade/pkg-common/Force.hpp>
  17. include<yade/pkg-common/NewtonsForceLaw.hpp>
  18. include<yade/pkg-common/NewtonsMomentumLaw.hpp>
  19. include<yade/pkg-common/LeapFrogPositionIntegrator.hpp>
  20. include<yade/pkg-common/LeapFrogOrientationIntegrator.hpp>
  21. include<yade/pkg-dem/InteractingSphere2InteractingSphere4SpheresContactGeometry.hpp>
  22. include<yade/pkg-dem/InteractingBox2InteractingSphere4SpheresContactGeometry.hpp>
  23. include<yade/pkg-common/PhysicalActionContainerInitializer.hpp>
  24. include<yade/pkg-common/PhysicalParametersMetaEngine.hpp>
  25. include<yade/pkg-common/InteractionGeometryMetaEngine.hpp>
  26. include<yade/pkg-common/InteractionPhysicsMetaEngine.hpp>
  27. include<yade/pkg-common/BoundingVolumeMetaEngine.hpp>
  28. include<yade/pkg-common/PhysicalActionDamper.hpp>
  29. include<yade/pkg-common/PhysicalActionApplier.hpp>
  30. include<yade/pkg-common/CundallNonViscousForceDamping.hpp>
  31. include<yade/pkg-common/CundallNonViscousMomentumDamping.hpp>
  32. include<yade/pkg-common/GravityEngine.hpp>
  33. include<yade/pkg-dem/ElasticContactLaw.hpp>
  34. include<yade/pkg-dem/SpheresContactGeometry.hpp>
  35. include<yade/pkg-dem/SimpleElasticRelationships.hpp>
  36. include<yade/pkg-common/PersistentSAPCollider.hpp>


YADE_PLUGIN("SimpleScene");

bool SimpleScene::generate(){         message=""; </source> Omega is the super-class that orchestrates the whole program. It holds the entire simulation (MetaBody), takes care of loading/saving, starting/stopping the simulation, loads plugins and more. <source lang="cpp">         rootBody=Shop::rootBody(); </source> Initializers are run before the simulation. <source lang="cpp">         /* initializers */                 rootBody->initializers.clear(); </source> Create and reset to zero container of all PhysicalActions that will be used <source lang="cpp">                 rootBody->initializers.push_back(shared_ptr<PhysicalActionContainerInitializer>(new PhysicalActionContainerInitializer)); </source> Create bounding boxes. They are needed to zoom the 3d view properly before we start the simulation. <source lang="cpp">                 shared_ptr<BoundingVolumeMetaEngine> boundingVolumeDispatcher        = shared_ptr<BoundingVolumeMetaEngine>(new BoundingVolumeMetaEngine);                         boundingVolumeDispatcher->add(new InteractingSphere2AABB);                         boundingVolumeDispatcher->add(new InteractingBox2AABB);                         boundingVolumeDispatcher->add(new MetaInteractingGeometry2AABB);                         rootBody->initializers.push_back(boundingVolumeDispatcher); </source> Engines are called consecutively at each iteration. Their order matters.

Some of them work by themselves (StandAloneEngine, DeusExMachina - the difference of these two is unimportant).

MetaEngines act as dispatchers and based on the type of objects they operate on, different EngineUnits are called. <source lang="cpp">         /* engines */                 rootBody->engines.clear(); </source> Resets forces and momenta the act on bodies <source lang="cpp">                 rootBody->engines.push_back(shared_ptr<Engine>(new PhysicalActionContainerReseter)); </source> associates bounding volume - in this case, AxisAlignedBoundingBox (AABB) - to each body. MetaEngine calls corresponding EngineUnit, depending on whether the body is Sphere, Box, or MetaBody (rootBody). AABBs will be used to detect collisions later, by PersistentSAPCollider <source lang="cpp">                 // use boundingVolumeDispatcher that we defined above                 rootBody->engines.push_back(boundingVolumeDispatcher); </source> Using bounding boxes created by the previous engine, find possible body collisions. These possible collisions are inserted in Omega.interactions container (MetaBody::transientInteractions in c++). <source lang="cpp">                 shared_ptr<PersistentSAPCollider> collider(new PersistentSAPCollider);                         rootBody->engines.push_back(collider); </source> Decide whether the potential collisions are real; if so, create geometry information about each potential collision. Here, the decision about which EngineUnit to use depends on types of _both_ bodies. Note that there is no EngineUnit for box-box collision. They are not implemented. <source lang="cpp">                 shared_ptr<InteractionGeometryMetaEngine> igeomDispatcher(new InteractionGeometryMetaEngine);                         igeomDispatcher->add(new InteractingSphere2InteractingSphere4SpheresContactGeometry);                         igeomDispatcher->add(new InteractingBox2InteractingSphere4SpheresContactGeometry);                         rootBody->engines.push_back(igeomDispatcher); </source> Create physical information about the interaction. This may consist in deriving contact rigidity from elastic moduli of each body, for example. The purpose is that the contact may be "solved" without reference to related bodies, only with the information contained in contact geometry and physics. <source lang="cpp">                 shared_ptr<InteractionPhysicsMetaEngine> iphysDispatcher(new InteractionPhysicsMetaEngine);                         iphysDispatcher->add(new SimpleElasticRelationships);                         rootBody->engines.push_back(iphysDispatcher); </source> "Solver" of the contact, also called (consitutive) law. Based on the information in interaction physics and geometry, it applies corresponding forces on bodies in interaction. <source lang="cpp">                 shared_ptr<ElasticContactLaw> ecl(new ElasticContactLaw);                         rootBody->engines.push_back(ecl); </source> Apply gravity: all bodies will have gravity applied on them. Note the engine parameter 'gravity', a vector that gives the acceleration. <source lang="cpp">                 shared_ptr<GravityEngine> ge(new GravityEngine);                         ge->gravity=Vector3r(0,0,-9.81);                         rootBody->engines.push_back(ge); </source> Forces acting on bodies are damped to artificially increase energy dissipation in simulation. (In this model, the restitution coefficient of interaction is 1, which is not realistic.) This MetaEngine acts on all PhysicalActions and selects the right EngineUnit base on type of the PhysicalAction. <source lang="cpp">                 shared_ptr<PhysicalActionDamper> dampingDispatcher(new PhysicalActionDamper);                         shared_ptr<CundallNonViscousForceDamping> forceDamper(new CundallNonViscousForceDamping);                         forceDamper->damping=0.2;                         dampingDispatcher->add(forceDamper);                         shared_ptr<CundallNonViscousMomentumDamping> momentumDamper(new CundallNonViscousMomentumDamping);                         momentumDamper->damping=0.2;                         dampingDispatcher->add(momentumDamper);                         rootBody->engines.push_back(dampingDispatcher); </source> Now we have forces and momenta acting on bodies. Newton's law calculates acceleration that corresponds to them. <source lang="cpp">                 shared_ptr<PhysicalActionApplier> applyActionDispatcher(new PhysicalActionApplier);                         applyActionDispatcher->add(new NewtonsForceLaw);                         applyActionDispatcher->add(new NewtonsMomentumLaw);                         rootBody->engines.push_back(applyActionDispatcher); </source> Acceleration results in velocity change. Integrating the velocity over dt, position of the body will change. <source lang="cpp">                 shared_ptr<PhysicalParametersMetaEngine> positionIntegrator(new PhysicalParametersMetaEngine);                         positionIntegrator->add(new LeapFrogPositionIntegrator);                         rootBody->engines.push_back(positionIntegrator); </source> Angular acceleration changes angular velocity, resulting in position and/or orientation change of the body. <source lang="cpp">                 shared_ptr<PhysicalParametersMetaEngine> orientationIntegrator(new PhysicalParametersMetaEngine);                         orientationIntegrator->add(new LeapFrogOrientationIntegrator);                         rootBody->engines.push_back(orientationIntegrator); </source> The yade.utils module contains some handy functions, like yade.utils.box and yade.utils.sphere. After this import, they will be accessible as utils.box and utils.sphere. <source lang="cpp">         // set default values for Shop                 Shop::setDefault("body_sdecGroupMask",1);         Shop::setDefault("phys_density",2400.); Shop::setDefault("phys_young",30e9); Shop::setDefault("phys_poisson",.3);         Shop::setDefault("aabb_randomColor",false);Shop::setDefault("shape_randomColor",false); Shop::setDefault("mold_randomColor",false);         Shop::setDefault("aabb_color",Vector3r(0,1,0)); Shop::setDefault("shape_color",Vector3r(1,0,0)); Shop::setDefault("mold_color",Vector3r(1,0,0)); </source> create bodies in the simulation: one box in the origin and one floating above it.

The box:

  • extents: half-size of the box. [.5,.5,.5] is unit cube
  • center: position of the center of the box
  • dynamic: it is not dynamic, i.e. will not move during simulation, even if forces are applied to it
  • color: for the 3d display; specified within unit cube in the RGB space; [1,0,0] is, therefore, red
  • young: Young's modulus
  • poisson: Poissons's ratio

<source lang="cpp">         shared_ptr<Body> box=Shop::box(Vector3r(0,0,0),Vector3r(.5,.5,.5));         box->isDynamic=false;         rootBody->bodies->insert(box);          </source> The above command could be actully written without the util.box function like this: (will not be executed, since the condition is never True) <source lang="cpp">         if(false){                 Vector3r extents(.5,.5,.5);                 shared_ptr<Body> b=shared_ptr<Body>(new Body(body_id_t(0),0));                 b->isDynamic=true;                                  // phys                 shared_ptr<BodyMacroParameters> physics(new BodyMacroParameters);                 physics->mass=8*extents[0]*extents[1]*extents[2]*2400;                 physics->inertia=Vector3r(physics->mass*(4*extents[1]*extents[1]+4*extents[2]*extents[2])/12.,physics->mass*(4*extents[0]*extents[0]+4*extents[2]*extents[2])/12.,physics->mass*(4*extents[0]*extents[0]+4*extents[1]*extents[1])/12.);                 physics->se3=Se3r(Vector3r(0,0,0),Quaternionr::IDENTITY);                 physics->young=30e9;                 physics->poisson=.3;                 b->physicalParameters=physics;

                // aabb                 shared_ptr<AABB> aabb(new AABB);                 aabb->diffuseColor=Vector3r(0,1,0);                 b->boundingVolume=aabb;

                //shape                 shared_ptr<Box> shape(new Box);                 shape->extents=extents;                 shape->diffuseColor=Vector3r(1,0,0);                 b->geometricalModel=shape;

                // mold                 shared_ptr<InteractingBox> mold(new InteractingBox);                 mold->extents=extents;                 mold->diffuseColor=Vector3r(1,0,0);                 b->interactingGeometry=mold;

                rootBody->bodies->insert(b);         } </source> The sphere

  • First two arguments are radius and center, respectively. They are used as "positional arguments" here:

python will deduce based on where they are what they mean.

It could also be written without using utils.sphere - see gui/py/utils.py for the code of the utils.sphere function <source lang="cpp">         Shop::setDefault("shape_color",Vector3r(0,1,0)); Shop::setDefault("mold_color",Vector3r(0,1,0));         shared_ptr<Body> sphere(Shop::sphere(Vector3r(0,0,2),1));         rootBody->bodies->insert(sphere);          </source> Estimate timestep from p-wave speed and multiply it by safety factor of .2 <source lang="cpp">         rootBody->dt=.2*Shop::ElasticWaveTimestepEstimate(rootBody); </source> Save the scene to file, so that it can be loaded later. Supported extension are: .xml, .xml.gz, .xml.bz2. <source lang="cpp">         return true; } </source>