feat(poly): moved to a block form for poly

essential dofs can be applied to both theta and phi (grad theta) if we move to a block form. I have done this derivation and made that change so that we can properly apply the central boundary condition to the slope
This commit is contained in:
2025-04-02 14:57:37 -04:00
parent 407eef4e48
commit e3afe90f37
12 changed files with 640 additions and 731 deletions

View File

@@ -19,20 +19,23 @@
#
# *********************************************************************** #
polyutils_sources = files(
'private/polyIO.cpp',
'private/polyMFEMUtils.cpp'
'private/integrators.cpp',
'private/operator.cpp'
)
polyutils_headers = files(
'public/polyIO.h',
'public/polyMFEMUtils.h'
)
dependencies = [
mfem_dep,
macros_dep,
probe_dep,
quill_dep,
config_dep,
]
libpolyutils = static_library('polyutils',
polyutils_sources,
include_directories : include_directories('./public'),
cpp_args: ['-fvisibility=default'],
dependencies: [mfem_dep, macros_dep, probe_dep, quill_dep, config_dep],
dependencies: dependencies,
install: true
)
@@ -40,5 +43,5 @@ polyutils_dep = declare_dependency(
include_directories : include_directories('./public'),
link_with : libpolyutils,
sources : polyutils_sources,
dependencies : [mfem_dep, macros_dep, probe_dep, quill_dep, config_dep]
dependencies : dependencies
)

View File

@@ -0,0 +1,118 @@
/* ***********************************************************************
//
// Copyright (C) 2025 -- The 4D-STAR Collaboration
// File Author: Emily Boudreaux
// Last Modified: March 19, 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
//
// *********************************************************************** */
#include "mfem.hpp"
#include <cmath>
#include <vector>
#include <limits>
#include <stdexcept>
#include "quill/LogMacros.h"
#include "integrators.h"
#include "debug.h"
namespace polyMFEMUtils {
NonlinearPowerIntegrator::NonlinearPowerIntegrator(
mfem::Coefficient &coeff,
double n) :
m_coeff(coeff),
m_polytropicIndex(n) {}
void NonlinearPowerIntegrator::AssembleElementVector(
const mfem::FiniteElement &el,
mfem::ElementTransformation &Trans,
const mfem::Vector &elfun,
mfem::Vector &elvect) {
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 3);
int dof = el.GetDof();
elvect.SetSize(dof);
elvect = 0.0;
mfem::Vector shape(dof);
for (int iqp = 0; iqp < ir->GetNPoints(); iqp++) {
mfem::IntegrationPoint ip = ir->IntPoint(iqp);
Trans.SetIntPoint(&ip);
double weight = ip.weight * Trans.Weight();
el.CalcShape(ip, shape);
double u_val = 0.0;
for (int j = 0; j < dof; j++) {
u_val += elfun(j) * shape(j);
}
double u_safe = std::max(u_val, 0.0);
double u_nl = std::pow(u_safe, m_polytropicIndex);
double coeff_val = m_coeff.Eval(Trans, ip);
double x2_u_nl = coeff_val * u_nl;
for (int i = 0; i < dof; i++){
elvect(i) += shape(i) * x2_u_nl * weight;
}
}
}
void NonlinearPowerIntegrator::AssembleElementGrad (
const mfem::FiniteElement &el,
mfem::ElementTransformation &Trans,
const mfem::Vector &elfun,
mfem::DenseMatrix &elmat) {
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 3);
int dof = el.GetDof();
elmat.SetSize(dof);
elmat = 0.0;
mfem::Vector shape(dof);
for (int iqp = 0; iqp < ir->GetNPoints(); iqp++) {
const mfem::IntegrationPoint &ip = ir->IntPoint(iqp);
Trans.SetIntPoint(&ip);
double weight = ip.weight * Trans.Weight();
el.CalcShape(ip, shape);
double u_val = 0.0;
for (int j = 0; j < dof; j++) {
u_val += elfun(j) * shape(j);
}
double coeff_val = m_coeff.Eval(Trans, ip);
// Calculate the Jacobian
double u_safe = std::max(u_val, 0.0);
double d_u_nl = coeff_val * m_polytropicIndex * std::pow(u_safe, m_polytropicIndex - 1);
double x2_d_u_nl = d_u_nl;
for (int i = 0; i < dof; i++) {
for (int j = 0; j < dof; j++) {
elmat(i, j) += shape(i) * x2_d_u_nl * shape(j) * weight;
}
}
}
}
} // namespace polyMFEMUtils

