1 // -*- tab-width: 2; indent-tabs-mode: nil -*- 2 // vi: set et ts=2 sw=2 sts=2: 3 #ifndef DUNE_PDELAB_LOCALOPERATOR_BLOCKOFFDIAGONALWRAPPER_HH 4 #define DUNE_PDELAB_LOCALOPERATOR_BLOCKOFFDIAGONALWRAPPER_HH 5 6 #include <dune/pdelab/common/intersectiontype.hh> 7 #include <dune/pdelab/localoperator/blockdiagonalwrapper.hh> 8 namespace Dune { 9 namespace PDELab { 10 11 namespace impl { 12 13 // This can be used to get a vector view that returns a zero coefficient. 14 template <typename View> 15 class ZeroViewWrapper 16 { 17 public: 18 using Container = typename View::Container; 19 using ElementType = typename View::value_type; 20 using SizeType = typename View::size_type; 21 22 // If zero is set to false this class will forward all container 23 // accesses to the vector view that is passed as first argument. This 24 // means it will basically behave the same way as the view itself. 25 // 26 // If you set zero to false it will return 0.0 when it is asked for a 27 // coefficient. 28 // 29 // Use case: The methods in the localoperator interface sometimes get 30 // multiple coefficient views that need to have the same type (e.g. x_s 31 // and x_n for the ansatz function in skeleton terms). This can be used 32 // to 'null' one of those vectors without actually changing any values 33 // in memory. ZeroViewWrapper(const View & view,bool zero)34 ZeroViewWrapper(const View& view, bool zero) 35 : _view(view), _zero(zero), _zeroCoefficient(0.0) 36 {} 37 38 template <typename LFS> operator ()(const LFS & lfs,SizeType i) const39 const ElementType& operator()(const LFS& lfs, SizeType i) const 40 { 41 if (_zero) 42 return _zeroCoefficient; 43 else 44 return _view.container()(lfs, i); 45 } 46 47 private: 48 const View& _view; 49 bool _zero; 50 ElementType _zeroCoefficient; 51 }; 52 53 // Interfaces look different in the fast-DG case 54 template <typename Container, typename LocalFunctionSpaceCache> 55 class ZeroViewWrapper<AliasedVectorView<Container, LocalFunctionSpaceCache>> 56 { 57 public: 58 using View = ConstAliasedVectorView<Container, LocalFunctionSpaceCache>; 59 using ElementType = typename View::ElementType; 60 using SizeType = typename View::size_type; 61 ZeroViewWrapper(const View & view,bool zero)62 ZeroViewWrapper(const View& view, bool zero) 63 : _view(view), _zero(zero), _zeroCoefficient(0.0) 64 {} 65 66 template <typename LFS> operator ()(const LFS & lfs,SizeType i) const67 const ElementType& operator()(const LFS& lfs, SizeType i) const 68 { 69 if (_zero) 70 return _zeroCoefficient; 71 else 72 return _view(lfs, i); 73 } 74 data() const75 const ElementType* data() const 76 { 77 // Note: There is no principal problem in implementing this. Create 78 // an array of ElementType that has the correct size and contains 79 // only zeros. This was not implemted since there was no way of 80 // testing the implementation. Better to have a clear error message 81 // than a delicate implementation bug. 82 DUNE_THROW(Dune::Exception, "So far the ZeroViewWrapper does not support fast DG local operators using the data() method to access coefficients. ."); 83 } 84 85 private: 86 const View& _view; 87 bool _zero; 88 ElementType _zeroCoefficient; 89 }; 90 91 92 } // namespace impl 93 94 /** \brief A local operator that accumulates the off block diagonal 95 * 96 * This makes only sense for methods that have a block structure like 97 * Discontinuous Galerking methods or Finite Vvolume methods. For those the 98 * resulting operator assembles only the off diagonal blocks when the 99 * jacobian methods are called. 100 * 101 * Note: This operator does skeletons in a one sided fashion. This means 102 * that every skeleton-intersection is visited twice for assembling the 103 * whole block diagonal. Once from one side and once from the other. This 104 * behavior is needed for the implementation of matrix-free block 105 * preconditioners. 106 * 107 * \tparam[in] LocalOperator Type of the local operator that gets wrapped 108 */ 109 template <typename LocalOperator> 110 class BlockOffDiagonalLocalOperatorWrapper 111 : public Dune::PDELab::LocalOperatorDefaultFlags 112 { 113 public: 114 // We only want to assemble the off-diagonal blocks so we only need 115 // skeleton pattern 116 static constexpr bool doPatternSkeleton = true; 117 118 // For assembling the off-diagonal blocks we only need alpha skeleton 119 static constexpr bool doAlphaSkeleton = LocalOperator::doAlphaSkeleton; 120 121 // If the underlying lop is linear, this one will be linear too 122 static constexpr bool isLinear = LocalOperator::isLinear; 123 124 // This wrapper is designed to use two sided assembly. If it was just 125 // about assembling the whole diagonal block matrix one sided assembly 126 // would be more efficient. For the implementation of matrix-free 127 // preconditioner we need to assemble a diagonal block locally for a 128 // given cell so we need to assemble in a two sided fashion. 129 static constexpr bool doSkeletonTwoSided = true; 130 131 /** \brief Construct new instance of class 132 * 133 * \param[in] _localOperator Wrapped local operator instance 134 */ BlockOffDiagonalLocalOperatorWrapper(const LocalOperator & localOperator)135 BlockOffDiagonalLocalOperatorWrapper(const LocalOperator& localOperator) 136 : _localOperator(localOperator) 137 {} 138 139 /** \brief Copy constructor */ BlockOffDiagonalLocalOperatorWrapper(const BlockOffDiagonalLocalOperatorWrapper & other)140 BlockOffDiagonalLocalOperatorWrapper(const BlockOffDiagonalLocalOperatorWrapper& other) 141 : _localOperator(other._localOperator) 142 {} 143 144 // define sparsity pattern connecting self and neighbor dofs 145 template<typename LFSU, typename LFSV, typename LocalPattern> pattern_skeleton(const LFSU & lfsu_s,const LFSV & lfsv_s,const LFSU & lfsu_n,const LFSV & lfsv_n,LocalPattern & pattern_sn,LocalPattern & pattern_ns) const146 void pattern_skeleton (const LFSU& lfsu_s, const LFSV& lfsv_s, const LFSU& lfsu_n, const LFSV& lfsv_n, 147 LocalPattern& pattern_sn, 148 LocalPattern& pattern_ns) const 149 { 150 _localOperator.pattern_skeleton (lfsu_s, lfsv_s, lfsu_n, lfsv_n, pattern_sn, pattern_ns); 151 } 152 153 template<typename IG, typename LFSU, typename X, typename LFSV, typename MAT> jacobian_skeleton(const IG & ig,const LFSU & lfsu_s,const X & x_s,const LFSV & lfsv_s,const LFSU & lfsu_n,const X & x_n,const LFSV & lfsv_n,MAT & mat_ss,MAT & mat_sn,MAT & mat_ns,MAT & mat_nn) const154 void jacobian_skeleton (const IG& ig, 155 const LFSU& lfsu_s, const X& x_s, const LFSV& lfsv_s, 156 const LFSU& lfsu_n, const X& x_n, const LFSV& lfsv_n, 157 MAT& mat_ss, MAT& mat_sn, 158 MAT& mat_ns, MAT& mat_nn) const 159 { 160 // Since we do two sided assembly (visiting each intersection twice) we 161 // have options. We could assemble either mat_sn or mat_ns. Both lead 162 // to the same solution. In order to be consistent with the choice for 163 // jacobian_apply_skeleton we will assemble m_ns. 164 impl::BlockDiagonalAccumulationViewWrapper<MAT> view_ns(mat_ns, true); 165 impl::BlockDiagonalAccumulationViewWrapper<MAT> view_other(mat_ns, false); 166 _localOperator.jacobian_skeleton(ig, lfsu_s, x_s, lfsv_s, lfsu_n, x_n, lfsv_n, view_other, view_other, view_ns, view_other); 167 } 168 169 template<typename IG, typename LFSU, typename Z, typename LFSV, typename Y> jacobian_apply_skeleton(const IG & ig,const LFSU & lfsu_s,const Z & z_s,const LFSV & lfsv_s,const LFSU & lfsu_n,const Z & z_n,const LFSV & lfsv_n,Y & y_s,Y & y_n) const170 void jacobian_apply_skeleton(const IG& ig, 171 const LFSU& lfsu_s, const Z& z_s, const LFSV& lfsv_s, 172 const LFSU& lfsu_n, const Z& z_n, const LFSV& lfsv_n, 173 Y& y_s, Y& y_n) const 174 { 175 // This is more tricky. For a full Jacobian apply you would do: 176 // 177 // y_s = A_ss z_s + A_ns z_n 178 // y_n = A_sn z_s + A_nn z_n 179 // 180 // Instead we want: 181 // 182 // (1) y_s = A_ns z_n 183 // (2) y_n = A_sn z_s 184 // 185 // Since we do two sided assembly we only need to assemble one of these 186 // two. For the matrix free preconditioner we need to apply the 187 // jacobian locally for one block so we need to implement equation (1) 188 // to get all contributions of other cell on the current one. 189 190 // Set input coefficients z_s to zero 191 impl::ZeroViewWrapper<Z> z_zero(z_s, true); 192 impl::ZeroViewWrapper<Z> z_neigh(z_n, false); 193 194 // Only accumulate in y_s 195 impl::BlockDiagonalAccumulationViewWrapper<Y> view_s_on(y_s, true); 196 impl::BlockDiagonalAccumulationViewWrapper<Y> view_n_off(y_n, false); 197 198 // Apply Jacobian 199 Dune::PDELab::impl::jacobianApplySkeleton(_localOperator, ig, lfsu_s, z_zero, lfsv_s, lfsu_n, z_neigh, lfsv_n, view_s_on, view_n_off); 200 } 201 202 template<typename IG, typename LFSU, typename X, typename Z, typename LFSV, typename Y> jacobian_apply_skeleton(const IG & ig,const LFSU & lfsu_s,const X & x_s,const Z & z_s,const LFSV & lfsv_s,const LFSU & lfsu_n,const X & x_n,const Z & z_n,const LFSV & lfsv_n,Y & y_s,Y & y_n) const203 void jacobian_apply_skeleton(const IG& ig, 204 const LFSU& lfsu_s, const X& x_s, const Z& z_s, const LFSV& lfsv_s, 205 const LFSU& lfsu_n, const X& x_n, const Z& z_n, const LFSV& lfsv_n, 206 Y& y_s, Y& y_n) const 207 { 208 // This is more tricky. For a full Jacobian apply you would do: 209 // 210 // y_s = A_ss z_s + A_ns z_n 211 // y_n = A_sn z_s + A_nn z_n 212 // 213 // Instead we want: 214 // 215 // (1) y_s = A_ns z_n 216 // (2) y_n = A_sn z_s 217 // 218 // Since we do two sided assembly we only need to assemble one of these 219 // two. For the matrix free preconditioner we need to apply the 220 // jacobian locally for one block so we need to implement equation (1) 221 // to get all contributions of other cell on the current one. 222 223 // Set input coefficients z_s to zero 224 impl::ZeroViewWrapper<Z> z_zero(z_s, true); 225 impl::ZeroViewWrapper<Z> z_neigh(z_n, false); 226 227 // Only accumulate in y_s 228 impl::BlockDiagonalAccumulationViewWrapper<Y> view_s_on(y_s, true); 229 impl::BlockDiagonalAccumulationViewWrapper<Y> view_n_off(y_n, false); 230 231 // Apply Jacobian 232 Dune::PDELab::impl::jacobianApplySkeleton(_localOperator, ig, lfsu_s, x_s, z_zero, lfsv_s, lfsu_n, x_n, z_neigh, lfsv_n, view_s_on, view_n_off); 233 } 234 235 private: 236 /** \brief Wrapped original local operator */ 237 const LocalOperator& _localOperator; 238 }; 239 240 } // namespace PDELab 241 } // namespace Dune 242 243 #endif 244