void convert_to_xyz(float *P, int n, SHORTIM im) { ///////////////////////////////////////////////////////// // convert P from (i,j,k) to (x,y,z) coordinates float T[16]; float dum[4]={0.0,0.0,0.0,1.0}; DIM dim; set_dim(dim,im); ijk2xyz(T, dim); for(int i=0; i<n; i++) { // note that P is (3 x n) dum[0]=P[0*n + i]; dum[1]=P[1*n + i]; dum[2]=P[2*n + i]; multi(T,4,4,dum,4,1,dum); P[0*n + i]=dum[0]; P[1*n + i]=dum[1]; P[2*n + i]=dum[2]; } }
void FlowAnalysis::addOneParticle(const Real& diameter, const int& mask, const Real& solidRatio, const shared_ptr<Node>& node){ const auto& dyn(node->getData<DemData>()); Real V(pow(cellSize,3)); // all things saved are actually densities over the volume of a single cell Vector3r momentum_V(dyn.vel*dyn.mass/V); Real Ek_V(dyn.getEk_any(node,/*trans*/true,/*rot*/true,scene)/V); size_t fraction=0; // default is the only fraction, which is always present // by diameter if(!dLim.empty()){ fraction=std::lower_bound(dLim.begin(),dLim.end(),diameter)-dLim.begin(); } // by particle mask if(!masks.empty()){ bool found=false; for(size_t i=0; i<masks.size(); i++){ if(mask&masks[i]){ if(found){ LOG_WARN("Particle with mask "<<mask<<" matching both masks["<<fraction<<"]="<<masks[i]<<" and masks["<<i<<"]="<<masks[i]<<"; only first match used."); } else { found=true; fraction=i; } } } if(!found){ LOG_WARN("Particle not matching any mask, ignoring; set FlowAnalysis.mask to filter those out upfront."); return; } } // loop over neighboring grid points, add relevant data with weights. Vector3i ijk=xyz2ijk(node->pos); // it does not matter here if ijk are inside the grid; // the enlargedBox test in addCurrentData already pruned cases too far away. // The rest is checked in the loop below with validIjkRange; // that ensure that even points just touching the grid from outside are accounted for correctly. Vector3r n=(node->pos-ijk2xyz(ijk))/cellSize; // normalized coordinate in the cube (0..1)x(0..1)x(0..1) if(n.minCoeff()<0 || n.maxCoeff()>1){ LOG_ERROR("n="<<n.transpose()<<", ijk="<<ijk.transpose()<<", pos="<<node->pos.transpose()<<", ijk2xyz="<<ijk2xyz(ijk)); } // trilinear interpolation const Real& x(n[0]); const Real& y(n[1]); const Real& z(n[2]); Real X(1-x), Y(1-y), Z(1-z); const int& i(ijk[0]); const int& j(ijk[1]); const int& k(ijk[2]); int I(i+1), J(j+1), K(k+1); // traverse all affected points Real weights[]={ X*Y*Z,x*Y*Z,x*y*Z,X*y*Z, X*Y*z,x*Y*z,x*y*z,X*y*z }; // the sum should be equal to one assert(abs(weights[0]+weights[1]+weights[2]+weights[3]+weights[4]+weights[5]+weights[6]+weights[7]-1) < 1e-5); Vector3i pts[]={ Vector3i(i,j,k),Vector3i(I,j,k),Vector3i(I,J,k),Vector3i(i,J,k), Vector3i(i,j,K),Vector3i(I,j,K),Vector3i(I,J,K),Vector3i(i,J,K) }; Eigen::AlignedBox<int,3> validIjkRange(Vector3i::Zero(),Vector3i(data.shape()[1]-1,data.shape()[2]-1,data.shape()[3]-1)); for(int ii=0; ii<8; ii++){ // make sure we are within bounds here if(!validIjkRange.contains(pts[ii])) continue; // subarray where we write the actual data for this point auto pt(data[fraction][pts[ii][0]][pts[ii][1]][pts[ii][2]]); const Real& w(weights[ii]); /* ensured by validIjkRange */ assert(w>0); pt[PT_FLOW_X]+=w*momentum_V[0]; pt[PT_FLOW_Y]+=w*momentum_V[1]; pt[PT_FLOW_Z]+=w*momentum_V[2]; pt[PT_VEL_X]+=w*dyn.vel[0]; pt[PT_VEL_Y]+=w*dyn.vel[1]; pt[PT_VEL_Z]+=w*dyn.vel[2]; pt[PT_EK]+=w*Ek_V; pt[PT_SUM_WEIGHT]+=w*1.; pt[PT_SUM_DIAM]+=w*diameter; // the average is computed later, dividing by PT_SUM_WEIGHT if(!isnan(solidRatio)) pt[PT_SUM_SOLID_RATIO]+=w*solidRatio; assert(!isnan(pt[PT_FLOW_X]) && !isnan(pt[PT_FLOW_Y]) && !isnan(pt[PT_FLOW_Z]) && !isnan(pt[PT_EK]) && !isnan(pt[PT_SUM_WEIGHT])); } };