OpenFPM_pdata  4.1.0
Project that contain the implementation of distributed structures
DCPSE_Solver.hpp
1 //
2 // Created by Abhinav Singh on 20.01.20.
3 //
4 
5 #ifndef OPENFPM_PDATA_DCPSE_SOLVER_HPP
6 #define OPENFPM_PDATA_DCPSE_SOLVER_HPP
7 #ifdef HAVE_EIGEN
8 
9 
10 #include "DCPSE_op.hpp"
11 #include "Matrix/SparseMatrix.hpp"
12 #include "Vector/Vector.hpp"
13 #include "NN/CellList/CellDecomposer.hpp"
14 #include "Vector/Vector_util.hpp"
15 #include "Vector/vector_dist.hpp"
16 #include "Solvers/umfpack_solver.hpp"
17 #include "Solvers/petsc_solver.hpp"
18 #include "util/eq_solve_common.hpp"
19 
20 /*enum eq_struct
21 {
22  VECTOR,
23  SCALAR
24 };*/
25 
26 //template<unsigned int prp_id> using prop_id = boost::mpl::int_<prp_id>;
38 template<typename Sys_eqs, typename particles_type>
39 class DCPSE_scheme {
40 
42  typename Sys_eqs::SparseMatrix_type A;
43 
45  typename Sys_eqs::Vector_type b;
46 
48  typedef typename Sys_eqs::SparseMatrix_type::triplet_type triplet;
49 
52 
54  p_map_type p_map;
55 
58 
60  particles_type &parts;
61 
63  //int col_sm[Sys_eqs::nvar];
64 
69  size_t s_pnt;
70 
72  size_t row;
73 
75  size_t row_b;
76 
78  size_t tot;
79 
81  options_solver opt;
82 
83  size_t offset;
84 
85 template<typename options>
89  void construct_pmap(options opt = options_solver::STANDARD) {
90  Vcluster<> &v_cl = create_vcluster();
91 
92  // Calculate the size of the local domain
93  size_t sz = p_map.size_local();
94 
95  // Get the total size of the local grids on each processors
96  v_cl.allGather(sz, pnt);
97  v_cl.execute();
98  s_pnt = 0;
99 
100  // calculate the starting point for this processor
101  for (size_t i = 0; i < v_cl.getProcessUnitID(); i++)
102  s_pnt += pnt.get(i);
103 
104  tot = sz;
105  v_cl.sum(tot);
106  v_cl.execute();
107 
108  // resize b if needed
109  if (opt == options_solver::STANDARD) {
110  b.resize(Sys_eqs::nvar * tot, Sys_eqs::nvar * sz);
111  } else if (opt == options_solver::LAGRANGE_MULTIPLIER) {
112  if (v_cl.rank() == v_cl.size() - 1) {
113  b.resize(Sys_eqs::nvar * tot + 1, Sys_eqs::nvar * sz + 1);
114  } else {
115  b.resize(Sys_eqs::nvar * tot + 1, Sys_eqs::nvar * sz);
116  }
117  }
118  //Use Custom number of constraints using opt as an integer
119  else {
120  if (v_cl.rank() == v_cl.size() - 1) {
121  b.resize(Sys_eqs::nvar * tot - offset, Sys_eqs::nvar * sz - offset);
122  } else {
123  b.resize(Sys_eqs::nvar * tot - offset, Sys_eqs::nvar * sz);
124  }
125  }
126 
127  // Calculate the starting point
128 
129  // Counter
130  size_t cnt = 0;
131 
132  // Create the re-mapping grid
133  auto it = p_map.getDomainIterator();
134 
135  while (it.isNext()) {
136  auto key = it.get();
137 
138  for (int i = 0; i < particles_type::dims; i++) {
139  p_map.getPos(key)[i] = parts.getPos(key)[i];
140  }
141 
142  p_map.template getProp<0>(key) = cnt + s_pnt;
143 
144  ++cnt;
145  ++it;
146  }
147 
148  // sync the ghost
149  p_map.template ghost_get<0>();
150  }
151 
153  struct constant_b {
155  typename Sys_eqs::stype scal;
156 
162  constant_b(typename Sys_eqs::stype scal) {
163  this->scal = scal;
164  }
165 
175  typename Sys_eqs::stype get(size_t key) {
176  return scal;
177  }
178  };
179 
181  template<unsigned int prp_id>
182  struct variable_b {
184  typename Sys_eqs::stype scal;
185 
186  particles_type &parts;
187 
193  variable_b(particles_type &parts)
194  : parts(parts) {}
195 
205  inline typename Sys_eqs::stype get(size_t key) {
206  return parts.template getProp<prp_id>(key);
207  }
208  };
209 
210 
214  void consistency() {
215  openfpm::vector<triplet> &trpl = A.getMatrixTriplets();
216 
217  // A and B must have the same rows
218  if (row != row_b) {
219  std::cerr << "Error " << __FILE__ << ":" << __LINE__
220  << " the term B and the Matrix A for Ax=B must contain the same number of rows " << row << "!=" << row_b << "\n";
221  return;
222  }
223  if (row_b != p_map.size_local() * Sys_eqs::nvar) {
224  std::cerr << "Error " << __FILE__ << ":" << __LINE__ << " your system is underdetermined you set "
225  << row_b << " conditions " << " but i am expecting " << p_map.size_local() * Sys_eqs::nvar
226  << std::endl;
227  return;
228  }
229 
230  // Indicate all the non zero rows
232  nz_rows.resize(row_b);
233 
234  for (size_t i = 0; i < trpl.size(); i++) {
235  if (trpl.get(i).row() - s_pnt * Sys_eqs::nvar >= nz_rows.size()) {
236  std::cerr << "Error " << __FILE__ << ":" << __LINE__
237  << " It seems that you are setting colums that does not exist \n";
238  }
239  if (trpl.get(i).value() != 0) { nz_rows.get(trpl.get(i).row() - s_pnt * Sys_eqs::nvar) = true; }
240  }
241 
242  // Indicate all the non zero colums
243  // This check can be done only on single processor
244 
245  Vcluster<> &v_cl = create_vcluster();
246  if (v_cl.getProcessingUnits() == 1) {
248  nz_cols.resize(row_b);
249 
250  for (size_t i = 0; i < trpl.size(); i++) {
251  if (trpl.get(i).value() != 0) { nz_cols.get(trpl.get(i).col()) = true; }
252  }
253 
254  // all the rows must have a non zero element
255  for (size_t i = 0; i < nz_rows.size(); i++) {
256  if (nz_rows.get(i) == false) {
257  std::cerr << "Error: " << __FILE__ << ":" << __LINE__ << " Ill posed matrix row " << i
258  << " is not filled " << " equation: " << "\n";
259  }
260  }
261 
262  // all the colums must have a non zero element
263  for (size_t i = 0; i < nz_cols.size(); i++) {
264  if (nz_cols.get(i) == false)
265  std::cerr << "Error: " << __FILE__ << ":" << __LINE__ << " Ill posed matrix colum " << i
266  << " is not filled\n";
267  }
268  }
269  }
270 
278  template<typename solType, typename expr_type>
279  void copy_impl(solType & x, expr_type exp, unsigned int comp)
280  {
281  auto & parts = exp.getVector();
282 
283  auto it = parts.getDomainIterator();
284 
285  while (it.isNext()) {
286  auto p = it.get();
287  exp.value(p) = x(p.getKey() * Sys_eqs::nvar + comp + s_pnt * Sys_eqs::nvar);
288  ++it;
289  }
290  }
291 
292  template<typename solType, typename exp1, typename ... othersExp>
293  void copy_nested(solType &x, unsigned int &comp, exp1 exp, othersExp ... exps) {
294  copy_impl(x, exp, comp);
295  comp++;
296 
297  copy_nested(x, comp, exps ...);
298  }
299 
300 
301  template<typename solType, typename exp1>
302  void copy_nested(solType &x, unsigned int &comp, exp1 exp) {
303  copy_impl(x, exp, comp);
304  comp++;
305  }
306 
307 public:
308 
318 /* void setEquationStructure(std::initializer_list<eq_struct> l)
319  {
320  int i = 0;
321  for (eq_struct e : l)
322  {
323  if (e == eq_struct::VECTOR)
324  {
325  for (int j = 0 ; j < Sys_eqs::dims ; j++)
326  {
327  col_sm[i+j] = i;
328  }
329  i += Sys_eqs::dims;
330  }
331  else
332  {
333  col_sm[i] = i;
334  }
335  }
336  }*/
337 
338 
346  template<typename ... expr_type>
347  void solve(expr_type ... exps) {
348  if (sizeof...(exps) != Sys_eqs::nvar) {
349  std::cerr << __FILE__ << ":" << __LINE__ << " Error the number of properties you gave does not match the solution in\
350  dimensionality, I am expecting " << Sys_eqs::nvar <<
351  " properties " << std::endl;
352  };
353  typename Sys_eqs::solver_type solver;
354 // umfpack_solver<double> solver;
355  auto x = solver.solve(getA(opt), getB(opt));
356 
357  unsigned int comp = 0;
358  copy_nested(x, comp, exps ...);
359  }
360 
369  template<typename SolverType, typename ... expr_type>
370  void solve_with_solver(SolverType &solver, expr_type ... exps) {
371 #ifdef SE_CLASS1
372 
373  if (sizeof...(exps) != Sys_eqs::nvar) {
374  std::cerr << __FILE__ << ":" << __LINE__ << " Error the number of properties you gave does not match the solution in\
375  dimensionality, I am expecting " << Sys_eqs::nvar <<
376  " properties " << std::endl;
377  };
378 #endif
379  auto x = solver.solve(getA(opt), getB(opt));
380 
381  unsigned int comp = 0;
382  copy_nested(x, comp, exps ...);
383  }
384 
393 /* template<typename NullspaceType, typename SolverType, typename ... expr_type>
394  void solve_with_nullspace_solver(NullspaceType &nullspace,SolverType &solver, expr_type ... exps) {
395 #ifdef SE_CLASS1
396 
397  if (sizeof...(exps) != Sys_eqs::nvar) {
398  std::cerr << __FILE__ << ":" << __LINE__ << " Error the number of properties you gave does not match the solution in\
399  dimensionality, I am expecting " << Sys_eqs::nvar <<
400  " properties " << std::endl;
401  };
402 #endif
403  auto x = solver.nullspace_solve(nullspace,getA(opt), getB(opt));
404 
405  unsigned int comp = 0;
406  copy_nested(x, comp, exps ...);
407  }*/
408 
417  template<typename SolverType, typename ... expr_type>
418  void solve_with_default_nullspace_solver(SolverType &solver, expr_type ... exps) {
419 #ifdef SE_CLASS1
420 
421  if (sizeof...(exps) != Sys_eqs::nvar) {
422  std::cerr << __FILE__ << ":" << __LINE__ << " Error the number of properties you gave does not match the solution in\
423  dimensionality, I am expecting " << Sys_eqs::nvar <<
424  " properties " << std::endl;
425  };
426 #endif
427  auto x = solver.with_constant_nullspace_solve(getA(opt), getB(opt));
428 
429  unsigned int comp = 0;
430  copy_nested(x, comp, exps ...);
431  }
432 
440  template<typename SolverType, typename ... expr_type>
441  void try_solve_with_solver(SolverType &solver, expr_type ... exps) {
442  if (sizeof...(exps) != Sys_eqs::nvar) {
443  std::cerr << __FILE__ << ":" << __LINE__ << " Error the number of properties you gave does not match the solution in\
444  dimensionality, I am expecting " << Sys_eqs::nvar <<
445  " properties " << std::endl;
446  };
447 
448  auto x = solver.try_solve(getA(opt), getB(opt));
449 
450  unsigned int comp = 0;
451  copy_nested(x, comp, exps ...);
452  }
453 
454  void reset_b()
455  {
456  row_b = 0;
457  }
458 
459  void reset(particles_type &part, options_solver opt = options_solver::STANDARD)
460  {
461  row = 0;
462  row_b = 0;
463 
464  p_map.clear();
465  p_map.resize(part.size_local());
466 
467  A.getMatrixTriplets().clear();
468 
469  construct_pmap(opt);
470  }
471 
472  void reset_nodec()
473  {
474  row = 0;
475  row_b = 0;
476 
477  A.getMatrixTriplets().clear();
478  }
479 
487  DCPSE_scheme(particles_type &part, options_solver opt = options_solver::STANDARD)
488  : parts(part), p_map(part.getDecomposition(), 0), row(0), row_b(0), opt(opt) {
489  p_map.resize(part.size_local());
490 
491  construct_pmap(opt);
492  }
493 
494  /*DCPSE_scheme(particles_type &part, int option_num)
495  : parts(part), p_map(part.getDecomposition(), 0), row(0), row_b(0),opt(options_solver::CUSTOM),offset(option_num) {
496  p_map.resize(part.size_local());
497  construct_pmap(option_num);
498  }*/
499 
500 
514  template<typename T, typename index_type, unsigned int prp_id>
515  void impose(const T &op, openfpm::vector<index_type> &subset,
516  const prop_id<prp_id> &num,
517  eq_id id = eq_id()) {
518  auto itd = subset.template getIteratorElements<0>();
519 
520  variable_b<prp_id> vb(parts);
521 
522  impose_git(op, vb, id.getId(), itd);
523  }
524 
535  template<typename index_type, unsigned int prp_id>
536  void impose_b(openfpm::vector<index_type> &subset,
537  const prop_id<prp_id> &num,
538  eq_id id = eq_id()) {
539  auto itd = subset.template getIteratorElements<0>();
540 
541  variable_b<prp_id> vb(parts);
542 
543  impose_git_b(vb, id.getId(), itd);
544  }
545 
559  template<typename T, typename index_type, typename RHS_type, typename sfinae = typename std::enable_if<!std::is_fundamental<RHS_type>::type::value>::type>
560  void impose(const T &op, openfpm::vector<index_type> &subset,
561  const RHS_type &rhs,
562  eq_id id = eq_id()) {
563  auto itd = subset.template getIteratorElements<0>();
564 
565  impose_git(op, rhs, id.getId(), itd);
566  }
578  template<typename index_type, typename RHS_type, typename sfinae = typename std::enable_if<!std::is_fundamental<RHS_type>::type::value>::type>
579  void impose_b(openfpm::vector<index_type> &subset,
580  const RHS_type &rhs,
581  eq_id id = eq_id()) {
582  auto itd = subset.template getIteratorElements<0>();
583  impose_git_b(rhs, id.getId(), itd);
584  }
585 
599  template<typename T, typename index_type>
600  void impose(const T &op,
602  const typename Sys_eqs::stype num,
603  eq_id id = eq_id()) {
604  auto itd = subset.template getIteratorElements<0>();
605 
606  constant_b b(num);
607 
608  impose_git(op, b, id.getId(), itd);
609  }
621  template< typename index_type>
622  void impose_b(openfpm::vector<index_type> &subset,
623  const typename Sys_eqs::stype num,
624  eq_id id = eq_id()) {
625  auto itd = subset.template getIteratorElements<0>();
626 
627  constant_b b(num);
628 
629  impose_git_b(b, id.getId(), itd);
630  }
631 
637  template<typename options>
638  typename Sys_eqs::SparseMatrix_type &getA(options opt) {
639 #ifdef SE_CLASS1
640  consistency();
641 #endif
642  if (opt == options_solver::STANDARD) {
643  A.resize(tot * Sys_eqs::nvar, tot * Sys_eqs::nvar,
644  p_map.size_local() * Sys_eqs::nvar,
645  p_map.size_local() * Sys_eqs::nvar);
646  }
647  else if (opt == options_solver::LAGRANGE_MULTIPLIER) {
648  auto &v_cl = create_vcluster();
649  openfpm::vector<triplet> &trpl = A.getMatrixTriplets();
650 
651  if (v_cl.rank() == v_cl.size() - 1) {
652  A.resize(tot * Sys_eqs::nvar + 1, tot * Sys_eqs::nvar + 1,
653  p_map.size_local() * Sys_eqs::nvar + 1,
654  p_map.size_local() * Sys_eqs::nvar + 1);
655 
656  for (int i = 0; i < tot * Sys_eqs::nvar; i++) {
657  triplet t1;
658 
659  t1.row() = tot * Sys_eqs::nvar;
660  t1.col() = i;
661  t1.value() = 1;
662 
663  trpl.add(t1);
664  }
665 
666  for (int i = 0; i < p_map.size_local() * Sys_eqs::nvar; i++) {
667  triplet t2;
668 
669  t2.row() = i + s_pnt * Sys_eqs::nvar;
670  t2.col() = tot * Sys_eqs::nvar;
671  t2.value() = 1;
672 
673  trpl.add(t2);
674  }
675 
676  triplet t3;
677 
678  t3.col() = tot * Sys_eqs::nvar;
679  t3.row() = tot * Sys_eqs::nvar;
680  t3.value() = 0;
681 
682  trpl.add(t3);
683 
684  row_b++;
685  row++;
686  }
687  else {
688  A.resize(tot * Sys_eqs::nvar + 1, tot * Sys_eqs::nvar + 1,
689  p_map.size_local() * Sys_eqs::nvar,
690  p_map.size_local() * Sys_eqs::nvar);
691 
692  for (int i = 0; i < p_map.size_local() * Sys_eqs::nvar; i++) {
693  triplet t2;
694 
695  t2.row() = i + s_pnt * Sys_eqs::nvar;
696  t2.col() = tot * Sys_eqs::nvar;
697  t2.value() = 1;
698 
699  trpl.add(t2);
700  }
701  }
702 
703 
704  }
705 
706  else{
707  auto &v_cl = create_vcluster();
708  if (v_cl.rank() == v_cl.size() - 1) {
709  A.resize(tot * Sys_eqs::nvar - offset, tot * Sys_eqs::nvar - offset,
710  p_map.size_local() * Sys_eqs::nvar - offset,
711  p_map.size_local() * Sys_eqs::nvar - offset);
712  }
713  else {
714  A.resize(tot * Sys_eqs::nvar - offset, tot * Sys_eqs::nvar - offset,
715  p_map.size_local() * Sys_eqs::nvar,
716  p_map.size_local() * Sys_eqs::nvar);
717  }
718  }
719 
720  return A;
721 
722  }
723 
729  typename Sys_eqs::Vector_type &getB(options_solver opt = options_solver::STANDARD) {
730 #ifdef SE_CLASS1
731  consistency();
732 #endif
733  if (opt == options_solver::LAGRANGE_MULTIPLIER) {
734  auto &v_cl = create_vcluster();
735  if (v_cl.rank() == v_cl.size() - 1) {
736 
737  b(tot * Sys_eqs::nvar) = 0;
738  }
739  }
740  return b;
741  }
742 
743 
744  template<typename bop, typename iterator>
745  void impose_git_b(bop num,
746  long int id,
747  const iterator &it_d) {
748  auto it = it_d;
749  // iterate all particles points
750  while (it.isNext()) {
751  // get the particle
752  auto key = it.get();
753  // Calculate the non-zero colums
754  b(p_map.template getProp<0>(key) * Sys_eqs::nvar + id) = num.get(key);
755 // std::cout << "b=(" << p_map.template getProp<0>(key)*Sys_eqs::nvar + id << "," << num.get(key)<<")" <<"\n";
756 
757  // if SE_CLASS1 is defined check the position
758 #ifdef SE_CLASS1
759  // T::position(key,gs,s_pos);
760 #endif
761  ++row_b;
762  ++it;
763  }
764  }
765 
781  template<typename T, typename bop, typename iterator>
782  void impose_git(const T &op,
783  bop num,
784  long int id,
785  const iterator &it_d) {
786  openfpm::vector<triplet> &trpl = A.getMatrixTriplets();
787 
788  auto it = it_d;
789 
790  //std::unordered_map<long int, typename particles_type::stype> cols;
791 
793 
794  // iterate all particles points
795  while (it.isNext()) {
796  // get the particle
797  auto key = it.get();
798 
799 /*
800  if (key == 298 && create_vcluster().rank() == 1)
801  {
802  int debug = 0;
803  debug++;
804  }
805 */
806 
807  // Calculate the non-zero colums
808  typename Sys_eqs::stype coeff = 1.0;
809  op.template value_nz<Sys_eqs>(p_map, key, cols, coeff, 0);
810 
811  // indicate if the diagonal has been set
812  bool is_diag = false;
813 
814  // create the triplet
815  for (auto it = cols.begin(); it != cols.end(); ++it) {
816  trpl.add();
817  trpl.last().row() = p_map.template getProp<0>(key) * Sys_eqs::nvar + id;
818  trpl.last().col() = it->first;
819  trpl.last().value() = it->second;
820  if (trpl.last().row() == trpl.last().col())
821  {is_diag = true;}
822  }
823 
824  // If does not have a diagonal entry put it to zero
825  if (is_diag == false)
826  {
827  trpl.add();
828  trpl.last().row() = p_map.template getProp<0>(key) * Sys_eqs::nvar + id;
829  trpl.last().col() = p_map.template getProp<0>(key) * Sys_eqs::nvar + id;
830  trpl.last().value() = 0.0;
831  }
832  b(p_map.template getProp<0>(key) * Sys_eqs::nvar + id) = num.get(key);
833  cols.clear();
834 
835  // if SE_CLASS1 is defined check the position
836 #ifdef SE_CLASS1
837  // T::position(key,gs,s_pos);
838 #endif
839 
840  ++row;
841  ++row_b;
842  ++it;
843  }
844  }
845 
846 };
847 
848 #include "DCPSE/DCPSE_op/EqnsStruct.hpp"
849 #endif //Eigen
850 #endif //OPENFPM_PDATA_DCPSE_SOLVER_HPP
size_t getProcessUnitID()
Get the process unit id.
It store the non zero elements of the matrix.
bool allGather(T &send, openfpm::vector< T, Mem, gr > &v)
Gather the data from all processors.
auto getPos(vect_dist_key_dx vec_key) -> decltype(v_pos.template get< 0 >(vec_key.getKey()))
Get the position of an element.
size_t size()
Stub size.
Definition: map_vector.hpp:211
Implementation of VCluster class.
Definition: VCluster.hpp:58
void resize(size_t rs)
Resize the vector (locally)
void execute()
Execute all the requests.
It model an expression expr1 * expr2.
Definition: mul.hpp:119
size_t size_local() const
return the local size of the vector
size_t rank()
Get the process unit id.
static void value(const grid_dist_id< Sys_eqs::dims, typename Sys_eqs::stype, aggregate< size_t >, typename Sys_eqs::b_grid::decomposition::extended_type > &g_map, grid_dist_key_dx< Sys_eqs::dims > &kmap, const grid_sm< Sys_eqs::dims, void > &gs, typename Sys_eqs::stype(&spacing)[Sys_eqs::dims], std::unordered_map< long int, typename Sys_eqs::stype > &cols, typename Sys_eqs::stype coeff)
Calculate which colums of the Matrix are non zero.
Definition: mul.hpp:140
size_t getProcessingUnits()
Get the total number of processors.
static const unsigned int dims
dimensions of space
void sum(T &num)
Sum the numbers across all processors and get the result.
Distributed vector.
size_t size()
Get the total number of processors.
vect_dist_key_dx get()
Get the actual key.
vector_dist_iterator getDomainIterator() const
Get an iterator that traverse the particles in the domain.