CellModules
PluginHertzianPhysics.hpp
Go to the documentation of this file.
1#ifndef PLUGINBODYHERTZIANPHYSICS_HPP
2#define PLUGINBODYHERTZIANPHYSICS_HPP
3
11#include <vector>
12#include <mecacell/mecacell.h>
13#include <math.h>
14#include <chrono>
15#include "Tensor.hpp"
17
18#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
19#include <CGAL/Delaunay_triangulation_3.h>
20#include <CGAL/Triangulation_vertex_base_with_info_3.h>
21
22using K = CGAL::Exact_predicates_inexact_constructions_kernel;
23using Vb = CGAL::Triangulation_vertex_base_with_info_3<void *, K>;
24using Tds = CGAL::Triangulation_data_structure_3<Vb>;
25using Delaunay = CGAL::Delaunay_triangulation_3<K, Tds>;
27
32namespace HertzianPhysics {
33
40 template<typename cell_t>
42
43 private:
45 double coherenceCoeff = 0.2;
46 bool areForcesNegligible = false;
59 bool coherence = true;
60
61 if (triangulation.number_of_vertices() > 0) {
62
63 typename Delaunay::Finite_vertices_iterator vit;
64
65 double tolerance;
66 for (vit = triangulation.finite_vertices_begin();
67 vit != triangulation.finite_vertices_end(); ++vit) { // For each vertex
68 cell_t *c = static_cast<cell_t *>(vit->info());
69 tolerance = c->getBody().getBoundingBoxRadius() * coherenceCoeff;
70 tolerance *= tolerance;
71 if ((c->getBody().getPosition().x() * vit->point().x() +
72 c->getBody().getPosition().y() * vit->point().y() +
73 c->getBody().getPosition().z() * vit->point().z())
74 >= tolerance) { // Verify the coherence between the real position of a cell and its position in the triangulation
75 coherence = false;
76 break;
77 }
78 }
79 } else coherence = false;
80 return coherence;
81 }
82
86 void moveDelaunay() {
87 typename Delaunay::Finite_vertices_iterator vit;
88 vit = triangulation.finite_vertices_begin();
89 double tolerance;
90 for (int i = 0; i < triangulation.number_of_vertices(); ++i) { // For each vertex
91 cell_t *c = static_cast<cell_t *>(vit->info());
92 tolerance = c->getBody().getBoundingBoxRadius() * coherenceCoeff;
93 tolerance *= tolerance;
94 if ((c->getBody().getPosition().x() * vit->point().x() +
95 c->getBody().getPosition().y() * vit->point().y() +
96 c->getBody().getPosition().z() * vit->point().z())
97 >= tolerance) { // Verify the coherence between the real position of a cell and its position in the triangulation
98 Point p(c->getBody().getPosition().x(), c->getBody().getPosition().y(),
99 c->getBody().getPosition().z());
100 triangulation.move(vit, p);
101 }
102 ++vit;
103 }
104 }
105
112 template<typename world_t>
113 void computeDelaunay(world_t *w) {
114 for (cell_t *c : w->cells) c->clearConnectedCells();
115 if (!isDelaunayCoherent()) {
116 std::vector<Point> points;
117 points.reserve(w->cells.size());
118 for (cell_t *c : w->cells) points.push_back(Point(c->getBody().getPosition().x(), c->getBody().getPosition().y(), c->getBody().getPosition().z()));
119
120 triangulation.clear();
121 triangulation = Delaunay(boost::make_zip_iterator(boost::make_tuple(points.begin(), w->cells.begin())),
122 boost::make_zip_iterator(boost::make_tuple(points.end(), w->cells.end())));
123 }
124 }
125
132 template<typename world_t>
133 void addCells(world_t *w) {
134 std::vector<Point> points;
135 points.reserve(w->newCells.size());
136 for (cell_t *c : w->newCells) points.push_back(Point(c->getBody().getPosition().x(), c->getBody().getPosition().y(), c->getBody().getPosition().z()));
137
138 triangulation.insert(boost::make_zip_iterator(boost::make_tuple(points.begin(), w->newCells.begin())),
139 boost::make_zip_iterator(boost::make_tuple(points.end(), w->newCells.end())));
140 }
141
145 void removeCells() {
146 typename Delaunay::Finite_vertices_iterator vit;
147
148 for (vit = triangulation.finite_vertices_begin();
149 vit != triangulation.finite_vertices_end(); ++vit) { // For each vertex
150 auto *c1 = static_cast<cell_t *>(vit->info());
151
152 if (c1->isDead()) {
153 triangulation.remove(vit);
154 }
155 }
156 }
157
164 template<typename world_t>
165 void updateConnections(world_t *w) {
166 if (!triangulation.is_valid()) {
167 std::cout << "UNVALID DELAUNAY !!" << std::endl;
168 }
169 for (auto c : w->cells) c->clearConnectedCells();
170
171 typename Delaunay::Finite_edges_iterator eit;
172
173 for (eit = triangulation.finite_edges_begin();
174 eit != triangulation.finite_edges_end(); ++eit) { // For each connection
175 cell_t *c1 = static_cast<cell_t *>(eit->first->vertex(eit->second)->info());
176 cell_t *c2 = static_cast<cell_t *>(eit->first->vertex(eit->third)->info());
177 if (c1 != nullptr && c2 != nullptr) {
178 double maxdist = c1->getBody().getBoundingBoxRadius() + c2->getBody().getBoundingBoxRadius();
179 if ((c1->getBody().getPosition() - c2->getBody().getPosition()).sqlength() <=
180 maxdist * maxdist) { // Max distance to consider 2 neighbour cells
181 c1->addConnectedCell(c2);
182 c2->addConnectedCell(c1);
183 }
184 } else {
185 exit(41);
186 }
187 }
188 }
189
196 template<typename world_t>
197 void customConstraint(world_t *w) {
198 for (auto c : w->cells) {
199 double minConstX = minConstraint.x() + c->getBody().getBoundingBoxRadius();
200 double minConstY = minConstraint.y() + c->getBody().getBoundingBoxRadius();
201 double minConstZ = minConstraint.z() + c->getBody().getBoundingBoxRadius();
202
203 double maxConstX = maxConstraint.x() - c->getBody().getBoundingBoxRadius();
204 double maxConstZ = maxConstraint.z() - c->getBody().getBoundingBoxRadius();
205 double maxConstY = maxConstraint.y() - c->getBody().getBoundingBoxRadius();
206
207 if (c->getBody().getPosition().x() < minConstX) {
208 c->getBody().setPosition(
209 MecaCell::Vec(minConstX, c->getBody().getPosition().y(), c->getBody().getPosition().z()));
210 } else if (c->getBody().getPosition().x() > maxConstX) {
211 c->getBody().setPosition(
212 MecaCell::Vec(maxConstX, c->getBody().getPosition().y(), c->getBody().getPosition().z()));
213 }
214 if (c->getBody().getPosition().y() < minConstY) {
215 c->getBody().setPosition(
216 MecaCell::Vec(c->getBody().getPosition().x(), minConstY, c->getBody().getPosition().z()));
217 } else if (c->getBody().getPosition().y() > maxConstY) {
218 c->getBody().setPosition(
219 MecaCell::Vec(c->getBody().getPosition().x(), maxConstY, c->getBody().getPosition().z()));
220 }
221 if (c->getBody().getPosition().z() < minConstZ) {
222 c->getBody().setPosition(
223 MecaCell::Vec(c->getBody().getPosition().x(), c->getBody().getPosition().y(), minConstZ));
224 } else if (c->getBody().getPosition().z() > maxConstZ) {
225 c->getBody().setPosition(
226 MecaCell::Vec(c->getBody().getPosition().x(), c->getBody().getPosition().y(), maxConstZ));
227 }
228 }
229 }
230
238 template<typename world_t>
239 double updateForces(world_t *w) {
240 double dtMin2 = w->dt * w->dt;
241
242 double currentDt2 = dtMin2; // s²
243
244 for (auto c : w->cells) {
246 c->getBody().receiveForce(c->getBody().getPurposeVelocity() * c->getBody().getGamma());
247 c->getBody().receiveForce(constraints.getConstraintVelocity(c) * c->getBody().getGamma());
248 }
249
250 for (auto c : w->cells) {
251 c->getBody().setComputed(false);
252 MecaCell::Vector3D vel = c->getBody().getForce() / c->getBody().getGamma();
253 c->getBody().setVelocity(vel);
254 currentDt2 = std::pow(coherenceCoeff * c->getBody().getBoundingBoxRadius(), 2) / vel.sqlength();
255 if (currentDt2 < dtMin2) dtMin2 = currentDt2;
256 }
257 return sqrt(dtMin2);
258 }
259
265 void updateInteractionForces(cell_t *c1) {
266 double delta, R, E, Frep, Fadh;
267 areForcesNegligible = true;
268 for (auto c2 : c1->getConnectedCells()) {
269 if (c2 != nullptr && !c2->getBody().getComputed()) {
270 MecaCell::Vec rij(c2->getBody().getPosition() - c1->getBody().getPosition());
271
272 delta = c1->getBody().getBoundingBoxRadius() + c2->getBody().getBoundingBoxRadius() - rij.length();
273 if (delta > 0) {
274 R = 1.0 / (1.0 / c1->getBody().getBoundingBoxRadius() + 1.0 / c2->getBody().getBoundingBoxRadius());
275 E = 1.0 /
276 ((1.0 - c1->getBody().getPoissonNumber() * c1->getBody().getPoissonNumber()) / c1->getBody().getYoungModulus() +
277 (1.0 - c2->getBody().getPoissonNumber() * c2->getBody().getPoissonNumber()) / c2->getBody().getYoungModulus());
278
279 Frep = 4.0 / 3.0 * E * sqrt(R) * pow(delta, 3.0 / 2.0);
280 Fadh = -M_PI * R *
281 (c1->getBody().getAdhesion() + c2->getBody().getAdhesion()) / 2.0 *
282 M_PI * delta * R;
283 MecaCell::Vec direction = rij.normalized();
284 c1->getBody().receiveForce(-direction * (Frep + Fadh));
285 c2->getBody().receiveForce(direction * (Frep + Fadh));
286 c1->getBody().addStress(Tensor(-direction * (Frep + Fadh), rij));
287 c2->getBody().addStress(Tensor(direction * (Frep + Fadh), -rij));
288 }
289 }
290 }
291 c1->getBody().setComputed(true);
292 c1->getBody().divTensor(c1->getBody().getVolume());
293 }
294
304 template<typename world_t>
305 void updatePositions(world_t *w, double dt) {
306 double radius;
308 for (cell_t *c : w->cells) {
309 MecaCell::Vec vel = c->getBody().getVelocity();
310 radius = c->getBody().getBoundingBoxRadius();
311
312 c->getBody().setPosition(c->getBody().getPosition() + vel * dt);
315
317 }
318 }
319
326 template<typename world_t>
327 inline void resetForces(world_t *w) {
328 for (cell_t *c : w->cells) {
329 c->getBody().resetForce();
330 }
331 }
332
342 template<typename world_t>
343 void applyPhysics(world_t *w) {
344 double time = 0.0;
345 double dt;
346 int nbStep = 0;
347
348 do {
349 resetForces(w);
350 dt = updateForces(w);
351 if (time + dt > w->dt) dt = w->dt - time;
352 updatePositions(w, dt);
353 time += dt;
354
355 nbStep++;
356 } while (time < w->dt);
357 }
358
359 public:
360
367 minConstraint = MecaCell::Vec(-99999.0, -99999.0, -99999.0);
368 maxConstraint = MecaCell::Vec(99999.0, 99999.0, 99999.0);
369 }
370
375 inline void setCoherenceCoeff(double c) { coherenceCoeff = c; }
376
385 template<typename world_t>
386 void beginUpdate(world_t *w) {
389 }
390
397 template <typename world_t>
398 void onAddCell(world_t *w) {
399 addCells(w);
400 }
401
410 template<typename world_t>
411 void preDeleteDeadCellsUpdate(world_t *w) {
412 for (cell_t *c : w->cells) {
413 if (c->isDead()) {
414 removeCells();
415 break;
416 }
417 }
418 }
419
428 template<typename world_t>
429 void preBehaviorUpdate(world_t *w) {
430 applyPhysics(w);
431 }
432
439 minConstraint = min;
440 }
441
448 maxConstraint = max;
449 }
450
456
464 inline void addPlane(MecaCell::Vec _n = MecaCell::Vec(0, 1, 0), MecaCell::Vec _p = MecaCell::Vec(0, 0, 0), bool _sticky = false) { constraints.addPlane(_n, _p, _sticky); }
465
477 inline void addSymAxis(std::function<double(double, MecaCell::Vec, MecaCell::Vec)> _radiusFunction, MecaCell::Vec _axis = MecaCell::Vec(0., 1., 0.), MecaCell::Vec _origin = MecaCell::Vec(0., 0., 0.), double _length = 100, double _maxEffectRadius = 100, bool _fitShape = true, double _velocity_attraction = 0.001) {
478 constraints.addSymAxis(_radiusFunction, _axis, _origin, _length, _maxEffectRadius, _fitShape, _velocity_attraction);
479 }
480
487
495 inline void setCurvedBox(double radiusBoxPlane, double curvedDistToMaxSpeed, double maxSpeed) { constraints.setCurvedBox(radiusBoxPlane, curvedDistToMaxSpeed, maxSpeed); }
496
497 };
498}
499#endif
double radius
Definition: CellBody.hpp:33
Delaunay::Point Point
CGAL::Delaunay_triangulation_3< K, Tds > Delaunay
CGAL::Triangulation_vertex_base_with_info_3< void *, K > Vb
CGAL::Triangulation_data_structure_3< Vb > Tds
CGAL::Exact_predicates_inexact_constructions_kernel K
Class for managing Delaunay triangulation and Hertzian physics.
void preBehaviorUpdate(world_t *w)
Pre-behavior update hook for MecaCell.
void resetForces(world_t *w)
Resets the forces acting on the cells.
void addVelocity(MecaCell::Vec _v=MecaCell::Vec(0, 1, 0))
Adds a velocity constraint.
void moveDelaunay()
Moves the points in the triangulation to match their real positions.
void setCoherenceCoeff(double c)
Sets the coherence coefficient.
void setMinConstraint(MecaCell::Vec min)
Sets the minimum constraint.
void computeDelaunay(world_t *w)
Computes the Delaunay triangulation from the list of cells.
void applyPhysics(world_t *w)
Applies the physics with a coherent time step.
double updateForces(world_t *w)
Updates the forces acting on the cells.
void addSymAxis(std::function< double(double, MecaCell::Vec, MecaCell::Vec)> _radiusFunction, MecaCell::Vec _axis=MecaCell::Vec(0., 1., 0.), MecaCell::Vec _origin=MecaCell::Vec(0., 0., 0.), double _length=100, double _maxEffectRadius=100, bool _fitShape=true, double _velocity_attraction=0.001)
Adds a symmetrical axis constraint.
PhysicsConstraints getConstraints()
Gets the physics constraints.
void preDeleteDeadCellsUpdate(world_t *w)
Pre-delete dead cells update hook for MecaCell.
void updateConnections(world_t *w)
Updates the connections between cells.
void addCells(world_t *w)
Adds new cells to the triangulation.
void beginUpdate(world_t *w)
Begin update hook for MecaCell.
void onAddCell(world_t *w)
On add cell hook for MecaCell.
void removeCells()
Removes dead cells from the triangulation.
void setMaxConstraint(MecaCell::Vec max)
Sets the maximum constraint.
void setCurvedBox(double radiusBoxPlane, double curvedDistToMaxSpeed, double maxSpeed)
Sets the curved box parameters.
void customConstraint(world_t *w)
Applies custom spatial constraints to the cells.
void updateInteractionForces(cell_t *c1)
Updates the interaction forces between cells.
void addPlane(MecaCell::Vec _n=MecaCell::Vec(0, 1, 0), MecaCell::Vec _p=MecaCell::Vec(0, 0, 0), bool _sticky=false)
Adds a plane constraint.
bool isDelaunayCoherent()
Checks the coherence of the Delaunay triangulation.
void updatePositions(world_t *w, double dt)
Updates the positions of the cells.
general purpose 3D vector/point class.
Definition: vector3D.h:20
double x() const
Definition: vector3D.h:94
double y() const
Definition: vector3D.h:100
double length() const
compute the length of the vector
Definition: vector3D.h:257
double sqlength() const
compute the square length of the current vector (faster than length)
Definition: vector3D.h:265
double z() const
Definition: vector3D.h:106
Vector3D normalized() const
returns a normalized copy of the current vector
Definition: vector3D.h:452
Class for managing physical constraints in a simulation.
void addPlane(MecaCell::Vec _n=MecaCell::Vec(0, 1, 0), MecaCell::Vec _p=MecaCell::Vec(0, 0, 0), bool _sticky=false)
Adds a plane constraint.
MecaCell::Vec getConstraintVelocity(cell_t *c)
Computes the constraint velocity for a cell.
void snapCellsToPlane(cell_t *c)
Snaps cells to the plane.
void addSymAxis(std::function< double(double, MecaCell::Vec, MecaCell::Vec)> _radiusFunction, MecaCell::Vec _axis=MecaCell::Vec(0., 1., 0.), MecaCell::Vec _origin=MecaCell::Vec(0., 0., 0.), double _length=100, double _maxEffectRadius=100, bool _fitShape=true, double _velocity_attraction=0.001)
Adds a symmetrical axis constraint.
void addVelocity(MecaCell::Vec _v=MecaCell::Vec(0, 1, 0))
Adds a velocity constraint.
void setCurvedBox(double radiusBoxPlane, double curvedDistToMaxSpeed, double maxSpeed)
Sets the curved box parameters.
A simple vector class template.
Definition: std.hpp:290
void reserve(size_t newCapacity)
Reserves storage for the specified number of elements.
Definition: std.hpp:372
void push_back(const T &value)
Adds an element to the end of the vector.
Definition: std.hpp:304
iterator begin()
Returns an iterator to the first element.
Definition: std.hpp:409
iterator end()
Returns an iterator to the last element.
Definition: std.hpp:418
Namespace for Hertzian physics-related classes and functions.
Vector3D Vec
alias for Vector3D
Definition: utils.hpp:22
iostream cout
Standard output stream.
Definition: std.hpp:267
double sqrt(double x)
Computes the square root of a number.
Definition: std.hpp:32
iostream endl
End-of-line manipulator.
Definition: std.hpp:274
double pow(double base, double exponent)
Computes the power of a number.
Definition: std.hpp:43
Represents a 3x3 tensor for stress calculations.
Definition: Tensor.hpp:10