MueLu Version of the Day
Loading...
Searching...
No Matches
Thyra_MueLuMaxwell1PreconditionerFactory_def.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// MueLu: A package for multigrid based preconditioning
4//
5// Copyright 2012 NTESS and the MueLu contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#ifndef THYRA_MUELU_MAXWELL1_PRECONDITIONER_FACTORY_DEF_HPP
11#define THYRA_MUELU_MAXWELL1_PRECONDITIONER_FACTORY_DEF_HPP
12
13#include <list>
15#include <Xpetra_CrsMatrixWrap.hpp>
16#include <Xpetra_CrsMatrix.hpp>
17#include <Xpetra_Matrix.hpp>
18#include <Xpetra_ThyraUtils.hpp>
19#include <MueLu_Maxwell1.hpp>
20#include <Xpetra_TpetraHalfPrecisionOperator.hpp>
21
22#if defined(HAVE_MUELU_STRATIMIKOS) && defined(HAVE_MUELU_THYRA)
23
24// This is not as general as possible, but should be good enough for most builds.
25#if ((defined(HAVE_TPETRA_INST_DOUBLE) && defined(HAVE_TPETRA_INST_FLOAT) && !defined(HAVE_TPETRA_INST_COMPLEX_DOUBLE) && !defined(HAVE_TPETRA_INST_COMPLEX_FLOAT)) || \
26 (!defined(HAVE_TPETRA_INST_DOUBLE) && !defined(HAVE_TPETRA_INST_FLOAT) && defined(HAVE_TPETRA_INST_COMPLEX_DOUBLE) && defined(HAVE_TPETRA_INST_COMPLEX_FLOAT)) || \
27 (defined(HAVE_TPETRA_INST_DOUBLE) && defined(HAVE_TPETRA_INST_FLOAT) && defined(HAVE_TPETRA_INST_COMPLEX_DOUBLE) && defined(HAVE_TPETRA_INST_COMPLEX_FLOAT)))
28#define MUELU_CAN_USE_MIXED_PRECISION
29#endif
30
31namespace Thyra {
32
33using Teuchos::ParameterList;
34using Teuchos::RCP;
35using Teuchos::rcp;
36using Teuchos::rcp_const_cast;
37using Teuchos::rcp_dynamic_cast;
38
39// Constructors/initializers/accessors
40
41template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
42MueLuMaxwell1PreconditionerFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::MueLuMaxwell1PreconditionerFactory()
43 : paramList_(rcp(new ParameterList())) {}
44
45// Overridden from PreconditionerFactoryBase
46
47template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
48bool MueLuMaxwell1PreconditionerFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::isCompatible(const LinearOpSourceBase<Scalar>& fwdOpSrc) const {
49 const RCP<const LinearOpBase<Scalar>> fwdOp = fwdOpSrc.getOp();
50
51 if (Xpetra::ThyraUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::isTpetra(fwdOp)) return true;
52
53 return false;
54}
55
56template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
57RCP<PreconditionerBase<Scalar>> MueLuMaxwell1PreconditionerFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::createPrec() const {
58 return Teuchos::rcp(new DefaultPreconditioner<Scalar>);
59}
60
61template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
62void MueLuMaxwell1PreconditionerFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
63 initializePrec(const RCP<const LinearOpSourceBase<Scalar>>& fwdOpSrc, PreconditionerBase<Scalar>* prec, const ESupportSolveUse /*supportSolveUse*/) const {
64 // we are using typedefs here, since we are using objects from different packages (Xpetra, Thyra,...)
65 typedef Xpetra::Operator<Scalar, LocalOrdinal, GlobalOrdinal, Node> XpOp;
66 typedef Xpetra::ThyraUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node> XpThyUtils;
67 typedef Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node> XpMat;
68 typedef Thyra::LinearOpBase<Scalar> ThyLinOpBase;
70#if defined(MUELU_CAN_USE_MIXED_PRECISION)
71 typedef Xpetra::TpetraHalfPrecisionOperator<Scalar, LocalOrdinal, GlobalOrdinal, Node> XpHalfPrecOp;
72 typedef Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node> XpMV;
73 typedef typename XpHalfPrecOp::HalfScalar HalfScalar;
74 typedef typename Teuchos::ScalarTraits<Scalar>::magnitudeType Magnitude;
75 typedef typename Teuchos::ScalarTraits<Magnitude>::halfPrecision HalfMagnitude;
76 typedef Xpetra::MultiVector<HalfScalar, LocalOrdinal, GlobalOrdinal, Node> XphMV;
77 typedef Xpetra::MultiVector<Magnitude, LocalOrdinal, GlobalOrdinal, Node> XpmMV;
78 typedef Xpetra::MultiVector<HalfMagnitude, LocalOrdinal, GlobalOrdinal, Node> XphmMV;
79 typedef Xpetra::Matrix<HalfScalar, LocalOrdinal, GlobalOrdinal, Node> XphMat;
80#endif
81 Teuchos::TimeMonitor tM(*Teuchos::TimeMonitor::getNewTimer(std::string("ThyraMueLuMaxwell1::initializePrec")));
82
83 // Check precondition
84 TEUCHOS_ASSERT(Teuchos::nonnull(fwdOpSrc));
85 TEUCHOS_ASSERT(this->isCompatible(*fwdOpSrc));
86 TEUCHOS_ASSERT(prec);
87
88 // Create a copy, as we may remove some things from the list
89 ParameterList paramList = *paramList_;
90
91 // Retrieve wrapped concrete Xpetra matrix from FwdOp
92 const RCP<const ThyLinOpBase> fwdOp = fwdOpSrc->getOp();
93 TEUCHOS_TEST_FOR_EXCEPT(Teuchos::is_null(fwdOp));
94
95 // Check whether it is Epetra/Tpetra
96 bool bIsTpetra = XpThyUtils::isTpetra(fwdOp);
97 TEUCHOS_TEST_FOR_EXCEPT((bIsTpetra == false));
98
99 // wrap the forward operator as an Xpetra::Matrix that MueLu can work with
100 // MueLu needs a non-const object as input
101 RCP<XpMat> A = XpThyUtils::toXpetra(Teuchos::rcp_const_cast<ThyLinOpBase>(fwdOp));
102 TEUCHOS_TEST_FOR_EXCEPT(Teuchos::is_null(A));
103
104 // Retrieve concrete preconditioner object
105 const Teuchos::Ptr<DefaultPreconditioner<Scalar>> defaultPrec = Teuchos::ptr(dynamic_cast<DefaultPreconditioner<Scalar>*>(prec));
106 TEUCHOS_TEST_FOR_EXCEPT(Teuchos::is_null(defaultPrec));
107
108 // extract preconditioner operator
109 RCP<ThyLinOpBase> thyra_precOp = Teuchos::null;
110 thyra_precOp = rcp_dynamic_cast<Thyra::LinearOpBase<Scalar>>(defaultPrec->getNonconstUnspecifiedPrecOp(), true);
111
112 // make a decision whether to (re)build the multigrid preconditioner or reuse the old one
113 // rebuild preconditioner if startingOver == true
114 // reuse preconditioner if startingOver == false
115 const bool startingOver = (thyra_precOp.is_null() || !paramList.isParameter("Maxwell1: enable reuse") || !paramList.get<bool>("Maxwell1: enable reuse"));
116 const bool useHalfPrecision = paramList.get<bool>("half precision", false) && bIsTpetra;
117
118 RCP<XpOp> xpPrecOp;
119 if (startingOver == true) {
120 // Convert to Xpetra
121 std::list<std::string> convertXpetra = {"Coordinates", "Nullspace", "Kn", "D0", "CurlCurl"};
122 for (auto it = convertXpetra.begin(); it != convertXpetra.end(); ++it)
123 Converters<Scalar, LocalOrdinal, GlobalOrdinal, Node>::replaceWithXpetra(paramList, *it);
124 for (auto it = convertXpetra.begin(); it != convertXpetra.end(); ++it)
125 Converters<Scalar, LocalOrdinal, GlobalOrdinal, Node>::replaceWithXpetra(paramList.sublist("user data"), *it);
126
127 std::list<std::string> sublists = {"maxwell1: 11list", "maxwell1: 22list"};
128 for (auto itSublist = sublists.begin(); itSublist != sublists.end(); ++itSublist)
129 if (paramList.isSublist(*itSublist)) {
130 ParameterList& sublist = paramList.sublist(*itSublist);
131 if (sublist.isSublist("user data")) {
132 auto& userData = sublist.sublist("user data");
133 std::list<std::string> convertKeys;
134 for (auto it = userData.begin(); it != userData.end(); ++it)
135 convertKeys.push_back(userData.name(it));
136 for (auto it = convertKeys.begin(); it != convertKeys.end(); ++it)
137 Converters<Scalar, LocalOrdinal, GlobalOrdinal, Node>::replaceWithXpetra(userData, *it);
138 }
139 for (int lvlNo = 0; lvlNo < 10; ++lvlNo) {
140 if (sublist.isSublist("level " + std::to_string(lvlNo) + " user data")) {
141 ParameterList& lvlList = sublist.sublist("level " + std::to_string(lvlNo) + " user data");
142 std::list<std::string> convertKeys;
143 for (auto it = lvlList.begin(); it != lvlList.end(); ++it)
144 convertKeys.push_back(lvlList.name(it));
145 for (auto it = convertKeys.begin(); it != convertKeys.end(); ++it)
146 Converters<Scalar, LocalOrdinal, GlobalOrdinal, Node>::replaceWithXpetra(lvlList, *it);
147 }
148 }
149 }
150
151 ParameterList& sublist = paramList.sublist("maxwell1: 11list");
152 if (sublist.isParameter("D0")) {
153 Converters<Scalar, LocalOrdinal, GlobalOrdinal, Node>::replaceWithXpetra(sublist, "D0");
154 }
155
156 paramList.set<bool>("Maxwell1: use as preconditioner", true);
157 if (useHalfPrecision) {
158#if defined(MUELU_CAN_USE_MIXED_PRECISION)
159
160 // convert to half precision
161 RCP<XphMat> halfA = Xpetra::convertToHalfPrecision(A);
162 if (paramList.isType<RCP<XpmMV>>("Coordinates")) {
163 RCP<XpmMV> coords = paramList.get<RCP<XpmMV>>("Coordinates");
164 paramList.remove("Coordinates");
165 RCP<XphmMV> halfCoords = Xpetra::convertToHalfPrecision(coords);
166 paramList.set("Coordinates", halfCoords);
167 }
168 if (paramList.isType<RCP<XpMV>>("Nullspace")) {
169 RCP<XpMV> nullspace = paramList.get<RCP<XpMV>>("Nullspace");
170 paramList.remove("Nullspace");
171 RCP<XphMV> halfNullspace = Xpetra::convertToHalfPrecision(nullspace);
172 paramList.set("Nullspace", halfNullspace);
173 }
174 std::list<std::string> convertMat = {"Kn", "D0"};
175 for (auto it = convertMat.begin(); it != convertMat.end(); ++it) {
176 if (paramList.isType<RCP<XpMat>>(*it)) {
177 RCP<XpMat> M = paramList.get<RCP<XpMat>>(*it);
178 paramList.remove(*it);
179 RCP<XphMat> halfM = Xpetra::convertToHalfPrecision(M);
180 paramList.set(*it, halfM);
181 }
182 }
183
184 // build a new half-precision MueLu Maxwell1 preconditioner
185 RCP<MueLu::Maxwell1<HalfScalar, LocalOrdinal, GlobalOrdinal, Node>> halfPrec = rcp(new MueLu::Maxwell1<HalfScalar, LocalOrdinal, GlobalOrdinal, Node>(halfA, paramList, true));
186 xpPrecOp = rcp(new XpHalfPrecOp(halfPrec));
187#else
188 TEUCHOS_TEST_FOR_EXCEPT(true);
189#endif
190 } else {
191 // build a new MueLu Maxwell1 preconditioner
192 RCP<MueLu::Maxwell1<Scalar, LocalOrdinal, GlobalOrdinal, Node>> preconditioner = rcp(new MueLu::Maxwell1<Scalar, LocalOrdinal, GlobalOrdinal, Node>(A, paramList, true));
193 xpPrecOp = rcp_dynamic_cast<XpOp>(preconditioner);
194 }
195 } else {
196 // reuse old MueLu preconditioner stored in MueLu Xpetra operator and put in new matrix
197
198 RCP<ThyXpOp> thyXpOp = rcp_dynamic_cast<ThyXpOp>(thyra_precOp, true);
199 RCP<XpOp> xpOp = thyXpOp->getXpetraOperator();
200#if defined(MUELU_CAN_USE_MIXED_PRECISION)
201 RCP<XpHalfPrecOp> xpHalfPrecOp = rcp_dynamic_cast<XpHalfPrecOp>(xpOp);
202 if (!xpHalfPrecOp.is_null()) {
203 RCP<MueLu::Maxwell1<HalfScalar, LocalOrdinal, GlobalOrdinal, Node>> preconditioner = rcp_dynamic_cast<MueLu::Maxwell1<HalfScalar, LocalOrdinal, GlobalOrdinal, Node>>(xpHalfPrecOp->GetHalfPrecisionOperator(), true);
204 RCP<XphMat> halfA = Xpetra::convertToHalfPrecision(A);
205 preconditioner->resetMatrix(halfA);
206 xpPrecOp = rcp_dynamic_cast<XpOp>(preconditioner);
207 } else
208#endif
209 {
210 RCP<MueLu::Maxwell1<Scalar, LocalOrdinal, GlobalOrdinal, Node>> preconditioner = rcp_dynamic_cast<MueLu::Maxwell1<Scalar, LocalOrdinal, GlobalOrdinal, Node>>(xpOp, true);
211 preconditioner->resetMatrix(A);
212 xpPrecOp = rcp_dynamic_cast<XpOp>(preconditioner);
213 }
214 }
215
216 // wrap preconditioner in thyraPrecOp
217 RCP<const VectorSpaceBase<Scalar>> thyraRangeSpace = Xpetra::ThyraUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::toThyra(xpPrecOp->getRangeMap());
218 RCP<const VectorSpaceBase<Scalar>> thyraDomainSpace = Xpetra::ThyraUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::toThyra(xpPrecOp->getDomainMap());
219
220 RCP<ThyLinOpBase> thyraPrecOp = Thyra::xpetraLinearOp<Scalar, LocalOrdinal, GlobalOrdinal, Node>(thyraRangeSpace, thyraDomainSpace, xpPrecOp);
221 TEUCHOS_TEST_FOR_EXCEPT(Teuchos::is_null(thyraPrecOp));
222
223 defaultPrec->initializeUnspecified(thyraPrecOp);
224}
225
226template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
227void MueLuMaxwell1PreconditionerFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
228 uninitializePrec(PreconditionerBase<Scalar>* prec, RCP<const LinearOpSourceBase<Scalar>>* fwdOp, ESupportSolveUse* supportSolveUse) const {
229 TEUCHOS_ASSERT(prec);
230
231 // Retrieve concrete preconditioner object
232 const Teuchos::Ptr<DefaultPreconditioner<Scalar>> defaultPrec = Teuchos::ptr(dynamic_cast<DefaultPreconditioner<Scalar>*>(prec));
233 TEUCHOS_TEST_FOR_EXCEPT(Teuchos::is_null(defaultPrec));
234
235 if (fwdOp) {
236 // TODO: Implement properly instead of returning default value
237 *fwdOp = Teuchos::null;
238 }
239
240 if (supportSolveUse) {
241 // TODO: Implement properly instead of returning default value
242 *supportSolveUse = Thyra::SUPPORT_SOLVE_UNSPECIFIED;
243 }
244
245 defaultPrec->uninitialize();
246}
247
248// Overridden from ParameterListAcceptor
249template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
250void MueLuMaxwell1PreconditionerFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::setParameterList(RCP<ParameterList> const& paramList) {
251 TEUCHOS_TEST_FOR_EXCEPT(Teuchos::is_null(paramList));
252 paramList_ = paramList;
253}
254
255template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
256RCP<ParameterList> MueLuMaxwell1PreconditionerFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::getNonconstParameterList() {
257 return paramList_;
258}
259
260template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
261RCP<ParameterList> MueLuMaxwell1PreconditionerFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::unsetParameterList() {
262 RCP<ParameterList> savedParamList = paramList_;
263 paramList_ = Teuchos::null;
264 return savedParamList;
265}
266
267template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
268RCP<const ParameterList> MueLuMaxwell1PreconditionerFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::getParameterList() const {
269 return paramList_;
270}
271
272template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
273RCP<const ParameterList> MueLuMaxwell1PreconditionerFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::getValidParameters() const {
274 static RCP<const ParameterList> validPL;
275
276 if (Teuchos::is_null(validPL))
277 validPL = rcp(new ParameterList());
278
279 return validPL;
280}
281
282// Public functions overridden from Teuchos::Describable
283template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
284std::string MueLuMaxwell1PreconditionerFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::description() const {
285 return "Thyra::MueLuMaxwell1PreconditionerFactory";
286}
287} // namespace Thyra
288
289#endif // HAVE_MUELU_STRATIMIKOS
290
291#endif // ifdef THYRA_MUELU_MAXWELL1_PRECONDITIONER_FACTORY_DEF_HPP
Preconditioner (wrapped as a Xpetra::Operator) for Maxwell's equations in curl-curl form.
Concrete Thyra::LinearOpBase subclass for Xpetra::Operator.