1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
|
#include <lib/high-precision/Constants.hpp>
#include <pkg/dem/DemXDofGeom.hpp>
#include <pkg/dem/DomainLimiter.hpp>
#include <preprocessing/dem/Shop.hpp>
#include <lib/pyutil/gil.hpp>
#include <lib/smoothing/LinearInterpolate.hpp>
#include <pkg/common/NormShearPhys.hpp>
#include <pkg/dem/DemXDofGeom.hpp>
#include <pkg/dem/L3Geom.hpp>
#include <pkg/dem/ScGeom.hpp>
#ifdef YADE_OPENGL
#include <lib/opengl/GLUtils.hpp>
#include <lib/opengl/OpenGLWrapper.hpp>
#include <pkg/common/GLDrawFunctors.hpp>
#include <pkg/common/OpenGLRenderer.hpp>
#endif
namespace yade { // Cannot have #include directive inside.
using math::max;
using math::min; // using inside .cpp file is ok.
YADE_PLUGIN((DomainLimiter)(LawTester)
#ifdef YADE_OPENGL
(GlExtra_LawTester)(GlExtra_OctreeCubes)
#endif
);
void DomainLimiter::action()
{
std::list<Body::id_t> out;
for (const auto& b : *scene->bodies) {
if ((!b) or ((mask > 0) and ((b->groupMask & mask) == 0)) or b->isClumpMember()) continue;
const Sphere* sphere = dynamic_cast<Sphere*>(b->shape.get());
if (sphere or b->isClump()) { //Delete only spheres and clumps
const Vector3r& p(b->state->pos);
if (p[0] < lo[0] || p[0] > hi[0] || p[1] < lo[1] || p[1] > hi[1] || p[2] < lo[2] || p[2] > hi[2]) {
out.push_back(b->id);
nDeleted++;
mDeleted += b->state->mass;
if (sphere) vDeleted += (4 / 3.) * Mathr::PI * pow(sphere->radius, 3);
}
}
}
FOREACH(Body::id_t id, out) { scene->bodies->erase(id, true /*delete clump members*/); }
}
CREATE_LOGGER(LawTester);
void LawTester::postLoad(LawTester&)
{
if (ids.size() == 0) return; // uninitialized object, don't do nothing at all
if (ids.size() != 2) throw std::invalid_argument("LawTester.ids: exactly two values must be given.");
if (disPath.empty() && rotPath.empty()) throw invalid_argument("LawTester.{disPath,rotPath}: at least one point must be given.");
if (pathSteps.empty()) throw invalid_argument("LawTester.pathSteps: at least one value must be given.");
size_t pathSize = max(disPath.size(), rotPath.size());
// update path points
_path.clear();
_path.push_back(Vector6r::Zero());
for (size_t i = 0; i < pathSize; i++) {
Vector6r pt;
pt.head<3>() = Vector3r(i < disPath.size() ? disPath[i] : (disPath.empty() ? Vector3r::Zero() : *(disPath.rbegin())));
pt.tail<3>() = Vector3r(i < rotPath.size() ? rotPath[i] : (rotPath.empty() ? Vector3r::Zero() : *(rotPath.rbegin())));
_path.push_back(pt);
}
// update time points from distances, repeat last distance if shorter than path
_pathT.clear();
_pathT.push_back(0);
for (size_t i = 0; i < pathSteps.size(); i++)
_pathT.push_back(_pathT[i] + pathSteps[i]);
int lastDist = pathSteps[pathSteps.size() - 1];
for (size_t i = pathSteps.size(); i < pathSize; i++)
_pathT.push_back(*(_pathT.rbegin()) + lastDist);
}
void LawTester::action()
{
if (ids.size() != 2) throw std::invalid_argument("LawTester.ids: exactly two values must be given.");
LOG_DEBUG("=================== LawTester step " << step << " ========================");
const shared_ptr<Interaction> Inew = scene->interactions->find(ids[0], ids[1]);
string strIds("##" + boost::lexical_cast<string>(ids[0]) + "+" + boost::lexical_cast<string>(ids[1]));
// interaction not found at initialization
if (!I && (!Inew || !Inew->isReal())) {
LOG_WARN("Interaction " << strIds << " does not exist (yet?), no-op.");
return;
//throw std::runtime_error("LawTester: interaction "+strIds+" does not exist"+(Inew?" (to be honest, it does exist, but it is not real).":"."));
}
// interaction was deleted meanwhile
if (I && (!Inew || !Inew->isReal()))
throw std::runtime_error("LawTester: interaction " + strIds + " was deleted" + (Inew ? " (is not real anymore)." : "."));
// different interaction object
if (I && Inew && I != Inew) throw std::logic_error("LawTester: interacion " + strIds + " is a different object now?!");
assert(Inew);
bool doInit = (!I);
if (doInit) I = Inew;
id1 = I->getId1();
id2 = I->getId2();
// test object types
GenericSpheresContact* gsc = dynamic_cast<GenericSpheresContact*>(I->geom.get());
ScGeom* scGeom = dynamic_cast<ScGeom*>(I->geom.get());
L3Geom* l3Geom = dynamic_cast<L3Geom*>(I->geom.get());
L6Geom* l6Geom = dynamic_cast<L6Geom*>(I->geom.get());
ScGeom6D* scGeom6d = dynamic_cast<ScGeom6D*>(I->geom.get());
bool hasRot = (l6Geom || scGeom6d);
//NormShearPhys* phys=dynamic_cast<NormShearPhys*>(I->phys.get()); //Disabled because of warning
if (!gsc) throw std::invalid_argument("LawTester: IGeom of " + strIds + " not a GenericSpheresContact.");
if (!scGeom && !l3Geom) throw std::invalid_argument("LawTester: IGeom of " + strIds + " is neither ScGeom, nor L3Geom (or L6Geom).");
assert(!((bool)scGeom && (bool)l3Geom)); // nonsense
// get body objects
State *state1 = Body::byId(id1, scene)->state.get(), *state2 = Body::byId(id2, scene)->state.get();
scene->forces.sync();
if (state1->blockedDOFs != State::DOF_ALL) {
LOG_INFO("Blocking all DOFs for #" << id1);
state1->blockedDOFs = State::DOF_ALL;
}
if (state2->blockedDOFs != State::DOF_ALL) {
LOG_INFO("Blocking all DOFs for #" << id2);
state2->blockedDOFs = State::DOF_ALL;
}
if (step - 1 > *(_pathT.rbegin())) {
LOG_INFO("Last step done, setting zero velocities on #" << id1 << ", #" << id2 << ".");
state1->vel = state1->angVel = state2->vel = state2->angVel = Vector3r::Zero();
uTest = uTestNext;
if (doneHook.empty()) {
LOG_INFO("No doneHook set, dying.");
dead = true;
} else {
LOG_INFO("Running doneHook: " << doneHook);
pyRunString(doneHook);
}
return;
}
/* initialize or update local axes and trsf */
uGeom.tail<3>() = Vector3r(NaN, NaN, NaN);
if (!l3Geom) { // IGeom's that don't have local axes
axX = gsc->normal; /* just in case */
axX.normalize();
if (doInit) { // initialization of the new interaction -- define local axes
// take vector in the y or z direction, depending on its length; arbitrary, but one of them is sure to be non-zero
axY = axX.cross(math::abs(axX[1]) < math::abs(axX[2]) ? Vector3r::UnitY() : Vector3r::UnitZ());
axY.normalize();
axZ = axX.cross(axY);
LOG_DEBUG("Initial axes x=" << axX << ", y=" << axY << ", z=" << axZ);
if (scGeom6d) uGeom.tail<3>() = Vector3r::Zero();
} else { // udpate of an existing interaction
if (scGeom) {
scGeom->rotate(axY);
scGeom->rotate(axZ);
scGeom->rotate(shearTot);
shearTot += scGeom->shearIncrement();
uGeom.head<3>() = Vector3r(-scGeom->penetrationDepth, shearTot.dot(axY), shearTot.dot(axZ));
if (scGeom6d)
uGeom.tail<3>()
= -1. * Vector3r(scGeom6d->getTwist(), scGeom6d->getBending().dot(axY), scGeom6d->getBending().dot(axZ));
} else { // d3dGeom
throw runtime_error("Geom type not yet supported.");
}
}
// update the transformation
// the matrix is orthonormal, since axX, axY are normalized and and axZ is their cross-product
trsf.row(0) = axX;
trsf.row(1) = axY;
trsf.row(2) = axZ;
} else {
trsf = Matrix3r(l3Geom->trsf);
axX = trsf.row(0);
axY = trsf.row(1);
axZ = trsf.row(2);
uGeom.head<3>() = l3Geom->u;
if (l6Geom) uGeom.tail<3>() = l6Geom->phi;
}
// perform all shearing by translation, as it does not induce bending
if (hasRot && rotWeight != 0) {
LOG_INFO("LawTester.rotWeight set to 0 (was " << rotWeight << "), since rotational DoFs are in use.");
rotWeight = 0;
}
contPt = gsc->contactPoint;
refLength = gsc->refR1 + gsc->refR2;
renderLength = .5 * refLength;
// here we go ahead, finally
Vector6r uu = linearInterpolate<Vector6r, int>(step, _pathT, _path, _interpPos);
Vector6r dUU = uu - uuPrev;
uuPrev = uu;
Vector3r dU(dUU.head<3>()), dPhi(dUU.tail<3>());
//Vector3r dU=u-uPrev.head<3>(); uPrev.head<3>()=u;
//Vector3r dPhi=phi-uPrev.tail<3>(); uPrev.tail<3>()=phi;
if (displIsRel) {
LOG_DEBUG("Relative displacement diff is " << dU << " (will be normalized by " << gsc->refR1 + gsc->refR2 << ")");
dU *= refLength;
}
LOG_DEBUG("Absolute diff is: displacement " << dU << ", rotation " << dPhi);
uTest = uTestNext; // the value that was next in the previous step is the current one now
uTestNext.head<3>() += dU;
uTestNext.tail<3>() += dPhi;
// reset velocities where displacement is controlled
//for(int i=0; i<3; i++){ if(forceControl[i]==0){ state1.vel[i]=0; state2.vel[i]=0; }
// shear is applied as rotation of id2: dε=r₁dθ → dθ=dε/r₁;
Vector3r vel[2], angVel[2];
//State* states[]={state1,state2};
for (int i = 0; i < 2; i++) {
int sign = (i == 0 ? -1 : 1);
Real weight = (i == 0 ? 1 - idWeight : idWeight);
// FIXME: this should not use refR1, but real CP-particle distance perhaps?
Real radius = (i == 0 ? gsc->refR1 : gsc->refR2);
Real relRad = radius / refLength;
// signed and weighted displacement/rotation to be applied on this sphere (reversed for #0)
// some rotations must cancel the sign, by multiplying by sign again
Vector3r ddU = sign * dU * weight;
// twist can be still distributed with idWeight (!)
Vector3r ddPhi = sign * dPhi * (1 - relRad); /* shear angles must distribute to both, otherwise it would induce shear */
ddPhi[0] = sign * dPhi[0] * weight; // twist can be still distributed with idWeight
vel[i] = angVel[i] = Vector3r::Zero();
// normal displacement
vel[i] += axX * ddU[0] / scene->dt;
// shear rotation
// multiplication by sign cancels sign in ddU, since rotation is non-symmetric (to increase shear, both spheres have the same rotation)
// (unlike shear displacement, which is symmetric)
// rotation around Z (which gives y-shear) must be inverted: +ry gives +εzm while -rz gives +εy
Real rotZ = -sign * rotWeight * ddU[1] / radius, rotY = sign * rotWeight * ddU[2] / radius;
angVel[i] += (rotY * axY + rotZ * axZ) / scene->dt;
// shear displacement
// angle that is traversed by a sphere in order to give desired ddU when displaced on the branch of r1+r2
// FIXME: is the branch value correct here?!
Real arcAngleY = atan((1 - rotWeight) * ddU[1] / radius), arcAngleZ = atan((1 - rotWeight) * ddU[2] / radius);
// same, but without the atan, which can be disregarded for small increments:
// Real arcAngleY=(1-rotWeight)*ddU[1]/radius, arcAngleZ=(1-rotWeight)*ddU[2]/radius;
vel[i] += axY * radius * sin(arcAngleY) / scene->dt;
vel[i] += axZ * radius * sin(arcAngleZ) / scene->dt;
// compensate distance increase caused by motion along the perpendicular axis
// cos(argAngle*) is always positive, regardless of the orientation
// and the compensation is always in the -εx sense (-sign → +1 for #0, -1 for #1)
vel[i] += -sign * axX * radius * ((1 - cos(arcAngleY)) + (1 - cos(arcAngleZ))) / scene->dt;
// rotation, convert from local to global
angVel[i] += trsf.transpose() * ddPhi;
LOG_DEBUG(
"vel=" << vel[i] << ", angVel=" << angVel[i] << ", rotY,rotZ=" << rotY << "," << rotZ << ", arcAngle=" << arcAngleY << "," << arcAngleZ
<< ", sign=" << sign << ", weight=" << weight);
}
state1->vel = vel[0];
state1->angVel = angVel[0];
state2->vel = vel[1];
state2->angVel = angVel[1];
LOG_DEBUG("Body #" << id1 << ", setting vel=" << vel[0] << ", angVel=" << angVel[0]);
LOG_DEBUG("Body #" << id2 << ", setting vel=" << vel[1] << ", angVel=" << angVel[1]);
/* find out where are we at in the path, run hooks if approriate */
// _pathT has the first (zero) value added by us, so we skip it
int nPathT = _pathT.size();
for (int i = 1; i < nPathT; i++) {
// i-th point on _pathT is (i-1)th on path; run corresponding hook, if it exists
if (step == _pathT[i] && ((int)hooks.size()) > (i - 1) && !hooks[i - 1].empty()) pyRunString(hooks[i - 1]);
}
step++;
}
#ifdef YADE_OPENGL
CREATE_LOGGER(GlExtra_LawTester);
void GlExtra_LawTester::render()
{
// scene object changed (after reload, for instance), for re-initialization
if (tester && tester->scene != scene) tester = shared_ptr<LawTester>();
if (!tester) {
FOREACH(shared_ptr<Engine> e, scene->engines)
{
tester = YADE_PTR_DYN_CAST<LawTester>(e);
if (tester) break;
}
}
if (!tester) {
LOG_ERROR("No LawTester in O.engines, killing myself.");
dead = true;
return;
}
//if(tester->renderLength<=0) return;
glColor3v(Vector3r(1, 0, 1));
// switch to local coordinates
glTranslatev(tester->contPt);
//glMultMatrixd(Eigen::Affine3d(tester->trsf.transpose()).data());
glMultMatrix(Eigen::Transform<Real, 3, Eigen::Affine>(tester->trsf.transpose()).data());
glDisable(GL_LIGHTING);
//glColor3v(Vector3r(1,0,1));
//glBegin(GL_LINES); glVertex3v(Vector3r::Zero()); glVertex3v(.1*Vector3r::Ones()); glEnd();
//GLUtils::GLDrawText(string("This is the contact point!"),Vector3r::Zero(),Vector3r(1,0,1));
// local axes
glLineWidth(2.);
for (int i = 0; i < 3; i++) {
Vector3r pt = Vector3r::Zero();
pt[i] = .5 * tester->renderLength;
Vector3r color = .3 * Vector3r::Ones();
color[i] = 1;
GLUtils::GLDrawLine(Vector3r::Zero(), pt, color);
GLUtils::GLDrawText(string(i == 0 ? "x" : (i == 1 ? "y" : "z")), pt, color);
}
// put the origin to the initial (no-shear) point, so that the current point appears at the contact point
glTranslatev(Vector3r(0, tester->uTestNext[1], tester->uTestNext[2]));
const int t(tester->step);
const vector<int>& TT(tester->_pathT);
const vector<Vector6r>& VV(tester->_path);
size_t numSegments = TT.size();
const Vector3r colorBefore = Vector3r(.7, 1, .7), colorAfter = Vector3r(1, .7, .7);
// scale displacement, if they have the strain meaning
Real scale = 1;
if (tester->displIsRel) scale = tester->refLength;
// find maximum displacement, draw axes in the shear plane
Real displMax = 0;
FOREACH(const Vector6r& v, VV) displMax = max(v.head<3>().squaredNorm(), displMax);
displMax = 1.2 * scale * sqrt(displMax);
glLineWidth(1.);
GLUtils::GLDrawLine(Vector3r(0, -displMax, 0), Vector3r(0, displMax, 0), Vector3r(.5, 0, 0));
GLUtils::GLDrawLine(Vector3r(0, 0, -displMax), Vector3r(0, 0, displMax), Vector3r(.5, 0, 0));
// draw displacement path
glLineWidth(4.);
for (size_t segment = 0; segment < numSegments - 1; segment++) {
// different colors before and after the current point
Real t0 = TT[segment], t1 = TT[segment + 1];
const Vector3r &from = -VV[segment].head<3>() * scale, &to = -VV[segment + 1].head<3>() * scale;
// current segment
if (t > t0 && t < t1) {
Real norm = (t - t0) / (t1 - t0);
GLUtils::GLDrawLine(from, from + (to - from) * norm, colorBefore);
GLUtils::GLDrawLine(from + (to - from) * norm, to, colorAfter);
} else { // other segment
GLUtils::GLDrawLine(from, to, t < t0 ? colorAfter : colorBefore);
}
}
glLineWidth(1.);
}
void GlExtra_OctreeCubes::postLoad(GlExtra_OctreeCubes&)
{
if (boxesFile.empty()) return;
boxes.clear();
std::ifstream txt(boxesFile.c_str());
while (!txt.eof()) {
Real data[8];
for (int i = 0; i < 8; i++) {
if (i < 7 && txt.eof()) goto done;
txt >> data[i];
}
OctreeBox ob;
Vector3r mn(data[0], data[1], data[2]), mx(data[3], data[4], data[5]);
ob.center = .5 * (mn + mx);
ob.extents = (.5 * (mx - mn));
ob.level = (int)data[6];
ob.fill = (int)data[7];
// for(int i=0; i<=ob.level; i++) cerr<<"\t"; cerr<<ob.level<<": "<<mn<<"; "<<mx<<"; "<<ob.center<<"; "<<ob.extents<<"; "<<ob.fill<<endl;
boxes.push_back(ob);
}
done:
std::cerr << "GlExtra_OctreeCubes::postLoad: loaded " << boxes.size() << " boxes." << std::endl;
}
void GlExtra_OctreeCubes::render()
{
FOREACH(const OctreeBox& ob, boxes)
{
if (ob.fill < fillRangeDraw[0] || ob.fill > fillRangeDraw[1]) continue;
if (ob.level < levelRangeDraw[0] || ob.level > levelRangeDraw[1]) continue;
bool doFill = (ob.fill >= fillRangeFill[0] && ob.fill <= fillRangeFill[1] && (ob.fill != 0 || !noFillZero));
// -2: empty
// -1: recursion limit, empty
// 0: subdivided
// 1: recursion limit, full
// 2: full
Vector3r color
= (ob.fill == -2 ? Vector3r(1, 0, 0)
: (ob.fill == -1 ? Vector3r(1, 1, 0)
: (ob.fill == 0 ? Vector3r(0, 0, 1)
: (ob.fill == 1) ? Vector3r(0, 1, 0)
: (ob.fill == 2) ? Vector3r(0, 1, 1)
: Vector3r(1, 1, 1))));
glColor3v(color);
glPushMatrix();
glTranslatev(ob.center);
glScale(2 * ob.extents[0], 2 * ob.extents[1], 2 * ob.extents[2]);
if (doFill) glutSolidCube(1);
else
glutWireCube(1);
glPopMatrix();
}
}
#endif /* YADE_OPENGL */
} // namespace yade
|