View File

@@ -0,0 +1,122 @@
#include "operator.h"
#include "mfem.hpp"
#include "linalg/vector.hpp"
#include <memory>
PolytropeOperator::PolytropeOperator(
std::unique_ptr<mfem::MixedBilinearForm> M,
std::unique_ptr<mfem::MixedBilinearForm> Q,
std::unique_ptr<mfem::BilinearForm> D,
std::unique_ptr<mfem::NonlinearForm> f,
const mfem::Array<int> &blockOffsets) :
mfem::Operator(blockOffsets.Last()), // Initialize the base class with the total size of the block offset vector
m_blockOffsets(blockOffsets),
m_jacobian(nullptr) {
m_M = std::move(M);
m_Q = std::move(Q);
m_D = std::move(D);
m_f = std::move(f);
m_Mmat = std::make_unique<mfem::SparseMatrix>(m_M->SpMat());
m_Qmat = std::make_unique<mfem::SparseMatrix>(m_Q->SpMat());
m_Dmat = std::make_unique<mfem::SparseMatrix>(m_D->SpMat());
m_negM_op = std::make_unique<mfem::ScaledOperator>(m_Mmat.get(), -1.0);
m_negQ_op = std::make_unique<mfem::ScaledOperator>(m_Qmat.get(), -1.0);
MFEM_ASSERT(m_Mmat.get() != nullptr, "Matrix m_Mmat is null in PolytropeOperator constructor");
MFEM_ASSERT(m_Qmat.get() != nullptr, "Matrix m_Qmat is null in PolytropeOperator constructor");
MFEM_ASSERT(m_Dmat.get() != nullptr, "Matrix m_Dmat is null in PolytropeOperator constructor");
MFEM_ASSERT(m_f.get() != nullptr, "NonlinearForm m_f is null in PolytropeOperator constructor");
}
void PolytropeOperator::Mult(const mfem::Vector &x, mfem::Vector &y) const {
// -- Create BlockVector views for input x and output y
mfem::BlockVector x_block(const_cast<mfem::Vector&>(x), m_blockOffsets);
mfem::BlockVector y_block(y, m_blockOffsets);
// -- Get Vector views for individual blocks
const mfem::Vector &x_theta = x_block.GetBlock(0);
const mfem::Vector &x_phi = x_block.GetBlock(1);
mfem::Vector &y_R0 = y_block.GetBlock(0); // Residual Block 0 (theta)
mfem::Vector &y_R1 = y_block.GetBlock(1); // Residual Block 1 (phi)
int theta_size = m_blockOffsets[1] - m_blockOffsets[0];
int phi_size = m_blockOffsets[2] - m_blockOffsets[1];
mfem::Vector f_term(theta_size);
mfem::Vector Mphi_term(theta_size);
mfem::Vector Dphi_term(phi_size);
mfem::Vector Qtheta_term(phi_size);
// Caucluate R0 and R1 terms
// R0 = f(θ) - Mɸ
// R1 = Dɸ - Qθ
MFEM_ASSERT(m_f.get() != nullptr, "NonlinearForm m_f is null in PolytropeOperator::Mult");
MFEM_ASSERT(m_Mmat.get() != nullptr, "SparseMatrix m_Mmat is null in PolytropeOperator::Mult");
MFEM_ASSERT(m_Dmat.get() != nullptr, "SparseMatrix m_Dmat is null in PolytropeOperator::Mult");
MFEM_ASSERT(m_Qmat.get() != nullptr, "SparseMatrix m_Qmat is null in PolytropeOperator::Mult");
m_f->Mult(x_theta, f_term);
m_Mmat->Mult(x_phi, Mphi_term);
m_Dmat->Mult(x_phi, Dphi_term);
m_Qmat->Mult(x_theta, Qtheta_term);
subtract(f_term, Mphi_term, y_R0);
subtract(Dphi_term, Qtheta_term, y_R1);
// -- Apply essential boundary conditions --
for (int i = 0; i < m_theta_ess_tofs.Size(); i++) {
int idx = m_theta_ess_tofs[i];
if (idx >= 0 && idx < y_R0.Size()) {
y_block.GetBlock(0)[idx] = 0.0; // Zero out the essential theta dofs in the bilinear form
}
}
for (int i = 0; i < m_phi_ess_tofs.Size(); i++) {
int idx = m_phi_ess_tofs[i];
if (idx >= 0 && idx < y_R1.Size()) {
y_block.GetBlock(1)[idx] = 0.0; // Zero out the essential phi dofs in the bilinear form
}
}
}
mfem::Operator& PolytropeOperator::GetGradient(const mfem::Vector &x) const {
// -- Get the gradient of f --
mfem::BlockVector x_block(const_cast<mfem::Vector&>(x), m_blockOffsets);
const mfem::Vector& x_theta = x_block.GetBlock(0);
mfem::Operator& J00 = m_f->GetGradient(x_theta);
if (m_jacobian == nullptr) {
m_jacobian = std::make_unique<mfem::BlockOperator>(m_blockOffsets);
m_jacobian->SetBlock(0, 0, &J00); // df/dθ (state-dependent)
m_jacobian->SetBlock(0, 1, m_negM_op.get()); // -M (constant)
m_jacobian->SetBlock(1, 0, m_negQ_op.get()); // -Q (constant)
m_jacobian->SetBlock(1, 1, m_Dmat.get()); // D (constant)
} else {
// The Jacobian already exists, we only need to update the first block
// since the other blocks have a constant derivitive (they are linear)
m_jacobian->SetBlock(0, 0, &J00);
}
return *m_jacobian;
}
void PolytropeOperator::SetEssentialTrueDofs(const mfem::Array<int> &theta_ess_tofs,
const mfem::Array<int> &phi_ess_tofs) {
m_theta_ess_tofs = theta_ess_tofs;
m_phi_ess_tofs = phi_ess_tofs;
if (m_f) {
m_f->SetEssentialTrueDofs(theta_ess_tofs);
} else {
MFEM_ABORT("m_f is null in PolytropeOperator::SetEssentialTrueDofs");
}
}

