// MFEM Example 37 - Serial/Parallel Shared Code #include "mfem.hpp" #include #include #include namespace mfem { /// @brief Inverse sigmoid function double inv_sigmoid(double x) { double tol = 1e-12; x = std::min(std::max(tol,x),1.0-tol); return std::log(x/(1.0-x)); } /// @brief Sigmoid function double sigmoid(double x) { if (x >= 0) { return 1.0/(1.0+std::exp(-x)); } else { return std::exp(x)/(1.0+std::exp(x)); } } /// @brief Derivative of sigmoid function double der_sigmoid(double x) { double tmp = sigmoid(-x); return tmp - std::pow(tmp,2); } /// @brief Returns f(u(x)) where u is a scalar GridFunction and f:R → R class MappedGridFunctionCoefficient : public GridFunctionCoefficient { protected: std::function fun; // f:R → R public: MappedGridFunctionCoefficient() :GridFunctionCoefficient(), fun([](double x) {return x;}) {} MappedGridFunctionCoefficient(const GridFunction *gf, std::function fun_, int comp=1) :GridFunctionCoefficient(gf, comp), fun(fun_) {} virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip) { return fun(GridFunctionCoefficient::Eval(T, ip)); } void SetFunction(std::function fun_) { fun = fun_; } }; /// @brief Returns f(u(x)) - f(v(x)) where u, v are scalar GridFunctions and f:R → R class DiffMappedGridFunctionCoefficient : public GridFunctionCoefficient { protected: const GridFunction *OtherGridF; GridFunctionCoefficient OtherGridF_cf; std::function fun; // f:R → R public: DiffMappedGridFunctionCoefficient() :GridFunctionCoefficient(), OtherGridF(nullptr), OtherGridF_cf(), fun([](double x) {return x;}) {} DiffMappedGridFunctionCoefficient(const GridFunction *gf, const GridFunction *other_gf, std::function fun_, int comp=1) :GridFunctionCoefficient(gf, comp), OtherGridF(other_gf), OtherGridF_cf(OtherGridF), fun(fun_) {} virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip) { const double value1 = fun(GridFunctionCoefficient::Eval(T, ip)); const double value2 = fun(OtherGridF_cf.Eval(T, ip)); return value1 - value2; } void SetFunction(std::function fun_) { fun = fun_; } }; /// @brief Solid isotropic material penalization (SIMP) coefficient class SIMPInterpolationCoefficient : public Coefficient { protected: GridFunction *rho_filter; double min_val; double max_val; double exponent; public: SIMPInterpolationCoefficient(GridFunction *rho_filter_, double min_val_= 1e-6, double max_val_ = 1.0, double exponent_ = 3) : rho_filter(rho_filter_), min_val(min_val_), max_val(max_val_), exponent(exponent_) { } virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip) { double val = rho_filter->GetValue(T, ip); double coeff = min_val + pow(val,exponent)*(max_val-min_val); return coeff; } }; /// @brief Strain energy density coefficient class StrainEnergyDensityCoefficient : public Coefficient { protected: Coefficient * lambda=nullptr; Coefficient * mu=nullptr; GridFunction *u = nullptr; // displacement GridFunction *rho_filter = nullptr; // filter density DenseMatrix grad; // auxiliary matrix, used in Eval double exponent; double rho_min; public: StrainEnergyDensityCoefficient(Coefficient *lambda_, Coefficient *mu_, GridFunction * u_, GridFunction * rho_filter_, double rho_min_=1e-6, double exponent_ = 3.0) : lambda(lambda_), mu(mu_), u(u_), rho_filter(rho_filter_), exponent(exponent_), rho_min(rho_min_) { MFEM_ASSERT(rho_min_ >= 0.0, "rho_min must be >= 0"); MFEM_ASSERT(rho_min_ < 1.0, "rho_min must be > 1"); MFEM_ASSERT(u, "displacement field is not set"); MFEM_ASSERT(rho_filter, "density field is not set"); } virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip) { double L = lambda->Eval(T, ip); double M = mu->Eval(T, ip); u->GetVectorGradient(T, grad); double div_u = grad.Trace(); double density = L*div_u*div_u; int dim = T.GetSpaceDim(); for (int i=0; iGetValue(T,ip); return -exponent * pow(val, exponent-1.0) * (1-rho_min) * density; } }; /// @brief Volumetric force for linear elasticity class VolumeForceCoefficient : public VectorCoefficient { private: double r; Vector center; Vector force; public: VolumeForceCoefficient(double r_,Vector & center_, Vector & force_) : VectorCoefficient(center_.Size()), r(r_), center(center_), force(force_) { } using VectorCoefficient::Eval; virtual void Eval(Vector &V, ElementTransformation &T, const IntegrationPoint &ip) { Vector xx; xx.SetSize(T.GetDimension()); T.Transform(ip,xx); for (int i=0; i ess_bdr; Array neumann_bdr; GridFunction * u = nullptr; LinearForm * b = nullptr; bool parallel; #ifdef MFEM_USE_MPI ParMesh * pmesh = nullptr; ParFiniteElementSpace * pfes = nullptr; #endif public: DiffusionSolver() { } DiffusionSolver(Mesh * mesh_, int order_, Coefficient * diffcf_, Coefficient * cf_); void SetMesh(Mesh * mesh_) { mesh = mesh_; parallel = false; #ifdef MFEM_USE_MPI pmesh = dynamic_cast(mesh); if (pmesh) { parallel = true; } #endif } void SetOrder(int order_) { order = order_ ; } void SetDiffusionCoefficient(Coefficient * diffcf_) { diffcf = diffcf_; } void SetMassCoefficient(Coefficient * masscf_) { masscf = masscf_; } void SetRHSCoefficient(Coefficient * rhscf_) { rhscf = rhscf_; } void SetEssentialBoundary(const Array & ess_bdr_) { ess_bdr = ess_bdr_;}; void SetNeumannBoundary(const Array & neumann_bdr_) { neumann_bdr = neumann_bdr_;}; void SetNeumannData(Coefficient * neumann_cf_) {neumann_cf = neumann_cf_;} void SetEssBdrData(Coefficient * essbdr_cf_) {essbdr_cf = essbdr_cf_;} void SetGradientData(VectorCoefficient * gradient_cf_) {gradient_cf = gradient_cf_;} void ResetFEM(); void SetupFEM(); void Solve(); GridFunction * GetFEMSolution(); LinearForm * GetLinearForm() {return b;} #ifdef MFEM_USE_MPI ParGridFunction * GetParFEMSolution(); ParLinearForm * GetParLinearForm() { if (parallel) { return dynamic_cast(b); } else { MFEM_ABORT("Wrong code path. Call GetLinearForm"); return nullptr; } } #endif ~DiffusionSolver(); }; /** * @brief Class for solving linear elasticity: * * -∇ ⋅ σ(u) = f in Ω + BCs * * where * * σ(u) = λ ∇⋅u I + μ (∇ u + ∇uᵀ) * */ class LinearElasticitySolver { private: Mesh * mesh = nullptr; int order = 1; Coefficient * lambda_cf = nullptr; Coefficient * mu_cf = nullptr; VectorCoefficient * essbdr_cf = nullptr; VectorCoefficient * rhs_cf = nullptr; // FEM solver int dim; FiniteElementCollection * fec = nullptr; FiniteElementSpace * fes = nullptr; Array ess_bdr; Array neumann_bdr; GridFunction * u = nullptr; LinearForm * b = nullptr; bool parallel; #ifdef MFEM_USE_MPI ParMesh * pmesh = nullptr; ParFiniteElementSpace * pfes = nullptr; #endif public: LinearElasticitySolver() { } LinearElasticitySolver(Mesh * mesh_, int order_, Coefficient * lambda_cf_, Coefficient * mu_cf_); void SetMesh(Mesh * mesh_) { mesh = mesh_; parallel = false; #ifdef MFEM_USE_MPI pmesh = dynamic_cast(mesh); if (pmesh) { parallel = true; } #endif } void SetOrder(int order_) { order = order_ ; } void SetLameCoefficients(Coefficient * lambda_cf_, Coefficient * mu_cf_) { lambda_cf = lambda_cf_; mu_cf = mu_cf_; } void SetRHSCoefficient(VectorCoefficient * rhs_cf_) { rhs_cf = rhs_cf_; } void SetEssentialBoundary(const Array & ess_bdr_) { ess_bdr = ess_bdr_;}; void SetNeumannBoundary(const Array & neumann_bdr_) { neumann_bdr = neumann_bdr_;}; void SetEssBdrData(VectorCoefficient * essbdr_cf_) {essbdr_cf = essbdr_cf_;} void ResetFEM(); void SetupFEM(); void Solve(); GridFunction * GetFEMSolution(); LinearForm * GetLinearForm() {return b;} #ifdef MFEM_USE_MPI ParGridFunction * GetParFEMSolution(); ParLinearForm * GetParLinearForm() { if (parallel) { return dynamic_cast(b); } else { MFEM_ABORT("Wrong code path. Call GetLinearForm"); return nullptr; } } #endif ~LinearElasticitySolver(); }; // Poisson solver DiffusionSolver::DiffusionSolver(Mesh * mesh_, int order_, Coefficient * diffcf_, Coefficient * rhscf_) : mesh(mesh_), order(order_), diffcf(diffcf_), rhscf(rhscf_) { #ifdef MFEM_USE_MPI pmesh = dynamic_cast(mesh); if (pmesh) { parallel = true; } #endif SetupFEM(); } void DiffusionSolver::SetupFEM() { dim = mesh->Dimension(); fec = new H1_FECollection(order, dim); #ifdef MFEM_USE_MPI if (parallel) { pfes = new ParFiniteElementSpace(pmesh, fec); u = new ParGridFunction(pfes); b = new ParLinearForm(pfes); } else { fes = new FiniteElementSpace(mesh, fec); u = new GridFunction(fes); b = new LinearForm(fes); } #else fes = new FiniteElementSpace(mesh, fec); u = new GridFunction(fes); b = new LinearForm(fes); #endif *u=0.0; if (!ess_bdr.Size()) { if (mesh->bdr_attributes.Size()) { ess_bdr.SetSize(mesh->bdr_attributes.Max()); ess_bdr = 1; } } } void DiffusionSolver::Solve() { OperatorPtr A; Vector B, X; Array ess_tdof_list; #ifdef MFEM_USE_MPI if (parallel) { pfes->GetEssentialTrueDofs(ess_bdr,ess_tdof_list); } else { fes->GetEssentialTrueDofs(ess_bdr,ess_tdof_list); } #else fes->GetEssentialTrueDofs(ess_bdr,ess_tdof_list); #endif *u=0.0; if (b) { delete b; #ifdef MFEM_USE_MPI if (parallel) { b = new ParLinearForm(pfes); } else { b = new LinearForm(fes); } #else b = new LinearForm(fes); #endif } if (rhscf) { b->AddDomainIntegrator(new DomainLFIntegrator(*rhscf)); } if (neumann_cf) { MFEM_VERIFY(neumann_bdr.Size(), "neumann_bdr attributes not provided"); b->AddBoundaryIntegrator(new BoundaryLFIntegrator(*neumann_cf),neumann_bdr); } else if (gradient_cf) { MFEM_VERIFY(neumann_bdr.Size(), "neumann_bdr attributes not provided"); b->AddBoundaryIntegrator(new BoundaryNormalLFIntegrator(*gradient_cf), neumann_bdr); } b->Assemble(); BilinearForm * a = nullptr; #ifdef MFEM_USE_MPI if (parallel) { a = new ParBilinearForm(pfes); } else { a = new BilinearForm(fes); } #else a = new BilinearForm(fes); #endif a->AddDomainIntegrator(new DiffusionIntegrator(*diffcf)); if (masscf) { a->AddDomainIntegrator(new MassIntegrator(*masscf)); } a->Assemble(); if (essbdr_cf) { u->ProjectBdrCoefficient(*essbdr_cf,ess_bdr); } a->FormLinearSystem(ess_tdof_list, *u, *b, A, X, B); CGSolver * cg = nullptr; Solver * M = nullptr; #ifdef MFEM_USE_MPI if (parallel) { M = new HypreBoomerAMG; dynamic_cast(M)->SetPrintLevel(0); cg = new CGSolver(pmesh->GetComm()); } else { M = new GSSmoother((SparseMatrix&)(*A)); cg = new CGSolver; } #else M = new GSSmoother((SparseMatrix&)(*A)); cg = new CGSolver; #endif cg->SetRelTol(1e-12); cg->SetMaxIter(10000); cg->SetPrintLevel(0); cg->SetPreconditioner(*M); cg->SetOperator(*A); cg->Mult(B, X); delete M; delete cg; a->RecoverFEMSolution(X, *b, *u); delete a; } GridFunction * DiffusionSolver::GetFEMSolution() { return u; } #ifdef MFEM_USE_MPI ParGridFunction * DiffusionSolver::GetParFEMSolution() { if (parallel) { return dynamic_cast(u); } else { MFEM_ABORT("Wrong code path. Call GetFEMSolution"); return nullptr; } } #endif DiffusionSolver::~DiffusionSolver() { delete u; u = nullptr; delete fes; fes = nullptr; #ifdef MFEM_USE_MPI delete pfes; pfes=nullptr; #endif delete fec; fec = nullptr; delete b; } // Elasticity solver LinearElasticitySolver::LinearElasticitySolver(Mesh * mesh_, int order_, Coefficient * lambda_cf_, Coefficient * mu_cf_) : mesh(mesh_), order(order_), lambda_cf(lambda_cf_), mu_cf(mu_cf_) { #ifdef MFEM_USE_MPI pmesh = dynamic_cast(mesh); if (pmesh) { parallel = true; } #endif SetupFEM(); } void LinearElasticitySolver::SetupFEM() { dim = mesh->Dimension(); fec = new H1_FECollection(order, dim,BasisType::Positive); #ifdef MFEM_USE_MPI if (parallel) { pfes = new ParFiniteElementSpace(pmesh, fec, dim); u = new ParGridFunction(pfes); b = new ParLinearForm(pfes); } else { fes = new FiniteElementSpace(mesh, fec,dim); u = new GridFunction(fes); b = new LinearForm(fes); } #else fes = new FiniteElementSpace(mesh, fec, dim); u = new GridFunction(fes); b = new LinearForm(fes); #endif *u=0.0; if (!ess_bdr.Size()) { if (mesh->bdr_attributes.Size()) { ess_bdr.SetSize(mesh->bdr_attributes.Max()); ess_bdr = 1; } } } void LinearElasticitySolver::Solve() { GridFunction * x = nullptr; OperatorPtr A; Vector B, X; Array ess_tdof_list; #ifdef MFEM_USE_MPI if (parallel) { x = new ParGridFunction(pfes); pfes->GetEssentialTrueDofs(ess_bdr,ess_tdof_list); } else { x = new GridFunction(fes); fes->GetEssentialTrueDofs(ess_bdr,ess_tdof_list); } #else x = new GridFunction(fes); fes->GetEssentialTrueDofs(ess_bdr,ess_tdof_list); #endif *u=0.0; if (b) { delete b; #ifdef MFEM_USE_MPI if (parallel) { b = new ParLinearForm(pfes); } else { b = new LinearForm(fes); } #else b = new LinearForm(fes); #endif } if (rhs_cf) { b->AddDomainIntegrator(new VectorDomainLFIntegrator(*rhs_cf)); } b->Assemble(); *x = 0.0; BilinearForm * a = nullptr; #ifdef MFEM_USE_MPI if (parallel) { a = new ParBilinearForm(pfes); } else { a = new BilinearForm(fes); } #else a = new BilinearForm(fes); #endif a->AddDomainIntegrator(new ElasticityIntegrator(*lambda_cf, *mu_cf)); a->Assemble(); if (essbdr_cf) { u->ProjectBdrCoefficient(*essbdr_cf,ess_bdr); } a->FormLinearSystem(ess_tdof_list, *x, *b, A, X, B); CGSolver * cg = nullptr; Solver * M = nullptr; #ifdef MFEM_USE_MPI if (parallel) { M = new HypreBoomerAMG; dynamic_cast(M)->SetPrintLevel(0); cg = new CGSolver(pmesh->GetComm()); } else { M = new GSSmoother((SparseMatrix&)(*A)); cg = new CGSolver; } #else M = new GSSmoother((SparseMatrix&)(*A)); cg = new CGSolver; #endif cg->SetRelTol(1e-10); cg->SetMaxIter(10000); cg->SetPrintLevel(0); cg->SetPreconditioner(*M); cg->SetOperator(*A); cg->Mult(B, X); delete M; delete cg; a->RecoverFEMSolution(X, *b, *x); *u+=*x; delete a; delete x; } GridFunction * LinearElasticitySolver::GetFEMSolution() { return u; } #ifdef MFEM_USE_MPI ParGridFunction * LinearElasticitySolver::GetParFEMSolution() { if (parallel) { return dynamic_cast(u); } else { MFEM_ABORT("Wrong code path. Call GetFEMSolution"); return nullptr; } } #endif LinearElasticitySolver::~LinearElasticitySolver() { delete u; u = nullptr; delete fes; fes = nullptr; #ifdef MFEM_USE_MPI delete pfes; pfes=nullptr; #endif delete fec; fec = nullptr; delete b; } } // namespace mfem