MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_Maxwell1_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 MUELU_MAXWELL1_DEF_HPP
11#define MUELU_MAXWELL1_DEF_HPP
12
13#include <sstream>
15
16#include "MueLu_ConfigDefs.hpp"
17
18#include "Xpetra_CrsMatrixWrap_decl.hpp"
19#include "Xpetra_Map.hpp"
21#include "Xpetra_MatrixUtils.hpp"
22
24#include "MueLu_Maxwell_Utils.hpp"
25
26#include "MueLu_ReitzingerPFactory.hpp"
27#include "MueLu_Utilities.hpp"
28#include "MueLu_Level.hpp"
29#include "MueLu_Hierarchy.hpp"
30#include "MueLu_RAPFactory.hpp"
31#include "MueLu_RebalanceAcFactory.hpp"
32#include "MueLu_RebalanceTransferFactory.hpp"
33#include "MueLu_Monitor.hpp"
34#include "MueLu_PerfUtils.hpp"
35#include "MueLu_ParameterListInterpreter.hpp"
36#include "MueLu_HierarchyManager.hpp"
37#include <MueLu_HierarchyUtils.hpp>
41#include <MueLu_RefMaxwellSmoother.hpp>
42#include "MueLu_Behavior.hpp"
43
44#ifdef HAVE_MUELU_CUDA
45#include "cuda_profiler_api.h"
46#endif
47
48namespace MueLu {
49
50template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
51const Teuchos::RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> Maxwell1<Scalar, LocalOrdinal, GlobalOrdinal, Node>::getDomainMap() const {
52 return SM_Matrix_->getDomainMap();
53}
54
55template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
56const Teuchos::RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> Maxwell1<Scalar, LocalOrdinal, GlobalOrdinal, Node>::getRangeMap() const {
57 return SM_Matrix_->getRangeMap();
58}
59
60template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
62 if (list.isType<std::string>("parameterlist: syntax") && list.get<std::string>("parameterlist: syntax") == "ml") {
63 list.remove("parameterlist: syntax");
64 Teuchos::ParameterList newList;
65
66 // interpret ML list
67 newList.sublist("maxwell1: 22list") = *Teuchos::getParametersFromXmlString(MueLu::ML2MueLuParameterTranslator::translate(list, "Maxwell"));
68
69 // Hardwiring options to ensure ML compatibility
70 newList.sublist("maxwell1: 22list").set("use kokkos refactor", false);
71
72 newList.sublist("maxwell1: 11list").set("use kokkos refactor", false);
73 newList.sublist("maxwell1: 11list").set("tentative: constant column sums", false);
74 newList.sublist("maxwell1: 11list").set("tentative: calculate qr", false);
75
76 newList.sublist("maxwell1: 11list").set("aggregation: use ml scaling of drop tol", true);
77 newList.sublist("maxwell1: 22list").set("aggregation: use ml scaling of drop tol", true);
78
79 newList.sublist("maxwell1: 22list").set("aggregation: min agg size", 3);
80 newList.sublist("maxwell1: 22list").set("aggregation: match ML phase1", true);
81 newList.sublist("maxwell1: 22list").set("aggregation: match ML phase2a", true);
82 newList.sublist("maxwell1: 22list").set("aggregation: match ML phase2b", true);
83
84 if (!list.sublist("maxwell1: 11list").isParameter("multigrid algorithm") || (list.sublist("maxwell1: 11list").get<std::string>("multigrid algorithm") != "emin reitzinger")) {
85 if (list.isParameter("aggregation: damping factor") && list.get<double>("aggregation: damping factor") == 0.0)
86 newList.sublist("maxwell1: 11list").set("multigrid algorithm", "unsmoothed reitzinger");
87 else
88 newList.sublist("maxwell1: 11list").set("multigrid algorithm", "smoothed reitzinger");
89 }
90 newList.sublist("maxwell1: 11list").set("aggregation: type", "uncoupled");
91
92 newList.sublist("maxwell1: 22list").set("multigrid algorithm", "unsmoothed");
93 newList.sublist("maxwell1: 22list").set("aggregation: type", "uncoupled");
94
95 if (newList.sublist("maxwell1: 22list").isType<std::string>("verbosity"))
96 newList.set("verbosity", newList.sublist("maxwell1: 22list").get<std::string>("verbosity"));
97
98 // Move coarse solver and smoother stuff to 11list
99 std::vector<std::string> convert = {"coarse:", "smoother:", "smoother: pre", "smoother: post"};
100 for (auto it = convert.begin(); it != convert.end(); ++it) {
101 if (newList.sublist("maxwell1: 22list").isType<std::string>(*it + " type")) {
102 newList.sublist("maxwell1: 11list").set(*it + " type", newList.sublist("maxwell1: 22list").get<std::string>(*it + " type"));
103 newList.sublist("maxwell1: 22list").remove(*it + " type");
104 }
105 if (newList.sublist("maxwell1: 22list").isSublist(*it + " params")) {
106 newList.sublist("maxwell1: 11list").set(*it + " params", newList.sublist("maxwell1: 22list").sublist(*it + " params"));
107 newList.sublist("maxwell1: 22list").remove(*it + " params");
108 }
109 }
110
111 newList.sublist("maxwell1: 22list").set("smoother: type", "none");
112 newList.sublist("maxwell1: 22list").set("coarse: type", "none");
113
114 newList.set("maxwell1: nodal smoother fix zero diagonal threshold", 1e-10);
115 newList.sublist("maxwell1: 22list").set("rap: fix zero diagonals", true);
116 newList.sublist("maxwell1: 22list").set("rap: fix zero diagonals threshold", 1e-10);
117
118 list = newList;
119 }
120
121 std::string mode_string = list.get("maxwell1: mode", MasterList::getDefault<std::string>("maxwell1: mode"));
122 applyBCsTo22_ = list.get("maxwell1: apply BCs to 22", false);
123 dump_matrices_ = list.get("maxwell1: dump matrices", MasterList::getDefault<bool>("maxwell1: dump matrices"));
124 check_D0_scaling_ = list.get("maxwell1: check and fix D0 scaling", MasterList::getDefault<bool>("maxwell1: check and fix D0 scaling"));
125
126 // Default smoother. We'll copy this around.
127 Teuchos::ParameterList defaultSmootherList;
128 defaultSmootherList.set("smoother: type", "CHEBYSHEV");
129 defaultSmootherList.sublist("smoother: params").set("chebyshev: degree", 2);
130 defaultSmootherList.sublist("smoother: params").set("chebyshev: ratio eigenvalue", 7.0);
131 defaultSmootherList.sublist("smoother: params").set("chebyshev: eigenvalue max iterations", 30);
132
133 // Make sure verbosity gets passed to the sublists
134 std::string verbosity = list.get("verbosity", "high");
136
137 // Check the validity of the run mode
138 if (mode_ != MODE_GMHD_STANDARD) {
139 if (mode_string == "standard")
140 mode_ = MODE_STANDARD;
141 else if (mode_string == "refmaxwell")
142 mode_ = MODE_REFMAXWELL;
143 else if (mode_string == "edge only")
144 mode_ = MODE_EDGE_ONLY;
145 else {
146 TEUCHOS_TEST_FOR_EXCEPTION(true, Exceptions::RuntimeError, "Must use mode 'standard', 'refmaxwell', 'edge only', or use the GMHD constructor.");
147 }
148 }
149
150 // If we're in edge only or standard modes, then the (2,2) hierarchy gets built without smoothers.
151 // Otherwise we use the user's smoothers (defaulting to Chebyshev if unspecified)
152 if (list.isSublist("maxwell1: 22list"))
153 precList22_ = list.sublist("maxwell1: 22list");
154 else if (list.isSublist("refmaxwell: 22list"))
155 precList22_ = list.sublist("refmaxwell: 22list");
156 if (mode_ == MODE_EDGE_ONLY || mode_ == MODE_STANDARD || mode_ == MODE_GMHD_STANDARD)
157 precList22_.set("smoother: pre or post", "none");
158 else if (!precList22_.isType<std::string>("Preconditioner Type") &&
159 !precList22_.isType<std::string>("smoother: type") &&
160 !precList22_.isType<std::string>("smoother: pre type") &&
161 !precList22_.isType<std::string>("smoother: post type")) {
162 precList22_ = defaultSmootherList;
163 }
164 precList22_.set("verbosity", precList22_.get("verbosity", verbosity));
165
166 // For the (1,1) hierarchy we'll use Hiptmair (STANDARD) or Chebyshev (EDGE_ONLY / REFMAXWELL) if
167 // the user doesn't specify things
168 if (list.isSublist("maxwell1: 11list"))
169 precList11_ = list.sublist("maxwell1: 11list");
170 else if (list.isSublist("refmaxwell: 11list"))
171 precList11_ = list.sublist("refmaxwell: 11list");
172
173 if (mode_ == MODE_GMHD_STANDARD) {
174 precList11_.set("smoother: pre or post", "none");
175 precList11_.set("smoother: type", "none");
176 }
177 if (!precList11_.isType<std::string>("Preconditioner Type") &&
178 !precList11_.isType<std::string>("smoother: type") &&
179 !precList11_.isType<std::string>("smoother: pre type") &&
180 !precList11_.isType<std::string>("smoother: post type")) {
181 if (mode_ == MODE_EDGE_ONLY || mode_ == MODE_REFMAXWELL) {
182 precList11_ = defaultSmootherList;
183 }
184 if (mode_ == MODE_STANDARD) {
185 precList11_.set("smoother: type", "HIPTMAIR");
186 precList11_.set("hiptmair: smoother type 1", "CHEBYSHEV");
187 precList11_.set("hiptmair: smoother type 2", "CHEBYSHEV");
188 precList11_.sublist("hiptmair: smoother list 1") = defaultSmootherList;
189 precList11_.sublist("hiptmair: smoother list 2") = defaultSmootherList;
190 }
191 }
192 precList11_.set("verbosity", precList11_.get("verbosity", verbosity));
193
194 // Reuse support
195 if (enable_reuse_ &&
196 !precList11_.isType<std::string>("Preconditioner Type") &&
197 !precList11_.isParameter("reuse: type"))
198 precList11_.set("reuse: type", "full");
199 if (enable_reuse_ &&
200 !precList22_.isType<std::string>("Preconditioner Type") &&
201 !precList22_.isParameter("reuse: type"))
202 precList22_.set("reuse: type", "full");
203
204 // Are we using Kokkos?
205 useKokkos_ = !Node::is_serial;
206 useKokkos_ = list.get("use kokkos refactor", useKokkos_);
207
208 parameterList_ = list;
209}
210
211template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
213 Teuchos::ParameterList precListGmhd;
214
215 MueLu::HierarchyUtils<SC, LO, GO, NO>::CopyBetweenHierarchies(*Hierarchy11_, *HierarchyGmhd_, "P", "Psubblock", "RCP<Matrix>");
216
217 HierarchyGmhd_->GetLevel(0)->Set("A", GmhdA_Matrix_);
218 GmhdA_Matrix_->setObjectLabel("GmhdA");
219
220 TEUCHOS_TEST_FOR_EXCEPTION(!List.isSublist("maxwell1: Gmhdlist"), Exceptions::RuntimeError, "Must provide maxwell1: Gmhdlist for GMHD setup");
221 precListGmhd = List.sublist("maxwell1: Gmhdlist");
222 precListGmhd.set("coarse: max size", 1);
223 precListGmhd.set("max levels", HierarchyGmhd_->GetNumLevels());
224 RCP<MueLu::HierarchyManager<SC, LO, GO, NO>> mueLuFactory = rcp(new MueLu::ParameterListInterpreter<SC, LO, GO, NO>(precListGmhd, GmhdA_Matrix_->getDomainMap()->getComm()));
225 HierarchyGmhd_->setlib(GmhdA_Matrix_->getDomainMap()->lib());
226 HierarchyGmhd_->SetProcRankVerbose(GmhdA_Matrix_->getDomainMap()->getComm()->getRank());
227 mueLuFactory->SetupHierarchy(*HierarchyGmhd_);
228}
229
230template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
232 /* Algorithm overview for Maxwell1 construction:
233
234 1) Create a nodal auxiliary hierarchy based on (a) the user's nodal matrix or (b) a matrix constructed
235 by D0^T A D0 if the user doesn't provide a nodal matrix. We call this matrix "NodeAggMatrix."
236
237 2) If the user provided a node matrix, we use the prolongators from the auxiliary nodal hierarchy
238 to generate matrices for smoothers on all levels. We call this "NodeMatrix." Otherwise NodeMatrix = NodeAggMatrix
239
240 3) We stick all of the nodal P matrices and NodeMatrix objects on the final (1,1) hierarchy and then use the
241 ReitzingerPFactory to generate the edge P and A matrices.
242 */
243
244#ifdef HAVE_MUELU_CUDA
245 if (parameterList_.get<bool>("maxwell1: cuda profile setup", false)) cudaProfilerStart();
246#endif
247
248 std::string timerLabel;
249 if (reuse)
250 timerLabel = "MueLu Maxwell1: compute (reuse)";
251 else
252 timerLabel = "MueLu Maxwell1: compute";
253 RCP<Teuchos::TimeMonitor> tmCompute = getTimer(timerLabel);
254
256 // Generate Kn and apply BCs (if needed)
257 bool have_generated_Kn = false;
258 if (Kn_Matrix_.is_null()) {
259 // generate_kn() will do diagonal repair if requested
260 GetOStream(Runtime0) << "Maxwell1::compute(): Kn not provided. Generating." << std::endl;
261 Kn_Matrix_ = generate_kn();
262 have_generated_Kn = true;
263 } else if (parameterList_.get<bool>("rap: fix zero diagonals", true)) {
264 magnitudeType threshold;
265 if (parameterList_.isType<magnitudeType>("rap: fix zero diagonals threshold"))
266 threshold = parameterList_.get<magnitudeType>("rap: fix zero diagonals threshold",
267 Teuchos::ScalarTraits<double>::eps());
268 else
269 threshold = Teuchos::as<magnitudeType>(parameterList_.get<double>("rap: fix zero diagonals threshold",
270 Teuchos::ScalarTraits<double>::eps()));
271 Scalar replacement = Teuchos::as<Scalar>(parameterList_.get<double>("rap: fix zero diagonals replacement",
272 MasterList::getDefault<double>("rap: fix zero diagonals replacement")));
273 Xpetra::MatrixUtils<SC, LO, GO, NO>::CheckRepairMainDiagonal(Kn_Matrix_, true, GetOStream(Warnings1), threshold, replacement);
274 }
275
277 // Generate the (2,2) Hierarchy
278 Kn_Matrix_->setObjectLabel("Maxwell1 (2,2)");
279
280 /* Critical ParameterList changes */
281 if (!Coords_.is_null())
282 precList22_.sublist("user data").set("Coordinates", Coords_);
283 if (!Material_.is_null())
284 precList22_.sublist("user data").set("Material", Material_);
285
286 if (mode_ == MODE_STANDARD) {
287 /* We do not need to set up any smoothers or coarse solver for the (2,2) hierarchy. */
288 if (!precList22_.isParameter("smoother: type") && !precList22_.isParameter("smoother: pre type") && !precList22_.isParameter("smoother: post type")) {
289 precList22_.set("smoother: type", "none");
290 }
291 if (!precList22_.isParameter("coarse: type")) {
292 precList22_.set("coarse: type", "none");
293 }
294 }
295
296 auto algo11 = precList11_.get<std::string>("multigrid algorithm");
297 auto algo22 = precList22_.get<std::string>("multigrid algorithm");
298
299 // | algo22 | algo11 | allowed | comment |
300 // |------------|-----------------------|---------|-------------------------------------------|
301 // | unsmoothed | unsmoothed reitzinger | yes | classical unsmoothed RS |
302 // | unsmoothed | smoothed reitzinger | yes | smoothed RS with edge-only smoothing |
303 // | unsmoothed | emin reitzinger | yes | emin RS with unsmoothed nodal prolongator |
304 // | sa | unsmoothed reitzinger | no | |
305 // | sa | smoothed reitzinger | yes | smoothed RS with edge and nodal smoothing |
306 // | sa | emin reitzinger | yes | emin RS with SA nodal prolongator |
307 // | emin | unsmoothed reitzinger | no | |
308 // | emin | smoothed reitzinger | no | |
309 // | emin | emin reitzinger | yes | emin RS with emin nodal prolongator |
310
311 if (algo11 == "unsmoothed reitzinger") {
312 TEUCHOS_ASSERT(algo22 == "unsmoothed");
313 } else if (algo11 == "smoothed reitzinger") {
314 TEUCHOS_ASSERT((algo22 == "unsmoothed") || (algo22 == "sa"));
315 }
316
317 if (algo22 == "sa") {
318 // Nodal prolongators are smoothed. Make the damping factor available in the edge list.
319 double nodalDamping;
320 if (precList22_.isType<double>("sa: damping factor")) {
321 nodalDamping = precList22_.get<double>("sa: damping factor");
322 } else {
323 nodalDamping = MasterList::getDefault<double>("sa: damping factor");
324 }
325 precList11_.set("sa: nodal damping factor", nodalDamping);
326 } else if (algo22 == "unsmoothed")
327 precList11_.set("sa: nodal damping factor", 0.0);
328
329 if (algo11 == "smoothed reitzinger") {
330 precList11_.set("sa: use filtered matrix", false);
331 precList22_.set("sa: use filtered matrix", false);
332 if (!precList11_.sublist("user data").isParameter("CurlCurl")) {
333 if (precList11_.isType<double>("sa: damping factor")) {
334 double edgeDamping = precList11_.get<double>("sa: damping factor");
335 if (edgeDamping != 0) {
336 GetOStream(Warnings0) << "\"sa: damping factor\" in \"maxwell1: 11list\" is set to " << std::to_string(edgeDamping) << ", but no CurlCurl matrix has been passed in \"user data\". Switching off edge-only damping." << std::endl;
337 precList11_.set("sa: damping factor", 0.);
338 }
339 } else {
340 GetOStream(Warnings0) << "\"sa: damping factor\" in \"maxwell1: 11list\" is nonzero by default, but no CurlCurl matrix has been passed in \"user data\". Switching off edge-only damping." << std::endl;
341 precList11_.set("sa: damping factor", 0.);
342 }
343 }
344 }
345
346 if (!precList22_.isParameter("tentative: constant column sums"))
347 precList22_.set("tentative: constant column sums", false);
348 else if (precList22_.get<bool>("tentative: constant column sums") != false)
349 GetOStream(Warnings0) << "\"tentative: constant column sums\" is set to \"true\". There is no guarantee that this will work." << std::endl;
350
351 if (!precList22_.isParameter("tentative: calculate qr"))
352 precList22_.set("tentative: calculate qr", false);
353 else if (precList22_.get<bool>("tentative: calculate qr") != false)
354 GetOStream(Warnings0) << "\"tentative: calculate qr\" is set to \"true\". There is no guarantee that this will work." << std::endl;
355
356 /* We need both nodal Ptent and P for Emin. */
357 if (((parameterList_.sublist("maxwell1: 11list").get<std::string>("multigrid algorithm") == "smoothed reitzinger") || (parameterList_.sublist("maxwell1: 11list").get<std::string>("multigrid algorithm") == "emin reitzinger")) &&
358 (!precList22_.isParameter("sa: keep tentative prolongator") || !precList22_.get<bool>("sa: keep tentative prolongator")))
359 precList22_.set("sa: keep tentative prolongator", true);
360
361 /* Repartitioning *must* be in sync between hierarchies, which means
362 that we need to keep the importers in sync too */
363 if (precList22_.isParameter("repartition: enable")) {
364 bool repartition = precList22_.get<bool>("repartition: enable");
365 precList11_.set("repartition: enable", repartition);
366
367 // If we're repartitioning (2,2), we need to rebalance for (1,1) to do the right thing,
368 // as well as keep the importers
369 if (repartition) {
370 if (!precList22_.isParameter("repartition: save importer"))
371 precList22_.set("repartition: save importer", true);
372 else if (!precList22_.get<bool>("repartition: save importer")) {
373 GetOStream(Warnings0) << "\"repartition: save importer\" is set to false, but it is required to be true. Changing it." << std::endl;
374 precList22_.set("repartition: save importer", true);
375 }
376
377 if (!precList22_.isParameter("repartition: rebalance P and R"))
378 precList22_.set("repartition: rebalance P and R", true);
379 else if (!precList22_.get<bool>("repartition: rebalance P and R")) {
380 GetOStream(Warnings0) << "\"repartition: rebalance P and R\" is set to false, but it is required to be true. Changing it." << std::endl;
381 precList22_.set("repartition: rebalance P and R", true);
382 }
383
384 if (!precList22_.isParameter("repartition: use subcommunicators"))
385 precList22_.set("repartition: use subcommunicators", true);
386 else if (!precList22_.get<bool>("repartition: use subcommunicators")) {
387 GetOStream(Warnings0) << "\"repartition: use subcommunicators\" is set to false, but it is required to be true. Changing it." << std::endl;
388 precList22_.set("repartition: use subcommunicators", true);
389 }
390 }
391
392 if (precList22_.isParameter("repartition: use subcommunicators")) {
393 precList11_.set("repartition: use subcommunicators", precList22_.get<bool>("repartition: use subcommunicators"));
394
395 // We do not want (1,1) and (2,2) blocks being repartitioned seperately, so we specify the map that
396 // is going to be used (this is generated in ReitzingerPFactory)
397 if (precList11_.get<bool>("repartition: use subcommunicators") == true)
398 precList11_.set("repartition: use subcommunicators in place", true);
399 } else {
400 // We'll have Maxwell1 default to using subcommunicators if you don't specify
401 precList11_.set("repartition: use subcommunicators", true);
402 precList22_.set("repartition: use subcommunicators", true);
403
404 // We do not want (1,1) and (2,2) blocks being repartitioned seperately, so we specify the map that
405 // is going to be used (this is generated in ReitzingerPFactory)
406 precList11_.set("repartition: use subcommunicators in place", true);
407 }
408
409 } else
410 precList11_.remove("repartition: enable", false);
411
413 // Remove explicit zeros from matrices
414 /*
415 Maxwell_Utils<SC,LO,GO,NO>::removeExplicitZeros(parameterList_,D0_Matrix_,SM_Matrix_);
416 */
417
418 if (IsPrint(Statistics2)) {
419 RCP<ParameterList> params = rcp(new ParameterList());
420 ;
421 params->set("printLoadBalancingInfo", true);
422 params->set("printCommInfo", true);
423 GetOStream(Statistics2) << PerfUtils::PrintMatrixInfo(*SM_Matrix_, "SM_Matrix", params);
424 }
425
427 // Detect Dirichlet boundary conditions
428 if (!reuse) {
429 magnitudeType rowSumTol = precList11_.get("aggregation: row sum drop tol", -1.0);
430 Maxwell_Utils<SC, LO, GO, NO>::detectBoundaryConditionsSM(SM_Matrix_, D0_Matrix_, rowSumTol,
431 useKokkos_, BCrowsKokkos_, BCcolsKokkos_, BCdomainKokkos_,
432 BCedges_, BCnodes_, BCrows_, BCcols_, BCdomain_,
433 allEdgesBoundary_, allNodesBoundary_);
434 if (IsPrint(Statistics2)) {
435 GetOStream(Statistics2) << "MueLu::Maxwell1::compute(): Detected " << BCedges_ << " BC rows and " << BCnodes_ << " BC columns." << std::endl;
436 }
437 }
438
439 if (allEdgesBoundary_) {
440 // All edges have been detected as boundary edges.
441 // Do not attempt to construct sub-hierarchies, but just set up a single level preconditioner.
442 GetOStream(Warnings0) << "All edges are detected as boundary edges!" << std::endl;
443 mode_ = MODE_EDGE_ONLY;
444
445 // Generate single level hierarchy for the edge
446 precList22_.set("max levels", 1);
447 }
448
449 if (allNodesBoundary_) {
450 // All Nodes have been detected as boundary nodes.
451 // Do not attempt to construct sub-hierarchies, but just set up a single level preconditioner.
452 GetOStream(Warnings0) << "All nodes are detected as boundary nodes!" << std::endl;
453 mode_ = MODE_EDGE_ONLY;
454
455 // Generate single level hierarchy for the edge
456 precList22_.set("max levels", 1);
457 }
458
460 // Build (2,2) hierarchy
461 Hierarchy22_ = MueLu::CreateXpetraPreconditioner(Kn_Matrix_, precList22_);
462
464 // Apply boundary conditions to D0 (if needed)
465 if (!reuse) {
466 D0_Matrix_->resumeFill();
467 Scalar replaceWith = Teuchos::ScalarTraits<SC>::zero();
468
469 if (applyBCsTo22_) {
470 GetOStream(Runtime0) << "Maxwell1::compute(): nuking BC rows/cols of D0" << std::endl;
471 if (useKokkos_) {
472 Utilities::ZeroDirichletCols(D0_Matrix_, BCcolsKokkos_, replaceWith);
473 } else {
474 Utilities::ZeroDirichletCols(D0_Matrix_, BCcols_, replaceWith);
475 }
476 } else {
477 GetOStream(Runtime0) << "Maxwell1::compute(): nuking BC rows of D0" << std::endl;
478 if (useKokkos_) {
479 Utilities::ZeroDirichletRows(D0_Matrix_, BCrowsKokkos_, replaceWith);
480 } else {
481 Utilities::ZeroDirichletRows(D0_Matrix_, BCrows_, replaceWith);
482 }
483 }
484
485 D0_Matrix_->fillComplete(D0_Matrix_->getDomainMap(), D0_Matrix_->getRangeMap());
486 }
487
489 // What ML does is generate nodal prolongators with an auxiliary hierarchy based on the
490 // user's (2,2) matrix. The actual nodal matrices for smoothing are generated by the
491 // Hiptmair smoother construction. We're not going to do that --- we'll
492 // do as we insert them into the final (1,1) hierarchy.
493
494 // Level 0
495 RCP<Matrix> Kn_Smoother_0;
496 if (have_generated_Kn) {
497 Kn_Smoother_0 = Kn_Matrix_;
498 } else {
499 Kn_Smoother_0 = generate_kn();
500 }
501
503 // Copy the relevant (2,2) data to the (1,1) hierarchy
504 Hierarchy11_ = rcp(new Hierarchy("Maxwell1 (1,1)"));
505 RCP<Matrix> OldSmootherMatrix;
506 RCP<Level> OldEdgeLevel;
507 for (int i = 0; i < Hierarchy22_->GetNumLevels(); i++) {
508 Hierarchy11_->AddNewLevel();
509 RCP<Level> NodeL = Hierarchy22_->GetLevel(i);
510 RCP<Level> EdgeL = Hierarchy11_->GetLevel(i);
511 RCP<Operator> NodeAggOp = NodeL->Get<RCP<Operator>>("A");
512 RCP<Matrix> NodeAggMatrix = rcp_dynamic_cast<Matrix>(NodeAggOp);
513 std::string labelstr = FormattingHelper::getColonLabel(EdgeL->getObjectLabel());
514
515 if (i == 0) {
516 EdgeL->Set("A", SM_Matrix_);
517 EdgeL->Set("D0", D0_Matrix_);
518
519 EdgeL->Set("NodeAggMatrix", NodeAggMatrix);
520 EdgeL->Set("NodeMatrix", Kn_Smoother_0);
521 OldSmootherMatrix = Kn_Smoother_0;
522 OldEdgeLevel = EdgeL;
523 } else {
524 // Set the Nodal P
525 auto NodalP = NodeL->Get<RCP<Matrix>>("P");
526
527 // NOTE: ML uses normalized prolongators for the aggregation hierarchy
528 // and then prolongators of all 1's for doing the Reitzinger prolongator
529 // generation for the edge hierarchy.
530
531 // Get the importer if we have one (for repartitioning)
532 RCP<const Import> importer;
533 if (NodeL->IsAvailable("Importer")) {
534 importer = NodeL->Get<RCP<const Import>>("Importer");
535 EdgeL->Set("NodeImporter", importer);
536 }
537
538 // We used tenative P to generate the coarse discrete gradient.
539 RCP<Matrix> NodalPtent;
540 if (parameterList_.sublist("maxwell1: 22list").get<std::string>("multigrid algorithm") == "unsmoothed")
541 NodalPtent = NodeL->Get<RCP<Matrix>>("P");
542 else {
543 NodalPtent = NodeL->Get<RCP<Matrix>>("Ptent");
544
545 if (!importer.is_null()) {
546 // The nodal hierarchy rebalanced the coarse level.
547 // P got rebalanced as well, but not Ptent.
548 // Let's do that here.
549 Level fineLevel;
550 Level coarseLevel;
551 fineLevel.SetFactoryManager(null);
552 coarseLevel.SetFactoryManager(null);
553 fineLevel.SetLevelID(0);
554 coarseLevel.SetLevelID(1);
555 auto rebalTransfer = rcp(new RebalanceTransferFactory());
556 ParameterList rebalTransferParams;
557 rebalTransferParams.set("repartition: rebalance P and R", true);
558 rebalTransferParams.set("type", "Interpolation");
559 rebalTransfer->SetParameterList(rebalTransferParams);
560 coarseLevel.Set("P", NodalPtent);
561 coarseLevel.Set("Importer", importer);
562 rebalTransfer->Build(fineLevel, coarseLevel);
563 NodalPtent = coarseLevel.Get<RCP<Matrix>>("P");
564 }
565 }
566
567 auto P_for_RS_construction = Utilities::ReplaceNonZerosWithOnes(NodalPtent);
568 TEUCHOS_TEST_FOR_EXCEPTION(P_for_RS_construction.is_null(), Exceptions::RuntimeError, "Applying ones to prolongator failed");
569
570 // Pnodal is used by ReitzingerP to generate a coarse discrete gradient D0c and by the Hiptmair smoother
571 EdgeL->Set("Pnodal", P_for_RS_construction);
572
573 if (parameterList_.sublist("maxwell1: 11list").get<std::string>("multigrid algorithm") == "emin reitzinger") {
574 // PnodalEmin is used for the RHS of the sparse constraint in energy minimization:
575 // Pe*D0c = D0*PnodalEmin
576 EdgeL->Set("PnodalEmin", NodalP);
577 }
578
579 // If we repartition a processor away, a RCP<Operator> is stuck
580 // on the level instead of an RCP<Matrix>
581 if (!OldSmootherMatrix.is_null() && !P_for_RS_construction.is_null()) {
582 EdgeL->Set("NodeAggMatrix", NodeAggMatrix);
583 if (!have_generated_Kn) {
584 // The user gave us a Kn on the fine level, so we're using a seperate aggregation
585 // hierarchy from the smoothing hierarchy.
586
587 // ML does a *fixed* 1e-10 diagonal repair on the Nodal Smoothing Matrix
588 // This fix is applied *after* the next level is generated, but before the smoother is.
589 // We can see this behavior from ML, though it isn't 100% clear from the code *how* it happens.
590 // So, here we turn the fix off, then once we've generated the new matrix, we fix the old one.
591
592 // Generate the new matrix
593 Teuchos::ParameterList RAPlist;
594 RAPlist.set("rap: fix zero diagonals", false);
595 RCP<Matrix> NewKn = Maxwell_Utils<SC, LO, GO, NO>::PtAPWrapper(OldSmootherMatrix, P_for_RS_construction, RAPlist, labelstr);
596
597 // If there's an importer, we need to Rebalance the NewKn
598 if (!importer.is_null()) {
599 ParameterList rebAcParams;
600 rebAcParams.set("repartition: use subcommunicators", true);
601 rebAcParams.set("repartition: use subcommunicators in place", true);
602 auto newAfact = rcp(new RebalanceAcFactory());
603 newAfact->SetParameterList(rebAcParams);
604 RCP<const Map> InPlaceMap = NodeAggMatrix.is_null() ? Teuchos::null : NodeAggMatrix->getRowMap();
605
606 Level fLevel, cLevel;
607 cLevel.SetPreviousLevel(Teuchos::rcpFromRef(fLevel));
608 cLevel.Set("InPlaceMap", InPlaceMap);
609 cLevel.Set("A", NewKn);
610 cLevel.Request("A", newAfact.get());
611 newAfact->Build(fLevel, cLevel);
612
613 NewKn = cLevel.Get<RCP<Matrix>>("A", newAfact.get());
614 EdgeL->Set("NodeMatrix", NewKn);
615 } else {
616 EdgeL->Set("NodeMatrix", NewKn);
617 }
618
619 // Fix the old one
620 double thresh = parameterList_.get("maxwell1: nodal smoother fix zero diagonal threshold", 1e-10);
621 if (thresh > 0.0) {
622 GetOStream(Runtime0) << "Maxwell1::compute(): Fixing zero diagonal for nodal smoother matrix" << std::endl;
623 Scalar replacement = Teuchos::ScalarTraits<Scalar>::one();
624 Xpetra::MatrixUtils<SC, LO, GO, NO>::CheckRepairMainDiagonal(OldSmootherMatrix, true, GetOStream(Warnings1), thresh, replacement);
625 }
626 OldEdgeLevel->Set("NodeMatrix", OldSmootherMatrix);
627
628 OldSmootherMatrix = NewKn;
629 } else {
630 // The user didn't give us a Kn, so we aggregate and smooth with the same matrix
631 EdgeL->Set("NodeMatrix", NodeAggMatrix);
632 }
633 } else {
634 // We've partitioned things away.
635 EdgeL->Set("NodeMatrix", NodeAggOp);
636 EdgeL->Set("NodeAggMatrix", NodeAggOp);
637 }
638
639 OldEdgeLevel = EdgeL;
640 }
641
642 } // end Hierarchy22 loop
643
645 // Generating the (1,1) Hierarchy
646 {
647 SM_Matrix_->setObjectLabel("A(1,1)");
648 precList11_.set("coarse: max size", 1);
649 precList11_.set("max levels", Hierarchy22_->GetNumLevels());
650
651 const bool refmaxwellCoarseSolve = (precList11_.get<std::string>("coarse: type",
652 MasterList::getDefault<std::string>("coarse: type")) == "RefMaxwell");
653 if (refmaxwellCoarseSolve) {
654 GetOStream(Runtime0) << "Maxwell1::compute(): Will set up RefMaxwell coarse solver" << std::endl;
655 precList11_.set("coarse: type", "none");
656 }
657
658 // Rip off non-serializable data before validation
659 Teuchos::ParameterList nonSerialList11;
660 Teuchos::ParameterList processedPrecList11;
661 MueLu::ExtractNonSerializableData(precList11_, processedPrecList11, nonSerialList11);
662 RCP<HierarchyManager<SC, LO, GO, NO>> mueLuFactory = rcp(new ParameterListInterpreter<SC, LO, GO, NO>(processedPrecList11, SM_Matrix_->getDomainMap()->getComm()));
663 Hierarchy11_->setlib(SM_Matrix_->getDomainMap()->lib());
664 Hierarchy11_->SetProcRankVerbose(SM_Matrix_->getDomainMap()->getComm()->getRank());
665
666 // Stick the non-serializable data on the hierarchy and do setup
667 if (nonSerialList11.numParams() > 0) {
668 HierarchyUtils<SC, LO, GO, NO>::AddNonSerializableDataToHierarchy(*mueLuFactory, *Hierarchy11_, nonSerialList11);
669 }
670 mueLuFactory->SetupHierarchy(*Hierarchy11_);
671
672 if (refmaxwellCoarseSolve) {
673 GetOStream(Runtime0) << "Maxwell1::compute(): Setting up RefMaxwell coarse solver" << std::endl;
674 auto coarseLvl = Hierarchy11_->GetLevel(Hierarchy11_->GetNumLevels() - 1);
675 auto coarseSolver = rcp(new MueLu::RefMaxwellSmoother<Scalar, LocalOrdinal, GlobalOrdinal, Node>("RefMaxwell",
676 precList11_.sublist("coarse: params")));
677 coarseSolver->Setup(*coarseLvl);
678 coarseLvl->Set("PreSmoother",
679 rcp_dynamic_cast<SmootherBase>(coarseSolver, true));
680 }
681
682 if (mode_ == MODE_REFMAXWELL) {
683 if (Hierarchy11_->GetNumLevels() > 1) {
684 RCP<Level> EdgeL = Hierarchy11_->GetLevel(1);
685 P11_ = EdgeL->Get<RCP<Matrix>>("P");
686 }
687 }
688 }
689
691 // Allocate temporary MultiVectors for solve (only needed for RefMaxwell style)
692 allocateMemory(1);
693
694 describe(GetOStream(Runtime0));
695
696 if (Behavior::debug()) {
697 for (int i = 1; i < Hierarchy11_->GetNumLevels(); i++) {
698 auto EdgeL = Hierarchy11_->GetLevel(i);
699 auto EdgeL_fine = Hierarchy11_->GetLevel(i - 1);
700 auto NodeL = Hierarchy22_->GetLevel(i);
701
702 auto Pe = EdgeL->template Get<RCP<Matrix>>("P");
703 auto Pn = NodeL->template Get<RCP<Matrix>>("P");
704 auto D0_f = EdgeL_fine->template Get<RCP<Matrix>>("D0");
705 auto D0_c = EdgeL->template Get<RCP<Matrix>>("D0");
706
707 if (!Pe.is_null() && !D0_c.is_null() && (Pe->getRowMap()->getComm()->getSize() == D0_c->getRowMap()->getComm()->getSize())) {
708 using XMM = Xpetra::MatrixMatrix<SC, LO, GO, NO>;
709 auto one = Teuchos::ScalarTraits<SC>::one();
710
711 RCP<Matrix> dummy;
712 RCP<Matrix> left = XMM::Multiply(*Pe, false, *D0_c, false, dummy, GetOStream(Runtime0));
713 RCP<Matrix> right = XMM::Multiply(*D0_f, false, *Pn, false, dummy, GetOStream(Runtime0));
714
715 RCP<Matrix> summation;
716 XMM::TwoMatrixAdd(*left, false, one, *right, false, -one, summation, GetOStream(Runtime0));
717 summation->fillComplete(left->getDomainMap(), left->getRangeMap());
718
719 auto norm = summation->getFrobeniusNorm();
720 GetOStream(Runtime0) << "CheckCommutingProperty on level " << i - 1 << ": || Pe D0_c - D0_f Pn || = " << norm << std::endl;
721 } else if (!Pe.is_null()) {
722 GetOStream(Runtime0) << "Cannot run CheckCommutingProperty on level " << i - 1 << " due to rebalancing" << std::endl;
723 }
724 }
725 }
726#ifdef MUELU_MAXWELL1_DEBUG
727
728 for (int i = 0; i < Hierarchy11_->GetNumLevels(); i++) {
729 RCP<Level> L = Hierarchy11_->GetLevel(i);
730 RCP<Matrix> EdgeMatrix = rcp_dynamic_cast<Matrix>(L->Get<RCP<Operator>>("A"));
731 RCP<Matrix> NodeMatrix = rcp_dynamic_cast<Matrix>(L->Get<RCP<Operator>>("NodeMatrix"));
732 RCP<Matrix> NodeAggMatrix = rcp_dynamic_cast<Matrix>(L->Get<RCP<Operator>>("NodeAggMatrix"));
733 RCP<Matrix> D0 = rcp_dynamic_cast<Matrix>(L->Get<RCP<Operator>>("D0"));
734
735 auto nrmE = EdgeMatrix->getFrobeniusNorm();
736 auto nrmN = NodeMatrix->getFrobeniusNorm();
737 auto nrmNa = NodeAggMatrix->getFrobeniusNorm();
738 auto nrmD0 = D0->getFrobeniusNorm();
739
740 std::cout << "DEBUG: Norms on Level " << i << " E/N/NA/D0 = " << nrmE << " / " << nrmN << " / " << nrmNa << " / " << nrmD0 << std::endl;
741 std::cout << "DEBUG: NNZ on Level " << i << " E/N/NA/D0 = " << EdgeMatrix->getGlobalNumEntries() << " / " << NodeMatrix->getGlobalNumEntries() << " / " << NodeAggMatrix->getGlobalNumEntries() << " / " << D0->getGlobalNumEntries() << std::endl;
742 }
743#endif
744}
745
746template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
747RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>> Maxwell1<Scalar, LocalOrdinal, GlobalOrdinal, Node>::generate_kn() const {
748 // This is important, as we'll be doing diagonal repair *after* the next-level matrix is generated, not before
749 Teuchos::ParameterList RAPlist;
750 RAPlist.set("rap: fix zero diagonals", false);
751
752 std::string labelstr = "NodeMatrix (Level 0)";
753 RCP<Matrix> rv = Maxwell_Utils<SC, LO, GO, NO>::PtAPWrapper(SM_Matrix_, D0_Matrix_, RAPlist, labelstr);
754 return rv;
755}
756
757template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
759 if (mode_ == MODE_REFMAXWELL) {
760 RCP<Teuchos::TimeMonitor> tmAlloc = getTimer("MueLu Maxwell1: Allocate MVs");
761
762 residualFine_ = MultiVectorFactory::Build(SM_Matrix_->getRangeMap(), numVectors);
763
764 if (!Hierarchy11_.is_null() && Hierarchy11_->GetNumLevels() > 1) {
765 RCP<Level> EdgeL = Hierarchy11_->GetLevel(1);
766 RCP<Matrix> A = EdgeL->Get<RCP<Matrix>>("A");
767 residual11c_ = MultiVectorFactory::Build(A->getRangeMap(), numVectors);
768 update11c_ = MultiVectorFactory::Build(A->getDomainMap(), numVectors);
769 }
770
771 if (!Hierarchy22_.is_null()) {
772 residual22_ = MultiVectorFactory::Build(D0_Matrix_->getDomainMap(), numVectors);
773 update22_ = MultiVectorFactory::Build(D0_Matrix_->getDomainMap(), numVectors);
774 }
775 }
776}
777
778template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
779void Maxwell1<Scalar, LocalOrdinal, GlobalOrdinal, Node>::dump(const Matrix& A, std::string name) const {
780 if (dump_matrices_) {
781 GetOStream(Runtime0) << "Dumping to " << name << std::endl;
782 Xpetra::IO<SC, LO, GO, NO>::Write(name, A);
783 }
784}
785
786template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
787void Maxwell1<Scalar, LocalOrdinal, GlobalOrdinal, Node>::dump(const MultiVector& X, std::string name) const {
788 if (dump_matrices_) {
789 GetOStream(Runtime0) << "Dumping to " << name << std::endl;
790 Xpetra::IO<SC, LO, GO, NO>::Write(name, X);
791 }
792}
793
794template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
796 if (dump_matrices_) {
797 GetOStream(Runtime0) << "Dumping to " << name << std::endl;
798 Xpetra::IO<coordinateType, LO, GO, NO>::Write(name, X);
799 }
800}
801
802template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
803void Maxwell1<Scalar, LocalOrdinal, GlobalOrdinal, Node>::dump(const Teuchos::ArrayRCP<bool>& v, std::string name) const {
804 if (dump_matrices_) {
805 GetOStream(Runtime0) << "Dumping to " << name << std::endl;
806 std::ofstream out(name);
807 for (size_t i = 0; i < Teuchos::as<size_t>(v.size()); i++)
808 out << v[i] << "\n";
809 }
810}
811
812template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
813void Maxwell1<Scalar, LocalOrdinal, GlobalOrdinal, Node>::dump(const Kokkos::View<bool*, typename Node::device_type>& v, std::string name) const {
814 if (dump_matrices_) {
815 GetOStream(Runtime0) << "Dumping to " << name << std::endl;
816 std::ofstream out(name);
817 auto vH = Kokkos::create_mirror_view(v);
818 Kokkos::deep_copy(vH, v);
819 for (size_t i = 0; i < vH.size(); i++)
820 out << vH[i] << "\n";
821 }
822}
823
824template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
825Teuchos::RCP<Teuchos::TimeMonitor> Maxwell1<Scalar, LocalOrdinal, GlobalOrdinal, Node>::getTimer(std::string name, RCP<const Teuchos::Comm<int>> comm) const {
826 if (IsPrint(Timings)) {
827 if (!syncTimers_)
828 return Teuchos::rcp(new Teuchos::TimeMonitor(*Teuchos::TimeMonitor::getNewTimer(name)));
829 else {
830 if (comm.is_null())
831 return Teuchos::rcp(new Teuchos::SyncTimeMonitor(*Teuchos::TimeMonitor::getNewTimer(name), SM_Matrix_->getRowMap()->getComm().ptr()));
832 else
833 return Teuchos::rcp(new Teuchos::SyncTimeMonitor(*Teuchos::TimeMonitor::getNewTimer(name), comm.ptr()));
834 }
835 } else
836 return Teuchos::null;
837}
838
839template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
840void Maxwell1<Scalar, LocalOrdinal, GlobalOrdinal, Node>::resetMatrix(RCP<Matrix> SM_Matrix_new, bool ComputePrec) {
841 bool reuse = !SM_Matrix_.is_null();
842 SM_Matrix_ = SM_Matrix_new;
843 dump(*SM_Matrix_, "SM.m");
844 if (ComputePrec)
845 compute(reuse);
846}
847
848template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
850 // make sure that we have enough temporary memory
851 const SC one = Teuchos::ScalarTraits<SC>::one();
852 if (!allEdgesBoundary_ && X.getNumVectors() != residualFine_->getNumVectors())
853 allocateMemory(X.getNumVectors());
854
855 TEUCHOS_TEST_FOR_EXCEPTION(Hierarchy11_.is_null() || Hierarchy11_->GetNumLevels() == 0, Exceptions::RuntimeError, "(1,1) Hierarchy is null.");
856
857 // 1) Run fine pre-smoother using Hierarchy11
858 RCP<Level> Fine = Hierarchy11_->GetLevel(0);
859 if (Fine->IsAvailable("PreSmoother")) {
860 RCP<Teuchos::TimeMonitor> tmRes = getTimer("MueLu Maxwell1: PreSmoother");
861 RCP<SmootherBase> preSmoo = Fine->Get<RCP<SmootherBase>>("PreSmoother");
862 preSmoo->Apply(X, RHS, true);
863 }
864
865 // 2) Compute residual
866 {
867 RCP<Teuchos::TimeMonitor> tmRes = getTimer("MueLu Maxwell1: residual calculation");
868 Utilities::Residual(*SM_Matrix_, X, RHS, *residualFine_);
869 }
870
871 // 3a) Restrict residual to (1,1) Hierarchy's level 1 and execute (1,1) hierarchy (use startLevel and InitialGuessIsZero)
872 if (!P11_.is_null()) {
873 RCP<Teuchos::TimeMonitor> tmRes = getTimer("MueLu Maxwell1: (1,1) correction");
874 P11_->apply(*residualFine_, *residual11c_, Teuchos::TRANS);
875 Hierarchy11_->Iterate(*residual11c_, *update11c_, 1, true, 1);
876 }
877
878 // 3b) Restrict residual to (2,2) Hierarchy's level 0 and execute (2,2) hierarchy (use InitialGuessIsZero)
879 if (!allNodesBoundary_) {
880 RCP<Teuchos::TimeMonitor> tmRes = getTimer("MueLu Maxwell1: (2,2) correction");
881 D0_Matrix_->apply(*residualFine_, *residual22_, Teuchos::TRANS);
882 Hierarchy22_->Iterate(*residual22_, *update22_, 1, true, 0);
883 }
884
885 // 4) Prolong both updates back into X-vector (Need to do both the P11 null and not null cases
886 {
887 RCP<Teuchos::TimeMonitor> tmRes = getTimer("MueLu Maxwell1: Orolongation");
888 if (!P11_.is_null())
889 P11_->apply(*update11c_, X, Teuchos::NO_TRANS, one, one);
890 if (!allNodesBoundary_)
891 D0_Matrix_->apply(*update22_, X, Teuchos::NO_TRANS, one, one);
892 }
893
894 // 5) Run fine post-smoother using Hierarchy11
895 if (Fine->IsAvailable("PostSmoother")) {
896 RCP<Teuchos::TimeMonitor> tmRes = getTimer("MueLu Maxwell1: PostSmoother");
897 RCP<SmootherBase> postSmoo = Fine->Get<RCP<SmootherBase>>("PostSmoother");
898 postSmoo->Apply(X, RHS, false);
899 }
900}
901
902template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
903void Maxwell1<Scalar, LocalOrdinal, GlobalOrdinal, Node>::applyInverseStandard(const MultiVector& RHS, MultiVector& X) const {
904 Hierarchy11_->Iterate(RHS, X, 1, true);
905}
906
907template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
908void Maxwell1<Scalar, LocalOrdinal, GlobalOrdinal, Node>::apply(const MultiVector& RHS, MultiVector& X,
909 Teuchos::ETransp /* mode */,
910 Scalar /* alpha */,
911 Scalar /* beta */) const {
912 RCP<Teuchos::TimeMonitor> tm = getTimer("MueLu Maxwell1: solve");
913 if (mode_ == MODE_GMHD_STANDARD)
914 HierarchyGmhd_->Iterate(RHS, X, 1, true);
915 else if (mode_ == MODE_STANDARD || mode_ == MODE_EDGE_ONLY)
916 applyInverseStandard(RHS, X);
917 else if (mode_ == MODE_REFMAXWELL)
918 applyInverseRefMaxwellAdditive(RHS, X);
919 else
920 TEUCHOS_TEST_FOR_EXCEPTION(true, Exceptions::RuntimeError, "Must use mode 'standard', 'refmaxwell' or 'edge only' when not doing GMHD.");
921}
922
923template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
927
928template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
929void scaleD0(Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& D0) {
930 // We assume that D0 has entries +-1.
931 // Construction D0 via interpolation can instead give +-0.5
932 // Let's check and scale D0.
933
934 using range_type = Kokkos::RangePolicy<LocalOrdinal, typename Node::execution_space>;
935 using Matrix = Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>;
936 using impl_scalar_type = typename Matrix::impl_scalar_type;
937 using impl_ATS = KokkosKernels::ArithTraits<impl_scalar_type>;
938 using magnitude_type = typename impl_ATS::magnitudeType;
939 using MinMax = Kokkos::MinMax<magnitude_type>;
940
941 typename MinMax::value_type result;
942 {
943 auto lclD0 = D0.getLocalMatrixDevice();
944 Kokkos::parallel_reduce(
945 "MueLu::Maxwell1::D0_fixup",
946 range_type(0, lclD0.nnz()),
947 KOKKOS_LAMBDA(const LocalOrdinal k, typename MinMax::value_type& res) {
948 auto val = impl_ATS::magnitude(lclD0.values(k));
949 res.min_val = Kokkos::min(res.min_val, val);
950 res.max_val = Kokkos::max(res.max_val, val);
951 },
952 MinMax(result));
953 }
954
955 TEUCHOS_ASSERT_EQUALITY(result.min_val, result.max_val);
956
957 if (impl_ATS::magnitude(result.max_val - impl_ATS::one()) > impl_ATS::eps()) {
958 Scalar scaling = impl_ATS::one() / result.max_val;
959 D0.scale(scaling);
960 }
961}
962
963template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
965 initialize(const Teuchos::RCP<Matrix>& D0_Matrix,
966 const Teuchos::RCP<Matrix>& Kn_Matrix,
967 const Teuchos::RCP<MultiVector>& Nullspace,
968 const Teuchos::RCP<RealValuedMultiVector>& Coords,
969 const Teuchos::RCP<Matrix>& CurlCurl_Matrix,
970 const Teuchos::RCP<MultiVector>& Material,
971 Teuchos::ParameterList& List) {
972 // some pre-conditions
973 TEUCHOS_ASSERT(D0_Matrix != Teuchos::null);
974
975 if (Behavior::debug()) {
976 if (!Kn_Matrix.is_null()) {
977 TEUCHOS_ASSERT(Kn_Matrix->getDomainMap()->isSameAs(*D0_Matrix->getDomainMap()));
978 TEUCHOS_ASSERT(Kn_Matrix->getRangeMap()->isSameAs(*D0_Matrix->getDomainMap()));
979 }
980
981 TEUCHOS_ASSERT(D0_Matrix->getRangeMap()->isSameAs(*D0_Matrix->getRowMap()));
982 }
983
984 Hierarchy11_ = Teuchos::null;
985 Hierarchy22_ = Teuchos::null;
986 HierarchyGmhd_ = Teuchos::null;
987 if (mode_ != MODE_GMHD_STANDARD) mode_ = MODE_STANDARD;
988
989 // Default settings
990 useKokkos_ = false;
991 allEdgesBoundary_ = false;
992 allNodesBoundary_ = false;
993 dump_matrices_ = false;
994 check_D0_scaling_ = false;
995 enable_reuse_ = false;
996 syncTimers_ = false;
997 applyBCsTo22_ = false;
998
999 // set parameters
1000 setParameters(List);
1001
1002 if (D0_Matrix->getRowMap()->lib() == Xpetra::UseTpetra) {
1003 // We will remove boundary conditions from D0, and potentially change maps, so we copy the input.
1004 // Fortunately, D0 is quite sparse.
1005 // We cannot use the Tpetra copy constructor, since it does not copy the graph.
1006
1007 RCP<Matrix> D0copy = MatrixFactory::Build(D0_Matrix->getRowMap(), D0_Matrix->getColMap(), 0);
1008 RCP<CrsMatrix> D0copyCrs = toCrsMatrix(D0copy);
1009 ArrayRCP<const size_t> D0rowptr_RCP;
1010 ArrayRCP<const LO> D0colind_RCP;
1011 ArrayRCP<const SC> D0vals_RCP;
1012 toCrsMatrix(D0_Matrix)->getAllValues(D0rowptr_RCP, D0colind_RCP, D0vals_RCP);
1013
1014 ArrayRCP<size_t> D0copyrowptr_RCP;
1015 ArrayRCP<LO> D0copycolind_RCP;
1016 ArrayRCP<SC> D0copyvals_RCP;
1017 D0copyCrs->allocateAllValues(D0vals_RCP.size(), D0copyrowptr_RCP, D0copycolind_RCP, D0copyvals_RCP);
1018 D0copyrowptr_RCP.deepCopy(D0rowptr_RCP());
1019 D0copycolind_RCP.deepCopy(D0colind_RCP());
1020 D0copyvals_RCP.deepCopy(D0vals_RCP());
1021 D0copyCrs->setAllValues(D0copyrowptr_RCP,
1022 D0copycolind_RCP,
1023 D0copyvals_RCP);
1024 D0copyCrs->expertStaticFillComplete(D0_Matrix->getDomainMap(), D0_Matrix->getRangeMap(),
1025 toCrsMatrix(D0_Matrix)->getCrsGraph()->getImporter(),
1026 toCrsMatrix(D0_Matrix)->getCrsGraph()->getExporter());
1027 D0_Matrix_ = D0copy;
1028 } else
1029 D0_Matrix_ = MatrixFactory::BuildCopy(D0_Matrix);
1030
1031 if (check_D0_scaling_)
1032 scaleD0(*D0_Matrix_);
1033
1034 Kn_Matrix_ = Kn_Matrix;
1035 Coords_ = Coords;
1036 Material_ = Material;
1037 Nullspace_ = Nullspace;
1038
1039 if (!CurlCurl_Matrix.is_null()) {
1040 precList11_.sublist("user data").set("CurlCurl", CurlCurl_Matrix);
1041 dump(*CurlCurl_Matrix, "CurlCurl.m");
1042 }
1043
1044 dump(*D0_Matrix_, "D0.m");
1045 if (!Kn_Matrix_.is_null()) dump(*Kn_Matrix_, "Kn.m");
1046 if (!Nullspace_.is_null()) dump(*Nullspace_, "nullspace.m");
1047 if (!Coords_.is_null()) dumpCoords(*Coords_, "coords.m");
1048}
1049
1050template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1052 describe(Teuchos::FancyOStream& out, const Teuchos::EVerbosityLevel /* verbLevel */) const {
1053 std::ostringstream oss;
1054
1055 RCP<const Teuchos::Comm<int>> comm = SM_Matrix_->getDomainMap()->getComm();
1056
1057#ifdef HAVE_MPI
1058 int root;
1059 if (!Kn_Matrix_.is_null())
1060 root = comm->getRank();
1061 else
1062 root = -1;
1063
1064 int actualRoot;
1065 reduceAll(*comm, Teuchos::REDUCE_MAX, root, Teuchos::ptr(&actualRoot));
1066 root = actualRoot;
1067#endif
1068
1069 oss << "\n--------------------------------------------------------------------------------\n"
1070 << "--- Maxwell1 Summary ---\n"
1071 "--------------------------------------------------------------------------------"
1072 << std::endl;
1073 oss << std::endl;
1074
1075 GlobalOrdinal numRows;
1076 GlobalOrdinal nnz;
1077
1078 SM_Matrix_->getRowMap()->getComm()->barrier();
1079
1080 numRows = SM_Matrix_->getGlobalNumRows();
1081 nnz = SM_Matrix_->getGlobalNumEntries();
1082
1083 Xpetra::global_size_t tt = numRows;
1084 int rowspacer = 3;
1085 while (tt != 0) {
1086 tt /= 10;
1087 rowspacer++;
1088 }
1089 tt = nnz;
1090 int nnzspacer = 2;
1091 while (tt != 0) {
1092 tt /= 10;
1093 nnzspacer++;
1094 }
1095
1096 oss << "block " << std::setw(rowspacer) << " rows " << std::setw(nnzspacer) << " nnz " << std::setw(9) << " nnz/row" << std::endl;
1097 oss << "(1, 1)" << std::setw(rowspacer) << numRows << std::setw(nnzspacer) << nnz << std::setw(9) << as<double>(nnz) / numRows << std::endl;
1098
1099 if (!Kn_Matrix_.is_null()) {
1100 // ToDo: make sure that this is printed correctly
1101 numRows = Kn_Matrix_->getGlobalNumRows();
1102 nnz = Kn_Matrix_->getGlobalNumEntries();
1103
1104 oss << "(2, 2)" << std::setw(rowspacer) << numRows << std::setw(nnzspacer) << nnz << std::setw(9) << as<double>(nnz) / numRows << std::endl;
1105 }
1106
1107 oss << std::endl;
1108
1109 std::string outstr = oss.str();
1110
1111#ifdef HAVE_MPI
1112 RCP<const Teuchos::MpiComm<int>> mpiComm = rcp_dynamic_cast<const Teuchos::MpiComm<int>>(comm);
1113 MPI_Comm rawComm = (*mpiComm->getRawMpiComm())();
1114
1115 int strLength = outstr.size();
1116 MPI_Bcast(&strLength, 1, MPI_INT, root, rawComm);
1117 if (comm->getRank() != root)
1118 outstr.resize(strLength);
1119 MPI_Bcast(&outstr[0], strLength, MPI_CHAR, root, rawComm);
1120#endif
1121
1122 out << outstr;
1123
1124 if (!Hierarchy11_.is_null())
1125 Hierarchy11_->describe(out, GetVerbLevel());
1126
1127 if (!Hierarchy22_.is_null())
1128 Hierarchy22_->describe(out, GetVerbLevel());
1129
1130 if (!HierarchyGmhd_.is_null())
1131 HierarchyGmhd_->describe(out, GetVerbLevel());
1132
1133 if (IsPrint(Statistics2)) {
1134 // Print the grid of processors
1135 std::ostringstream oss2;
1136
1137 oss2 << "Sub-solver distribution over ranks" << std::endl;
1138 oss2 << "( (1,1) block only is indicated by '1', (2,2) block only by '2', and both blocks by 'B' and none by '.')" << std::endl;
1139
1140 int numProcs = comm->getSize();
1141#ifdef HAVE_MPI
1142 RCP<const Teuchos::MpiComm<int>> tmpic = rcp_dynamic_cast<const Teuchos::MpiComm<int>>(comm);
1143
1144 RCP<const Teuchos::OpaqueWrapper<MPI_Comm>> rawMpiComm = tmpic->getRawMpiComm();
1145#endif
1146
1147 char status = 0;
1148 if (!Kn_Matrix_.is_null())
1149 status += 1;
1150 std::vector<char> states(numProcs, 0);
1151#ifdef HAVE_MPI
1152 MPI_Gather(&status, 1, MPI_CHAR, &states[0], 1, MPI_CHAR, 0, *rawMpiComm);
1153#else
1154 states.push_back(status);
1155#endif
1156
1157 int rowWidth = std::min(Teuchos::as<int>(ceil(sqrt(numProcs))), 100);
1158 for (int proc = 0; proc < numProcs; proc += rowWidth) {
1159 for (int j = 0; j < rowWidth; j++)
1160 if (proc + j < numProcs)
1161 if (states[proc + j] == 0)
1162 oss2 << ".";
1163 else if (states[proc + j] == 1)
1164 oss2 << "1";
1165 else if (states[proc + j] == 2)
1166 oss2 << "2";
1167 else
1168 oss2 << "B";
1169 else
1170 oss2 << " ";
1171
1172 oss2 << " " << proc << ":" << std::min(proc + rowWidth, numProcs) - 1 << std::endl;
1173 }
1174 oss2 << std::endl;
1175 GetOStream(Statistics2) << oss2.str();
1176 }
1177}
1178
1179} // namespace MueLu
1180
1181#define MUELU_MAXWELL1_SHORT
1182#endif // ifdef MUELU_MAXWELL1_DEF_HPP
Various adapters that will create a MueLu preconditioner that is an Xpetra::Matrix.
MueLu::DefaultLocalOrdinal LocalOrdinal
MueLu::DefaultScalar Scalar
MueLu::DefaultGlobalOrdinal GlobalOrdinal
static bool debug()
Whether MueLu is in debug mode.
Exception throws to report errors in the internal logical of the program.
Provides methods to build a multigrid hierarchy and apply multigrid cycles.
static void AddNonSerializableDataToHierarchy(HierarchyManager &HM, Hierarchy &H, const ParameterList &nonSerialList)
Add non-serializable data to Hierarchy.
static void CopyBetweenHierarchies(Hierarchy &fromHierarchy, Hierarchy &toHierarchy, const std::string fromLabel, const std::string toLabel, const std::string dataType)
Class that holds all level-specific information.
void SetLevelID(int levelID)
Set level number.
T & Get(const std::string &ename, const FactoryBase *factory=NoFactory::get())
Get data without decrementing associated storage counter (i.e., read-only access)....
void Set(const std::string &ename, const T &entry, const FactoryBase *factory=NoFactory::get())
void Request(const FactoryBase &factory)
Increment the storage counter for all the inputs of a factory.
void SetPreviousLevel(const RCP< Level > &previousLevel)
void SetFactoryManager(const RCP< const FactoryManagerBase > &factoryManager)
Set default factories (used internally by Hierarchy::SetLevel()).
static std::string translate(Teuchos::ParameterList &paramList, const std::string &defaultVals="")
: Translate ML parameters to MueLu parameter XML string
const Teuchos::RCP< const Map > getDomainMap() const
Returns the Xpetra::Map object associated with the domain of this operator.
Teuchos::RCP< Matrix > generate_kn() const
Generates the Kn matrix.
typename Teuchos::ScalarTraits< Scalar >::magnitudeType magnitudeType
void compute(bool reuse=false)
Setup the preconditioner.
void GMHDSetupHierarchy(Teuchos::ParameterList &List) const
Sets up hiearchy for GMHD matrices that include generalized Ohms law equations.
const Teuchos::RCP< const Map > getRangeMap() const
Returns the Xpetra::Map object associated with the range of this operator.
void applyInverseRefMaxwellAdditive(const MultiVector &RHS, MultiVector &X) const
apply RefMaxwell additive 2x2 style cycle
void apply(const MultiVector &RHS, MultiVector &X, Teuchos::ETransp mode=Teuchos::NO_TRANS, Scalar alpha=Teuchos::ScalarTraits< Scalar >::one(), Scalar beta=Teuchos::ScalarTraits< Scalar >::zero()) const
void applyInverseStandard(const MultiVector &RHS, MultiVector &X) const
apply standard Maxwell1 cycle
void dumpCoords(const RealValuedMultiVector &X, std::string name) const
dump out real-valued multivector
void describe(Teuchos::FancyOStream &out, const Teuchos::EVerbosityLevel verbLevel=Teuchos::VERB_HIGH) const
void dump(const Matrix &A, std::string name) const
dump out matrix
typename Xpetra::MultiVector< coordinateType, LO, GO, NO > RealValuedMultiVector
void resetMatrix(Teuchos::RCP< Matrix > SM_Matrix_new, bool ComputePrec=true)
Reset system matrix.
bool hasTransposeApply() const
Indicates whether this operator supports applying the adjoint operator.
Teuchos::RCP< Teuchos::TimeMonitor > getTimer(std::string name, RCP< const Teuchos::Comm< int > > comm=Teuchos::null) const
get a (synced) timer
void initialize(const Teuchos::RCP< Matrix > &D0_Matrix, const Teuchos::RCP< Matrix > &Kn_Matrix, const Teuchos::RCP< MultiVector > &Nullspace, const Teuchos::RCP< RealValuedMultiVector > &Coords, const Teuchos::RCP< Matrix > &CurlCurl_Matrix, const Teuchos::RCP< MultiVector > &Material, Teuchos::ParameterList &List)
void setParameters(Teuchos::ParameterList &list)
Set parameters.
void allocateMemory(int numVectors) const
allocate multivectors for solve
static void detectBoundaryConditionsSM(RCP< Matrix > &SM_Matrix, RCP< Matrix > &D0_Matrix, magnitudeType rowSumTol, bool useKokkos_, Kokkos::View< bool *, typename Node::device_type::memory_space > &BCrowsKokkos, Kokkos::View< bool *, typename Node::device_type::memory_space > &BCcolsKokkos, Kokkos::View< bool *, typename Node::device_type::memory_space > &BCdomainKokkos, int &BCedges, int &BCnodes, Teuchos::ArrayRCP< bool > &BCrows, Teuchos::ArrayRCP< bool > &BCcols, Teuchos::ArrayRCP< bool > &BCdomain, bool &allEdgesBoundary, bool &allNodesBoundary)
Detect Dirichlet boundary conditions.
static RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > PtAPWrapper(const RCP< Matrix > &A, const RCP< Matrix > &P, Teuchos::ParameterList &params, const std::string &label)
Performs an P^T AP.
static std::string PrintMatrixInfo(const Matrix &A, const std::string &msgTag, RCP< const Teuchos::ParameterList > params=Teuchos::null)
Factory for building coarse matrices.
Applies permutation to grid transfer operators.
Class that encapsulates Operator smoothers.
static void ZeroDirichletRows(Teuchos::RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > &A, const std::vector< LocalOrdinal > &dirichletRows, Scalar replaceWith=Teuchos::ScalarTraits< Scalar >::zero())
static void ZeroDirichletCols(Teuchos::RCP< Matrix > &A, const Teuchos::ArrayRCP< const bool > &dirichletCols, Scalar replaceWith=Teuchos::ScalarTraits< Scalar >::zero())
static RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > ReplaceNonZerosWithOnes(const RCP< Matrix > &original)
Creates a copy of a matrix where the non-zero entries are replaced by ones.
static RCP< MultiVector > Residual(const Xpetra::Operator< Scalar, LocalOrdinal, GlobalOrdinal, Node > &Op, const MultiVector &X, const MultiVector &RHS)
static void SetDefaultVerbLevel(const VerbLevel defaultVerbLevel)
Set the default (global) verbosity level.
Namespace for MueLu classes and methods.
long ExtractNonSerializableData(const Teuchos::ParameterList &inList, Teuchos::ParameterList &serialList, Teuchos::ParameterList &nonSerialList)
Extract non-serializable data from level-specific sublists and move it to a separate parameter list.
@ Warnings0
Important warning messages (one line)
@ Statistics2
Print even more statistics.
@ Runtime0
One-liner description of what is happening.
@ Warnings1
Additional warnings.
@ Timings
Print all timing information.
MsgType toVerbLevel(const std::string &verbLevelStr)
Teuchos::RCP< MueLu::Hierarchy< Scalar, LocalOrdinal, GlobalOrdinal, Node > > CreateXpetraPreconditioner(Teuchos::RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > op, const Teuchos::ParameterList &inParamList)
Helper function to create a MueLu preconditioner that can be used by Xpetra.Given an Xpetra::Matrix,...
void scaleD0(Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &D0)
static std::string getColonLabel(const std::string &label)
Helper function for object label.