View File

@@ -1,43 +0,0 @@
/* ***********************************************************************
//
// Copyright (C) 2025 -- The 4D-STAR Collaboration
// File Author: Emily Boudreaux
// Last Modified: February 14, 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
//
// *********************************************************************** */
#include "mfem.hpp"
#include <string>
#include <fstream>
#include "polyIO.h"
void write_solution_to_csv(const mfem::GridFunction &u, const mfem::Mesh &mesh, const std::string &filename) {
std::ofstream file(filename);
if (!file.is_open()) {
std::cerr << "Error: Could not open " << filename << " for writing." << std::endl;
return;
}
file << "xi,u\n"; // CSV header
for (int i = 0; i < u.Size(); i++) {
double xi = mesh.GetVertex(i)[0]; // Get spatial coordinate
file << xi << "," << u[i] << "\n"; // Write to CSV
}
file.close();
std::cout << "Solution written to " << filename << std::endl;
}

View File

@@ -1,326 +0,0 @@
/* ***********************************************************************
//
// Copyright (C) 2025 -- The 4D-STAR Collaboration
// File Author: Emily Boudreaux
// Last Modified: March 19, 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
//
// *********************************************************************** */
#include "mfem.hpp"
#include <cmath>
#include <vector>
#include <limits>
#include <stdexcept>
#include <set>
#include <unordered_map>
#include "quill/LogMacros.h"
#include "polyMFEMUtils.h"
#include "debug.h"
namespace polyMFEMUtils {
NonlinearPowerIntegrator::NonlinearPowerIntegrator(
mfem::Coefficient &coeff,
double n) : coeff_(coeff), polytropicIndex(n) {
}
void NonlinearPowerIntegrator::AssembleElementVector(
const mfem::FiniteElement &el,
mfem::ElementTransformation &Trans,
const mfem::Vector &elfun,
mfem::Vector &elvect) {
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 3);
int dof = el.GetDof();
elvect.SetSize(dof);
elvect = 0.0;
mfem::Vector shape(dof);
for (int iqp = 0; iqp < ir->GetNPoints(); iqp++) {
mfem::IntegrationPoint ip = ir->IntPoint(iqp);
Trans.SetIntPoint(&ip);
double weight = ip.weight * Trans.Weight();
el.CalcShape(ip, shape);
double u_val = 0.0;
for (int j = 0; j < dof; j++) {
u_val += elfun(j) * shape(j);
}
double u_safe = std::max(u_val, 0.0);
double u_nl = std::pow(u_safe, polytropicIndex);
double coeff_val = coeff_.Eval(Trans, ip);
double x2_u_nl = coeff_val * u_nl;
for (int i = 0; i < dof; i++){
elvect(i) += shape(i) * x2_u_nl * weight;
}
}
}
void NonlinearPowerIntegrator::AssembleElementGrad (
const mfem::FiniteElement &el,
mfem::ElementTransformation &Trans,
const mfem::Vector &elfun,
mfem::DenseMatrix &elmat) {
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 3);
int dof = el.GetDof();
elmat.SetSize(dof);
elmat = 0.0;
mfem::Vector shape(dof);
for (int iqp = 0; iqp < ir->GetNPoints(); iqp++) {
const mfem::IntegrationPoint &ip = ir->IntPoint(iqp);
Trans.SetIntPoint(&ip);
double weight = ip.weight * Trans.Weight();
el.CalcShape(ip, shape);
double u_val = 0.0;
for (int j = 0; j < dof; j++) {
u_val += elfun(j) * shape(j);
}
double coeff_val = coeff_.Eval(Trans, ip);
// Calculate the Jacobian
double u_safe = std::max(u_val, 0.0);
double d_u_nl = coeff_val * polytropicIndex * std::pow(u_safe, polytropicIndex - 1);
double x2_d_u_nl = d_u_nl;
for (int i = 0; i < dof; i++) {
for (int j = 0; j < dof; j++) {
elmat(i, j) += shape(i) * x2_d_u_nl * shape(j) * weight;
}
}
}
}
BilinearIntegratorWrapper::BilinearIntegratorWrapper(
mfem::BilinearFormIntegrator *integratorInput
) : integrator(integratorInput) { }
BilinearIntegratorWrapper::~BilinearIntegratorWrapper() {
delete integrator;
}
void BilinearIntegratorWrapper::AssembleElementVector(
const mfem::FiniteElement &el,
mfem::ElementTransformation &Trans,
const mfem::Vector &elfun,
mfem::Vector &elvect) {
int dof = el.GetDof();
mfem::DenseMatrix elMat(dof);
integrator->AssembleElementMatrix(el, Trans, elMat);
elvect.SetSize(dof);
elvect = 0.0;
for (int i = 0; i < dof; i++)
{
double sum = 0.0;
for (int j = 0; j < dof; j++)
{
sum += elMat(i, j) * elfun(j);
}
elvect(i) = sum;
}
}
void BilinearIntegratorWrapper::AssembleElementGrad(const mfem::FiniteElement &el,
mfem::ElementTransformation &Trans,
const mfem::Vector &elfun,
mfem::DenseMatrix &elmat) {
int dof = el.GetDof();
elmat.SetSize(dof, dof);
elmat = 0.0;
integrator->AssembleElementMatrix(el, Trans, elmat);
}
CompositeNonlinearIntegrator::CompositeNonlinearIntegrator() { }
CompositeNonlinearIntegrator::~CompositeNonlinearIntegrator() { }
void CompositeNonlinearIntegrator::add_integrator(mfem::NonlinearFormIntegrator *integrator) {
integrators.push_back(integrator);
}
void CompositeNonlinearIntegrator::AssembleElementVector(
const mfem::FiniteElement &el,
mfem::ElementTransformation &Trans,
const mfem::Vector &elfun,
mfem::Vector &elvect) {
int dof = el.GetDof();
elvect.SetSize(dof);
elvect = 0.0;
mfem::Vector temp(dof);
for (size_t i = 0; i < integrators.size(); i++) {
temp= 0.0;
integrators[i]->AssembleElementVector(el, Trans, elfun, temp);
elvect.Add(1.0, temp);
}
}
void CompositeNonlinearIntegrator::AssembleElementGrad(
const mfem::FiniteElement &el,
mfem::ElementTransformation &Trans,
const mfem::Vector &elfun,
mfem::DenseMatrix &elmat) {
int dof = el.GetDof();
elmat.SetSize(dof, dof);
elmat = 0.0;
mfem::DenseMatrix temp(dof);
temp.SetSize(dof, dof);
for (size_t i = 0; i < integrators.size(); i++) {
temp = 0.0;
integrators[i] -> AssembleElementGrad(el, Trans, elfun, temp);
elmat.Add(1.0, temp);
}
}
// TODO: break this up into smaller functions
// TODO: think of a more efficient way to find connected elements
ConstraintIntegrator::ConstraintIntegrator(const double gamma, mfem::Mesh* mesh): m_gamma(gamma), m_mesh(mesh) {
LOG_INFO(m_logger, "Initializing Constraint Integrator...");
m_originCoordinateMatrix.SetSize(3, 1);
m_originCoordinateMatrix(0, 0) = 0.0;
m_originCoordinateMatrix(1, 0) = 0.0;
m_originCoordinateMatrix(2, 0) = 0.0;
m_originCoordinateVector.SetSize(3);
m_originCoordinateVector = 0.0;
m_mesh->FindPoints(m_originCoordinateMatrix, m_originElementIDs, m_originIntegrationPoints);
if (m_originElementIDs.Size() == 0) {
LOG_ERROR(m_logger, "The origin point is not found in the mesh.");
throw std::runtime_error("The origin point is not found in the mesh.");
}
LOG_INFO(m_logger, "The origin point is found in the mesh.");
// NOTE (EMB, March 2025): This function as it is currently written will break if the mesh is refined after being passed to the constructor
// This may or may not be an issue (it does seem unlikley that the mesh would be refined after being passed to the constructor)
// But if something mysteriously breaks in the future this is may be a good place to start looking
mfem::Table* VETable = m_mesh->GetVertexToElementTable();
const int nVertices = VETable->Size();
LOG_INFO(m_logger, "The number of vertices in the mesh is {}", nVertices);
std::vector<int> originVertexIds;
mfem::Array<int> connectedElements;
// -- Get all vertices connected to the origin element --
for (int vertexID = 0; vertexID < nVertices; vertexID++) {
VETable->GetRow(vertexID, connectedElements);
for (int j = 0; j < connectedElements.Size(); j++) {
if (connectedElements[j] == m_originElementIDs[0]) {
originVertexIds.push_back(vertexID);
}
}
}
double minDistanceToOrigin = std::numeric_limits<double>::max();
int minDistanceVertexId = -1;
// -- Get the vertex closest to the origin ID --
for (const auto &vertexId : originVertexIds) {
mfem::Vector vertex;
const double* vcoord = m_mesh->GetVertex(vertexId);
// --- Note if this is run with a 2D or 1D mesh this may lead to a segfault ---
// TODO: Add a check for the dimension of the mesh
double distance = vcoord[0]*vcoord[0] + vcoord[1]*vcoord[1] + vcoord[2]*vcoord[2];
if (distance < minDistanceToOrigin) {
minDistanceToOrigin = distance;
minDistanceVertexId = vertexId;
}
}
if (minDistanceVertexId == -1 || minDistanceToOrigin > 1e-10) {
LOG_ERROR(m_logger, "The origin vertex is not found in the mesh.");
throw std::runtime_error("The origin vertex is not found in the mesh.");
}
// -- Find all elements connected to the origin vertex by looping through the VE table
VETable->GetRow(minDistanceVertexId, m_originConnectedElementIds);
LOG_INFO(m_logger, "Found {} elements connected to the origin vertex.", m_originConnectedElementIds.Size());
}
void ConstraintIntegrator::AssembleElementMatrix(const mfem::FiniteElement &el, mfem::ElementTransformation &Trans, mfem::DenseMatrix &elmat) {
int elemID = Trans.ElementNo;
// -- Check if the element is connected to the origin vertex --
bool isConnected = m_originConnectedElementIds.Find(elemID) != -1 ? true : false;
if (!isConnected) {
elmat = 0.0;
return;
}
// -- Compute the derivitives using MFEM's build in shape routines --
int numDoF = el.GetDof();
int dim = m_mesh->Dimension();
// -- Map the origin in physical space to the reference space of the element --
mfem::Vector originReferenceCoordinate(dim);
mfem::IntegrationPoint originIntegrationPoint;
Trans.TransformBack(m_originCoordinateVector, originIntegrationPoint);
// -- Compute the derivitives of the shape functions at the origin --
mfem::DenseMatrix dshape(numDoF, dim);
el.CalcDShape(originIntegrationPoint, dshape);
// -- Transform derivitives from reference space to physical space using the inverse of the Jacobian --
mfem::DenseMatrix invJac(dim, dim);
invJac = Trans.InverseJacobian();
mfem::DenseMatrix dshapePhysical(numDoF, dim);
dshapePhysical = 0.0;
for (int dofID = 0; dofID < numDoF; dofID++) {
for (int dimID = 0; dimID < dim; dimID++) {
for (int i = 0; i < dim; i++) {
dshapePhysical(dofID, dimID) += dshape(dofID, i) * invJac(i, dimID);
}
}
}
// -- Assemble the element matrix contribution = gamma * (grad(phi_i) dot grad(phi_j)) --
elmat.SetSize(numDoF);
elmat = 0.0;
for (int i = 0; i < numDoF; i++) {
for (int j = 0; j < numDoF; j++) {
double dotProduct = 0.0;
for (int dimID = 0; dimID < dim; dimID++) {
dotProduct += dshapePhysical(i, dimID) * dshapePhysical(j, dimID);
}
elmat(i, j) += m_gamma * dotProduct;
}
}
}
} // namespace polyMFEMUtils

