29#include <polysolve/linear/FEMSolver.hpp>
30#include <polysolve/nonlinear/Solver.hpp>
34 using namespace varform::internal;
38 const Eigen::MatrixXd &solution,
48 Eigen::MatrixXd &solution)
54 mesh_->dimension(), 0, solution);
92 for (
const std::string field : {
"pressure_boundary",
"pressure_cavity"})
94 const json::json_pointer path(
"/boundary_conditions/" + field);
95 if (
args.contains(path) && !
args.at(path).empty())
100 const bool is_time_dependent =
args.contains(
"time") && !
args[
"time"].is_null();
101 const json &discr_orders =
args.at(
"space").at(
"discr_order");
103 const json &materials =
args.at(
"materials");
104 if (materials.is_array() && materials.empty())
106 const json &first_material = materials.is_array() ? materials.at(0) : materials;
112 if (discr_orders.is_array())
114 bool has_velocity_space =
false;
115 bool has_pressure_space =
false;
116 for (
const json &entry : discr_orders)
118 const int fe_space_id = entry.at(
"fe_space").get<
int>();
122 if (!has_velocity_space || !has_pressure_space)
123 log_and_throw_error(
"Fluid discretization-order lists must explicitly name the velocity and pressure FE spaces.");
126 if (materials.is_array())
128 for (
const json &material : materials)
132 log_and_throw_error(
"All fluid materials must use the same velocity and pressure FE space IDs.");
142 if (!
args.contains(
"preset_problem"))
144 problem = std::make_shared<assembler::GenericTensorProblem>(
"GenericTensor");
148 tmp[
"is_time_dependent"] = is_time_dependent;
151 auto bc =
args[
"boundary_conditions"];
158 if (
args[
"preset_problem"][
"type"] ==
"Kernel")
173 t0 = is_time_dependent ?
args[
"time"][
"t0"].get<
double>() : 0.0;
174 time_steps = is_time_dependent ?
args[
"time"][
"time_steps"].get<
int>() : 0;
175 dt = is_time_dependent ?
args[
"time"][
"dt"].get<
double>() : 0.0;
184 logger().error(
"Load the mesh first!");
187 if (solution.size() <= 0)
189 logger().error(
"Solve the problem first!");
193 logger().info(
"Saving json...");
195 const Eigen::MatrixXd stats_solution =
196 solution.rows() >= primary_size
197 ? solution.topRows(primary_size).eval()
205 args[
"output"][
"advanced"][
"sol_at_node"], j);
206 out << j.dump(4) << std::endl;
214 for (
int e = 0; e < output_orders.size(); ++e)
216 if (
mesh_->is_prism(e))
236 if (!
args[
"output"][
"advanced"][
"compute_error"])
240 if (!
args[
"time"].is_null())
241 tend =
args[
"time"][
"tend"];
243 Eigen::MatrixXd velocity, pressure;
254 logger().error(
"Load the mesh first!");
257 if (solution.size() <= 0)
259 logger().error(
"Solve the problem first!");
266 const bool has_time =
args.contains(
"time") && !
args[
"time"].is_null();
267 double tend = has_time ?
args[
"time"][
"tend"].get<
double>() : 1.0;
281 Eigen::MatrixXd velocity, pressure;
285 if (!solution_path.empty())
287 const int primary_rows = std::min<int>(velocity.rows(),
primary_ndof());
288 const Eigen::MatrixXd primary_solution = velocity.topRows(primary_rows);
292 Eigen::MatrixXd reordered = Eigen::MatrixXd::Zero(nodal_solution.rows(), nodal_solution.cols());
296 if (node >= 0 && node < nodal_solution.rows() && input_node < reordered.rows())
297 reordered.row(input_node) = nodal_solution.row(node);
308 if (!nodes_path.empty())
313 for (
const auto &global : basis.global())
314 nodes.row(global.index) = global.node;
322 Eigen::MatrixXd stress;
323 Eigen::VectorXd mises;
328 if (!stress_path.empty())
330 if (!mises_path.empty())
365 json rhs_solver_params =
args[
"solver"][
"linear"];
366 if (!rhs_solver_params.contains(
"Pardiso"))
367 rhs_solver_params[
"Pardiso"] = {};
368 rhs_solver_params[
"Pardiso"][
"mtype"] = -2;
375 args[
"space"][
"advanced"][
"bc_method"],
387 Eigen::VectorXi space_disc_orders, space_disc_ordersq;
390 if (
args[
"space"][
"use_p_ref"])
394 args[
"space"][
"advanced"][
"B"],
395 args[
"space"][
"advanced"][
"h1_formula"],
396 args[
"space"][
"discr_order"],
397 args[
"space"][
"advanced"][
"discr_order_max"],
401 logger().info(
"min p: {} max p: {}", space_disc_orders.minCoeff(), space_disc_orders.maxCoeff());
409 args[
"space"][
"basis_type"],
410 args[
"space"][
"poly_basis_type"],
413 args[
"space"][
"advanced"][
"quadrature_order"],
414 args[
"space"][
"advanced"][
"mass_quadrature_order"],
415 args[
"space"][
"advanced"][
"use_corner_quadrature"],
416 args[
"space"][
"advanced"][
"n_harmonic_samples"],
417 args[
"space"][
"advanced"][
"integral_constraints"],
425 if (
args[
"space"][
"advanced"][
"count_flipped_els"])
428 const int n_samples = 10;
441 igl::Timer cache_timer;
443 logger().info(
"Building cache...");
447 logger().info(
" took {}s", cache_timer.getElapsedTime());
458 const int prev_b_size = int(all_boundary.size());
459 const bool use_corner_quadrature =
args[
"space"][
"advanced"][
"use_corner_quadrature"];
460 const int quadrature_order =
args[
"space"][
"advanced"][
"quadrature_order"].get<
int>();
461 const int mass_quadrature_order =
args[
"space"][
"advanced"][
"mass_quadrature_order"].get<
int>();
462 Eigen::VectorXi pressure_disc_orders, pressure_disc_ordersq;
465 const std::string pressure_basis_type =
args[
"space"][
"basis_type"].get<std::string>() ==
"Bernstein" ?
"Bernstein" :
"Lagrange";
469 pressure_disc_orders,
470 pressure_disc_ordersq,
472 args[
"space"][
"poly_basis_type"],
476 mass_quadrature_order,
477 use_corner_quadrature,
478 args[
"space"][
"advanced"][
"n_harmonic_samples"],
479 args[
"space"][
"advanced"][
"integral_constraints"],
509 for (
int d = 0; d < mesh.
dimension(); ++d)
521 for (
int d = 0; d < mesh.
dimension(); ++d)
546 delta = (max - min) / 2. + min;
548 p_params[
"bbox_center"] = {delta(0), delta(1), delta(2)};
550 p_params[
"bbox_center"] = {delta(0), delta(1)};
557 logger().info(
"Assigning rhs...");
566 const int prev_size =
rhs_.rows();
573 if (!
problem->is_time_dependent())
585 logger().info(
"Assembling mass mat...");
592 std::vector<Eigen::Triplet<double>> blocks;
593 blocks.reserve(velocity_mass.nonZeros());
594 for (
int k = 0; k < velocity_mass.outerSize(); ++k)
595 for (StiffnessMatrix::InnerIterator it(velocity_mass, k); it; ++it)
596 blocks.emplace_back(it.row(), it.col(), it.value());
599 mass_.setFromTriplets(blocks.begin(), blocks.end());
600 mass_.makeCompressed();
603 for (
int k = 0; k < velocity_mass.outerSize(); ++k)
604 for (StiffnessMatrix::InnerIterator it(velocity_mass, k); it; ++it)
606 assert(it.col() == k);
609 avg_mass_ /= std::max(1,
int(velocity_mass.rows()));
612 if (
args[
"solver"][
"advanced"][
"lump_mass_matrix"])
630 if (
problem->is_time_dependent())
634 sol.resize(
rhs_.size(), 1);
639 sol.conservativeResize(Eigen::NoChange, 1);
646 const int cols = std::max(1,
int(stacked.cols()));
650 const int primary_rows = std::min(
primary_ndof(),
int(stacked.rows()));
651 if (primary_rows > 0)
652 primary.topRows(primary_rows) = stacked.topRows(primary_rows);
657 if (pressure_rows > 0)
658 pressure.topRows(pressure_rows) = stacked.middleRows(
primary_ndof(), pressure_rows);
666 logger().info(
"Assembling stiffness mat...");
668 StiffnessMatrix velocity_stiffness, mixed_stiffness, pressure_stiffness;
675 velocity_stiffness, mixed_stiffness, pressure_stiffness, stiffness);
683 stats.
mat_size = (
long long)stiffness.rows() * (
long long)stiffness.cols();
690 const std::unique_ptr<polysolve::linear::Solver> &solver,
693 const bool compute_spectrum,
694 Eigen::MatrixXd &sol)
704 args[
"output"][
"data"][
"stiffness_mat"],
712 const double error = (A *
x - b).norm();
714 logger().error(
"Solver error: {}", error);
716 logger().debug(
"Solver error: {}", error);
721 const Eigen::MatrixXd &solution,
724 std::vector<io::OutputField> fields;
728 Eigen::MatrixXd velocity, pressure;
731 const int field_dim =
mesh_->dimension();
734 const bool export_solution_gradient =
736 const bool export_pressure_gradient =
739 const auto resize_to_output_rows = [&](Eigen::MatrixXd &values) {
740 if (output_rows <= values.rows())
743 const int previous_rows = values.rows();
744 values.conservativeResize(output_rows, values.cols());
745 values.bottomRows(output_rows - previous_rows).setZero();
748 const auto sample_vector_field = [&](
const Eigen::MatrixXd &dof_values, Eigen::MatrixXd &values, Eigen::MatrixXd *gradients =
nullptr) ->
bool {
749 if (dof_values.size() <= 0 || field_dim <= 0)
752 if (has_element_samples)
762 values.row(i).setZero();
764 gradients->row(i).setZero();
768 Eigen::MatrixXd local_sol, local_grad;
771 element_id, sample.
local_points.row(i), dof_values, local_sol, local_grad);
773 for (
int d = 0; d < field_dim; ++d)
774 values(i, d) = local_sol(d);
776 gradients->row(i) = local_grad;
779 resize_to_output_rows(values);
781 resize_to_output_rows(*gradients);
787 values.resize(sample.
node_ids.size(), field_dim);
788 for (
int i = 0; i < sample.
node_ids.size(); ++i)
790 const int node_id = sample.
node_ids(i);
791 for (
int d = 0; d < field_dim; ++d)
793 const int dof = node_id * field_dim + d;
794 if (dof < 0 || dof >= dof_values.rows())
796 values(i, d) = dof_values(dof);
799 return sample.
points.rows() == 0 || sample.
points.rows() == values.rows();
805 Eigen::MatrixXd velocity_values, velocity_gradients;
806 const bool sampled_velocity = sample_vector_field(
807 velocity, velocity_values,
808 export_solution_gradient ? &velocity_gradients :
nullptr);
809 if (sampled_velocity && options.
export_field(
"velocity"))
813 if (sampled_velocity && export_solution_gradient)
816 if (mesh_ && (options.export_field(
"pressure") || export_pressure_gradient))
818 Eigen::MatrixXd values, gradients;
820 *mesh_, pressure_space_.basis_list(),
space_.geometry_basis_list(), sample, pressure, values,
821 export_pressure_gradient ? &gradients : nullptr))
823 if (options.export_field(
"pressure"))
825 if (export_pressure_gradient)
830 const auto ¶view_options =
args[
"output"][
"paraview"][
"options"];
831 if (paraview_options[
"material"] && has_element_samples)
833 const auto ¶ms = primary_assembler_->parameters();
834 std::map<std::string, Eigen::MatrixXd> param_values;
835 for (
const auto &[p, _] : params)
836 param_values[p].setZero(output_rows, 1);
838 Eigen::MatrixXd rhos = Eigen::MatrixXd::Zero(output_rows, 1);
839 const auto &density = mass_assembler_->density();
840 for (
int i = 0; i < sample.local_points.rows(); ++i)
842 const int element_id = sample.element_ids(i);
846 for (
const auto &[p, func] : params)
847 param_values.at(p)(i) =
func(sample.local_points.row(i), sample.
points.row(i), sample.time, element_id);
848 rhos(i) = density(sample.local_points.row(i), sample.points.row(i), sample.time, element_id);
851 for (
const auto &[name, values] : param_values)
852 if (options.export_field(name))
854 if (options.export_field(
"rho"))
858 if (paraview_options[
"body_ids"] && options.export_field(
"body_ids") && has_element_samples)
860 Eigen::MatrixXd ids = Eigen::MatrixXd::Zero(output_rows, 1);
861 for (
int i = 0; i < sample.element_ids.size(); ++i)
863 const int element_id = sample.element_ids(i);
865 ids(i) = mesh_->get_body_id(element_id);
873 void StokesVarForm::solve_static_linear(Eigen::MatrixXd &sol)
875 auto solver = polysolve::linear::Solver::create(args[
"solver"][
"linear"],
logger());
876 logger().info(
"{}...", solver->name());
878 const int gdiscr_order = mesh_->orders().size() <= 0 ? 1 : mesh_->orders().maxCoeff();
879 const QuadratureOrders boundary_samples = n_boundary_samples(
space_.disc_orders.maxCoeff(),
space_.disc_ordersq.maxCoeff(), gdiscr_order);
880 rhs_assembler_->set_bc(
881 boundary_.local_boundary, boundary_.boundary_nodes, boundary_samples,
882 boundary_.local_neumann_boundary, rhs_);
885 build_stiffness_mat(A);
886 Eigen::VectorXd b = rhs_;
887 solve_linear_system(solver, A, b, args[
"output"][
"advanced"][
"spectrum"], sol);
890 void StokesVarForm::solve_transient_linear(Eigen::MatrixXd &sol)
892 auto solver = polysolve::linear::Solver::create(args[
"solver"][
"linear"],
logger());
893 logger().info(
"{}...", solver->name());
895 Eigen::MatrixXd velocity, pressure;
896 split_solution(sol, velocity, pressure);
899 args[
"time"][
"integrator"]);
902 Eigen::MatrixXd::Zero(velocity.rows(), velocity.cols()),
903 Eigen::MatrixXd::Zero(velocity.rows(), velocity.cols()),
905 time_integrator = bdf;
906 restore_checkpoint_integrator(
907 time_integrator,
"/checkpoint/state/primary_integrator", dt,
908 "primary",
space_, mesh_->dimension());
910 save_timestep(t0, 0, t0, dt, sol);
912 Eigen::MatrixXd current_rhs = rhs_;
914 build_stiffness_mat(stiffness);
916 const int gdiscr_order = mesh_->orders().size() <= 0 ? 1 : mesh_->orders().maxCoeff();
917 const QuadratureOrders boundary_samples = n_boundary_samples(
space_.disc_orders.maxCoeff(),
space_.disc_ordersq.maxCoeff(), gdiscr_order);
919 for (
int t = 1; t <= time_steps; ++t)
921 const double time = t0 + t * dt;
922 rhs_assembler_->compute_energy_grad(
923 boundary_.local_boundary, boundary_.boundary_nodes, mass_assembler_->density(), boundary_samples, boundary_.local_neumann_boundary, rhs_, time,
925 rhs_assembler_->set_bc(
926 boundary_.local_boundary, boundary_.boundary_nodes, boundary_samples, boundary_.local_neumann_boundary, current_rhs, velocity, time);
928 if (current_rhs.rows() != stacked_ndof())
930 const int old_rows = current_rhs.rows();
931 current_rhs.conservativeResize(stacked_ndof(), current_rhs.cols());
932 if (stacked_ndof() > old_rows)
933 current_rhs.bottomRows(stacked_ndof() - old_rows).setZero();
935 current_rhs.bottomRows(pressure_block_size()).setZero();
938 Eigen::VectorXd b = Eigen::VectorXd::Zero(stacked_ndof());
939 b.head(primary_ndof()) = (mass_ * bdf->weighted_sum_x_prevs()) / bdf->beta_dt();
940 for (
int i : boundary_.boundary_nodes)
944 solve_linear_system(solver, A, b, args[
"output"][
"advanced"][
"spectrum"].get<bool>() && t == time_steps, sol);
945 split_solution(sol, velocity, pressure);
946 bdf->update_quantities(velocity.col(0));
948 save_timestep(time, t, t0, dt, sol);
949 save_step_state(t0, dt, t, sol, time_integrator.get());
950 logger().info(
"{}/{} t={}", t, time_steps, time);
951 notify_time_step(t, time_steps, t0, dt);
955 void StokesVarForm::solve_problem(
956 Eigen::MatrixXd &sol,
960 assert(!initial_condition_override &&
"Stokes does not support initial-condition overrides");
961 assert(!post_step &&
"Stokes does not support post-step callbacks");
963 stats.spectrum.setZero();
966 logger().info(
"Solving {}", primary_assembler_->name());
968 prepare_initial_solution(sol);
969 if (problem->is_time_dependent())
970 solve_transient_linear(sol);
973 time_integrator =
nullptr;
974 solve_static_linear(sol);
978 timings.solving_time = timer.getElapsedTime();
979 logger().info(
" took {}s", timings.solving_time);
984 json residual_solver_params(
const json &input)
987 params[
"solver"] =
"Newton";
988 params[
"line_search"][
"method"] =
"ResidualBacktracking";
990 if (!params.contains(
"Newton") || params[
"Newton"].is_null())
991 params[
"Newton"] = json::object();
992 params[
"Newton"][
"force_psd_projection"] =
false;
993 params[
"Newton"][
"use_psd_projection"] =
true;
999 std::vector<Eigen::Triplet<double>>
entries;
1000 entries.reserve(velocity_mass.nonZeros() + extra_size);
1001 for (
int k = 0; k < velocity_mass.outerSize(); ++k)
1002 for (StiffnessMatrix::InnerIterator it(velocity_mass, k); it; ++it)
1003 entries.emplace_back(it.row(), it.col(), it.value());
1004 for (
int i = 0; i < extra_size; ++i)
1005 entries.emplace_back(velocity_mass.rows() + i, velocity_mass.cols() + i, 1.0);
1008 velocity_mass.rows() + extra_size,
1009 velocity_mass.cols() + extra_size);
1011 result.makeCompressed();
1016 void NavierStokesVarForm::build_forms(Eigen::MatrixXd &sol,
const double t)
1018 assert(sol.rows() == stacked_ndof());
1019 assert(sol.cols() == 1);
1021 auto stokes_assembler = std::make_shared<assembler::StokesVelocity>();
1022 set_materials(*stokes_assembler, mesh_->dimension());
1024 assert(navier_stokes_assembler);
1026 stacked_form_ = std::make_shared<solver::StackedForm>();
1027 const auto velocity_block = stacked_form_->add_block(primary_ndof());
1028 const auto pressure_block = stacked_form_->add_block(pressure_space_.n_bases);
1030 navier_stokes_form_ = std::make_shared<solver::NavierStokesForm>(
1032 stokes_assembler, *navier_stokes_assembler, ass_vals_cache_, t, mesh_->is_volume());
1033 stacked_form_->add(velocity_block, navier_stokes_form_);
1035 velocity_rhs_ = rhs_.topRows(primary_ndof());
1036 const int gdiscr_order = mesh_->orders().size() <= 0 ? 1 : mesh_->orders().maxCoeff();
1037 const QuadratureOrders boundary_samples = n_boundary_samples(
space_.disc_orders.maxCoeff(),
space_.disc_ordersq.maxCoeff(), gdiscr_order);
1038 body_form_ = std::make_shared<solver::BodyForm>(
1040 boundary_.boundary_nodes, boundary_.local_boundary,
1041 boundary_.local_neumann_boundary, boundary_samples,
1042 velocity_rhs_, *rhs_assembler_, mass_assembler_->density(),
1043 false, problem->is_time_dependent());
1044 body_form_->update_quantities(t, sol.topRows(primary_ndof()));
1045 stacked_form_->add(velocity_block, body_form_);
1047 mixed_form_ = std::make_shared<solver::MixedLinearForm>(
1048 space_.n_bases, pressure_space_.n_bases,
1049 space_.basis_list(), pressure_space_.basis_list(),
space_.geometry_basis_list(),
1050 *mixed_assembler_, ass_vals_cache_, pressure_ass_vals_cache_, t, mesh_->is_volume());
1051 stacked_form_->add(velocity_block, pressure_block, mixed_form_);
1053 if (use_avg_pressure)
1055 const auto average_block = stacked_form_->add_block(1);
1056 average_pressure_form_ = std::make_shared<solver::AveragePressureForm>(pressure_space_.n_bases);
1057 stacked_form_->add(pressure_block, average_block, average_pressure_form_);
1060 average_pressure_form_ =
nullptr;
1062 inertia_form_ =
nullptr;
1063 if (problem->is_time_dependent())
1065 assert(time_integrator);
1066 inertia_form_ = std::make_shared<solver::InertiaForm>(mass_, *time_integrator);
1067 if (!boundary_.boundary_nodes.empty())
1069 inertia_form_->set_x_tilde_updater(
1070 [
this, boundary_samples](
1072 const Eigen::VectorXd &,
1073 Eigen::VectorXd &target) {
1074 Eigen::MatrixXd projected_target = target;
1075 const std::vector<mesh::LocalBoundary> empty_neumann_boundary;
1076 rhs_assembler_->set_bc(
1077 boundary_.local_boundary, boundary_.boundary_nodes,
1078 boundary_samples, empty_neumann_boundary,
1079 projected_target, Eigen::MatrixXd(), time);
1080 assert(projected_target.cols() == 1);
1081 target = projected_target.col(0);
1084 stacked_form_->add(velocity_block, inertia_form_);
1085 update_transient_form_weights();
1088 forms_ = {stacked_form_};
1089 for (
const auto &form : forms_)
1090 form->set_output_dir(output_path);
1093 if (!boundary_.boundary_nodes.empty())
1095 auto stacked_al = std::make_shared<solver::StackedAugmentedLagrangianForm>();
1096 const auto velocity_al_block = stacked_al->add_block(primary_ndof());
1097 stacked_al->add_block(pressure_space_.n_bases);
1098 if (use_avg_pressure)
1099 stacked_al->add_block(1);
1102 std::make_shared<solver::BCLagrangianForm>(
1103 primary_ndof(), boundary_.boundary_nodes,
1104 boundary_.local_boundary, boundary_.local_neumann_boundary,
1105 boundary_samples, pure_mass_, *rhs_assembler_,
1106 0, problem->is_time_dependent(), t));
1107 al_forms_.push_back(stacked_al);
1110 const StiffnessMatrix residual_mass = append_identity_mass(pure_mass_, pressure_block_size());
1111 nl_problem_ = std::make_shared<solver::NLProblem>(
1112 stacked_ndof(), t, forms_, al_forms_,
1113 polysolve::linear::Solver::create(args[
"solver"][
"linear"],
logger()),
1114 units.characteristic_length(), 1,
1115 residual_mass, mesh_->dimension(),
true);
1116 nl_problem_->init(sol);
1117 nl_problem_->update_quantities(t, sol);
1118 stats.solver_info = json::array();
1121 void NavierStokesVarForm::update_transient_form_weights()
1123 assert(time_integrator);
1124 const double scaling = time_integrator->acceleration_scaling();
1125 navier_stokes_form_->set_weight(scaling);
1126 body_form_->set_weight(scaling);
1127 mixed_form_->set_row_weights(scaling, scaling);
1128 if (average_pressure_form_)
1129 average_pressure_form_->set_weight(scaling);
1132 void NavierStokesVarForm::solve_nonlinear_step(
const int step, Eigen::MatrixXd &sol)
1134 assert(nl_problem_);
1135 const json nonlinear_params = residual_solver_params(args[
"solver"][
"nonlinear"]);
1136 const json al_nonlinear_params = residual_solver_params(args[
"solver"][
"augmented_lagrangian"][
"nonlinear"]);
1137 std::shared_ptr<polysolve::nonlinear::Solver> nl_solver =
1138 polysolve::nonlinear::Solver::create(
1139 nonlinear_params, args[
"solver"][
"linear"], units.characteristic_length(),
logger());
1142 al_forms_, args[
"solver"][
"augmented_lagrangian"][
"initial_weight"],
1143 args[
"solver"][
"augmented_lagrangian"][
"scaling"],
1144 args[
"solver"][
"augmented_lagrangian"][
"max_weight"],
1145 args[
"solver"][
"augmented_lagrangian"][
"eta"],
1146 [](
const Eigen::VectorXd &) {});
1149 stats.solver_info.push_back(
1150 {{
"type", al_weight > 0 ?
"al" :
"rc"},
1152 {
"info", nl_solver->info()}});
1154 stats.solver_info.back()[
"weight"] = al_weight;
1155 save_subsolve(stats.solver_info.size(), step, sol);
1158 if (!al_forms_.empty())
1160 *nl_problem_, sol, al_nonlinear_params,
1161 args[
"solver"][
"linear"], units.characteristic_length(), nl_solver);
1162 al_solver.solve_reduced(
1163 *nl_problem_, sol, nonlinear_params,
1164 args[
"solver"][
"linear"], units.characteristic_length(), nl_solver);
1167 void NavierStokesVarForm::solve_problem(
1168 Eigen::MatrixXd &sol,
1172 assert(!initial_condition_override &&
"Navier-Stokes does not support initial-condition overrides");
1173 assert(!post_step &&
"Navier-Stokes does not support post-step callbacks");
1175 stats.spectrum.setZero();
1178 logger().info(
"Solving {}", primary_assembler_->name());
1180 prepare_initial_solution(sol);
1181 if (!problem->is_time_dependent())
1183 time_integrator =
nullptr;
1184 build_forms(sol, 1.0);
1185 solve_nonlinear_step(0, sol);
1189 Eigen::MatrixXd velocity, pressure;
1190 split_solution(sol, velocity, pressure);
1192 args[
"time"][
"integrator"],
1196 Eigen::MatrixXd::Zero(velocity.rows(), velocity.cols()),
1197 Eigen::MatrixXd::Zero(velocity.rows(), velocity.cols()), dt);
1198 time_integrator = bdf;
1199 restore_checkpoint_integrator(
1200 time_integrator,
"/checkpoint/state/primary_integrator", dt,
1201 "primary",
space_, mesh_->dimension());
1203 build_forms(sol, t0 + dt);
1204 save_timestep(t0, 0, t0, dt, sol);
1205 for (
int step = 1; step <= time_steps; ++step)
1207 const double time = t0 + step * dt;
1208 logger().info(
"{}/{} steps, dt={}s t={}s", step, time_steps, dt, time);
1209 solve_nonlinear_step(step, sol);
1211 split_solution(sol, velocity, pressure);
1212 time_integrator->update_quantities(velocity.col(0));
1213 update_transient_form_weights();
1214 nl_problem_->update_quantities(t0 + (step + 1) * dt, sol);
1216 save_timestep(time, step, t0, dt, sol);
1217 save_step_state(t0, dt, step, sol, time_integrator.get());
1218 notify_time_step(step, time_steps, t0, dt);
1223 timings.solving_time = timer.getElapsedTime();
1224 logger().info(
" took {}s", timings.solving_time);
std::vector< Eigen::Triplet< double > > entries
std::array< Matrix< int, 3, 3 >, 3 > space_
static std::shared_ptr< MixedAssembler > make_mixed_assembler(const std::string &formulation)
static std::string other_assembler_name(const std::string &formulation)
static void merge_mixed_matrices(const int n_bases, const int n_pressure_bases, const int problem_dim, const bool add_average, const StiffnessMatrix &velocity_stiffness, const StiffnessMatrix &mixed_stiffness, const StiffnessMatrix &pressure_stiffness, StiffnessMatrix &stiffness)
utility to merge 3 blocks of mixed matrices, A=velocity_stiffness, B=mixed_stiffness,...
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.
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 assembling_stiffness_mat_time
time to assembly
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)
json solver_info
information of the solver, eg num iteration, time, errors, etc the informations varies depending on t...
Eigen::Vector4d spectrum
spectrum of the stiffness matrix, enable only if POLYSOLVE_WITH_SPECTRA is ON (off 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...
long long nn_zero
non zeros and sytem matrix size num dof is the total dof in the system
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
std::function< void(const double)> post_subsolve
static std::shared_ptr< BDF > construct_bdf_integrator(const json ¶ms, DynamicOrder dynamic_order=DynamicOrder::Second)
Construct a BDF integrator for algorithms using BDF-specific operations.
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::SparseMatrix< double > lump_matrix(const Eigen::SparseMatrix< double > &M)
Lump each row of a matrix into the diagonal.
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