VertexCFD  0.0-dev
VertexCFD_TempusObserver_WriteMatrix_impl.hpp
1 #ifndef VERTEXCFD_TEMPUSOBSERVER_WRITEMATRIX_IMPL_HPP
2 #define VERTEXCFD_TEMPUSOBSERVER_WRITEMATRIX_IMPL_HPP
3 
4 #include <string>
5 #include <vector>
6 
7 #include <EpetraExt_MultiVectorOut.h>
8 #include <EpetraExt_RowMatrixOut.h>
9 #include <Epetra_CrsMatrix.h>
10 #include <MatrixMarket_Tpetra.hpp>
11 #include <NOX_Abstract_Group.H>
12 #include <NOX_Solver_Generic.H>
13 #include <NOX_Thyra_Vector.H>
14 #include <Panzer_NodeType.hpp>
15 #include <Thyra_Amesos2LinearOpWithSolve_decl.hpp>
16 #include <Thyra_AmesosLinearOpWithSolve.hpp>
17 #include <Thyra_AztecOOLinearOpWithSolve.hpp>
18 #include <Thyra_BelosLinearOpWithSolve.hpp>
19 #include <Thyra_DefaultSpmdVector.hpp>
20 #include <Thyra_EpetraLinearOp.hpp>
21 #include <Thyra_EpetraThyraWrappers.hpp>
22 #include <Thyra_NonlinearSolver_NOX.hpp>
23 #include <Thyra_TpetraLinearOp.hpp>
24 #include <Thyra_TpetraThyraWrappers.hpp>
25 #include <Tpetra_CrsMatrix.hpp>
26 
27 namespace VertexCFD
28 {
29 namespace TempusObserver
30 {
31 //---------------------------------------------------------------------------//
32 template<class Scalar>
33 WriteMatrix<Scalar>::WriteMatrix(const Teuchos::ParameterList& write_params)
34 {
35  // Determine which time steps will write out matrix files
36  if (write_params.isType<Teuchos::Array<int>>("Write Steps"))
37  {
38  _write_steps
39  = write_params.template get<Teuchos::Array<int>>("Write Steps");
40  }
41  else
42  {
43  throw std::runtime_error(
44  "Requested matrix output but 'Write Steps' was not provided'");
45  }
46 
47  // Determine if we want the residual in addition to matrix
48  _write_residual = false;
49  if (write_params.isType<bool>("Write Residual"))
50  {
51  _write_residual = write_params.template get<bool>("Write Residual");
52  }
53 
54  // Set base filename for files. If not specified, use generic
55  // "jacobian" and "residual"
56  if (write_params.isType<std::string>("Matrix File Prefix"))
57  {
58  auto base_prefix
59  = write_params.template get<std::string>("Matrix File Prefix");
60  _jacobian_prefix = base_prefix + "-jacobian";
61  _residual_prefix = base_prefix + "-residual";
62  }
63  else
64  {
65  _jacobian_prefix = "jacobian";
66  _residual_prefix = "residual";
67  }
68 }
69 
70 //---------------------------------------------------------------------------//
71 template<class Scalar>
73  const Tempus::Integrator<Scalar>&)
74 {
75 }
76 
77 //---------------------------------------------------------------------------//
78 template<class Scalar>
79 void WriteMatrix<Scalar>::observeStartTimeStep(const Tempus::Integrator<Scalar>&)
80 {
81 }
82 
83 //---------------------------------------------------------------------------//
84 template<class Scalar>
85 void WriteMatrix<Scalar>::observeNextTimeStep(const Tempus::Integrator<Scalar>&)
86 {
87 }
88 
89 //---------------------------------------------------------------------------//
90 template<class Scalar>
91 void WriteMatrix<Scalar>::observeBeforeTakeStep(const Tempus::Integrator<Scalar>&)
92 {
93 }
94 
95 //---------------------------------------------------------------------------//
96 template<class Scalar>
97 void WriteMatrix<Scalar>::observeAfterTakeStep(const Tempus::Integrator<Scalar>&)
98 {
99 }
100 
101 //---------------------------------------------------------------------------//
102 template<class Scalar>
104  const Tempus::Integrator<Scalar>&)
105 {
106 }
107 
108 //---------------------------------------------------------------------------//
109 template<class Scalar>
111  const Tempus::Integrator<Scalar>& integrator)
112 {
113  auto step_index = integrator.getIndex();
114 
115  // Only write solution at specified time steps
116  if (std::find(_write_steps.begin(), _write_steps.end(), step_index)
117  != _write_steps.end())
118  {
119  // Determine filename for this step
120  std::string filename = _jacobian_prefix + "-"
121  + std::to_string(step_index) + ".mtx";
122 
123  // Message to put into comments of file
124  std::string descr = "VertexCFD Jacobian matrix at time step "
125  + std::to_string(step_index);
126 
127  // Get nonlinear solver from time integrator
128  auto stepper = integrator.getStepper();
129  auto nonlinear_solver = stepper->getSolver();
130  if (!nonlinear_solver)
131  {
132  throw std::runtime_error("Nonlinear solver not available");
133  }
134 
135  // Extract linear solver operator
136  auto linear_solver = nonlinear_solver->get_W();
137  if (!linear_solver)
138  {
139  throw std::runtime_error("Linear solver is not available");
140  }
141 
142  // Extra linear operator (Jacobian), toggling on Belos vs. AztecOO
143  auto jacobian_op = this->extractLinearOp(linear_solver);
144 
145  // Process matrix output, toggling on Epetra vs. Tpetra
146  this->write_jacobian(jacobian_op, filename, descr);
147 
148  if (_write_residual)
149  {
150  filename = _residual_prefix + "-" + std::to_string(step_index)
151  + ".mtx";
152  descr = "Residual vector at time step "
153  + std::to_string(step_index);
154 
155  // Extract NOX solver
156  auto thyranox_solver
157  = Teuchos::rcp_dynamic_cast<const Thyra::NOXNonlinearSolver>(
158  nonlinear_solver);
159  auto nox_solver = thyranox_solver->getNOXSolver();
160 
161  // Get residual vector from the solver
162  // Use the _previous_ solution group so that the residual
163  // is the RHS from the last nonlinear iteration rather than
164  // the converged residual after the solve is complete.
165  const auto& group = nox_solver->getPreviousSolutionGroup();
166  const auto& nox_resid = group.getF();
167  const NOX::Thyra::Vector& noxthyra_resid
168  = dynamic_cast<const NOX::Thyra::Vector&>(nox_resid);
169  auto thyra_resid = noxthyra_resid.getThyraRCPVector();
170 
171  // Process vector output, toggling on Epetra vs. Tpetra
172  this->write_residual(thyra_resid, filename, descr);
173  }
174  }
175 }
176 
177 //---------------------------------------------------------------------------//
178 template<class Scalar>
179 void WriteMatrix<Scalar>::observeEndIntegrator(const Tempus::Integrator<Scalar>&)
180 {
181 }
182 
183 //---------------------------------------------------------------------------//
184 template<class Scalar>
185 Teuchos::RCP<const Thyra::LinearOpBase<Scalar>>
187  const Teuchos::RCP<const Thyra::LinearOpWithSolveBase<Scalar>>&
188  linear_solver) const
189 {
190  Teuchos::RCP<const Thyra::LinearOpBase<Scalar>> jacobian_op;
191 
192  // Currently support Belos, AztecOO, Amesos, Amesos2 solvers.
193  // These all implement the same "getOp" interface, but that method is
194  // NOT a member of the LinearOpWithSolve base class.
195  // Try casting to each of these in turn to extract the Jacobian
196  jacobian_op
197  = this->tryExtractForwardOp<Thyra::BelosLinearOpWithSolve<double>>(
198  linear_solver);
199  if (jacobian_op)
200  return jacobian_op;
201 
202  jacobian_op = this->tryExtractForwardOp<Thyra::AztecOOLinearOpWithSolve>(
203  linear_solver);
204  if (jacobian_op)
205  return jacobian_op;
206 
207  jacobian_op = this->tryExtractForwardOp<Thyra::AmesosLinearOpWithSolve>(
208  linear_solver);
209  if (jacobian_op)
210  return jacobian_op;
211 
212  jacobian_op
213  = this->tryExtractForwardOp<Thyra::Amesos2LinearOpWithSolve<double>>(
214  linear_solver);
215  if (jacobian_op)
216  return jacobian_op;
217 
218  throw std::runtime_error(
219  "Unrecognized linear solver type in matrix output");
220 }
221 
222 //---------------------------------------------------------------------------//
223 // Attempt to extract the Jacobian from a solver of the type specified by
224 // the SolverType template parameter. If the concrete solver is not of the
225 // specified type, a null RCP will be returned.
226 //---------------------------------------------------------------------------//
227 template<class Scalar>
228 template<class SolverType>
229 Teuchos::RCP<const Thyra::LinearOpBase<Scalar>>
230 WriteMatrix<Scalar>::tryExtractForwardOp(
231  const Teuchos::RCP<const Thyra::LinearOpWithSolveBase<Scalar>>&
232  linear_solver) const
233 {
234  Teuchos::RCP<const Thyra::LinearOpBase<Scalar>> jacobian_op;
235 
236  auto derived_solver
237  = Teuchos::rcp_dynamic_cast<const SolverType>(linear_solver);
238  if (derived_solver)
239  {
240  // Const-cast and extract forward source operator (which is the
241  // Jacobian)
242  auto nonconst_derived_solver
243  = Teuchos::rcp_const_cast<SolverType>(derived_solver);
244  auto fwd_opsrc = nonconst_derived_solver->extract_fwdOpSrc();
245  jacobian_op = fwd_opsrc->getOp();
246  }
247 
248  return jacobian_op;
249 }
250 
251 //---------------------------------------------------------------------------//
252 template<class Scalar>
253 void WriteMatrix<Scalar>::write_jacobian(
254  const Teuchos::RCP<const Thyra::LinearOpBase<Scalar>>& jacobian_op,
255  const std::string& filename,
256  const std::string& description) const
257 {
258  // Toggle on Epetra vs. Tpetra
259  auto jacobian_epetra
260  = Teuchos::rcp_dynamic_cast<const Thyra::EpetraLinearOp>(jacobian_op);
261  if (jacobian_epetra)
262  {
263  auto epetra_op = jacobian_epetra->epetra_op();
264  auto epetra_mat
265  = Teuchos::rcp_dynamic_cast<const Epetra_RowMatrix>(epetra_op);
266  if (!epetra_mat)
267  {
268  throw std::runtime_error("Epetra operator is not a RowMatrix");
269  }
270 
271  EpetraExt::RowMatrixToMatrixMarketFile(
272  filename.c_str(), *epetra_mat, description.c_str());
273  }
274  else
275  {
276  // If not Epetra, must be Tpetra
277  auto jacobian_tpetra = Teuchos::rcp_dynamic_cast<
278  const Thyra::TpetraLinearOp<Scalar,
279  int,
280  panzer::GlobalOrdinal,
281  panzer::TpetraNodeType>>(jacobian_op);
282  if (!jacobian_tpetra)
283  {
284  throw std::runtime_error("Unexpected matrix type");
285  }
286 
287  // Tpetra matrix output requires concrete type (CrsMatrix, not
288  // RowMatrix)
289  using TpetraMatrix = Tpetra::
290  CrsMatrix<Scalar, int, panzer::GlobalOrdinal, panzer::TpetraNodeType>;
291  auto tpetra_op = jacobian_tpetra->getConstTpetraOperator();
292  auto tpetra_mat
293  = Teuchos::rcp_dynamic_cast<const TpetraMatrix>(tpetra_op);
294  if (!tpetra_mat)
295  {
296  throw std::runtime_error("Tpetra operator is not a CrsMatrix");
297  }
298 
299  Tpetra::MatrixMarket::Writer<TpetraMatrix>::writeSparseFile(
300  filename, *tpetra_mat, "", description);
301  }
302 }
303 
304 //---------------------------------------------------------------------------//
305 template<class Scalar>
306 void WriteMatrix<Scalar>::write_residual(
307  const Teuchos::RCP<const Thyra::VectorBase<Scalar>>& vec,
308  const std::string& filename,
309  const std::string& description) const
310 {
311  // Vector should be a DefaultSpmdVector (Epetra) or a TpetraVector.
312  auto spmd_vec
313  = Teuchos::rcp_dynamic_cast<const Thyra::DefaultSpmdVector<double>>(
314  vec);
315  if (spmd_vec)
316  {
317  auto space = vec->space();
318  auto epetra_map = Thyra::get_Epetra_Map(space);
319  auto epetra_vec = Thyra::get_Epetra_Vector(vec, epetra_map);
320 
321  EpetraExt::MultiVectorToMatrixMarketFile(
322  filename.c_str(), *epetra_vec, "", description.c_str());
323  }
324 
325  auto thyratpetra_vec = Teuchos::rcp_dynamic_cast<
326  const Thyra::
327  TpetraVector<double, int, panzer::GlobalOrdinal, panzer::TpetraNodeType>>(
328  vec);
329  if (thyratpetra_vec)
330  {
331  auto tpetra_vec = thyratpetra_vec->getConstTpetraVector();
332 
333  using TpetraMatrix = Tpetra::
334  CrsMatrix<Scalar, int, panzer::GlobalOrdinal, panzer::TpetraNodeType>;
335  Tpetra::MatrixMarket::Writer<TpetraMatrix>::writeDenseFile(
336  filename, *tpetra_vec, "", description);
337  }
338 }
339 
340 //---------------------------------------------------------------------------//
341 
342 } // end namespace TempusObserver
343 } // end namespace VertexCFD
344 
345 #endif // end VERTEXCFD_TEMPUSOBSERVER_WRITEMATRIX_IMPL_HPP
VertexCFD::TempusObserver::WriteMatrix::observeAfterTakeStep
void observeAfterTakeStep(const Tempus::Integrator< Scalar > &integrator) override
Observe after Stepper takes step.
Definition: VertexCFD_TempusObserver_WriteMatrix_impl.hpp:97
VertexCFD
Definition: tstMethodManufacturedSolutionBC.cpp:23
VertexCFD::TempusObserver::WriteMatrix::observeNextTimeStep
void observeNextTimeStep(const Tempus::Integrator< Scalar > &integrator) override
Definition: VertexCFD_TempusObserver_WriteMatrix_impl.hpp:85
VertexCFD::TempusObserver::WriteMatrix::observeBeforeTakeStep
void observeBeforeTakeStep(const Tempus::Integrator< Scalar > &integrator) override
Observe before Stepper takes step.
Definition: VertexCFD_TempusObserver_WriteMatrix_impl.hpp:91
VertexCFD::TempusObserver::WriteMatrix::observeStartTimeStep
void observeStartTimeStep(const Tempus::Integrator< Scalar > &integrator) override
Observe the beginning of the time step loop.
Definition: VertexCFD_TempusObserver_WriteMatrix_impl.hpp:79
VertexCFD::TempusObserver::WriteMatrix::observeStartIntegrator
void observeStartIntegrator(const Tempus::Integrator< Scalar > &integrator) override
Observe the beginning of the time integrator.
Definition: VertexCFD_TempusObserver_WriteMatrix_impl.hpp:72
VertexCFD::TempusObserver::WriteMatrix::observeEndIntegrator
void observeEndIntegrator(const Tempus::Integrator< Scalar > &integrator) override
Observe the end of the time integrator.
Definition: VertexCFD_TempusObserver_WriteMatrix_impl.hpp:179
VertexCFD::TempusObserver::WriteMatrix
Definition: VertexCFD_TempusObserver_WriteMatrix.hpp:17
VertexCFD::TempusObserver::WriteMatrix::observeAfterCheckTimeStep
void observeAfterCheckTimeStep(const Tempus::Integrator< Scalar > &integrator) override
Definition: VertexCFD_TempusObserver_WriteMatrix_impl.hpp:103
VertexCFD::TempusObserver::WriteMatrix::observeEndTimeStep
void observeEndTimeStep(const Tempus::Integrator< Scalar > &integrator) override
Observe the end of the time step loop.
Definition: VertexCFD_TempusObserver_WriteMatrix_impl.hpp:110