fix(poly): phi boundary values now coorespond to theta flux through polytrope surface

This commit is contained in:
2025-04-25 10:32:06 -04:00
parent 58840d82cd
commit 2acc037111
7 changed files with 146 additions and 94 deletions

View File

@@ -139,7 +139,7 @@ std::unique_ptr<formBundle> PolySolver::buildIndividualForms(const mfem::Array<i
);
// --- -M negation -> M ---
mfem::Vector negOneVec(m_fePhi->GetVDim());
mfem::Vector negOneVec(m_mesh->SpaceDimension());
negOneVec = -1.0;
m_negationCoeff = std::make_unique<mfem::VectorConstantCoefficient>(negOneVec);
@@ -184,9 +184,6 @@ void PolySolver::solve() const {
throw std::runtime_error("PolytropeOperator is not finalized. Cannot solve.");
}
GetDofCoordinates(*m_feTheta, "dof2posTheta.csv");
GetDofCoordinates(*m_fePhi, "dof2posPhi.csv");
// It's safer to get the offsets directly from the operator after finalization
const mfem::Array<int>& block_offsets = m_polytropOperator->GetBlockOffsets(); // Assuming a getter exists or accessing member if public/friend
mfem::BlockVector state_vector(block_offsets);
@@ -194,23 +191,14 @@ void PolySolver::solve() const {
state_vector.GetBlock(1) = phiVec; // NOLINT(*-slicing)
mfem::Vector zero_rhs(block_offsets.Last());
mfem::Vector zero_rhs(block_offsets.Last()); // TODO: Is this right?
zero_rhs = 0.0;
const solverBundle sb = setupNewtonSolver();
// EMB 2025: Calling Mult gets the gradient of the operator for the NewtonSolver
// This then is set as the operator for the solver for NewtonSolver
// The solver (assuming it is an iterative solver) then sets the
// operator for its preconditioner to this.
// What this means is that there is no need to manually deal
// with updating the preconditioner at every newton step as the
// changes to the jacobian are automatically propagated through the
// solving chain. This is at least true with MFEM 4.8-rc0
std::string windowTitle = "testWindow";
std::string keyset = "";
sb.newton.Mult(zero_rhs, state_vector);
// Ax = b for x
// --- Save and view the solution ---
saveAndViewSolution(state_vector);
}
@@ -219,28 +207,31 @@ SSE::MFEMArrayPairSet PolySolver::getEssentialTrueDof() const {
mfem::Array<int> theta_ess_tdof_list;
mfem::Array<int> phi_ess_tdof_list;
mfem::Array<int> thetaCenterDofs, phiCenterDofs;
mfem::Array<double> thetaCenterVals, phiCenterVals;
std::tie(thetaCenterDofs, phi_ess_tdof_list) = findCenterElement();
mfem::Array<int> thetaCenterDofs, phiCenterDofs; // phiCenterDofs are not used
mfem::Array<double> thetaCenterVals;
std::tie(thetaCenterDofs, phiCenterDofs) = findCenterElement();
thetaCenterVals.SetSize(thetaCenterDofs.Size());
phiCenterVals.SetSize(phi_ess_tdof_list.Size());
thetaCenterVals = 1.0;
phiCenterVals = 0.0;
mfem::Array<int> ess_brd(m_mesh->bdr_attributes.Max());
ess_brd = 1;
mfem::Array<double> thetaSurfaceVals;
mfem::Array<double> thetaSurfaceVals, phiSurfaceVals;
m_feTheta->GetEssentialTrueDofs(ess_brd, theta_ess_tdof_list);
m_fePhi->GetEssentialTrueDofs(ess_brd, phi_ess_tdof_list);
thetaSurfaceVals.SetSize(theta_ess_tdof_list.Size());
thetaSurfaceVals = 0.0;
phiSurfaceVals.SetSize(phi_ess_tdof_list.Size());
phiSurfaceVals = polycoeff::thetaSurfaceFlux(m_polytropicIndex);
// combine the essential dofs with the center dofs
theta_ess_tdof_list.Append(thetaCenterDofs);
thetaSurfaceVals.Append(thetaCenterVals);
SSE::MFEMArrayPair thetaPair = std::make_pair(theta_ess_tdof_list, thetaSurfaceVals);
SSE::MFEMArrayPair phiPair = std::make_pair(phi_ess_tdof_list, phiCenterVals);
SSE::MFEMArrayPair phiPair = std::make_pair(phi_ess_tdof_list, phiSurfaceVals);
SSE::MFEMArrayPairSet pairSet = std::make_pair(thetaPair, phiPair);
return pairSet;
@@ -275,16 +266,39 @@ void PolySolver::setInitialGuess() const {
const double radius = Probe::getMeshRadius(*m_mesh);
const double u = 1/radius;
return (-1.0/radius) * r + 1;
// return (-1.0/radius) * r + 1;
// return -std::pow((u*r), 2)+1.0; // The series expansion is a better guess; however, this is cheaper and ensures that the value at the surface is very close to zero in a way that the series expansion does not
return laneEmden::thetaSeriesExpansion(r, m_polytropicIndex, 5);
}
);
mfem::VectorFunctionCoefficient phiSurfaceVectors (m_mesh->SpaceDimension(),
[this](const mfem::Vector &x, mfem::Vector &y) {
const double r = x.Norml2();
mfem::Vector xh(x);
xh /= r; // Normalize the vector
y.SetSize(m_mesh->SpaceDimension());
y = xh;
y *= polycoeff::thetaSurfaceFlux(m_polytropicIndex);
}
);
// We want to apply specific boundary conditions to the surface
mfem::Array<int> ess_brd(m_mesh->bdr_attributes.Max());
ess_brd = 1;
// θ = 0 at surface
mfem::ConstantCoefficient surfacePotential(0);
m_theta->ProjectCoefficient(thetaInitGuess);
m_theta->ProjectBdrCoefficient(surfacePotential, ess_brd);
mfem::GradientGridFunctionCoefficient phiInitGuess (m_theta.get());
m_phi->ProjectCoefficient(phiInitGuess);
m_phi->ProjectBdrCoefficientNormal(phiSurfaceVectors, ess_brd);
if (m_config.get<bool>("Poly:Solver:ViewInitialGuess", false)) {
Probe::glVisView(*m_theta, *m_mesh, "θ init");
Probe::glVisView(*m_phi, *m_mesh, "ɸ init");
Probe::glVisView(*m_phi, *m_mesh, "φ init");
}
}
@@ -335,7 +349,7 @@ void PolySolver::LoadSolverUserParams(double &newtonRelTol, double &newtonAbsTol
LOG_DEBUG(m_logger, "GMRES Solver (relTol: {:0.2E}, absTol: {:0.2E}, maxIter: {}, printLevel: {})", gmresRelTol, gmresAbsTol, gmresMaxIter, gmresPrintLevel);
}
void PolySolver::GetDofCoordinates(mfem::FiniteElementSpace &fes, const std::string& filename) const {
void PolySolver::GetDofCoordinates(const mfem::FiniteElementSpace &fes, const std::string& filename) {
mfem::Mesh *mesh = fes.GetMesh();
double r = Probe::getMeshRadius(*mesh);
std::ofstream outputFile(filename, std::ios::out | std::ios::trunc);