LCOV - code coverage report
Current view: top level - include/parallel - parallel_algebra.h (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 45 76 59.2 %
Date: 2025-08-19 19:27:09 Functions: 6 21 28.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
       3             : 
       4             : // This library is free software; you can redistribute it and/or
       5             : // modify it under the terms of the GNU Lesser General Public
       6             : // License as published by the Free Software Foundation; either
       7             : // version 2.1 of the License, or (at your option) any later version.
       8             : 
       9             : // This library is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             : // Lesser General Public License for more details.
      13             : 
      14             : // You should have received a copy of the GNU Lesser General Public
      15             : // License along with this library; if not, write to the Free Software
      16             : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      17             : 
      18             : 
      19             : #ifndef LIBMESH_PARALLEL_ALGEBRA_H
      20             : #define LIBMESH_PARALLEL_ALGEBRA_H
      21             : 
      22             : 
      23             : // libMesh includes
      24             : #include "libmesh/libmesh_config.h"
      25             : #include "libmesh/point.h"
      26             : #include "libmesh/tensor_value.h"
      27             : #include "libmesh/vector_value.h"
      28             : 
      29             : // TIMPI includes
      30             : #include "timpi/attributes.h"
      31             : #include "timpi/op_function.h"
      32             : #include "timpi/standard_type.h"
      33             : 
      34             : // C++ includes
      35             : #include <cstddef>
      36             : #include <memory>
      37             : #include <type_traits>
      38             : 
      39             : namespace libMesh {
      40             : 
      41             : // OpFunction<> specializations to return an MPI_Op version of the
      42             : // reduction operations on LIBMESH_DIM-vectors.
      43             : //
      44             : // We use static variables to minimize the number of MPI datatype
      45             : // construction calls executed over the course of the program.
      46             : //
      47             : // We use a singleton pattern because a global variable would
      48             : // have tried to call MPI functions before MPI got initialized.
      49             : //
      50             : // min() and max() are applied component-wise; this makes them useful
      51             : // for bounding box reduction operations.
      52             : 
      53             : template <typename V>
      54             : class TypeVectorOpFunction
      55             : {
      56             : public:
      57             : #ifdef LIBMESH_HAVE_MPI
      58     2898326 :   static void vector_max (void * invec, void * inoutvec, int * len, MPI_Datatype *)
      59             :   {
      60      465906 :     V *in = static_cast<V *>(invec);
      61      465906 :     V *inout = static_cast<V *>(inoutvec);
      62     5796652 :     for (int i=0; i != *len; ++i)
      63    11593304 :       for (int d=0; d != LIBMESH_DIM; ++d)
      64    10107525 :         inout[i](d) = std::max(in[i](d), inout[i](d));
      65     2898326 :   }
      66             : 
      67     2898464 :   static void vector_min (void * invec, void * inoutvec, int * len, MPI_Datatype *)
      68             :   {
      69      465906 :     V *in = static_cast<V *>(invec);
      70      465906 :     V *inout = static_cast<V *>(inoutvec);
      71     5796928 :     for (int i=0; i != *len; ++i)
      72    11593856 :       for (int d=0; d != LIBMESH_DIM; ++d)
      73    10045680 :         inout[i](d) = std::min(in[i](d), inout[i](d));
      74     2898464 :   }
      75             : 
      76             :   static void vector_sum (void * invec, void * inoutvec, int * len, MPI_Datatype *)
      77             :   {
      78             :     V *in = static_cast<V *>(invec);
      79             :     V *inout = static_cast<V *>(inoutvec);
      80             :     for (int i=0; i != *len; ++i)
      81             :       for (int d=0; d != LIBMESH_DIM; ++d)
      82             :         inout[i](d) += in[i](d);
      83             :   }
      84             : 
      85     2069326 :   static MPI_Op max()
      86             :   {
      87             :     // _static_op never gets freed, but it only gets committed once
      88             :     // per T, so it's not a *huge* memory leak...
      89             :     static MPI_Op _static_op;
      90             :     static bool _is_initialized = false;
      91     2085938 :     if (!_is_initialized)
      92             :       {
      93       13410 :         timpi_call_mpi
      94             :           (MPI_Op_create (vector_max, /*commute=*/ true,
      95             :                           &_static_op));
      96             : 
      97       13410 :         _is_initialized = true;
      98             :       }
      99             : 
     100     2085938 :     return _static_op;
     101             :   }
     102     2069326 :   static MPI_Op min()
     103             :   {
     104             :     // _static_op never gets freed, but it only gets committed once
     105             :     // per T, so it's not a *huge* memory leak...
     106             :     static MPI_Op _static_op;
     107             :     static bool _is_initialized = false;
     108     2085938 :     if (!_is_initialized)
     109             :       {
     110       13410 :         timpi_call_mpi
     111             :           (MPI_Op_create (vector_min, /*commute=*/ true,
     112             :                           &_static_op));
     113             : 
     114       13410 :         _is_initialized = true;
     115             :       }
     116             : 
     117     2085938 :     return _static_op;
     118             :   }
     119             :   static MPI_Op sum()
     120             :   {
     121             :     // _static_op never gets freed, but it only gets committed once
     122             :     // per T, so it's not a *huge* memory leak...
     123             :     static MPI_Op _static_op;
     124             :     static bool _is_initialized = false;
     125             :     if (!_is_initialized)
     126             :       {
     127             :         timpi_call_mpi
     128             :           (MPI_Op_create (vector_sum, /*commute=*/ true,
     129             :                           &_static_op));
     130             : 
     131             :         _is_initialized = true;
     132             :       }
     133             : 
     134             :     return _static_op;
     135             :   }
     136             : 
     137             : #endif // LIBMESH_HAVE_MPI
     138             : };
     139             : 
     140             : 
     141             : template <typename V>
     142             : struct TypeVectorAttributes
     143             : {
     144             :   static const bool has_min_max = true;
     145           0 :   static void set_lowest(V & x) {
     146           0 :     for (int d=0; d != LIBMESH_DIM; ++d)
     147           0 :       TIMPI::Attributes<typename std::remove_reference<decltype(x(d))>::type>::set_lowest(x(d));
     148           0 :   }
     149           0 :   static void set_highest(V & x) {
     150           0 :     for (int d=0; d != LIBMESH_DIM; ++d)
     151           0 :       TIMPI::Attributes<typename std::remove_reference<decltype(x(d))>::type>::set_highest(x(d));
     152           0 :   }
     153             : };
     154             : 
     155             : } // namespace libMesh
     156             : 
     157             : 
     158             : namespace TIMPI {
     159             : 
     160             : // StandardType<> specializations to return a derived MPI datatype
     161             : // to handle communication of LIBMESH_DIM-vectors.
     162             : //
     163             : // We use MPI_Create_struct here because our vector classes might
     164             : // have vptrs, and I'd rather not have the datatype break in those
     165             : // cases.
     166             : template <typename T>
     167             : class StandardType<libMesh::TypeVector<T>, typename std::enable_if<StandardType<T>::is_fixed_type>::type>
     168             :   : public DataType
     169             : {
     170             : public:
     171             :   explicit
     172             :   StandardType(const libMesh::TypeVector<T> * example=nullptr)
     173             :   {
     174             :     using libMesh::TypeVector;
     175             : 
     176             :     // We need an example for MPI_Address to use
     177             :     TypeVector<T> * ex;
     178             :     std::unique_ptr<TypeVector<T>> temp;
     179             :     if (example)
     180             :       ex = const_cast<TypeVector<T> *>(example);
     181             :     else
     182             :       {
     183             :         temp = std::make_unique<TypeVector<T>>();
     184             :         ex = temp.get();
     185             :       }
     186             : 
     187             : #ifdef LIBMESH_HAVE_MPI
     188             :     StandardType<T> T_type(&((*ex)(0)));
     189             : 
     190             :     // We require MPI-2 here:
     191             :     int blocklength = LIBMESH_DIM;
     192             :     MPI_Aint displs, start;
     193             :     MPI_Datatype tmptype, type = T_type;
     194             : 
     195             :     timpi_call_mpi
     196             :       (MPI_Get_address (ex, &start));
     197             :     timpi_call_mpi
     198             :       (MPI_Get_address (&((*ex)(0)), &displs));
     199             : 
     200             :     // subtract off offset to first value from the beginning of the structure
     201             :     displs -= start;
     202             : 
     203             :     // create a prototype structure
     204             :     timpi_call_mpi
     205             :       (MPI_Type_create_struct (1, &blocklength, &displs, &type,
     206             :                                &tmptype));
     207             :     timpi_call_mpi
     208             :       (MPI_Type_commit (&tmptype));
     209             : 
     210             :     // resize the structure type to account for padding, if any
     211             :     timpi_call_mpi
     212             :       (MPI_Type_create_resized (tmptype, 0, sizeof(TypeVector<T>),
     213             :                                 &_datatype));
     214             : 
     215             :     timpi_call_mpi
     216             :       (MPI_Type_commit (&_datatype));
     217             : 
     218             :     timpi_call_mpi
     219             :       (MPI_Type_free (&tmptype));
     220             : #endif // #ifdef LIBMESH_HAVE_MPI
     221             :   }
     222             : 
     223             :   StandardType(const StandardType<libMesh::TypeVector<T>> & timpi_mpi_var(t))
     224             :     : DataType()
     225             :   {
     226             :     timpi_call_mpi (MPI_Type_dup (t._datatype, &_datatype));
     227             :   }
     228             : 
     229             :   ~StandardType() { this->free(); }
     230             : 
     231             :   static const bool is_fixed_type = true;
     232             : };
     233             : 
     234             : 
     235             : template <typename T>
     236             : class StandardType<libMesh::VectorValue<T>, typename std::enable_if<StandardType<T>::is_fixed_type>::type>
     237             :   : public DataType
     238             : {
     239             : public:
     240             :   explicit
     241           0 :   StandardType(const libMesh::VectorValue<T> * example=nullptr)
     242           0 :   {
     243             :     using libMesh::VectorValue;
     244             : 
     245             :     // We need an example for MPI_Address to use
     246             :     VectorValue<T> * ex;
     247           0 :     std::unique_ptr<VectorValue<T>> temp;
     248           0 :     if (example)
     249           0 :       ex = const_cast<VectorValue<T> *>(example);
     250             :     else
     251             :       {
     252           0 :         temp = std::make_unique<VectorValue<T>>();
     253           0 :         ex = temp.get();
     254             :       }
     255             : 
     256             : #ifdef LIBMESH_HAVE_MPI
     257           0 :     StandardType<T> T_type(&((*ex)(0)));
     258             : 
     259           0 :     int blocklength = LIBMESH_DIM;
     260             :     MPI_Aint displs, start;
     261           0 :     MPI_Datatype tmptype, type = T_type;
     262             : 
     263           0 :     timpi_call_mpi
     264             :       (MPI_Get_address (ex, &start));
     265           0 :     timpi_call_mpi
     266             :       (MPI_Get_address (&((*ex)(0)), &displs));
     267             : 
     268             :     // subtract off offset to first value from the beginning of the structure
     269           0 :     displs -= start;
     270             : 
     271             :     // create a prototype structure
     272           0 :     timpi_call_mpi
     273             :       (MPI_Type_create_struct (1, &blocklength, &displs, &type,
     274             :                                &tmptype));
     275           0 :     timpi_call_mpi
     276             :       (MPI_Type_commit (&tmptype));
     277             : 
     278             :     // resize the structure type to account for padding, if any
     279           0 :     timpi_call_mpi
     280             :       (MPI_Type_create_resized (tmptype, 0,
     281             :                                 sizeof(VectorValue<T>),
     282             :                                 &_datatype));
     283             : 
     284           0 :     timpi_call_mpi
     285             :       (MPI_Type_commit (&_datatype));
     286             : 
     287           0 :     timpi_call_mpi
     288             :       (MPI_Type_free (&tmptype));
     289             : #endif // #ifdef LIBMESH_HAVE_MPI
     290           0 :   }
     291             : 
     292             :   StandardType(const StandardType<libMesh::VectorValue<T>> & timpi_mpi_var(t))
     293             :     : DataType()
     294             :   {
     295             : #ifdef LIBMESH_HAVE_MPI
     296             :     timpi_call_mpi (MPI_Type_dup (t._datatype, &_datatype));
     297             : #endif
     298             :   }
     299             : 
     300           0 :   ~StandardType() { this->free(); }
     301             : 
     302             :   static const bool is_fixed_type = true;
     303             : };
     304             : 
     305             : template <>
     306             : class StandardType<libMesh::Point> : public DataType
     307             : {
     308             : public:
     309             :   explicit
     310     2202408 :   StandardType(const libMesh::Point * example=nullptr)
     311     2202408 :   {
     312             :     using libMesh::Point;
     313             : 
     314             :     // Prevent unused variable warnings when !LIBMESH_HAVE_MPI
     315     1835524 :     libmesh_ignore(example);
     316             : 
     317             : #ifdef LIBMESH_HAVE_MPI
     318             : 
     319             :     // We need an example for MPI_Address to use
     320             :     Point * ex;
     321             : 
     322     2161894 :     std::unique_ptr<Point> temp;
     323     2202408 :     if (example)
     324     1828494 :       ex = const_cast<Point *>(example);
     325             :     else
     326             :       {
     327       12230 :         temp = std::make_unique<Point>();
     328        7030 :         ex = temp.get();
     329             :       }
     330             : 
     331     3671048 :     StandardType<libMesh::Real> T_type(&((*ex)(0)));
     332             : 
     333     2202408 :     int blocklength = LIBMESH_DIM;
     334             :     MPI_Aint displs, start;
     335     2202408 :     MPI_Datatype tmptype, type = T_type;
     336             : 
     337     2202408 :     timpi_call_mpi
     338             :       (MPI_Get_address (ex, &start));
     339     2202408 :     timpi_call_mpi
     340             :       (MPI_Get_address (&((*ex)(0)), &displs));
     341             : 
     342             :     // subtract off offset to first value from the beginning of the structure
     343     2202408 :     displs -= start;
     344             : 
     345             :     // create a prototype structure
     346     2202408 :     timpi_call_mpi
     347             :       (MPI_Type_create_struct (1, &blocklength, &displs, &type,
     348             :                                &tmptype));
     349     2202408 :     timpi_call_mpi
     350             :       (MPI_Type_commit (&tmptype));
     351             : 
     352             :     // resize the structure type to account for padding, if any
     353     2202408 :     timpi_call_mpi
     354             :       (MPI_Type_create_resized (tmptype, 0, sizeof(Point),
     355             :                                 &_datatype));
     356             : 
     357     2202408 :     timpi_call_mpi
     358             :       (MPI_Type_commit (&_datatype));
     359             : 
     360     2202408 :     timpi_call_mpi
     361             :       (MPI_Type_free (&tmptype));
     362             : #endif // #ifdef LIBMESH_HAVE_MPI
     363     2202408 :   }
     364             : 
     365             :   StandardType(const StandardType<libMesh::Point> & timpi_mpi_var(t))
     366             :     : DataType()
     367             :   {
     368             :     timpi_call_mpi (MPI_Type_dup (t._datatype, &_datatype));
     369             :   }
     370             : 
     371     4174950 :   ~StandardType() { this->free(); }
     372             : 
     373             :   static const bool is_fixed_type = true;
     374             : };
     375             : 
     376             : template <typename T>
     377             : class OpFunction<libMesh::TypeVector<T>> : public libMesh::TypeVectorOpFunction<libMesh::TypeVector<T>> {};
     378             : 
     379             : template <typename T>
     380             : class OpFunction<libMesh::VectorValue<T>> : public libMesh::TypeVectorOpFunction<libMesh::VectorValue<T>> {};
     381             : 
     382             : template <>
     383             : class OpFunction<libMesh::Point> : public libMesh::TypeVectorOpFunction<libMesh::Point> {};
     384             : 
     385             : 
     386             : template <typename T>
     387             : class Attributes<libMesh::TypeVector<T>> : public libMesh::TypeVectorAttributes<libMesh::TypeVector<T>> {};
     388             : 
     389             : template <typename T>
     390             : class Attributes<libMesh::VectorValue<T>> : public libMesh::TypeVectorAttributes<libMesh::VectorValue<T>> {};
     391             : 
     392             : template <>
     393             : class Attributes<libMesh::Point> : public libMesh::TypeVectorAttributes<libMesh::Point> {};
     394             : 
     395             : 
     396             : // StandardType<> specializations to return a derived MPI datatype
     397             : // to handle communication of LIBMESH_DIM*LIBMESH_DIM-tensors.
     398             : //
     399             : // We assume contiguous storage here
     400             : template <typename T>
     401             : class StandardType<libMesh::TypeTensor<T>, typename std::enable_if<StandardType<T>::is_fixed_type>::type>
     402             :   : public DataType
     403             : {
     404             : public:
     405             :   explicit
     406             :   StandardType(const libMesh::TypeTensor<T> * example=nullptr) :
     407             :     DataType(StandardType<T>(example ?  &((*example)(0,0)) : nullptr), LIBMESH_DIM*LIBMESH_DIM) {}
     408             : 
     409             :   inline ~StandardType() { this->free(); }
     410             : 
     411             :   static const bool is_fixed_type = true;
     412             : };
     413             : 
     414             : template <typename T>
     415             : class StandardType<libMesh::TensorValue<T>, typename std::enable_if<StandardType<T>::is_fixed_type>::type>
     416             :   : public DataType
     417             : {
     418             : public:
     419             :   explicit
     420           0 :   StandardType(const libMesh::TensorValue<T> * example=nullptr) :
     421           0 :     DataType(StandardType<T>(example ?  &((*example)(0,0)) : nullptr), LIBMESH_DIM*LIBMESH_DIM) {}
     422             : 
     423           0 :   inline ~StandardType() { this->free(); }
     424             : 
     425             :   static const bool is_fixed_type = true;
     426             : };
     427             : } // namespace TIMPI
     428             : 
     429             : #endif // LIBMESH_PARALLEL_ALGEBRA_H

Generated by: LCOV version 1.14