OpenFPM  5.2.0
Project that contain the implementation of distributed structures
Dcpse.hpp
1 //
2 // DCPSE Created by tommaso on 29/03/19.
3 // Modified, Updated and Maintained by Abhinav and Pietro
4 //Surface Operators by Abhinav Singh on 07/10/2021
5 #ifndef OPENFPM_PDATA_DCPSE_HPP
6 #define OPENFPM_PDATA_DCPSE_HPP
7 
8 #ifdef HAVE_EIGEN
9 
10 #include "Vector/vector_dist.hpp"
11 #include "MonomialBasis.hpp"
12 #include "DMatrix/EMatrix.hpp"
13 #include "SupportBuilder.hpp"
14 #include "Vandermonde.hpp"
15 #include "DcpseDiagonalScalingMatrix.hpp"
16 #include "DcpseRhs.hpp"
17 #include "hash_map/hopscotch_map.h"
18 #include <type_traits>
19 
20 template<unsigned int N> struct value_t {};
21 
22 template<bool cond>
23 struct is_scalar {
24  template<typename op_type>
25  static auto
26  analyze(const vect_dist_key_dx &key, op_type &o1) -> typename std::remove_reference<decltype(o1.value(
27  key))>::type {
28  return o1.value(key);
29  };
30 };
31 
32 template<>
33 struct is_scalar<false> {
34  template<typename op_type>
35  static auto
36  analyze(const vect_dist_key_dx &key, op_type &o1) -> typename std::remove_reference<decltype(o1.value(
37  key))>::type {
38  return o1.value(key);
39  };
40 };
41 
42 
43 template<unsigned int dim, typename VerletList_type, typename vector_type, typename vector_type2=vector_type>
44 class Dcpse {
45 public:
46  typedef typename vector_type::stype T;
47  typedef typename vector_type::value_type part_type;
48  typedef vector_type vtype;
49 
50  #ifdef SE_CLASS1
51  int update_ctr=0;
52  #endif
53 
54 protected:
55  const Point<dim, unsigned int> differentialSignature;
56  const unsigned int differentialOrder;
57  const MonomialBasis<dim> monomialBasis;
58 
59  openfpm::vector<T> localEps; // Each MPI rank has just access to the local ones
60  openfpm::vector<T> localEpsInvPow; // Each MPI rank has just access to the local ones
61 
62  openfpm::vector<size_t> kerOffsets;
63  openfpm::vector<T> calcKernels;
64  VerletList_type & verletList;
65  vector_type & particlesSupport;
66  vector_type2 & particlesDomain;
67  T rCut;
68  unsigned int convergenceOrder;
69 
70  support_options opt;
71 
72 public:
73  // This works in this way:
74  // 1) User constructs this by giving a domain of points (where one of the properties is the value of our f),
75  // the signature of the differential operator and the error order bound.
76  // 2) The machinery for assembling and solving the linear system for coefficients starts...
77  // 3) The user can then call an evaluate(point) method to get the evaluation of the differential operator
78  // on the given point.
80  T HOverEpsilon=0.9;
81 
82 #ifdef SE_CLASS1
83  int getUpdateCtr() const
84  {
85  return update_ctr;
86  }
87 #endif
88 
89  // Here we require the first element of the aggregate to be:
90  // 1) the value of the function f on the point
91  Dcpse(
93  VerletList_type& verletList,
94  Point<dim, unsigned int> differentialSignature,
95  unsigned int convergenceOrder,
96  T rCut,
97  support_options opt = support_options::RADIUS
98  ):
99  particlesSupport(particles),
100  particlesDomain(particles),
101  verletList(verletList),
102  differentialSignature(differentialSignature),
103  differentialOrder(Monomial<dim>(differentialSignature).order()),
104  monomialBasis(differentialSignature.asArray(), convergenceOrder),
105  opt(opt)
106  {
107  particles.ghost_get_subset(); // This communicates which ghost particles to be excluded from support
108  initializeStaticSize(particles, particles, convergenceOrder, rCut);
109  }
110 
111  Dcpse(
112  vector_type &particlesSupport,
113  vector_type2 &particlesDomain,
114  VerletList_type& verletList,
115  Point<dim, unsigned int> differentialSignature,
116  unsigned int convergenceOrder,
117  T rCut,
118  support_options opt = support_options::RADIUS
119  ):
120  particlesSupport(particlesSupport),
121  particlesDomain(particlesDomain),
122  verletList(verletList),
123  differentialSignature(differentialSignature),
124  differentialOrder(Monomial<dim>(differentialSignature).order()),
125  monomialBasis(differentialSignature.asArray(), convergenceOrder),
126  opt(opt)
127  {
128  particlesSupport.ghost_get_subset();
129  initializeStaticSize(particlesSupport,particlesDomain,convergenceOrder, rCut);
130  }
131 
132  // Default constructor to call from SurfaceDcpse
133  // to initialize protected members
134  Dcpse(
135  vector_type &particlesSupport,
136  vector_type2 &particlesDomain,
137  VerletList_type& verletList,
138  Point<dim, unsigned int> differentialSignature,
139  unsigned int convergenceOrder,
140  support_options opt
141  ):
142  particlesSupport(particlesSupport),
143  particlesDomain(particlesDomain),
144  verletList(verletList),
145  differentialSignature(differentialSignature),
146  differentialOrder(Monomial<dim>(differentialSignature).order()),
147  monomialBasis(differentialSignature.asArray(), convergenceOrder),
148  opt(opt)
149  {}
150 
151  template<unsigned int prp>
152  void DrawKernel(vector_type &particles, int p)
153  {
154  size_t kerOff = kerOffsets.get(p);
155  auto verletIt = this->verletList.getNNIterator(p);
156  int i = 0;
157  while (verletIt.isNext())
158  {
159  size_t q = verletIt.get();
160  particles.template getProp<prp>(q) += calcKernels.get(kerOff+i);
161  ++verletIt; ++i;
162  }
163  }
164 
165  template<unsigned int prp>
166  void DrawKernelNN(vector_type &particles, int p)
167  {
168  size_t kerOff = kerOffsets.get(p);
169  auto verletIt = this->verletList.getNNIterator(p);
170  int i = 0;
171  while (verletIt.isNext())
172  {
173  size_t q = verletIt.get();
174 
175  particles.template getProp<prp>(q) = 1.0;
176  ++verletIt; ++i;
177  }
178  }
179 
180  template<unsigned int prp>
181  void DrawKernel(vector_type &particles, int p, int index)
182  {
183 
184  size_t kerOff = kerOffsets.get(p);
185  auto verletIt = this->verletList.getNNIterator(p);
186  int i = 0;
187  while (verletIt.isNext())
188  {
189  size_t q = verletIt.get();
190  particles.template getProp<prp>(q)[index] += calcKernels.get(kerOff+i);
191  ++verletIt; ++i;
192  }
193  }
194 
195  /*
196  * breif Particle to Particle Interpolation Evaluation
197  */
198  template<unsigned int prp1,unsigned int prp2>
199  void p2p()
200  {
201  typedef typename std::remove_reference<decltype(particlesDomain.template getProp<prp2>(0))>::type T2;
202 
203  auto it = particlesDomain.getDomainIterator();
204  auto epsItInvPow = localEpsInvPow.begin();
205  while (it.isNext()){
206  T epsInvPow = *epsItInvPow;
207  T2 Dfxp = 0;
208 
209  size_t p = it.get();
210  auto verletIt = this->verletList.getNNIterator(p);
211 
212  size_t kerOff = kerOffsets.get(p);
213  int i = 0;
214  while (verletIt.isNext())
215  {
216  size_t q = verletIt.get();
217  T2 fxq = particlesSupport.template getProp<prp1>(q);
218  Dfxp += fxq * calcKernels.get(kerOff+i);
219  ++verletIt; ++i;
220  }
221  Dfxp = epsInvPow*Dfxp;
222  particlesDomain.template getProp<prp2>(p) = Dfxp;
223  ++it;
224  ++epsItInvPow;
225  }
226  }
227 
228  // foggia 16.09.24
238  template<unsigned int prp1,unsigned int prp2, size_t N1>
239  void p2p()
240  {
241  typedef typename std::remove_reference<decltype(particlesDomain.template getProp<prp2>(0)[0])>::type T2;
242 
243  // Using this one could probably get rid of the N1 tparam. It requires some thought.
244  // size_t extent_prp2{std::extent<typename std::remove_reference<decltype(particlesDomain.template getProp<prp2>(0))>::type,0>::value};
245  // std::cout << "extent: " << extent_prp2 << std::endl;
246 
247  auto it = particlesDomain.getDomainIterator();
248  auto epsItInvPow = localEpsInvPow.begin();
249  while (it.isNext()){
250  double epsInvPow = *epsItInvPow;
251  T2 Dfxp[N1];
252 
253  for (size_t i1 = 0; i1 < N1; ++i1)
254  {
255  Dfxp[i1] = 0;
256  }
257 
258  size_t p = it.get();
259  auto verletIt = this->verletList.getNNIterator(p);
260  //Point<dim, typename vector_type::stype> xp = particlesDomain.getPos(p);
261  //T fxp = sign * particlesDomain.template getProp<fValuePos>(p);
262  size_t kerOff = kerOffsets.get(p);
263 
264  int i = 0;
265  while (verletIt.isNext())
266  {
267  size_t q = verletIt.get();
268  T2 fxq[N1];
269 
270  for (size_t i1 = 0; i1 < N1; ++i1)
271  {
272  fxq[i1] = particlesSupport.template getProp<prp1>(q)[i1];
273  Dfxp[i1] += fxq[i1] * calcKernels.get(kerOff+i);
274  }
275 
276  ++verletIt; ++i;
277  }
278 
279  for (size_t i1 = 0; i1 < N1; ++i1)
280  {
281  Dfxp[i1] = epsInvPow*Dfxp[i1];
282  }
283 
284  //
285  //T trueDfxp = particles.template getProp<2>(p);
286  // Store Dfxp in the right position
287 
288  for (size_t i1 = 0; i1 < N1; ++i1)
289  {
290  particlesDomain.template getProp<prp2>(p)[i1] = Dfxp[i1];
291  }
292 
293  //
294  ++it;
295  ++epsItInvPow;
296  }
297  }
298 
302  void save(const std::string &file){
303  auto & v_cl=create_vcluster();
304  size_t req = 0;
305 
306  Packer<decltype(localEps),HeapMemory>::packRequest(localEps,req);
307  Packer<decltype(localEpsInvPow),HeapMemory>::packRequest(localEpsInvPow,req);
308  Packer<decltype(calcKernels),HeapMemory>::packRequest(calcKernels,req);
309  Packer<decltype(kerOffsets),HeapMemory>::packRequest(kerOffsets,req);
310 
311  // allocate the memory
312  HeapMemory pmem;
313  //pmem.allocate(req);
314  ExtPreAlloc<HeapMemory> mem(req,pmem);
315 
316  //Packing
317  Pack_stat sts;
318  Packer<decltype(localEps),HeapMemory>::pack(mem,localEps,sts);
319  Packer<decltype(localEpsInvPow),HeapMemory>::pack(mem,localEpsInvPow,sts);
320  Packer<decltype(calcKernels),HeapMemory>::pack(mem,calcKernels,sts);
321  Packer<decltype(kerOffsets),HeapMemory>::pack(mem,kerOffsets,sts);
322 
323  // Save into a binary file
324  std::ofstream dump (file+"_"+std::to_string(v_cl.rank()), std::ios::out | std::ios::binary);
325  if (dump.is_open() == false)
326  { std::cerr << __FILE__ << ":" << __LINE__ <<" Unable to write since dump is open at rank "<<v_cl.rank()<<std::endl;
327  return;
328  }
329  dump.write ((const char *)pmem.getPointer(), pmem.size());
330  return;
331  }
332 
337  void load(const std::string & file)
338  {
339  auto & v_cl=create_vcluster();
340  std::ifstream fs (file+"_"+std::to_string(v_cl.rank()), std::ios::in | std::ios::binary | std::ios::ate );
341  if (fs.is_open() == false)
342  {
343  std::cerr << __FILE__ << ":" << __LINE__ << " error, opening file: " << file << std::endl;
344  return;
345  }
346 
347  // take the size of the file
348  size_t sz = fs.tellg();
349 
350  fs.close();
351 
352  // reopen the file without ios::ate to read
353  std::ifstream input (file+"_"+std::to_string(v_cl.rank()), std::ios::in | std::ios::binary );
354  if (input.is_open() == false)
355  {//some message here maybe
356  return;}
357 
358  // Create the HeapMemory and the ExtPreAlloc memory
359  size_t req = 0;
360  req += sz;
361  HeapMemory pmem;
362  ExtPreAlloc<HeapMemory> mem(req,pmem);
363 
364  mem.allocate(pmem.size());
365 
366  // read
367  input.read((char *)pmem.getPointer(), sz);
368 
369  //close the file
370  input.close();
371 
372  //Unpacking
373  Unpack_stat ps;
374  Unpacker<decltype(localEps),HeapMemory>::unpack(mem,localEps,ps);
375  Unpacker<decltype(localEpsInvPow),HeapMemory>::unpack(mem,localEpsInvPow,ps);
376  Unpacker<decltype(calcKernels),HeapMemory>::unpack(mem,calcKernels,ps);
377  Unpacker<decltype(kerOffsets),HeapMemory>::unpack(mem,kerOffsets,ps);
378  return;
379  }
380 
381  void checkMomenta(vector_type &particles)
382  {
384  openfpm::vector<aggregate<T,T>> momenta_accu;
385 
386  momenta.resize(monomialBasis.size());
387  momenta_accu.resize(monomialBasis.size());
388 
389  for (int i = 0 ; i < momenta.size() ; i++)
390  {
391  momenta.template get<0>(i) = 3000000000.0;
392  momenta.template get<1>(i) = -3000000000.0;
393  }
394 
395  auto it = particles.getDomainIterator();
396  auto epsIt = localEps.begin();
397  while (it.isNext())
398  {
399  T eps = *epsIt;
400 
401  for (int i = 0 ; i < momenta.size() ; i++)
402  {
403  momenta_accu.template get<0>(i) = 0.0;
404  }
405 
406  size_t p = it.get();
407  auto verletIt = this->verletList.getNNIterator(p);
408 
410  size_t kerOff = kerOffsets.get(p);
411  int i = 0;
412  while (verletIt.isNext())
413  {
414  size_t q = verletIt.get();
415 
417  Point<dim, T> normalizedArg = (xp - xq) / eps;
418 
419  auto ker = calcKernels.get(kerOff+i);
420 
421  int counter = 0;
422  size_t N = monomialBasis.getElements().size();
423 
424  for (size_t j = 0; j < N; ++j)
425  {
426  const Monomial<dim> &m = monomialBasis.getElement(j);
427 
428  T mbValue = m.evaluate(normalizedArg);
429  momenta_accu.template get<0>(counter) += mbValue * ker;
430 
431  ++counter;
432  }
433  ++verletIt; ++i;
434  }
435 
436  for (int i = 0 ; i < momenta.size() ; i++)
437  {
438  if (momenta_accu.template get<0>(i) < momenta.template get<0>(i))
439  {
440  momenta.template get<0>(i) = momenta_accu.template get<0>(i);
441  }
442 
443  if (momenta_accu.template get<1>(i) > momenta.template get<1>(i))
444  {
445  momenta.template get<1>(i) = momenta_accu.template get<0>(i);
446  }
447  }
448 
449  ++it;
450  ++epsIt;
451  }
452 
453  for (size_t i = 0 ; i < momenta.size() ; i++)
454  {
455  std::cout << "MOMENTA: " << monomialBasis.getElement(i) << "Min: " << momenta.template get<0>(i) << " " << "Max: " << momenta.template get<1>(i) << std::endl;
456  }
457  }
458 
467  template<unsigned int fValuePos, unsigned int DfValuePos>
468  void computeDifferentialOperator(vector_type &particles) {
469  char sign = 1;
470  if (differentialOrder % 2 == 0) {
471  sign = -1;
472  }
473 
474  auto it = particles.getDomainIterator();
475  auto epsItInvPow = localEpsInvPow.begin();
476  while (it.isNext()) {
477  T epsInvPow = *epsItInvPow;
478 
479  T Dfxp = 0;
480  size_t p = it.get();
481  auto verletIt = this->verletList.getNNIterator(p);
482  T fxp = sign * particles.template getProp<fValuePos>(p);
483  size_t kerOff = kerOffsets.get(p);
484  int i = 0;
485  while (verletIt.isNext())
486  {
487  size_t q = verletIt.get();
488  T fxq = particles.template getProp<fValuePos>(q);
489 
490  Dfxp += (fxq + fxp) * calcKernels.get(kerOff+i);
491  ++verletIt; ++i;
492  }
493  Dfxp *= epsInvPow;
494  particles.template getProp<DfValuePos>(p) = Dfxp;
495 
496  ++it;
497  ++epsItInvPow;
498  }
499  }
500 
501 
507  inline int getNumNN(const vect_dist_key_dx &p)
508  {
509  return verletList.getNNPart(p);
510  }
511 
520  inline T getCoeffNN(const vect_dist_key_dx &p, int j)
521  {
522  size_t base = kerOffsets.get(p.getKey());
523  return calcKernels.get(base + j);
524  }
525 
531  inline size_t getIndexNN(const vect_dist_key_dx &p, int q)
532  {
533  return verletList.get(p, q);
534  }
535 
536 
537  inline T getSign()
538  {
539  T sign = 1.0;
540  if (differentialOrder % 2 == 0 && differentialOrder!=0) {
541  sign = -1;
542  }
543 
544  return sign;
545  }
546 
547  T getEpsilonInvPrefactor(const vect_dist_key_dx &p)
548  {
549  return localEpsInvPow.get(p.getKey());
550  }
551 
560  template<typename op_type>
561  auto computeDifferentialOperator(const vect_dist_key_dx &p,
562  op_type &o1) -> decltype(is_scalar<std::is_fundamental<decltype(o1.value(
563  p))>::value>::analyze(p, o1)) {
564 
565  typedef decltype(is_scalar<std::is_fundamental<decltype(o1.value(p))>::value>::analyze(p, o1)) expr_type;
566 
567  T sign = 1.0;
568  if (differentialOrder % 2 == 0) {
569  sign = -1;
570  }
571 
572  T eps = localEps.get(p.getKey());
573  T epsInvPow = localEpsInvPow.get(p.getKey());
574 
575  auto &particles = o1.getVector();
576 
577 #ifdef SE_CLASS1
578  if(particles.getMapCtr()!=this->getUpdateCtr())
579  {
580  std::cerr<<__FILE__<<":"<<__LINE__<<" Error: You forgot a DCPSE operator update after map."<<std::endl;
581  }
582 #endif
583 
584  expr_type Dfxp = 0;
585  auto verletIt = this->verletList.getNNIterator(p);
586 
587  expr_type fxp = sign * o1.value(p);
588  size_t kerOff = kerOffsets.get(p);
589 
590  int i = 0;
591  while (verletIt.isNext())
592  {
593  size_t q = verletIt.get();
594 
595  expr_type fxq = o1.value(vect_dist_key_dx(q));
596  Dfxp = Dfxp + (fxq + fxp) * calcKernels.get(kerOff+i);
597  ++verletIt; ++i;
598  }
599 
600  Dfxp = Dfxp * epsInvPow;
601 
602  return Dfxp;
603  }
604 
614  template<typename op_type>
615  auto computeDifferentialOperator(
616  const vect_dist_key_dx &p,
617  op_type &o1,
618  int i
619  ) -> typename decltype(is_scalar<std::is_fundamental<decltype(o1.value(p))>::value>::analyze(p, o1))::coord_type
620  {
621  typedef typename decltype(is_scalar<std::is_fundamental<decltype(o1.value(p))>::value>::analyze(p, o1))::coord_type expr_type;
622 
623  T sign = 1.0;
624  if (differentialOrder % 2 == 0) {
625  sign = -1;
626  }
627 
628  T eps = localEps.get(p.getKey());
629  T epsInvPow = localEpsInvPow.get(p.getKey());
630 
631  auto &particles = o1.getVector();
632 #ifdef SE_CLASS1
633  if(particles.getMapCtr()!=this->getUpdateCtr())
634  {
635  std::cerr<<__FILE__<<":"<<__LINE__<<" Error: You forgot a DCPSE operator update after map."<<std::endl;
636  }
637 #endif
638 
639  expr_type Dfxp = 0;
640 
641  expr_type fxp = sign * o1.value(p)[i];
642  size_t kerOff = kerOffsets.get(p);
643 
644  auto verletIt = this->verletList.getNNIterator(p);
645  int j = 0;
646 
647  while (verletIt.isNext())
648  {
649  size_t q = verletIt.get();
650 
651  expr_type fxq = o1.value(vect_dist_key_dx(q))[i];
652  Dfxp = Dfxp + (fxq + fxp) * calcKernels.get(kerOff+j);
653  ++verletIt; ++j;
654  }
655 
656  Dfxp = Dfxp * epsInvPow;
657 
658  return Dfxp;
659  }
660 
661  void initializeUpdate(vector_type &particlesSupport,vector_type2 &particlesDomain)
662  {
663 #ifdef SE_CLASS1
664  update_ctr=particlesSupport.getMapCtr();
665 #endif
666 
667  localEps.clear();
668  localEpsInvPow.clear();
669  calcKernels.clear();
670  kerOffsets.clear();
671 
672  initializeStaticSize(particlesSupport, particlesDomain, convergenceOrder, rCut);
673  }
674 
675  void initializeUpdate(vector_type &particles)
676  {
677 #ifdef SE_CLASS1
678  update_ctr=particles.getMapCtr();
679 #endif
680 
681  localEps.clear();
682  localEpsInvPow.clear();
683  calcKernels.clear();
684  kerOffsets.clear();
685 
686  initializeStaticSize(particles, particles, convergenceOrder, rCut);
687  }
688 
689 protected:
690  void initializeStaticSize(
691  vector_type &particlesSupport,
692  vector_type2 &particlesDomain,
693  unsigned int convergenceOrder,
694  T rCut
695  ) {
696 #ifdef SE_CLASS1
697  this->update_ctr=particlesSupport.getMapCtr();
698 #endif
699  this->rCut=rCut;
700  this->convergenceOrder=convergenceOrder;
701  auto & v_cl=create_vcluster();
702 #ifdef DCPSE_VERBOSE
703  if(this->opt==LOAD){
704  if(v_cl.rank()==0)
705  {std::cout<<"Warning: Creating empty DC-PSE operator! Please use update or load to get kernels."<<std::endl;}
706  return;
707  }
708 #endif
709  localEps.resize(particlesDomain.size_local());
710  localEpsInvPow.resize(particlesDomain.size_local());
711  kerOffsets.resize(particlesDomain.size_local());
712  kerOffsets.fill(-1);
713  T avgSpacingGlobal=0,avgSpacingGlobal2=0,maxSpacingGlobal=0,minSpacingGlobal=std::numeric_limits<T>::max();
714  size_t Counter=0;
715 
716  auto it = particlesDomain.getDomainIterator();
717  while (it.isNext()) {
718  // Get the points in the support of the DCPSE kernel and store the support for reuse
719  auto p = it.get();
720 
721  auto verletIt = verletList.getNNIterator(p);
722  // Vandermonde matrix computation
724  vandermonde(p, verletIt, monomialBasis,particlesSupport,particlesDomain,HOverEpsilon);
725 
726  EMatrix<T, Eigen::Dynamic, Eigen::Dynamic> V(verletList.getNNPart(p), monomialBasis.size());
727  vandermonde.getMatrix(V);
728 
729  T eps = vandermonde.getEps();
730  avgSpacingGlobal+=eps;
731  T tSpacing = vandermonde.getMinSpacing();
732  avgSpacingGlobal2+=tSpacing;
733  if(tSpacing>maxSpacingGlobal)
734  {
735  maxSpacingGlobal=tSpacing;
736  }
737  if(tSpacing<minSpacingGlobal)
738  {
739  minSpacingGlobal=tSpacing;
740  }
741 
742  localEps.get(p.getKey()) = eps;
743  localEpsInvPow.get(p.getKey()) = 1.0 / openfpm::math::intpowlog(eps,differentialOrder);
744  // Compute the diagonal matrix E
745  DcpseDiagonalScalingMatrix<dim> diagonalScalingMatrix(monomialBasis);
746  EMatrix<T, Eigen::Dynamic, Eigen::Dynamic> E(verletList.getNNPart(p), verletList.getNNPart(p));
747 
748  assert(verletList.getNNPart(p) >= monomialBasis.size());
749  assert(E.rows() == verletList.getNNPart(p));
750  assert(E.cols() == verletList.getNNPart(p));
751 
752  verletIt.reset();
753  diagonalScalingMatrix.buildMatrix(E, p, verletIt, eps, particlesSupport, particlesDomain);
754  // Compute intermediate matrix B
755  EMatrix<T, Eigen::Dynamic, Eigen::Dynamic> B = E * V;
756  // Compute matrix A
757  EMatrix<T, Eigen::Dynamic, Eigen::Dynamic> A = B.transpose() * B;
758 
759  // Compute RHS vector b
760  DcpseRhs<dim> rhs(monomialBasis, differentialSignature);
761  EMatrix<T, Eigen::Dynamic, 1> b(monomialBasis.size(), 1);
762  rhs.template getVector<T>(b);
763  // Get the vector where to store the coefficients...
764  EMatrix<T, Eigen::Dynamic, 1> a(monomialBasis.size(), 1);
765  // ...solve the linear system...
766  a = A.colPivHouseholderQr().solve(b);
767  // ...and store the solution for later reuse
768  kerOffsets.get(p.getKey()) = calcKernels.size();
769 
770  Point<dim, T> xp = particlesDomain.getPos(p);
771 
772  verletIt.reset();
773  unsigned matVRow = 0;
774  while (verletIt.isNext())
775  {
776  size_t q = verletIt.get();
777  Point<dim, T> xq = particlesSupport.getPos(q);
778  Point<dim, T> x_pqNorm = (xp - xq) / eps;
779  calcKernels.add(computeKernel(x_pqNorm, a, V, matVRow, monomialBasis.getElements().size()));
780  ++verletIt;
781  ++matVRow;
782  }
783  ++it;
784  ++Counter;
785  }
786 #ifdef DCPSE_VERBOSE
787  v_cl.sum(avgSpacingGlobal);
788  v_cl.sum(avgSpacingGlobal2);
789  v_cl.max(maxSpacingGlobal);
790  v_cl.min(minSpacingGlobal);
791  v_cl.sum(Counter);
792  v_cl.execute();
793  if(v_cl.rank()==0)
794  {std::cout<<"DCPSE Operator Construction Complete. The global avg spacing in the support <h> is: "<<HOverEpsilon*avgSpacingGlobal/(T(Counter))<<" (c="<<HOverEpsilon<<"). Avg:"<<avgSpacingGlobal2/(T(Counter))<<" Range:["<<minSpacingGlobal<<","<<maxSpacingGlobal<<"]."<<std::endl;}
795 #endif
796  }
797 
798  T computeKernel(Point<dim, T> x, EMatrix<T, Eigen::Dynamic, 1> & a, EMatrix<T, Eigen::Dynamic, Eigen::Dynamic>& V, size_t matVRow, size_t monomialBasisSize) const {
799  T res = 0;
800  T expFactor = exp(-norm2(x));
801 
802  for (size_t i = 0; i < monomialBasisSize; ++i)
803  {
804  T coeff = a(i);
805  T mbValue = V(matVRow, i);
806  res += coeff * mbValue * expFactor;
807  }
808  return res;
809  }
810 
811 
812  T conditionNumber(const EMatrix<T, -1, -1> &V, T condTOL) const {
813  Eigen::JacobiSVD<Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>> svd(V);
814  T cond = svd.singularValues()(0)
815  / svd.singularValues()(svd.singularValues().size() - 1);
816  if (cond > condTOL) {
817  std::cout
818  << "WARNING: cond(V) = " << cond
819  << " is greater than TOL = " << condTOL
820  << ", numPoints(V) = " << V.rows()
821  << std::endl; // debug
822  }
823  return cond;
824  }
825 
826 };
827 
828 
829 template<unsigned int dim, typename VerletList_type, typename vector_type, typename vector_type2=vector_type>
830 class SurfaceDcpse : Dcpse<dim, VerletList_type, vector_type, vector_type2> {
831 public:
832  typedef typename vector_type::stype T;
833 
834 protected:
835  openfpm::vector<size_t> accKerOffsets;
836  openfpm::vector<T> accCalcKernels;
837  openfpm::vector<T> nSpacings;
838  T nSpacing;
839  unsigned int nCount;
840 
841  bool isSurfaceDerivative=false;
842  size_t initialParticleSize;
843 
844 
853  template<unsigned int NORMAL_ID>
854  void createNormalParticles()
855  {
856  this->particlesSupport.template ghost_get<NORMAL_ID>(SKIP_LABELLING);
857  initialParticleSize=this->particlesSupport.size_local_with_ghost();
858  T nSpacing_p = nSpacing;
859 
860  auto it = this->particlesSupport.getDomainAndGhostIterator();
861  while (it.isNext()) {
862  size_t p = it.get();
863 
864  Point<dim,T> xp=this->particlesSupport.getPos(p), Normals=this->particlesSupport.template getProp<NORMAL_ID>(p);
865 
866  if (this->opt == support_options::ADAPTIVE)
867  nSpacing_p = nSpacings.get(p);
868 
869  for(int i=1;i<=nCount;i++) {
870  this->particlesSupport.appendLocal();
871  for(size_t j=0;j<dim;j++)
872  this->particlesSupport.getLastPosEnd()[j] = xp[j]+i*nSpacing_p*Normals[j];
873 
874  this->particlesSupport.appendLocal();
875  for(size_t j=0;j<dim;j++)
876  this->particlesSupport.getLastPosEnd()[j] = xp[j]-i*nSpacing_p*Normals[j];
877  }
878  ++it;
879  }
880 
881  if (this->opt==support_options::ADAPTIVE)
882  {
883  // Get all rCuts from particlesDomain
884  openfpm::vector<T> rCuts;
885  auto it = this->particlesDomain.getDomainIterator();
886  while (it.isNext()) {
887  size_t p = it.get();
888  rCuts.add();
889  rCuts.get(rCuts.size()-1) = this->verletList.getRCuts(p);
890  ++it;
891  }
892  this->verletList.clear(); // clear the Verlet list before refilling it
893 #ifdef SE_CLASS1
894  if (rCuts.size() != this->particlesDomain.size_local())
895  {
896  std::cerr << __FILE__ << ":" << __LINE__
897  << " ERROR: when updating adaptive cut-off Verlet list in createNormalParticles, rCuts.size() != particlesDomain.size_local(), ["
898  << rCuts.size() << "!=" << this->particlesDomain.size_local() << "]" << std::endl;
899  std::runtime_error("Runtime adaptive cut-off Verlet list error");
900  }
901 #endif
902  // Fill out the Verlet list
903  auto domainIt = this->particlesDomain.getDomainIterator();
904  this->verletList.fillNonSymmAdaptiveIterator(domainIt,
905  this->particlesDomain.getPosVector(),
906  this->particlesSupport.getPosVector(),
907  rCuts,
908  this->particlesDomain.size_local()
909  );
910  }
911  else
912  {
913  this->verletList.initCl(
914  this->verletList.getInternalCellList(),
915  this->particlesSupport.getPosVector(),
916  this->particlesSupport.size_local()
917  );
918 
919  auto domainIt = this->particlesDomain.getDomainIterator();
920  this->verletList.Initialize(
921  this->verletList.getInternalCellList(),
922  this->verletList.getRCut(),
923  domainIt,
924  this->particlesDomain.getPosVector(),
925  this->particlesSupport.getPosVector(),
926  this->particlesDomain.size_local()
927  );
928  }
929  }
930 
945  void accumulateAndDeleteNormalParticles()
946  {
947  accCalcKernels.clear();
948  accKerOffsets.clear();
949  accKerOffsets.resize(this->particlesDomain.size_local());
950  accKerOffsets.fill(-1);
951 
953  openfpm::vector_std<size_t> supportBuffer; // list of real (surface) particles
954 
955  auto it = this->particlesDomain.getDomainIterator();
956  while (it.isNext()) {
957  supportBuffer.clear();
958  nMap.clear();
959 
960  size_t p=it.get();
961  size_t kerOff = this->kerOffsets.get(p);
962  // accumulate kernel offsets of each particle in particlesDomain
963  accKerOffsets.get(p)=accCalcKernels.size();
964  auto verletIt = this->verletList.getNNIterator(p);
965  int i = 0;
966 
967  while (verletIt.isNext())
968  {
969  size_t q = verletIt.get();
970 
971  int difference = static_cast<int>(q) - static_cast<int>(initialParticleSize);
972  int real_particle;
973 
974  // error here, last element is overflown
975  // find out whether particle is a real particle (surfSupport) or a virtual normal particle (normalSupport)
976  if (std::signbit(difference))
977  // it's a real (surface) particle
978  real_particle = q;
979  else
980  // it's not (it's a particle along the normal)
981  real_particle = difference / (2 * nCount);
982 
983  auto found=nMap.find(real_particle);
984 
985  if (found != nMap.end())
986  accCalcKernels.get(found->second) += this->calcKernels.get(kerOff+i);
987  else
988  {
989  supportBuffer.add();
990  supportBuffer.get(supportBuffer.size()-1) = real_particle;
991  accCalcKernels.add();
992  accCalcKernels.get(accCalcKernels.size()-1) = this->calcKernels.get(kerOff+i);
993  nMap[real_particle]=accCalcKernels.size()-1;
994  }
995 
996  ++verletIt; ++i;
997  }
998 
999  this->verletList.clear(p);
1000 
1001  // Verlet list ends up with only one particle per particle (themselves)
1002  for (int i = 0; i < supportBuffer.size(); ++i)
1003  this->verletList.addPart(p, supportBuffer.get(i));
1004 
1005  ++it;
1006  }
1007 
1008  // Delete all normal particles in the particlesSupport
1009  this->particlesSupport.discardLocalAppend(initialParticleSize);
1010  // this->localEps.resize(initialParticleSize);
1011  // this->localEpsInvPow.resize(initialParticleSize);
1012  // store accumulated kernels (including normalSupport) into surfSupport particles
1013  this->calcKernels.swap(accCalcKernels);
1014  this->kerOffsets.swap(accKerOffsets);
1015  }
1016 
1017 public:
1035  template<unsigned int NORMAL_ID>
1036  SurfaceDcpse(
1037  vector_type& particlesSupport,
1038  vector_type2& particlesDomain,
1039  VerletList_type& verletList,
1040  Point<dim, unsigned int> differentialSignature,
1041  unsigned int convergenceOrder,
1042  T rCut, // TODO: delete this parameter, it is not used
1043  T nSpacing,
1044  unsigned int nCount,
1045  value_t< NORMAL_ID >,
1046  support_options opt = support_options::RADIUS)
1047  :
1048  Dcpse<dim, VerletList_type, vector_type, vector_type2>(particlesSupport, particlesDomain, verletList, differentialSignature, convergenceOrder, opt),
1049  isSurfaceDerivative(true),
1050  nSpacing(nSpacing),
1051  nCount(nCount)
1052  {
1053  this->rCut = rCut;
1054 
1055  if(opt==support_options::ADAPTIVE) {
1056  // The spacing along the normal has to be similar to the surface spacing of the support particles
1057  particlesSupport.template ghost_get<0>(); // communicate the rCut to the ghost
1058  nSpacings.clear();
1059  auto it = particlesSupport.getDomainAndGhostIterator();
1060  while (it.isNext()) {
1061  size_t p = it.get();
1062  nSpacings.add(getPropSFINAE<T, vector_type, 0>::get(particlesSupport, p)/nCount);
1063  ++it;
1064  }
1065  }
1066 
1067  if(opt!=support_options::LOAD) {
1068  createNormalParticles<NORMAL_ID>();
1069 #ifdef SE_CLASS1
1070  particlesSupport.write("WithNormalParticlesQC"); // TODO: this gives an error in ParaView: the properties of the particles are not there I think.
1071 #endif
1072  }
1073 
1074  this->initializeStaticSize(
1075  particlesSupport,
1076  particlesDomain,
1077  convergenceOrder,
1078  rCut
1079  );
1080 
1081  if(opt!=support_options::LOAD) {
1082  accumulateAndDeleteNormalParticles();
1083  }
1084  }
1085 };
1086 
1087 #endif
1088 #endif //OPENFPM_PDATA_DCPSE_HPP
1089 
This class allocate, and destroy CPU memory.
Definition: HeapMemory.hpp:40
virtual void * getPointer()
get a readable pointer with the data
Definition: HeapMemory.cpp:228
virtual size_t size() const
the the size of the allocated memory
Definition: HeapMemory.cpp:153
Packing status object.
Definition: Pack_stat.hpp:61
Packing class.
Definition: Packer.hpp:50
Unpacking status object.
Definition: Pack_stat.hpp:16
Unpacker class.
Definition: Unpacker.hpp:34
size_t size()
Stub size.
Definition: map_vector.hpp:212
Grid key for a distributed grid.
size_t size_local() const
return the local size of vector_dist_ws
vector_dist_iterator_subset getDomainIterator() const
Get an iterator that traverse the particles in the domain.
auto getPos(size_t vec_key) -> decltype(vd.getPos(vec_key))
Get the position of an element.
Distributed vector.
void appendLocal()
Add at the END of local and ghost particle.
size_t size_local_with_ghost() const
return the local size of the vector
auto getPos(vect_dist_key_dx vec_key) -> decltype(vPos.template get< 0 >(vec_key.getKey()))
Get the position of an element.
vector_dist_iterator getDomainIterator() const
Get an iterator that traverse the particles in the domain.
bool write(std::string out, int opt=VTK_WRITER)
Output particle position and properties.
vector_dist_iterator getDomainAndGhostIterator() const
Get an iterator that traverse the particles in the domain.
auto getLastPosEnd() -> decltype(vPos.template get< 0 >(0))
Get the position of the last element after ghost.
void ghost_get_subset()
Stub does not do anything.
void discardLocalAppend(size_t rs)
Resize the vector at the end of the ghost (locally)
aggregate of properties, from a list of object if create a struct that follow the OPENFPM native stru...
Definition: aggregate.hpp:221