MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_IfpackSmoother.cpp
Go to the documentation of this file.
1// @HEADER
2//
3// ***********************************************************************
4//
5// MueLu: A package for multigrid based preconditioning
6// Copyright 2012 Sandia Corporation
7//
8// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
9// the U.S. Government retains certain rights in this software.
10//
11// Redistribution and use in source and binary forms, with or without
12// modification, are permitted provided that the following conditions are
13// met:
14//
15// 1. Redistributions of source code must retain the above copyright
16// notice, this list of conditions and the following disclaimer.
17//
18// 2. Redistributions in binary form must reproduce the above copyright
19// notice, this list of conditions and the following disclaimer in the
20// documentation and/or other materials provided with the distribution.
21//
22// 3. Neither the name of the Corporation nor the names of the
23// contributors may be used to endorse or promote products derived from
24// this software without specific prior written permission.
25//
26// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
27// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
30// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37//
38// Questions? Contact
39// Jonathan Hu (jhu@sandia.gov)
40// Andrey Prokopenko (aprokop@sandia.gov)
41// Ray Tuminaro (rstumin@sandia.gov)
42//
43// ***********************************************************************
44//
45// @HEADER
46#include "MueLu_ConfigDefs.hpp"
47
48#if defined(HAVE_MUELU_EPETRA) && defined(HAVE_MUELU_IFPACK)
49#include <Ifpack.h>
50#include <Ifpack_Chebyshev.h>
51#include "Xpetra_MultiVectorFactory.hpp"
52
54
55#include "MueLu_Level.hpp"
56#include "MueLu_Utilities.hpp"
57#include "MueLu_Monitor.hpp"
58#include "MueLu_Aggregates.hpp"
59
60
61namespace MueLu {
62
63 template <class Node>
64 IfpackSmoother<Node>::IfpackSmoother(std::string const & type, Teuchos::ParameterList const & paramList, LO const &overlap)
65 : type_(type), overlap_(overlap)
66 {
67 this->declareConstructionOutcome(false, "");
68 SetParameterList(paramList);
69 }
70
71 template <class Node>
72 void IfpackSmoother<Node>::SetParameterList(const Teuchos::ParameterList& paramList) {
74
76 // It might be invalid to change parameters after the setup, but it depends entirely on Ifpack implementation.
77 // TODO: I don't know if Ifpack returns an error code or exception or ignore parameters modification in this case...
78 prec_->SetParameters(const_cast<ParameterList&>(this->GetParameterList()));
79 }
80 }
81
82 template <class Node>
83 void IfpackSmoother<Node>::SetPrecParameters(const Teuchos::ParameterList& list) const {
84 ParameterList& paramList = const_cast<ParameterList&>(this->GetParameterList());
85 paramList.setParameters(list);
86
87 RCP<ParameterList> precList = this->RemoveFactoriesFromList(this->GetParameterList());
88
89 prec_->SetParameters(*precList);
90
91 // We would like to have the following line here:
92 // paramList.setParameters(*precList);
93 // For instance, if Ifpack sets somem parameters internally, we would like to have
94 // them listed when we call this->GetParameterList()
95 // But because of the way Ifpack handles the list, we cannot do that.
96 // The bad scenario goes like this:
97 // * SmootherFactory calls Setup
98 // * Setup calls SetPrecParameters
99 // * We call prec_->SetParameters(*precList)
100 // This actually updates the internal parameter list with default prec_ parameters
101 // This means that we get a parameter ("chebyshev: max eigenvalue", -1) in the list
102 // * Setup calls prec_->Compute()
103 // Here we may compute the max eigenvalue, but we get no indication of this. If we
104 // do compute it, our parameter list becomes outdated
105 // * SmootherFactory calls Apply
106 // * Apply constructs a list with a list with an entry "chebyshev: zero starting solution"
107 // * We call prec_->SetParameters(*precList)
108 // The last call is the problem. At this point, we have a list with an outdated entry
109 // "chebyshev: max eigenvalue", but prec_ uses this entry and replaces the computed max
110 // eigenvalue with the one from the list, resulting in -1.0 eigenvalue.
111 //
112 // Ifpack2 does not have this problem, as it does not populate the list with new entries
113 }
114
115 template <class Node>
116 void IfpackSmoother<Node>::DeclareInput(Level &currentLevel) const {
117 this->Input(currentLevel, "A");
118
119 if (type_ == "LINESMOOTHING_BANDED_RELAXATION" ||
120 type_ == "LINESMOOTHING_BANDED RELAXATION" ||
121 type_ == "LINESMOOTHING_BANDEDRELAXATION" ||
122 type_ == "LINESMOOTHING_BLOCK_RELAXATION" ||
123 type_ == "LINESMOOTHING_BLOCK RELAXATION" ||
124 type_ == "LINESMOOTHING_BLOCKRELAXATION") {
125 this->Input(currentLevel, "CoarseNumZLayers"); // necessary for fallback criterion
126 this->Input(currentLevel, "LineDetection_VertLineIds"); // necessary to feed block smoother
127 } // if (type_ == "LINESMOOTHING_BANDEDRELAXATION")
128 else if (type_ == "AGGREGATE")
129 {
130 // Aggregate smoothing needs aggregates
131 this->Input(currentLevel,"Aggregates");
132 }
133
134 }
135
136 template <class Node>
137 void IfpackSmoother<Node>::Setup(Level &currentLevel) {
138 FactoryMonitor m(*this, "Setup Smoother", currentLevel);
139 if (SmootherPrototype::IsSetup() == true)
140 this->GetOStream(Warnings0) << "MueLu::IfpackSmoother::Setup(): Setup() has already been called" << std::endl;
141
142 A_ = Factory::Get< RCP<Matrix> >(currentLevel, "A");
143
144 double lambdaMax = -1.0;
145 if (type_ == "Chebyshev") {
146 std::string maxEigString = "chebyshev: max eigenvalue";
147 std::string eigRatioString = "chebyshev: ratio eigenvalue";
148
149 try {
150 lambdaMax = Teuchos::getValue<Scalar>(this->GetParameter(maxEigString));
151 this->GetOStream(Statistics1) << maxEigString << " (cached with smoother parameter list) = " << lambdaMax << std::endl;
152
153 } catch (Teuchos::Exceptions::InvalidParameterName&) {
154 lambdaMax = A_->GetMaxEigenvalueEstimate();
155
156 if (lambdaMax != -1.0) {
157 this->GetOStream(Statistics1) << maxEigString << " (cached with matrix) = " << lambdaMax << std::endl;
158 this->SetParameter(maxEigString, ParameterEntry(lambdaMax));
159 }
160 }
161
162 // Calculate the eigenvalue ratio
163 const Scalar defaultEigRatio = 20;
164
165 Scalar ratio = defaultEigRatio;
166 try {
167 ratio = Teuchos::getValue<Scalar>(this->GetParameter(eigRatioString));
168
169 } catch (Teuchos::Exceptions::InvalidParameterName&) {
170 this->SetParameter(eigRatioString, ParameterEntry(ratio));
171 }
172
173 if (currentLevel.GetLevelID()) {
174 // Update ratio to be
175 // ratio = max(number of fine DOFs / number of coarse DOFs, defaultValue)
176 //
177 // NOTE: We don't need to request previous level matrix as we know for sure it was constructed
178 RCP<const Matrix> fineA = currentLevel.GetPreviousLevel()->Get<RCP<Matrix> >("A");
179 size_t nRowsFine = fineA->getGlobalNumRows();
180 size_t nRowsCoarse = A_->getGlobalNumRows();
181
182 ratio = std::max(ratio, as<Scalar>(nRowsFine)/nRowsCoarse);
183
184 this->GetOStream(Statistics1) << eigRatioString << " (computed) = " << ratio << std::endl;
185 this->SetParameter(eigRatioString, ParameterEntry(ratio));
186 }
187 } // if (type_ == "Chebyshev")
188
189 if (type_ == "LINESMOOTHING_BANDED_RELAXATION" ||
190 type_ == "LINESMOOTHING_BANDED RELAXATION" ||
191 type_ == "LINESMOOTHING_BANDEDRELAXATION" ||
192 type_ == "LINESMOOTHING_BLOCK_RELAXATION" ||
193 type_ == "LINESMOOTHING_BLOCK RELAXATION" ||
194 type_ == "LINESMOOTHING_BLOCKRELAXATION" ) {
195 ParameterList& myparamList = const_cast<ParameterList&>(this->GetParameterList());
196
197 LO CoarseNumZLayers = currentLevel.Get<LO>("CoarseNumZLayers",Factory::GetFactory("CoarseNumZLayers").get());
198 if (CoarseNumZLayers > 0) {
199 Teuchos::ArrayRCP<LO> TVertLineIdSmoo = currentLevel.Get< Teuchos::ArrayRCP<LO> >("LineDetection_VertLineIds", Factory::GetFactory("LineDetection_VertLineIds").get());
200
201 // determine number of local parts
202 LO maxPart = 0;
203 for(size_t k = 0; k < Teuchos::as<size_t>(TVertLineIdSmoo.size()); k++) {
204 if(maxPart < TVertLineIdSmoo[k]) maxPart = TVertLineIdSmoo[k];
205 }
206
207 size_t numLocalRows = A_->getLocalNumRows();
208 TEUCHOS_TEST_FOR_EXCEPTION(numLocalRows % TVertLineIdSmoo.size() != 0, Exceptions::RuntimeError, "MueLu::Ifpack2Smoother::Setup(): the number of local nodes is incompatible with the TVertLineIdsSmoo.");
209
210 if (numLocalRows == Teuchos::as<size_t>(TVertLineIdSmoo.size())) {
211 myparamList.set("partitioner: type","user");
212 myparamList.set("partitioner: map",&(TVertLineIdSmoo[0]));
213 myparamList.set("partitioner: local parts",maxPart+1);
214 } else {
215 // we assume a constant number of DOFs per node
216 size_t numDofsPerNode = numLocalRows / TVertLineIdSmoo.size();
217
218 // Create a new Teuchos::ArrayRCP<LO> of size numLocalRows and fill it with the corresponding information
219 Teuchos::ArrayRCP<LO> partitionerMap(numLocalRows, Teuchos::OrdinalTraits<LocalOrdinal>::invalid());
220 for (size_t blockRow = 0; blockRow < Teuchos::as<size_t>(TVertLineIdSmoo.size()); ++blockRow)
221 for (size_t dof = 0; dof < numDofsPerNode; dof++)
222 partitionerMap[blockRow * numDofsPerNode + dof] = TVertLineIdSmoo[blockRow];
223 myparamList.set("partitioner: type","user");
224 myparamList.set("partitioner: map",&(partitionerMap[0]));
225 myparamList.set("partitioner: local parts",maxPart + 1);
226 }
227
228 if (type_ == "LINESMOOTHING_BANDED_RELAXATION" ||
229 type_ == "LINESMOOTHING_BANDED RELAXATION" ||
230 type_ == "LINESMOOTHING_BANDEDRELAXATION")
231 type_ = "block relaxation";
232 else
233 type_ = "block relaxation";
234 } else {
235 // line detection failed -> fallback to point-wise relaxation
236 this->GetOStream(Runtime0) << "Line detection failed: fall back to point-wise relaxation" << std::endl;
237 myparamList.remove("partitioner: type",false);
238 myparamList.remove("partitioner: map", false);
239 myparamList.remove("partitioner: local parts",false);
240 type_ = "point relaxation stand-alone";
241 }
242
243 } // if (type_ == "LINESMOOTHING_BANDEDRELAXATION")
244
245 if(type_ == "AGGREGATE") {
246 SetupAggregate(currentLevel);
247 }
248
249 else {
250 // If we're using a linear partitioner and haven't set the # local parts, set it to match the operator's block size
251 ParameterList precList = this->GetParameterList();
252 if(precList.isParameter("partitioner: type") && precList.get<std::string>("partitioner: type") == "linear" &&
253 !precList.isParameter("partitioner: local parts")) {
254 precList.set("partitioner: local parts", (int)A_->getLocalNumRows() / A_->GetFixedBlockSize());
255 }
256
257
258 RCP<Epetra_CrsMatrix> epA = Utilities::Op2NonConstEpetraCrs(A_);
259
260 Ifpack factory;
261 prec_ = rcp(factory.Create(type_, &(*epA), overlap_));
262 TEUCHOS_TEST_FOR_EXCEPTION(prec_.is_null(), Exceptions::RuntimeError, "Could not create an Ifpack preconditioner with type = \"" << type_ << "\"");
263 SetPrecParameters();
264 prec_->Compute();
265 }
266
268
269 if (type_ == "Chebyshev" && lambdaMax == -1.0) {
270 Teuchos::RCP<Ifpack_Chebyshev> chebyPrec = rcp_dynamic_cast<Ifpack_Chebyshev>(prec_);
271 if (chebyPrec != Teuchos::null) {
272 lambdaMax = chebyPrec->GetLambdaMax();
273 A_->SetMaxEigenvalueEstimate(lambdaMax);
274 this->GetOStream(Statistics1) << "chebyshev: max eigenvalue (calculated by Ifpack)" << " = " << lambdaMax << std::endl;
275 }
276 TEUCHOS_TEST_FOR_EXCEPTION(lambdaMax == -1.0, Exceptions::RuntimeError, "MueLu::IfpackSmoother::Setup(): no maximum eigenvalue estimate");
277 }
278
279 this->GetOStream(Statistics1) << description() << std::endl;
280 }
281
282
283 template <class Node>
285
286 ParameterList& paramList = const_cast<ParameterList&>(this->GetParameterList());
287
288 if (this->IsSetup() == true) {
289 this->GetOStream(Warnings0) << "MueLu::Ifpack2moother::SetupAggregate(): Setup() has already been called" << std::endl;
290 this->GetOStream(Warnings0) << "MueLu::IfpackSmoother::SetupAggregate(): reuse of this type is not available, reverting to full construction" << std::endl;
291 }
292
293 this->GetOStream(Statistics0) << "IfpackSmoother: Using Aggregate Smoothing"<<std::endl;
294
295 RCP<Aggregates> aggregates = Factory::Get<RCP<Aggregates> >(currentLevel,"Aggregates");
296 RCP<const LOMultiVector> vertex2AggId = aggregates->GetVertex2AggId();
297 ArrayRCP<LO> aggregate_ids = rcp_const_cast<LOMultiVector>(vertex2AggId)->getDataNonConst(0);
298 ArrayRCP<LO> dof_ids;
299
300 // We need to unamalgamate, if the FixedBlockSize > 1
301 if(A_->GetFixedBlockSize() > 1) {
302 // NOTE: We're basically going to have to leave a deallocated pointer hanging out
303 // in the paramList object (and inside the partitioner). This never gets
304 // use again after Compute() gets called, so this is OK, but I'm still leaving
305 // this note here in case it bites us again later.
306 LO blocksize = (LO) A_->GetFixedBlockSize();
307 dof_ids.resize(aggregate_ids.size() * blocksize);
308 for(LO i=0; i<(LO)aggregate_ids.size(); i++) {
309 for(LO j=0; j<(LO)blocksize; j++)
310 dof_ids[i*blocksize+j] = aggregate_ids[i];
311 }
312 }
313 else {
314 dof_ids = aggregate_ids;
315 }
316
317 paramList.set("partitioner: map", dof_ids.getRawPtr());
318 paramList.set("partitioner: type", "user");
319 paramList.set("partitioner: overlap", 0);
320 paramList.set("partitioner: local parts", (int)aggregates->GetNumAggregates());
321 // In case of Dirichlet nodes
322 paramList.set("partitioner: keep singletons",true);
323
324 RCP<Epetra_CrsMatrix> A = Utilities::Op2NonConstEpetraCrs(A_);
325 type_ = "block relaxation stand-alone";
326
327 Ifpack factory;
328 prec_ = rcp(factory.Create(type_, &(*A), overlap_));
329 TEUCHOS_TEST_FOR_EXCEPTION(prec_.is_null(), Exceptions::RuntimeError, "Could not create an Ifpack preconditioner with type = \"" << type_ << "\"");
330 SetPrecParameters();
331
332 int rv = prec_->Compute();
333 TEUCHOS_TEST_FOR_EXCEPTION(rv, Exceptions::RuntimeError, "Ifpack preconditioner with type = \"" << type_ << "\" Compute() call failed.");
334
335 }
336
337
338 template <class Node>
339 void IfpackSmoother<Node>::Apply(MultiVector& X, const MultiVector& B, bool InitialGuessIsZero) const {
340 TEUCHOS_TEST_FOR_EXCEPTION(SmootherPrototype::IsSetup() == false, Exceptions::RuntimeError, "MueLu::IfpackSmoother::Apply(): Setup() has not been called");
341
342
343 // Forward the InitialGuessIsZero option to Ifpack
344 Teuchos::ParameterList paramList;
345 bool supportInitialGuess = false;
346 if (type_ == "Chebyshev") {
347 paramList.set("chebyshev: zero starting solution", InitialGuessIsZero);
348 supportInitialGuess = true;
349
350 } else if (type_ == "point relaxation stand-alone") {
351 paramList.set("relaxation: zero starting solution", InitialGuessIsZero);
352 supportInitialGuess = true;
353 }
354
355 SetPrecParameters(paramList);
356
357 // Apply
358 if (InitialGuessIsZero || supportInitialGuess) {
361
362 prec_->ApplyInverse(epB, epX);
363
364 } else {
365 RCP<MultiVector> Residual = Utilities::Residual(*A_, X, B);
366 RCP<MultiVector> Correction = MultiVectorFactory::Build(A_->getDomainMap(), X.getNumVectors());
367
369 const Epetra_MultiVector& epB = Utilities::MV2EpetraMV(*Residual);
370
371 prec_->ApplyInverse(epB, epX);
372
373 X.update(1.0, *Correction, 1.0);
374 }
375 }
376
377 template <class Node>
378 RCP<MueLu::SmootherPrototype<double, int, int, Node> > IfpackSmoother<Node>::Copy() const {
379 RCP<IfpackSmoother<Node> > smoother = rcp(new IfpackSmoother<Node>(*this) );
380 smoother->SetParameterList(this->GetParameterList());
381 return Teuchos::rcp_dynamic_cast<MueLu::SmootherPrototype<double, int, int, Node> >(smoother);
382 }
383
384 template <class Node>
386 std::ostringstream out;
387 // The check "GetVerbLevel() == Test" is to avoid
388 // failures in the EasyInterface test.
389 if (prec_ == Teuchos::null || this->GetVerbLevel() == InterfaceTest) {
391 out << "{type = " << type_ << "}";
392 } else {
393 out << prec_->Label();
394 }
395 return out.str();
396 }
397
398 template <class Node>
399 void IfpackSmoother<Node>::print(Teuchos::FancyOStream &out, const VerbLevel verbLevel) const {
401
402 if (verbLevel & Parameters0)
403 out0 << "Prec. type: " << type_ << std::endl;
404
405 if (verbLevel & Parameters1) {
406 out0 << "Parameter list: " << std::endl;
407 Teuchos::OSTab tab2(out);
408 out << this->GetParameterList();
409 out0 << "Overlap: " << overlap_ << std::endl;
410 }
411
412 if (verbLevel & External)
413 if (prec_ != Teuchos::null) {
414 Teuchos::OSTab tab2(out);
415 out << *prec_ << std::endl;
416 }
417
418 if (verbLevel & Debug) {
419 out0 << "IsSetup: " << Teuchos::toString(SmootherPrototype::IsSetup()) << std::endl
420 << "-" << std::endl
421 << "RCP<A_>: " << A_ << std::endl
422 << "RCP<prec_>: " << prec_ << std::endl;
423 }
424 }
425
426 template <class Node>
428 // FIXME: This is a placeholder
429 return Teuchos::OrdinalTraits<size_t>::invalid();
430 }
431
432
433} // namespace MueLu
434
435// The IfpackSmoother is only templated on the Node, since it is an Epetra only object
436// Therefore we do not need the full ETI instantiations as we do for the other MueLu
437// objects which are instantiated on all template parameters.
438#if defined(HAVE_MUELU_EPETRA)
440#endif
441
442#endif
#define MUELU_DESCRIBE
Helper macro for implementing Describable::describe() for BaseClass objects.
Ifpack_Preconditioner * Create(const std::string PrecType, Epetra_RowMatrix *Matrix, const int overlap=0, bool overrideSerialDefault=false)
virtual std::string description() const
Return a simple one-line description of this object.
Exception throws to report errors in the internal logical of the program.
Timer to be used in factories. Similar to Monitor but with additional timers.
T Get(Level &level, const std::string &varName) const
const RCP< const FactoryBase > GetFactory(const std::string &varName) const
Default implementation of FactoryAcceptor::GetFactory()
Class that encapsulates Ifpack smoothers.
void Apply(MultiVector &X, const MultiVector &B, bool InitialGuessIsZero=false) const
Apply the preconditioner.
void SetPrecParameters(const Teuchos::ParameterList &list=Teuchos::ParameterList()) const
IfpackSmoother(std::string const &type, Teuchos::ParameterList const &paramList=Teuchos::ParameterList(), LO const &overlap=0)
Constructor.
void print(Teuchos::FancyOStream &out, const VerbLevel verbLevel=Default) const
Print the object with some verbosity level to an FancyOStream object.
std::string description() const
Return a simple one-line description of this object.
void Setup(Level &currentLevel)
Set up the smoother.
RCP< SmootherPrototype > Copy() const
void DeclareInput(Level &currentLevel) const
Input.
size_t getNodeSmootherComplexity() const
Get a rough estimate of cost per iteration.
void SetupAggregate(Level &currentLevel)
void SetParameterList(const Teuchos::ParameterList &paramList)
Set parameters from a parameter list and return with default values.
Class that holds all level-specific information.
RCP< Level > & GetPreviousLevel()
Previous level.
int GetLevelID() const
Return 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)....
virtual void SetParameterList(const Teuchos::ParameterList &paramList)=0
Set parameters from a parameter list and return with default values.
void declareConstructionOutcome(bool fail, std::string msg)
bool IsSetup() const
Get the state of a smoother prototype.
static RCP< Epetra_MultiVector > MV2NonConstEpetraMV(RCP< Xpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > > vec)
static RCP< Xpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > > Residual(const Xpetra::Operator< Scalar, LocalOrdinal, GlobalOrdinal, Node > &Op, const Xpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > &X, const Xpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > &RHS)
static RCP< Epetra_CrsMatrix > Op2NonConstEpetraCrs(RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > Op)
static RCP< const Epetra_MultiVector > MV2EpetraMV(RCP< Xpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > > const vec)
Helper utility to pull out the underlying Epetra objects from an Xpetra object.
Namespace for MueLu classes and methods.
@ Warnings0
Important warning messages (one line)
@ Debug
Print additional debugging information.
@ Statistics1
Print more statistics.
@ External
Print external lib objects.
@ Runtime0
One-liner description of what is happening.
@ Parameters0
Print class parameters.
@ Statistics0
Print statistics that do not involve significant additional computation.
@ Parameters1
Print class parameters (more parameters, more verbose)