25#include <unsupported/Eigen/SparseExtra>
27#include <polysolve/linear/FEMSolver.hpp>
37 const std::string full_mat_path =
args[
"output"][
"data"][
"full_mat"];
38 if (!full_mat_path.empty())
39 Eigen::saveMarket(stiffness, full_mat_path);
45 const Eigen::MatrixXd &solution,
54 Eigen::MatrixXd &solution)
86 for (
const std::string field : {
"pressure_boundary",
"pressure_cavity"})
88 const json::json_pointer path(
"/boundary_conditions/" + field);
89 if (
args.contains(path) && !
args.at(path).empty())
94 const bool is_time_dependent =
args.contains(
"time") && !
args[
"time"].is_null();
103 if (!
args.contains(
"preset_problem"))
105 problem = std::make_shared<assembler::GenericScalarProblem>(
"GenericScalar");
109 tmp[
"is_time_dependent"] = is_time_dependent;
112 auto bc =
args[
"boundary_conditions"];
126 t0 = is_time_dependent ?
args[
"time"][
"t0"].get<
double>() : 0.0;
127 time_steps = is_time_dependent ?
args[
"time"][
"time_steps"].get<
int>() : 0;
128 dt = is_time_dependent ?
args[
"time"][
"dt"].get<
double>() : 0.0;
147 Eigen::VectorXi space_disc_orders, space_disc_ordersq;
150 if (
args[
"space"][
"use_p_ref"])
154 args[
"space"][
"advanced"][
"B"],
155 args[
"space"][
"advanced"][
"h1_formula"],
156 args[
"space"][
"discr_order"],
157 args[
"space"][
"advanced"][
"discr_order_max"],
161 logger().info(
"min p: {} max p: {}", space_disc_orders.minCoeff(), space_disc_orders.maxCoeff());
169 args[
"space"][
"basis_type"],
170 args[
"space"][
"poly_basis_type"],
173 args[
"space"][
"advanced"][
"quadrature_order"],
174 args[
"space"][
"advanced"][
"mass_quadrature_order"],
175 args[
"space"][
"advanced"][
"use_corner_quadrature"],
176 args[
"space"][
"advanced"][
"n_harmonic_samples"],
177 args[
"space"][
"advanced"][
"integral_constraints"],
195 std::vector<int> unused_neumann_boundary_nodes;
203 unused_neumann_boundary_nodes,
222 if (
problem->is_nodal_dimension_dirichlet(n_id, tag, 0))
232 if (
args[
"space"][
"advanced"][
"count_flipped_els"])
235 const int n_samples = 10;
245 logger().info(
"Building cache...");
249 logger().info(
" took {}s", timer.getElapsedTime());
261 json rhs_solver_params =
args[
"solver"][
"linear"];
262 if (!rhs_solver_params.contains(
"Pardiso"))
263 rhs_solver_params[
"Pardiso"] = {};
264 rhs_solver_params[
"Pardiso"][
"mtype"] = -2;
271 args[
"space"][
"advanced"][
"bc_method"],
285 delta = (max - min) / 2. + min;
287 p_params[
"bbox_center"] = {delta(0), delta(1), delta(2)};
289 p_params[
"bbox_center"] = {delta(0), delta(1)};
296 logger().info(
"Assigning rhs...");
309 if (!
problem->is_time_dependent())
320 logger().info(
"Assembling mass mat...");
324 assert(
mass_.size() > 0);
327 for (
int k = 0; k <
mass_.outerSize(); ++k)
329 for (StiffnessMatrix::InnerIterator it(
mass_, k); it; ++it)
331 assert(it.col() == k);
339 if (
args[
"solver"][
"advanced"][
"lump_mass_matrix"])
354 if (solution.size() > 0)
357 if (
problem->is_time_dependent())
361 solution.resize(
rhs_.size(), 1);
370 logger().error(
"Load the mesh first!");
373 if (solution.size() <= 0)
375 logger().error(
"Solve the problem first!");
379 logger().info(
"Saving json...");
381 const Eigen::MatrixXd stats_solution =
382 solution.rows() >= primary_size
383 ? solution.topRows(primary_size).eval()
391 args[
"output"][
"advanced"][
"sol_at_node"], j);
392 out << j.dump(4) << std::endl;
400 for (
int e = 0; e < output_orders.size(); ++e)
402 if (
mesh_->is_prism(e))
422 if (!
args[
"output"][
"advanced"][
"compute_error"])
426 if (!
args[
"time"].is_null())
427 tend =
args[
"time"][
"tend"];
438 logger().error(
"Load the mesh first!");
441 if (solution.size() <= 0)
443 logger().error(
"Solve the problem first!");
450 const bool has_time =
args.contains(
"time") && !
args[
"time"].is_null();
451 double tend = has_time ?
args[
"time"][
"tend"].get<
double>() : 1.0;
466 if (!solution_path.empty())
468 const int primary_ndof = std::min<int>(solution.rows(),
space_.
n_bases);
469 const Eigen::MatrixXd primary_solution = solution.topRows(primary_ndof);
472 const Eigen::MatrixXd nodal_solution =
utils::unflatten(primary_solution, 1);
473 Eigen::MatrixXd reordered = Eigen::MatrixXd::Zero(nodal_solution.rows(), nodal_solution.cols());
477 if (node >= 0 && node < nodal_solution.rows() && input_node < reordered.rows())
478 reordered.row(input_node) = nodal_solution.row(node);
489 if (!nodes_path.empty())
494 for (
const auto &global : basis.global())
495 nodes.row(global.index) = global.node;
503 Eigen::MatrixXd stress;
504 Eigen::VectorXd mises;
509 if (!stress_path.empty())
511 if (!mises_path.empty())
518 const Eigen::MatrixXd &solution,
521 std::vector<io::OutputField> fields;
528 const int primary_ndof = std::min<int>(solution.rows(),
space_.
n_bases);
529 const Eigen::MatrixXd primary_solution = solution.topRows(primary_ndof);
531 const auto sample_dof_field = [&](
const Eigen::MatrixXd &dof_values, Eigen::MatrixXd &values, Eigen::MatrixXd *gradients =
nullptr) ->
bool {
532 if (dof_values.size() <= 0)
535 if (has_element_samples)
547 gradients->row(i).setZero();
551 Eigen::MatrixXd local_sol, local_grad;
554 element_id, sample.
local_points.row(i), dof_values, local_sol, local_grad);
555 values(i) = local_sol(0);
557 gradients->row(i) = local_grad;
560 if (output_rows > values.rows())
562 const int previous_rows = values.rows();
563 values.conservativeResize(output_rows, Eigen::NoChange);
564 values.bottomRows(output_rows - previous_rows).setZero();
567 gradients->conservativeResize(output_rows, Eigen::NoChange);
568 gradients->bottomRows(output_rows - previous_rows).setZero();
576 values.resize(sample.
node_ids.size(), 1);
577 for (
int i = 0; i < sample.
node_ids.size(); ++i)
579 const int node_id = sample.
node_ids(i);
580 if (node_id < 0 || node_id >= dof_values.rows())
582 values(i) = dof_values(node_id);
584 return sample.
points.rows() == 0 || sample.
points.rows() == values.rows();
590 const auto ¶view_options =
args[
"output"][
"paraview"][
"options"];
591 if (has_element_samples &&
problem->has_exact_sol() && sample.
points.rows() == output_rows)
593 Eigen::MatrixXd exact;
595 if (exact.rows() == output_rows)
601 Eigen::MatrixXd values;
602 if (sample_dof_field(primary_solution, values))
608 if ((paraview_options[
"nodes"] || (!options.
fields.empty() && options.
export_field(
"nodes")))
609 && has_element_samples
612 Eigen::MatrixXd dof_ids(primary_ndof, 1);
613 dof_ids.col(0).setLinSpaced(primary_ndof, 0, primary_ndof - 1);
614 Eigen::MatrixXd values;
615 if (sample_dof_field(dof_ids, values))
619 if ((paraview_options[
"jacobian_validity"] || (!options.
fields.empty() && options.
export_field(
"validity")))
620 && has_element_samples
621 &&
mesh_->dimension() == 1
625 Eigen::MatrixXd validity = Eigen::MatrixXd::Zero(output_rows, 1);
626 for (
int i = 0; i < sample.
element_ids.size(); ++i)
627 validity(i) = std::find(invalid_elements.begin(), invalid_elements.end(), sample.
element_ids(i)) != invalid_elements.end();
631 const bool export_solution_gradient =
633 if (options.
export_field(
"solution") || export_solution_gradient)
635 Eigen::MatrixXd values, gradients;
636 if (sample_dof_field(
638 export_solution_gradient ? &gradients :
nullptr))
642 if (export_solution_gradient)
647 if (paraview_options[
"material"] && has_element_samples)
649 const auto ¶ms = primary_assembler_->parameters();
650 std::map<std::string, Eigen::MatrixXd> param_values;
651 for (
const auto &[p, _] : params)
652 param_values[p].setZero(output_rows, 1);
654 Eigen::MatrixXd rhos = Eigen::MatrixXd::Zero(output_rows, 1);
655 const auto &density = mass_assembler_->density();
656 for (
int i = 0; i < sample.local_points.rows(); ++i)
658 const int element_id = sample.element_ids(i);
662 for (
const auto &[p, func] : params)
663 param_values.at(p)(i) = func(sample.local_points.row(i), sample.points.row(i), sample.time, element_id);
664 rhos(i) = density(sample.local_points.row(i), sample.points.row(i), sample.time, element_id);
667 for (
const auto &[name, values] : param_values)
668 if (options.export_field(name))
670 if (options.export_field(
"rho"))
674 if (paraview_options[
"body_ids"] && options.export_field(
"body_ids") && has_element_samples)
676 Eigen::MatrixXd ids = Eigen::MatrixXd::Zero(output_rows, 1);
677 for (
int i = 0; i < sample.element_ids.size(); ++i)
679 const int element_id = sample.element_ids(i);
681 ids(i) = mesh_->get_body_id(element_id);
693 logger().info(
"Assembling stiffness mat...");
694 assert(primary_assembler_->is_linear());
695 assert(problem->is_scalar());
697 primary_assembler_->assemble(mesh_->is_volume(),
space_.n_bases,
space_.basis_list(),
space_.geometry_basis_list(), ass_vals_cache_, 0, stiffness);
700 timings.assembling_stiffness_mat_time = timer.getElapsedTime();
701 logger().info(
" took {}s", timings.assembling_stiffness_mat_time);
703 stats.nn_zero = stiffness.nonZeros();
704 stats.num_dofs = stiffness.rows();
705 stats.mat_size = (
long long)stiffness.rows() * (
long long)stiffness.cols();
706 logger().info(
"sparsity: {}/{}", stats.nn_zero, stats.mat_size);
711 void ScalarVarForm::solve_linear_system(
712 const std::unique_ptr<polysolve::linear::Solver> &solver,
715 const bool compute_spectrum,
716 Eigen::MatrixXd &sol)
718 assert(primary_assembler_->is_linear());
719 assert(problem->is_scalar());
720 assert(rhs_assembler_ !=
nullptr);
723 stats.spectrum = dirichlet_solve(
727 boundary_.boundary_nodes,
730 args[
"output"][
"data"][
"stiffness_mat"],
736 solver->get_info(stats.solver_info);
738 const auto error = (A *
x - b).norm();
740 logger().error(
"Solver error: {}", error);
742 logger().debug(
"Solver error: {}", error);
745 void ScalarVarForm::solve_linear_system_with_constraints(
746 const std::unique_ptr<polysolve::linear::Solver> &solver,
749 const bool compute_spectrum,
752 Eigen::MatrixXd &sol)
754 const json &periodic_conditions = args[
"boundary_conditions"][
"periodic"];
755 const json &zero_mean = args[
"constraints"][
"zero_mean"];
756 const bool add_zero_mean =
757 zero_mean.is_boolean()
758 ? zero_mean.get<
bool>()
759 : (zero_mean.is_array()
760 && std::find(zero_mean.begin(), zero_mean.end(), 0) != zero_mean.end());
761 const bool has_global_constraints = !periodic_conditions.empty() || add_zero_mean;
763 if (!has_global_constraints)
765 solve_linear_system(solver, A, b, compute_spectrum, sol);
769 if (!zero_mean.is_boolean() && !zero_mean.is_array())
773 if (constraint_mass.rows() != A.rows() || constraint_mass.cols() != A.cols())
775 mass_assembler_->assemble(
776 mesh_->is_volume(),
space_.n_bases,
space_.basis_list(),
space_.geometry_basis_list(),
777 mass_ass_vals_cache_, 0, constraint_mass,
true);
779 if (constraint_mass.rows() != A.rows() || constraint_mass.cols() != A.cols())
780 log_and_throw_error(
"Unable to assemble scalar constraint mass matrix for {} DoFs", A.rows());
782 std::vector<std::shared_ptr<solver::AugmentedLagrangianForm>> constraint_forms;
783 if (!boundary_.boundary_nodes.empty())
785 constraint_forms.push_back(std::make_shared<solver::BCLagrangianForm>(
786 A.rows(), boundary_.boundary_nodes, boundary_.local_boundary,
787 boundary_.local_neumann_boundary, boundary_samples, constraint_mass,
788 *rhs_assembler_, 0, problem->is_time_dependent(), time));
791 for (
const json &condition : periodic_conditions)
793 const int fe_space = condition.value(
"fe_space", -1);
794 if (fe_space >= 0 && fe_space != 0)
797 const std::array<int, 2> boundary_ids = {{condition[
"boundary_ids"][0].get<
int>(),
798 condition[
"boundary_ids"][1].get<int>()}};
799 constraint_forms.push_back(std::make_shared<solver::PeriodicBoundaryLagrangianForm>(
800 A.rows(), 1, *mesh_,
space_.basis_list(),
801 boundary_.total_local_boundary, boundary_ids,
802 condition.value(
"tolerance", 1e-5)));
807 const Eigen::VectorXd
weights = constraint_mass * Eigen::VectorXd::Ones(A.rows());
808 const double weight_sum =
weights.cwiseAbs().sum();
812 std::vector<Eigen::Triplet<double>>
entries;
814 for (
int dof = 0; dof <
weights.size(); ++dof)
820 constraint_forms.push_back(std::make_shared<solver::MatrixLagrangianForm>(
821 C, Eigen::MatrixXd::Zero(1, 1)));
824 if (constraint_forms.empty())
826 solve_linear_system(solver, A, b, compute_spectrum, sol);
830 auto constraint_solver = polysolve::linear::Solver::create(args[
"solver"][
"linear"],
logger());
831 std::shared_ptr<polysolve::linear::Solver> shared_constraint_solver(std::move(constraint_solver));
833 A.rows(), time, {}, constraint_forms, shared_constraint_solver,
834 1, 1, constraint_mass, 1);
837 const Eigen::VectorXd affine_offset =
842 Eigen::VectorXd reduced_solution;
843 stats.spectrum = dirichlet_solve(
844 *solver, A, b, {}, reduced_solution, A.rows(),
845 args[
"output"][
"data"][
"stiffness_mat"],
849 solver->get_info(stats.solver_info);
851 const double error = (A * reduced_solution - b).norm();
853 logger().error(
"Solver error: {}", error);
855 logger().debug(
"Solver error: {}", error);
860 auto solver = polysolve::linear::Solver::create(args[
"solver"][
"linear"],
logger());
861 logger().info(
"{}...", solver->name());
863 const int gdiscr_order = mesh_->orders().size() <= 0 ? 1 : mesh_->orders().maxCoeff();
864 const QuadratureOrders boundary_samples = n_boundary_samples(
space_.disc_orders.maxCoeff(),
space_.disc_ordersq.maxCoeff(), gdiscr_order);
866 rhs_assembler_->set_bc(
867 boundary_.local_boundary, boundary_.boundary_nodes, boundary_samples,
868 (primary_assembler_->name() !=
"Bilaplacian") ? boundary_.local_neumann_boundary : std::vector<mesh::LocalBoundary>(), rhs_);
871 build_stiffness_mat(A);
873 Eigen::VectorXd b = rhs_;
874 solve_linear_system_with_constraints(
875 solver, A, b, args[
"output"][
"advanced"][
"spectrum"],
876 boundary_samples, 1, sol);
881 solve_data_.elastic_form = std::make_shared<solver::ElasticForm>(
883 *primary_assembler_, ass_vals_cache_, 0, 0, mesh_->is_volume(),
884 args[
"solver"][
"advanced"][
"jacobian_threshold"],
885 args[
"solver"][
"advanced"][
"check_inversion"],
886 args[
"solver"][
"advanced"][
"conservative_max_iter"]);
887 solve_data_.body_form = std::make_shared<solver::BodyForm>(
888 space_.ndof(), 0, boundary_.boundary_nodes, boundary_.local_boundary,
889 boundary_.local_neumann_boundary, boundary_samples, rhs_, *rhs_assembler_,
890 mass_assembler_->density(),
false,
false);
891 solve_data_.body_form->update_quantities(0, sol);
898 assert(problem->is_time_dependent());
899 assert(rhs_assembler_ !=
nullptr);
901 auto solver = polysolve::linear::Solver::create(args[
"solver"][
"linear"],
logger());
902 logger().info(
"{}...", solver->name());
905 args[
"time"][
"integrator"]);
906 bdf->init(sol, Eigen::VectorXd::Zero(sol.size()), Eigen::VectorXd::Zero(sol.size()), dt);
907 time_integrator = bdf;
908 restore_checkpoint_integrator(
909 time_integrator,
"/checkpoint/state/primary_integrator", dt,
912 save_timestep(t0, 0, t0, dt, sol);
916 Eigen::MatrixXd current_rhs = rhs_;
919 build_stiffness_mat(stiffness);
921 const int gdiscr_order = mesh_->orders().size() <= 0 ? 1 : mesh_->orders().maxCoeff();
923 for (
int t = 1; t <= time_steps; ++t)
925 const double time = t0 + t * dt;
927 rhs_assembler_->compute_energy_grad(
928 boundary_.local_boundary, boundary_.boundary_nodes, mass_assembler_->density(), n_b_samples,
929 boundary_.local_neumann_boundary, rhs_, time, current_rhs);
931 rhs_assembler_->set_bc(
932 boundary_.local_boundary, boundary_.boundary_nodes, n_b_samples, boundary_.local_neumann_boundary, current_rhs, sol, time);
935 Eigen::VectorXd b = (mass_ * bdf->weighted_sum_x_prevs()) / bdf->beta_dt();
936 for (
int i : boundary_.boundary_nodes)
940 solve_linear_system_with_constraints(
942 args[
"output"][
"advanced"][
"spectrum"].get<bool>() && t == time_steps,
943 n_b_samples, time, sol);
947 bdf->update_quantities(sol);
948 save_timestep(time, t, t0, dt, sol);
949 save_step_state(t0, dt, t, sol, time_integrator.get());
951 logger().info(
"{}/{} t={}", t, time_steps, time);
952 notify_time_step(t, time_steps, t0, dt);
956 void ScalarVarForm::solve_problem(
957 Eigen::MatrixXd &sol,
962 (!initial_condition_override
963 || (initial_condition_override->
velocity.size() == 0
964 && initial_condition_override->
acceleration.size() == 0))
965 &&
"Scalar formulations do not accept initial velocity or acceleration overrides");
967 stats.spectrum.setZero();
971 logger().info(
"Solving {}", primary_assembler_->name());
976 if (initial_condition_override && initial_condition_override->
solution.size() != 0)
978 sol = initial_condition_override->
solution;
980 sol.rows() ==
space_.ndof() && sol.cols() == 1
981 &&
"Scalar initial solution override must match the simulation DOFs and have one column");
983 else if (sol.size() <= 0)
984 prepare_initial_solution(sol);
987 sol.conservativeResize(Eigen::NoChange, 1);
990 time_integrator =
nullptr;
991 if (problem->is_time_dependent())
992 solve_transient(sol, post_step);
994 solve_static(sol, post_step);
997 timings.solving_time = timer.getElapsedTime();
998 logger().info(
" took {}s", timings.solving_time);
std::vector< Eigen::Triplet< double > > entries
std::array< Matrix< int, 3, 3 >, 3 > space_
#define POLYFEM_SCOPED_TIMER(...)
static std::shared_ptr< Assembler > make_assembler(const std::string &formulation)
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 assigning_rhs_time
time to computing the rhs
double assembling_mass_mat_time
time to assembly mass
int n_flipped
number of flipped elements, compute only when using count_flipped_els (false 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
virtual TVector full_to_reduced_grad(const TVector &full) const
virtual TVector reduced_to_full(const TVector &reduced) const
void full_hessian_to_reduced_hessian(StiffnessMatrix &hessian) const
class to store time stepping data
std::shared_ptr< assembler::RhsAssembler > rhs_assembler
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