24 const Eigen::MatrixXd &solution,
30 Eigen::MatrixXd velocity, pressure;
32 writer.
write_matrix(
"/checkpoint/state/velocity", velocity);
33 writer.
write_matrix(
"/checkpoint/state/pressure", pressure);
38 Eigen::MatrixXd &solution)
42 for (
const std::string &path : {
43 "/checkpoint/state/velocity",
"/checkpoint/state/pressure"})
47 Eigen::MatrixXd velocity = reader.
read_matrix(
"/checkpoint/state/velocity");
48 Eigen::MatrixXd pressure = reader.
read_matrix(
"/checkpoint/state/pressure");
49 if (velocity.rows() !=
primary_ndof() || velocity.cols() != 1)
55 log_and_throw_error(
"Operator-splitting checkpoint contains inconsistent solution blocks.");
63 using namespace varform::internal;
92 const bool is_time_dependent =
args.contains(
"time") && !
args[
"time"].is_null();
99 if (!
args.contains(
"preset_problem"))
101 problem = std::make_shared<assembler::GenericTensorProblem>(
"GenericTensor");
105 tmp[
"is_time_dependent"] = is_time_dependent;
108 auto bc =
args[
"boundary_conditions"];
115 if (
args[
"preset_problem"][
"type"] ==
"Kernel")
130 t0 = is_time_dependent ?
args[
"time"][
"t0"].get<
double>() : 0.0;
131 time_steps = is_time_dependent ?
args[
"time"][
"time_steps"].get<
int>() : 0;
132 dt = is_time_dependent ?
args[
"time"][
"dt"].get<
double>() : 0.0;
142 logger().error(
"Load the mesh first!");
145 if (solution.size() <= 0)
147 logger().error(
"Solve the problem first!");
151 logger().info(
"Saving json...");
153 const Eigen::MatrixXd stats_solution =
154 solution.rows() >= primary_size
155 ? solution.topRows(primary_size).eval()
163 args[
"output"][
"advanced"][
"sol_at_node"], j);
164 out << j.dump(4) << std::endl;
172 for (
int e = 0; e < output_orders.size(); ++e)
174 if (
mesh_->is_prism(e))
194 if (!
args[
"output"][
"advanced"][
"compute_error"])
198 if (
args.contains(
"time") && !
args[
"time"].is_null())
200 if (
args[
"time"].contains(
"tend"))
201 tend =
args[
"time"][
"tend"].get<
double>();
206 Eigen::MatrixXd velocity, pressure;
217 logger().error(
"Load the mesh first!");
220 if (solution.size() <= 0)
222 logger().error(
"Solve the problem first!");
229 const bool has_time =
args.contains(
"time") && !
args[
"time"].is_null();
233 if (
args[
"time"].contains(
"tend"))
234 tend =
args[
"time"][
"tend"].get<
double>();
240 local_dt =
args[
"time"][
"dt"];
251 Eigen::MatrixXd velocity, pressure;
255 if (!solution_path.empty())
257 const int primary_rows = std::min<int>(velocity.rows(),
primary_ndof());
258 const Eigen::MatrixXd primary_solution = velocity.topRows(primary_rows);
262 Eigen::MatrixXd reordered = Eigen::MatrixXd::Zero(nodal_solution.rows(), nodal_solution.cols());
266 if (node >= 0 && node < nodal_solution.rows() && input_node < reordered.rows())
267 reordered.row(input_node) = nodal_solution.row(node);
278 if (!nodes_path.empty())
283 for (
const auto &global : basis.global())
284 nodes.row(global.index) = global.node;
292 Eigen::MatrixXd stress;
293 Eigen::VectorXd mises;
298 if (!stress_path.empty())
300 if (!mises_path.empty())
335 json rhs_solver_params =
args[
"solver"][
"linear"];
336 if (!rhs_solver_params.contains(
"Pardiso"))
337 rhs_solver_params[
"Pardiso"] = {};
338 rhs_solver_params[
"Pardiso"][
"mtype"] = -2;
345 args[
"space"][
"advanced"][
"bc_method"],
356 Eigen::VectorXi space_disc_orders, space_disc_ordersq;
359 if (
args[
"space"][
"use_p_ref"])
363 args[
"space"][
"advanced"][
"B"],
364 args[
"space"][
"advanced"][
"h1_formula"],
365 args[
"space"][
"discr_order"],
366 args[
"space"][
"advanced"][
"discr_order_max"],
370 logger().info(
"min p: {} max p: {}", space_disc_orders.minCoeff(), space_disc_orders.maxCoeff());
378 args[
"space"][
"basis_type"],
379 args[
"space"][
"poly_basis_type"],
382 args[
"space"][
"advanced"][
"quadrature_order"],
383 args[
"space"][
"advanced"][
"mass_quadrature_order"],
384 args[
"space"][
"advanced"][
"use_corner_quadrature"],
385 args[
"space"][
"advanced"][
"n_harmonic_samples"],
386 args[
"space"][
"advanced"][
"integral_constraints"],
394 if (
args[
"space"][
"advanced"][
"count_flipped_els"])
397 const int n_samples = 10;
410 igl::Timer cache_timer;
412 logger().info(
"Building cache...");
415 logger().info(
" took {}s", cache_timer.getElapsedTime());
425 const int prev_b_size = int(all_boundary.size());
426 const bool use_corner_quadrature =
args[
"space"][
"advanced"][
"use_corner_quadrature"];
427 const int quadrature_order =
args[
"space"][
"advanced"][
"quadrature_order"].get<
int>();
428 const int mass_quadrature_order =
args[
"space"][
"advanced"][
"mass_quadrature_order"].get<
int>();
429 Eigen::VectorXi pressure_disc_orders, pressure_disc_ordersq;
431 const std::string pressure_basis_type =
args[
"space"][
"basis_type"].get<std::string>() ==
"Bernstein" ?
"Bernstein" :
"Lagrange";
435 pressure_disc_orders,
436 pressure_disc_ordersq,
438 args[
"space"][
"poly_basis_type"],
442 mass_quadrature_order,
443 use_corner_quadrature,
444 args[
"space"][
"advanced"][
"n_harmonic_samples"],
445 args[
"space"][
"advanced"][
"integral_constraints"],
459 for (
const auto &lb : all_boundary)
482 for (
int d = 0; d < mesh.
dimension(); ++d)
507 delta = (max - min) / 2. + min;
509 p_params[
"bbox_center"] = {delta(0), delta(1), delta(2)};
511 p_params[
"bbox_center"] = {delta(0), delta(1)};
518 logger().info(
"Assigning rhs...");
524 const int prev_size =
rhs_.rows();
547 if (
problem->is_time_dependent())
551 sol.resize(
rhs_.size(), 1);
556 sol.conservativeResize(Eigen::NoChange, 1);
563 const int cols = std::max(1,
int(stacked.cols()));
567 const int primary_rows = std::min(
primary_ndof(),
int(stacked.rows()));
568 if (primary_rows > 0)
569 primary.topRows(primary_rows) = stacked.topRows(primary_rows);
574 if (pressure_rows > 0)
575 pressure.topRows(pressure_rows) = stacked.middleRows(
primary_ndof(), pressure_rows);
581 const int cols = std::max(primary.cols(), pressure.cols());
585 const int primary_rows = std::min(
primary_ndof(),
int(primary.rows()));
586 if (primary_rows > 0)
587 stacked.topRows(primary_rows) = primary.topRows(primary_rows);
590 if (pressure_rows > 0)
591 stacked.middleRows(
primary_ndof(), pressure_rows) = pressure.topRows(pressure_rows);
596 const Eigen::MatrixXd &solution,
599 std::vector<io::OutputField> fields;
603 Eigen::MatrixXd velocity, pressure;
606 const int field_dim =
mesh_->dimension();
609 const bool export_solution_gradient =
611 const bool export_pressure_gradient =
614 const auto resize_to_output_rows = [&](Eigen::MatrixXd &values) {
615 if (output_rows <= values.rows())
618 const int previous_rows = values.rows();
619 values.conservativeResize(output_rows, values.cols());
620 values.bottomRows(output_rows - previous_rows).setZero();
623 const auto sample_vector_field = [&](
const Eigen::MatrixXd &dof_values, Eigen::MatrixXd &values, Eigen::MatrixXd *gradients =
nullptr) ->
bool {
624 if (dof_values.size() <= 0 || field_dim <= 0)
627 if (has_element_samples)
637 values.row(i).setZero();
639 gradients->row(i).setZero();
643 Eigen::MatrixXd local_sol, local_grad;
646 element_id, sample.
local_points.row(i), dof_values, local_sol, local_grad);
648 for (
int d = 0; d < field_dim; ++d)
649 values(i, d) = local_sol(d);
651 gradients->row(i) = local_grad;
654 resize_to_output_rows(values);
656 resize_to_output_rows(*gradients);
662 values.resize(sample.
node_ids.size(), field_dim);
663 for (
int i = 0; i < sample.
node_ids.size(); ++i)
665 const int node_id = sample.
node_ids(i);
666 for (
int d = 0; d < field_dim; ++d)
668 const int dof = node_id * field_dim + d;
669 if (dof < 0 || dof >= dof_values.rows())
671 values(i, d) = dof_values(dof);
674 return sample.
points.rows() == 0 || sample.
points.rows() == values.rows();
680 Eigen::MatrixXd velocity_values, velocity_gradients;
681 const bool sampled_velocity = sample_vector_field(
682 velocity, velocity_values,
683 export_solution_gradient ? &velocity_gradients :
nullptr);
684 if (sampled_velocity && options.
export_field(
"velocity"))
686 if (sampled_velocity && options.export_field(
"solution"))
688 if (sampled_velocity && export_solution_gradient)
691 if (mesh_ && (options.export_field(
"pressure") || export_pressure_gradient))
693 Eigen::MatrixXd values, gradients;
694 if (sample_scalar_field(
695 *mesh_, pressure_space_.basis_list(),
space_.geometry_basis_list(), sample, pressure, values,
696 export_pressure_gradient ? &gradients : nullptr))
698 if (options.export_field(
"pressure"))
700 if (export_pressure_gradient)
705 const auto ¶view_options =
args[
"output"][
"paraview"][
"options"];
706 if (paraview_options[
"material"] && has_element_samples)
708 const auto ¶ms = primary_assembler_->parameters();
709 std::map<std::string, Eigen::MatrixXd> param_values;
710 for (
const auto &[p, _] : params)
711 param_values[p].setZero(output_rows, 1);
713 Eigen::MatrixXd rhos = Eigen::MatrixXd::Zero(output_rows, 1);
714 const auto &density = mass_assembler_->density();
715 for (
int i = 0; i < sample.local_points.rows(); ++i)
717 const int element_id = sample.element_ids(i);
721 for (
const auto &[p, func] : params)
722 param_values.at(p)(i) =
func(sample.local_points.row(i), sample.
points.row(i), sample.time, element_id);
723 rhos(i) = density(sample.local_points.row(i), sample.points.row(i), sample.time, element_id);
726 for (
const auto &[field_name, values] : param_values)
727 if (options.export_field(field_name))
729 if (options.export_field(
"rho"))
733 if (paraview_options[
"body_ids"] && options.export_field(
"body_ids") && has_element_samples)
735 Eigen::MatrixXd ids = Eigen::MatrixXd::Zero(output_rows, 1);
736 for (
int i = 0; i < sample.element_ids.size(); ++i)
738 const int element_id = sample.element_ids(i);
740 ids(i) = mesh_->get_body_id(element_id);
748 void OperatorSplittingVarForm::solve_problem(
749 Eigen::MatrixXd &sol,
753 assert(!initial_condition_override &&
"Operator splitting does not support initial-condition overrides");
754 assert(!post_step &&
"Operator splitting does not support post-step callbacks");
756 stats.spectrum.setZero();
758 if (!problem->is_time_dependent())
763 logger().info(
"Solving {}", primary_assembler_->name());
765 prepare_initial_solution(sol);
767 Eigen::MatrixXd velocity, pressure;
768 split_solution(sol, velocity, pressure);
769 stack_solution(velocity, pressure, sol);
770 save_timestep(t0, 0, t0, dt, sol);
772 Eigen::MatrixXd local_pts;
773 const auto &gbases =
space_.geometry_basis_list();
774 assert(
space_.disc_orders.size() > 0 &&
"Operator splitting requires initialized FE orders");
775 const int discr_order =
space_.disc_orders.maxCoeff();
776 if (mesh_->dimension() == 2)
778 if (gbases[0].bases.size() == 3)
785 if (gbases[0].bases.size() == 4)
791 std::vector<int> bnd_nodes;
792 bnd_nodes.reserve(boundary_.boundary_nodes.size() / std::max(1, mesh_->dimension()));
793 for (
auto it = boundary_.boundary_nodes.begin(); it != boundary_.boundary_nodes.end(); ++it)
795 if (!(*it % mesh_->dimension()))
797 bnd_nodes.push_back(*it / mesh_->dimension());
800 const int n_el = int(
space_.basis_list().size());
801 const int shape = gbases[0].bases.size();
803 auto fluid_assembler = std::dynamic_pointer_cast<assembler::OperatorSplitting>(primary_assembler_);
804 if (!fluid_assembler)
805 log_and_throw_error(
"Invalid assembler {}!", primary_assembler_ ? primary_assembler_->name() : name());
806 const double viscosity = fluid_assembler->viscosity()(0, 0, 0, 0, 0);
807 assert(viscosity >= 0);
809 logger().info(
"Matrices assembly...");
810 StiffnessMatrix stiffness_viscosity, mixed_stiffness, velocity_mass, pressure_stiffness;
814 lapl_assembler.
assemble(mesh_->is_volume(),
space_.n_bases,
space_.basis_list(), gbases, ass_vals_cache_, 0, stiffness_viscosity);
815 mass_assembler_->set_size(1);
816 mass_assembler_->assemble(mesh_->is_volume(),
space_.n_bases,
space_.basis_list(), gbases, mass_ass_vals_cache_, 0, mass_,
true);
818 lapl_assembler.
assemble(mesh_->is_volume(), pressure_space_.n_bases, pressure_space_.basis_list(), gbases, pressure_ass_vals_cache_, 0, pressure_stiffness);
820 mixed_assembler_->assemble(mesh_->is_volume(), pressure_space_.n_bases,
space_.n_bases, pressure_space_.basis_list(),
space_.basis_list(), gbases,
821 pressure_ass_vals_cache_, ass_vals_cache_, 0, mixed_stiffness);
822 mass_assembler_->set_size(mesh_->dimension());
823 mass_assembler_->assemble(mesh_->is_volume(),
space_.n_bases,
space_.basis_list(), gbases, mass_ass_vals_cache_, 0, velocity_mass,
true);
824 mixed_stiffness = mixed_stiffness.transpose();
825 logger().info(
"Matrices assembly ends!");
828 *mesh_, shape, n_el, boundary_.local_boundary, boundary_.boundary_nodes, pressure_boundary_.boundary_nodes, bnd_nodes, mass_,
829 stiffness_viscosity, pressure_stiffness, velocity_mass, dt, viscosity, args[
"solver"][
"linear"]);
831 const QuadratureOrders boundary_samples = n_boundary_samples(
space_.disc_orders.maxCoeff(),
space_.disc_ordersq.maxCoeff(), discr_order);
832 for (
int t = 1; t <= time_steps; ++t)
834 const double time = t0 + t * dt;
835 logger().info(
"{}/{} steps, t={}s", t, time_steps, time);
837 logger().info(
"Advection...");
838 if (args[
"space"][
"advanced"][
"use_particle_advection"])
841 solver.
advection(*mesh_, gbases,
space_.basis_list(), velocity, dt, local_pts);
842 logger().info(
"Advection finished!");
844 rhs_assembler_->set_bc(
845 boundary_.local_boundary, boundary_.boundary_nodes, boundary_samples, boundary_.local_neumann_boundary, velocity, Eigen::MatrixXd(), time);
847 logger().info(
"Solving diffusion...");
850 logger().info(
"Diffusion solved!");
852 solver.
external_force(*mesh_, *primary_assembler_, gbases,
space_.basis_list(), dt, velocity, local_pts, problem, time);
854 logger().info(
"Pressure projection...");
855 solver.
solve_pressure(mixed_stiffness, pressure_boundary_.boundary_nodes, velocity, pressure);
856 solver.
projection(
space_.n_bases, gbases,
space_.basis_list(), pressure_space_.basis_list(), local_pts, pressure, velocity);
857 logger().info(
"Pressure projection finished!");
859 pressure = pressure / dt;
861 rhs_assembler_->set_bc(
862 boundary_.local_boundary, boundary_.boundary_nodes, boundary_samples, boundary_.local_neumann_boundary, velocity, Eigen::MatrixXd(), time);
864 stack_solution(velocity, pressure, sol);
865 save_timestep(time, t, t0, dt, sol);
866 save_step_state(t0, dt, t, sol,
nullptr);
867 notify_time_step(t, time_steps, t0, dt);
871 timings.solving_time = timer.getElapsedTime();
872 logger().info(
" took {}s", timings.solving_time);
std::array< Matrix< int, 3, 3 >, 3 > space_
virtual void set_size(const int size)
static std::shared_ptr< MixedAssembler > make_mixed_assembler(const std::string &formulation)
static std::string other_assembler_name(const std::string &formulation)
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.
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, 9, 1 > assemble(const LinearAssemblerData &data) const override
computes local stiffness matrix (1x1) for bases i,j where i,j is passed in through data ie integral o...
Represents one basis function and its gradient.
Stores the basis functions for a given element in a mesh (facet in 2d, cell in 3d).
Eigen::MatrixXd read_matrix(const std::string &path) const
bool exists(const std::string &path) const
void write_matrix(const std::string &path, const Eigen::MatrixXd &value)
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...
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.
int dimension() const
utily for dimension
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
void solve_pressure(const StiffnessMatrix &mixed_stiffness, const std::vector< int > &pressure_boundary_nodes, Eigen::MatrixXd &sol, Eigen::MatrixXd &pressure)
void projection(const StiffnessMatrix &velocity_mass, const StiffnessMatrix &mixed_stiffness, const std::vector< int > &boundary_nodes_, Eigen::MatrixXd &sol, const Eigen::MatrixXd &pressure)
void external_force(const mesh::Mesh &mesh, const assembler::Assembler &assembler, const std::vector< basis::ElementBases > &gbases, const std::vector< basis::ElementBases > &bases, const double dt, Eigen::MatrixXd &sol, const Eigen::MatrixXd &local_pts, const std::shared_ptr< assembler::Problem > problem, const double time)
void advection(const mesh::Mesh &mesh, const std::vector< basis::ElementBases > &gbases, const std::vector< basis::ElementBases > &bases, Eigen::MatrixXd &sol, const double dt, const Eigen::MatrixXd &local_pts, const int order=1, const int RK=1)
void advection_FLIP(const mesh::Mesh &mesh, const std::vector< basis::ElementBases > &gbases, const std::vector< basis::ElementBases > &bases, Eigen::MatrixXd &sol, const double dt, const Eigen::MatrixXd &local_pts, const int order=1)
void solve_diffusion_1st(const StiffnessMatrix &mass, const std::vector< int > &bnd_nodes, Eigen::MatrixXd &sol)
void q_nodes_2d(const int q, Eigen::MatrixXd &val)
void p_nodes_2d(const int p, Eigen::MatrixXd &val)
void p_nodes_3d(const int p, Eigen::MatrixXd &val)
void q_nodes_3d(const int q, Eigen::MatrixXd &val)
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::MatrixXd unflatten(const Eigen::VectorXd &x, int dim)
Unflatten rowwises, so every dim elements in x become a row.
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 element_ids
Eigen::MatrixXd local_points