DUNE-FEM (unstable)

cachedcommmanager.hh
1#ifndef DUNE_FEM_CACHED_COMMUNICATION_MANAGER_HH
2#define DUNE_FEM_CACHED_COMMUNICATION_MANAGER_HH
3
4#include <cassert>
5#include <cstddef>
6
7//- system includes
8#include <iostream>
9#include <unordered_map>
10#include <unordered_set>
11#include <queue>
12#include <memory>
13#include <vector>
14
15//- dune-common includes
16#include <dune/common/math.hh>
17#include <dune/common/timer.hh>
19
20//- dune-grid includes
23#include <dune/grid/utility/entitycommhelper.hh>
24
25// include alugrid headers to have to communicator class from ALUGrid
26#if HAVE_DUNE_ALUGRID
27#include <dune/alugrid/3d/alu3dinclude.hh>
28#endif
29
30//- dune-fem includes
31#include <dune/fem/common/hybrid.hh>
32#include <dune/fem/storage/singletonlist.hh>
33#include <dune/fem/space/common/commoperations.hh>
34#include <dune/fem/space/common/commindexmap.hh>
35#include <dune/fem/misc/functor.hh>
36#include <dune/fem/misc/mpimanager.hh>
37#include <dune/fem/misc/capabilities.hh>
38
39namespace Dune
40{
41
42 namespace Fem
43 {
44
45 // External Forward Declarations
46 // -----------------------------
47
48 template< class DiscreteFunctionSpace >
49 class PetscDiscreteFunction;
50
51 class IsDiscreteFunction;
52
53 class IsBlockVector;
54
59// only if ALUGrid found and was build for parallel runs
60// if HAVE_DUNE_ALUGRID is not defined, ALU3DGRID_PARALLEL shouldn't be either
61#if ALU3DGRID_PARALLEL
62
69 template< class BlockMapper >
70 class DependencyCache
71 {
72 public:
75 typedef BlockMapper BlockMapperType;
76
77 protected:
78 // type of communication indices
79 typedef CommunicationIndexMap IndexMapType;
80
81 // type of IndexMapVector
82 typedef std::vector< IndexMapType > IndexMapVectorType;
83
84 // type of set of links
85 typedef std :: set< int > LinkStorageType;
86
87 // ALUGrid send/recv buffers
88 typedef ALU3DSPACE ObjectStream ObjectStreamType;
89
90 // type of communicator
91 typedef ALU3DSPACE MpAccessLocal MPAccessInterfaceType;
92 // type of communication implementation
93 typedef ALU3DSPACE MpAccessMPI MPAccessImplType;
94
96 typedef std :: vector< ObjectStreamType > ObjectStreamVectorType;
97
98 protected:
99 const InterfaceType interface_;
100 const CommunicationDirection dir_;
101
102 LinkStorageType linkStorage_;
103
104 IndexMapVectorType recvIndexMap_;
105 IndexMapVectorType sendIndexMap_;
106
107 // ALUGrid communicator Class
108 std::unique_ptr< MPAccessInterfaceType > mpAccess_;
109
110 // exchange time
111 double exchangeTime_;
112 // setup time
113 double buildTime_;
114
116 int sequence_;
117
118 int nonBlockingObjects_ ;
119
120 protected:
121 template< class Communication, class LinkStorage,
122 class IndexMapVector, InterfaceType CommInterface >
123 class LinkBuilder;
124
126 // begin NonBlockingCommunication
128
129 class NonBlockingCommunication
130 {
131 typedef DependencyCache < BlockMapper > DependencyCacheType;
132
133#if HAVE_DUNE_ALUGRID
134 typedef MPAccessInterfaceType :: NonBlockingExchange NonBlockingExchange;
135
136 template <class DiscreteFunction>
137 class Pack : public NonBlockingExchange :: DataHandleIF
138 {
139 protected:
140 NonBlockingCommunication& commObj_;
141 const DiscreteFunction& discreteFunction_;
142
143 public:
144 Pack( NonBlockingCommunication& commObj, const DiscreteFunction& df )
145 : commObj_( commObj ), discreteFunction_( df )
146 {}
147
148 void pack( const int link, ObjectStreamType& buffer )
149 {
150 commObj_.pack( link, buffer, discreteFunction_ );
151 }
152
153 void unpack( const int link, ObjectStreamType& buffer )
154 {
155 DUNE_THROW(InvalidStateException,"Pack::unpack should not be called!");
156 }
157 };
158
159 template <class DiscreteFunction, class Operation>
160 class Unpack : public NonBlockingExchange :: DataHandleIF
161 {
162 protected:
163 NonBlockingCommunication& commObj_;
164 DiscreteFunction& discreteFunction_;
165
166 // communication operation (usually ADD or COPY)
167 const Operation operation_;
168
169 public:
170 Unpack( NonBlockingCommunication& commObj, DiscreteFunction& df )
171 : commObj_( commObj ), discreteFunction_( df ), operation_()
172 {}
173
174 void pack( const int link, ObjectStreamType& buffer )
175 {
176 DUNE_THROW(InvalidStateException,"Unpack::pack should not be called!");
177 }
178
179 void unpack( const int link, ObjectStreamType& buffer )
180 {
181 commObj_.unpack( link, buffer, discreteFunction_, operation_ );
182 }
183 };
184#else // ALUGRID_HAS_NONBLOCKING_COMM is false
185 typedef int NonBlockingExchange;
186#endif
187
188 protected:
189 static const int initialTagCounter = 665 ;
190
191 // create an unique tag variable for the communication
192 // use int16_t to only allow ranges from 0 to 32768 (MPI specification)
193 DUNE_EXPORT static int16_t& getMTagRef()
194 {
195 static int16_t tagCounter = initialTagCounter ;
196 return tagCounter;
197 }
198
199 // create an unique tag int for the communication
200 DUNE_EXPORT static int getMessageTag()
201 {
202 int16_t& tagCounter = getMTagRef() ;
203 // increase counter
204 ++ tagCounter;
205
206 // avoid overflow (valid values from 0 to 32768 (MPI specification))
207 if( tagCounter < 0 )
208 {
209 tagCounter = initialTagCounter ;
210 }
211
212 return int(tagCounter);
213 }
214
215 public:
216 template <class Space>
217 NonBlockingCommunication( const Space& space,
218 DependencyCacheType& dependencyCache )
219 : dependencyCache_( dependencyCache ),
220 nonBlockingExchange_(),
221 buffer_(),
222 exchangeTime_( 0.0 ),
223 mySize_( space.gridPart().comm().size() )
224 {
225 // make sure cache is up2date
226 dependencyCache_.rebuild( space );
227
228 // notify dependency cache of open communication
229 dependencyCache_.attachComm();
230 }
231
232 // copy constructor
233 NonBlockingCommunication( const NonBlockingCommunication& other )
234 : dependencyCache_( other.dependencyCache_ ),
235 nonBlockingExchange_(),
236 buffer_(),
237 exchangeTime_( 0.0 ),
238 mySize_( other.mySize_ )
239 {
240 // notify dependency cache of open communication
241 dependencyCache_.attachComm();
242 }
243
244 ~NonBlockingCommunication()
245 {
246 // if this assertion fails some communication has not been finished
247 assert( ! nonBlockingExchange_ );
248 // notify dependency cache that comm is finished
249 dependencyCache_.detachComm() ;
250 }
251
253 template < class DiscreteFunction, class Comm >
254 static bool skip( const DiscreteFunction&, const Comm& comm )
255 {
256 return comm.size() <= 1;
257 }
258
260 template <class DiscreteFunctionSpace, class Comm>
261 static bool skip( const PetscDiscreteFunction< DiscreteFunctionSpace >&, const Comm& )
262 {
263 return false;
264 }
265
266 template < class DiscreteFunctionSpace >
267 void send( const PetscDiscreteFunction< DiscreteFunctionSpace >& discreteFunction )
268 {
269 // nothing to do for the PetscDiscreteFunction here
270 }
271
272 template < class DiscreteFunction >
273 void send( const DiscreteFunction& discreteFunction )
274 {
275 // check that object is in non-sent state
276 assert( ! nonBlockingExchange_ );
277
278 // on serial runs: do nothing
279 if( mySize_ <= 1 ) return;
280
281 // take time
282 Dune::Timer sendTimer ;
283
284 // this variable can change during rebuild
285 const int nLinks = dependencyCache_.nlinks();
286
287 // resize buffer vector
288 buffer_.resize( nLinks );
289
290#if HAVE_DUNE_ALUGRID
291 // get non-blocking exchange object from mpAccess including message tag
292 nonBlockingExchange_.reset( dependencyCache_.mpAccess().nonBlockingExchange( getMessageTag() ) );
293
294 // pack data object
295 Pack< DiscreteFunction > packData( *this, discreteFunction );
296
297 // perform send operation including packing of data
298 nonBlockingExchange_->send( buffer_, packData );
299#else
300 // write buffers
301 for( int link = 0; link < nLinks; ++link )
302 pack( link, buffer_[ link ], discreteFunction );
303#endif
304
305 // store time needed for sending
306 exchangeTime_ = sendTimer.elapsed();
307 }
308
310 template < class DiscreteFunctionSpace, class Operation >
311 double receive( PetscDiscreteFunction< DiscreteFunctionSpace >& discreteFunction,
312 const Operation& operation )
313 {
314 // take time
315 Dune::Timer exchTimer;
316
317 // PetscDiscreteFunction has it's own communication
318 discreteFunction.dofVector().communicateNow( operation );
319
320 return exchTimer.elapsed();
321 }
322
324 template < class DiscreteFunction, class Operation >
325 double receive( DiscreteFunction& discreteFunction, const Operation& operation )
326 {
327 // on serial runs: do nothing
328 if( mySize_ <= 1 ) return 0.0;
329
330 // take time
331 Dune::Timer recvTimer ;
332
333#if HAVE_DUNE_ALUGRID
334 // unpack data object
335 Unpack< DiscreteFunction, Operation > unpackData( *this, discreteFunction );
336
337 // receive data and unpack
338 nonBlockingExchange_->receive( unpackData );
339#else
340 // use exchange for older ALUGrid versions (send and receive)
341 buffer_ = dependencyCache_.mpAccess().exchange( buffer_ );
342
343 // this variable can change during rebuild
344 const int nLinks = buffer_.size();
345
346 // read buffers and store to discrete function
347 for( int link = 0; link < nLinks; ++link )
348 unpack( link, buffer_[ link ], discreteFunction, operation );
349#endif
350
351 // store time needed for sending
352 exchangeTime_ += recvTimer.elapsed();
353
354#if HAVE_DUNE_ALUGRID
355 // clear nonBlockingExchange object
356 nonBlockingExchange_.reset();
357#endif
358 return exchangeTime_;
359 }
360
362 template < class DiscreteFunction >
363 double receive( DiscreteFunction& discreteFunction )
364 {
365 // get type of default operation
366 typedef typename DiscreteFunction :: DiscreteFunctionSpaceType
367 :: template CommDataHandle< DiscreteFunction > :: OperationType DefaultOperationType;
368 DefaultOperationType operation;
369 return receive( discreteFunction, operation );
370 }
371
372 protected:
373 template <class DiscreteFunction>
374 void pack( const int link, ObjectStreamType& buffer, const DiscreteFunction& discreteFunction )
375 {
376 // reset buffer counters
377 buffer.clear();
378 // write data of discrete function to message buffer
379 dependencyCache_.writeBuffer( link, buffer, discreteFunction );
380 }
381
382 template <class DiscreteFunction, class Operation>
383 void unpack( const int link, ObjectStreamType& buffer,
384 DiscreteFunction& discreteFunction, const Operation& operation )
385 {
386 // read data of discrete function from message buffer
387 dependencyCache_.readBuffer( link, buffer, discreteFunction, operation );
388 }
389
390 protected:
391 DependencyCacheType& dependencyCache_;
392 std::unique_ptr< NonBlockingExchange > nonBlockingExchange_ ;
393 ObjectStreamVectorType buffer_;
394 double exchangeTime_ ;
395 const int mySize_;
396 };
397
398 public:
399 typedef NonBlockingCommunication NonBlockingCommunicationType;
400
402 template <class Space>
403 NonBlockingCommunicationType nonBlockingCommunication( const Space& space )
404 {
405 // create non-blocking communication object
406 return NonBlockingCommunicationType( space, *this );
407 }
409 // end NonBlockingCommunication
411
413 DependencyCache( const int nProcs, const InterfaceType interface, const CommunicationDirection dir )
414 : interface_( interface ),
415 dir_( dir ),
416 linkStorage_(),
417 recvIndexMap_( nProcs ),
418 sendIndexMap_( nProcs ),
419 mpAccess_(),
420 exchangeTime_( 0.0 ),
421 buildTime_( 0.0 ),
422 sequence_( -1 ),
423 nonBlockingObjects_( 0 )
424 {
425 }
426
427 template <class Communication>
428 void init( const Communication& comm )
429 {
430 if( ! mpAccess_ )
431 {
432 mpAccess_.reset( new MPAccessImplType( comm ) );
433 }
434 }
435
436 // no copying
437 DependencyCache( const DependencyCache & ) = delete;
438
440 InterfaceType communicationInterface() const
441 {
442 return interface_;
443 }
444
446 CommunicationDirection communicationDirection() const
447 {
448 return dir_;
449 }
450
452 double buildTime() const
453 {
454 return buildTime_;
455 }
456
458 double exchangeTime() const
459 {
460 return exchangeTime_;
461 }
462
463 // notify for open non-blocking communications
464 void attachComm()
465 {
466 ++nonBlockingObjects_;
467 }
468
469 // notify for finished non-blocking communication
470 void detachComm()
471 {
472 --nonBlockingObjects_;
473 assert( nonBlockingObjects_ >= 0 );
474 }
475
476 bool noOpenCommunications() const
477 {
478 return true ;
479 }
480
481 protected:
482 // build linkage and index maps
483 template < class Space >
484 inline void buildMaps( const Space& space );
485
486 // check consistency of maps
487 inline void checkConsistency();
488
489 template< class Space, class Comm, class LS, class IMV, InterfaceType CI >
490 inline void buildMaps( const Space& space, LinkBuilder< Comm, LS, IMV, CI > &handle );
491
492 public:
494 inline int dest( const int link ) const
495 {
496 return mpAccess().dest()[ link ];
497 }
498
500 inline int nlinks() const
501 {
502 return mpAccess().nlinks();
503 }
504
508 template <class Space>
509 inline void rebuild( const Space& space )
510 {
511 const auto& comm = space.gridPart().comm();
512 const int spcSequence = space.sequence();
513
514 // only in parallel we have to do something
515 if( comm.size() <= 1 ) return;
516
517 // make sure all non-blocking communications have been finished by now
518 assert( noOpenCommunications() );
519#ifndef NDEBUG
520 // make sure buildMaps is called on every process
521 // otherwise the programs wait here until forever
522 int willRebuild = (sequence_ != spcSequence) ? 1 : 0;
523 const int myRebuild = willRebuild;
524
525 // send willRebuild from rank 0 to all
526 comm.broadcast( &willRebuild, 1 , 0);
527
528 assert( willRebuild == myRebuild );
529#endif
530
531 // check whether grid has changed.
532 if( sequence_ != spcSequence )
533 {
534 // take timer needed for rebuild
535 Dune::Timer buildTime;
536
537 // rebuild maps holding exchange dof information
538 buildMaps( space );
539 // update sequence number
540 sequence_ = spcSequence;
541
542 // store time needed
543 buildTime_ = buildTime.elapsed();
544 }
545 }
546
548 template< class Space, class DiscreteFunction, class Operation >
549 inline void exchange( const Space& space, DiscreteFunction &discreteFunction, const Operation& operation );
550
552 template< class DiscreteFunction >
553 inline void writeBuffer( ObjectStreamVectorType &osv, const DiscreteFunction &discreteFunction ) const;
554
556 template< class DiscreteFunctionType, class Operation >
557 inline void readBuffer( ObjectStreamVectorType &osv,
558 DiscreteFunctionType &discreteFunction,
559 const Operation& operation ) const;
560
562 inline MPAccessInterfaceType &mpAccess()
563 {
564 assert( mpAccess_ );
565 return *mpAccess_;
566 }
567
569 inline const MPAccessInterfaceType &mpAccess() const
570 {
571 assert( mpAccess_ );
572 return *mpAccess_;
573 }
574
575 protected:
576 // specialization for PetscDiscreteFunction doing nothing
577 template< class DiscreteFunctionSpace >
578 inline void writeBuffer( const int link,
579 ObjectStreamType &str,
580 const PetscDiscreteFunction< DiscreteFunctionSpace > &discreteFunction ) const
581 {
582 DUNE_THROW(NotImplemented,"writeBuffer not implemented for PetscDiscteteFunction" );
583 }
584
585 // write data of DataImp& vector to object stream
586 // --writeBuffer
587 template< class Data >
588 inline void writeBuffer( const int link,
589 ObjectStreamType &str,
590 const Data &data ) const
591 {
592 const auto &indexMap = sendIndexMap_[ dest( link ) ];
593 const int size = indexMap.size();
594 typedef typename Data :: DofType DofType;
595
596 // Dune::Fem::DiscreteFunctionInterface and derived
597 if constexpr ( std::is_base_of< IsDiscreteFunction, Data >::value )
598 {
599 assert( sequence_ == data.space().sequence() );
600 // reserve write buffer for storage of dofs
601 typename Data::DiscreteFunctionSpaceType::LocalBlockIndices localBlockIndices;
602 str.reserve( size * Hybrid::size( localBlockIndices ) * sizeof( DofType ) );
603 for( int i = 0; i < size; ++i )
604 {
605 const auto &block = data.dofVector()[ indexMap[ i ] ];
606 Hybrid::forEach( localBlockIndices, [ &str, &block ] ( auto &&k ) { str.writeUnchecked( block[ k ] ); } );
607 }
608 }
609
610 // Dune::Fem::BlockVectorInterface and derived
611 if constexpr ( std::is_base_of< IsBlockVector, Data > :: value )
612 {
613 static const int blockSize = Data::blockSize;
614 str.reserve( size * blockSize * sizeof( DofType ) );
615 for( int i = 0; i < size; ++i )
616 {
617 const auto &block = data[ indexMap[ i ] ];
618 for( int k=0; k<blockSize; ++k )
619 str.writeUnchecked( block[ k ] );
620 }
621 }
622 }
623
624 // read data from object stream to DataImp& data vector
625 // specialization for PetscDiscreteFunction doing nothing
626 template< class DiscreteFunctionSpace, class Operation >
627 inline void readBuffer( const int link,
628 ObjectStreamType &str,
629 PetscDiscreteFunction< DiscreteFunctionSpace > &discreteFunction,
630 const Operation& ) const
631 {
632 DUNE_THROW(NotImplemented,"readBuffer not implemented for PetscDiscteteFunction" );
633 }
634
635 // read data from object stream to DataImp& data vector
636 // --readBuffer
637 template< class Data, class Operation >
638 inline void readBuffer( const int link,
639 ObjectStreamType &str,
640 Data &data,
641 const Operation& operation ) const
642 {
643 static_assert( ! std::is_pointer< Operation > :: value,
644 "DependencyCache::readBuffer: Operation needs to be a reference!");
645
646 // get index map of rank belonging to link
647 const auto &indexMap = recvIndexMap_[ dest( link ) ];
648 const int size = indexMap.size();
649 typedef typename Data :: DofType DofType;
650
651 // Dune::Fem::DiscreteFunctionInterface and derived
652 if constexpr ( std::is_base_of< IsDiscreteFunction, Data >::value )
653 {
654 assert( sequence_ == data.space().sequence() );
655
656 // make sure that the receive buffer has the correct size
657 typename Data::DiscreteFunctionSpaceType::LocalBlockIndices localBlockIndices;
658 assert( static_cast< std::size_t >( size * Hybrid::size( localBlockIndices ) * sizeof( DofType ) ) <= static_cast< std::size_t >( str.size() ) );
659 for( int i = 0; i < size; ++i )
660 {
661 auto &&block = data.dofVector()[ indexMap[ i ] ];
662 Hybrid::forEach( localBlockIndices, [ &str, &operation, &block ] ( auto &&k ) {
663 DofType value;
664#if HAVE_DUNE_ALUGRID
665 str.readUnchecked( value );
666#else // #if HAVE_DUNE_ALUGRID
667 str.read( value );
668#endif // #else // #if HAVE_DUNE_ALUGRID
669 // apply operation, i.e. COPY, ADD, etc.
670 operation( value, block[ k ] );
671 } );
672 }
673 }
674
675 // Dune::Fem::BlockVectorInterface and derived
676 if constexpr ( std::is_base_of< IsBlockVector, Data > :: value )
677 {
678 static const int blockSize = Data::blockSize;
679 assert( static_cast< std::size_t >( size * blockSize * sizeof( DofType ) ) <= static_cast< std::size_t >( str.size() ) );
680 for( int i = 0; i < size; ++i )
681 {
682 auto &&block = data[ indexMap[ i ] ];
683 for( int k=0; k<blockSize; ++k )
684 {
685 DofType value;
686#if HAVE_DUNE_ALUGRID
687 str.readUnchecked( value );
688#else // #if HAVE_DUNE_ALUGRID
689 str.read( value );
690#endif // #else // #if HAVE_DUNE_ALUGRID
691 // apply operation, i.e. COPY, ADD, etc.
692 operation( value, block[ k ] );
693 }
694 }
695 }
696 }
697 };
698
699 // --LinkBuilder
700 template< class BlockMapper >
701 template< class Communication, class LinkStorage, class IndexMapVector, InterfaceType CommInterface >
702 class DependencyCache< BlockMapper > :: LinkBuilder
703 : public CommDataHandleIF
704 < LinkBuilder< Communication, LinkStorage, IndexMapVector, CommInterface >,
705 typename BlockMapper :: GlobalKeyType >
706 {
707 public:
708 typedef Communication CommunicationType;
709 typedef BlockMapper BlockMapperType;
710
711 typedef typename BlockMapperType :: GlobalKeyType GlobalKeyType;
712
713 typedef LinkStorage LinkStorageType;
714 typedef IndexMapVector IndexMapVectorType;
715
716 typedef GlobalKeyType DataType;
717
718 protected:
719 const CommunicationType& comm_;
720 const BlockMapperType &blockMapper_;
721
722 const GlobalKeyType myRank_;
723 const GlobalKeyType mySize_;
724
725 LinkStorageType &linkStorage_;
726
727 IndexMapVectorType &sendIndexMap_;
728 IndexMapVectorType &recvIndexMap_;
729
730 typedef typename BlockMapper::GridPartType::GridType GridType;
731 static constexpr bool isMMesh = Capabilities::isMMesh<GridType>::v;
732
733 public:
734 LinkBuilder( const CommunicationType& comm,
735 const BlockMapperType& blockMapper,
736 LinkStorageType &linkStorage,
737 IndexMapVectorType &sendIdxMap,
738 IndexMapVectorType &recvIdxMap )
739 : comm_( comm ),
740 blockMapper_( blockMapper ),
741 myRank_( comm.rank() ),
742 mySize_( comm.size() ),
743 linkStorage_( linkStorage ),
744 sendIndexMap_( sendIdxMap ),
745 recvIndexMap_( recvIdxMap )
746 {}
747
748 protected:
749 void sendBackSendMaps()
750 {
751 // create ALU communicator
752 MPAccessImplType mpAccess( comm_ );
753
754 // build linkage
755 mpAccess.removeLinkage();
756 // insert new linkage
757 mpAccess.insertRequestSymetric( linkStorage_ );
758 // get destination ranks
759 std::vector<int> dest = mpAccess.dest();
760 // get number of links
761 const int nlinks = mpAccess.nlinks();
762
763 // create buffers
764 ObjectStreamVectorType osv( nlinks );
765
766 const int codimensions = BlockMapper::GridPartType::dimension;
767 int hasHigherCodims = 0;
768 for( int c=1; c<=codimensions; ++c )
769 hasHigherCodims += int(blockMapper_.contains( c ));
770
771 // make sure all dof pairs are unique
772 // otherwise remove duplicates, for codimension 0 it does not matter
773 if( hasHigherCodims > 0 )
774 {
775 typedef typename IndexMapVectorType::value_type::IndexType IndexType;
776 for(int link=0; link<nlinks; ++link)
777 {
778 std::unordered_map< IndexType, std::unordered_set<IndexType> > uniqueRecvIndicesForSendIndex;
779 auto& sendMap = sendIndexMap_[ dest[link] ];
780 auto& recvMap = recvIndexMap_[ dest[link] ];
781 assert( sendMap.size() == recvMap.size() );
782
783 const size_t size = sendMap.size();
784 for( size_t i=0; i<size; ++i )
785 {
786 auto& uniqueRecvIndices = uniqueRecvIndicesForSendIndex[ sendMap[ i ] ];
787 if( uniqueRecvIndices.count( recvMap[ i ] ) == 0 )
788 uniqueRecvIndices.insert( recvMap[ i ] );
789 else
790 {
791 sendMap.erase( i );
792 recvMap.erase( i );
793 }
794 }
795
796 // make maps consecutive again
797 sendMap.compress();
798 recvMap.compress();
799 }
800 }
802 //
803 // at this point complete send maps exsist on receiving side,
804 // so send them back to sending side
805 //
807
808 // write all send maps to buffer
809 for(int link=0; link<nlinks; ++link)
810 sendIndexMap_[ dest[link] ].writeToBuffer( osv[link] );
811
812 // exchange data
813 osv = mpAccess.exchange( osv );
814
815 // read all send maps from buffer
816 for(int link=0; link<nlinks; ++link)
817 sendIndexMap_[ dest[link] ].readFromBuffer( osv[link] );
818 }
819
820 public:
822 ~LinkBuilder()
823 {
824 sendBackSendMaps();
825 }
826
828 bool contains( int dim, int codim ) const
829 {
830 return blockMapper_.contains( codim );
831 }
832
834 bool fixedSize( int dim, int codim ) const
835 {
836 return false;
837 }
838
840 template< class MessageBuffer, class Entity >
841 void gather( MessageBuffer &buffer, const Entity &entity ) const
842 {
843 // check whether we are a sending entity
844 const auto myPartitionType = entity.partitionType();
845 // see dune/grid/utility/entitycommhelper.hh
846 const bool send = EntityCommHelper< CommInterface > :: send( myPartitionType );
847
848 if constexpr (isMMesh)
849 {
850 // send rank for linkage
851 buffer.write( myRank_ );
852 }
853
854 // if we send data then send rank and dofs
855 if( send )
856 {
857 if constexpr (!isMMesh)
858 {
859 // send rank for linkage
860 buffer.write( myRank_ );
861 }
862
863 const int numDofs = blockMapper_.numEntityDofs( entity );
864
865 typedef std::vector< GlobalKeyType > IndicesType ;
866 IndicesType indices( numDofs );
867
868 // copy all global keys
869 blockMapper_.mapEachEntityDof( entity, AssignFunctor< IndicesType >( indices ) );
870
871 // write global keys to message buffer
872 for( int i = 0; i < numDofs; ++i )
873 buffer.write( indices[ i ] );
874 }
875 }
876
878 template< class MessageBuffer, class Entity >
879 void scatter( MessageBuffer &buffer, const Entity &entity, const size_t dataSize )
880 {
881 // if data size > 0 then other side is sender
882 if( dataSize > 0 )
883 {
884 // read rank of other side
885 GlobalKeyType rank;
886 buffer.read( rank );
887 assert( (rank >= 0) && (rank < mySize_) );
888
889 // check whether we are a sending entity
890 const auto myPartitionType = entity.partitionType();
891 // see dune/grid/utility/entitycommhelper.hh
892 const bool receive = EntityCommHelper< CommInterface > :: receive( myPartitionType );
893
894 // insert rank of link into set of links
895 linkStorage_.insert( rank );
896
897 // read indices from stream
898 typedef std::vector< GlobalKeyType > IndicesType ;
899 IndicesType indices( dataSize - 1 );
900 for(size_t i=0; i<dataSize-1; ++i)
901 buffer.read( indices[i] );
902
903 // if we are a receiving entity
904 if( receive )
905 {
907 //
908 // Problem here: sending and receiving order might differ
909 // Solution: sort all dofs after receiving order and send
910 // senders dofs back at the end
911 //
913
914 // if data has been send and we are receive entity
915 // then insert indices into send map of rank
916 sendIndexMap_[ rank ].insert( indices );
917
918 // build local mapping for receiving of dofs
919 const int numDofs = blockMapper_.numEntityDofs( entity );
920 indices.resize( numDofs );
921
922 // map each entity dof and store in indices
923 blockMapper_.mapEachEntityDof( entity, AssignFunctor< IndicesType >( indices ) );
924
925 // insert receiving dofs
926 recvIndexMap_[ rank ].insert( indices );
927 }
928 }
929 }
930
932 template< class Entity >
933 size_t size( const Entity &entity ) const
934 {
935 const PartitionType myPartitionType = entity.partitionType();
936 const bool send = EntityCommHelper< CommInterface > :: send( myPartitionType );
937 return (send) ? (blockMapper_.numEntityDofs( entity ) + 1) : isMMesh;
938 }
939 };
940
941
942
943 template< class BlockMapper >
944 template< class Space >
945 inline void DependencyCache< BlockMapper > :: buildMaps( const Space& space )
946 {
947 typedef typename Space::GridPartType::CommunicationType CommunicationType;
948 if( interface_ == InteriorBorder_All_Interface )
949 {
950 LinkBuilder< CommunicationType, LinkStorageType, IndexMapVectorType,
952 handle( space.gridPart().comm(),
953 space.blockMapper(),
954 linkStorage_, sendIndexMap_, recvIndexMap_ );
955 buildMaps( space, handle );
956 }
957 else if( interface_ == InteriorBorder_InteriorBorder_Interface )
958 {
959 LinkBuilder< CommunicationType, LinkStorageType, IndexMapVectorType,
961 handle( space.gridPart().comm(),
962 space.blockMapper(),
963 linkStorage_, sendIndexMap_, recvIndexMap_ );
964 buildMaps( space, handle );
965 }
966 else if( interface_ == All_All_Interface )
967 {
968 LinkBuilder< CommunicationType, LinkStorageType, IndexMapVectorType, All_All_Interface >
969 handle( space.gridPart().comm(),
970 space.blockMapper(),
971 linkStorage_, sendIndexMap_, recvIndexMap_ );
972 buildMaps( space, handle );
973 }
974 else
975 DUNE_THROW( NotImplemented, "DependencyCache for the given interface has not been implemented, yet." );
976#ifndef NDEBUG
977 // checks that sizes of index maps are equal on sending and receiving proc
978 checkConsistency();
979#endif
980 }
981
982
983 template< class BlockMapper >
984 template< class Space, class Comm, class LS, class IMV, InterfaceType CI >
985 inline void DependencyCache< BlockMapper >
986 :: buildMaps( const Space& space, LinkBuilder< Comm, LS, IMV, CI > &handle )
987 {
988 linkStorage_.clear();
989 const size_t size = recvIndexMap_.size();
990 for( size_t i = 0; i < size; ++i )
991 {
992 recvIndexMap_[ i ].clear();
993 sendIndexMap_[ i ].clear();
994 }
995
996 // make one all to all communication to build up communication pattern
997 space.gridPart().communicate( handle, All_All_Interface , ForwardCommunication );
998
999 // remove old linkage
1000 mpAccess().removeLinkage();
1001 // create new linkage
1002 mpAccess().insertRequestSymetric( linkStorage_ );
1003 }
1004
1005 template< class BlockMapper >
1006 inline void DependencyCache< BlockMapper > :: checkConsistency()
1007 {
1008 const int nLinks = nlinks();
1009
1010 ObjectStreamVectorType buffer( nLinks );
1011
1012 // check that order and size are consistent
1013 for(int l=0; l<nLinks; ++l)
1014 {
1015 buffer[l].clear();
1016 const int sendSize = sendIndexMap_[ dest( l ) ].size();
1017 buffer[l].write( sendSize );
1018 for(int i=0; i<sendSize; ++i)
1019 buffer[l].write( i );
1020 }
1021
1022 // exchange data to other procs
1023 buffer = mpAccess().exchange( buffer );
1024
1025 // check that order and size are consistent
1026 for(int l=0; l<nLinks; ++l)
1027 {
1028 const int recvSize = recvIndexMap_[ dest( l ) ].size();
1029 int sendedSize;
1030 buffer[l].read( sendedSize );
1031
1032 // compare sizes, must be the same
1033 if( recvSize != sendedSize )
1034 {
1035 DUNE_THROW(InvalidStateException,"Sizes do not match!" << sendedSize << " o|r " << recvSize);
1036 }
1037
1038 for(int i=0; i<recvSize; ++i)
1039 {
1040 int idx;
1041 buffer[l].read( idx );
1042
1043 // ordering should be the same on both sides
1044 if( i != idx )
1045 {
1046 DUNE_THROW(InvalidStateException,"Wrong ordering of send and recv maps!");
1047 }
1048 }
1049 }
1050 }
1051
1052 template< class BlockMapper >
1053 template< class Space, class DiscreteFunction, class Operation >
1054 inline void DependencyCache< BlockMapper >
1055 :: exchange( const Space& space, DiscreteFunction &discreteFunction, const Operation& operation )
1056 {
1057 // on serial runs: do nothing expect for PetscDiscreteFunction
1058 if( NonBlockingCommunicationType::skip( discreteFunction, space.gridPart().comm() ) )
1059 return ;
1060
1061 // create non-blocking communication object
1062 NonBlockingCommunicationType nbc( space, *this );
1063
1064 // perform send operation
1065 nbc.send( discreteFunction );
1066
1067 // store time for send and receive of data
1068 exchangeTime_ = nbc.receive( discreteFunction, operation );
1069 }
1070
1071 template< class BlockMapper >
1072 template< class DiscreteFunction >
1073 inline void DependencyCache< BlockMapper >
1074 :: writeBuffer( ObjectStreamVectorType &osv,
1075 const DiscreteFunction &discreteFunction ) const
1076 {
1077 const int numLinks = nlinks();
1078 for( int link = 0; link < numLinks; ++link )
1079 writeBuffer( link, osv[ link ], discreteFunction );
1080 }
1081
1082 template< class BlockMapper >
1083 template< class DiscreteFunction, class Operation >
1084 inline void DependencyCache< BlockMapper >
1085 :: readBuffer( ObjectStreamVectorType &osv,
1086 DiscreteFunction &discreteFunction,
1087 const Operation& operation ) const
1088 {
1089 const int numLinks = nlinks();
1090 for( int link = 0; link < numLinks; ++link )
1091 readBuffer( link, osv[ link ], discreteFunction, operation );
1092 }
1093
1095 template < class BlockMapper >
1096 class CommManagerSingletonKey
1097 {
1098 const BlockMapper& blockMapper_;
1099 const InterfaceType interface_;
1100 const CommunicationDirection dir_;
1101 const int pSize_;
1102 public:
1104 CommManagerSingletonKey(const int pSize,
1105 const BlockMapper& blockMapper,
1106 const InterfaceType interface,
1107 const CommunicationDirection dir)
1108 : blockMapper_( blockMapper ),
1109 interface_(interface), dir_(dir), pSize_( pSize )
1110 {}
1111
1113 CommManagerSingletonKey(const CommManagerSingletonKey & org) = default;
1114
1116 bool operator == (const CommManagerSingletonKey & otherKey) const
1117 {
1118 // block mapper of space is either singleton or the pointers differ anyway
1119 return (&(blockMapper_) == &(otherKey.blockMapper_) );
1120 }
1121
1123 InterfaceType interface() const
1124 {
1125 return interface_;
1126 }
1127
1129 CommunicationDirection direction() const
1130 {
1131 return dir_;
1132 }
1133
1135 int pSize () const { return pSize_; }
1136 };
1137
1140 template <class KeyImp, class ObjectImp>
1141 class CommManagerFactory
1142 {
1143 public:
1145 static ObjectImp * createObject( const KeyImp & key )
1146 {
1147 return new ObjectImp(key.pSize(), key.interface(), key.direction());
1148 }
1149
1151 static void deleteObject( ObjectImp * obj )
1152 {
1153 delete obj;
1154 }
1155 };
1156
1158 template <class SpaceImp>
1159 class CommunicationManager
1160 {
1161 typedef CommunicationManager<SpaceImp> ThisType;
1162
1163 typedef typename SpaceImp::BlockMapperType BlockMapperType;
1164
1165 // type of communication manager object which does communication
1166 typedef DependencyCache< BlockMapperType > DependencyCacheType;
1167
1168 typedef CommManagerSingletonKey< BlockMapperType > KeyType;
1169 typedef CommManagerFactory<KeyType, DependencyCacheType> FactoryType;
1170
1171 typedef SingletonList< KeyType , DependencyCacheType , FactoryType > CommunicationProviderType;
1172
1173 typedef SpaceImp SpaceType;
1174 const SpaceType& space_;
1175
1176 typedef ALU3DSPACE MpAccessLocal MPAccessInterfaceType;
1177
1178 // is singleton per block mapper (spaces can differ)
1179 std::unique_ptr< DependencyCacheType, typename CommunicationProviderType::Deleter > cache_;
1180
1181 // copy constructor
1182 CommunicationManager(const ThisType& org) = delete;
1183 public:
1184 // type of non-blocking communication object
1185 typedef typename DependencyCacheType :: NonBlockingCommunicationType NonBlockingCommunicationType;
1186
1188 CommunicationManager(const SpaceType& space,
1189 const InterfaceType interface,
1190 const CommunicationDirection dir)
1191 : space_( space ) // my space which should have a longer life time than
1192 // this communicator since the communicator is created inside the space
1193 , cache_( &CommunicationProviderType::getObject(
1194 KeyType( space.gridPart().comm().size(), space_.blockMapper(), interface,dir) ) )
1195 {
1196 // pass communication on to dependency cache
1197 cache().init( space.gridPart().comm() );
1198
1199 //std::cout << "P["<< space.gridPart().comm().rank() <<"] CommunicationManager: created and got cache " << &cache_ << std::endl;
1200 }
1201
1203 CommunicationManager(const SpaceType& space)
1204 : CommunicationManager( space, space.communicationInterface(), space.communicationDirection() )
1205 {}
1206
1207 DependencyCacheType& cache () const { assert( cache_ ); return *cache_; }
1208
1211 {
1212 return cache().communicationInterface();
1213 }
1214
1217 {
1218 return cache().communicationDirection();
1219 }
1220
1222 double buildTime() const
1223 {
1224 return cache().buildTime();
1225 }
1226
1228 double exchangeTime() const
1229 {
1230 return cache().exchangeTime();
1231 }
1232
1233 MPAccessInterfaceType& mpAccess()
1234 {
1235 return cache().mpAccess();
1236 }
1237
1239 NonBlockingCommunicationType nonBlockingCommunication() const
1240 {
1241 return cache().nonBlockingCommunication( space_ );
1242 }
1243
1246 template <class DiscreteFunctionType>
1247 void exchange(DiscreteFunctionType & df) const
1248 {
1249 // get type of default operation
1250 typedef typename DiscreteFunctionType :: DiscreteFunctionSpaceType
1251 :: template CommDataHandle< DiscreteFunctionType > :: OperationType DefaultOperationType;
1252
1253 // create default operation
1254 DefaultOperationType operation;
1255
1256 exchange( df, operation );
1257 }
1258
1261 template <class DiscreteFunctionType, class Operation>
1262 void exchange(DiscreteFunctionType & df, const Operation& operation ) const
1263 {
1264 cache().exchange( df.space(), df, operation );
1265 }
1266
1269 template <class Vector, class Operation>
1270 void exchange(const SpaceType& space, Vector& v, const Operation& operation ) const
1271 {
1272 static_assert( std::is_base_of< IsBlockVector, Vector > :: value, "exchange needs BlockVectorInterface and derived");
1273 cache().exchange( space, v, operation );
1274 }
1275
1277 template <class ObjectStreamVectorType, class DiscreteFunctionType>
1278 void writeBuffer(ObjectStreamVectorType& osv, const DiscreteFunctionType & df) const
1279 {
1280 cache().writeBuffer( osv, df );
1281 }
1282
1283 // read given df from given buffer
1284 template <class ObjectStreamVectorType, class DiscreteFunctionType>
1285 void readBuffer(ObjectStreamVectorType& osv, DiscreteFunctionType & df) const
1286 {
1287 typedef typename DiscreteFunctionType :: DiscreteFunctionSpaceType
1288 :: template CommDataHandle<DiscreteFunctionType> :: OperationType OperationType;
1289
1290 // communication operation to be performed on the received data
1291 OperationType operation;
1292
1293 readBuffer( osv, df, operation );
1294 }
1295
1296 // read given df from given buffer
1297 template <class ObjectStreamVectorType, class DiscreteFunctionType, class OperationType>
1298 void readBuffer(ObjectStreamVectorType& osv, DiscreteFunctionType & df, const OperationType& operation) const
1299 {
1300 cache().readBuffer( osv, df , operation);
1301 }
1302
1304 void rebuildCache()
1305 {
1306 cache().rebuild( space_ );
1307 }
1308 };
1309
1311 class CommunicationManagerList
1312 {
1314 template <class MPAccessType, class ObjectStreamVectorType>
1315 class DiscreteFunctionCommunicatorInterface
1316 {
1317 protected:
1318 DiscreteFunctionCommunicatorInterface()
1319 {}
1320 public:
1321 virtual ~DiscreteFunctionCommunicatorInterface()
1322 {}
1323
1324 virtual MPAccessType& mpAccess() = 0;
1325 virtual void writeBuffer(ObjectStreamVectorType&) const = 0;
1326 virtual void readBuffer(ObjectStreamVectorType&) = 0;
1327 virtual void rebuildCache() = 0;
1328
1329 virtual bool handles ( IsDiscreteFunction &df ) const = 0;
1330 };
1331
1335 template <class DiscreteFunctionImp,
1336 class MPAccessType,
1337 class ObjectStreamVectorType,
1338 class OperationType >
1339 class DiscreteFunctionCommunicator
1340 : public DiscreteFunctionCommunicatorInterface<MPAccessType,ObjectStreamVectorType>
1341 {
1342 typedef DiscreteFunctionImp DiscreteFunctionType;
1343 typedef typename DiscreteFunctionType :: DiscreteFunctionSpaceType DiscreteFunctionSpaceType;
1344
1345 typedef CommunicationManager<DiscreteFunctionSpaceType> CommunicationManagerType;
1346
1347 // object to communicate
1350 CommunicationManagerType comm_;
1351
1353 const OperationType operation_;
1354 public:
1356 DiscreteFunctionCommunicator(DiscreteFunctionType& df, const OperationType& op )
1357 : df_(df), comm_(df_.space()), operation_( op )
1358 {}
1359
1361 virtual MPAccessType& mpAccess()
1362 {
1363 return comm_.mpAccess();
1364 }
1365
1367 virtual void writeBuffer(ObjectStreamVectorType& osv) const
1368 {
1369 comm_.writeBuffer(osv,df_);
1370 }
1371
1373 virtual void readBuffer(ObjectStreamVectorType& osv)
1374 {
1375 comm_.readBuffer(osv, df_, operation_ );
1376 }
1377
1379 virtual void rebuildCache()
1380 {
1381 comm_.rebuildCache();
1382 }
1383
1384 virtual bool handles ( IsDiscreteFunction &df ) const { return (&static_cast< IsDiscreteFunction & >( df_ ) == &df); }
1385 };
1386
1387 // ALUGrid send/recv buffers
1388 typedef ALU3DSPACE ObjectStream ObjectStreamType;
1389
1390 // type of buffer vector
1391 typedef std::vector< ObjectStreamType > ObjectStreamVectorType;
1392
1393 // type of ALUGrid Communicator
1394 typedef ALU3DSPACE MpAccessLocal MPAccessInterfaceType;
1395
1396 // interface for communicated objects
1397 typedef DiscreteFunctionCommunicatorInterface<MPAccessInterfaceType,ObjectStreamVectorType>
1398 CommObjInterfaceType;
1399
1400 // list of communicated objects
1401 typedef std::list < std::unique_ptr< CommObjInterfaceType > > CommObjListType;
1402 CommObjListType objList_;
1403
1404 // number of processors
1405 int mySize_;
1406
1407 public:
1409 template <class CombinedObjectType>
1410 CommunicationManagerList(CombinedObjectType& cObj) :
1411 mySize_( -1 )
1412 {
1413 // add all discrete functions containd in cObj to list
1414 cObj.addToList(*this);
1415 }
1416
1418 CommunicationManagerList()
1419 : mySize_( -1 )
1420 {}
1421
1422 CommunicationManagerList ( const CommunicationManagerList & ) = delete;
1423
1425 template <class DiscreteFunctionImp, class Operation>
1426 void addToList(DiscreteFunctionImp &df, const Operation& operation )
1427 {
1428 // type of communication object
1429 typedef DiscreteFunctionCommunicator<DiscreteFunctionImp,
1430 MPAccessInterfaceType,
1431 ObjectStreamVectorType,
1432 Operation > CommObj;
1433 CommObj * obj = new CommObj(df, operation);
1434 objList_.push_back( std::unique_ptr< CommObjInterfaceType > (obj) );
1435
1436 // if mySize wasn't set, set to number of processors
1437 if( mySize_ < 0 )
1438 {
1439 // get ALUGrid communicator
1440 MPAccessInterfaceType& mpAccess = objList_.front()->mpAccess();
1441
1442 // set number of processors
1443 mySize_ = mpAccess.psize();
1444 }
1445 }
1446
1448 template <class DiscreteFunctionImp>
1449 void addToList(DiscreteFunctionImp &df)
1450 {
1451 DFCommunicationOperation::Copy operation;
1452 addToList( df, operation );
1453 }
1454
1455 template< class DiscreteFunction >
1456 void removeFromList ( DiscreteFunction &df )
1457 {
1458 const auto handles = [ &df ] ( const std::unique_ptr< CommObjInterfaceType > &commObj ) { assert( commObj ); return commObj->handles( df ); };
1459 CommObjListType::reverse_iterator pos = std::find_if( objList_.rbegin(), objList_.rend(), handles );
1460 if( pos != objList_.rend() )
1461 objList_.erase( --pos.base() );
1462 else
1463 DUNE_THROW( RangeError, "Trying to remove discrete function that was never added" );
1464 }
1465
1468 void exchange()
1469 {
1470 // if only one process, do nothing
1471 if( mySize_ <= 1 ) return ;
1472
1473 // exchange data
1474 if(objList_.size() > 0)
1475 {
1476 // rebuild cahce if grid has changed
1477 for(auto& elem : objList_)
1478 elem->rebuildCache();
1479
1480 // get ALUGrid communicator
1481 auto& mpAccess = objList_.front()->mpAccess();
1482
1483 // create buffer
1484 ObjectStreamVectorType osv( mpAccess.nlinks() );
1485
1486 // fill buffers
1487 for(auto& elem : objList_)
1488 elem->writeBuffer(osv);
1489
1490 // exchange data
1491 osv = mpAccess.exchange(osv);
1492
1493 // read buffers
1494 for(auto& elem : objList_)
1495 elem->readBuffer(osv);
1496 }
1497 }
1498 };
1499#endif // #if ALU3DGRID_PARALLEL
1501
1502 } // namespace Fem
1503
1504} // namespace Dune
1505#endif // #ifndef DUNE_FEM_CACHED_COMMUNICATION_MANAGER_HH
A vector valued function space.
Definition: functionspace.hh:60
forward declaration
Definition: discretefunction.hh:51
const DiscreteFunctionSpaceType & space() const
obtain a reference to the corresponding DiscreteFunctionSpace
Definition: discretefunction.hh:709
TupleDiscreteFunctionSpace< typename DiscreteFunctions::DiscreteFunctionSpaceType ... > DiscreteFunctionSpaceType
type for the discrete function space this function lives in
Definition: discretefunction.hh:69
A simple stop watch.
Definition: timer.hh:43
double elapsed() const noexcept
Get elapsed user-time from last reset until now/last stop in seconds.
Definition: timer.hh:77
Different resources needed by all grid implementations.
Describes the parallel communication interface class for MessageBuffers and DataHandles.
double buildTime() const
return time needed for last build
Definition: communicationmanager.hh:203
InterfaceType communicationInterface() const
return communication interface
Definition: communicationmanager.hh:189
void exchange() const
Definition: communicationmanager.hh:391
void exchange(DiscreteFunction &discreteFunction) const
exchange data for a discrete function using the copy operation
Definition: communicationmanager.hh:225
CommunicationDirection communicationDirection() const
return communication direction
Definition: communicationmanager.hh:194
NonBlockingCommunicationType nonBlockingCommunication() const
return object for non-blocking communication
Definition: communicationmanager.hh:215
double exchangeTime() const
return time needed for last exchange of data
Definition: communicationmanager.hh:209
void addToList(DiscreteFunctionImp &df, const Operation &operation)
add discrete function to communication list
Definition: communicationmanager.hh:363
#define DUNE_THROW(E, m)
Definition: exceptions.hh:218
PartitionType
Attributes used in the generic overlap model.
Definition: gridenums.hh:30
CommunicationDirection
Define a type for communication direction parameter.
Definition: gridenums.hh:170
InterfaceType
Parameter to be used for the communication functions.
Definition: gridenums.hh:86
@ ForwardCommunication
communicate as given in InterfaceType
Definition: gridenums.hh:171
@ InteriorBorder_All_Interface
send interior and border, receive all entities
Definition: gridenums.hh:88
@ All_All_Interface
send all and receive all entities
Definition: gridenums.hh:91
@ InteriorBorder_InteriorBorder_Interface
send/receive interior and border entities
Definition: gridenums.hh:87
constexpr void forEach(Range &&range, F &&f)
Range based for loop.
Definition: hybridutilities.hh:256
EnableIfInterOperable< T1, T2, bool >::type operator==(const ForwardIteratorFacade< T1, V1, R1, D > &lhs, const ForwardIteratorFacade< T2, V2, R2, D > &rhs)
Checks for equality.
Definition: iteratorfacades.hh:238
Some useful basic math stuff.
Dune namespace.
Definition: alignedallocator.hh:13
constexpr std::integral_constant< std::size_t, sizeof...(II)> size(std::integer_sequence< T, II... >)
Return the size of the sequence.
Definition: integersequence.hh:75
constexpr std::bool_constant<((II==value)||...)> contains(std::integer_sequence< T, II... >, std::integral_constant< T, value >)
Checks whether or not a given sequence contains a value.
Definition: integersequence.hh:137
A simple timing class.
std::size_t fixedSize
The number of data items per index if it is fixed, 0 otherwise.
Definition: variablesizecommunicator.hh:264
Definition of macros controlling symbol visibility at the ABI level.
#define DUNE_EXPORT
Export a symbol as part of the public ABI.
Definition: visibility.hh:20
Creative Commons License   |  Legal Statements / Impressum  |  Hosted by TU Dresden  |  generated with Hugo v0.111.3 (Jul 27, 22:29, 2024)