2025-04-21 08:56:45 -04:00
|
|
|
|
/* ***********************************************************************
|
|
|
|
|
|
//
|
|
|
|
|
|
// Copyright (C) 2025 -- The 4D-STAR Collaboration
|
|
|
|
|
|
// File Author: Emily Boudreaux
|
|
|
|
|
|
// Last Modified: April 21, 2025
|
|
|
|
|
|
//
|
|
|
|
|
|
// 4DSSE is free software; you can use it and/or modify
|
|
|
|
|
|
// it under the terms and restrictions the GNU General Library Public
|
|
|
|
|
|
// License version 3 (GPLv3) as published by the Free Software Foundation.
|
|
|
|
|
|
//
|
|
|
|
|
|
// 4DSSE is distributed in the hope that it will be useful,
|
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
|
|
// See the GNU Library General Public License for more details.
|
|
|
|
|
|
//
|
|
|
|
|
|
// You should have received a copy of the GNU Library General Public License
|
|
|
|
|
|
// along with this software; if not, write to the Free Software
|
|
|
|
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
|
//
|
|
|
|
|
|
// *********************************************************************** */
|
2025-04-21 08:54:59 -04:00
|
|
|
|
#pragma once
|
2025-02-19 14:35:15 -05:00
|
|
|
|
|
|
|
|
|
|
#include "mfem.hpp"
|
|
|
|
|
|
#include <memory>
|
2025-04-02 14:57:37 -04:00
|
|
|
|
#include <utility>
|
2025-02-19 14:35:15 -05:00
|
|
|
|
|
2025-04-02 14:57:37 -04:00
|
|
|
|
#include "integrators.h"
|
2025-04-09 15:17:55 -04:00
|
|
|
|
#include "4DSTARTypes.h"
|
2025-06-05 12:37:00 -04:00
|
|
|
|
#include "polytropeOperator.h"
|
2025-03-03 09:54:13 -05:00
|
|
|
|
#include "config.h"
|
2025-04-30 07:28:00 -04:00
|
|
|
|
#include "meshIO.h"
|
2025-03-03 09:54:13 -05:00
|
|
|
|
#include "probe.h"
|
|
|
|
|
|
#include "quill/Logger.h"
|
2025-02-19 14:35:15 -05:00
|
|
|
|
|
2025-04-09 15:17:55 -04:00
|
|
|
|
|
2025-03-03 09:54:13 -05:00
|
|
|
|
namespace laneEmden {
|
2025-04-21 09:09:09 -04:00
|
|
|
|
double a (const int k, const double n);
|
|
|
|
|
|
double c(const int m, const double n);
|
|
|
|
|
|
double thetaSeriesExpansion(const double xi, const double n, const int order);
|
2025-03-03 09:54:13 -05:00
|
|
|
|
}
|
2025-02-19 14:35:15 -05:00
|
|
|
|
|
2025-04-21 08:35:29 -04:00
|
|
|
|
// Struct to persist lifetime of the linear and nonlinear solvers
|
|
|
|
|
|
struct solverBundle {
|
2025-04-21 08:54:59 -04:00
|
|
|
|
mfem::GMRESSolver solver; // Must be first so it lives longer than the newton solver
|
|
|
|
|
|
mfem::NewtonSolver newton; // Must be second so that when it is destroyed the solver is still alive preventing a double delete
|
2025-04-21 08:35:29 -04:00
|
|
|
|
};
|
|
|
|
|
|
|
2025-04-23 09:13:30 -04:00
|
|
|
|
struct formBundle {
|
2025-04-23 11:00:32 -04:00
|
|
|
|
std::unique_ptr<mfem::MixedBilinearForm> M; //<-- M ∫∇ψ^θ·N^φ dV
|
|
|
|
|
|
std::unique_ptr<mfem::MixedBilinearForm> Q; //<-- Q ∫ψ^φ·∇N^θ dV
|
|
|
|
|
|
std::unique_ptr<mfem::BilinearForm> D; //<-- D ∫ψ^φ·N^φ dV
|
2025-06-11 10:43:09 -04:00
|
|
|
|
std::unique_ptr<mfem::BilinearForm> S; //<-- S ∫∇ψ^θ·∇N^θ dV
|
2025-04-23 11:00:32 -04:00
|
|
|
|
std::unique_ptr<mfem::NonlinearForm> f; //<-- f(θ) ∫ψ^θ·θ^n dV
|
2025-04-23 09:13:30 -04:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class PolySolver final{
|
2025-04-02 14:57:37 -04:00
|
|
|
|
public: // Public methods
|
2025-04-21 09:22:21 -04:00
|
|
|
|
PolySolver(const double n, const double order);
|
2025-04-02 14:57:37 -04:00
|
|
|
|
~PolySolver();
|
|
|
|
|
|
|
2025-04-09 15:17:55 -04:00
|
|
|
|
void solve() const;
|
2025-04-02 14:57:37 -04:00
|
|
|
|
|
2025-04-21 08:54:59 -04:00
|
|
|
|
double getN() const { return m_polytropicIndex; }
|
|
|
|
|
|
double getOrder() const { return m_feOrder; }
|
2025-04-30 07:28:00 -04:00
|
|
|
|
mfem::Mesh& getMesh() const { return m_mesh; }
|
2025-04-21 08:54:59 -04:00
|
|
|
|
mfem::GridFunction& getSolution() const { return *m_theta; }
|
2025-04-02 14:57:37 -04:00
|
|
|
|
|
|
|
|
|
|
private: // Private Attributes
|
2025-04-30 07:28:00 -04:00
|
|
|
|
Config& m_config;
|
|
|
|
|
|
Probe::LogManager& m_logManager;
|
|
|
|
|
|
quill::Logger* m_logger;
|
2025-04-02 14:57:37 -04:00
|
|
|
|
double m_polytropicIndex, m_feOrder;
|
2025-04-30 07:28:00 -04:00
|
|
|
|
|
|
|
|
|
|
mfem::Mesh& m_mesh;
|
2025-04-02 14:57:37 -04:00
|
|
|
|
std::unique_ptr<mfem::H1_FECollection> m_fecH1;
|
|
|
|
|
|
std::unique_ptr<mfem::RT_FECollection> m_fecRT;
|
2025-02-19 14:35:15 -05:00
|
|
|
|
|
2025-04-02 14:57:37 -04:00
|
|
|
|
std::unique_ptr<mfem::FiniteElementSpace> m_feTheta;
|
|
|
|
|
|
std::unique_ptr<mfem::FiniteElementSpace> m_fePhi;
|
2025-02-19 14:35:15 -05:00
|
|
|
|
|
2025-04-02 14:57:37 -04:00
|
|
|
|
std::unique_ptr<mfem::GridFunction> m_theta;
|
|
|
|
|
|
std::unique_ptr<mfem::GridFunction> m_phi;
|
2025-02-19 14:35:15 -05:00
|
|
|
|
|
2025-04-02 14:57:37 -04:00
|
|
|
|
std::unique_ptr<PolytropeOperator> m_polytropOperator;
|
2025-03-03 09:54:13 -05:00
|
|
|
|
|
2025-04-09 15:17:55 -04:00
|
|
|
|
std::unique_ptr<mfem::OperatorJacobiSmoother> m_prec;
|
|
|
|
|
|
|
2025-04-02 14:57:37 -04:00
|
|
|
|
|
|
|
|
|
|
private: // Private methods
|
2025-04-30 07:28:00 -04:00
|
|
|
|
PolySolver(mfem::Mesh& mesh, double n, double order);
|
|
|
|
|
|
|
|
|
|
|
|
static mfem::Mesh& prepareMesh(double n);
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-04-02 14:57:37 -04:00
|
|
|
|
void assembleBlockSystem();
|
2025-04-23 09:13:30 -04:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @breif Compute the block offsets for the operator. These are the offsets that define which dofs belong to which variable.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @details
|
|
|
|
|
|
*
|
|
|
|
|
|
* Create the block offsets. These define the start of each block in the combined vector.
|
|
|
|
|
|
* Block offsets will be [0, thetaDofs, thetaDofs + phiDofs].
|
2025-04-23 11:00:32 -04:00
|
|
|
|
* The interpretation of this is that each block tells the operator where in the flattened (1D) vector
|
2025-04-23 09:13:30 -04:00
|
|
|
|
* the degrees of freedom or coefficients for that free parameter start and end. I.e.
|
|
|
|
|
|
* we know that in any flattened vector will have a size thetaDofs + phiDofs. The theta dofs will span
|
|
|
|
|
|
* from blockOffsets[0] -> blockOffsets[1] and the phiDofs will span from blockOffsets[1] -> blockOffsets[2].
|
|
|
|
|
|
*
|
|
|
|
|
|
* This is the same for matrices only in 2D (rows and columns)
|
|
|
|
|
|
*
|
|
|
|
|
|
* The key point here is that this is fundamentally an accounting structure, it is here to keep track of what
|
|
|
|
|
|
* parts of vectors and matrices belong to which variable.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Also note that we use VSize rather than Size. Size referees to the number of true dofs. That is the dofs which
|
|
|
|
|
|
* still are present in the system after eliminating boundary conditions. This is the wrong size to use if we are
|
|
|
|
|
|
* trying to account for the true size of the system. VSize on the other hand refers to the total number of dofs.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @return blockOffsets The offsets for the blocks in the operator
|
2025-04-25 10:32:06 -04:00
|
|
|
|
*
|
|
|
|
|
|
* @pre m_feTheta and m_fePhi must be valid, populated FiniteElementSpaces.
|
2025-04-23 09:13:30 -04:00
|
|
|
|
*/
|
|
|
|
|
|
mfem::Array<int> computeBlockOffsets() const;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @breif Build the individual forms for the block operator (M, Q, D, and f)
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param blockOffsets The offsets for the blocks in the operator
|
2025-04-23 11:00:32 -04:00
|
|
|
|
* @return forms The forms for the block operator
|
2025-04-23 09:13:30 -04:00
|
|
|
|
*
|
|
|
|
|
|
* @note These forms are build exactly how they are defined in the derivation. This means that Mform -> M not -M and Qform -> Q not -Q.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @details
|
|
|
|
|
|
* Computes the block offsets
|
|
|
|
|
|
* \f$\{0,\;|\theta|,\;|\theta|+|\phi|\}\f$, then builds and finalizes
|
|
|
|
|
|
* the MixedBilinearForms \c Mform, \c Qform and the BilinearForm \c Dform,
|
|
|
|
|
|
* plus the NonlinearForm \c fform. Finally, these are handed off to
|
|
|
|
|
|
* \c PolytropeOperator along with the offsets.
|
|
|
|
|
|
*
|
|
|
|
|
|
* The discretized weak form is
|
|
|
|
|
|
* \f[
|
|
|
|
|
|
* R(X)
|
|
|
|
|
|
* = \begin{pmatrix}
|
|
|
|
|
|
* f(\theta) - M\,\theta \\[6pt]
|
|
|
|
|
|
* D\,\theta - Q\,\phi
|
|
|
|
|
|
* \end{pmatrix}
|
|
|
|
|
|
* = \mathbf{0},
|
|
|
|
|
|
* \f]
|
|
|
|
|
|
* with
|
|
|
|
|
|
* \f[
|
|
|
|
|
|
* M = \int \nabla\psi^\theta \;\cdot\; N^\phi \,dV,\quad
|
|
|
|
|
|
* D = \int \psi^\phi \;\cdot\; N^\phi \,dV,
|
|
|
|
|
|
* \quad
|
|
|
|
|
|
* Q = \int \psi^\phi \;\cdot\; \nabla N^\theta \,dV,
|
|
|
|
|
|
* \quad
|
|
|
|
|
|
* f(\theta) = \int \psi^\theta \;\cdot\; \theta^n \,dV.
|
|
|
|
|
|
* \f]
|
|
|
|
|
|
*
|
|
|
|
|
|
* @note MFEM’s MixedVectorWeakDivergenceIntegrator implements
|
2025-04-23 11:00:32 -04:00
|
|
|
|
* \f$ -\nabla\!\cdot \f$ so we supply a –1 coefficient to make
|
2025-04-23 09:13:30 -04:00
|
|
|
|
* `Mform` represent the +M from the derivation. The single negation
|
|
|
|
|
|
* in `PolytropeOperator` then restores the final block sign.
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
* @pre \c m_feTheta and \c m_fePhi must be valid, populated FiniteElementSpaces.
|
|
|
|
|
|
* @post \c m_polytropOperator is constructed with assembled forms and offsets.
|
|
|
|
|
|
*
|
|
|
|
|
|
*/
|
|
|
|
|
|
std::unique_ptr<formBundle> buildIndividualForms(const mfem::Array<int>& blockOffsets);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief Assemble and finalize the form (Must be a form that can be assembled and finalized)
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param f form which is to be assembled and finalized
|
|
|
|
|
|
*
|
|
|
|
|
|
* @pre f is a valid form that can be assembled and finalized (Such as Bilinear or MixedBilinearForm)
|
|
|
|
|
|
* @post f is assembled and finalized
|
|
|
|
|
|
*/
|
|
|
|
|
|
static void assembleAndFinalizeForm(auto &f);
|
2025-04-09 15:17:55 -04:00
|
|
|
|
SSE::MFEMArrayPairSet getEssentialTrueDof() const;
|
|
|
|
|
|
std::pair<mfem::Array<int>, mfem::Array<int>> findCenterElement() const;
|
|
|
|
|
|
void setInitialGuess() const;
|
|
|
|
|
|
void saveAndViewSolution(const mfem::BlockVector& state_vector) const;
|
2025-04-21 08:35:29 -04:00
|
|
|
|
solverBundle setupNewtonSolver() const;
|
2025-04-21 09:05:34 -04:00
|
|
|
|
void setOperatorEssentialTrueDofs() const;
|
2025-02-19 14:35:15 -05:00
|
|
|
|
|
2025-04-09 15:17:55 -04:00
|
|
|
|
void LoadSolverUserParams(double &newtonRelTol, double &newtonAbsTol, int &newtonMaxIter, int &newtonPrintLevel,
|
|
|
|
|
|
double &gmresRelTol, double &gmresAbsTol, int &gmresMaxIter, int &gmresPrintLevel) const;
|
2025-02-19 14:35:15 -05:00
|
|
|
|
|
2025-04-23 11:00:32 -04:00
|
|
|
|
static void GetDofCoordinates(const mfem::FiniteElementSpace &fes, const std::string& filename);
|
2025-04-23 09:13:30 -04:00
|
|
|
|
|
2025-06-05 15:13:50 -04:00
|
|
|
|
};
|