272
272
return py::make_tuple(edges,cumm);
275
/* possible enhacement: proportions parameter, so that the domain is not cube, but box with sides having given proportions */
276
276
long SpherePack::particleSD2(const vector<Real>& radii, const vector<Real>& passing, int numSph, bool periodic, Real cloudPorosity, int seed){
277
typedef Eigen::Matrix<Real,Eigen::Dynamic,Eigen::Dynamic> MatrixXr;
278
typedef Eigen::Matrix<Real,Eigen::Dynamic,1> VectorXr;
280
int dim=radii.size()+1;
281
if(passing.size()!=radii.size()) throw std::invalid_argument("SpherePack.particleSD2: radii and passing must have the same length.");
282
MatrixXr M=MatrixXr::Zero(dim,dim);
283
VectorXr rhs=VectorXr::Zero(dim);
286
We know percentages for each fraction (Δpi) and their radii (ri), and want to find
287
the number of sphere for each fraction Ni and total solid volume Vs. For each fraction,
288
we know that the volume is equal to Ni*(4/3*πri³), which must be equal to Vs*Δpi (Δpi is
289
relative solid volume of the i-th fraction).
291
The last equation says that total number of particles (sum of fractions) is equal to N,
292
which is the total number of particles requested by the user.
302
for(int i=0; i<dim-1; i++){
303
M(i,i)=(4/3.)*Mathr::PI*pow(radii[i],3);
304
M(i,dim-1)=-(passing[i]-(i>0?passing[i-1]:0))/100.;
308
// NumsVs=M^-1*rhs: number of spheres and volume of solids
309
VectorXr NumsVs(dim); NumsVs=M.inverse()*rhs;
310
Real Vs=NumsVs[dim-1]; // total volume of solids
311
Real Vtot=Vs/(1-cloudPorosity); // total volume of cell containing the packing
312
Vector3r cellSize=pow(Vtot,1/3.)*Vector3r().Ones(); // for now, assume always cubic sample
313
Real rMean=pow(Vs/(numSph*(4/3.)*Mathr::PI),1/3.); // make rMean such that particleSD will compute the right Vs (called a bit confusingly Vtot anyway) inversely
314
// cerr<<"Vs="<<Vs<<", Vtot="<<Vtot<<", rMean="<<rMean<<endl;
315
// cerr<<"cellSize="<<cellSize<<", rMean="<<rMean<<", numSph="<<numSph<<endl;
316
return particleSD(Vector3r::Zero(),cellSize,rMean,periodic,"",numSph,radii,passing,false);
277
//deprecated (https://bugs.launchpad.net/yade/+bug/1024443)
278
LOG_ERROR("particleSD2() has been removed. Please use makeCloud() instead.");
319
// TODO: header, python wrapper, default params
321
282
// Discrete particle size distribution
322
283
long SpherePack::particleSD(Vector3r mn, Vector3r mx, Real rMean, bool periodic, string name, int numSph, const vector<Real>& radii, const vector<Real>& passing, bool passingIsNotPercentageButCount, int seed){
323
vector<Real> numbers;
324
if(!passingIsNotPercentageButCount){
325
Real Vtot=numSph*4./3.*Mathr::PI*pow(rMean,3.); // total volume of the packing (computed with rMean)
327
// calculate number of spheres necessary per each radius to match the wanted psd
328
// passing has to contain increasing values
329
for (size_t i=0; i<radii.size(); i++){
330
Real volS=4./3.*Mathr::PI*pow(radii[i],3.);
331
if (i==0) {numbers.push_back(passing[i]/100.*Vtot/volS);}
332
else {numbers.push_back((passing[i]-passing[i-1])/100.*Vtot/volS);} //
333
cout<<"fraction #"<<i<<" ("<<passing[i]<<"%, r="<<radii[i]<<"): "<<numbers[i]<<" spheres, fraction/cloud volumes "<<volS<<"/"<<Vtot<<endl;
336
FOREACH(Real p, passing) numbers.push_back(p);
339
static boost::minstd_rand randGen(seed!=0?seed:(int)TimingInfo::getNow(true));
340
static boost::variate_generator<boost::minstd_rand&, boost::uniform_real<> > rnd(randGen, boost::uniform_real<>(0,1));
342
const int maxTry=1000;
344
if(periodic)(cellSize=size);
345
for (int ii=(int)radii.size()-1; ii>=0; ii--){
346
Real r=radii[ii]; // select radius
347
for(int i=0; i<numbers[ii]; i++) { // place as many spheres as required by the psd for the selected radius into the free spot
349
for(t=0; t<maxTry; ++t){
351
if(!periodic) { for(int axis=0; axis<3; axis++) c[axis]=mn[axis]+r+(size[axis]-2*r)*rnd(); }
352
else { for(int axis=0; axis<3; axis++) c[axis]=mn[axis]+size[axis]*rnd(); }
353
size_t packSize=pack.size(); bool overlap=false;
355
for(size_t j=0; j<packSize; j++){ if(pow(pack[j].r+r,2) >= (pack[j].c-c).squaredNorm()) { overlap=true; break; } }
357
for(size_t j=0; j<packSize; j++){
359
for(int axis=0; axis<3; axis++) dr[axis]=min(cellWrapRel(c[axis],pack[j].c[axis],pack[j].c[axis]+size[axis]),cellWrapRel(pack[j].c[axis],c[axis],c[axis]+size[axis]));
360
if(pow(pack[j].r+r,2)>= dr.squaredNorm()){ overlap=true; break; }
363
if(!overlap) { pack.push_back(Sph(c,r)); break; }
366
if(numbers[ii]>0) LOG_WARN("Exceeded "<<maxTry<<" tries to insert non-overlapping sphere to packing. Only "<<i<<" spheres were added, although you requested "<<numbers[ii]<<" with radius "<<radii[ii]);
284
//deprecated (https://bugs.launchpad.net/yade/+bug/1024443)
285
LOG_ERROR("particleSD() has been removed. Please use makeCloud() instead.");
375
289
long SpherePack::particleSD_2d(Vector2r mn, Vector2r mx, Real rMean, bool periodic, string name, int numSph, const vector<Real>& radii, const vector<Real>& passing, bool passingIsNotPercentageButCount, int seed){
376
vector<Real> numbers;
377
if(!passingIsNotPercentageButCount){
378
Real Vtot=numSph*4./3.*Mathr::PI*pow(rMean,3.); // total volume of the packing (computed with rMean)
380
// calculate number of spheres necessary per each radius to match the wanted psd
381
// passing has to contain increasing values
382
for (size_t i=0; i<radii.size(); i++){
383
Real volS=4./3.*Mathr::PI*pow(radii[i],3.);
384
if (i==0) {numbers.push_back(passing[i]/100.*Vtot/volS);}
385
else {numbers.push_back((passing[i]-passing[i-1])/100.*Vtot/volS);} //
386
cout<<"fraction #"<<i<<" ("<<passing[i]<<"%, r="<<radii[i]<<"): "<<numbers[i]<<" spheres, fraction/cloud volumes "<<volS<<"/"<<Vtot<<endl;
389
FOREACH(Real p, passing) numbers.push_back(p);
392
static boost::minstd_rand randGen(seed!=0?seed:(int)TimingInfo::getNow(true));
393
static boost::variate_generator<boost::minstd_rand&, boost::uniform_real<> > rnd(randGen, boost::uniform_real<>(0,1));
395
const int maxTry=1000;
397
//if(periodic)(cellSize=size); in this case, it must be defined in py script as cell needs the third dimension
398
for (int ii=(int)radii.size()-1; ii>=0; ii--){
399
Real r=radii[ii]; // select radius
400
for(int i=0; i<numbers[ii]; i++) { // place as many spheres as required by the psd for the selected radius into the free spot
402
for(t=0; t<maxTry; ++t){
404
if(!periodic) { for(int axis=0; axis<2; axis++) c[axis]=mn[axis]+r+(size[axis]-2*r)*rnd(); }
405
else { for(int axis=0; axis<2; axis++) c[axis]=mn[axis]+size[axis]*rnd(); }
406
size_t packSize=pack.size(); bool overlap=false;
408
for(size_t j=0; j<packSize; j++){ if(pow(pack[j].r+r,2) >= (pack[j].c-c).squaredNorm()) { overlap=true; break; } }
410
for(size_t j=0; j<packSize; j++){
411
Vector3r dr=Vector3r::Zero();
412
for(int axis=0; axis<2; axis++) dr[axis]=min(cellWrapRel(c[axis],pack[j].c[axis],pack[j].c[axis]+size[axis]),cellWrapRel(pack[j].c[axis],c[axis],c[axis]+size[axis]));
413
if(pow(pack[j].r+r,2)>= dr.squaredNorm()){ overlap=true; break; }
416
if(!overlap) { pack.push_back(Sph(c,r)); break; }
419
if(numbers[ii]>0) LOG_WARN("Exceeded "<<maxTry<<" tries to insert non-overlapping sphere to packing. Only "<<i<<" spheres were added, although you requested "<<numbers[ii]<<" with radius "<<radii[ii]);
290
//deprecated (https://bugs.launchpad.net/yade/+bug/1024443)
291
LOG_ERROR("particleSD_2d() has been removed. Please use makeCloud() instead.");
427
295
long SpherePack::makeClumpCloud(const Vector3r& mn, const Vector3r& mx, const vector<shared_ptr<SpherePack> >& _clumps, bool periodic, int num, int seed){