File: OtherClassesForLSContact.cpp

package info (click to toggle)
yade 2026.1.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 34,448 kB
  • sloc: cpp: 97,645; python: 52,173; sh: 677; makefile: 162
file content (225 lines) | stat: -rw-r--r-- 13,187 bytes parent folder | download
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
/*************************************************************************
*  2021 jerome.duriez@inrae.fr                                           *
*  This program is free software, see file LICENSE for details.          *
*************************************************************************/

#ifdef YADE_LS_DEM
#include <pkg/levelSet/OtherClassesForLSContact.hpp>
#include <pkg/levelSet/ShopLS.hpp>

namespace yade {
YADE_PLUGIN((Bo1_LevelSet_Aabb)(MultiPhys)(MultiFrictPhys)(MultiViscElPhys)(Ip2_FrictMat_FrictMat_MultiFrictPhys)(Ip2_ViscElMat_ViscElMat_MultiViscElPhys));
CREATE_LOGGER(Bo1_LevelSet_Aabb);
CREATE_LOGGER(Ip2_FrictMat_FrictMat_MultiFrictPhys);
CREATE_LOGGER(Ip2_ViscElMat_ViscElMat_MultiViscElPhys);

void Bo1_LevelSet_Aabb::go(const shared_ptr<Shape>& cm, shared_ptr<Bound>& bv, const Se3r& se3, const Body*)
{ //TODO: use Eigen Aligned Box.extend to avoid the 2*6 if below ?
	// NB: see BoundDispatcher::processBody() (called by BoundDispatcher::action(), called by InsertionSortCollider::action()) in pkg/common/Dispatching.cpp for the attributes used upon calling
	if (!bv) { bv = shared_ptr<Bound>(new Aabb); }
	Aabb*     aabb    = static_cast<Aabb*>(bv.get()); // no need to bother deleting that raw pointer: e.g. https://stackoverflow.com/q/53908753/9864634
	LevelSet* lsShape = static_cast<LevelSet*>(cm.get());
	Real      inf     = std::numeric_limits<Real>::infinity();
	// We compute the bounds from LevelSet->corners serving as an Aabb in local frame, and considering the transformation from that local frame
	// NB: it is useless to try to make something much similar to Bo1_Box_Aabb::go(), in case se3.position of our level set body would not be in the middle of the lsShape->corners box, contrary to Box bodies and their extents and "halfSize" in Bo1_Box_Aabb::go()

	if (!lsShape->corners.size()) { // kind of "first iteration"
		Vector3i nGP(lsShape->lsGrid->nGP);
		Vector3r gp;
		int      nGPx(nGP[0]), nGPy(nGP[1]), nGPz(nGP[2]);
		Real     xGP, yGP, zGP;
		Real     xMin(inf), xMax(-inf), yMin(inf), yMax(-inf), zMin(inf), zMax(-inf); // extrema values for the "inside" gridpoints
		// identifying the extrema coordinates within the surface using a nGridPoints^3 loop. Would be much better to do 3*nGridpoints^2 loops, if possible, searching for find_if(firstIterator to lsValAlongZAxis,lastIterator to lsValAlongZAxis, within surface) which is easy for the zAxis = lsGrid->gridPoint[xInd][yInd], but how accessing an yAxis ~ lsGrid->gridPoint[xInd][:][zInd] which does not exist in C++ ?
		for (int xInd = 0; xInd < nGPx; xInd++) {
			for (int yInd = 0; yInd < nGPy; yInd++) {
				for (int zInd = 0; zInd < nGPz; zInd++) {
					gp  = lsShape->lsGrid->gridPoint(xInd, yInd, zInd);
					xGP = gp[0];
					yGP = gp[1];
					zGP = gp[2];
					if (lsShape->distField[xInd][yInd][zInd] <= 0) {
						if (xGP < xMin) xMin = xGP;
						if (xGP > xMax) xMax = xGP;
						if (yGP < yMin) yMin = yGP;
						if (yGP > yMax) yMax = yGP;
						if (zGP < zMin) zMin = zGP;
						if (zGP > zMax) zMax = zGP;
					}
				}
			}
		}
		if ((xMin == xMax) or (yMin == yMax) or (zMin == zMax))
			LOG_WARN("One flat LevelSet body, as detected by shape.corners computation, was that expected ? (is the grid too coarse ?)");
		// right now, our *Min, *Max define a downwards-rounded Aabb (smaller than true surface), let's make it upwards-rounded below:
		Real g(lsShape->lsGrid->spacing);
		for (int iInd = 0; iInd < 2; iInd++) {
			for (int jInd = 0; jInd < 2; jInd++) {
				for (int kInd = 0; kInd < 2; kInd++)
					lsShape->corners.push_back(
					        Vector3r(iInd == 0 ? xMin - g : xMax + g, jInd == 0 ? yMin - g : yMax + g, kInd == 0 ? zMin - g : zMax + g));
			}
		}
	}
	if (lsShape->corners.size() != 8) LOG_ERROR("We have a LevelSet-shaped body with some shape.corners computed but not 8 of them !");
	std::array<Vector3r, 8> cornersCurrent; // current positions of corners (in global frame)
	for (int corner = 0; corner < 8; corner++)
		cornersCurrent[corner] = ShopLS::rigidMapping(lsShape->corners[corner], Vector3r::Zero(), se3.position, se3.orientation);
	// NB: corners should average to the origin. This is kind of tested through LevelSet.center in levelSetBody() Py function

	Real xCorner, yCorner, zCorner;                                           // the ones of cornersCurrent
	Real xMin(inf), xMax(-inf), yMin(inf), yMax(-inf), zMin(inf), zMax(-inf); // extrema values for the cornersCurrent
	for (int corner = 0; corner < 8; corner++) {
		xCorner = cornersCurrent[corner][0];
		yCorner = cornersCurrent[corner][1];
		zCorner = cornersCurrent[corner][2];
		if (xCorner < xMin) xMin = xCorner;
		if (xCorner > xMax) xMax = xCorner;
		if (yCorner < yMin) yMin = yCorner;
		if (yCorner > yMax) yMax = yCorner;
		if (zCorner < zMin) zMin = zCorner;
		if (zCorner > zMax) zMax = zCorner;
	}
	aabb->min = Vector3r(xMin, yMin, zMin);
	aabb->max = Vector3r(xMax, yMax, zMax);
}


void Ip2_FrictMat_FrictMat_MultiFrictPhys::go(const shared_ptr<Material>& mat1, const shared_ptr<Material>& mat2, const shared_ptr<Interaction>& interaction)
{
	//NB: we will execute that Ip2::go at every iteration, because new items in interaction->phys->contacts have to be touched here (e.g., assigned kn)
	shared_ptr<MultiFrictPhys> multiFrictPhysPtr(new MultiFrictPhys);
	if (interaction->phys->getClassName()
	    == "MultiPhys") { // this interaction has just been created (and already populated with a phys, of MultiPhys type), by the Ig2*MultiScGeom::go
		LOG_DEBUG("Interaction " << interaction->id1 << " - " << interaction->id2 << " seen for the first time in the Ip2" << std::endl);
		// we will backport interaction->phys->contacts and ->nodesIds;
		shared_ptr<MultiPhys> iPhysPtrAsMultiPhys(YADE_PTR_CAST<MultiPhys>(interaction->phys));
		multiFrictPhysPtr->contacts = iPhysPtrAsMultiPhys->contacts;
		multiFrictPhysPtr->nodesIds = iPhysPtrAsMultiPhys->nodesIds;
	} else if (interaction->phys->getClassName() == "MultiFrictPhys") { // it has existed for a while: i->phys is already of MultiFrictPhys type
		LOG_DEBUG(
		        "Interaction " << interaction->id1 << " - " << interaction->id2 << " had already been Ip2-handled in the past, its phys is of "
		                       << interaction->phys->getClassName() << " at least" << std::endl);
		multiFrictPhysPtr = YADE_PTR_CAST<MultiFrictPhys>(interaction->phys);
	} else {
		LOG_ERROR(
		        "Interaction " << interaction->id1 << " - " << interaction->id2 << " has a phys of " << interaction->phys->getClassName()
		                       << ", that is clearly not expected ! (only MultiPhys or MultiFrictPhys are)" << std::endl);
	}
	LOG_DEBUG(
	        "YADE_PTR_DYN_CAST<MultiFrictPhys>(interaction->phys) = "
	        << YADE_PTR_DYN_CAST<MultiFrictPhys>(interaction->phys)
	        << " (0 would be a problem)"); // the dynamic cast will never be executed here unless LOG_DEBUG level is actually effective
	// Computing once for all (outside of below loop) the to-be-assigned contact friction from the Materials frictionAngle(s):
	const shared_ptr<FrictMat>& fmat1 = YADE_PTR_CAST<FrictMat>(mat1);
	const shared_ptr<FrictMat>& fmat2 = YADE_PTR_CAST<FrictMat>(mat2);
	Real                        frictAngle(std::min(fmat1->frictionAngle, fmat2->frictionAngle));
	// Looping over contacting nodes to assign mechanical properties into individual contacts items:
	LOG_DEBUG("About to looping over contacting nodes");
	for (unsigned int idx = 0; idx < multiFrictPhysPtr->contacts.size(); idx++) {
		if (multiFrictPhysPtr->contacts[idx]->getClassName() == "IPhys") { // this contacts[idx] item has just been created in Ig2::go
			LOG_DEBUG("Contact at node " << multiFrictPhysPtr->nodesIds[idx] << " is detected by the Ip2 as being just created" << std::endl);
			shared_ptr<FrictPhys> frictPhysPtr(new FrictPhys);
			// Direct assignment of values of stiffnesses:
			frictPhysPtr->kn = kn;
			frictPhysPtr->ks = ks;
			// Assignment of (tangent of) friction angle:
			frictPhysPtr->tangensOfFrictionAngle = std::tan(frictAngle);
			multiFrictPhysPtr->contacts[idx]     = frictPhysPtr;
		} else { // this contacts[idx] item shall have existed for a while, there is nothing to do
			LOG_DEBUG("Contact at node " << multiFrictPhysPtr->nodesIds[idx] << " is detected by the Ip2 as existing for a while" << std::endl);
		}
	}
	LOG_DEBUG("Nodes loop done");
	interaction->phys = multiFrictPhysPtr;
};


void Ip2_ViscElMat_ViscElMat_MultiViscElPhys::go(const shared_ptr<Material>& mat1, const shared_ptr<Material>& mat2, const shared_ptr<Interaction>& interaction)
{
	//NB: we will execute that Ip2::go at every iteration, because new items in interaction->phys->contacts have to be touched here (e.g., assigned kn)
	shared_ptr<MultiViscElPhys> multiViscElPhysPtr(new MultiViscElPhys);
	if (interaction->phys->getClassName()
	    == "MultiPhys") { // this interaction has just been created (and already populated with a phys, of MultiPhys type), by the Ig2*MultiScGeom::go
		LOG_DEBUG("Interaction " << interaction->id1 << " - " << interaction->id2 << " seen for the first time in the Ip2" << std::endl);
		// we will backport interaction->phys->contacts and ->nodesIds;
		shared_ptr<MultiPhys> iPhysPtrAsMultiPhys(YADE_PTR_CAST<MultiPhys>(interaction->phys));
		multiViscElPhysPtr->contacts = iPhysPtrAsMultiPhys->contacts;
		multiViscElPhysPtr->nodesIds = iPhysPtrAsMultiPhys->nodesIds;
	} else if (interaction->phys->getClassName() == "MultiViscElPhys") { // it has existed for a while: i->phys is already of MultiViscElPhys type
		LOG_DEBUG(
		        "Interaction " << interaction->id1 << " - " << interaction->id2 << " had already been Ip2-handled in the past, its phys is of "
		                       << interaction->phys->getClassName() << " at least" << std::endl);
		multiViscElPhysPtr = YADE_PTR_CAST<MultiViscElPhys>(interaction->phys);
	} else {
		LOG_ERROR(
		        "Interaction " << interaction->id1 << " - " << interaction->id2 << " has a phys of " << interaction->phys->getClassName()
		                       << ", that is clearly not expected ! (only MultiPhys or MultiViscElPhys are)" << std::endl);
	}
	LOG_DEBUG(
	        "YADE_PTR_DYN_CAST<MultiViscElPhys>(interaction->phys) = "
	        << YADE_PTR_DYN_CAST<MultiViscElPhys>(interaction->phys)
	        << " (0 would be a problem)"); // the dynamic cast will never be executed here unless LOG_DEBUG level is actually effective
	// Computing once for all (outside of below loop) the to-be-assigned contact friction from the Materials frictionAngle(s):

	const shared_ptr<ViscElMat>& fmat1 = YADE_PTR_CAST<ViscElMat>(mat1);
	const shared_ptr<ViscElMat>& fmat2 = YADE_PTR_CAST<ViscElMat>(mat2);

	Real frictAngle(std::min(fmat1->frictionAngle, fmat2->frictionAngle));

	Real kna = fmat1->kn;
	Real knb = fmat2->kn;

	Real ksa = fmat1->ks;
	Real ksb = fmat2->ks;

	Real ena = fmat1->en;
	Real enb = fmat2->en;

	Real esa = fmat1->et; // ViscElMat notes the tangential coefficient of restitution as "et" - here we use "es" where "s" stands for "shear"
	Real esb = fmat2->et;

	Real mass1 = Body::byId(interaction->getId1())->state->mass;
	Real mass2 = Body::byId(interaction->getId2())->state->mass;
	if (mass1 == 0.0 and mass2 > 0.0) {
		mass1 = mass2;
	} else if (mass2 == 0.0 and mass1 > 0.0) {
		mass2 = mass1;
	}

	// See [Pournin2001, just below equation (19)]
	const Real massR = mass1 * mass2 / (mass1 + mass2);

	Real Kn = (kn) ? (*kn)(mat1->id, mat2->id) : 2. * kna * knb / (kna + knb);
	Real Ks = (ks) ? (*ks)(mat1->id, mat2->id) : 2. * ksa * ksb / (ksa + ksb);

	Real En = (en) ? (*en)(mat1->id, mat2->id) : 2. * ena * enb / (ena + enb);
	Real Es = (et) ? (*et)(mat1->id, mat2->id) : 2. * esa * esb / (esa + esb);

	Real cn = 2.0 * find_cn_from_en(En, massR, contactParameterCalculation(Kn, Kn), interaction);
	Real cs = 2.0 * find_cn_from_en(Es, massR, contactParameterCalculation(Ks, Ks), interaction);

	// Looping over contacting nodes to assign mechanical properties into individual contacts items:
	LOG_DEBUG("About to looping over contacting nodes");
	for (unsigned int idx = 0; idx < multiViscElPhysPtr->contacts.size(); idx++) {
		if (multiViscElPhysPtr->contacts[idx]->getClassName() == "IPhys") { // this contacts[idx] item has just been created in Ig2::go
			LOG_DEBUG("Contact at node " << multiViscElPhysPtr->nodesIds[idx] << " is detected by the Ip2 as being just created" << std::endl);
			shared_ptr<ViscElPhys> viscElPhysPtr(new ViscElPhys);
			// Direct assignment of values of stiffnesses:
			viscElPhysPtr->kn = Kn;
			viscElPhysPtr->ks = Ks;
			// Direct assignment of values of viscous damping coefficients:
			viscElPhysPtr->cn = cn;
			viscElPhysPtr->cs = cs;
			// Assignment of (tangent of) friction angle:
			viscElPhysPtr->tangensOfFrictionAngle = std::tan(frictAngle);
			multiViscElPhysPtr->contacts[idx]     = viscElPhysPtr;
		} else { // this contacts[idx] item shall have existed for a while, there is nothing to do
			LOG_DEBUG("Contact at node " << multiViscElPhysPtr->nodesIds[idx] << " is detected by the Ip2 as existing for a while" << std::endl);
		}
	}
	LOG_DEBUG("Nodes loop done");
	interaction->phys = multiViscElPhysPtr;
};

} // namespace yade
#endif //YADE_LS_DEM