20#include <polysolve/linear/FEMSolver.hpp>
24 using namespace varform::internal;
28 const Eigen::MatrixXd &solution,
38 Eigen::MatrixXd &solution)
79 for (
const std::string field : {
"pressure_boundary",
"pressure_cavity"})
81 const json::json_pointer path(
"/boundary_conditions/" + field);
82 if (
args.contains(path) && !
args.at(path).empty())
87 const bool is_time_dependent =
args.contains(
"time") && !
args[
"time"].is_null();
88 const json &discr_orders =
args.at(
"space").at(
"discr_order");
90 const json &materials =
args.at(
"materials");
91 if (materials.is_array() && materials.empty())
93 const json &first_material = materials.is_array() ? materials.at(0) : materials;
97 log_and_throw_error(
"Bilaplacian solution and auxiliary fields must use different FE space IDs.");
99 if (discr_orders.is_array())
101 bool has_solution_space =
false;
102 bool has_auxiliary_space =
false;
103 for (
const json &entry : discr_orders)
105 const int fe_space_id = entry.at(
"fe_space").get<
int>();
109 if (!has_solution_space || !has_auxiliary_space)
110 log_and_throw_error(
"Bilaplacian discretization-order lists must explicitly name the solution and auxiliary FE spaces.");
113 if (materials.is_array())
115 for (
const json &material : materials)
119 log_and_throw_error(
"All Bilaplacian materials must use the same solution and auxiliary FE space IDs.");
129 if (!
args.contains(
"preset_problem"))
131 problem = std::make_shared<assembler::GenericScalarProblem>(
"GenericScalar");
134 tmp[
"is_time_dependent"] = is_time_dependent;
137 auto bc =
args[
"boundary_conditions"];
150 t0 = is_time_dependent ?
args[
"time"][
"t0"].get<
double>() : 0.0;
151 time_steps = is_time_dependent ?
args[
"time"][
"time_steps"].get<
int>() : 0;
152 dt = is_time_dependent ?
args[
"time"][
"dt"].get<
double>() : 0.0;
159 logger().error(
"Load the mesh first!");
162 if (solution.size() <= 0)
164 logger().error(
"Solve the problem first!");
168 logger().info(
"Saving json...");
169 const Eigen::MatrixXd stats_solution =
179 args[
"output"][
"advanced"][
"sol_at_node"], j);
180 out << j.dump(4) << std::endl;
188 for (
int e = 0; e < output_orders.size(); ++e)
190 if (
mesh_->is_prism(e))
210 if (!
args[
"output"][
"advanced"][
"compute_error"])
214 if (!
args[
"time"].is_null())
215 tend =
args[
"time"][
"tend"];
217 Eigen::MatrixXd value, pressure;
228 logger().error(
"Load the mesh first!");
231 if (solution.size() <= 0)
233 logger().error(
"Solve the problem first!");
240 const bool has_time =
args.contains(
"time") && !
args[
"time"].is_null();
241 double tend = has_time ?
args[
"time"][
"tend"].get<
double>() : 1.0;
255 Eigen::MatrixXd value, pressure;
259 if (!solution_path.empty())
261 const int primary_ndof = std::min<int>(value.rows(),
space_.
n_bases);
262 const Eigen::MatrixXd primary_solution = value.topRows(primary_ndof);
265 const Eigen::MatrixXd nodal_solution =
utils::unflatten(primary_solution, 1);
266 Eigen::MatrixXd reordered = Eigen::MatrixXd::Zero(nodal_solution.rows(), nodal_solution.cols());
270 if (node >= 0 && node < nodal_solution.rows() && input_node < reordered.rows())
271 reordered.row(input_node) = nodal_solution.row(node);
282 if (!nodes_path.empty())
287 for (
const auto &global : basis.global())
288 nodes.row(global.index) = global.node;
296 Eigen::MatrixXd stress;
297 Eigen::VectorXd mises;
302 if (!stress_path.empty())
304 if (!mises_path.empty())
324 const std::vector<basis::ElementBases> &bases,
326 const int fe_space_id)
328 json rhs_solver_params =
args[
"solver"][
"linear"];
329 if (!rhs_solver_params.contains(
"Pardiso"))
330 rhs_solver_params[
"Pardiso"] = {};
331 rhs_solver_params[
"Pardiso"][
"mtype"] = -2;
333 return std::make_shared<assembler::RhsAssembler>(
338 args[
"space"][
"advanced"][
"bc_method"],
350 Eigen::VectorXi space_disc_orders, space_disc_ordersq;
353 if (
args[
"space"][
"use_p_ref"])
357 args[
"space"][
"advanced"][
"B"],
358 args[
"space"][
"advanced"][
"h1_formula"],
359 args[
"space"][
"discr_order"],
360 args[
"space"][
"advanced"][
"discr_order_max"],
364 logger().info(
"min p: {} max p: {}", space_disc_orders.minCoeff(), space_disc_orders.maxCoeff());
372 args[
"space"][
"basis_type"],
373 args[
"space"][
"poly_basis_type"],
376 args[
"space"][
"advanced"][
"quadrature_order"],
377 args[
"space"][
"advanced"][
"mass_quadrature_order"],
378 args[
"space"][
"advanced"][
"use_corner_quadrature"],
379 args[
"space"][
"advanced"][
"n_harmonic_samples"],
380 args[
"space"][
"advanced"][
"integral_constraints"],
388 if (
args[
"space"][
"advanced"][
"count_flipped_els"])
391 const int n_samples = 10;
401 logger().info(
"Building cache...");
405 logger().info(
" took {}s", timer.getElapsedTime());
420 const bool use_corner_quadrature =
args[
"space"][
"advanced"][
"use_corner_quadrature"];
421 const int quadrature_order =
args[
"space"][
"advanced"][
"quadrature_order"].get<
int>();
422 const int mass_quadrature_order =
args[
"space"][
"advanced"][
"mass_quadrature_order"].get<
int>();
423 Eigen::VectorXi pressure_disc_orders, pressure_disc_ordersq;
426 const std::string pressure_basis_type =
args[
"space"][
"basis_type"].get<std::string>() ==
"Bernstein" ?
"Bernstein" :
"Lagrange";
430 pressure_disc_orders,
431 pressure_disc_ordersq,
433 args[
"space"][
"poly_basis_type"],
437 mass_quadrature_order,
438 use_corner_quadrature,
439 args[
"space"][
"advanced"][
"n_harmonic_samples"],
440 args[
"space"][
"advanced"][
"integral_constraints"],
500 delta = (max - min) / 2. + min;
502 p_params[
"bbox_center"] = {delta(0), delta(1), delta(2)};
504 p_params[
"bbox_center"] = {delta(0), delta(1)};
511 logger().info(
"Assigning rhs...");
521 const int prev_size =
rhs_.rows();
531 const int gdiscr_order =
mesh_->orders().size() <= 0 ? 1 :
mesh_->orders().maxCoeff();
533 tmp_rhs_assembler->set_bc(
541 if (!
problem->is_time_dependent())
551 logger().info(
"Assembling mass mat...");
554 for (
int k = 0; k <
mass_.outerSize(); ++k)
555 for (StiffnessMatrix::InnerIterator it(
mass_, k); it; ++it)
557 assert(it.col() == k);
561 if (
args[
"solver"][
"advanced"][
"lump_mass_matrix"])
581 if (
problem->is_time_dependent())
585 sol.resize(
rhs_.size(), 1);
590 sol.conservativeResize(Eigen::NoChange, 1);
597 const int cols = std::max(1,
int(stacked.cols()));
600 const int primary_rows = std::min(
space_.
n_bases,
int(stacked.rows()));
601 if (primary_rows > 0)
602 primary.topRows(primary_rows) = stacked.topRows(primary_rows);
606 if (pressure_rows > 0)
607 pressure.topRows(pressure_rows) = stacked.middleRows(
space_.
n_bases, pressure_rows);
615 logger().info(
"Assembling stiffness mat...");
624 main_stiffness, mixed_stiffness, aux_stiffness, stiffness);
631 stats.
mat_size = (
long long)stiffness.rows() * (
long long)stiffness.cols();
636 const std::unique_ptr<polysolve::linear::Solver> &solver,
639 const bool compute_spectrum,
640 Eigen::MatrixXd &sol)
650 args[
"output"][
"data"][
"stiffness_mat"],
660 auto solver = polysolve::linear::Solver::create(
args[
"solver"][
"linear"],
logger());
661 logger().info(
"{}...", solver->name());
662 const int gdiscr_order =
mesh_->orders().size() <= 0 ? 1 :
mesh_->orders().maxCoeff();
669 Eigen::VectorXd b =
rhs_;
675 auto solver = polysolve::linear::Solver::create(
args[
"solver"][
"linear"],
logger());
676 logger().info(
"{}...", solver->name());
678 Eigen::MatrixXd value, pressure;
681 args[
"time"][
"integrator"]);
684 Eigen::MatrixXd::Zero(value.rows(), value.cols()),
685 Eigen::MatrixXd::Zero(value.rows(), value.cols()),
694 Eigen::MatrixXd current_rhs =
rhs_;
698 const int gdiscr_order =
mesh_->orders().size() <= 0 ? 1 :
mesh_->orders().maxCoeff();
703 const double time =
t0 + t *
dt;
712 const int old_rows = current_rhs.rows();
713 current_rhs.conservativeResize(
stacked_ndof(), current_rhs.cols());
715 current_rhs.bottomRows(
stacked_ndof() - old_rows).setZero();
720 Eigen::VectorXd b = Eigen::VectorXd::Zero(
stacked_ndof());
728 bdf->update_quantities(value.col(0));
738 Eigen::MatrixXd &sol,
742 assert(!initial_condition_override &&
"Bilaplacian does not support initial-condition overrides");
743 assert(!post_step &&
"Bilaplacian does not support post-step callbacks");
750 if (
problem->is_time_dependent())
764 const Eigen::MatrixXd &solution,
767 std::vector<io::OutputField> fields;
771 Eigen::MatrixXd value, pressure;
776 const int primary_ndof = std::min<int>(value.rows(),
space_.
n_bases);
777 const Eigen::MatrixXd primary_solution = value.topRows(primary_ndof);
779 const auto sample_dof_field = [&](
const Eigen::MatrixXd &dof_values, Eigen::MatrixXd &values, Eigen::MatrixXd *gradients =
nullptr) ->
bool {
780 if (dof_values.size() <= 0)
783 if (has_element_samples)
795 gradients->row(i).setZero();
799 Eigen::MatrixXd local_sol, local_grad;
802 element_id, sample.
local_points.row(i), dof_values, local_sol, local_grad);
803 values(i) = local_sol(0);
805 gradients->row(i) = local_grad;
808 if (output_rows > values.rows())
810 const int previous_rows = values.rows();
811 values.conservativeResize(output_rows, Eigen::NoChange);
812 values.bottomRows(output_rows - previous_rows).setZero();
815 gradients->conservativeResize(output_rows, Eigen::NoChange);
816 gradients->bottomRows(output_rows - previous_rows).setZero();
824 values.resize(sample.
node_ids.size(), 1);
825 for (
int i = 0; i < sample.
node_ids.size(); ++i)
827 const int node_id = sample.
node_ids(i);
828 if (node_id < 0 || node_id >= dof_values.rows())
830 values(i) = dof_values(node_id);
832 return sample.
points.rows() == 0 || sample.
points.rows() == values.rows();
838 const auto ¶view_options =
args[
"output"][
"paraview"][
"options"];
839 if (has_element_samples &&
problem->has_exact_sol() && sample.
points.rows() == output_rows)
841 Eigen::MatrixXd exact;
843 if (exact.rows() == output_rows)
849 Eigen::MatrixXd values;
850 if (sample_dof_field(primary_solution, values))
856 if ((paraview_options[
"nodes"] || (!options.
fields.empty() && options.
export_field(
"nodes")))
857 && has_element_samples
860 Eigen::MatrixXd dof_ids(primary_ndof, 1);
861 dof_ids.col(0).setLinSpaced(primary_ndof, 0, primary_ndof - 1);
862 Eigen::MatrixXd values;
863 if (sample_dof_field(dof_ids, values))
867 if ((paraview_options[
"jacobian_validity"] || (!options.
fields.empty() && options.
export_field(
"validity")))
868 && has_element_samples
869 &&
mesh_->dimension() == 1
873 Eigen::MatrixXd validity = Eigen::MatrixXd::Zero(output_rows, 1);
874 for (
int i = 0; i < sample.
element_ids.size(); ++i)
875 validity(i) = std::find(invalid_elements.begin(), invalid_elements.end(), sample.
element_ids(i)) != invalid_elements.end();
879 const bool export_solution_gradient =
881 if (options.
export_field(
"solution") || export_solution_gradient)
883 Eigen::MatrixXd values, gradients;
884 if (sample_dof_field(
886 export_solution_gradient ? &gradients :
nullptr))
890 if (export_solution_gradient)
895 if (paraview_options[
"material"] && has_element_samples)
897 const auto ¶ms = primary_assembler_->parameters();
898 std::map<std::string, Eigen::MatrixXd> param_values;
899 for (
const auto &[p, _] : params)
900 param_values[p].setZero(output_rows, 1);
902 Eigen::MatrixXd rhos = Eigen::MatrixXd::Zero(output_rows, 1);
903 const auto &density = mass_assembler_->density();
904 for (
int i = 0; i < sample.local_points.rows(); ++i)
906 const int element_id = sample.element_ids(i);
910 for (
const auto &[p, func] : params)
911 param_values.at(p)(i) = func(sample.local_points.row(i), sample.points.row(i), sample.time, element_id);
912 rhos(i) = density(sample.local_points.row(i), sample.points.row(i), sample.time, element_id);
915 for (
const auto &[name, values] : param_values)
916 if (options.export_field(name))
918 if (options.export_field(
"rho"))
922 if (paraview_options[
"body_ids"] && options.export_field(
"body_ids") && has_element_samples)
924 Eigen::MatrixXd ids = Eigen::MatrixXd::Zero(output_rows, 1);
925 for (
int i = 0; i < sample.element_ids.size(); ++i)
927 const int element_id = sample.element_ids(i);
929 ids(i) = mesh_->get_body_id(element_id);
934 const bool export_pressure_gradient =
935 !options.fields.empty() && options.export_field(
"pressure_gradient");
936 if (mesh_ && (options.export_field(
"pressure") || export_pressure_gradient || (!options.fields.empty() && options.export_field(
"auxiliary"))))
938 Eigen::MatrixXd values, gradients;
940 *mesh_, pressure_space_.basis_list(),
space_.geometry_basis_list(), sample, pressure, values,
941 export_pressure_gradient ? &gradients : nullptr))
943 if (options.export_field(
"pressure"))
945 if (export_pressure_gradient)
947 if (!options.fields.empty() && options.export_field(
"auxiliary"))
std::array< Matrix< int, 3, 3 >, 3 > space_
static std::shared_ptr< MixedAssembler > make_mixed_assembler(const std::string &formulation)
static std::string other_assembler_name(const std::string &formulation)
static void merge_mixed_matrices(const int n_bases, const int n_pressure_bases, const int problem_dim, const bool add_average, const StiffnessMatrix &velocity_stiffness, const StiffnessMatrix &mixed_stiffness, const StiffnessMatrix &pressure_stiffness, StiffnessMatrix &stiffness)
utility to merge 3 blocks of mixed matrices, A=velocity_stiffness, B=mixed_stiffness,...
static std::shared_ptr< Assembler > make_assembler(const std::string &formulation)
Caches basis evaluation and geometric mapping at every element.
void init(const bool is_volume, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const bool is_mass=false)
computes the basis evaluation and geometric mapping for each of the given ElementBases in bases initi...
void init_empty(const bool is_mass=false)
initialize an empty cache.
Represents one basis function and its gradient.
Stores the basis functions for a given element in a mesh (facet in 2d, cell in 3d).
static void interpolate_at_local_vals(const mesh::Mesh &mesh, const bool is_problem_scalar, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const int el_index, const Eigen::MatrixXd &local_pts, const Eigen::MatrixXd &fun, Eigen::MatrixXd &result, Eigen::MatrixXd &result_grad)
interpolate solution and gradient at element (calls interpolate_at_local_vals with sol)
static void compute_stress_at_quadrature_points(const mesh::Mesh &mesh, const bool is_problem_scalar, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const Eigen::VectorXi &disc_orders, const Eigen::VectorXi &disc_ordersq, const assembler::Assembler &assembler, const Eigen::MatrixXd &fun, const double t, Eigen::MatrixXd &result, Eigen::VectorXd &von_mises)
compute von mises stress at quadrature points for the function fun, also compute the interpolated fun...
void export_data(const OutputSpace &space, const OutputFieldFunction &output_fields, const bool is_time_dependent, const double tend_in, const double dt, const ExportOptions &opts, const std::string &vis_mesh_path) const
exports everytihng, txt, vtu, etc
double assembling_stiffness_mat_time
time to assembly
double assigning_rhs_time
time to computing the rhs
double assembling_mass_mat_time
time to assembly mass
double solving_time
time to solve
int n_flipped
number of flipped elements, compute only when using count_flipped_els (false by default)
json solver_info
information of the solver, eg num iteration, time, errors, etc the informations varies depending on t...
Eigen::Vector4d spectrum
spectrum of the stiffness matrix, enable only if POLYSOLVE_WITH_SPECTRA is ON (off by default)
void count_flipped_elements(const polyfem::mesh::Mesh &mesh, const std::vector< polyfem::basis::ElementBases > &gbases)
counts the number of flipped elements
void compute_errors(const int n_bases, const std::vector< polyfem::basis::ElementBases > &bases, const std::vector< polyfem::basis::ElementBases > &gbases, const polyfem::mesh::Mesh &mesh, const assembler::Problem &problem, const double tend, const Eigen::MatrixXd &sol)
compute errors
void compute_mesh_size(const polyfem::mesh::Mesh &mesh_in, const std::vector< polyfem::basis::ElementBases > &bases_in, const int n_samples, const bool use_curved_mesh_size)
computes the mesh size, it samples every edges n_samples times uses curved_mesh_size (false by defaul...
long long nn_zero
non zeros and sytem matrix size num dof is the total dof in the system
double mesh_size
max edge lenght
void save_json(const nlohmann::json &args, const int n_bases, const int n_pressure_bases, const Eigen::MatrixXd &sol, const mesh::Mesh &mesh, const Eigen::VectorXi &disc_orders, const Eigen::VectorXi &disc_ordersq, const assembler::Problem &problem, const OutRuntimeData &runtime, const std::string &formulation, const bool isoparametric, const int sol_at_node_id, nlohmann::json &j) const
saves the output statistic to a json object
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
virtual void bounding_box(RowVectorNd &min, RowVectorNd &max) const =0
computes the bbox of the mesh
virtual bool is_volume() const =0
checks if mesh is volume
void update_nodes(const Eigen::VectorXi &in_node_to_node)
Update the node ids to reorder them.
virtual int get_node_id(const int node_id) const
Get the boundary selection of a node.
static const ProblemFactory & factory()
std::shared_ptr< assembler::Problem > get_problem(const std::string &problem) const
static void p_refine(const mesh::Mesh &mesh, const double B, const bool h1_formula, const int base_p, const int discr_order_max, io::OutStatsData &stats, Eigen::VectorXi &disc_orders)
compute a priori prefinement
static std::shared_ptr< BDF > construct_bdf_integrator(const json ¶ms, DynamicOrder dynamic_order=DynamicOrder::Second)
Construct a BDF integrator for algorithms using BDF-specific operations.
bool write_matrix(const std::string &path, const Mat &mat)
Writes a matrix to a file. Determines the file format based on the path's extension.
Eigen::SparseMatrix< double > lump_matrix(const Eigen::SparseMatrix< double > &M)
Lump each row of a matrix into the diagonal.
Eigen::MatrixXd unflatten(const Eigen::VectorXd &x, int dim)
Unflatten rowwises, so every dim elements in x become a row.
std::vector< int > count_invalid(const int dim, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const Eigen::VectorXd &u, const unsigned max_iter)
spdlog::logger & logger()
Retrieves the current logger.
std::array< int, 2 > QuadratureOrders
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
void log_and_throw_error(const std::string &msg)
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
bool export_field(const std::string &field) const
std::vector< std::string > fields
Eigen::VectorXi primitive_ids
Eigen::VectorXi element_ids
Eigen::MatrixXd local_points