openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
SF_abstract_vector.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (c) NumeriCor GmbH
2 // SPDX-License-Identifier: Apache-2.0
3 
4 #ifndef _SF_ABSTRACT_VECTOR_H
5 #define _SF_ABSTRACT_VECTOR_H
6 
7 #include <mpi.h>
8 
9 #include <memory>
10 
11 #include "SF_container.h"
12 #include "SF_globals.h"
13 #include "SF_parallel_layout.h"
14 #include "SF_parallel_utils.h"
15 #include "backend.h"
16 #include "mpi_utils.h"
17 
18 #include "hashmap.hpp"
19 
20 namespace SF {
21 
22 // Forward declare the scattering class
23 class scattering;
24 
38 template<class T, class S>
40 
41  public:
42 
45 
47  int dpn = 0;
49 
51  virtual ~abstract_vector() = default;
52 
60  virtual void init(const meshdata<mesh_int_t, mesh_real_t>& imesh,
61  int idpn,
62  ltype inp_layout) = 0;
63 
69  virtual void init(const abstract_vector<T, S>& vec) = 0;
70 
79  virtual void init(T igsize, T ilsize, int idpn = 1, ltype ilayout = unset) = 0;
80 
90  inline std::tuple<T, T> init_common(const meshdata<mesh_int_t, mesh_real_t>& imesh,
91  int idpn,
92  ltype inp_layout)
93  {
94  mesh = &imesh;
95  dpn = idpn;
96  T N = 0, n = 0;
97 
98  switch(inp_layout) {
99  case algebraic:
100  N = static_cast<T>(mesh->pl.num_global_idx()) * static_cast<T>(dpn);
101  n = static_cast<T>(mesh->pl.num_algebraic_idx()) * static_cast<T>(dpn);
102  layout = algebraic;
103  break;
104 
105  case nodal:
106  N = static_cast<T>(mesh->l_numpts) * static_cast<T>(dpn);
107  MPI_Allreduce(MPI_IN_PLACE, &N, 1, opencarp::mpi_datatype<T>(), MPI_SUM, mesh->comm);
108  n = static_cast<T>(mesh->l_numpts) * static_cast<T>(dpn);
109  layout = nodal;
110  break;
111 
112  case elemwise:
113  N = static_cast<T>(mesh->g_numelem*dpn);
114  n = static_cast<T>(mesh->l_numelem*dpn);
115  layout = elemwise;
116  break;
117 
118  default: break;
119  }
120 
121  return std::tuple<T, T>(N, n);
122  }
123 
132  virtual void set(const vector<T>& idx, const vector<S>& vals, const bool additive = false, const bool local = false) = 0;
133 
142  virtual void set(const vector<T>& idx, const S val, const bool additive = false, const bool local = false) = 0;
143 
149  virtual void set(const S val) = 0;
150 
160  virtual void set(const T idx, const S val) = 0;
161 
169  virtual void get(const vector<T> & idx, S *out) = 0;
170 
178  virtual S get(const T idx) = 0;
179 
185  virtual void operator*=(const S sca) = 0;
186 
192  virtual void operator /= (const S sca) = 0;
193 
199  virtual void operator*=(const abstract_vector<T, S>& vec) = 0;
200 
207  virtual void add_scaled(const abstract_vector<T, S>& vec, S k) = 0;
208 
214  virtual void operator += (const abstract_vector<T,S> & vec) = 0;
215 
221  virtual void operator-=(const abstract_vector<T, S>& vec) = 0;
222 
228  virtual void operator+=(S c) = 0;
229 
235  virtual void operator=(const vector<S>& rhs) = 0;
236 
242  virtual void operator=(const abstract_vector<T, S>& rhs) = 0;
243 
251  virtual void shallow_copy(const abstract_vector<T, S>& v) = 0;
252 
260  virtual void deep_copy(const abstract_vector<T, S>& v) = 0;
261 
274  virtual void overshadow(const abstract_vector<T, S>& sub, bool member, int offset, int sz, bool share) = 0;
275 
281  virtual T lsize() const = 0;
282 
288  virtual T gsize() const = 0;
289 
296  virtual void get_ownership_range(T& start, T& stop) const = 0;
297 
305  virtual S* ptr() = 0;
306 
315  virtual const S* const_ptr() const = 0;
316 
325  virtual S* device_ptr() = 0;
326 
334  virtual void release_ptr(S*& p) = 0;
335 
343  virtual void const_release_ptr(const S*& p) const = 0;
344 
352  virtual void release_device_ptr(S*& p) = 0;
353 
359  virtual S mag() const = 0;
360 
366  virtual S sum() const = 0;
367 
373  virtual S min() const = 0;
374 
382  virtual S dot(const abstract_vector<T, S>& v) const = 0;
383 
389  virtual bool is_init() const = 0;
390 
396  virtual std::string to_string() const = 0;
397 
403  virtual bool equals(const abstract_vector<T,S> & rhs) const = 0;
404 
408  virtual void finish_assembly() = 0;
409 
417  virtual void forward(abstract_vector<T, S>& out, scattering &sc, bool add = false) = 0;
418 
426  virtual void backward(abstract_vector<T,S> & out, scattering &sc, bool add = false) = 0;
427 
434  virtual void apply_scattering(scattering& sc, bool fwd) = 0;
435 
444  inline size_t write_ascii(const char* file, bool write_header)
445  {
446  int size, rank;
447  MPI_Comm comm = this->mesh != NULL ? this->mesh->comm : SF_COMM;
448  MPI_Comm_rank(comm, &rank);
449  MPI_Comm_size(comm, &size);
450  const MPI_Datatype mpi_t = opencarp::mpi_datatype<T>();
451 
452  T glb_size = this->gsize();
453  T loc_size = this->lsize();
454 
455  int err = 0;
456  FILE* fd = NULL;
457  long int nwr = 0;
458 
459  if(rank == 0) {
460  fd = fopen(file, "w");
461  if(!fd) err = 1;
462  }
463 
464  MPI_Allreduce(MPI_IN_PLACE, &err, 1, MPI_INT, MPI_MAX, comm);
465  if(err) {
466  treat_file_open_error(file, __func__, errno, false, rank);
467  return nwr;
468  }
469 
470  S* p = this->ptr();
471 
472  if(rank == 0) {
473  if(write_header)
474  fprintf(fd, "%jd\n", opencarp::printable_int(glb_size));
475 
476  for(T i=0; i<loc_size/this->dpn; i++) {
477  for(int j=0; j<this->dpn; j++)
478  nwr += fprintf(fd, "%f ", p[i*this->dpn+j]);
479  nwr += fprintf(fd, "\n");
480  }
481 
482  vector<S> wbuff;
483  for(int pid=1; pid < size; pid++)
484  {
485  T rsize;
486  MPI_Status stat;
487 
488  MPI_Recv(&rsize, 1, mpi_t, pid, SF_MPITAG, comm, &stat);
489  wbuff.resize(rsize);
490  MPI_Recv(wbuff.data(), rsize*sizeof(S), MPI_BYTE, pid, SF_MPITAG, comm, &stat);
491 
492  for(T i=0; i<rsize/this->dpn; i++) {
493  for(int j=0; j<this->dpn; j++)
494  nwr += fprintf(fd, "%f ", wbuff[i*this->dpn+j]);
495  nwr += fprintf(fd, "\n");
496  }
497  }
498  fclose(fd);
499  }
500  else {
501  MPI_Send(&loc_size, 1, mpi_t, 0, SF_MPITAG, comm);
502  MPI_Send(p, loc_size*sizeof(S), MPI_BYTE, 0, SF_MPITAG, comm);
503  }
504 
505  this->release_ptr(p);
506  MPI_Bcast(&nwr, 1, MPI_LONG, 0, comm);
507 
508  return nwr;
509  }
510 
518  template<typename V>
519  inline size_t write_binary(FILE* fd)
520  {
521  int size, rank;
522  MPI_Comm comm = this->mesh != NULL ? this->mesh->comm : SF_COMM;
523  MPI_Comm_rank(comm, &rank); MPI_Comm_size(comm, &size);
524 
525  long int loc_size = this->lsize();
526  S* p = this->ptr();
527 
528  vector<V> buff(loc_size);
529  for(long int i=0; i<loc_size; i++) buff[i] = p[i];
530 
531  long int nwr = root_write(fd, buff, comm);
532 
533  this->release_ptr(p);
534  return nwr;
535  }
536 
538  template<typename V>
539  inline size_t write_binary(std::string file)
540  {
541  size_t nwr = 0;
542  MPI_Comm comm = this->mesh != NULL ? this->mesh->comm : SF_COMM;
543  int rank; MPI_Comm_rank(comm, &rank);
544 
545  FILE* fd = NULL;
546  int error = 0;
547 
548  if(rank == 0) {
549  fd = fopen(file.c_str(), "w");
550  if(fd == NULL)
551  error++;
552  }
553 
554  MPI_Allreduce(MPI_IN_PLACE, &error, 1, MPI_INT, MPI_SUM, comm);
555 
556  if(error == 0) {
557  nwr = this->write_binary<V>(fd);
558  if(fd) fclose(fd);
559  }
560  else {
561  treat_file_open_error(file.c_str(), __func__, errno, false, rank);
562  }
563 
564  return nwr;
565  }
566 
567  template<typename V>
568  inline size_t read_binary(FILE* fd)
569  {
570  MPI_Comm comm = this->mesh != NULL ? this->mesh->comm : SF_COMM;
571 
572  size_t loc_size = this->lsize();
573  S* p = this->ptr();
574  vector<V> buff(loc_size);
575 
576  size_t nrd = root_read(fd, buff, comm);
577 
578  for(size_t i=0; i<loc_size; i++)
579  p[i] = buff[i];
580 
581  this->release_ptr(p);
582 
583  return nrd;
584  }
585 
586  template<typename V>
587  inline size_t read_binary(std::string file)
588  {
589  MPI_Comm comm = mesh != NULL ? mesh->comm : SF_COMM;
590 
591  size_t nrd = 0;
592  int rank; MPI_Comm_rank(comm, &rank);
593 
594  FILE* fd = NULL;
595  int error = 0;
596 
597  if(rank == 0) {
598  fd = fopen(file.c_str(), "r");
599  if(fd == NULL)
600  error++;
601  }
602 
603  MPI_Allreduce(MPI_IN_PLACE, &error, 1, MPI_INT, MPI_SUM, comm);
604 
605  if(error == 0) {
606  nrd = read_binary<V>(fd);
607  if(fd) fclose(fd);
608  }
609  else {
610  treat_file_open_error(file.c_str(), __func__, errno, false, rank);
611  }
612 
613  return nrd;
614  }
615 
616  inline size_t read_ascii(FILE* fd)
617  {
618  MPI_Comm comm = this->mesh != NULL ? this->mesh->comm : SF_COMM;
619 
620  size_t loc_size = this->lsize();
621  S* p = this->ptr();
622 
623  size_t nrd = root_read_ascii(fd, p, loc_size, comm, false);
624  this->release_ptr(p);
625  return nrd;
626  }
627 
628  inline size_t read_ascii(std::string file)
629  {
630  MPI_Comm comm = this->mesh != NULL ? this->mesh->comm : SF_COMM;
631  int rank; MPI_Comm_rank(comm, &rank);
632 
633  size_t nrd = 0;
634 
635  FILE* fd = NULL;
636  int error = 0;
637 
638  if(rank == 0) {
639  fd = fopen(file.c_str(), "r");
640  if(fd == NULL)
641  error++;
642  }
643 
644  MPI_Allreduce(MPI_IN_PLACE, &error, 1, MPI_INT, MPI_SUM, comm);
645 
646  if(error == 0) {
647  nrd = read_ascii(fd);
648  if(fd) fclose(fd);
649  }
650  else {
651  treat_file_open_error(file.c_str(), __func__, errno, false, rank);
652  }
653 
654  return nrd;
655  }
656 };
657 
658 
661 template<class T, class S>
662 inline bool is_init(const abstract_vector<T,S>* v)
663 {
664  return (v && v->is_init());
665 }
666 
667 
668 // TODO: This is a type of Vector and can most likely be integrated into the
669 // `abstract_vector` type in some way. Everything here and also in
670 // `SF_parallel_layout.h` probably need to be abstracted away.
678 {
679  public:
682 
686  std::shared_ptr<void> backend_data;
687 
694  template<class T, class S>
695  inline void forward(abstract_vector<T,S> & in, abstract_vector<T,S> & out, bool add = false)
696  {
697  in.forward(out, *this, add);
698  }
699 
706  template<class T, class S>
707  inline void backward(abstract_vector<T,S> & in, abstract_vector<T,S> & out, bool add = false)
708  {
709  in.backward(out, *this, add);
710  }
711 
718  template<class T, class S>
719  inline void operator()(abstract_vector<T,S> & v, bool fwd)
720  {
721  v.apply_scattering(*this, fwd);
722  }
723 };
724 
725 #ifdef WITH_PETSC
727 void petsc_setup_scattering(scattering& sc, SF_int lsize_a, SF_int gsize_a,
728  SF_int lsize_b, SF_int gsize_b, bool b_is_permutation);
729 #endif
730 
740 {
741  private:
744 
745  public:
758  template<class T> inline scattering*
760  const vector<T> & nbr_a,
761  const vector<T> & nbr_b,
762  const size_t gsize_a,
763  const size_t gsize_b,
764  const short dpn)
765  {
766  assert(_registry.count(spec) == 0);
767 
768  scattering* sc = new scattering();
769  _registry[spec] = sc;
770 
771  vector<SF_int> idx_a(nbr_a.size() * dpn);
772  vector<SF_int> idx_b(nbr_b.size() * dpn);
773 
774  // we copy indexing into new containers because of dpn and also
775  // because of the implicit typecast between T and SF_int
776  for(size_t i=0; i<nbr_a.size(); i++)
777  for(short j=0; j<dpn; j++) idx_a[i*dpn+j] = nbr_a[i]*dpn+j;
778 
779  for(size_t i=0; i<nbr_b.size(); i++)
780  for(short j=0; j<dpn; j++) idx_b[i*dpn+j] = nbr_b[i]*dpn+j;
781 
782  sc->idx_a.assign(idx_a.begin(), idx_a.end());
783  sc->idx_b.assign(idx_b.begin(), idx_b.end());
784 
785 #ifdef WITH_PETSC
787  petsc_setup_scattering(*sc, idx_a.size(), gsize_a*dpn, idx_b.size(), gsize_b*dpn, true);
788 #endif
789 
790  return sc;
791  }
792 
793 
799  template<class T> inline scattering*
801  const vector<T> & layout_a,
802  const vector<T> & layout_b,
803  const vector<T> & idx_a,
804  const vector<T> & idx_b,
805  const int rank,
806  const int dpn)
807  {
808  // scattering spec must not exist yet
809  assert(_registry.count(spec) == 0);
810 
811  // the local index sets have to be of equal size.
812  assert(idx_a.size() == idx_b.size());
813 
814  scattering* sc = new scattering();
815  _registry[spec] = sc;
816 
817  vector<SF_int> sidx_a(idx_a.size() * dpn);
818  vector<SF_int> sidx_b(idx_b.size() * dpn);
819 
820  T lsize_a = layout_a[rank+1] - layout_a[rank];
821  T lsize_b = layout_b[rank+1] - layout_b[rank];
822  T gsize_a = layout_a[layout_a.size()-1];
823  T gsize_b = layout_b[layout_b.size()-1];
824 
825  // we copy indexing into new containers because of dpn and also
826  // because of the implicit typecast between T and SF_int
827  for(size_t i=0; i<idx_a.size(); i++)
828  for(short j=0; j<dpn; j++) sidx_a[i*dpn+j] = idx_a[i]*dpn+j;
829 
830  for(size_t i=0; i<idx_b.size(); i++)
831  for(short j=0; j<dpn; j++) sidx_b[i*dpn+j] = idx_b[i]*dpn+j;
832 
833  sc->idx_a.assign(sidx_a.begin(), sidx_a.end());
834  sc->idx_b.assign(sidx_b.begin(), sidx_b.end());
835 
836 #ifdef WITH_PETSC
838  petsc_setup_scattering(*sc, lsize_a*dpn, gsize_a*dpn, lsize_b*dpn, gsize_b*dpn, false);
839 #endif
840 
841  return sc;
842  }
843 
855  {
856  scattering* ret = NULL;
857 
858  // scattering must be present
859  if(_registry.count(spec))
860  ret = _registry[spec];
861 
862  return ret;
863  }
864 
868  inline void free_scatterings()
869  {
870  typename hashmap::unordered_map<quadruple<int>,scattering*>::iterator it;
871  for(it = _registry.begin(); it != _registry.end(); ++it) {
872  if(it->second) {
873  delete it->second;
874  it->second = NULL;
875  }
876  }
877  }
878 };
879 
880 
881 } // namespace SF
882 
883 
884 #endif // _SF_ABSTRACT_VECTOR_H
Basic containers.
#define SF_COMM
the default SlimFem MPI communicator
Definition: SF_globals.h:25
opencarp::global_index_t SF_int
Global algebraic index type.
Definition: SF_globals.h:29
#define SF_MPITAG
the MPI tag when communicating
Definition: SF_globals.h:27
Classes and algorithms related to the layout of distributed meshes.
Simple utility functions for parallel data.
Starts and stops the linear algebra backend the flavor parameter selects.
size_t read_ascii(FILE *fd)
size_t read_binary(std::string file)
virtual void get(const vector< T > &idx, S *out)=0
virtual void apply_scattering(scattering &sc, bool fwd)=0
virtual S mag() const =0
virtual S * ptr()=0
virtual void operator-=(const abstract_vector< T, S > &vec)=0
virtual void release_ptr(S *&p)=0
virtual std::string to_string() const =0
virtual S min() const =0
virtual void operator*=(const abstract_vector< T, S > &vec)=0
virtual S sum() const =0
virtual T gsize() const =0
size_t write_ascii(const char *file, bool write_header)
virtual void init(T igsize, T ilsize, int idpn=1, ltype ilayout=unset)=0
size_t read_binary(FILE *fd)
virtual void operator/=(const S sca)=0
virtual void forward(abstract_vector< T, S > &out, scattering &sc, bool add=false)=0
virtual bool is_init() const =0
ltype layout
used vector layout (nodal, algebraic, unset)
virtual S * device_ptr()=0
virtual bool equals(const abstract_vector< T, S > &rhs) const =0
virtual void const_release_ptr(const S *&p) const =0
size_t read_ascii(std::string file)
virtual void operator=(const abstract_vector< T, S > &rhs)=0
virtual void deep_copy(const abstract_vector< T, S > &v)=0
size_t write_binary(std::string file)
write binary. Open file descriptor myself.
virtual void get_ownership_range(T &start, T &stop) const =0
virtual S get(const T idx)=0
virtual void set(const vector< T > &idx, const S val, const bool additive=false, const bool local=false)=0
virtual void shallow_copy(const abstract_vector< T, S > &v)=0
std::tuple< T, T > init_common(const meshdata< mesh_int_t, mesh_real_t > &imesh, int idpn, ltype inp_layout)
virtual void add_scaled(const abstract_vector< T, S > &vec, S k)=0
virtual void overshadow(const abstract_vector< T, S > &sub, bool member, int offset, int sz, bool share)=0
virtual void release_device_ptr(S *&p)=0
virtual void set(const S val)=0
virtual ~abstract_vector()=default
virtual void operator+=(const abstract_vector< T, S > &vec)=0
virtual void operator=(const vector< S > &rhs)=0
virtual void set(const T idx, const S val)=0
size_t write_binary(FILE *fd)
Write a vector to HD in binary. File descriptor is already set up.
virtual void backward(abstract_vector< T, S > &out, scattering &sc, bool add=false)=0
virtual T lsize() const =0
virtual void init(const abstract_vector< T, S > &vec)=0
int dpn
d.o.f. per mesh vertex; data is stored node-major (index = node*dpn + component).
virtual const S * const_ptr() const =0
virtual void set(const vector< T > &idx, const vector< S > &vals, const bool additive=false, const bool local=false)=0
virtual void operator*=(const S sca)=0
virtual void finish_assembly()=0
virtual void init(const meshdata< mesh_int_t, mesh_real_t > &imesh, int idpn, ltype inp_layout)=0
const meshdata< mesh_int_t, mesh_real_t > * mesh
the connected mesh
virtual void operator+=(S c)=0
virtual S dot(const abstract_vector< T, S > &v) const =0
overlapping_layout< T > pl
nodal parallel layout
Definition: SF_container.h:414
size_t l_numelem
local number of elements
Definition: SF_container.h:384
size_t l_numpts
local number of points
Definition: SF_container.h:386
size_t g_numelem
global number of elements
Definition: SF_container.h:383
MPI_Comm comm
the parallel mesh is defined on a MPI world
Definition: SF_container.h:389
The scatterer registry class.
void free_scatterings()
Free the registered scatterings.
scattering * register_scattering(const quadruple< int > spec, const vector< T > &layout_a, const vector< T > &layout_b, const vector< T > &idx_a, const vector< T > &idx_b, const int rank, const int dpn)
Register a scattering.
scattering * register_permutation(const quadruple< int > spec, const vector< T > &nbr_a, const vector< T > &nbr_b, const size_t gsize_a, const size_t gsize_b, const short dpn)
Register a permutation scattering.
scattering * get_scattering(const quadruple< int > spec)
Access an previously registered scattering.
Redistributes vector data from indexing "a" to indexing "b".
vector< SF_int > idx_a
the d.o.f. indices of the two sides, which the backends build their scatters from
vector< SF_int > idx_b
void operator()(abstract_vector< T, S > &v, bool fwd)
Apply the scattering on a data vector.
void forward(abstract_vector< T, S > &in, abstract_vector< T, S > &out, bool add=false)
Forward scattering.
std::shared_ptr< void > backend_data
void backward(abstract_vector< T, S > &in, abstract_vector< T, S > &out, bool add=false)
Backward scattering.
A vector storing arbitrary data.
Definition: SF_vector.h:28
size_t size() const
The current size of the vector.
Definition: SF_vector.h:89
void resize(size_t n)
Resize a vector.
Definition: SF_vector.h:194
const T * end() const
Pointer to the vector's end.
Definition: SF_vector.h:113
void assign(InputIterator s, InputIterator e)
Assign a memory range.
Definition: SF_vector.h:146
const T * begin() const
Pointer to the vector's start.
Definition: SF_vector.h:101
T * data()
Pointer to the vector's start.
Definition: SF_vector.h:76
hm_int count(const K &key) const
Check if key exists.
Definition: hashmap.hpp:612
Classes similar to unordered_set and unordered_map, but with better performance.
Definition: dense_mat.hpp:19
void treat_file_open_error(const char *file, const char *caller, const int errnum, const bool do_exit, int rank)
treat a file open error by displaying the errnum string interpretation and the caller
Definition: SF_io_base.h:86
bool is_init(const abstract_vector< T, S > *v)
size_t root_read(FILE *fd, vector< V > &vec, MPI_Comm comm)
Read binary data into a vector.
size_t root_read_ascii(FILE *fd, vector< V > &vec, MPI_Comm comm, bool int_data)
Read binary data into a vector.
size_t root_write(FILE *fd, const vector< V > &vec, MPI_Comm comm)
Write vector data binary to disk.
std::intmax_t printable_int(T value)
Definition: mpi_utils.h:115
bool petsc_active()
Definition: backend.cc:53