feat(mean_field): added initial implementation
note this implementation lacks many tests
This commit is contained in:
206
libmeanfield/impl/integrators/advection.cpp
Normal file
206
libmeanfield/impl/integrators/advection.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
module;
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
|
||||
namespace mean_field::integrators {
|
||||
AdvectionIntegrator::AdvectionIntegrator(const mapping::DomainMapper &map) : m_map(map) {}
|
||||
|
||||
void AdvectionIntegrator::AssembleElementVector(
|
||||
const mfem::Array<const mfem::FiniteElement *> &el,
|
||||
mfem::ElementTransformation &Tr,
|
||||
const mfem::Array<const mfem::Vector *> &elfun,
|
||||
const mfem::Array<mfem::Vector *> &elvec
|
||||
) {
|
||||
if (utils::is_vacuum(Tr, elvec)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement *fe_v = el[0];
|
||||
const mfem::FiniteElement *fe_rho = el[1];
|
||||
|
||||
const int dof_v = fe_v->GetDof();
|
||||
const int dof_rho = fe_rho->GetDof();
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
|
||||
const mfem::Vector &v_dofs = *elfun[0];
|
||||
const mfem::Vector &rho_dofs = *elfun[1];
|
||||
|
||||
mfem::Vector &r_v = *elvec[0];
|
||||
r_v.SetSize(dof_v * dim);
|
||||
r_v = 0.0;
|
||||
if (elvec[1]) {
|
||||
elvec[1]->SetSize(dof_rho);
|
||||
*elvec[1] = 0.0;
|
||||
}
|
||||
|
||||
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
|
||||
mfem::DenseMatrix dshape_v_ref(dof_v, dim), dshape_v_phys(dof_v, dim);
|
||||
|
||||
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder() + 1);
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); q++) {
|
||||
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
|
||||
Tr.SetIntPoint(&ip);
|
||||
|
||||
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
|
||||
|
||||
fe_v->CalcShape(ip, shape_v);
|
||||
fe_v->CalcDShape(ip, dshape_v_ref);
|
||||
mfem::Mult(dshape_v_ref, J_inv, dshape_v_phys);
|
||||
fe_rho->CalcShape(ip, shape_rho);
|
||||
|
||||
double rho_val = 0.0;
|
||||
for (int i = 0; i < dof_rho; ++i) {
|
||||
rho_val += rho_dofs(i) * shape_rho(i);
|
||||
}
|
||||
|
||||
mfem::Vector v_val(dim);
|
||||
v_val = 0.0;
|
||||
mfem::DenseMatrix grad_v(dim, dim);
|
||||
grad_v = 0.0;
|
||||
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
const double v_ic = v_dofs(i + c * dof_v);
|
||||
v_val(c) += v_ic * shape_v(i);
|
||||
for (int d = 0; d < dim; ++d) {
|
||||
grad_v(c, d) += v_ic * dshape_v_phys(i, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mfem::Vector adv_val(dim);
|
||||
adv_val = 0.0;
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
for (int d = 0; d < dim; ++d) {
|
||||
adv_val(c) += v_val(d) * grad_v(c, d);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
r_v(i + c * dof_v) += shape_v(i) * rho_val * adv_val(c) * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdvectionIntegrator::AssembleElementGrad(
|
||||
const mfem::Array<const mfem::FiniteElement *> &el,
|
||||
mfem::ElementTransformation &Tr,
|
||||
const mfem::Array<const mfem::Vector *> &elfun,
|
||||
const mfem::Array2D<mfem::DenseMatrix *> &elmats
|
||||
) {
|
||||
const mfem::FiniteElement *fe_v = el[0];
|
||||
const mfem::FiniteElement *fe_rho = el[1];
|
||||
|
||||
const int dof_v = fe_v->GetDof();
|
||||
const int dof_rho = fe_rho->GetDof();
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
|
||||
const mfem::Vector &v_dofs = *elfun[0];
|
||||
const mfem::Vector &rho_dofs = *elfun[1];
|
||||
|
||||
mfem::DenseMatrix *dv_dv = elmats(0, 0);
|
||||
mfem::DenseMatrix *dv_drho = elmats(0, 1);
|
||||
|
||||
if (dv_dv) *dv_dv = 0.0;
|
||||
if (dv_drho) *dv_drho = 0.0;
|
||||
|
||||
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
|
||||
mfem::DenseMatrix dshape_v_ref(dof_v, dim), dshape_v_phys(dof_v, dim);
|
||||
|
||||
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder() + 1);
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); q++) {
|
||||
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
|
||||
Tr.SetIntPoint(&ip);
|
||||
|
||||
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
|
||||
|
||||
fe_v->CalcShape(ip, shape_v);
|
||||
fe_v->CalcDShape(ip, dshape_v_ref);
|
||||
mfem::Mult(dshape_v_ref, J_inv, dshape_v_phys);
|
||||
fe_rho->CalcShape(ip, shape_rho);
|
||||
|
||||
double rho_val = 0.0;
|
||||
for (int i = 0; i < dof_rho; ++i) {
|
||||
rho_val += rho_dofs(i) * shape_rho(i);
|
||||
}
|
||||
|
||||
mfem::Vector v_val(dim);
|
||||
v_val = 0.0;
|
||||
mfem::DenseMatrix grad_v(dim, dim);
|
||||
grad_v = 0.0;
|
||||
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
double v_ic = v_dofs(i + c * dof_v);
|
||||
v_val(c) += v_ic * shape_v(i);
|
||||
for (int d = 0; d < dim; ++d) {
|
||||
grad_v(c, d) += v_ic * dshape_v_phys(i, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mfem::Vector adv_val(dim);
|
||||
adv_val = 0.0;
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
for (int d = 0; d < dim; ++d) {
|
||||
adv_val(c) += v_val(d) * grad_v(c, d);
|
||||
}
|
||||
}
|
||||
|
||||
// Jacobian wrt. Velocity: dR_v/dv
|
||||
if (dv_dv) {
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
// Test function index
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
// Test function component
|
||||
int row = i + c * dof_v;
|
||||
for (int j = 0; j < dof_v; ++j) {
|
||||
// Trial function index
|
||||
double v_dot_grad_phi_j = 0.0;
|
||||
|
||||
for (int k = 0; k < dim; ++k) {
|
||||
v_dot_grad_phi_j += v_val(k) * dshape_v_phys(j, k);
|
||||
}
|
||||
|
||||
for (int d = 0; d < dim; ++d) {
|
||||
// Trial function component
|
||||
int col = j + d * dof_v;
|
||||
|
||||
// \rho (\delta \vec{v} \cdot \nabla \vec{v})
|
||||
// \delta v is along direction 'd' for the cth component of advection
|
||||
double termA = shape_v(j) * grad_v(c, d);
|
||||
|
||||
// \rho(\vec{v} \cdot \nabla \delta \vec{v})
|
||||
// Only non-zero when the advected component matches the test component
|
||||
double termB = (c == d) ? v_dot_grad_phi_j : 0.0;
|
||||
|
||||
(*dv_dv)(row, col) += shape_v(i) * rho_val * (termA + termB) * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Jacobian wrt. Density: dR_v / drho
|
||||
if (dv_drho) {
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
int row = i + c * dof_v;
|
||||
for (int j = 0; j < dof_rho; ++j) {
|
||||
int col = j;
|
||||
|
||||
// \delta \rho * (\vec{v} \cdot \nabla \vec{v})
|
||||
double term = shape_rho(j) * adv_val(c);
|
||||
(*dv_drho)(row, col) += shape_v(i) * term * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
146
libmeanfield/impl/integrators/centrifugal.cpp
Normal file
146
libmeanfield/impl/integrators/centrifugal.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
module;
|
||||
#include <mfem.hpp>
|
||||
module mean_field;
|
||||
|
||||
namespace mean_field::integrators {
|
||||
CentrifugalForceIntegrator::CentrifugalForceIntegrator(
|
||||
const mapping::DomainMapper& map,
|
||||
const mfem::Vector& omega
|
||||
) : m_map(map), m_omega(3) {
|
||||
MFEM_ASSERT(omega.Size() == 3, "Omega vector must be 3D");
|
||||
m_omega = omega;
|
||||
}
|
||||
|
||||
void CentrifugalForceIntegrator::SetOmega(const mfem::Vector& omega) {
|
||||
MFEM_ASSERT(omega.Size() == 3, "Omega vector must be 3D");
|
||||
m_omega = omega;
|
||||
}
|
||||
|
||||
void CentrifugalForceIntegrator::AssembleElementVector(
|
||||
const mfem::Array<const mfem::FiniteElement *> &el,
|
||||
mfem::ElementTransformation &Tr,
|
||||
const mfem::Array<const mfem::Vector *> &elfun,
|
||||
const mfem::Array<mfem::Vector *> &elvec
|
||||
) {
|
||||
if (utils::is_vacuum(Tr, elvec)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement* fe_v = el[0];
|
||||
const mfem::FiniteElement* fe_rho = el[1];
|
||||
|
||||
const int dof_v = fe_v->GetDof();
|
||||
const int dof_rho = fe_rho->GetDof();
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
|
||||
const mfem::Vector& rho_dofs = *elfun[1];
|
||||
|
||||
mfem::Vector& r_v = *elvec[0];
|
||||
r_v = 0.0;
|
||||
r_v.SetSize(dof_v * dim);
|
||||
if (elvec[1]) {
|
||||
elvec[1]->SetSize(dof_rho);
|
||||
*elvec[1] = 0.0;
|
||||
}
|
||||
|
||||
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
|
||||
mfem::Vector x_phys(dim);
|
||||
mfem::Vector a(dim), b(dim);
|
||||
|
||||
const mfem::IntegrationRule* ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint& ip = ir->IntPoint(q);
|
||||
Tr.SetIntPoint(&ip);
|
||||
|
||||
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
|
||||
|
||||
fe_v->CalcShape(ip, shape_v);
|
||||
fe_rho->CalcShape(ip, shape_rho);
|
||||
|
||||
m_map.GetPhysicalPoint(Tr, ip, x_phys);
|
||||
|
||||
// ω x r
|
||||
a(0) = m_omega(1) * x_phys(2) - m_omega(2) * x_phys(1);
|
||||
a(1) = m_omega(2) * x_phys(0) - m_omega(0) * x_phys(2);
|
||||
a(2) = m_omega(0) * x_phys(1) - m_omega(1) * x_phys(0);
|
||||
|
||||
// ω x (ω x r) [centrifugal acceleration]
|
||||
b(0) = m_omega(1) * a(2) - m_omega(2) * a(1);
|
||||
b(1) = m_omega(2) * a(0) - m_omega(0) * a(2);
|
||||
b(2) = m_omega(0) * a(1) - m_omega(1) * a(0);
|
||||
|
||||
double rho_val = 0.0;
|
||||
for (int i = 0; i < dof_rho; ++i) {
|
||||
rho_val += rho_dofs(i) * shape_rho(i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
r_v(i + c * dof_v) += shape_v(i) * rho_val * b(c) * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CentrifugalForceIntegrator::AssembleElementGrad(
|
||||
const mfem::Array<const mfem::FiniteElement *> &el,
|
||||
mfem::ElementTransformation &Tr,
|
||||
const mfem::Array<const mfem::Vector *> &elfun,
|
||||
const mfem::Array2D<mfem::DenseMatrix *> &elmats
|
||||
) {
|
||||
const mfem::FiniteElement* fe_v = el[0];
|
||||
const mfem::FiniteElement* fe_rho = el[1];
|
||||
|
||||
const int dof_v = fe_v->GetDof();
|
||||
const int dof_rho = fe_rho->GetDof();
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
|
||||
mfem::DenseMatrix* dv_dv = elmats(0,0);
|
||||
mfem::DenseMatrix* dv_drho = elmats(0,1);
|
||||
|
||||
if (dv_dv) *dv_dv = 0.0;
|
||||
if (elmats(1, 0)) *elmats(1, 0) = 0.0;
|
||||
if (elmats(1, 1)) *elmats(1, 1) = 0.0;
|
||||
if (dv_drho) *dv_drho = 0.0;
|
||||
if (!dv_drho) return;
|
||||
|
||||
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
|
||||
mfem::Vector x_phys(dim);
|
||||
mfem::Vector a(dim), b(dim);
|
||||
|
||||
const mfem::IntegrationRule* ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint& ip = ir->IntPoint(q);
|
||||
Tr.SetIntPoint(&ip);
|
||||
|
||||
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
|
||||
|
||||
fe_v->CalcShape(ip, shape_v);
|
||||
fe_rho->CalcShape(ip, shape_rho);
|
||||
|
||||
m_map.GetPhysicalPoint(Tr, ip, x_phys);
|
||||
|
||||
// ω x r
|
||||
a(0) = m_omega(1) * x_phys(2) - m_omega(2) * x_phys(1);
|
||||
a(1) = m_omega(2) * x_phys(0) - m_omega(0) * x_phys(2);
|
||||
a(2) = m_omega(0) * x_phys(1) - m_omega(1) * x_phys(0);
|
||||
|
||||
// ω x (ω x r) [centrifugal acceleration]
|
||||
b(0) = m_omega(1) * a(2) - m_omega(2) * a(1);
|
||||
b(1) = m_omega(2) * a(0) - m_omega(0) * a(2);
|
||||
b(2) = m_omega(0) * a(1) - m_omega(1) * a(0);
|
||||
|
||||
// dR_dv_i_c / drho_j = φ_i * φ_j * b_c
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
const int row = i + c * dof_v;
|
||||
for (int j = 0; j < dof_rho; ++j) {
|
||||
(*dv_drho)(row, j) += shape_v(i) * shape_rho(j) * b(c) * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
152
libmeanfield/impl/integrators/coriolis.cpp
Normal file
152
libmeanfield/impl/integrators/coriolis.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
module;
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
|
||||
namespace mean_field::integrators {
|
||||
CoriolisIntegrator::CoriolisIntegrator(const mapping::DomainMapper& map, const mfem::Vector& omega)
|
||||
: m_map(map), m_omega(omega) {
|
||||
m_omega_mat.SetSize(3, 3);
|
||||
m_omega_mat = 0.0;
|
||||
m_omega_mat(0, 1) = -m_omega(2);
|
||||
m_omega_mat(0, 2) = m_omega(1);
|
||||
m_omega_mat(1, 0) = m_omega(2);
|
||||
m_omega_mat(1, 2) = -m_omega(0);
|
||||
m_omega_mat(2, 0) = -m_omega(1);
|
||||
m_omega_mat(2, 1) = m_omega(0);
|
||||
}
|
||||
|
||||
void CoriolisIntegrator::AssembleElementVector(const mfem::Array<const mfem::FiniteElement *> &el,
|
||||
mfem::ElementTransformation &Tr,
|
||||
const mfem::Array<const mfem::Vector *> &elfun,
|
||||
const mfem::Array<mfem::Vector *> &elvec
|
||||
) {
|
||||
if (utils::is_vacuum(Tr, elvec)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement* fe_v = el[0];
|
||||
const mfem::FiniteElement* fe_rho = el[1];
|
||||
|
||||
const int dof_v = fe_v->GetDof();
|
||||
const int dof_rho = fe_rho->GetDof();
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
|
||||
const mfem::Vector& v_dofs = *elfun[0];
|
||||
const mfem::Vector& rho_dofs = *elfun[1];
|
||||
|
||||
mfem::Vector& r_v = *elvec[0];
|
||||
r_v.SetSize(dof_v * dim);
|
||||
r_v = 0.0;
|
||||
if (elvec[1]) {
|
||||
elvec[1]->SetSize(dof_rho);
|
||||
*elvec[1] = 0.0;
|
||||
}
|
||||
|
||||
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
|
||||
const mfem::IntegrationRule* ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint& ip = ir->IntPoint(q);
|
||||
Tr.SetIntPoint(&ip);
|
||||
|
||||
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
|
||||
|
||||
fe_v->CalcShape(ip, shape_v);
|
||||
fe_rho->CalcShape(ip, shape_rho);
|
||||
|
||||
double rho_val = 0.0;
|
||||
for (int i = 0; i < dof_rho; ++i) rho_val += rho_dofs(i) * shape_rho(i);
|
||||
|
||||
mfem::Vector v_val(dim); v_val = 0.0;
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int c = 0; c < dim; ++c) v_val(c) += v_dofs(i + c * dof_v) * shape_v(i);
|
||||
}
|
||||
|
||||
mfem::Vector F_coriolis(dim);
|
||||
m_omega_mat.Mult(v_val, F_coriolis);
|
||||
F_coriolis *= 2.0;
|
||||
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
r_v(i + c * dof_v) += shape_v(i) * rho_val * F_coriolis(c) * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CoriolisIntegrator::AssembleElementGrad(const mfem::Array<const mfem::FiniteElement*> &el,
|
||||
mfem::ElementTransformation &Tr,
|
||||
const mfem::Array<const mfem::Vector *> &elfun,
|
||||
const mfem::Array2D<mfem::DenseMatrix *> &elmats
|
||||
) {
|
||||
|
||||
const mfem::FiniteElement* fe_v = el[0];
|
||||
const mfem::FiniteElement* fe_rho = el[1];
|
||||
|
||||
const int dof_v = fe_v->GetDof();
|
||||
const int dof_rho = fe_rho->GetDof();
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
|
||||
const mfem::Vector& v_dofs = *elfun[0];
|
||||
const mfem::Vector& rho_dofs = *elfun[1];
|
||||
|
||||
mfem::DenseMatrix* dv_dv = elmats(0, 0);
|
||||
mfem::DenseMatrix* dv_drho = elmats(0, 1);
|
||||
|
||||
if (dv_dv) *dv_dv = 0.0;
|
||||
if (dv_drho) *dv_drho = 0.0;
|
||||
|
||||
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
|
||||
const mfem::IntegrationRule* ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint& ip = ir->IntPoint(q);
|
||||
Tr.SetIntPoint(&ip);
|
||||
|
||||
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
|
||||
|
||||
fe_v->CalcShape(ip, shape_v);
|
||||
fe_rho->CalcShape(ip, shape_rho);
|
||||
|
||||
double rho_val = 0.0;
|
||||
for (int i = 0; i < dof_rho; ++i) rho_val += rho_dofs(i) * shape_rho(i);
|
||||
|
||||
mfem::Vector v_val(dim); v_val = 0.0;
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int c = 0; c < dim; ++c) v_val(c) += v_dofs(i + c * dof_v) * shape_v(i);
|
||||
}
|
||||
|
||||
mfem::Vector F_coriolis(dim);
|
||||
m_omega_mat.Mult(v_val, F_coriolis);
|
||||
F_coriolis *= 2.0;
|
||||
|
||||
if (dv_dv) {
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
int row = i + c * dof_v;
|
||||
for (int j = 0; j < dof_v; ++j) {
|
||||
for (int d = 0; d < dim; ++d) {
|
||||
int col = j + d * dof_v;
|
||||
double coupling = m_omega_mat(c, d);
|
||||
(*dv_dv)(row, col) += shape_v(i) * shape_v(j) * 2.0 * rho_val * coupling * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dv_drho) {
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
int row = i + c * dof_v;
|
||||
for (int j = 0; j < dof_rho; ++j) {
|
||||
int col = j;
|
||||
(*dv_drho)(row, col) += shape_v(i) * shape_rho(j) * F_coriolis(c) * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
124
libmeanfield/impl/integrators/gravity.cpp
Normal file
124
libmeanfield/impl/integrators/gravity.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
module;
|
||||
#include <mfem.hpp>
|
||||
module mean_field;
|
||||
|
||||
namespace mean_field::integrators {
|
||||
GravityForceIntegrator::GravityForceIntegrator(
|
||||
const mapping::DomainMapper& map,
|
||||
const mfem::GridFunction& phi
|
||||
): m_map(map), m_phi(&phi) {}
|
||||
|
||||
void GravityForceIntegrator::SetPotential(const mfem::GridFunction& phi) { m_phi = φ };
|
||||
|
||||
void GravityForceIntegrator::AssembleElementVector(
|
||||
const mfem::Array<const mfem::FiniteElement *> &el,
|
||||
mfem::ElementTransformation &Tr,
|
||||
const mfem::Array<const mfem::Vector *> &elfun,
|
||||
const mfem::Array<mfem::Vector *> &elvec
|
||||
) {
|
||||
|
||||
if (utils::is_vacuum(Tr, elvec)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement* fe_v = el[0];
|
||||
const mfem::FiniteElement* fe_rho = el[1];
|
||||
|
||||
const int dof_v = fe_v->GetDof();
|
||||
const int dof_rho = fe_rho->GetDof();
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
|
||||
const mfem::Vector& rho_dofs = *elfun[1];
|
||||
|
||||
mfem::Vector& r_v = *elvec[0];
|
||||
r_v.SetSize(dof_v * dim);
|
||||
r_v = 0.0;
|
||||
if (elvec[1]) {
|
||||
elvec[1]->SetSize(dof_rho);
|
||||
*elvec[1] = 0.0;
|
||||
}
|
||||
|
||||
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
|
||||
mfem::Vector grad_phi_ref(dim), grad_phi_phys(dim), grad_phi_elem(dim);
|
||||
|
||||
const mfem::IntegrationRule* ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint& ip = ir->IntPoint(q);
|
||||
Tr.SetIntPoint(&ip);
|
||||
|
||||
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
|
||||
m_phi->GetGradient(Tr, grad_phi_elem);
|
||||
mfem::DenseMatrix J_map(dim, dim), J_map_inv(dim, dim);
|
||||
m_map.ComputeJacobian(Tr, J_map);
|
||||
mfem::CalcInverse(J_map, J_map_inv);
|
||||
J_map_inv.MultTranspose(grad_phi_elem, grad_phi_phys);
|
||||
|
||||
fe_v->CalcShape(ip, shape_v);
|
||||
fe_rho->CalcShape(ip, shape_rho);
|
||||
|
||||
double rho_val = 0.0;
|
||||
for (int i = 0; i < dof_rho; ++i) {
|
||||
rho_val += rho_dofs(i) * shape_rho(i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
r_v(i + c * dof_v) += shape_v(i) * rho_val * grad_phi_phys(c) * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GravityForceIntegrator::AssembleElementGrad(
|
||||
const mfem::Array<const mfem::FiniteElement *> &el,
|
||||
mfem::ElementTransformation &Tr,
|
||||
const mfem::Array<const mfem::Vector *> &elfun,
|
||||
const mfem::Array2D<mfem::DenseMatrix *> &elmats
|
||||
) {
|
||||
const mfem::FiniteElement* fe_v = el[0];
|
||||
const mfem::FiniteElement* fe_rho = el[1];
|
||||
|
||||
const int dof_v = fe_v->GetDof();
|
||||
const int dof_rho = fe_rho->GetDof();
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
|
||||
mfem::DenseMatrix* dv_dv = elmats(0, 0);
|
||||
mfem::DenseMatrix* dv_drho = elmats(0, 1);
|
||||
|
||||
if (dv_dv) *dv_dv = 0.0;
|
||||
if (dv_drho) *dv_drho = 0.0;
|
||||
|
||||
if (!dv_drho) return;
|
||||
|
||||
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
|
||||
mfem::Vector grad_phi_ref(dim), grad_phi_phys(dim), grad_phi_elem(dim);
|
||||
|
||||
const mfem::IntegrationRule* ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint& ip = ir->IntPoint(q);
|
||||
Tr.SetIntPoint(&ip);
|
||||
|
||||
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
|
||||
m_phi->GetGradient(Tr, grad_phi_elem);
|
||||
mfem::DenseMatrix J_map(dim, dim), J_map_inv(dim, dim);
|
||||
m_map.ComputeJacobian(Tr, J_map);
|
||||
mfem::CalcInverse(J_map, J_map_inv);
|
||||
J_map_inv.MultTranspose(grad_phi_elem, grad_phi_phys);
|
||||
|
||||
fe_v->CalcShape(ip, shape_v);
|
||||
fe_rho->CalcShape(ip, shape_rho);
|
||||
|
||||
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
const int row = i + c * dof_v;
|
||||
for (int j = 0; j < dof_rho; ++j) {
|
||||
(*dv_drho)(row, j) += shape_v(i) * shape_rho(j) * grad_phi_phys(c) * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
417
libmeanfield/impl/integrators/mass_continuity.cpp
Normal file
417
libmeanfield/impl/integrators/mass_continuity.cpp
Normal file
@@ -0,0 +1,417 @@
|
||||
module;
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
|
||||
namespace mean_field::integrators {
|
||||
ContinuityVolumeIntegrator::ContinuityVolumeIntegrator(const mapping::DomainMapper& map) : m_map(map) {};
|
||||
|
||||
void ContinuityVolumeIntegrator::AssembleElementVector(
|
||||
const mfem::Array<const mfem::FiniteElement *> &el,
|
||||
mfem::ElementTransformation &Tr,
|
||||
const mfem::Array<const mfem::Vector *> &elfun,
|
||||
const mfem::Array<mfem::Vector *> &elvec
|
||||
) {
|
||||
if (utils::is_vacuum(Tr, elvec)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement *fe_v = el[0];
|
||||
const mfem::FiniteElement *fe_rho = el[1];
|
||||
|
||||
const int dof_v = fe_v->GetDof();
|
||||
const int dof_rho = fe_rho->GetDof();
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
|
||||
const mfem::Vector v_dofs = *elfun[0];
|
||||
const mfem::Vector rho_dofs = *elfun[1];
|
||||
|
||||
void* data_rho_before = elvec[1] ? (void*)elvec[1]->GetData() : nullptr;
|
||||
int size_rho_before = elvec[1] ? elvec[1]->Size() : -1;
|
||||
|
||||
if (elvec[0]) {
|
||||
elvec[0]->SetSize(dof_v * dim);
|
||||
*elvec[0] = 0.0;
|
||||
}
|
||||
mfem::Vector& r_rho = *elvec[1];
|
||||
r_rho.SetSize(dof_rho);
|
||||
r_rho = 0.0;
|
||||
|
||||
|
||||
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
|
||||
mfem::DenseMatrix dshape_rho_ref(dof_rho, dim), dshape_rho_phys(dof_rho, dim);
|
||||
|
||||
const mfem::IntegrationRule* ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint& ip = ir->IntPoint(q);
|
||||
Tr.SetIntPoint(&ip);
|
||||
|
||||
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
|
||||
|
||||
fe_v->CalcShape(ip, shape_v);
|
||||
fe_rho->CalcShape(ip, shape_rho);
|
||||
fe_rho->CalcDShape(ip, dshape_rho_ref);
|
||||
mfem::Mult(dshape_rho_ref, J_inv, dshape_rho_phys);
|
||||
|
||||
mfem::Vector v_val(dim); v_val = 0.0;
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
const int row = i + c * dof_v;
|
||||
v_val(c) += v_dofs(row) * shape_v(i);
|
||||
}
|
||||
}
|
||||
double rho_val = 0.0;
|
||||
for (int i = 0; i < dof_rho; ++i) {
|
||||
rho_val += rho_dofs(i) * shape_rho(i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < dof_rho; ++i) {
|
||||
double grad_dot_rhov = 0.0;
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
grad_dot_rhov += dshape_rho_phys(i, c) * rho_val * v_val(c);
|
||||
}
|
||||
r_rho(i) -= grad_dot_rhov * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ContinuityVolumeIntegrator::AssembleElementGrad(
|
||||
const mfem::Array<const mfem::FiniteElement *> &el,
|
||||
mfem::ElementTransformation &Tr,
|
||||
const mfem::Array<const mfem::Vector *> &elfun,
|
||||
const mfem::Array2D<mfem::DenseMatrix *> &elmats
|
||||
) {
|
||||
|
||||
const mfem::FiniteElement *fe_v = el[0];
|
||||
const mfem::FiniteElement *fe_rho = el[1];
|
||||
|
||||
const int dof_v = fe_v->GetDof();
|
||||
const int dof_rho = fe_rho->GetDof();
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
|
||||
const mfem::Vector& v_dofs = *elfun[0];
|
||||
const mfem::Vector& rho_dofs = *elfun[1];
|
||||
|
||||
mfem::DenseMatrix* drho_dv = elmats(1, 0);
|
||||
mfem::DenseMatrix* drho_drho = elmats(1, 1);
|
||||
|
||||
if (elmats(0, 0)) *elmats(0, 0) = 0.0;
|
||||
if (elmats(0, 1)) *elmats(0, 1) = 0.0;
|
||||
|
||||
if (drho_dv) *drho_dv = 0.0;
|
||||
if (drho_drho) *drho_drho = 0.0;
|
||||
|
||||
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
|
||||
mfem::DenseMatrix dshape_rho_ref(dof_rho, dim), dshape_rho_phys(dof_rho, dim);
|
||||
|
||||
const mfem::IntegrationRule* ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint& ip = ir->IntPoint(q);
|
||||
Tr.SetIntPoint(&ip);
|
||||
|
||||
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
|
||||
|
||||
fe_v->CalcShape(ip, shape_v);
|
||||
fe_rho->CalcShape(ip, shape_rho);
|
||||
fe_rho->CalcDShape(ip, dshape_rho_ref);
|
||||
mfem::Mult(dshape_rho_ref, J_inv, dshape_rho_phys);
|
||||
|
||||
mfem::Vector v_val(dim); v_val = 0.0;
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
const int row = i + c * dof_v;
|
||||
v_val(c) += v_dofs(row) * shape_v(i);
|
||||
}
|
||||
}
|
||||
|
||||
double rho_val = 0.0;
|
||||
for (int i = 0; i < dof_rho; ++i) {
|
||||
rho_val += rho_dofs(i) * shape_rho(i);
|
||||
}
|
||||
|
||||
if (drho_dv) {
|
||||
for (int i = 0; i < dof_rho; ++i) {
|
||||
for (int j = 0; j < dof_v; ++j) {
|
||||
for (int d = 0; d < dim; ++d) {
|
||||
const int col = j + d * dof_v;
|
||||
(*drho_dv)(i, col) -= dshape_rho_phys(i, d) * rho_val * shape_v(j) * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (drho_drho) {
|
||||
for (int i = 0; i < dof_rho; ++i) {
|
||||
double grad_psi_dot_v = 0.0;
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
grad_psi_dot_v += dshape_rho_phys(i, c) * v_val(c);
|
||||
}
|
||||
for (int j = 0; j < dof_rho; ++j) {
|
||||
(*drho_drho)(i, j) -= grad_psi_dot_v * shape_rho(j) * weight;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContinuityFaceIntegrator::ContinuityFaceIntegrator(const mapping::DomainMapper& map): m_map(map) {}
|
||||
|
||||
void ContinuityFaceIntegrator::AssembleFaceVector(
|
||||
const mfem::Array<const mfem::FiniteElement *> &el1,
|
||||
const mfem::Array<const mfem::FiniteElement *> &el2,
|
||||
mfem::FaceElementTransformations &Tr,
|
||||
const mfem::Array<const mfem::Vector *> &elfun,
|
||||
const mfem::Array<mfem::Vector *> &elvect
|
||||
) {
|
||||
const mfem::FiniteElement *fe_v_minus = el1[0];
|
||||
const mfem::FiniteElement *fe_v_plus = el2[0];
|
||||
|
||||
const mfem::FiniteElement *fe_rho_minus = el1[1];
|
||||
const mfem::FiniteElement *fe_rho_plus = el2[1];
|
||||
|
||||
const int dof_v_minus = fe_v_minus->GetDof();
|
||||
const int dof_v_plus = fe_v_plus->GetDof();
|
||||
|
||||
const int dof_rho_minus = fe_rho_minus->GetDof();
|
||||
const int dof_rho_plus = fe_rho_plus->GetDof();
|
||||
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
|
||||
if (elvect[0]) {
|
||||
elvect[0]->SetSize(dim * dof_v_minus + dim * dof_v_plus);
|
||||
*elvect[0] = 0.0;
|
||||
}
|
||||
mfem::Vector &r_rho = *elvect[1];
|
||||
r_rho.SetSize(dof_rho_minus + dof_rho_plus);
|
||||
r_rho = 0.0;
|
||||
|
||||
const int attr_minus = Tr.Elem1->Attribute;
|
||||
const int attr_plus = (Tr.Elem2 != nullptr) ? Tr.Elem2->Attribute : -1;
|
||||
constexpr int VACUUM_ATTR = 3;
|
||||
|
||||
if (attr_minus == VACUUM_ATTR || attr_plus == VACUUM_ATTR) {
|
||||
return; // No flux contribution for vacuum faces
|
||||
}
|
||||
|
||||
if (Tr.Elem2 == nullptr) {
|
||||
return; // Boundary face,
|
||||
}
|
||||
|
||||
const mfem::Vector &v_dofs = *elfun[0]; // Size: dim * dof_v_minus + dim*dof_v_plus
|
||||
const mfem::Vector &rho_dofs = *elfun[1]; // Size: dof_rho_minus + dof_rho_plus
|
||||
|
||||
// Helpers to auto offset to the correct point in the dof array
|
||||
auto rho_minus_dof = [&](const int i) {return rho_dofs(i);};
|
||||
auto rho_plus_dof = [&](const int i) {return rho_dofs(i + dof_rho_minus);};
|
||||
auto v_minus_dof = [&](const int k, const int c) {return v_dofs(k + c * dof_v_minus);};
|
||||
|
||||
const int p_v = fe_v_minus->GetOrder();
|
||||
const int p_rho = fe_rho_minus->GetOrder();
|
||||
const int int_order = 2 * std::max(p_v, p_rho) + 1;
|
||||
|
||||
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(Tr.GetGeometryType(), int_order);
|
||||
|
||||
mfem::Vector shape_v_minus(dof_v_minus), shape_rho_minus(dof_rho_minus), shape_rho_plus(dof_rho_plus);
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint& face_ip = ir->IntPoint(q);
|
||||
|
||||
Tr.SetAllIntPoints(&face_ip);
|
||||
const mfem::IntegrationPoint &ip_minus = Tr.GetElement1IntPoint();
|
||||
const mfem::IntegrationPoint &ip_plus = Tr.GetElement2IntPoint();
|
||||
|
||||
auto [n_unit, ds, v_dot_n_scale] = m_map.GetFaceQuadratureContext(Tr, face_ip);
|
||||
|
||||
fe_v_minus->CalcShape(ip_minus, shape_v_minus);
|
||||
fe_rho_minus->CalcShape(ip_minus, shape_rho_minus);
|
||||
fe_rho_plus->CalcShape(ip_plus, shape_rho_plus);
|
||||
|
||||
// v dot n
|
||||
// u_n = ∑ n_c * ∑ v_kc * φ_k
|
||||
double u_n = 0.0;
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
double v_c = 0.0;
|
||||
for (int k = 0; k < dof_v_minus; ++k) {
|
||||
v_c += v_minus_dof(k, c) * shape_v_minus(k);
|
||||
}
|
||||
u_n += v_c * n_unit(c);
|
||||
}
|
||||
|
||||
double rho_minus_val = 0.0;
|
||||
for (int i = 0; i < dof_rho_minus; ++i) {
|
||||
rho_minus_val += shape_rho_minus(i) * rho_minus_dof(i);
|
||||
}
|
||||
|
||||
double rho_plus_val = 0.0;
|
||||
for (int i = 0; i < dof_rho_plus; ++i) {
|
||||
rho_plus_val += shape_rho_plus(i) * rho_plus_dof(i);
|
||||
}
|
||||
|
||||
// Upwind density
|
||||
// I use the convention that the flow is positive when moving from minus to plus
|
||||
const double rho_up = (u_n >= 0) ? rho_minus_val : rho_plus_val;
|
||||
|
||||
const double flux_weighted = u_n * rho_up * ds;
|
||||
|
||||
// Note the normals need to be in opposite directions for these two fluxes
|
||||
for (int i = 0; i < dof_rho_minus; ++i) {
|
||||
r_rho(i) += shape_rho_minus(i) * flux_weighted;
|
||||
}
|
||||
|
||||
for (int i = 0; i < dof_rho_plus; ++i) {
|
||||
r_rho(dof_rho_minus + i) -= shape_rho_plus(i) * flux_weighted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ContinuityFaceIntegrator::AssembleFaceGrad(
|
||||
const mfem::Array<const mfem::FiniteElement *> &el1,
|
||||
const mfem::Array<const mfem::FiniteElement *> &el2,
|
||||
mfem::FaceElementTransformations &Tr,
|
||||
const mfem::Array<const mfem::Vector *> &elfun,
|
||||
const mfem::Array2D<mfem::DenseMatrix *> &elmats
|
||||
) {
|
||||
const mfem::FiniteElement *fe_v_minus = el1[0];
|
||||
const mfem::FiniteElement *fe_v_plus = el2[0];
|
||||
const mfem::FiniteElement *fe_rho_minus = el1[1];
|
||||
const mfem::FiniteElement *fe_rho_plus = el2[1];
|
||||
|
||||
const int dof_v_minus = fe_v_minus->GetDof();
|
||||
const int dof_v_plus = fe_v_plus->GetDof();
|
||||
const int dof_rho_minus = fe_rho_minus->GetDof();
|
||||
const int dof_rho_plus = fe_rho_plus->GetDof();
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
|
||||
const int N_v_total = dim * (dof_v_minus + dof_v_plus);
|
||||
const int N_rho_total = dof_rho_minus + dof_rho_plus;
|
||||
|
||||
auto size_and_zero_mat = [&](mfem::DenseMatrix* mat, const int r_size, const int c_size) {
|
||||
if (mat) {
|
||||
mat->SetSize(r_size, c_size);
|
||||
*mat = 0.0;
|
||||
}
|
||||
};
|
||||
|
||||
size_and_zero_mat(elmats(0, 0), N_v_total, N_v_total);
|
||||
size_and_zero_mat(elmats(0, 1), N_v_total, N_rho_total);
|
||||
size_and_zero_mat(elmats(1, 0), N_rho_total, N_v_total);
|
||||
size_and_zero_mat(elmats(1, 1), N_rho_total, N_rho_total);
|
||||
|
||||
if (skip_face(Tr)) return;
|
||||
|
||||
mfem::DenseMatrix *drho_dv = elmats(1, 0);
|
||||
mfem::DenseMatrix *drho_drho = elmats(1, 1);
|
||||
|
||||
if (!drho_dv && !drho_drho) return;
|
||||
|
||||
const mfem::Vector &v_dofs = *elfun[0];
|
||||
const mfem::Vector &rho_dofs = *elfun[1];
|
||||
|
||||
const int int_order = 2 * std::max(fe_v_minus->GetOrder(), fe_rho_minus->GetOrder()) + 1;
|
||||
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(Tr.GetGeometryType(), int_order);
|
||||
|
||||
mfem::Vector shape_v_minus(dof_v_minus), shape_rho_minus(dof_rho_minus), shape_rho_plus(dof_rho_plus);
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint& face_ip = ir->IntPoint(q);
|
||||
Tr.SetAllIntPoints(&face_ip);
|
||||
const mfem::IntegrationPoint &ip_minus = Tr.GetElement1IntPoint();
|
||||
const mfem::IntegrationPoint &ip_plus = Tr.GetElement2IntPoint();
|
||||
|
||||
auto [n_unit, ds, v_dot_n_scale] = m_map.GetFaceQuadratureContext(Tr, face_ip);
|
||||
|
||||
fe_v_minus->CalcShape(ip_minus, shape_v_minus);
|
||||
fe_rho_minus->CalcShape(ip_minus, shape_rho_minus);
|
||||
fe_rho_plus->CalcShape(ip_plus, shape_rho_plus);
|
||||
|
||||
const double u_n = compute_u_n(v_dofs, shape_v_minus, n_unit, dof_v_minus, dim);
|
||||
|
||||
double rho_minus_val = 0.0;
|
||||
for (int i = 0; i < dof_rho_minus; ++i) {
|
||||
rho_minus_val += shape_rho_minus(i) * rho_dofs(i);
|
||||
}
|
||||
|
||||
double rho_plus_val = 0.0;
|
||||
for (int i = 0; i < dof_rho_plus; ++i) {
|
||||
rho_plus_val += shape_rho_plus(i) * rho_dofs(dof_rho_minus + i);
|
||||
}
|
||||
|
||||
const bool upwind_minus = (u_n >= 0.0);
|
||||
const double rho_up = upwind_minus ? rho_minus_val : rho_plus_val;
|
||||
|
||||
// (1, 1)
|
||||
if (drho_drho) {
|
||||
const double u_w = u_n * ds;
|
||||
|
||||
if (upwind_minus) {
|
||||
for (int ip = 0; ip < dof_rho_minus; ++ip) {
|
||||
const double col_w = u_w * shape_rho_minus(ip);
|
||||
for (int i = 0; i < dof_rho_minus; ++i) {
|
||||
(*drho_drho)(i, ip) += shape_rho_minus(i) * col_w;
|
||||
}
|
||||
for (int j = 0; j < dof_rho_plus; ++j) {
|
||||
(*drho_drho)(dof_rho_minus + j, ip) -= shape_rho_plus(j) * col_w;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int jp = 0; jp < dof_rho_plus; ++jp) {
|
||||
const double col_w = u_w * shape_rho_plus(jp);
|
||||
const int col_idx = dof_rho_minus + jp;
|
||||
for (int i = 0; i < dof_rho_minus; ++i) {
|
||||
(*drho_drho)(i, col_idx) += shape_rho_minus(i) * col_w;
|
||||
}
|
||||
for (int j = 0; j < dof_rho_plus; ++j) {
|
||||
(*drho_drho)(dof_rho_minus + j, col_idx) -= shape_rho_plus(j) * col_w;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// (1, 0)
|
||||
if (drho_dv) {
|
||||
const double rho_w = rho_up * ds;
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
const double n_c_rho_w = n_unit(c) * rho_w;
|
||||
for (int k = 0; k < dof_v_minus; ++k) {
|
||||
const int col_idx = k + c * dof_v_minus;
|
||||
const double col_w = n_c_rho_w * shape_v_minus(k);
|
||||
for (int i = 0; i < dof_rho_minus; ++i) {
|
||||
(*drho_dv)(i, col_idx) += shape_rho_minus(i) * col_w;
|
||||
}
|
||||
for (int j = 0; j < dof_rho_plus; ++j) {
|
||||
(*drho_dv)(dof_rho_minus + j, col_idx) -= shape_rho_plus(j) * col_w;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ContinuityFaceIntegrator::skip_face(const mfem::FaceElementTransformations& Tr) {
|
||||
constexpr int VACUUM_ATTR = 3;
|
||||
const int attr_minus = Tr.Elem1->Attribute;
|
||||
const int attr_plus = (Tr.Elem2 != nullptr) ? Tr.Elem2->Attribute : -1;
|
||||
if (attr_minus == VACUUM_ATTR || attr_plus == VACUUM_ATTR) {
|
||||
return true; // No flux contribution for vacuum faces
|
||||
}
|
||||
if (Tr.Elem2 == nullptr) {
|
||||
return true; // Boundary face,
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
double ContinuityFaceIntegrator::compute_u_n(const mfem::Vector& v_dofs, const mfem::Vector& shape_v_minus, const mfem::Vector& n_unit, int dof_v_minus, int dim) {
|
||||
double u_n = 0.0;
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
double v_c = 0.0;
|
||||
for (int k = 0; k < dof_v_minus; ++k) {
|
||||
v_c += v_dofs(k + c * dof_v_minus) * shape_v_minus(k);
|
||||
}
|
||||
u_n += v_c * n_unit(c);
|
||||
}
|
||||
return u_n;
|
||||
}
|
||||
}
|
||||
|
||||
156
libmeanfield/impl/integrators/viscosity.cpp
Normal file
156
libmeanfield/impl/integrators/viscosity.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
module;
|
||||
#include <mfem.hpp>
|
||||
module mean_field;
|
||||
|
||||
namespace mean_field::integrators {
|
||||
ViscosityIntegrator::ViscosityIntegrator(
|
||||
const mapping::DomainMapper& map,
|
||||
const double mu,
|
||||
const int quad_boost
|
||||
) : m_map(map), m_mu(mu), m_quad_boost(quad_boost) {}
|
||||
|
||||
void ViscosityIntegrator::SetMu(const double mu) { m_mu = mu; }
|
||||
|
||||
void ViscosityIntegrator::AssembleElementVector(
|
||||
const mfem::Array<const mfem::FiniteElement *> &el,
|
||||
mfem::ElementTransformation &Tr,
|
||||
const mfem::Array<const mfem::Vector *> &elfun,
|
||||
const mfem::Array<mfem::Vector *> &elvec
|
||||
) {
|
||||
if (utils::is_vacuum(Tr, elvec)) {
|
||||
return;
|
||||
}
|
||||
|
||||
void* data_before = (void*)elvec[0]->GetData();
|
||||
int size_before = elvec[0]->Size();
|
||||
|
||||
const mfem::FiniteElement* fe_v = el[0];
|
||||
const mfem::FiniteElement* fe_rho = el[1];
|
||||
|
||||
const int dof_v = fe_v->GetDof();
|
||||
const int dof_rho = fe_rho->GetDof();
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
|
||||
const mfem::Vector& v_dofs = *elfun[0];
|
||||
|
||||
mfem::Vector& r_v = *elvec[0];
|
||||
r_v.SetSize(dof_v * dim);
|
||||
r_v = 0.0;
|
||||
if (elvec[1]) {
|
||||
elvec[1]->SetSize(dof_rho);
|
||||
*elvec[1] = 0.0;
|
||||
}
|
||||
|
||||
mfem::DenseMatrix dshape_v_ref(dof_v, dim), dshape_v_phys(dof_v, dim);
|
||||
|
||||
const mfem::IntegrationRule* ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder() + m_quad_boost);
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
|
||||
|
||||
const mfem::IntegrationPoint& ip = ir->IntPoint(q);
|
||||
Tr.SetIntPoint(&ip);
|
||||
|
||||
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
|
||||
|
||||
fe_v->CalcDShape(ip, dshape_v_ref);
|
||||
mfem::Mult(dshape_v_ref, J_inv, dshape_v_phys);
|
||||
|
||||
// ∇v(c,j) = δj v_c
|
||||
mfem::DenseMatrix grad_v(dim, dim); grad_v = 0.0;
|
||||
for (int n = 0; n < dof_v; ++n) {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
const double vn_c = v_dofs(n + c * dof_v);
|
||||
for (int j = 0; j < dim; ++j) {
|
||||
grad_v( c, j) += vn_c * dshape_v_phys(n, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double div_v = 0.0;
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
div_v += grad_v(c, c);
|
||||
}
|
||||
|
||||
// D_cj = dj v_c + dc v_j - (2/3) δcj ∇v
|
||||
// R_v_i^c += μ * weight * ∑_j (δj φi) D_cj
|
||||
const double mu_w = m_mu * weight;
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
double acc = 0.0;
|
||||
for (int j = 0; j < dim; ++j) {
|
||||
double D_cj = grad_v(c, j) + grad_v(j, c);
|
||||
if (c == j) D_cj -= (2.0 / 3.0) * div_v;
|
||||
acc += dshape_v_phys(i, j) * D_cj;
|
||||
}
|
||||
r_v(i + c * dof_v) += mu_w * acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ViscosityIntegrator::AssembleElementGrad(
|
||||
const mfem::Array<const mfem::FiniteElement *> &el,
|
||||
mfem::ElementTransformation &Tr,
|
||||
const mfem::Array<const mfem::Vector *> &elfun,
|
||||
const mfem::Array2D<mfem::DenseMatrix *> &elmats
|
||||
) {
|
||||
const mfem::FiniteElement* fe_v = el[0];
|
||||
const mfem::FiniteElement* fe_rho = el[1];
|
||||
|
||||
const int dof_v = fe_v->GetDof();
|
||||
const int dof_rho = fe_rho->GetDof();
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
|
||||
mfem::DenseMatrix* dv_dv = elmats(0, 0);
|
||||
mfem::DenseMatrix* dv_drho = elmats(0, 1);
|
||||
|
||||
if (dv_drho) *dv_drho =0.0;
|
||||
if (dv_dv) *dv_dv = 0.0;
|
||||
if (elmats(1, 0)) *elmats(1, 0) = 0.0;
|
||||
if (elmats(1, 1)) *elmats(1, 1) = 0.0;
|
||||
if (!dv_dv) return;
|
||||
|
||||
mfem::DenseMatrix dshape_v_ref(dof_v, dim), dshape_v_phys(dof_v, dim);
|
||||
|
||||
const mfem::IntegrationRule* ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint& ip = ir->IntPoint(q);
|
||||
Tr.SetIntPoint(&ip);
|
||||
|
||||
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
|
||||
|
||||
fe_v->CalcDShape(ip, dshape_v_ref);
|
||||
mfem::Mult(dshape_v_ref, J_inv, dshape_v_phys);
|
||||
|
||||
const double mu_w = m_mu * weight;
|
||||
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int n = 0; n < dof_v; ++n) {
|
||||
double dot_grad = 0.0;
|
||||
for (int j = 0; j < dim; ++j) {
|
||||
dot_grad += dshape_v_phys(i, j) * dshape_v_phys(n, j);
|
||||
}
|
||||
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
const int row = i + c * dof_v;
|
||||
for (int d = 0; d < dim; ++d) {
|
||||
const int col = n + d * dof_v;
|
||||
|
||||
double val = 0.0;
|
||||
if (c == d) val += dot_grad;
|
||||
|
||||
val += dshape_v_phys(i, d) * dshape_v_phys(n, c);
|
||||
|
||||
val -= (2.0 / 3.0) * dshape_v_phys(i, c) * dshape_v_phys(n, d);
|
||||
(*dv_dv)(row, col) += mu_w * val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user