View File

@@ -0,0 +1,85 @@
/* ***********************************************************************
//
// Copyright (C) 2025 -- The 4D-STAR Collaboration
// File Author: Emily Boudreaux
// Last Modified: March 19, 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
//
// *********************************************************************** */
#ifndef POLYMFEMUTILS_H
#define POLYMFEMUTILS_H
#include "mfem.hpp"
#include <string>
#include <vector>
#include "config.h"
#include "probe.h"
#include "quill/LogMacros.h"
/**
* @file polyMFEMUtils.h
* @brief A collection of utilities for working with MFEM and solving the lane-emden equation.
*/
/**
* @namespace polyMFEMUtils
* @brief A namespace for utilities for working with MFEM and solving the lane-emden equation.
*/
namespace polyMFEMUtils {
/**
* @brief A class for nonlinear power integrator.
*/
class NonlinearPowerIntegrator: public mfem::NonlinearFormIntegrator {
private:
Config& m_config = Config::getInstance();
Probe::LogManager& m_logManager = Probe::LogManager::getInstance();
quill::Logger* m_logger = m_logManager.getLogger("log");
mfem::Coefficient &m_coeff;
double m_polytropicIndex;
public:
/**
* @brief Constructor for NonlinearPowerIntegrator.
*
* @param coeff The function coefficient.
* @param n The polytropic index.
*/
NonlinearPowerIntegrator(mfem::Coefficient &coeff, double n);
/**
* @brief Assembles the element vector.
*
* @param el The finite element.
* @param Trans The element transformation.
* @param elfun The element function.
* @param elvect The element vector to be assembled.
*/
virtual void AssembleElementVector(const mfem::FiniteElement &el, mfem::ElementTransformation &Trans, const mfem::Vector &elfun, mfem::Vector &elvect) override;
/**
* @brief Assembles the element gradient.
*
* @param el The finite element.
* @param Trans The element transformation.
* @param elfun The element function.
* @param elmat The element matrix to be assembled.
*/
virtual void AssembleElementGrad (const mfem::FiniteElement &el, mfem::ElementTransformation &Trans, const mfem::Vector &elfun, mfem::DenseMatrix &elmat) override;
};
} // namespace polyMFEMUtils
#endif // POLYMFEMUTILS_H

