SERiF 0.0.1a
3+1D Stellar Structure and Evolution
Loading...
Searching...
No Matches
polytropeOperator.cpp
Go to the documentation of this file.
1/* ***********************************************************************
2//
3// Copyright (C) 2025 -- The 4D-STAR Collaboration
4// File Author: Emily Boudreaux
5// Last Modified: April 21, 2025
6//
7// 4DSSE is free software; you can use it and/or modify
8// it under the terms and restrictions the GNU General Library Public
9// License version 3 (GPLv3) as published by the Free Software Foundation.
10//
11// 4DSSE is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14// See the GNU Library General Public License for more details.
15//
16// You should have received a copy of the GNU Library General Public License
17// along with this software; if not, write to the Free Software
18// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19//
20// *********************************************************************** */
21#include "polytropeOperator.h"
22#include "4DSTARTypes.h"
23#include "utilities.h"
24
25#include "mfem.hpp"
26#include "mfem_smout.h"
27#include <memory>
28
29#include "config.h"
30
31namespace serif {
32namespace polytrope {
33 // --- SchurCompliment Class Implementation ---
34
35 // SchurCompliment: Constructors
37 const mfem::SparseMatrix &QOp,
38 const mfem::SparseMatrix &DOp,
39 const mfem::SparseMatrix &MOp,
40 const mfem::Solver &GradInvOp ) :
41mfem::Operator(DOp.Height(), DOp.Width()) {
42 // Initialize sizes
43 SetOperator(QOp, DOp, MOp, GradInvOp);
44 m_nPhi = m_DOp->Height();
45 m_nTheta = m_MOp->Height();
46}
47
49 const mfem::SparseMatrix &QOp,
50 const mfem::SparseMatrix &DOp,
51 const mfem::SparseMatrix &MOp) :
52mfem::Operator(DOp.Height(), DOp.Width())
53{
54 updateConstantTerms(QOp, DOp, MOp);
55 m_nPhi = m_DOp->Height();
56 m_nTheta = m_MOp->Height();
57}
58
59// SchurCompliment: Public Interface Methods
60void SchurCompliment::Mult(const mfem::Vector &x, mfem::Vector &y) const {
61 // Check that the input vector is the correct size
62 if (x.Size() != m_nPhi) {
63 MFEM_ABORT("Input vector x has size " + std::to_string(x.Size()) + ", expected " + std::to_string(m_nPhi));
64 }
65 if (y.Size() != m_nPhi) {
66 MFEM_ABORT("Output vector y has size " + std::to_string(y.Size()) + ", expected " + std::to_string(m_nPhi));
67 }
68
69 // Check that the operators are set
70 if (m_QOp == nullptr) {
71 MFEM_ABORT("QOp is null in SchurCompliment::Mult");
72 }
73 if (m_DOp == nullptr) {
74 MFEM_ABORT("DOp is null in SchurCompliment::Mult");
75 }
76 if (m_MOp == nullptr) {
77 MFEM_ABORT("MOp is null in SchurCompliment::Mult");
78 }
79 if (m_GradInvOp == nullptr) {
80 MFEM_ABORT("GradInvOp is null in SchurCompliment::Mult");
81 }
82
83 mfem::Vector v1(m_nTheta); // M * x
84 m_MOp -> Mult(x, v1); // M * x
85
86 mfem::Vector v2(m_nTheta); // GradInv * M * x
87 m_GradInvOp -> Mult(v1, v2); // GradInv * M * x
88
89 mfem::Vector v3(m_nPhi); // Q * GradInv * M * x
90 m_QOp -> Mult(v2, v3); // Q * GradInv * M * x
91
92 mfem::Vector v4(m_nPhi); // D * x
93 m_DOp -> Mult(x, v4); // D * x
94
95 subtract(v4, v3, y); // (D - Q * GradInv * M) * x
96}
97
98void SchurCompliment::SetOperator(const mfem::SparseMatrix &QOp, const mfem::SparseMatrix &DOp, const mfem::SparseMatrix &MOp, const mfem::Solver &GradInvOp) {
99 updateConstantTerms(QOp, DOp, MOp);
101}
102
103void SchurCompliment::updateInverseNonlinearJacobian(const mfem::Solver &gradInv) {
104 m_GradInvOp = &gradInv;
105}
106
107// SchurCompliment: Private Helper Methods
108void SchurCompliment::updateConstantTerms(const mfem::SparseMatrix &QOp, const mfem::SparseMatrix &DOp, const mfem::SparseMatrix &MOp) {
109 m_QOp = &QOp;
110 m_DOp = &DOp;
111 m_MOp = &MOp;
112}
113
114
115// --- GMRESInverter Class Implementation ---
116
117// GMRESInverter: Constructor
119mfem::Operator(op.Height(), op.Width()),
120m_op(op) {
121 m_solver.SetOperator(m_op);
122 m_solver.SetMaxIter(100);
123 m_solver.SetRelTol(1e-1);
124 m_solver.SetAbsTol(1e-1);
125}
126
127// GMRESInverter: Public Interface Methods
128void GMRESInverter::Mult(const mfem::Vector &x, mfem::Vector &y) const {
129 m_solver.Mult(x, y); // Approximates m_op^-1 * x
130}
131
132
133// --- PolytropeOperator Class Implementation ---
134 // PolytropeOperator: Constructor
136 std::unique_ptr<mfem::MixedBilinearForm> M,
137 std::unique_ptr<mfem::MixedBilinearForm> Q,
138 std::unique_ptr<mfem::BilinearForm> D,
139 std::unique_ptr<mfem::BilinearForm> S,
140 std::unique_ptr<mfem::NonlinearForm> f,
141 const mfem::Array<int> &blockOffsets) :
142
143 // TODO: Need to update this so that the size is that of the reduced system operator
144 mfem::Operator(blockOffsets.Last()), // Initialize the base class with the total size of the block offset vector
145 m_blockOffsets(blockOffsets),
146 m_jacobian(nullptr) {
147
148 m_M = std::move(M);
149 m_Q = std::move(Q);
150 m_D = std::move(D);
151 m_S = std::move(S);
152 m_f = std::move(f);
153
154 // Use Gauss-Seidel smoother to approximate the inverse of the matrix
155 // t = 0 (symmetric Gauss-Seidel), 1 (forward Gauss-Seidel), 2 (backward Gauss-Seidel)
156 // iterations = 3
157 m_invNonlinearJacobian = std::make_unique<mfem::GSSmoother>(0, 3);
158 }
159
160// PolytropeOperator: Core Operator Overrides
161void PolytropeOperator::Mult(const mfem::Vector &xFree, mfem::Vector &yFree) const {
162 if (!m_isFinalized) {
163 MFEM_ABORT("PolytropeOperator::Mult called before finalize");
164 }
165
166 // TODO: confirm that the vectors xFree and m_freeDofs are always parallel
167 m_state.SetSubVector(m_freeDofs, xFree); // Scatter the free dofs from the input vector xFree into the state vector
168 mfem::Vector y;
169 y.SetSize(m_blockOffsets.Last());
170
171 // -- Create BlockVector views for input x and output y
172 mfem::BlockVector x_block(const_cast<mfem::Vector&>(m_state), m_blockOffsets);
173 mfem::BlockVector y_block(y, m_blockOffsets);
174
175 // -- Get Vector views for individual blocks
176 const mfem::Vector &x_theta = x_block.GetBlock(0);
177 const mfem::Vector &x_phi = x_block.GetBlock(1);
178 mfem::Vector &y_R0 = y_block.GetBlock(0); // Residual Block 0 (theta)
179 mfem::Vector &y_R1 = y_block.GetBlock(1); // Residual Block 1 (phi)
180
181 int theta_size = m_blockOffsets[1] - m_blockOffsets[0];
182 int phi_size = m_blockOffsets[2] - m_blockOffsets[1];
183
184 mfem::Vector f_term(theta_size);
185 mfem::Vector Mphi_term(theta_size);
186 mfem::Vector Dphi_term(phi_size);
187 mfem::Vector Qtheta_term(phi_size);
188 mfem::Vector Stheta_term(theta_size);
189
190
191 // Calculate R0 and R1 terms
192 // R0 = f(θ) + (1+c)Mɸ + cSθ
193 // R1 = (1+c)Dɸ - (1+c)Qθ
194
195 MFEM_ASSERT(m_f.get() != nullptr, "NonlinearForm m_f is null in PolytropeOperator::Mult");
196
197 m_f->Mult(x_theta, f_term); // f(θ)
198 m_M->Mult(x_phi, Mphi_term); // Mɸ
199 m_D->Mult(x_phi, Dphi_term); // Dɸ
200 m_Q->Mult(x_theta, Qtheta_term); // Qθ
201 m_S->Mult(x_theta, Stheta_term); // Sθ
202
203 // PERF: these multiplications may be able to be refactored into the matrices so they only need to be done once.
204 Stheta_term *= m_stabilizationCoefficient; // cSθ
205 Qtheta_term *= m_IncrementedStabilizationCoefficient; // (1+c)Qθ
206 Mphi_term *= m_IncrementedStabilizationCoefficient; // (1+c)Mɸ
207 Dphi_term *= m_IncrementedStabilizationCoefficient; // (1+c)Dɸ
208
209 mfem::Vector y_R0_temp(theta_size);
210 add(f_term, Mphi_term, y_R0_temp); // R0 = f(θ) + (1+c)Mɸ
211 add(y_R0_temp, Stheta_term, y_R0); // R0 = f(θ) + (1+c)Mɸ + cSθ
212 subtract(Dphi_term, Qtheta_term, y_R1); // R1 = (1+c)Dɸ - (1+c)Qθ
213
214 // --- Scatter the residual vector y into the free dofs ---
215 yFree.SetSize(m_reducedBlockOffsets.Last());
216 MFEM_ASSERT(m_freeDofs.Size() == m_reducedBlockOffsets.Last(), "PolytropeOperator::Mult: Size of free dofs does not match reduced block offsets size.");
217 for (int i = 0, j = 0; i < y.Size(); ++i) {
218 if (m_freeDofs.Find(i) != -1) {
219 yFree[j] = y[i];
220 j++;
221 }
222 }
223}
224
225mfem::Operator& PolytropeOperator::GetGradient(const mfem::Vector &xFree) const {
226 if (!m_isFinalized) {
227 MFEM_ABORT("PolytropeOperator::GetGradient called before finalize");
228 }
229 m_state.SetSubVector(m_freeDofs, xFree); // Scatter the free dofs from the input vector xFree into the state vector
230 // --- Get the gradient of f ---
231 mfem::BlockVector x_block(const_cast<mfem::Vector&>(m_state), m_blockOffsets);
232 const mfem::Vector& x_theta = x_block.GetBlock(0);
233
234 // PERF: There are a lot of copies and loops here, probably performance could be gained by flattering some of these.
235 auto &grad = m_f->GetGradient(x_theta);
236 // updatePreconditioner(grad);
237 const auto gradMatrix = dynamic_cast<mfem::SparseMatrix*>(&grad); // ∂f(θ)/∂θ
238
239 if (gradMatrix == nullptr) {
240 MFEM_ABORT("PolytropeOperator::GetGradient: Gradient is not a SparseMatrix.");
241 }
242
243 m_gradReduced = std::make_unique<mfem::SparseMatrix> (
245 *gradMatrix,
246 m_theta_ess_tdofs.first,
248 )
249 ); // ∂f(θ)/∂θ (now reduced to only the free DOFs)
250
251
252 // TODO: Confirm this actually does what I want it to do
253 *m_gradReduced += *m_ScaledSReduced; // ∂f(θ)/∂θ + cS (reduced to only the free DOFs)
254
255 m_jacobian->SetBlock(0, 0, m_gradReduced.get());
256
257 return *m_jacobian;
258}
259
260
261// PolytropeOperator: Setup and Finalization
262void PolytropeOperator::finalize(const mfem::Vector &initTheta) {
264
265 if (m_isFinalized) {
266 return;
267 }
268
269 // These functions must be called in this order since they depend on each others post state
270 // TODO: Refactor this so that either there are explicit checks to make sure the order is correct or make
271 // them pure functions
277
278 // Override the size based on the reduced system
279 height = m_reducedBlockOffsets.Last();
280 width = m_reducedBlockOffsets.Last();
281
282 m_isFinalized = true;
283
284}
285
286// PolytropeOperator: Essential True DOF Management
288 m_isFinalized = false;
289 m_theta_ess_tdofs = theta_ess_tdofs;
290 m_phi_ess_tdofs = phi_ess_tdofs;
291
292 if (m_f) {
293 m_f->SetEssentialTrueDofs(theta_ess_tdofs.first);
294 } else {
295 MFEM_ABORT("m_f is null in PolytropeOperator::SetEssentialTrueDofs");
296 }
297}
298
300 set_essential_true_dofs(ess_tdof_pair_set.first, ess_tdof_pair_set.second);
301}
302
306
307// PolytropeOperator: Getter Methods
308const mfem::BlockOperator &PolytropeOperator::get_jacobian_operator() const {
309 if (m_jacobian == nullptr) {
310 MFEM_ABORT("Jacobian has not been initialized before GetJacobianOperator() call.");
311 }
312 if (!m_isFinalized) {
313 MFEM_ABORT("PolytropeOperator not finalized prior to call to GetJacobianOperator().");
314 }
315
316 return *m_jacobian;
317}
318
319mfem::BlockDiagonalPreconditioner& PolytropeOperator::get_preconditioner() const {
320 if (m_schurPreconditioner == nullptr) {
321 MFEM_ABORT("Schur preconditioner has not been initialized before GetPreconditioner() call.");
322 }
323 if (!m_isFinalized) {
324 MFEM_ABORT("PolytropeOperator not finalized prior to call to GetPreconditioner().");
325 }
326 return *m_schurPreconditioner;
327}
328
330 if (!m_isFinalized) {
331 MFEM_ABORT("PolytropeOperator not finalized prior to call to GetReducedSystemSize().");
332 }
333 return m_reducedBlockOffsets.Last();
334}
335
336// PolytropeOperator: State Reconstruction
337const mfem::Vector &PolytropeOperator::reconstruct_full_state_vector(const mfem::Vector &reducedState) const {
338 m_state.SetSubVector(m_freeDofs, reducedState); // Scatter the reduced state vector into the full state vector
339 return m_state;
340}
341
342const mfem::BlockVector PolytropeOperator::reconstruct_full_block_state_vector(const mfem::Vector &reducedState) const {
343 m_state.SetSubVector(m_freeDofs, reducedState); // Scatter the reduced state vector into the full state vector
344 mfem::BlockVector x_block(m_state, m_blockOffsets);
345 return x_block;
346}
347
348// PolytropeOperator: DOF Population
350 m_freeDofs.SetSize(0);
351 for (int i = 0; i < m_blockOffsets.Last(); i++) {
352 const int thetaSearchIndex = i;
353 const int phiSearchIndex = i - m_blockOffsets[1];
354 if (phiSearchIndex < 0){
355 if (m_theta_ess_tdofs.first.Find(thetaSearchIndex) == -1) {
356 m_freeDofs.Append(i);
357 }
358 } else {
359 if (m_phi_ess_tdofs.first.Find(phiSearchIndex) == -1) {
360 m_freeDofs.Append(i);
361 }
362 }
363 }
364}
365
366// PolytropeOperator: Private Helper Methods - Construction and Setup
368 // --- Construct the sparse matrix representations of M, Q, D, and S ---
369 m_Mmat = std::make_unique<mfem::SparseMatrix>(m_M->SpMat());
370 m_Qmat = std::make_unique<mfem::SparseMatrix>(m_Q->SpMat());
371 m_Dmat = std::make_unique<mfem::SparseMatrix>(m_D->SpMat());
372 m_Smat = std::make_unique<mfem::SparseMatrix>(m_S->SpMat());
373
374 // --- Reduce the matrices to only the free DOFs ---
375 m_MReduced = std::make_unique<mfem::SparseMatrix>(
377 *m_Mmat,
378 m_phi_ess_tdofs.first,
379 m_theta_ess_tdofs.first)
380 );
381 m_QReduced = std::make_unique<mfem::SparseMatrix>(
383 *m_Qmat,
384 m_theta_ess_tdofs.first,
385 m_phi_ess_tdofs.first)
386 );
387 m_DReduced = std::make_unique<mfem::SparseMatrix>(
389 *m_Dmat,
390 m_phi_ess_tdofs.first,
391 m_phi_ess_tdofs.first)
392 );
393 m_SReduced = std::make_unique<mfem::SparseMatrix>(
395 *m_Smat,
396 m_theta_ess_tdofs.first,
397 m_theta_ess_tdofs.first)
398 );
399
400 // --- Scale the reduced matrices by the stabilization coefficients ---
401 mfem::SparseMatrix MScaledTemp(*m_MReduced); // Create a temporary copy of the M matrix for scaling
402 MScaledTemp *= m_IncrementedStabilizationCoefficient; // Scale the M matrix by the incremented stabilization coefficient
403 m_MScaledReduced = std::make_unique<mfem::SparseMatrix>(MScaledTemp); // Store the scaled M matrix so that it persists
404
405 mfem::SparseMatrix QScaledTemp(*m_QReduced);
406 QScaledTemp *= m_IncrementedStabilizationCoefficient; // Scale the Q matrix by the incremented stabilization coefficient
407 m_QScaledReduced = std::make_unique<mfem::SparseMatrix>(QScaledTemp); // Store the scaled Q matrix so that it persists
408
409 mfem::SparseMatrix DRScaledTemp(*m_DReduced);
410 DRScaledTemp *= m_IncrementedStabilizationCoefficient; // Scale the D matrix by the incremented stabilization coefficient
411 m_DScaledReduced = std::make_unique<mfem::SparseMatrix>(DRScaledTemp); // Store the scaled D matrix so that it persists
412
413 // Scale the S matrix by the stabilization coefficient (so that the allocations only need to be done once)
414 mfem::SparseMatrix SScaledTemp(*m_SReduced); // Create a temporary copy of the S matrix for scaling
415 SScaledTemp *= m_stabilizationCoefficient; // Scale the S matrix by the stabilization coefficient
416 m_ScaledSReduced = std::make_unique<mfem::SparseMatrix>(SScaledTemp); // Store the scaled S matrix so that it persists
417
418 // --- Create the scaled operator for -(1+c)Q ---
419 m_negQ_mat = std::make_unique<mfem::ScaledOperator>(m_QScaledReduced.get(), -1.0);
420}
421
423 m_reducedBlockOffsets.SetSize(3);
424 m_reducedBlockOffsets[0] = 0; // R0 block (theta)
425 m_reducedBlockOffsets[1] = m_MReduced->Height(); // R1 block (theta)
426 m_reducedBlockOffsets[2] = m_QReduced->Height() + m_reducedBlockOffsets[1]; // R2 block (phi + theta)
427}
428
430 m_jacobian = std::make_unique<mfem::BlockOperator>(m_reducedBlockOffsets);
431
432 m_jacobian->SetBlock(0, 1, m_MScaledReduced.get()); //<- (1+c)M (constant, reduced to only free DOFs)
433 m_jacobian->SetBlock(1, 0, m_negQ_mat.get()); //<- -(1+c)Q (constant, reduced to only free DOFs)
434 m_jacobian->SetBlock(1, 1, m_DScaledReduced.get()); //<- (1+c)D (constant, reduced to only free DOFs)
435}
436
438 mfem::Vector thetaStateValues(m_theta_ess_tdofs.first.Size());
439 for (int i = 0; i < m_theta_ess_tdofs.first.Size(); i++) {
440 thetaStateValues[i] = m_theta_ess_tdofs.second[i];
441 }
442 mfem::Vector phiStateValues(m_phi_ess_tdofs.first.Size());
443 for (int i = 0; i < m_phi_ess_tdofs.first.Size(); i++) {
444 phiStateValues[i] = m_phi_ess_tdofs.second[i]; // TODO: figure out if this needs to be normalized
445 }
446
447 mfem::Array<int> phiDofIndices(m_phi_ess_tdofs.first.Size());
448 for (int i = 0; i < m_phi_ess_tdofs.first.Size(); i++) {
449 phiDofIndices[i] = m_phi_ess_tdofs.first[i] + m_blockOffsets[1];
450 }
451
452 m_state.SetSize(m_blockOffsets.Last());
453 m_state = 0.0;
454 m_state.SetSubVector(m_theta_ess_tdofs.first, thetaStateValues);
455 m_state.SetSubVector(phiDofIndices, phiStateValues);
456
457}
458
459// PolytropeOperator: Private Helper Methods - Jacobian and Preconditioner Updates
460void PolytropeOperator::update_inverse_nonlinear_jacobian(const mfem::Operator &grad) const {
461 m_invNonlinearJacobian->SetOperator(grad);
462}
463
465 // TODO: This entire function could probably be refactored out
466 if (!m_isFinalized) {
467 MFEM_ABORT("PolytropeOperator::updateInverseSchurCompliment called before finalize");
468 }
469 if (m_invNonlinearJacobian == nullptr) {
470 MFEM_ABORT("PolytropeOperator::updateInverseSchurCompliment called before updateInverseNonlinearJacobian");
471 }
472 if (m_schurCompliment == nullptr) {
473 MFEM_ABORT("PolytropeOperator::updateInverseSchurCompliment called before updateInverseSchurCompliment");
474 }
475
476 m_schurCompliment->updateInverseNonlinearJacobian(*m_invNonlinearJacobian);
477
478 if (m_schurPreconditioner == nullptr) {
479 m_schurPreconditioner = std::make_unique<mfem::BlockDiagonalPreconditioner>(m_blockOffsets);
480 }
481
482 m_schurPreconditioner->SetDiagonalBlock(0, m_invNonlinearJacobian.get());
483 m_schurPreconditioner->SetDiagonalBlock(1, m_invSchurCompliment.get());
484
485}
486
491
492} // namespace polytrope
493} // namespace serif
const SchurCompliment & m_op
Reference to the SchurCompliment operator to be inverted.
GMRESInverter(const SchurCompliment &op)
Constructs a GMRESInverter.
mfem::GMRESSolver m_solver
GMRES solver instance.
void Mult(const mfem::Vector &x, mfem::Vector &y) const override
Applies the GMRES solver to approximate op^-1 * x.
std::unique_ptr< mfem::BilinearForm > m_S
Bilinear form S, used for least squares stabilization.
mfem::Array< int > m_freeDofs
Array of indices for free (non-essential) DOFs.
std::unique_ptr< mfem::MixedBilinearForm > m_Q
Bilinear form Q, coupling φ and θ.
std::unique_ptr< mfem::BlockDiagonalPreconditioner > m_schurPreconditioner
Block diagonal preconditioner for the system.
std::unique_ptr< mfem::SparseMatrix > m_Qmat
Sparse matrix representation of Q.
std::unique_ptr< SchurCompliment > m_schurCompliment
Schur complement S = D_reduced - Q_reduced * G_inv_reduced * M_reduced.
const mfem::BlockVector reconstruct_full_block_state_vector(const mfem::Vector &reducedState) const
void finalize(const mfem::Vector &initTheta)
Finalizes the operator setup. This must be called after setting essential true DOFs and before using ...
std::unique_ptr< mfem::SparseMatrix > m_QScaledReduced
Scaled Q matrix (free DOFs only, scaled by stabilization coefficient).
std::unique_ptr< mfem::MixedBilinearForm > m_M
Bilinear form M, coupling θ and φ.
void Mult(const mfem::Vector &xFree, mfem::Vector &yFree) const override
Applies the PolytropeOperator: y = F(x). This computes the residual of the nonlinear system.
void populate_free_dof_array()
Populates the internal array of free (non-essential) degree of freedom indices. This is called during...
std::unique_ptr< mfem::SparseMatrix > m_DScaledReduced
Scaled D matrix (free DOFs only, scaled by stabilization coefficient).
std::unique_ptr< mfem::NonlinearForm > m_f
Nonlinear form f, acting on θ.
void construct_reduced_block_offsets()
Constructs the block offsets for the reduced system. Called during finalize().
std::unique_ptr< mfem::SparseMatrix > m_MScaledReduced
Scaled M matrix (free DOFs only, scaled by stabilization coefficient).
mfem::Operator & GetGradient(const mfem::Vector &xFree) const override
Computes the Jacobian of the PolytropeOperator at a given state xFree. The Jacobian is of the form: J...
std::unique_ptr< mfem::SparseMatrix > m_ScaledSReduced
Scaled S matrix (free DOFs only, scaled by stabilization coefficient).
std::unique_ptr< mfem::SparseMatrix > m_gradReduced
Reduced gradient operator (G) for the nonlinear part f(θ).
PolytropeOperator(std::unique_ptr< mfem::MixedBilinearForm > M, std::unique_ptr< mfem::MixedBilinearForm > Q, std::unique_ptr< mfem::BilinearForm > D, std::unique_ptr< mfem::BilinearForm > S, std::unique_ptr< mfem::NonlinearForm > f, const mfem::Array< int > &blockOffsets)
Constructs the PolytropeOperator.
void update_preconditioner(const mfem::Operator &grad) const
Updates the preconditioner components. This involves updating the inverse nonlinear Jacobian and then...
mfem::Vector m_state
Full state vector [θ, φ]^T, including essential DOFs.
void set_essential_true_dofs(const serif::types::MFEMArrayPair &theta_ess_tdofs, const serif::types::MFEMArrayPair &phi_ess_tdofs)
Sets the essential true degrees of freedom for both θ and φ variables. Marks the operator as not fina...
const mfem::BlockOperator & get_jacobian_operator() const
— Getters for key internal state and operators —
mfem::Array< int > m_reducedBlockOffsets
Block offsets for the reduced system (free DOFs).
const mfem::Array< int > m_blockOffsets
Block offsets for the full system [0, size(θ), size(θ)+size(φ)].
serif::types::MFEMArrayPairSet get_essential_true_dofs() const
Gets the currently set essential true degrees of freedom.
const mfem::Vector & reconstruct_full_state_vector(const mfem::Vector &reducedState) const
Reconstructs the full state vector (including essential DOFs) from a reduced state vector (free DOFs)...
void scatter_boundary_conditions()
Scatters the values of essential boundary conditions into the full state vector. Called during finali...
int get_reduced_system_size() const
Gets the size of the reduced system (number of free DOFs). Asserts that the operator is finalized.
std::unique_ptr< GMRESInverter > m_invSchurCompliment
Approximate inverse of the Schur complement.
void construct_matrix_representations()
Constructs the sparse matrix representations of M, Q, and D, and their reduced forms....
serif::types::MFEMArrayPair m_phi_ess_tdofs
Essential true DOFs for φ (indices and values).
std::unique_ptr< mfem::SparseMatrix > m_MReduced
Reduced M matrix (free DOFs only).
static constexpr double m_stabilizationCoefficient
Stabilization coefficient for the system, used to more tightly couple ∇θ and φ.
serif::types::MFEMArrayPair m_theta_ess_tdofs
Essential true DOFs for θ (indices and values).
std::unique_ptr< mfem::SparseMatrix > m_Smat
std::unique_ptr< mfem::Solver > m_invNonlinearJacobian
Solver for the inverse of the G block (gradient of f(θ)_reduced).
std::unique_ptr< mfem::SparseMatrix > m_Dmat
Sparse matrix representation of D.
std::unique_ptr< mfem::SparseMatrix > m_Mmat
Sparse matrix representation of M.
void update_inverse_schur_compliment() const
Updates the inverse Schur complement operator and its components. This is typically called after the ...
static constexpr double m_IncrementedStabilizationCoefficient
1 + Stabilization coefficient for the system, used to more tightly couple ∇θ and φ.
bool m_isFinalized
Flag indicating if finalize() has been called.
mfem::BlockDiagonalPreconditioner & get_preconditioner() const
Gets the block diagonal preconditioner for the Schur complement system. Asserts that the operator is ...
std::unique_ptr< mfem::SparseMatrix > m_SReduced
Reduced S matrix (free DOFs only, used for least squares stabilization).
void construct_jacobian_constant_terms()
Constructs the constant terms of the Jacobian operator (M, -Q, D). The (0,0) block (gradient of f) is...
std::unique_ptr< mfem::SparseMatrix > m_DReduced
Reduced D matrix (free DOFs only).
std::unique_ptr< mfem::ScaledOperator > m_negQ_mat
Scaled operator for -Q_reduced.
void update_inverse_nonlinear_jacobian(const mfem::Operator &grad) const
Updates the solver for the inverse of the nonlinear Jacobian block (G_00).
std::unique_ptr< mfem::BlockOperator > m_jacobian
Jacobian operator J = [G M; -Q D]_reduced.
std::unique_ptr< mfem::SparseMatrix > m_QReduced
Reduced Q matrix (free DOFs only).
std::unique_ptr< mfem::BilinearForm > m_D
Bilinear form D, acting on φ.
Represents the Schur complement operator used in the solution process.
SchurCompliment(const mfem::SparseMatrix &QOp, const mfem::SparseMatrix &DOp, const mfem::SparseMatrix &MOp, const mfem::Solver &GradInvOp)
Constructs a SchurCompliment operator.
const mfem::Solver * m_GradInvOp
Pointer to the inverse of the gradient operator.
int m_nPhi
Dimension related to the phi variable (typically number of rows/cols of D).
void updateInverseNonlinearJacobian(const mfem::Solver &gradInv)
Updates the inverse of the nonlinear Jacobian (GradInv).
void Mult(const mfem::Vector &x, mfem::Vector &y) const override
Applies the Schur complement operator: y = (D - Q * GradInv * M) * x.
int m_nTheta
Dimension related to the theta variable (typically number of rows of M).
const mfem::SparseMatrix * m_QOp
Pointer to the Q matrix operator.
const mfem::SparseMatrix * m_DOp
Pointer to the D matrix operator.
const mfem::SparseMatrix * m_MOp
Pointer to the M matrix operator.
void updateConstantTerms(const mfem::SparseMatrix &QOp, const mfem::SparseMatrix &DOp, const mfem::SparseMatrix &MOp)
Updates the constant matrix terms (Q, D, M).
void SetOperator(const mfem::SparseMatrix &QOp, const mfem::SparseMatrix &DOp, const mfem::SparseMatrix &MOp, const mfem::Solver &GradInvOp)
Sets all operators for the Schur complement.
std::pair< mfem::Array< int >, mfem::Array< double > > MFEMArrayPair
Definition 4DSTARTypes.h:27
std::pair< MFEMArrayPair, MFEMArrayPair > MFEMArrayPairSet
Definition 4DSTARTypes.h:28
mfem::SparseMatrix build_reduced_matrix(const mfem::SparseMatrix &matrix, const mfem::Array< int > &trialEssentialDofs, const mfem::Array< int > &testEssentialDofs)
Definition utilities.cpp:6