22 using namespace varform::internal;
51 const bool is_time_dependent =
args.contains(
"time") && !
args[
"time"].is_null();
58 if (!
args.contains(
"preset_problem"))
60 problem = std::make_shared<assembler::GenericTensorProblem>(
"GenericTensor");
64 tmp[
"is_time_dependent"] = is_time_dependent;
67 auto bc =
args[
"boundary_conditions"];
75 if (
args[
"preset_problem"][
"type"] ==
"Kernel")
90 t0 = is_time_dependent ?
args[
"time"][
"t0"].get<
double>() : 0.0;
91 time_steps = is_time_dependent ?
args[
"time"][
"time_steps"].get<
int>() : 0;
92 dt = is_time_dependent ?
args[
"time"][
"dt"].get<
double>() : 0.0;
102 logger().error(
"Load the mesh first!");
105 if (solution.size() <= 0)
107 logger().error(
"Solve the problem first!");
111 logger().info(
"Saving json...");
113 const Eigen::MatrixXd stats_solution =
114 solution.rows() >= primary_size
115 ? solution.topRows(primary_size).eval()
123 args[
"output"][
"advanced"][
"sol_at_node"], j);
124 out << j.dump(4) << std::endl;
132 for (
int e = 0; e < output_orders.size(); ++e)
134 if (
mesh_->is_prism(e))
154 if (!
args[
"output"][
"advanced"][
"compute_error"])
158 if (
args.contains(
"time") && !
args[
"time"].is_null())
160 if (
args[
"time"].contains(
"tend"))
161 tend =
args[
"time"][
"tend"].get<
double>();
166 Eigen::MatrixXd velocity, pressure;
177 logger().error(
"Load the mesh first!");
180 if (solution.size() <= 0)
182 logger().error(
"Solve the problem first!");
189 const bool has_time =
args.contains(
"time") && !
args[
"time"].is_null();
193 if (
args[
"time"].contains(
"tend"))
194 tend =
args[
"time"][
"tend"].get<
double>();
200 local_dt =
args[
"time"][
"dt"];
211 Eigen::MatrixXd velocity, pressure;
215 if (!solution_path.empty())
217 const int primary_rows = std::min<int>(velocity.rows(),
primary_ndof());
218 const Eigen::MatrixXd primary_solution = velocity.topRows(primary_rows);
222 Eigen::MatrixXd reordered = Eigen::MatrixXd::Zero(nodal_solution.rows(), nodal_solution.cols());
226 if (node >= 0 && node < nodal_solution.rows() && input_node < reordered.rows())
227 reordered.row(input_node) = nodal_solution.row(node);
238 if (!nodes_path.empty())
243 for (
const auto &global : basis.global())
244 nodes.row(global.index) = global.node;
252 Eigen::MatrixXd stress;
253 Eigen::VectorXd mises;
258 if (!stress_path.empty())
260 if (!mises_path.empty())
295 json rhs_solver_params =
args[
"solver"][
"linear"];
296 if (!rhs_solver_params.contains(
"Pardiso"))
297 rhs_solver_params[
"Pardiso"] = {};
298 rhs_solver_params[
"Pardiso"][
"mtype"] = -2;
305 args[
"space"][
"advanced"][
"bc_method"],
316 Eigen::VectorXi space_disc_orders, space_disc_ordersq;
319 if (
args[
"space"][
"use_p_ref"])
323 args[
"space"][
"advanced"][
"B"],
324 args[
"space"][
"advanced"][
"h1_formula"],
325 args[
"space"][
"discr_order"],
326 args[
"space"][
"advanced"][
"discr_order_max"],
330 logger().info(
"min p: {} max p: {}", space_disc_orders.minCoeff(), space_disc_orders.maxCoeff());
338 args[
"space"][
"basis_type"],
339 args[
"space"][
"poly_basis_type"],
342 args[
"space"][
"advanced"][
"quadrature_order"],
343 args[
"space"][
"advanced"][
"mass_quadrature_order"],
344 args[
"space"][
"advanced"][
"use_corner_quadrature"],
345 args[
"space"][
"advanced"][
"n_harmonic_samples"],
346 args[
"space"][
"advanced"][
"integral_constraints"],
354 if (
args[
"space"][
"advanced"][
"count_flipped_els"])
357 const int n_samples = 10;
370 igl::Timer cache_timer;
372 logger().info(
"Building cache...");
375 logger().info(
" took {}s", cache_timer.getElapsedTime());
385 const int prev_b_size = int(all_boundary.size());
386 const bool use_corner_quadrature =
args[
"space"][
"advanced"][
"use_corner_quadrature"];
387 const int quadrature_order =
args[
"space"][
"advanced"][
"quadrature_order"].get<
int>();
388 const int mass_quadrature_order =
args[
"space"][
"advanced"][
"mass_quadrature_order"].get<
int>();
389 Eigen::VectorXi pressure_disc_orders, pressure_disc_ordersq;
391 const std::string pressure_basis_type =
args[
"space"][
"basis_type"].get<std::string>() ==
"Bernstein" ?
"Bernstein" :
"Lagrange";
395 pressure_disc_orders,
396 pressure_disc_ordersq,
398 args[
"space"][
"poly_basis_type"],
402 mass_quadrature_order,
403 use_corner_quadrature,
404 args[
"space"][
"advanced"][
"n_harmonic_samples"],
405 args[
"space"][
"advanced"][
"integral_constraints"],
419 for (
const auto &lb : all_boundary)
442 for (
int d = 0; d < mesh.
dimension(); ++d)
468 delta = (max - min) / 2. + min;
470 p_params[
"bbox_center"] = {delta(0), delta(1), delta(2)};
472 p_params[
"bbox_center"] = {delta(0), delta(1)};
479 logger().info(
"Assigning rhs...");
485 const int prev_size =
rhs_.rows();
511 mesh_->dimension(), sol);
513 if (!was_solution_loaded)
515 if (
problem->is_time_dependent())
519 sol.resize(
rhs_.size(), 1);
525 sol.conservativeResize(Eigen::NoChange, 1);
532 const int cols = std::max(1,
int(stacked.cols()));
536 const int primary_rows = std::min(
primary_ndof(),
int(stacked.rows()));
537 if (primary_rows > 0)
538 primary.topRows(primary_rows) = stacked.topRows(primary_rows);
543 if (pressure_rows > 0)
544 pressure.topRows(pressure_rows) = stacked.middleRows(
primary_ndof(), pressure_rows);
550 const int cols = std::max(primary.cols(), pressure.cols());
554 const int primary_rows = std::min(
primary_ndof(),
int(primary.rows()));
555 if (primary_rows > 0)
556 stacked.topRows(primary_rows) = primary.topRows(primary_rows);
559 if (pressure_rows > 0)
560 stacked.middleRows(
primary_ndof(), pressure_rows) = pressure.topRows(pressure_rows);
565 const Eigen::MatrixXd &solution,
568 std::vector<io::OutputField> fields;
572 Eigen::MatrixXd velocity, pressure;
575 const int field_dim =
mesh_->dimension();
578 const bool export_solution_gradient =
580 const bool export_pressure_gradient =
583 const auto resize_to_output_rows = [&](Eigen::MatrixXd &values) {
584 if (output_rows <= values.rows())
587 const int previous_rows = values.rows();
588 values.conservativeResize(output_rows, values.cols());
589 values.bottomRows(output_rows - previous_rows).setZero();
592 const auto sample_vector_field = [&](
const Eigen::MatrixXd &dof_values, Eigen::MatrixXd &values, Eigen::MatrixXd *gradients =
nullptr) ->
bool {
593 if (dof_values.size() <= 0 || field_dim <= 0)
596 if (has_element_samples)
606 values.row(i).setZero();
608 gradients->row(i).setZero();
612 Eigen::MatrixXd local_sol, local_grad;
615 element_id, sample.
local_points.row(i), dof_values, local_sol, local_grad);
617 for (
int d = 0; d < field_dim; ++d)
618 values(i, d) = local_sol(d);
620 gradients->row(i) = local_grad;
623 resize_to_output_rows(values);
625 resize_to_output_rows(*gradients);
631 values.resize(sample.
node_ids.size(), field_dim);
632 for (
int i = 0; i < sample.
node_ids.size(); ++i)
634 const int node_id = sample.
node_ids(i);
635 for (
int d = 0; d < field_dim; ++d)
637 const int dof = node_id * field_dim + d;
638 if (dof < 0 || dof >= dof_values.rows())
640 values(i, d) = dof_values(dof);
643 return sample.
points.rows() == 0 || sample.
points.rows() == values.rows();
649 Eigen::MatrixXd velocity_values, velocity_gradients;
650 const bool sampled_velocity = sample_vector_field(
651 velocity, velocity_values,
652 export_solution_gradient ? &velocity_gradients :
nullptr);
653 if (sampled_velocity && options.
export_field(
"velocity"))
655 if (sampled_velocity && options.export_field(
"solution"))
657 if (sampled_velocity && export_solution_gradient)
660 if (mesh_ && (options.export_field(
"pressure") || export_pressure_gradient))
662 Eigen::MatrixXd values, gradients;
663 if (sample_scalar_field(
664 *mesh_, pressure_space_.basis_list(),
space_.geometry_basis_list(), sample, pressure, values,
665 export_pressure_gradient ? &gradients : nullptr))
667 if (options.export_field(
"pressure"))
669 if (export_pressure_gradient)
674 const auto ¶view_options =
args[
"output"][
"paraview"][
"options"];
675 if (paraview_options[
"material"] && has_element_samples)
677 const auto ¶ms = primary_assembler_->parameters();
678 std::map<std::string, Eigen::MatrixXd> param_values;
679 for (
const auto &[p, _] : params)
680 param_values[p].setZero(output_rows, 1);
682 Eigen::MatrixXd rhos = Eigen::MatrixXd::Zero(output_rows, 1);
683 const auto &density = mass_assembler_->density();
684 for (
int i = 0; i < sample.local_points.rows(); ++i)
686 const int element_id = sample.element_ids(i);
690 for (
const auto &[p, func] : params)
691 param_values.at(p)(i) =
func(sample.local_points.row(i), sample.
points.row(i), sample.time, element_id);
692 rhos(i) = density(sample.local_points.row(i), sample.points.row(i), sample.time, element_id);
695 for (
const auto &[field_name, values] : param_values)
696 if (options.export_field(field_name))
698 if (options.export_field(
"rho"))
702 if (paraview_options[
"body_ids"] && options.export_field(
"body_ids") && has_element_samples)
704 Eigen::MatrixXd ids = Eigen::MatrixXd::Zero(output_rows, 1);
705 for (
int i = 0; i < sample.element_ids.size(); ++i)
707 const int element_id = sample.element_ids(i);
709 ids(i) = mesh_->get_body_id(element_id);
717 void OperatorSplittingVarForm::solve_problem(
718 Eigen::MatrixXd &sol,
722 assert(!initial_condition_override &&
"Operator splitting does not support initial-condition overrides");
723 assert(!post_step &&
"Operator splitting does not support post-step callbacks");
725 stats.spectrum.setZero();
727 if (!problem->is_time_dependent())
732 logger().info(
"Solving {}", primary_assembler_->name());
734 prepare_initial_solution(sol);
736 Eigen::MatrixXd velocity, pressure;
737 split_solution(sol, velocity, pressure);
738 stack_solution(velocity, pressure, sol);
739 save_timestep(t0, 0, t0, dt, sol);
741 Eigen::MatrixXd local_pts;
742 const auto &gbases =
space_.geometry_basis_list();
743 assert(
space_.disc_orders.size() > 0 &&
"Operator splitting requires initialized FE orders");
744 const int discr_order =
space_.disc_orders.maxCoeff();
745 if (mesh_->dimension() == 2)
747 if (gbases[0].bases.size() == 3)
754 if (gbases[0].bases.size() == 4)
760 std::vector<int> bnd_nodes;
761 bnd_nodes.reserve(boundary_.boundary_nodes.size() / std::max(1, mesh_->dimension()));
762 for (
auto it = boundary_.boundary_nodes.begin(); it != boundary_.boundary_nodes.end(); ++it)
764 if (!(*it % mesh_->dimension()))
766 bnd_nodes.push_back(*it / mesh_->dimension());
769 const int n_el = int(
space_.basis_list().size());
770 const int shape = gbases[0].bases.size();
772 auto fluid_assembler = std::dynamic_pointer_cast<assembler::OperatorSplitting>(primary_assembler_);
773 if (!fluid_assembler)
774 log_and_throw_error(
"Invalid assembler {}!", primary_assembler_ ? primary_assembler_->name() : name());
775 const double viscosity = fluid_assembler->viscosity()(0, 0, 0, 0, 0);
776 assert(viscosity >= 0);
778 logger().info(
"Matrices assembly...");
779 StiffnessMatrix stiffness_viscosity, mixed_stiffness, velocity_mass, pressure_stiffness;
783 lapl_assembler.
assemble(mesh_->is_volume(),
space_.n_bases,
space_.basis_list(), gbases, ass_vals_cache_, 0, stiffness_viscosity);
784 mass_assembler_->set_size(1);
785 mass_assembler_->assemble(mesh_->is_volume(),
space_.n_bases,
space_.basis_list(), gbases, mass_ass_vals_cache_, 0, mass_,
true);
787 lapl_assembler.
assemble(mesh_->is_volume(), pressure_space_.n_bases, pressure_space_.basis_list(), gbases, pressure_ass_vals_cache_, 0, pressure_stiffness);
789 mixed_assembler_->assemble(mesh_->is_volume(), pressure_space_.n_bases,
space_.n_bases, pressure_space_.basis_list(),
space_.basis_list(), gbases,
790 pressure_ass_vals_cache_, ass_vals_cache_, 0, mixed_stiffness);
791 mass_assembler_->set_size(mesh_->dimension());
792 mass_assembler_->assemble(mesh_->is_volume(),
space_.n_bases,
space_.basis_list(), gbases, mass_ass_vals_cache_, 0, velocity_mass,
true);
793 mixed_stiffness = mixed_stiffness.transpose();
794 logger().info(
"Matrices assembly ends!");
797 *mesh_, shape, n_el, boundary_.local_boundary, boundary_.boundary_nodes, pressure_boundary_.boundary_nodes, bnd_nodes, mass_,
798 stiffness_viscosity, pressure_stiffness, velocity_mass, dt, viscosity, args[
"solver"][
"linear"]);
800 pressure = Eigen::MatrixXd::Zero(pressure_space_.n_bases, 1);
802 const QuadratureOrders boundary_samples = n_boundary_samples(
space_.disc_orders.maxCoeff(),
space_.disc_ordersq.maxCoeff(), discr_order);
803 for (
int t = 1; t <= time_steps; ++t)
805 const double time = t0 + t * dt;
806 logger().info(
"{}/{} steps, t={}s", t, time_steps, time);
808 logger().info(
"Advection...");
809 if (args[
"space"][
"advanced"][
"use_particle_advection"])
812 solver.
advection(*mesh_, gbases,
space_.basis_list(), velocity, dt, local_pts);
813 logger().info(
"Advection finished!");
815 rhs_assembler_->set_bc(
816 boundary_.local_boundary, boundary_.boundary_nodes, boundary_samples, boundary_.local_neumann_boundary, velocity, Eigen::MatrixXd(), time);
818 logger().info(
"Solving diffusion...");
821 logger().info(
"Diffusion solved!");
823 solver.
external_force(*mesh_, *primary_assembler_, gbases,
space_.basis_list(), dt, velocity, local_pts, problem, time);
825 logger().info(
"Pressure projection...");
826 solver.
solve_pressure(mixed_stiffness, pressure_boundary_.boundary_nodes, velocity, pressure);
827 solver.
projection(
space_.n_bases, gbases,
space_.basis_list(), pressure_space_.basis_list(), local_pts, pressure, velocity);
828 logger().info(
"Pressure projection finished!");
830 pressure = pressure / dt;
832 rhs_assembler_->set_bc(
833 boundary_.local_boundary, boundary_.boundary_nodes, boundary_samples, boundary_.local_neumann_boundary, velocity, Eigen::MatrixXd(), time);
835 stack_solution(velocity, pressure, sol);
836 save_timestep(time, t, t0, dt, sol);
837 notify_time_step(t, time_steps, t0, dt);
841 timings.solving_time = timer.getElapsedTime();
842 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).
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