View File

@@ -0,0 +1,45 @@
#ifndef POLY_UTILS_OPERATOR_H
#define POLY_UTILS_OPERATOR_H
#include "mfem.hpp"
#include <memory>
class PolytropeOperator : public mfem::Operator {
public:
PolytropeOperator(
std::unique_ptr<mfem::MixedBilinearForm> M,
std::unique_ptr<mfem::MixedBilinearForm> Q,
std::unique_ptr<mfem::BilinearForm> D,
std::unique_ptr<mfem::NonlinearForm> f,
const mfem::Array<int> &blockOffsets);
~PolytropeOperator() override = default;
void Mult(const mfem::Vector &x, mfem::Vector &y) const override;
mfem::Operator& GetGradient(const mfem::Vector &x) const override;
void SetEssentialTrueDofs(const mfem::Array<int> &theta_ess_tofs,
const mfem::Array<int> &phi_ess_tofs);
private:
std::unique_ptr<mfem::MixedBilinearForm> m_M;
std::unique_ptr<mfem::MixedBilinearForm> m_Q;
std::unique_ptr<mfem::BilinearForm> m_D;
std::unique_ptr<mfem::NonlinearForm> m_f;
const mfem::Array<int> &m_blockOffsets;
mfem::Array<int> m_theta_ess_tofs;
mfem::Array<int> m_phi_ess_tofs;
std::unique_ptr<mfem::SparseMatrix> m_Mmat;
std::unique_ptr<mfem::SparseMatrix> m_Qmat;
std::unique_ptr<mfem::SparseMatrix> m_Dmat;
std::unique_ptr<mfem::ScaledOperator> m_negM_op;
std::unique_ptr<mfem::ScaledOperator> m_negQ_op;
mutable std::unique_ptr<mfem::BlockOperator> m_jacobian;
};
#endif // POLY_UTILS_OPERATOR_H

