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;
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());
337 args[
"space"][
"basis_type"],
338 args[
"space"][
"poly_basis_type"],
341 args[
"space"][
"advanced"][
"quadrature_order"],
342 args[
"space"][
"advanced"][
"mass_quadrature_order"],
343 args[
"space"][
"advanced"][
"use_corner_quadrature"],
344 args[
"space"][
"advanced"][
"n_harmonic_samples"],
345 args[
"space"][
"advanced"][
"integral_constraints"],
353 if (
args[
"space"][
"advanced"][
"count_flipped_els"])
356 const int n_samples = 10;
369 igl::Timer cache_timer;
371 logger().info(
"Building cache...");
374 logger().info(
" took {}s", cache_timer.getElapsedTime());
384 const int prev_b_size = int(all_boundary.size());
385 const bool use_corner_quadrature =
args[
"space"][
"advanced"][
"use_corner_quadrature"];
386 const int quadrature_order =
args[
"space"][
"advanced"][
"quadrature_order"].get<
int>();
387 const int mass_quadrature_order =
args[
"space"][
"advanced"][
"mass_quadrature_order"].get<
int>();
388 Eigen::VectorXi pressure_disc_orders;
390 const std::string pressure_basis_type =
args[
"space"][
"basis_type"].get<std::string>() ==
"Bernstein" ?
"Bernstein" :
"Lagrange";
394 pressure_disc_orders,
396 args[
"space"][
"poly_basis_type"],
400 mass_quadrature_order,
401 use_corner_quadrature,
402 args[
"space"][
"advanced"][
"n_harmonic_samples"],
403 args[
"space"][
"advanced"][
"integral_constraints"],
417 for (
const auto &lb : all_boundary)
440 for (
int d = 0; d < mesh.
dimension(); ++d)
466 delta = (max - min) / 2. + min;
468 p_params[
"bbox_center"] = {delta(0), delta(1), delta(2)};
470 p_params[
"bbox_center"] = {delta(0), delta(1)};
477 logger().info(
"Assigning rhs...");
483 const int prev_size =
rhs_.rows();
509 mesh_->dimension(), sol);
511 if (!was_solution_loaded)
513 if (
problem->is_time_dependent())
517 sol.resize(
rhs_.size(), 1);
523 sol.conservativeResize(Eigen::NoChange, 1);
530 const int cols = std::max(1,
int(stacked.cols()));
534 const int primary_rows = std::min(
primary_ndof(),
int(stacked.rows()));
535 if (primary_rows > 0)
536 primary.topRows(primary_rows) = stacked.topRows(primary_rows);
541 if (pressure_rows > 0)
542 pressure.topRows(pressure_rows) = stacked.middleRows(
primary_ndof(), pressure_rows);
548 const int cols = std::max(primary.cols(), pressure.cols());
552 const int primary_rows = std::min(
primary_ndof(),
int(primary.rows()));
553 if (primary_rows > 0)
554 stacked.topRows(primary_rows) = primary.topRows(primary_rows);
557 if (pressure_rows > 0)
558 stacked.middleRows(
primary_ndof(), pressure_rows) = pressure.topRows(pressure_rows);
563 const Eigen::MatrixXd &solution,
566 std::vector<io::OutputField> fields;
570 Eigen::MatrixXd velocity, pressure;
573 const int field_dim =
mesh_->dimension();
576 const bool export_solution_gradient =
578 const bool export_pressure_gradient =
581 const auto resize_to_output_rows = [&](Eigen::MatrixXd &values) {
582 if (output_rows <= values.rows())
585 const int previous_rows = values.rows();
586 values.conservativeResize(output_rows, values.cols());
587 values.bottomRows(output_rows - previous_rows).setZero();
590 const auto sample_vector_field = [&](
const Eigen::MatrixXd &dof_values, Eigen::MatrixXd &values, Eigen::MatrixXd *gradients =
nullptr) ->
bool {
591 if (dof_values.size() <= 0 || field_dim <= 0)
594 if (has_element_samples)
604 values.row(i).setZero();
606 gradients->row(i).setZero();
610 Eigen::MatrixXd local_sol, local_grad;
613 element_id, sample.
local_points.row(i), dof_values, local_sol, local_grad);
615 for (
int d = 0; d < field_dim; ++d)
616 values(i, d) = local_sol(d);
618 gradients->row(i) = local_grad;
621 resize_to_output_rows(values);
623 resize_to_output_rows(*gradients);
629 values.resize(sample.
node_ids.size(), field_dim);
630 for (
int i = 0; i < sample.
node_ids.size(); ++i)
632 const int node_id = sample.
node_ids(i);
633 for (
int d = 0; d < field_dim; ++d)
635 const int dof = node_id * field_dim + d;
636 if (dof < 0 || dof >= dof_values.rows())
638 values(i, d) = dof_values(dof);
641 return sample.
points.rows() == 0 || sample.
points.rows() == values.rows();
647 Eigen::MatrixXd velocity_values, velocity_gradients;
648 const bool sampled_velocity = sample_vector_field(
649 velocity, velocity_values,
650 export_solution_gradient ? &velocity_gradients :
nullptr);
651 if (sampled_velocity && options.
export_field(
"velocity"))
653 if (sampled_velocity && options.export_field(
"solution"))
655 if (sampled_velocity && export_solution_gradient)
658 if (mesh_ && (options.export_field(
"pressure") || export_pressure_gradient))
660 Eigen::MatrixXd values, gradients;
661 if (sample_scalar_field(
662 *mesh_, pressure_space_.basis_list(),
space_.geometry_basis_list(), sample, pressure, values,
663 export_pressure_gradient ? &gradients : nullptr))
665 if (options.export_field(
"pressure"))
667 if (export_pressure_gradient)
672 const auto ¶view_options =
args[
"output"][
"paraview"][
"options"];
673 if (paraview_options[
"material"] && has_element_samples)
675 const auto ¶ms = primary_assembler_->parameters();
676 std::map<std::string, Eigen::MatrixXd> param_values;
677 for (
const auto &[p, _] : params)
678 param_values[p].setZero(output_rows, 1);
680 Eigen::MatrixXd rhos = Eigen::MatrixXd::Zero(output_rows, 1);
681 const auto &density = mass_assembler_->density();
682 for (
int i = 0; i < sample.local_points.rows(); ++i)
684 const int element_id = sample.element_ids(i);
688 for (
const auto &[p, func] : params)
689 param_values.at(p)(i) =
func(sample.local_points.row(i), sample.
points.row(i), sample.time, element_id);
690 rhos(i) = density(sample.local_points.row(i), sample.points.row(i), sample.time, element_id);
693 for (
const auto &[field_name, values] : param_values)
694 if (options.export_field(field_name))
696 if (options.export_field(
"rho"))
700 if (paraview_options[
"body_ids"] && options.export_field(
"body_ids") && has_element_samples)
702 Eigen::MatrixXd ids = Eigen::MatrixXd::Zero(output_rows, 1);
703 for (
int i = 0; i < sample.element_ids.size(); ++i)
705 const int element_id = sample.element_ids(i);
707 ids(i) = mesh_->get_body_id(element_id);
715 void OperatorSplittingVarForm::solve_problem(Eigen::MatrixXd &sol)
717 stats.spectrum.setZero();
719 if (!problem->is_time_dependent())
724 logger().info(
"Solving {}", primary_assembler_->name());
726 prepare_initial_solution(sol);
728 Eigen::MatrixXd velocity, pressure;
729 split_solution(sol, velocity, pressure);
730 stack_solution(velocity, pressure, sol);
731 save_timestep(t0, 0, t0, dt, sol);
733 Eigen::MatrixXd local_pts;
734 const auto &gbases =
space_.geometry_basis_list();
735 const int discr_order =
space_.disc_orders.size() > 0 ?
space_.disc_orders.maxCoeff() : 1;
736 if (mesh_->dimension() == 2)
738 if (gbases[0].bases.size() == 3)
745 if (gbases[0].bases.size() == 4)
751 std::vector<int> bnd_nodes;
752 bnd_nodes.reserve(boundary_.boundary_nodes.size() / std::max(1, mesh_->dimension()));
753 for (
auto it = boundary_.boundary_nodes.begin(); it != boundary_.boundary_nodes.end(); ++it)
755 if (!(*it % mesh_->dimension()))
757 bnd_nodes.push_back(*it / mesh_->dimension());
760 const int n_el = int(
space_.basis_list().size());
761 const int shape = gbases[0].bases.size();
763 auto fluid_assembler = std::dynamic_pointer_cast<assembler::OperatorSplitting>(primary_assembler_);
764 if (!fluid_assembler)
765 log_and_throw_error(
"Invalid assembler {}!", primary_assembler_ ? primary_assembler_->name() : name());
766 const double viscosity = fluid_assembler->viscosity()(0, 0, 0, 0, 0);
767 assert(viscosity >= 0);
769 logger().info(
"Matrices assembly...");
770 StiffnessMatrix stiffness_viscosity, mixed_stiffness, velocity_mass, pressure_stiffness;
774 lapl_assembler.
assemble(mesh_->is_volume(),
space_.n_bases,
space_.basis_list(), gbases, ass_vals_cache_, 0, stiffness_viscosity);
775 mass_assembler_->set_size(1);
776 mass_assembler_->assemble(mesh_->is_volume(),
space_.n_bases,
space_.basis_list(), gbases, mass_ass_vals_cache_, 0, mass_,
true);
778 lapl_assembler.
assemble(mesh_->is_volume(), pressure_space_.n_bases, pressure_space_.basis_list(), gbases, pressure_ass_vals_cache_, 0, pressure_stiffness);
780 mixed_assembler_->assemble(mesh_->is_volume(), pressure_space_.n_bases,
space_.n_bases, pressure_space_.basis_list(),
space_.basis_list(), gbases,
781 pressure_ass_vals_cache_, ass_vals_cache_, 0, mixed_stiffness);
782 mass_assembler_->set_size(mesh_->dimension());
783 mass_assembler_->assemble(mesh_->is_volume(),
space_.n_bases,
space_.basis_list(), gbases, mass_ass_vals_cache_, 0, velocity_mass,
true);
784 mixed_stiffness = mixed_stiffness.transpose();
785 logger().info(
"Matrices assembly ends!");
788 *mesh_, shape, n_el, boundary_.local_boundary, boundary_.boundary_nodes, pressure_boundary_.boundary_nodes, bnd_nodes, mass_,
789 stiffness_viscosity, pressure_stiffness, velocity_mass, dt, viscosity, args[
"solver"][
"linear"]);
791 pressure = Eigen::MatrixXd::Zero(pressure_space_.n_bases, 1);
794 for (
int t = 1; t <= time_steps; ++t)
796 const double time = t0 + t * dt;
797 logger().info(
"{}/{} steps, t={}s", t, time_steps, time);
799 logger().info(
"Advection...");
800 if (args[
"space"][
"advanced"][
"use_particle_advection"])
803 solver.
advection(*mesh_, gbases,
space_.basis_list(), velocity, dt, local_pts);
804 logger().info(
"Advection finished!");
806 rhs_assembler_->set_bc(
807 boundary_.local_boundary, boundary_.boundary_nodes, boundary_samples, boundary_.local_neumann_boundary, velocity, Eigen::MatrixXd(), time);
809 logger().info(
"Solving diffusion...");
812 logger().info(
"Diffusion solved!");
814 solver.
external_force(*mesh_, *primary_assembler_, gbases,
space_.basis_list(), dt, velocity, local_pts, problem, time);
816 logger().info(
"Pressure projection...");
817 solver.
solve_pressure(mixed_stiffness, pressure_boundary_.boundary_nodes, velocity, pressure);
818 solver.
projection(
space_.n_bases, gbases,
space_.basis_list(), pressure_space_.basis_list(), local_pts, pressure, velocity);
819 logger().info(
"Pressure projection finished!");
821 pressure = pressure / dt;
823 rhs_assembler_->set_bc(
824 boundary_.local_boundary, boundary_.boundary_nodes, boundary_samples, boundary_.local_neumann_boundary, velocity, Eigen::MatrixXd(), time);
826 stack_solution(velocity, pressure, sol);
827 save_timestep(time, t, t0, dt, sol);
828 notify_time_step(t, time_steps, t0, dt);
832 timings.solving_time = timer.getElapsedTime();
833 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