40#include "quill/LogMacros.h"
47 double a (
const int k,
const double n) {
48 if ( k == 0 ) {
return 1; }
49 if ( k == 1 ) {
return 0; }
50 else {
return -(
c(k-2, n)/(std::pow(k, 2)+k)); }
54 double c(
const int m,
const double n) {
55 if ( m == 0 ) {
return std::pow(
a(0, n), n); }
57 double termOne = 1.0/(m*
a(0, n));
59 for (
int k = 1; k <= m; k++) {
60 acc += (k*n-m+k)*
a(k, n)*
c(m-k, n);
68 for (
int k = 0; k < order; k++) {
69 acc +=
a(k, n) * std::pow(xi, k);
93 m_phi = std::make_unique<mfem::GridFunction>(
m_fePhi.get());
102 if (n > 4.99 || n < 0.0) {
103 throw std::runtime_error(
"The polytropic index n must be less than 5.0 and greater than 0.0. Currently it is " + std::to_string(n));
107 const auto &meshIO = std::get<std::unique_ptr<serif::mesh::MeshIO>>(genericResource);
109 return meshIO->GetMesh();
143 mfem::Array<int> blockOffsets;
144 blockOffsets.SetSize(3);
147 blockOffsets[2] =
m_fePhi->GetVSize();
148 blockOffsets.PartialSum();
154 auto forms = std::make_unique<formBundle>(
157 std::make_unique<mfem::BilinearForm>(
m_fePhi.get()),
158 std::make_unique<mfem::BilinearForm>(
m_feTheta.get()),
159 std::make_unique<mfem::NonlinearForm>(
m_feTheta.get())
163 forms->M->AddDomainIntegrator(
new mfem::MixedVectorWeakDivergenceIntegrator());
164 forms->Q->AddDomainIntegrator(
new mfem::MixedVectorGradientIntegrator());
165 forms->D->AddDomainIntegrator(
new mfem::VectorFEMassIntegrator());
166 forms->S->AddDomainIntegrator(
new mfem::DiffusionIntegrator());
196 const auto thetaVec =
static_cast<mfem::Vector
>(*m_theta);
197 const auto phiVec =
static_cast<mfem::Vector
>(*m_phi);
205 mfem::Vector x_full(full_block_offsets.Last());
206 mfem::BlockVector x_full_block(x_full, full_block_offsets);
207 x_full_block.GetBlock(0) = thetaVec;
208 x_full_block.GetBlock(1) = phiVec;
213 x_full.GetSubVector(freeDofs, x_free);
221 sb.
newton.Mult(zero_rhs, x_free);
224 mfem::BlockVector solution =
m_polytropOperator->reconstruct_full_block_state_vector(x_free);
231 mfem::Array<int> theta_ess_tdof_list;
232 mfem::Array<int> phi_ess_tdof_list;
234 mfem::Array<int> thetaCenterDofs, phiCenterDofs;
235 mfem::Array<double> thetaCenterVals, phiCenterVals;
237 thetaCenterVals.SetSize(thetaCenterDofs.Size());
241 thetaCenterVals = 1.0;
243 mfem::Array<int> ess_brd(
m_mesh.bdr_attributes.Max());
246 mfem::Array<double> thetaSurfaceVals, phiSurfaceVals;
247 m_feTheta->GetEssentialTrueDofs(ess_brd, theta_ess_tdof_list);
248 m_fePhi->GetEssentialTrueDofs(ess_brd, phi_ess_tdof_list);
250 thetaSurfaceVals.SetSize(theta_ess_tdof_list.Size());
251 thetaSurfaceVals = 0.0;
252 phiSurfaceVals.SetSize(phi_ess_tdof_list.Size());
256 theta_ess_tdof_list.Append(thetaCenterDofs);
257 thetaSurfaceVals.Append(thetaCenterVals);
270 mfem::Array<int> thetaCenterDofs;
271 mfem::Array<int> phiCenterDofs;
274 int center_vertex_idx = -1;
275 constexpr double tol = 1e-9;
277 for (
int i = 0; i <
m_mesh.GetNV(); ++i) {
278 const double* vertex_coords =
m_mesh.GetVertex(i);
279 if (std::abs(vertex_coords[0]) < tol &&
280 std::abs(vertex_coords[1]) < tol &&
281 std::abs(vertex_coords[2]) < tol) {
283 center_vertex_idx = i;
288 if (center_vertex_idx == -1) {
289 MFEM_ABORT(
"Could not find the center vertex at [0,0,0]. Check mesh construction.");
294 m_feTheta->GetVertexDofs(center_vertex_idx, thetaCenterDofs);
297 mfem::Array<int> central_element_ids;
300 mfem::Table* vertex_to_elements_table =
m_mesh.GetVertexToElementTable();
301 vertex_to_elements_table->Finalize();
302 mfem::Array<int> element_ids;
303 vertex_to_elements_table->GetRow(center_vertex_idx, element_ids);
304 delete vertex_to_elements_table;
306 for (
int i = 0; i < element_ids.Size(); ++i) {
307 int element_id = element_ids[i];
308 mfem::Array<int> tempDofs;
309 m_fePhi->GetElementDofs(element_id, tempDofs);
312 for (
int j = 0; j < tempDofs.Size(); ++j) {
313 int dof = tempDofs[j];
317 phiCenterDofs.Append(dof);
322 phiCenterDofs.Sort();
323 phiCenterDofs.Unique();
325 return std::make_pair(thetaCenterDofs, phiCenterDofs);
330 mfem::FunctionCoefficient thetaInitGuess (
331 [
this](
const mfem::Vector &x) {
332 const double r = x.Norml2();
342 mfem::VectorFunctionCoefficient phiSurfaceVectors (
m_mesh.SpaceDimension(),
343 [
this](
const mfem::Vector &x, mfem::Vector &y) {
344 const double r = x.Norml2();
347 y.SetSize(m_mesh.SpaceDimension());
349 y *= polycoeff::thetaSurfaceFlux(m_polytropicIndex);
353 mfem::Array<int> ess_brd(
m_mesh.bdr_attributes.Max());
357 mfem::ConstantCoefficient surfacePotential(0);
359 m_theta->ProjectCoefficient(thetaInitGuess);
360 m_theta->ProjectBdrCoefficient(surfacePotential, ess_brd);
362 mfem::GradientGridFunctionCoefficient phiInitGuess (
m_theta.get());
363 m_phi->ProjectCoefficient(phiInitGuess);
372 m_phi->ProjectBdrCoefficientNormal(phiSurfaceVectors, ess_brd);
376 for (
int i = 0; i < phiCenterDofs.Size(); ++i)
378 (*m_phi)(phiCenterDofs[i]) = 0.0;
381 if (
m_config.get<
bool>(
"Poly:Solver:ViewInitialGuess",
false)) {
389 mfem::BlockVector x_block(
const_cast<mfem::BlockVector&
>(state_vector),
m_polytropOperator->get_block_offsets());
390 mfem::Vector& x_theta = x_block.GetBlock(0);
391 mfem::Vector& x_phi = x_block.GetBlock(1);
393 if (
m_config.get<
bool>(
"Poly:Output:View",
false)) {
399 if (
m_config.get<
bool>(
"Poly:Output:1D:Save",
true)) {
400 const auto solutionPath =
m_config.get<std::string>(
"Poly:Output:1D:Path",
"polytropeSolution_1D.csv");
401 auto derivSolPath =
"d" + solutionPath;
403 const auto rayCoLatitude =
m_config.get<
double>(
"Poly:Output:1D:RayCoLatitude", 0.0);
404 const auto rayLongitude =
m_config.get<
double>(
"Poly:Output:1D:RayLongitude", 0.0);
405 const auto raySamples =
m_config.get<
int>(
"Poly:Output:1D:RaySamples", 100);
407 const std::vector rayDirection = {rayCoLatitude, rayLongitude};
420 double &gmresRelTol,
double &gmresAbsTol,
int &gmresMaxIter,
int &gmresPrintLevel)
const {
421 newtonRelTol =
m_config.get<
double>(
"Poly:Solver:Newton:RelTol", 1.e-4);
422 newtonAbsTol =
m_config.get<
double>(
"Poly:Solver:Newton:AbsTol", 1.e-6);
423 newtonMaxIter =
m_config.get<
int>(
"Poly:Solver:Newton:MaxIter", 10);
424 newtonPrintLevel =
m_config.get<
int>(
"Poly:Solver:Newton:PrintLevel", 3);
426 gmresRelTol =
m_config.get<
double>(
"Poly:Solver:GMRES:RelTol", 1.e-12);
427 gmresAbsTol =
m_config.get<
double>(
"Poly:Solver:GMRES:AbsTol", 1.e-12);
428 gmresMaxIter =
m_config.get<
int>(
"Poly:Solver:GMRES:MaxIter", 200);
429 gmresPrintLevel =
m_config.get<
int>(
"Poly:Solver:GMRES:PrintLevel", -1);
431 LOG_DEBUG(
m_logger,
"Newton Solver (relTol: {:0.2E}, absTol: {:0.2E}, maxIter: {}, printLevel: {})", newtonRelTol, newtonAbsTol, newtonMaxIter, newtonPrintLevel);
432 LOG_DEBUG(
m_logger,
"GMRES Solver (relTol: {:0.2E}, absTol: {:0.2E}, maxIter: {}, printLevel: {})", gmresRelTol, gmresAbsTol, gmresMaxIter, gmresPrintLevel);
436 mfem::Mesh *
mesh = fes.GetMesh();
438 std::ofstream outputFile(filename, std::ios::out | std::ios::trunc);
439 outputFile <<
"dof,R,r,x,y,z" <<
'\n';
441 const int nElements =
mesh->GetNE();
444 mfem::IntegrationPoint ipZero;
445 double p[3] = {0.0, 0.0, 0.0};
448 for (
int i = 0; i < nElements; i++) {
449 mfem::Array<int> elemDofs;
450 fes.GetElementDofs(i, elemDofs);
451 mfem::ElementTransformation* T =
mesh->GetElementTransformation(i);
452 mfem::Vector physCoord(3);
453 T->Transform(ipZero, physCoord);
454 for (
int dofID = 0; dofID < elemDofs.Size(); dofID++) {
455 if (elemDofs[dofID] < 0) {
456 actual_idx = -elemDofs[dofID] - 1;
458 actual_idx = elemDofs[dofID];
460 outputFile << actual_idx;
461 if (dofID != elemDofs.Size() - 1) {
467 outputFile << r <<
"," << physCoord.Norml2() <<
"," << physCoord[0] <<
"," << physCoord[1] <<
"," << physCoord[2] <<
'\n';
474 double newtonRelTol, newtonAbsTol, gmresRelTol, gmresAbsTol;
475 int newtonMaxIter, newtonPrintLevel, gmresMaxIter, gmresPrintLevel;
476 LoadSolverUserParams(newtonRelTol, newtonAbsTol, newtonMaxIter, newtonPrintLevel, gmresRelTol, gmresAbsTol,
477 gmresMaxIter, gmresPrintLevel);
480 solver.
solver.SetRelTol(gmresRelTol);
481 solver.
solver.SetAbsTol(gmresAbsTol);
482 solver.
solver.SetMaxIter(gmresMaxIter);
483 solver.
solver.SetPrintLevel(gmresPrintLevel);
487 solver.
newton.SetRelTol(newtonRelTol);
488 solver.
newton.SetAbsTol(newtonAbsTol);
489 solver.
newton.SetMaxIter(newtonMaxIter);
490 solver.
newton.SetPrintLevel(newtonPrintLevel);
void saveAndViewSolution(const mfem::BlockVector &state_vector) const
Saves the 1D radial solution and optionally displays the 2D/3D solution using GLVis.
~PolySolver()
Destructor for PolySolver.
double m_polytropicIndex
The polytropic index .
std::unique_ptr< mfem::H1_FECollection > m_fecH1
Finite Element Collection for (H1 elements).
serif::types::MFEMArrayPairSet getEssentialTrueDof() const
Computes the essential true degrees of freedom (DOFs) and their values for boundary conditions.
void solve() const
Solves the polytropic system.
PolySolver(const double n, const double order)
double m_feOrder
The polynomial order for H1 elements ( ). RT elements for are of order m_feOrder - 1.
std::unique_ptr< mfem::GridFunction > m_phi
Grid function for the vector flux .
static void GetDofCoordinates(const mfem::FiniteElementSpace &fes, const std::string &filename)
Utility function to get and save the coordinates of degrees of freedom for a finite element space.
static void assembleAndFinalizeForm(auto &f)
Assemble and finalize a given MFEM form.
mfem::Array< int > computeBlockOffsets() const
Compute the block offsets for the operator. These are the offsets that define which dofs belong to wh...
solverBundle setupNewtonSolver() const
Sets up the Newton solver and its associated linear solver (GMRES).
quill::Logger * m_logger
Pointer to the specific logger instance for this class.
std::unique_ptr< PolytropeOperator > m_polytropOperator
The main nonlinear operator for the mixed system.
void assembleBlockSystem()
Assembles the block system operator m_polytropOperator.
void LoadSolverUserParams(double &newtonRelTol, double &newtonAbsTol, int &newtonMaxIter, int &newtonPrintLevel, double &gmresRelTol, double &gmresAbsTol, int &gmresMaxIter, int &gmresPrintLevel) const
Loads Newton and GMRES solver parameters from the configuration.
mfem::Mesh & m_mesh
Reference to the computational mesh (owned by ResourceManager).
void setOperatorEssentialTrueDofs() const
Sets the essential true DOFs on the PolytropeOperator.
std::unique_ptr< mfem::FiniteElementSpace > m_fePhi
Finite Element Space for .
std::unique_ptr< formBundle > buildIndividualForms(const mfem::Array< int > &blockOffsets)
Build the individual forms for the block operator (M, Q, D, S, and f).
static mfem::Mesh & prepareMesh(double n)
Prepares the mesh for the simulation.
std::pair< mfem::Array< int >, mfem::Array< int > > findCenterElement() const
Finds the degrees of freedom (DOFs) associated with the geometric center (origin) of the mesh.
std::unique_ptr< mfem::GridFunction > m_theta
Grid function for the scalar potential .
void setInitialGuess() const
Sets the initial guess for the solution variables and .
std::unique_ptr< mfem::FiniteElementSpace > m_feTheta
Finite Element Space for .
serif::probe::LogManager & m_logManager
Reference to the global log manager instance.
std::unique_ptr< mfem::RT_FECollection > m_fecRT
Finite Element Collection for (Raviart-Thomas elements).
serif::config::Config & m_config
Reference to the global configuration manager instance.
A class for nonlinear power integrator.
const types::Resource & getResource(const std::string &name) const
Gets a resource by name.
static ResourceManager & getInstance()
Gets the singleton instance of the ResourceManager.
A collection of utilities for working with MFEM and solving the lane-emden equation.
Namespace for Lane-Emden equation related utility functions.
double thetaSeriesExpansion(const double xi, const double n, const int order)
double a(const int k, const double n)
Computes the coefficient for the Lane-Emden series expansion.
double c(const int m, const double n)
Computes the auxiliary coefficient used in determining .
double thetaSurfaceFlux(const double n)
double x1(const double n)
The Probe namespace contains utility functions for debugging and logging.
double getMeshRadius(mfem::Mesh &mesh)
void glVisView(mfem::GridFunction &u, mfem::Mesh &mesh, const std::string &windowTitle, const std::string &keyset)
Visualize a solution using GLVis.
std::pair< std::vector< double >, std::vector< double > > getRaySolution(mfem::GridFunction &u, mfem::Mesh &mesh, const std::vector< double > &rayDirection, int numSamples, std::string filename)
std::variant< std::unique_ptr< opat::OPAT >, std::unique_ptr< serif::mesh::MeshIO >, std::unique_ptr< serif::eos::EOSio > > Resource
A variant type that can hold different types of resources.
std::pair< mfem::Array< int >, mfem::Array< double > > MFEMArrayPair
std::pair< MFEMArrayPair, MFEMArrayPair > MFEMArrayPairSet
Defines types and functions for managing resources.
Structure to manage the lifetime of MFEM solver objects.
mfem::GMRESSolver solver
The linear solver (e.g., GMRES). Must be declared first.
mfem::NewtonSolver newton
The nonlinear solver (e.g., Newton). Must be declared second.