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;
91 for (
const std::string field : {
"pressure_boundary",
"pressure_cavity"})
93 const json::json_pointer path(
"/boundary_conditions/" + field);
94 if (
args.contains(path) && !
args.at(path).empty())
99 const bool is_time_dependent =
args.contains(
"time") && !
args[
"time"].is_null();
106 if (!
args.contains(
"preset_problem"))
108 problem = std::make_shared<assembler::GenericTensorProblem>(
"GenericTensor");
112 tmp[
"is_time_dependent"] = is_time_dependent;
115 auto bc =
args[
"boundary_conditions"];
122 if (
args[
"preset_problem"][
"type"] ==
"Kernel")
137 t0 = is_time_dependent ?
args[
"time"][
"t0"].get<
double>() : 0.0;
138 time_steps = is_time_dependent ?
args[
"time"][
"time_steps"].get<
int>() : 0;
139 dt = is_time_dependent ?
args[
"time"][
"dt"].get<
double>() : 0.0;
149 logger().error(
"Load the mesh first!");
152 if (solution.size() <= 0)
154 logger().error(
"Solve the problem first!");
158 logger().info(
"Saving json...");
160 const Eigen::MatrixXd stats_solution =
161 solution.rows() >= primary_size
162 ? solution.topRows(primary_size).eval()
170 args[
"output"][
"advanced"][
"sol_at_node"], j);
171 out << j.dump(4) << std::endl;
179 for (
int e = 0; e < output_orders.size(); ++e)
181 if (
mesh_->is_prism(e))
201 if (!
args[
"output"][
"advanced"][
"compute_error"])
205 if (
args.contains(
"time") && !
args[
"time"].is_null())
207 if (
args[
"time"].contains(
"tend"))
208 tend =
args[
"time"][
"tend"].get<
double>();
213 Eigen::MatrixXd velocity, pressure;
224 logger().error(
"Load the mesh first!");
227 if (solution.size() <= 0)
229 logger().error(
"Solve the problem first!");
236 const bool has_time =
args.contains(
"time") && !
args[
"time"].is_null();
240 if (
args[
"time"].contains(
"tend"))
241 tend =
args[
"time"][
"tend"].get<
double>();
247 local_dt =
args[
"time"][
"dt"];
258 Eigen::MatrixXd velocity, pressure;
262 if (!solution_path.empty())
264 const int primary_rows = std::min<int>(velocity.rows(),
primary_ndof());
265 const Eigen::MatrixXd primary_solution = velocity.topRows(primary_rows);
269 Eigen::MatrixXd reordered = Eigen::MatrixXd::Zero(nodal_solution.rows(), nodal_solution.cols());
273 if (node >= 0 && node < nodal_solution.rows() && input_node < reordered.rows())
274 reordered.row(input_node) = nodal_solution.row(node);
285 if (!nodes_path.empty())
290 for (
const auto &global : basis.global())
291 nodes.row(global.index) = global.node;
299 Eigen::MatrixXd stress;
300 Eigen::VectorXd mises;
305 if (!stress_path.empty())
307 if (!mises_path.empty())
342 json rhs_solver_params =
args[
"solver"][
"linear"];
343 if (!rhs_solver_params.contains(
"Pardiso"))
344 rhs_solver_params[
"Pardiso"] = {};
345 rhs_solver_params[
"Pardiso"][
"mtype"] = -2;
352 args[
"space"][
"advanced"][
"bc_method"],
363 Eigen::VectorXi space_disc_orders, space_disc_ordersq;
366 if (
args[
"space"][
"use_p_ref"])
370 args[
"space"][
"advanced"][
"B"],
371 args[
"space"][
"advanced"][
"h1_formula"],
372 args[
"space"][
"discr_order"],
373 args[
"space"][
"advanced"][
"discr_order_max"],
377 logger().info(
"min p: {} max p: {}", space_disc_orders.minCoeff(), space_disc_orders.maxCoeff());
385 args[
"space"][
"basis_type"],
386 args[
"space"][
"poly_basis_type"],
389 args[
"space"][
"advanced"][
"quadrature_order"],
390 args[
"space"][
"advanced"][
"mass_quadrature_order"],
391 args[
"space"][
"advanced"][
"use_corner_quadrature"],
392 args[
"space"][
"advanced"][
"n_harmonic_samples"],
393 args[
"space"][
"advanced"][
"integral_constraints"],
401 if (
args[
"space"][
"advanced"][
"count_flipped_els"])
404 const int n_samples = 10;
417 igl::Timer cache_timer;
419 logger().info(
"Building cache...");
422 logger().info(
" took {}s", cache_timer.getElapsedTime());
432 const int prev_b_size = int(all_boundary.size());
433 const bool use_corner_quadrature =
args[
"space"][
"advanced"][
"use_corner_quadrature"];
434 const int quadrature_order =
args[
"space"][
"advanced"][
"quadrature_order"].get<
int>();
435 const int mass_quadrature_order =
args[
"space"][
"advanced"][
"mass_quadrature_order"].get<
int>();
436 Eigen::VectorXi pressure_disc_orders, pressure_disc_ordersq;
438 const std::string pressure_basis_type =
args[
"space"][
"basis_type"].get<std::string>() ==
"Bernstein" ?
"Bernstein" :
"Lagrange";
442 pressure_disc_orders,
443 pressure_disc_ordersq,
445 args[
"space"][
"poly_basis_type"],
449 mass_quadrature_order,
450 use_corner_quadrature,
451 args[
"space"][
"advanced"][
"n_harmonic_samples"],
452 args[
"space"][
"advanced"][
"integral_constraints"],
482 for (
int d = 0; d < mesh.
dimension(); ++d)
483 if (
problem->is_nodal_dimension_dirichlet(n_id, tag, d, -1))
494 for (
int d = 0; d < mesh.
dimension(); ++d)
519 delta = (max - min) / 2. + min;
521 p_params[
"bbox_center"] = {delta(0), delta(1), delta(2)};
523 p_params[
"bbox_center"] = {delta(0), delta(1)};
530 logger().info(
"Assigning rhs...");
536 const int prev_size =
rhs_.rows();
559 if (
problem->is_time_dependent())
563 sol.resize(
rhs_.size(), 1);
568 sol.conservativeResize(Eigen::NoChange, 1);
575 const int cols = std::max(1,
int(stacked.cols()));
579 const int primary_rows = std::min(
primary_ndof(),
int(stacked.rows()));
580 if (primary_rows > 0)
581 primary.topRows(primary_rows) = stacked.topRows(primary_rows);
586 if (pressure_rows > 0)
587 pressure.topRows(pressure_rows) = stacked.middleRows(
primary_ndof(), pressure_rows);
593 const int cols = std::max(primary.cols(), pressure.cols());
597 const int primary_rows = std::min(
primary_ndof(),
int(primary.rows()));
598 if (primary_rows > 0)
599 stacked.topRows(primary_rows) = primary.topRows(primary_rows);
602 if (pressure_rows > 0)
603 stacked.middleRows(
primary_ndof(), pressure_rows) = pressure.topRows(pressure_rows);
608 const Eigen::MatrixXd &solution,
611 std::vector<io::OutputField> fields;
615 Eigen::MatrixXd velocity, pressure;
618 const int field_dim =
mesh_->dimension();
621 const bool export_solution_gradient =
623 const bool export_pressure_gradient =
626 const auto resize_to_output_rows = [&](Eigen::MatrixXd &values) {
627 if (output_rows <= values.rows())
630 const int previous_rows = values.rows();
631 values.conservativeResize(output_rows, values.cols());
632 values.bottomRows(output_rows - previous_rows).setZero();
635 const auto sample_vector_field = [&](
const Eigen::MatrixXd &dof_values, Eigen::MatrixXd &values, Eigen::MatrixXd *gradients =
nullptr) ->
bool {
636 if (dof_values.size() <= 0 || field_dim <= 0)
639 if (has_element_samples)
649 values.row(i).setZero();
651 gradients->row(i).setZero();
655 Eigen::MatrixXd local_sol, local_grad;
658 element_id, sample.
local_points.row(i), dof_values, local_sol, local_grad);
660 for (
int d = 0; d < field_dim; ++d)
661 values(i, d) = local_sol(d);
663 gradients->row(i) = local_grad;
666 resize_to_output_rows(values);
668 resize_to_output_rows(*gradients);
674 values.resize(sample.
node_ids.size(), field_dim);
675 for (
int i = 0; i < sample.
node_ids.size(); ++i)
677 const int node_id = sample.
node_ids(i);
678 for (
int d = 0; d < field_dim; ++d)
680 const int dof = node_id * field_dim + d;
681 if (dof < 0 || dof >= dof_values.rows())
683 values(i, d) = dof_values(dof);
686 return sample.
points.rows() == 0 || sample.
points.rows() == values.rows();
692 Eigen::MatrixXd velocity_values, velocity_gradients;
693 const bool sampled_velocity = sample_vector_field(
694 velocity, velocity_values,
695 export_solution_gradient ? &velocity_gradients :
nullptr);
696 if (sampled_velocity && options.
export_field(
"velocity"))
698 if (sampled_velocity && options.export_field(
"solution"))
700 if (sampled_velocity && export_solution_gradient)
703 if (mesh_ && (options.export_field(
"pressure") || export_pressure_gradient))
705 Eigen::MatrixXd values, gradients;
706 if (sample_scalar_field(
707 *mesh_, pressure_space_.basis_list(),
space_.geometry_basis_list(), sample, pressure, values,
708 export_pressure_gradient ? &gradients : nullptr))
710 if (options.export_field(
"pressure"))
712 if (export_pressure_gradient)
717 const auto ¶view_options =
args[
"output"][
"paraview"][
"options"];
718 if (paraview_options[
"material"] && has_element_samples)
720 const auto ¶ms = primary_assembler_->parameters();
721 std::map<std::string, Eigen::MatrixXd> param_values;
722 for (
const auto &[p, _] : params)
723 param_values[p].setZero(output_rows, 1);
725 Eigen::MatrixXd rhos = Eigen::MatrixXd::Zero(output_rows, 1);
726 const auto &density = mass_assembler_->density();
727 for (
int i = 0; i < sample.local_points.rows(); ++i)
729 const int element_id = sample.element_ids(i);
733 for (
const auto &[p, func] : params)
734 param_values.at(p)(i) =
func(sample.local_points.row(i), sample.
points.row(i), sample.time, element_id);
735 rhos(i) = density(sample.local_points.row(i), sample.points.row(i), sample.time, element_id);
738 for (
const auto &[field_name, values] : param_values)
739 if (options.export_field(field_name))
741 if (options.export_field(
"rho"))
745 if (paraview_options[
"body_ids"] && options.export_field(
"body_ids") && has_element_samples)
747 Eigen::MatrixXd ids = Eigen::MatrixXd::Zero(output_rows, 1);
748 for (
int i = 0; i < sample.element_ids.size(); ++i)
750 const int element_id = sample.element_ids(i);
752 ids(i) = mesh_->get_body_id(element_id);
760 void OperatorSplittingVarForm::solve_problem(
761 Eigen::MatrixXd &sol,
765 assert(!initial_condition_override &&
"Operator splitting does not support initial-condition overrides");
766 assert(!post_step &&
"Operator splitting does not support post-step callbacks");
768 stats.spectrum.setZero();
770 if (!problem->is_time_dependent())
775 logger().info(
"Solving {}", primary_assembler_->name());
777 prepare_initial_solution(sol);
779 Eigen::MatrixXd velocity, pressure;
780 split_solution(sol, velocity, pressure);
781 stack_solution(velocity, pressure, sol);
782 save_timestep(t0, 0, t0, dt, sol);
784 Eigen::MatrixXd local_pts;
785 const auto &gbases =
space_.geometry_basis_list();
786 assert(
space_.disc_orders.size() > 0 &&
"Operator splitting requires initialized FE orders");
787 const int discr_order =
space_.disc_orders.maxCoeff();
788 if (mesh_->dimension() == 2)
790 if (gbases[0].bases.size() == 3)
797 if (gbases[0].bases.size() == 4)
803 std::vector<int> bnd_nodes;
804 bnd_nodes.reserve(boundary_.boundary_nodes.size() / std::max(1, mesh_->dimension()));
805 for (
auto it = boundary_.boundary_nodes.begin(); it != boundary_.boundary_nodes.end(); ++it)
807 if (!(*it % mesh_->dimension()))
809 bnd_nodes.push_back(*it / mesh_->dimension());
812 const int n_el = int(
space_.basis_list().size());
813 const int shape = gbases[0].bases.size();
815 auto fluid_assembler = std::dynamic_pointer_cast<assembler::OperatorSplitting>(primary_assembler_);
816 if (!fluid_assembler)
817 log_and_throw_error(
"Invalid assembler {}!", primary_assembler_ ? primary_assembler_->name() : name());
818 const double viscosity = fluid_assembler->viscosity()(0, 0, 0, 0, 0);
819 assert(viscosity >= 0);
821 logger().info(
"Matrices assembly...");
822 StiffnessMatrix stiffness_viscosity, mixed_stiffness, velocity_mass, pressure_stiffness;
826 lapl_assembler.
assemble(mesh_->is_volume(),
space_.n_bases,
space_.basis_list(), gbases, ass_vals_cache_, 0, stiffness_viscosity);
827 mass_assembler_->set_size(1);
828 mass_assembler_->assemble(mesh_->is_volume(),
space_.n_bases,
space_.basis_list(), gbases, mass_ass_vals_cache_, 0, mass_,
true);
830 lapl_assembler.
assemble(mesh_->is_volume(), pressure_space_.n_bases, pressure_space_.basis_list(), gbases, pressure_ass_vals_cache_, 0, pressure_stiffness);
832 mixed_assembler_->assemble(mesh_->is_volume(), pressure_space_.n_bases,
space_.n_bases, pressure_space_.basis_list(),
space_.basis_list(), gbases,
833 pressure_ass_vals_cache_, ass_vals_cache_, 0, mixed_stiffness);
834 mass_assembler_->set_size(mesh_->dimension());
835 mass_assembler_->assemble(mesh_->is_volume(),
space_.n_bases,
space_.basis_list(), gbases, mass_ass_vals_cache_, 0, velocity_mass,
true);
836 mixed_stiffness = mixed_stiffness.transpose();
837 logger().info(
"Matrices assembly ends!");
840 *mesh_, shape, n_el, boundary_.local_boundary, boundary_.boundary_nodes, pressure_boundary_.boundary_nodes, bnd_nodes, mass_,
841 stiffness_viscosity, pressure_stiffness, velocity_mass, dt, viscosity, args[
"solver"][
"linear"]);
843 const QuadratureOrders boundary_samples = n_boundary_samples(
space_.disc_orders.maxCoeff(),
space_.disc_ordersq.maxCoeff(), discr_order);
844 for (
int t = 1; t <= time_steps; ++t)
846 const double time = t0 + t * dt;
847 logger().info(
"{}/{} steps, t={}s", t, time_steps, time);
849 logger().info(
"Advection...");
850 if (args[
"space"][
"advanced"][
"use_particle_advection"])
853 solver.
advection(*mesh_, gbases,
space_.basis_list(), velocity, dt, local_pts);
854 logger().info(
"Advection finished!");
856 rhs_assembler_->set_bc(
857 boundary_.local_boundary, boundary_.boundary_nodes, boundary_samples, boundary_.local_neumann_boundary, velocity, Eigen::MatrixXd(), time);
859 logger().info(
"Solving diffusion...");
862 logger().info(
"Diffusion solved!");
864 solver.
external_force(*mesh_, *primary_assembler_, gbases,
space_.basis_list(), dt, velocity, local_pts, problem, time);
866 logger().info(
"Pressure projection...");
867 solver.
solve_pressure(mixed_stiffness, pressure_boundary_.boundary_nodes, velocity, pressure);
868 solver.
projection(
space_.n_bases, gbases,
space_.basis_list(), pressure_space_.basis_list(), local_pts, pressure, velocity);
869 logger().info(
"Pressure projection finished!");
871 pressure = pressure / dt;
873 rhs_assembler_->set_bc(
874 boundary_.local_boundary, boundary_.boundary_nodes, boundary_samples, boundary_.local_neumann_boundary, velocity, Eigen::MatrixXd(), time);
876 stack_solution(velocity, pressure, sol);
877 save_timestep(time, t, t0, dt, sol);
878 save_step_state(t0, dt, t, sol,
nullptr);
879 notify_time_step(t, time_steps, t0, dt);
883 timings.solving_time = timer.getElapsedTime();
884 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
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
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