View File

@@ -1,36 +0,0 @@
/* ***********************************************************************
//
// Copyright (C) 2025 -- The 4D-STAR Collaboration
// File Author: Emily Boudreaux
// Last Modified: February 14, 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
//
// *********************************************************************** */
#ifndef POLY_IO_H
#define POLY_IO_H
#include "mfem.hpp"
#include <string>
/**
* @brief Writes the solution to a CSV file.
*
* @param u The GridFunction containing the solution.
* @param mesh The mesh associated with the solution.
* @param filename The name of the CSV file to write to.
*/
void write_solution_to_csv(const mfem::GridFunction &u, const mfem::Mesh &mesh, const std::string &filename);
#endif // POLY_IO_H

View File

@@ -1,190 +0,0 @@
/* ***********************************************************************
//
// Copyright (C) 2025 -- The 4D-STAR Collaboration
// File Author: Emily Boudreaux
// Last Modified: March 19, 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
//
// *********************************************************************** */
#ifndef POLYMFEMUTILS_H
#define POLYMFEMUTILS_H
#include "mfem.hpp"
#include <string>
#include <vector>
#include "config.h"
#include "probe.h"
#include <unordered_map>
/**
* @file polyMFEMUtils.h
* @brief A collection of utilities for working with MFEM and solving the lane-emden equation.
*/
/**
* @namespace polyMFEMUtils
* @brief A namespace for utilities for working with MFEM and solving the lane-emden equation.
*/
namespace polyMFEMUtils {
/**
* @brief A class for nonlinear power integrator.
*/
class NonlinearPowerIntegrator: public mfem::NonlinearFormIntegrator {
private:
Config& config = Config::getInstance();
mfem::Coefficient &coeff_;
double polytropicIndex;
public:
/**
* @brief Constructor for NonlinearPowerIntegrator.
*
* @param coeff The function coefficient.
* @param n The polytropic index.
*/
NonlinearPowerIntegrator(mfem::Coefficient &coeff, double n);
/**
* @brief Assembles the element vector.
*
* @param el The finite element.
* @param Trans The element transformation.
* @param elfun The element function.
* @param elvect The element vector to be assembled.
*/
virtual void AssembleElementVector(const mfem::FiniteElement &el, mfem::ElementTransformation &Trans, const mfem::Vector &elfun, mfem::Vector &elvect) override;
/**
* @brief Assembles the element gradient.
*
* @param el The finite element.
* @param Trans The element transformation.
* @param elfun The element function.
* @param elmat The element matrix to be assembled.
*/
virtual void AssembleElementGrad (const mfem::FiniteElement &el, mfem::ElementTransformation &Trans, const mfem::Vector &elfun, mfem::DenseMatrix &elmat) override;
};
/**
* @brief A wrapper class for bilinear integrator.
*/
class BilinearIntegratorWrapper : public mfem::NonlinearFormIntegrator
{
private:
mfem::BilinearFormIntegrator *integrator;
public:
/**
* @brief Constructor for BilinearIntegratorWrapper.
*
* @param integratorInput The bilinear form integrator input.
*/
BilinearIntegratorWrapper(mfem::BilinearFormIntegrator *integratorInput);
/**
* @brief Destructor for BilinearIntegratorWrapper.
*/
virtual ~BilinearIntegratorWrapper();
/**
* @brief Assembles the element vector.
*
* @param el The finite element.
* @param Trans The element transformation.
* @param elfun The element function.
* @param elvect The element vector to be assembled.
*/
virtual void AssembleElementVector(const mfem::FiniteElement &el, mfem::ElementTransformation &Trans, const mfem::Vector &elfun, mfem::Vector &elvect) override;
/**
* @brief Assembles the element gradient.
*
* @param el The finite element.
* @param Trans The element transformation.
* @param elfun The element function.
* @param elmat The element matrix to be assembled.
*/
virtual void AssembleElementGrad(const mfem::FiniteElement &el, mfem::ElementTransformation &Trans, const mfem::Vector &elfun, mfem::DenseMatrix &elmat) override;
};
/**
* @brief A class for composite nonlinear integrator.
*/
class CompositeNonlinearIntegrator: public mfem::NonlinearFormIntegrator {
private:
std::vector<mfem::NonlinearFormIntegrator*> integrators;
public:
/**
* @brief Constructor for CompositeNonlinearIntegrator.
*/
CompositeNonlinearIntegrator();
/**
* @brief Destructor for CompositeNonlinearIntegrator.
*/
virtual ~CompositeNonlinearIntegrator();
/**
* @brief Adds an integrator to the composite integrator.
*
* @param integrator The nonlinear form integrator to add.
*/
void add_integrator(mfem::NonlinearFormIntegrator *integrator);
/**
* @brief Assembles the element vector.
*
* @param el The finite element.
* @param Trans The element transformation.
* @param elfun The element function.
* @param elvect The element vector to be assembled.
*/
virtual void AssembleElementVector(const mfem::FiniteElement &el, mfem::ElementTransformation &Trans, const mfem::Vector &elfun, mfem::Vector &elvect) override;
/**
* @brief Assembles the element gradient.
*
* @param el The finite element.
* @param Trans The element transformation.
* @param elfun The element function.
* @param elmat The element matrix to be assembled.
*/
virtual void AssembleElementGrad(const mfem::FiniteElement &el, mfem::ElementTransformation &Trans, const mfem::Vector &elfun, mfem::DenseMatrix &elmat) override;
};
class ConstraintIntegrator: public mfem::BilinearFormIntegrator {
private:
Config& m_config = Config::getInstance();
Probe::LogManager& logManager = Probe::LogManager::getInstance();
quill::Logger* m_logger = logManager.getLogger("log");
const double m_gamma;
mfem::Array<int> m_originElementIDs;
mfem::Array<mfem::IntegrationPoint> m_originIntegrationPoints;
mfem::DenseMatrix m_originCoordinateMatrix;
mfem::Vector m_originCoordinateVector;
mfem::Mesh* m_mesh;
mfem::Array<int> m_originConnectedElementIds;
public:
ConstraintIntegrator(const double gamma, mfem::Mesh* mesh);
~ConstraintIntegrator() = default;
void AssembleElementMatrix(const mfem::FiniteElement &el, mfem::ElementTransformation &Trans, mfem::DenseMatrix &elmat) override;
};
} // namespace polyMFEMUtils
#endif // POLYMFEMUTILS_H