PolyFEM
Loading...
Searching...
No Matches
StateSolveLinear.cpp
Go to the documentation of this file.
2
6
9
13
16
18
19#include <Eigen/Core>
20#include <unsupported/Eigen/SparseExtra>
21#include <spdlog/fmt/fmt.h>
22#include <polysolve/linear/FEMSolver.hpp>
23
24#include <cassert>
25#include <memory>
26#include <string>
27#include <vector>
28
29namespace polyfem::legacy
30{
31 using namespace mesh;
32 using namespace time_integrator;
33 using namespace utils;
34 using namespace solver;
35 using namespace io;
36
38 {
39 igl::Timer timer;
40 timer.start();
41 logger().info("Assembling stiffness mat...");
42 assert(assembler->is_linear());
43
44 if (mixed_assembler != nullptr)
45 {
46
47 StiffnessMatrix velocity_stiffness, mixed_stiffness, pressure_stiffness;
48 assembler->assemble(mesh->is_volume(), n_bases, bases, geom_bases(), ass_vals_cache, 0, velocity_stiffness);
50 pressure_assembler->assemble(mesh->is_volume(), n_pressure_bases, pressure_bases, geom_bases(), pressure_ass_vals_cache, 0, pressure_stiffness);
51
52 const int problem_dim = problem->is_scalar() ? 1 : mesh->dimension();
53
55 velocity_stiffness, mixed_stiffness, pressure_stiffness,
56 stiffness);
57 }
58 else
59 {
60 assembler->assemble(mesh->is_volume(), n_bases, bases, geom_bases(), ass_vals_cache, 0, stiffness);
61 }
62
63 timer.stop();
64 timings.assembling_stiffness_mat_time = timer.getElapsedTime();
65 logger().info(" took {}s", timings.assembling_stiffness_mat_time);
66
67 stats.nn_zero = stiffness.nonZeros();
68 stats.num_dofs = stiffness.rows();
69 stats.mat_size = (long long)stiffness.rows() * (long long)stiffness.cols();
70 logger().info("sparsity: {}/{}", stats.nn_zero, stats.mat_size);
71
72 const std::string full_mat_path = args["output"]["data"]["full_mat"];
73 if (!full_mat_path.empty())
74 {
75 Eigen::saveMarket(stiffness, full_mat_path);
76 }
77 }
78
80 int step,
81 const std::unique_ptr<polysolve::linear::Solver> &solver,
83 Eigen::VectorXd &b,
84 const bool compute_spectrum,
85 Eigen::MatrixXd &sol, Eigen::MatrixXd &pressure, UserPostStepCallback user_post_step)
86 {
87 assert(assembler->is_linear() && !is_contact_enabled());
88 assert(solve_data.rhs_assembler != nullptr);
89
90 const int problem_dim = problem->is_scalar() ? 1 : mesh->dimension();
91 int precond_num = problem_dim * n_bases;
92
93 const std::vector<int> &boundary_nodes_tmp = boundary_nodes;
94
95 Eigen::VectorXd x;
96 stats.spectrum = dirichlet_solve(
97 *solver,
98 A,
99 b,
100 boundary_nodes_tmp,
101 x,
102 precond_num,
103 args["output"]["data"]["stiffness_mat"],
104 compute_spectrum,
105 assembler->is_fluid(),
107
108 sol = x; // Explicit copy because sol is a MatrixXd (with one column)
109
110 solver->get_info(stats.solver_info);
111
112 const auto error = (A * x - b).norm();
113
114 if (error > 1e-4)
115 logger().error("Solver error: {}", error);
116 else
117 logger().debug("Solver error: {}", error);
118
119 if (mixed_assembler != nullptr)
120 sol_to_pressure(sol, pressure);
121
122 if (user_post_step)
123 {
124 user_post_step(step, *this, sol, nullptr, nullptr);
125 }
126 }
127
128 void State::solve_linear(int step, Eigen::MatrixXd &sol, Eigen::MatrixXd &pressure, UserPostStepCallback user_post_step)
129 {
130 assert(!problem->is_time_dependent());
131 assert(assembler->is_linear() && !is_contact_enabled());
132
133 // --------------------------------------------------------------------
134
136 polysolve::linear::Solver::create(args["solver"]["linear"], logger());
137 logger().info("{}...", static_linear_solver_cache->name());
138
139 // --------------------------------------------------------------------
140
143 (assembler->name() != "Bilaplacian") ? local_neumann_boundary : std::vector<LocalBoundary>(), rhs);
144
147
148 Eigen::VectorXd b = rhs;
149
150 // --------------------------------------------------------------------
151
152 solve_linear(step, static_linear_solver_cache, A, b, args["output"]["advanced"]["spectrum"], sol, pressure, user_post_step);
153 }
154
155 void State::init_linear_solve(Eigen::MatrixXd &sol, const double t, const InitialConditionOverride *ic_override)
156 {
157 assert(sol.cols() == 1);
158 assert(assembler->is_linear() && !is_contact_enabled()); // linear
159
160 if (mixed_assembler != nullptr)
161 return;
162
163 const int ndof = n_bases * mesh->dimension();
164
165 solve_data.elastic_form = std::make_shared<ElasticForm>(
168 t, problem->is_time_dependent() ? args["time"]["dt"].get<double>() : 0.0,
169 mesh->is_volume(),
170 args["solver"]["advanced"]["jacobian_threshold"],
171 args["solver"]["advanced"]["check_inversion"],
172 args["solver"]["advanced"]["conservative_max_iter"]);
173
174 solve_data.body_form = std::make_shared<BodyForm>(
178 mass_matrix_assembler->density(),
179 /*is_formulation_mixed=*/false, problem->is_time_dependent());
180 solve_data.body_form->update_quantities(t, sol);
181
182 solve_data.inertia_form = nullptr;
183 solve_data.damping_form = nullptr;
184 if (problem->is_time_dependent())
185 {
187 solve_data.inertia_form = std::make_shared<InertiaForm>(mass, *solve_data.time_integrator);
188 }
189
190 solve_data.contact_form = nullptr;
191 solve_data.friction_form = nullptr;
192
194 // Initialize time integrator
195 if (problem->is_time_dependent() && assembler->is_tensor())
196 {
197 POLYFEM_SCOPED_TIMER("Initialize time integrator");
198
199 Eigen::MatrixXd solution, velocity, acceleration;
200 initial_solution(solution, ic_override); // Reload this because we need all previous solutions
201 solution.col(0) = sol; // Make sure the current solution is the same as `sol`
202 assert(solution.rows() == sol.size());
203 initial_velocity(velocity, ic_override);
204 assert(velocity.rows() == sol.size());
205 initial_acceleration(acceleration, ic_override);
206 assert(acceleration.rows() == sol.size());
207
208 if (solution.cols() != velocity.cols() || solution.cols() != acceleration.cols())
209 {
211 "Incompatible initial-condition history for transient solve: "
212 "solution has {} columns, velocity has {}, acceleration has {}.",
213 solution.cols(), velocity.cols(), acceleration.cols());
214 }
215
216 const double dt = args["time"]["dt"];
217 solve_data.time_integrator->init(solution, velocity, acceleration, dt);
218 }
220 }
221
222 void State::solve_transient_linear(const int time_steps,
223 const double t0,
224 const double dt,
225 Eigen::MatrixXd &sol,
226 Eigen::MatrixXd &pressure,
227 UserPostStepCallback user_post_step,
228 const InitialConditionOverride *ic_override)
229 {
230 assert(sol.cols() == 1);
231 assert(problem->is_time_dependent());
232 assert(assembler->is_linear() && !is_contact_enabled());
233 assert(solve_data.rhs_assembler != nullptr);
234
235 const bool is_scalar_or_mixed = problem->is_scalar() || mixed_assembler != nullptr;
236
237 // --------------------------------------------------------------------
238
239 auto solver =
240 polysolve::linear::Solver::create(args["solver"]["linear"], logger());
241 logger().info("{}...", solver->name());
242
243 // --------------------------------------------------------------------
244
245 std::shared_ptr<ImplicitTimeIntegrator> time_integrator;
246 if (is_scalar_or_mixed)
247 {
248 time_integrator = std::make_shared<BDF>();
249 time_integrator->set_parameters(args["time"]);
250 time_integrator->init(sol, Eigen::VectorXd::Zero(sol.size()), Eigen::VectorXd::Zero(sol.size()), dt);
251 }
252 else
253 {
254 Eigen::MatrixXd solution, velocity, acceleration;
255 initial_solution(solution, ic_override); // Reload this because we need all previous solutions
256 solution.col(0) = sol; // Make sure the current solution is the same as `sol`
257 assert(solution.rows() == sol.size());
258 initial_velocity(velocity, ic_override);
259 assert(velocity.rows() == sol.size());
260 initial_acceleration(acceleration, ic_override);
261 assert(acceleration.rows() == sol.size());
262
263 if (solution.cols() != velocity.cols() || solution.cols() != acceleration.cols())
264 {
266 "Incompatible initial-condition history for transient solve: "
267 "solution has {} columns, velocity has {}, acceleration has {}.",
268 solution.cols(), velocity.cols(), acceleration.cols());
269 }
270
271 time_integrator = ImplicitTimeIntegrator::construct_time_integrator(args["time"]["integrator"]);
272 time_integrator->init(solution, velocity, acceleration, dt);
273 }
274
275 // --------------------------------------------------------------------
276
277 const QuadratureOrders &n_b_samples = n_boundary_samples();
278
279 // Step 0.
280 if (user_post_step)
281 {
282 user_post_step(0, *this, sol, nullptr, nullptr);
283 }
284
285 Eigen::MatrixXd current_rhs = rhs;
286
287 StiffnessMatrix stiffness;
288 build_stiffness_mat(stiffness);
289
290 // --------------------------------------------------------------------
291 const int t_offset = args["output"]["data"]["file_index_offset"].get<int>();
292
293 // TODO rebuild stiffnes if material are time dept
294 for (int t = 1; t <= time_steps; ++t)
295 {
296 const double time = t0 + t * dt;
297
299 Eigen::VectorXd b;
300 bool compute_spectrum = args["output"]["advanced"]["spectrum"];
301
302 if (is_scalar_or_mixed)
303 {
304 solve_data.rhs_assembler->compute_energy_grad(
306 current_rhs);
307
309 local_boundary, boundary_nodes, n_b_samples, local_neumann_boundary, current_rhs, sol, time);
310
311 if (mixed_assembler != nullptr)
312 {
313 // divergence free
314 int fluid_offset = use_avg_pressure ? (assembler->is_fluid() ? 1 : 0) : 0;
315 current_rhs
316 .block(
317 current_rhs.rows() - n_pressure_bases - use_avg_pressure, 0,
318 n_pressure_bases + use_avg_pressure, current_rhs.cols())
319 .setZero();
320 }
321
322 std::shared_ptr<BDF> bdf = std::dynamic_pointer_cast<BDF>(time_integrator);
323 A = mass / bdf->beta_dt() + stiffness;
324 b = (mass * bdf->weighted_sum_x_prevs()) / bdf->beta_dt();
325 for (int i : boundary_nodes)
326 b[i] = 0;
327 b += current_rhs;
328
329 compute_spectrum &= t == time_steps;
330 }
331 else
332 {
333 solve_data.rhs_assembler->assemble(mass_matrix_assembler->density(), current_rhs, time);
334
335 current_rhs *= -1;
336
338 std::vector<LocalBoundary>(), std::vector<int>(), n_b_samples, local_neumann_boundary, current_rhs, sol, time);
339
340 current_rhs *= time_integrator->acceleration_scaling();
341 current_rhs += mass * time_integrator->x_tilde();
342
344 local_boundary, boundary_nodes, n_b_samples, std::vector<LocalBoundary>(), current_rhs, sol, time);
345
346 A = stiffness * time_integrator->acceleration_scaling() + mass;
347 b = current_rhs;
348
349 compute_spectrum &= t == 1;
350 }
351
352 solve_linear(t, solver, A, b, compute_spectrum, sol, pressure, user_post_step);
353
354 time_integrator->update_quantities(sol);
355
356 save_timestep(time, t + t_offset, t0, dt, sol, pressure);
357
358 const std::string &state_path = resolve_output_path(fmt::format(args["output"]["data"]["state"], t + t_offset));
359 if (!state_path.empty())
360 time_integrator->save_state(state_path);
361
362 logger().info("{}/{} t={}", t, time_steps, time);
363 }
364 }
365} // namespace polyfem::legacy
int x
#define POLYFEM_SCOPED_TIMER(...)
Definition Timer.hpp:10
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,...
double assembling_stiffness_mat_time
time to assembly
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)
long long nn_zero
non zeros and sytem matrix size num dof is the total dof in the system
Runtime override for initial-condition histories.
Definition State.hpp:90
std::shared_ptr< assembler::Problem > problem
current problem, it contains rhs and bc
Definition State.hpp:203
const std::vector< basis::ElementBases > & geom_bases() const
Get a constant reference to the geometry mapping bases.
Definition State.hpp:263
StiffnessMatrix mass
Mass matrix, it is computed only for time dependent problems.
Definition State.hpp:238
std::vector< mesh::LocalBoundary > local_boundary
mapping from elements to nodes for dirichlet boundary conditions
Definition State.hpp:540
std::shared_ptr< assembler::Mass > mass_matrix_assembler
Definition State.hpp:191
std::unique_ptr< mesh::Mesh > mesh
current mesh, it can be a Mesh2D or Mesh3D
Definition State.hpp:573
Eigen::MatrixXd rhs
System right-hand side.
Definition State.hpp:247
std::unique_ptr< polysolve::linear::Solver > static_linear_solver_cache
Linear solver instance from the most recent static linear solve.
Definition State.hpp:387
void init_linear_solve(Eigen::MatrixXd &sol, const double t=1.0, const InitialConditionOverride *ic_override=nullptr)
initialize the linear solve
void initial_acceleration(Eigen::MatrixXd &acceleration, const InitialConditionOverride *ic_override=nullptr) const
Load or compute the initial acceleration.
io::OutStatsData stats
Other statistics.
Definition State.hpp:723
json args
main input arguments containing all defaults
Definition State.hpp:135
void sol_to_pressure(Eigen::MatrixXd &sol, Eigen::MatrixXd &pressure)
splits the solution in solution and pressure for mixed problems
Definition State.cpp:379
int n_pressure_bases
number of pressure bases
Definition State.hpp:215
assembler::AssemblyValsCache pressure_ass_vals_cache
used to store assembly values for pressure for small problems
Definition State.hpp:235
int n_bases
number of bases
Definition State.hpp:213
std::string resolve_output_path(const std::string &path) const
Resolve output path relative to output_dir if the path is not absolute.
io::OutRuntimeData timings
runtime statistics
Definition State.hpp:721
void build_stiffness_mat(StiffnessMatrix &stiffness)
utility that builds the stiffness matrix and collects stats, used only for linear problems
std::vector< basis::ElementBases > pressure_bases
FE pressure bases for mixed elements, the size is #elements.
Definition State.hpp:208
std::shared_ptr< assembler::Assembler > pressure_assembler
Definition State.hpp:195
assembler::AssemblyValsCache ass_vals_cache
used to store assembly values for small problems
Definition State.hpp:231
void solve_transient_linear(const int time_steps, const double t0, const double dt, Eigen::MatrixXd &sol, Eigen::MatrixXd &pressure, UserPostStepCallback user_post_step={}, const InitialConditionOverride *ic_override=nullptr)
solves transient linear problem
QuadratureOrders n_boundary_samples() const
quadrature used for projecting boundary conditions
Definition State.hpp:303
bool use_avg_pressure
use average pressure for stokes problem to fix the additional dofs, true by default if false,...
Definition State.hpp:251
std::shared_ptr< assembler::Assembler > assembler
assemblers
Definition State.hpp:189
std::vector< basis::ElementBases > bases
FE bases, the size is #elements.
Definition State.hpp:206
void initial_velocity(Eigen::MatrixXd &velocity, const InitialConditionOverride *ic_override=nullptr) const
Load or compute the initial velocity.
void solve_linear(int step, Eigen::MatrixXd &sol, Eigen::MatrixXd &pressure, UserPostStepCallback user_post_step={})
solves a linear problem
void save_timestep(const double time, const int t, const double t0, const double dt, const Eigen::MatrixXd &sol, const Eigen::MatrixXd &pressure)
saves a timestep
void initial_solution(Eigen::MatrixXd &solution, const InitialConditionOverride *ic_override=nullptr) const
Load or compute the initial solution.
std::vector< mesh::LocalBoundary > local_neumann_boundary
mapping from elements to nodes for neumann boundary conditions
Definition State.hpp:542
std::vector< int > boundary_nodes
list of boundary nodes
Definition State.hpp:534
solver::SolveData solve_data
timedependent stuff cached
Definition State.hpp:380
bool is_contact_enabled() const
does the simulation have contact
Definition State.hpp:687
std::shared_ptr< assembler::MixedAssembler > mixed_assembler
Definition State.hpp:194
std::shared_ptr< solver::FrictionForm > friction_form
std::shared_ptr< solver::InertiaForm > inertia_form
void update_dt()
updates the dt inside the different forms
std::shared_ptr< solver::BodyForm > body_form
std::shared_ptr< solver::ContactForm > contact_form
std::shared_ptr< solver::ElasticForm > damping_form
std::shared_ptr< solver::ElasticForm > elastic_form
std::shared_ptr< time_integrator::ImplicitTimeIntegrator > time_integrator
std::shared_ptr< assembler::RhsAssembler > rhs_assembler
static std::shared_ptr< ImplicitTimeIntegrator > construct_time_integrator(const json &params, DynamicOrder dynamic_order=DynamicOrder::Second)
Factory method for constructing an implicit time integrator.
std::function< void(int step, State &state, const Eigen::MatrixXd &sol, const Eigen::MatrixXd *disp_grad, const Eigen::MatrixXd *pressure)> UserPostStepCallback
User callback at the end of every solver step.
Definition State.hpp:86
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44
std::array< int, 2 > QuadratureOrders
Definition Types.hpp:19
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:24