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(Eigen::MatrixXd &sol)
719 stats.spectrum.setZero();
721 if (!problem->is_time_dependent())
726 logger().info(
"Solving {}", primary_assembler_->name());
728 prepare_initial_solution(sol);
730 Eigen::MatrixXd velocity, pressure;
731 split_solution(sol, velocity, pressure);
732 stack_solution(velocity, pressure, sol);
733 save_timestep(t0, 0, t0, dt, sol);
735 Eigen::MatrixXd local_pts;
736 const auto &gbases =
space_.geometry_basis_list();
737 const int discr_order =
space_.disc_orders.size() > 0 ?
space_.disc_orders.maxCoeff() : 1;
738 if (mesh_->dimension() == 2)
740 if (gbases[0].bases.size() == 3)
747 if (gbases[0].bases.size() == 4)
753 std::vector<int> bnd_nodes;
754 bnd_nodes.reserve(boundary_.boundary_nodes.size() / std::max(1, mesh_->dimension()));
755 for (
auto it = boundary_.boundary_nodes.begin(); it != boundary_.boundary_nodes.end(); ++it)
757 if (!(*it % mesh_->dimension()))
759 bnd_nodes.push_back(*it / mesh_->dimension());
762 const int n_el = int(
space_.basis_list().size());
763 const int shape = gbases[0].bases.size();
765 auto fluid_assembler = std::dynamic_pointer_cast<assembler::OperatorSplitting>(primary_assembler_);
766 if (!fluid_assembler)
767 log_and_throw_error(
"Invalid assembler {}!", primary_assembler_ ? primary_assembler_->name() : name());
768 const double viscosity = fluid_assembler->viscosity()(0, 0, 0, 0, 0);
769 assert(viscosity >= 0);
771 logger().info(
"Matrices assembly...");
772 StiffnessMatrix stiffness_viscosity, mixed_stiffness, velocity_mass, pressure_stiffness;
776 lapl_assembler.
assemble(mesh_->is_volume(),
space_.n_bases,
space_.basis_list(), gbases, ass_vals_cache_, 0, stiffness_viscosity);
777 mass_assembler_->set_size(1);
778 mass_assembler_->assemble(mesh_->is_volume(),
space_.n_bases,
space_.basis_list(), gbases, mass_ass_vals_cache_, 0, mass_,
true);
780 lapl_assembler.
assemble(mesh_->is_volume(), pressure_space_.n_bases, pressure_space_.basis_list(), gbases, pressure_ass_vals_cache_, 0, pressure_stiffness);
782 mixed_assembler_->assemble(mesh_->is_volume(), pressure_space_.n_bases,
space_.n_bases, pressure_space_.basis_list(),
space_.basis_list(), gbases,
783 pressure_ass_vals_cache_, ass_vals_cache_, 0, mixed_stiffness);
784 mass_assembler_->set_size(mesh_->dimension());
785 mass_assembler_->assemble(mesh_->is_volume(),
space_.n_bases,
space_.basis_list(), gbases, mass_ass_vals_cache_, 0, velocity_mass,
true);
786 mixed_stiffness = mixed_stiffness.transpose();
787 logger().info(
"Matrices assembly ends!");
790 *mesh_, shape, n_el, boundary_.local_boundary, boundary_.boundary_nodes, pressure_boundary_.boundary_nodes, bnd_nodes, mass_,
791 stiffness_viscosity, pressure_stiffness, velocity_mass, dt, viscosity, args[
"solver"][
"linear"]);
793 pressure = Eigen::MatrixXd::Zero(pressure_space_.n_bases, 1);
795 const QuadratureOrders boundary_samples = n_boundary_samples(
space_.disc_orders.maxCoeff(),
space_.disc_ordersq.maxCoeff(), discr_order);
796 for (
int t = 1; t <= time_steps; ++t)
798 const double time = t0 + t * dt;
799 logger().info(
"{}/{} steps, t={}s", t, time_steps, time);
801 logger().info(
"Advection...");
802 if (args[
"space"][
"advanced"][
"use_particle_advection"])
805 solver.
advection(*mesh_, gbases,
space_.basis_list(), velocity, dt, local_pts);
806 logger().info(
"Advection finished!");
808 rhs_assembler_->set_bc(
809 boundary_.local_boundary, boundary_.boundary_nodes, boundary_samples, boundary_.local_neumann_boundary, velocity, Eigen::MatrixXd(), time);
811 logger().info(
"Solving diffusion...");
814 logger().info(
"Diffusion solved!");
816 solver.
external_force(*mesh_, *primary_assembler_, gbases,
space_.basis_list(), dt, velocity, local_pts, problem, time);
818 logger().info(
"Pressure projection...");
819 solver.
solve_pressure(mixed_stiffness, pressure_boundary_.boundary_nodes, velocity, pressure);
820 solver.
projection(
space_.n_bases, gbases,
space_.basis_list(), pressure_space_.basis_list(), local_pts, pressure, velocity);
821 logger().info(
"Pressure projection finished!");
823 pressure = pressure / dt;
825 rhs_assembler_->set_bc(
826 boundary_.local_boundary, boundary_.boundary_nodes, boundary_samples, boundary_.local_neumann_boundary, velocity, Eigen::MatrixXd(), time);
828 stack_solution(velocity, pressure, sol);
829 save_timestep(time, t, t0, dt, sol);
830 notify_time_step(t, time_steps, t0, dt);
834 timings.solving_time = timer.getElapsedTime();
835 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