Line data Source code
1 : //* This file is part of the MOOSE framework
2 : //* https://mooseframework.inl.gov
3 : //*
4 : //* All rights reserved, see COPYRIGHT for full restrictions
5 : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 : //*
7 : //* Licensed under LGPL 2.1, please see LICENSE for details
8 : //* https://www.gnu.org/licenses/lgpl-2.1.html
9 :
10 : #include "NonlinearSystemBase.h"
11 : #include "AuxiliarySystem.h"
12 : #include "Problem.h"
13 : #include "FEProblem.h"
14 : #include "MooseVariableFE.h"
15 : #include "MooseVariableScalar.h"
16 : #include "PetscSupport.h"
17 : #include "Factory.h"
18 : #include "ParallelUniqueId.h"
19 : #include "ThreadedElementLoop.h"
20 : #include "MaterialData.h"
21 : #include "ComputeResidualThread.h"
22 : #include "ComputeResidualAndJacobianThread.h"
23 : #include "ComputeFVFluxThread.h"
24 : #include "ComputeJacobianThread.h"
25 : #include "ComputeJacobianForScalingThread.h"
26 : #include "ComputeFullJacobianThread.h"
27 : #include "ComputeJacobianBlocksThread.h"
28 : #include "ComputeDiracThread.h"
29 : #include "ComputeElemDampingThread.h"
30 : #include "ComputeNodalDampingThread.h"
31 : #include "ComputeNodalKernelsThread.h"
32 : #include "ComputeNodalKernelBcsThread.h"
33 : #include "ComputeNodalKernelJacobiansThread.h"
34 : #include "ComputeNodalKernelBCJacobiansThread.h"
35 : #include "TimeKernel.h"
36 : #include "BoundaryCondition.h"
37 : #include "DirichletBCBase.h"
38 : #include "NodalBCBase.h"
39 : #include "IntegratedBCBase.h"
40 : #include "DGKernel.h"
41 : #include "InterfaceKernelBase.h"
42 : #include "ElementDamper.h"
43 : #include "NodalDamper.h"
44 : #include "GeneralDamper.h"
45 : #include "DisplacedProblem.h"
46 : #include "NearestNodeLocator.h"
47 : #include "PenetrationLocator.h"
48 : #include "NodalConstraint.h"
49 : #include "NodeFaceConstraint.h"
50 : #include "NodeElemConstraintBase.h"
51 : #include "MortarConstraint.h"
52 : #include "ElemElemConstraint.h"
53 : #include "ScalarKernelBase.h"
54 : #include "Parser.h"
55 : #include "Split.h"
56 : #include "FieldSplitPreconditioner.h"
57 : #include "MooseMesh.h"
58 : #include "MooseUtils.h"
59 : #include "MooseApp.h"
60 : #include "NodalKernelBase.h"
61 : #include "DiracKernelBase.h"
62 : #include "TimeIntegrator.h"
63 : #include "Predictor.h"
64 : #include "Assembly.h"
65 : #include "ElementPairLocator.h"
66 : #include "ODETimeKernel.h"
67 : #include "AllLocalDofIndicesThread.h"
68 : #include "FloatingPointExceptionGuard.h"
69 : #include "ADKernel.h"
70 : #include "ADDirichletBCBase.h"
71 : #include "Moose.h"
72 : #include "ConsoleStream.h"
73 : #include "MooseError.h"
74 : #include "FVElementalKernel.h"
75 : #include "FVScalarLagrangeMultiplierConstraint.h"
76 : #include "FVBoundaryScalarLagrangeMultiplierConstraint.h"
77 : #include "FVFluxKernel.h"
78 : #include "FVScalarLagrangeMultiplierInterface.h"
79 : #include "UserObject.h"
80 : #include "OffDiagonalScalingMatrix.h"
81 : #include "HDGKernel.h"
82 :
83 : // libMesh
84 : #include "libmesh/nonlinear_solver.h"
85 : #include "libmesh/quadrature_gauss.h"
86 : #include "libmesh/dense_vector.h"
87 : #include "libmesh/boundary_info.h"
88 : #include "libmesh/petsc_matrix.h"
89 : #include "libmesh/petsc_vector.h"
90 : #include "libmesh/petsc_nonlinear_solver.h"
91 : #include "libmesh/numeric_vector.h"
92 : #include "libmesh/mesh.h"
93 : #include "libmesh/dense_subvector.h"
94 : #include "libmesh/dense_submatrix.h"
95 : #include "libmesh/dof_map.h"
96 : #include "libmesh/sparse_matrix.h"
97 : #include "libmesh/petsc_matrix.h"
98 : #include "libmesh/default_coupling.h"
99 : #include "libmesh/diagonal_matrix.h"
100 : #include "libmesh/fe_interface.h"
101 : #include "libmesh/petsc_solver_exception.h"
102 :
103 : #include <ios>
104 :
105 : #include "petscsnes.h"
106 : #include <PetscDMMoose.h>
107 : EXTERN_C_BEGIN
108 : extern PetscErrorCode DMCreate_Moose(DM);
109 : EXTERN_C_END
110 :
111 : using namespace libMesh;
112 :
113 62186 : NonlinearSystemBase::NonlinearSystemBase(FEProblemBase & fe_problem,
114 : System & sys,
115 62186 : const std::string & name)
116 : : SolverSystem(fe_problem, fe_problem, name, Moose::VAR_SOLVER),
117 62186 : PerfGraphInterface(fe_problem.getMooseApp().perfGraph(), "NonlinearSystemBase"),
118 62186 : _sys(sys),
119 62186 : _last_nl_rnorm(0.),
120 62186 : _current_nl_its(0),
121 62186 : _residual_ghosted(NULL),
122 62186 : _Re_time_tag(-1),
123 62186 : _Re_time(NULL),
124 62186 : _Re_non_time_tag(-1),
125 62186 : _Re_non_time(NULL),
126 62186 : _scalar_kernels(/*threaded=*/false),
127 62186 : _nodal_bcs(/*threaded=*/false),
128 62186 : _preset_nodal_bcs(/*threaded=*/false),
129 62186 : _ad_preset_nodal_bcs(/*threaded=*/false),
130 62186 : _splits(/*threaded=*/false),
131 62186 : _increment_vec(NULL),
132 62186 : _use_finite_differenced_preconditioner(false),
133 62186 : _fdcoloring(nullptr),
134 62186 : _fsp(nullptr),
135 62186 : _add_implicit_geometric_coupling_entries_to_jacobian(false),
136 62186 : _assemble_constraints_separately(false),
137 62186 : _need_residual_ghosted(false),
138 62186 : _debugging_residuals(false),
139 62186 : _doing_dg(false),
140 62186 : _n_iters(0),
141 62186 : _n_linear_iters(0),
142 62186 : _n_residual_evaluations(0),
143 62186 : _final_residual(0.),
144 62186 : _computing_pre_smo_residual(false),
145 62186 : _pre_smo_residual(0),
146 62186 : _initial_residual(0),
147 62186 : _use_pre_smo_residual(false),
148 62186 : _print_all_var_norms(false),
149 62186 : _has_save_in(false),
150 62186 : _has_diag_save_in(false),
151 62186 : _has_nodalbc_save_in(false),
152 62186 : _has_nodalbc_diag_save_in(false),
153 62186 : _computed_scaling(false),
154 62186 : _compute_scaling_once(true),
155 62186 : _resid_vs_jac_scaling_param(0),
156 62186 : _off_diagonals_in_auto_scaling(false),
157 435302 : _auto_scaling_initd(false)
158 : {
159 62186 : getResidualNonTimeVector();
160 : // Don't need to add the matrix - it already exists (for now)
161 62186 : _Ke_system_tag = _fe_problem.addMatrixTag("SYSTEM");
162 :
163 : // The time matrix tag is not normally used - but must be added to the system
164 : // in case it is so that objects can have 'time' in their matrix tags by default
165 62186 : _fe_problem.addMatrixTag("TIME");
166 :
167 62186 : _Re_tag = _fe_problem.addVectorTag("RESIDUAL");
168 :
169 62186 : _sys.identify_variable_groups(_fe_problem.identifyVariableGroupsInNL());
170 :
171 62186 : if (!_fe_problem.defaultGhosting())
172 : {
173 62104 : auto & dof_map = _sys.get_dof_map();
174 62104 : dof_map.remove_algebraic_ghosting_functor(dof_map.default_algebraic_ghosting());
175 62104 : dof_map.set_implicit_neighbor_dofs(false);
176 : }
177 62186 : }
178 :
179 57816 : NonlinearSystemBase::~NonlinearSystemBase() = default;
180 :
181 : void
182 60284 : NonlinearSystemBase::preInit()
183 : {
184 60284 : SolverSystem::preInit();
185 :
186 60284 : if (_fe_problem.hasDampers())
187 159 : setupDampers();
188 :
189 60284 : if (_residual_copy.get())
190 0 : _residual_copy->init(_sys.n_dofs(), false, SERIAL);
191 60284 : }
192 :
193 : void
194 7573 : NonlinearSystemBase::reinitMortarFunctors()
195 : {
196 : // reinit is called on meshChanged() in FEProblemBase. We could implement meshChanged() instead.
197 : // Subdomains might have changed
198 7649 : for (auto & functor : _displaced_mortar_functors)
199 76 : functor.second.setupMortarMaterials();
200 7689 : for (auto & functor : _undisplaced_mortar_functors)
201 116 : functor.second.setupMortarMaterials();
202 7573 : }
203 :
204 : void
205 76 : NonlinearSystemBase::turnOffJacobian()
206 : {
207 76 : system().set_basic_system_only();
208 76 : nonlinearSolver()->jacobian = NULL;
209 76 : }
210 :
211 : void
212 59380 : NonlinearSystemBase::initialSetup()
213 : {
214 296900 : TIME_SECTION("nlInitialSetup", 2, "Setting Up Nonlinear System");
215 :
216 59380 : SolverSystem::initialSetup();
217 :
218 : {
219 296900 : TIME_SECTION("kernelsInitialSetup", 2, "Setting Up Kernels/BCs/Constraints");
220 :
221 124329 : for (THREAD_ID tid = 0; tid < libMesh::n_threads(); tid++)
222 : {
223 64949 : _kernels.initialSetup(tid);
224 64949 : _nodal_kernels.initialSetup(tid);
225 64949 : _dirac_kernels.initialSetup(tid);
226 64949 : if (_doing_dg)
227 1200 : _dg_kernels.initialSetup(tid);
228 64949 : _interface_kernels.initialSetup(tid);
229 :
230 64949 : _element_dampers.initialSetup(tid);
231 64949 : _nodal_dampers.initialSetup(tid);
232 64949 : _integrated_bcs.initialSetup(tid);
233 :
234 64949 : if (_fe_problem.haveFV())
235 : {
236 4324 : std::vector<FVElementalKernel *> fv_elemental_kernels;
237 4324 : _fe_problem.theWarehouse()
238 8648 : .query()
239 4324 : .template condition<AttribSystem>("FVElementalKernel")
240 4324 : .template condition<AttribThread>(tid)
241 4324 : .queryInto(fv_elemental_kernels);
242 :
243 8312 : for (auto * fv_kernel : fv_elemental_kernels)
244 3988 : fv_kernel->initialSetup();
245 :
246 4324 : std::vector<FVFluxKernel *> fv_flux_kernels;
247 4324 : _fe_problem.theWarehouse()
248 8648 : .query()
249 4324 : .template condition<AttribSystem>("FVFluxKernel")
250 4324 : .template condition<AttribThread>(tid)
251 4324 : .queryInto(fv_flux_kernels);
252 :
253 9767 : for (auto * fv_kernel : fv_flux_kernels)
254 5443 : fv_kernel->initialSetup();
255 4324 : }
256 : }
257 :
258 59380 : _scalar_kernels.initialSetup();
259 59380 : _constraints.initialSetup();
260 59380 : _general_dampers.initialSetup();
261 59380 : _nodal_bcs.initialSetup();
262 59380 : }
263 :
264 : {
265 296900 : TIME_SECTION("mortarSetup", 2, "Initializing Mortar Interfaces");
266 :
267 118760 : auto create_mortar_functors = [this](const bool displaced)
268 : {
269 : // go over mortar interfaces and construct functors
270 118760 : const auto & mortar_interfaces = _fe_problem.getMortarInterfaces(displaced);
271 119780 : for (const auto & mortar_interface : mortar_interfaces)
272 : {
273 1020 : const auto primary_secondary_boundary_pair = mortar_interface.first;
274 1020 : if (!_constraints.hasActiveMortarConstraints(primary_secondary_boundary_pair, displaced))
275 39 : continue;
276 :
277 981 : const auto & mortar_generation_object = mortar_interface.second;
278 :
279 : auto & mortar_constraints =
280 981 : _constraints.getActiveMortarConstraints(primary_secondary_boundary_pair, displaced);
281 :
282 : auto & subproblem = displaced
283 222 : ? static_cast<SubProblem &>(*_fe_problem.getDisplacedProblem())
284 1092 : : static_cast<SubProblem &>(_fe_problem);
285 :
286 981 : auto & mortar_functors =
287 981 : displaced ? _displaced_mortar_functors : _undisplaced_mortar_functors;
288 :
289 981 : mortar_functors.emplace(primary_secondary_boundary_pair,
290 1962 : ComputeMortarFunctor(mortar_constraints,
291 : mortar_generation_object,
292 : subproblem,
293 : _fe_problem,
294 : displaced,
295 981 : subproblem.assembly(0, number())));
296 : }
297 118760 : };
298 :
299 59380 : create_mortar_functors(false);
300 59380 : create_mortar_functors(true);
301 59380 : }
302 :
303 59380 : if (_automatic_scaling)
304 : {
305 445 : if (_off_diagonals_in_auto_scaling)
306 69 : _scaling_matrix = std::make_unique<OffDiagonalScalingMatrix<Number>>(_communicator);
307 : else
308 376 : _scaling_matrix = std::make_unique<DiagonalMatrix<Number>>(_communicator);
309 : }
310 :
311 59380 : if (_preconditioner)
312 14278 : _preconditioner->initialSetup();
313 59380 : }
314 :
315 : void
316 287469 : NonlinearSystemBase::timestepSetup()
317 : {
318 287469 : SolverSystem::timestepSetup();
319 :
320 600590 : for (THREAD_ID tid = 0; tid < libMesh::n_threads(); tid++)
321 : {
322 313121 : _kernels.timestepSetup(tid);
323 313121 : _nodal_kernels.timestepSetup(tid);
324 313121 : _dirac_kernels.timestepSetup(tid);
325 313121 : if (_doing_dg)
326 1775 : _dg_kernels.timestepSetup(tid);
327 313121 : _interface_kernels.timestepSetup(tid);
328 313121 : _element_dampers.timestepSetup(tid);
329 313121 : _nodal_dampers.timestepSetup(tid);
330 313121 : _integrated_bcs.timestepSetup(tid);
331 :
332 313121 : if (_fe_problem.haveFV())
333 : {
334 17435 : std::vector<FVFluxBC *> bcs;
335 17435 : _fe_problem.theWarehouse()
336 34870 : .query()
337 17435 : .template condition<AttribSystem>("FVFluxBC")
338 17435 : .template condition<AttribThread>(tid)
339 17435 : .queryInto(bcs);
340 :
341 17435 : std::vector<FVInterfaceKernel *> iks;
342 17435 : _fe_problem.theWarehouse()
343 34870 : .query()
344 17435 : .template condition<AttribSystem>("FVInterfaceKernel")
345 17435 : .template condition<AttribThread>(tid)
346 17435 : .queryInto(iks);
347 :
348 17435 : std::vector<FVFluxKernel *> kernels;
349 17435 : _fe_problem.theWarehouse()
350 34870 : .query()
351 17435 : .template condition<AttribSystem>("FVFluxKernel")
352 17435 : .template condition<AttribThread>(tid)
353 17435 : .queryInto(kernels);
354 :
355 34439 : for (auto * bc : bcs)
356 17004 : bc->timestepSetup();
357 17833 : for (auto * ik : iks)
358 398 : ik->timestepSetup();
359 38598 : for (auto * kernel : kernels)
360 21163 : kernel->timestepSetup();
361 17435 : }
362 : }
363 287469 : _scalar_kernels.timestepSetup();
364 287469 : _constraints.timestepSetup();
365 287469 : _general_dampers.timestepSetup();
366 287469 : _nodal_bcs.timestepSetup();
367 287469 : }
368 :
369 : void
370 1923833 : NonlinearSystemBase::customSetup(const ExecFlagType & exec_type)
371 : {
372 1923833 : SolverSystem::customSetup(exec_type);
373 :
374 4020605 : for (THREAD_ID tid = 0; tid < libMesh::n_threads(); tid++)
375 : {
376 2096772 : _kernels.customSetup(exec_type, tid);
377 2096772 : _nodal_kernels.customSetup(exec_type, tid);
378 2096772 : _dirac_kernels.customSetup(exec_type, tid);
379 2096772 : if (_doing_dg)
380 12149 : _dg_kernels.customSetup(exec_type, tid);
381 2096772 : _interface_kernels.customSetup(exec_type, tid);
382 2096772 : _element_dampers.customSetup(exec_type, tid);
383 2096772 : _nodal_dampers.customSetup(exec_type, tid);
384 2096772 : _integrated_bcs.customSetup(exec_type, tid);
385 :
386 2096772 : if (_fe_problem.haveFV())
387 : {
388 128830 : std::vector<FVFluxBC *> bcs;
389 128830 : _fe_problem.theWarehouse()
390 257660 : .query()
391 128830 : .template condition<AttribSystem>("FVFluxBC")
392 128830 : .template condition<AttribThread>(tid)
393 128830 : .queryInto(bcs);
394 :
395 128830 : std::vector<FVInterfaceKernel *> iks;
396 128830 : _fe_problem.theWarehouse()
397 257660 : .query()
398 128830 : .template condition<AttribSystem>("FVInterfaceKernel")
399 128830 : .template condition<AttribThread>(tid)
400 128830 : .queryInto(iks);
401 :
402 128830 : std::vector<FVFluxKernel *> kernels;
403 128830 : _fe_problem.theWarehouse()
404 257660 : .query()
405 128830 : .template condition<AttribSystem>("FVFluxKernel")
406 128830 : .template condition<AttribThread>(tid)
407 128830 : .queryInto(kernels);
408 :
409 232205 : for (auto * bc : bcs)
410 103375 : bc->customSetup(exec_type);
411 173049 : for (auto * ik : iks)
412 44219 : ik->customSetup(exec_type);
413 306443 : for (auto * kernel : kernels)
414 177613 : kernel->customSetup(exec_type);
415 128830 : }
416 : }
417 1923833 : _scalar_kernels.customSetup(exec_type);
418 1923833 : _constraints.customSetup(exec_type);
419 1923833 : _general_dampers.customSetup(exec_type);
420 1923833 : _nodal_bcs.customSetup(exec_type);
421 1923833 : }
422 :
423 : void
424 343727 : NonlinearSystemBase::setupDM()
425 : {
426 343727 : if (_fsp)
427 129 : _fsp->setupDM();
428 343727 : }
429 :
430 : void
431 82636 : NonlinearSystemBase::addKernel(const std::string & kernel_name,
432 : const std::string & name,
433 : InputParameters & parameters)
434 : {
435 172768 : for (THREAD_ID tid = 0; tid < libMesh::n_threads(); tid++)
436 : {
437 : // Create the kernel object via the factory and add to warehouse
438 : std::shared_ptr<KernelBase> kernel =
439 90332 : _factory.create<KernelBase>(kernel_name, name, parameters, tid);
440 90156 : _kernels.addObject(kernel, tid);
441 90136 : postAddResidualObject(*kernel);
442 : // Add to theWarehouse, a centralized storage for all moose objects
443 90132 : _fe_problem.theWarehouse().add(kernel);
444 90132 : }
445 :
446 82436 : if (parameters.get<std::vector<AuxVariableName>>("save_in").size() > 0)
447 130 : _has_save_in = true;
448 82436 : if (parameters.get<std::vector<AuxVariableName>>("diag_save_in").size() > 0)
449 104 : _has_diag_save_in = true;
450 82436 : }
451 :
452 : void
453 808 : NonlinearSystemBase::addHDGKernel(const std::string & kernel_name,
454 : const std::string & name,
455 : InputParameters & parameters)
456 : {
457 1621 : for (THREAD_ID tid = 0; tid < libMesh::n_threads(); tid++)
458 : {
459 : // Create the kernel object via the factory and add to warehouse
460 813 : auto kernel = _factory.create<HDGKernel>(kernel_name, name, parameters, tid);
461 813 : _kernels.addObject(kernel, tid);
462 813 : _hybridized_kernels.addObject(kernel, tid);
463 : // Add to theWarehouse, a centralized storage for all moose objects
464 813 : _fe_problem.theWarehouse().add(kernel);
465 813 : postAddResidualObject(*kernel);
466 813 : }
467 808 : }
468 :
469 : void
470 570 : NonlinearSystemBase::addNodalKernel(const std::string & kernel_name,
471 : const std::string & name,
472 : InputParameters & parameters)
473 : {
474 1241 : for (THREAD_ID tid = 0; tid < libMesh::n_threads(); tid++)
475 : {
476 : // Create the kernel object via the factory and add to the warehouse
477 : std::shared_ptr<NodalKernelBase> kernel =
478 671 : _factory.create<NodalKernelBase>(kernel_name, name, parameters, tid);
479 671 : _nodal_kernels.addObject(kernel, tid);
480 : // Add to theWarehouse, a centralized storage for all moose objects
481 671 : _fe_problem.theWarehouse().add(kernel);
482 671 : postAddResidualObject(*kernel);
483 671 : }
484 :
485 570 : if (parameters.get<std::vector<AuxVariableName>>("save_in").size() > 0)
486 0 : _has_save_in = true;
487 570 : if (parameters.get<std::vector<AuxVariableName>>("diag_save_in").size() > 0)
488 0 : _has_diag_save_in = true;
489 570 : }
490 :
491 : void
492 1551 : NonlinearSystemBase::addScalarKernel(const std::string & kernel_name,
493 : const std::string & name,
494 : InputParameters & parameters)
495 : {
496 : std::shared_ptr<ScalarKernelBase> kernel =
497 1551 : _factory.create<ScalarKernelBase>(kernel_name, name, parameters);
498 1543 : postAddResidualObject(*kernel);
499 : // Add to theWarehouse, a centralized storage for all moose objects
500 1543 : _fe_problem.theWarehouse().add(kernel);
501 1543 : _scalar_kernels.addObject(kernel);
502 1543 : }
503 :
504 : void
505 82315 : NonlinearSystemBase::addBoundaryCondition(const std::string & bc_name,
506 : const std::string & name,
507 : InputParameters & parameters)
508 : {
509 : // ThreadID
510 82315 : THREAD_ID tid = 0;
511 :
512 : // Create the object
513 : std::shared_ptr<BoundaryCondition> bc =
514 82315 : _factory.create<BoundaryCondition>(bc_name, name, parameters, tid);
515 82262 : postAddResidualObject(*bc);
516 :
517 : // Active BoundaryIDs for the object
518 82262 : const std::set<BoundaryID> & boundary_ids = bc->boundaryIDs();
519 82262 : auto bc_var = dynamic_cast<const MooseVariableFieldBase *>(&bc->variable());
520 82262 : _vars[tid].addBoundaryVar(boundary_ids, bc_var);
521 :
522 : // Cast to the various types of BCs
523 82262 : std::shared_ptr<NodalBCBase> nbc = std::dynamic_pointer_cast<NodalBCBase>(bc);
524 82262 : std::shared_ptr<IntegratedBCBase> ibc = std::dynamic_pointer_cast<IntegratedBCBase>(bc);
525 :
526 : // NodalBCBase
527 82262 : if (nbc)
528 : {
529 73216 : if (nbc->checkNodalVar() && !nbc->variable().isNodal())
530 4 : mooseError("Trying to use nodal boundary condition '",
531 4 : nbc->name(),
532 : "' on a non-nodal variable '",
533 4 : nbc->variable().name(),
534 : "'.");
535 :
536 73212 : _nodal_bcs.addObject(nbc);
537 : // Add to theWarehouse, a centralized storage for all moose objects
538 73212 : _fe_problem.theWarehouse().add(nbc);
539 73212 : _vars[tid].addBoundaryVars(boundary_ids, nbc->getCoupledVars());
540 :
541 73212 : if (parameters.get<std::vector<AuxVariableName>>("save_in").size() > 0)
542 99 : _has_nodalbc_save_in = true;
543 73212 : if (parameters.get<std::vector<AuxVariableName>>("diag_save_in").size() > 0)
544 21 : _has_nodalbc_diag_save_in = true;
545 :
546 : // DirichletBCs that are preset
547 73212 : std::shared_ptr<DirichletBCBase> dbc = std::dynamic_pointer_cast<DirichletBCBase>(bc);
548 73212 : if (dbc && dbc->preset())
549 63039 : _preset_nodal_bcs.addObject(dbc);
550 :
551 73212 : std::shared_ptr<ADDirichletBCBase> addbc = std::dynamic_pointer_cast<ADDirichletBCBase>(bc);
552 73212 : if (addbc && addbc->preset())
553 1636 : _ad_preset_nodal_bcs.addObject(addbc);
554 73212 : }
555 :
556 : // IntegratedBCBase
557 9046 : else if (ibc)
558 : {
559 9046 : _integrated_bcs.addObject(ibc, tid);
560 : // Add to theWarehouse, a centralized storage for all moose objects
561 9046 : _fe_problem.theWarehouse().add(ibc);
562 9046 : _vars[tid].addBoundaryVars(boundary_ids, ibc->getCoupledVars());
563 :
564 9046 : if (parameters.get<std::vector<AuxVariableName>>("save_in").size() > 0)
565 86 : _has_save_in = true;
566 9046 : if (parameters.get<std::vector<AuxVariableName>>("diag_save_in").size() > 0)
567 60 : _has_diag_save_in = true;
568 :
569 9722 : for (tid = 1; tid < libMesh::n_threads(); tid++)
570 : {
571 : // Create the object
572 676 : bc = _factory.create<BoundaryCondition>(bc_name, name, parameters, tid);
573 :
574 : // Give users opportunity to set some parameters
575 676 : postAddResidualObject(*bc);
576 :
577 : // Active BoundaryIDs for the object
578 676 : const std::set<BoundaryID> & boundary_ids = bc->boundaryIDs();
579 676 : _vars[tid].addBoundaryVar(boundary_ids, bc_var);
580 :
581 676 : ibc = std::static_pointer_cast<IntegratedBCBase>(bc);
582 :
583 676 : _integrated_bcs.addObject(ibc, tid);
584 676 : _vars[tid].addBoundaryVars(boundary_ids, ibc->getCoupledVars());
585 : }
586 : }
587 :
588 : else
589 0 : mooseError("Unknown BoundaryCondition type for object named ", bc->name());
590 82258 : }
591 :
592 : void
593 1727 : NonlinearSystemBase::addConstraint(const std::string & c_name,
594 : const std::string & name,
595 : InputParameters & parameters)
596 : {
597 1727 : std::shared_ptr<Constraint> constraint = _factory.create<Constraint>(c_name, name, parameters);
598 1719 : _constraints.addObject(constraint);
599 1719 : postAddResidualObject(*constraint);
600 :
601 1719 : if (constraint && constraint->addCouplingEntriesToJacobian())
602 1706 : addImplicitGeometricCouplingEntriesToJacobian(true);
603 1719 : }
604 :
605 : void
606 915 : NonlinearSystemBase::addDiracKernel(const std::string & kernel_name,
607 : const std::string & name,
608 : InputParameters & parameters)
609 : {
610 1906 : for (THREAD_ID tid = 0; tid < libMesh::n_threads(); tid++)
611 : {
612 : std::shared_ptr<DiracKernelBase> kernel =
613 999 : _factory.create<DiracKernelBase>(kernel_name, name, parameters, tid);
614 991 : postAddResidualObject(*kernel);
615 991 : _dirac_kernels.addObject(kernel, tid);
616 : // Add to theWarehouse, a centralized storage for all moose objects
617 991 : _fe_problem.theWarehouse().add(kernel);
618 991 : }
619 907 : }
620 :
621 : void
622 1403 : NonlinearSystemBase::addDGKernel(std::string dg_kernel_name,
623 : const std::string & name,
624 : InputParameters & parameters)
625 : {
626 2926 : for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
627 : {
628 1523 : auto dg_kernel = _factory.create<DGKernelBase>(dg_kernel_name, name, parameters, tid);
629 1523 : _dg_kernels.addObject(dg_kernel, tid);
630 : // Add to theWarehouse, a centralized storage for all moose objects
631 1523 : _fe_problem.theWarehouse().add(dg_kernel);
632 1523 : postAddResidualObject(*dg_kernel);
633 1523 : }
634 :
635 1403 : _doing_dg = true;
636 :
637 1403 : if (parameters.get<std::vector<AuxVariableName>>("save_in").size() > 0)
638 52 : _has_save_in = true;
639 1403 : if (parameters.get<std::vector<AuxVariableName>>("diag_save_in").size() > 0)
640 39 : _has_diag_save_in = true;
641 1403 : }
642 :
643 : void
644 907 : NonlinearSystemBase::addInterfaceKernel(std::string interface_kernel_name,
645 : const std::string & name,
646 : InputParameters & parameters)
647 : {
648 1889 : for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
649 : {
650 : std::shared_ptr<InterfaceKernelBase> interface_kernel =
651 982 : _factory.create<InterfaceKernelBase>(interface_kernel_name, name, parameters, tid);
652 982 : postAddResidualObject(*interface_kernel);
653 :
654 982 : const std::set<BoundaryID> & boundary_ids = interface_kernel->boundaryIDs();
655 982 : auto ik_var = dynamic_cast<const MooseVariableFieldBase *>(&interface_kernel->variable());
656 982 : _vars[tid].addBoundaryVar(boundary_ids, ik_var);
657 :
658 982 : _interface_kernels.addObject(interface_kernel, tid);
659 : // Add to theWarehouse, a centralized storage for all moose objects
660 982 : _fe_problem.theWarehouse().add(interface_kernel);
661 982 : _vars[tid].addBoundaryVars(boundary_ids, interface_kernel->getCoupledVars());
662 982 : }
663 907 : }
664 :
665 : void
666 177 : NonlinearSystemBase::addDamper(const std::string & damper_name,
667 : const std::string & name,
668 : InputParameters & parameters)
669 : {
670 328 : for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
671 : {
672 218 : std::shared_ptr<Damper> damper = _factory.create<Damper>(damper_name, name, parameters, tid);
673 :
674 : // Attempt to cast to the damper types
675 218 : std::shared_ptr<ElementDamper> ed = std::dynamic_pointer_cast<ElementDamper>(damper);
676 218 : std::shared_ptr<NodalDamper> nd = std::dynamic_pointer_cast<NodalDamper>(damper);
677 218 : std::shared_ptr<GeneralDamper> gd = std::dynamic_pointer_cast<GeneralDamper>(damper);
678 :
679 218 : if (gd)
680 : {
681 67 : _general_dampers.addObject(gd);
682 67 : break; // not threaded
683 : }
684 151 : else if (ed)
685 92 : _element_dampers.addObject(ed, tid);
686 59 : else if (nd)
687 59 : _nodal_dampers.addObject(nd, tid);
688 : else
689 0 : mooseError("Invalid damper type");
690 419 : }
691 177 : }
692 :
693 : void
694 485 : NonlinearSystemBase::addSplit(const std::string & split_name,
695 : const std::string & name,
696 : InputParameters & parameters)
697 : {
698 485 : std::shared_ptr<Split> split = _factory.create<Split>(split_name, name, parameters);
699 485 : _splits.addObject(split);
700 : // Add to theWarehouse, a centralized storage for all moose objects
701 485 : _fe_problem.theWarehouse().add(split);
702 485 : }
703 :
704 : std::shared_ptr<Split>
705 485 : NonlinearSystemBase::getSplit(const std::string & name)
706 : {
707 485 : return _splits.getActiveObject(name);
708 : }
709 :
710 : bool
711 312713 : NonlinearSystemBase::shouldEvaluatePreSMOResidual() const
712 : {
713 312713 : if (_fe_problem.solverParams(number())._type == Moose::ST_LINEAR)
714 10638 : return false;
715 :
716 : // The legacy behavior (#10464) _always_ performs the pre-SMO residual evaluation
717 : // regardless of whether it is needed.
718 : //
719 : // This is not ideal and has been fixed by #23472. This legacy option ensures a smooth transition
720 : // to the new behavior. Modules and Apps that want to migrate to the new behavior should set this
721 : // parameter to false.
722 302075 : if (_app.parameters().get<bool>("use_legacy_initial_residual_evaluation_behavior"))
723 0 : return true;
724 :
725 302075 : return _use_pre_smo_residual;
726 : }
727 :
728 : Real
729 386861 : NonlinearSystemBase::referenceResidual() const
730 : {
731 386861 : return usePreSMOResidual() ? preSMOResidual() : initialResidual();
732 : }
733 :
734 : Real
735 171 : NonlinearSystemBase::preSMOResidual() const
736 : {
737 171 : if (!shouldEvaluatePreSMOResidual())
738 0 : mooseError("pre-SMO residual is requested but not evaluated.");
739 :
740 171 : return _pre_smo_residual;
741 : }
742 :
743 : Real
744 386973 : NonlinearSystemBase::initialResidual() const
745 : {
746 386973 : return _initial_residual;
747 : }
748 :
749 : void
750 310100 : NonlinearSystemBase::setInitialResidual(Real r)
751 : {
752 310100 : _initial_residual = r;
753 310100 : }
754 :
755 : void
756 0 : NonlinearSystemBase::zeroVectorForResidual(const std::string & vector_name)
757 : {
758 0 : for (unsigned int i = 0; i < _vecs_to_zero_for_residual.size(); ++i)
759 0 : if (vector_name == _vecs_to_zero_for_residual[i])
760 0 : return;
761 :
762 0 : _vecs_to_zero_for_residual.push_back(vector_name);
763 : }
764 :
765 : void
766 131 : NonlinearSystemBase::computeResidualTag(NumericVector<Number> & residual, TagID tag_id)
767 : {
768 131 : _nl_vector_tags.clear();
769 131 : _nl_vector_tags.insert(tag_id);
770 131 : _nl_vector_tags.insert(residualVectorTag());
771 :
772 131 : associateVectorToTag(residual, residualVectorTag());
773 :
774 131 : computeResidualTags(_nl_vector_tags);
775 :
776 131 : disassociateVectorFromTag(residual, residualVectorTag());
777 131 : }
778 :
779 : void
780 0 : NonlinearSystemBase::computeResidual(NumericVector<Number> & residual, TagID tag_id)
781 : {
782 0 : mooseDeprecated(" Please use computeResidualTag");
783 :
784 0 : computeResidualTag(residual, tag_id);
785 0 : }
786 :
787 : void
788 3293480 : NonlinearSystemBase::computeResidualTags(const std::set<TagID> & tags)
789 : {
790 : parallel_object_only();
791 :
792 9880440 : TIME_SECTION("nl::computeResidualTags", 5);
793 :
794 3293480 : _fe_problem.setCurrentNonlinearSystem(number());
795 3293480 : _fe_problem.setCurrentlyComputingResidual(true);
796 :
797 3293480 : bool required_residual = tags.find(residualVectorTag()) == tags.end() ? false : true;
798 :
799 3293480 : _n_residual_evaluations++;
800 :
801 : // not suppose to do anythin on matrix
802 3293480 : deactivateAllMatrixTags();
803 :
804 3293480 : FloatingPointExceptionGuard fpe_guard(_app);
805 :
806 3293480 : for (const auto & numeric_vec : _vecs_to_zero_for_residual)
807 0 : if (hasVector(numeric_vec))
808 : {
809 0 : NumericVector<Number> & vec = getVector(numeric_vec);
810 0 : vec.close();
811 0 : vec.zero();
812 : }
813 :
814 : try
815 : {
816 3293480 : zeroTaggedVectors(tags);
817 3293480 : computeResidualInternal(tags);
818 3293315 : closeTaggedVectors(tags);
819 :
820 3293315 : if (required_residual)
821 : {
822 3261433 : auto & residual = getVector(residualVectorTag());
823 3261433 : if (!_time_integrators.empty())
824 : {
825 5738717 : for (auto & ti : _time_integrators)
826 2877784 : ti->postResidual(residual);
827 : }
828 : else
829 400500 : residual += *_Re_non_time;
830 3261433 : residual.close();
831 : }
832 3293315 : if (_fe_problem.computingScalingResidual())
833 : // We don't want to do nodal bcs or anything else
834 50 : return;
835 :
836 3293265 : computeNodalBCs(tags);
837 3293265 : closeTaggedVectors(tags);
838 :
839 : // If we are debugging residuals we need one more assignment to have the ghosted copy up to
840 : // date
841 3293265 : if (_need_residual_ghosted && _debugging_residuals && required_residual)
842 : {
843 2010 : auto & residual = getVector(residualVectorTag());
844 :
845 2010 : *_residual_ghosted = residual;
846 2010 : _residual_ghosted->close();
847 : }
848 : // Need to close and update the aux system in case residuals were saved to it.
849 3293265 : if (_has_nodalbc_save_in)
850 171 : _fe_problem.getAuxiliarySystem().solution().close();
851 3293265 : if (hasSaveIn())
852 309 : _fe_problem.getAuxiliarySystem().update();
853 : }
854 121 : catch (MooseException & e)
855 : {
856 : // The buck stops here, we have already handled the exception by
857 : // calling stopSolve(), it is now up to PETSc to return a
858 : // "diverged" reason during the next solve.
859 121 : }
860 :
861 : // not supposed to do anything on matrix
862 3293386 : activateAllMatrixTags();
863 :
864 3293386 : _fe_problem.setCurrentlyComputingResidual(false);
865 3293486 : }
866 :
867 : void
868 3467 : NonlinearSystemBase::computeResidualAndJacobianTags(const std::set<TagID> & vector_tags,
869 : const std::set<TagID> & matrix_tags)
870 : {
871 : const bool required_residual =
872 3467 : vector_tags.find(residualVectorTag()) == vector_tags.end() ? false : true;
873 :
874 : try
875 : {
876 3467 : zeroTaggedVectors(vector_tags);
877 3467 : computeResidualAndJacobianInternal(vector_tags, matrix_tags);
878 3467 : closeTaggedVectors(vector_tags);
879 3467 : closeTaggedMatrices(matrix_tags);
880 :
881 3467 : if (required_residual)
882 : {
883 3467 : auto & residual = getVector(residualVectorTag());
884 3467 : if (!_time_integrators.empty())
885 : {
886 3818 : for (auto & ti : _time_integrators)
887 1909 : ti->postResidual(residual);
888 : }
889 : else
890 1558 : residual += *_Re_non_time;
891 3467 : residual.close();
892 : }
893 :
894 3467 : computeNodalBCsResidualAndJacobian();
895 3467 : closeTaggedVectors(vector_tags);
896 3467 : closeTaggedMatrices(matrix_tags);
897 : }
898 0 : catch (MooseException & e)
899 : {
900 : // The buck stops here, we have already handled the exception by
901 : // calling stopSolve(), it is now up to PETSc to return a
902 : // "diverged" reason during the next solve.
903 0 : }
904 3467 : }
905 :
906 : void
907 258647 : NonlinearSystemBase::onTimestepBegin()
908 : {
909 518012 : for (auto & ti : _time_integrators)
910 259365 : ti->preSolve();
911 258647 : if (_predictor.get())
912 387 : _predictor->timestepSetup();
913 258647 : }
914 :
915 : void
916 313848 : NonlinearSystemBase::setInitialSolution()
917 : {
918 313848 : deactivateAllMatrixTags();
919 :
920 313848 : NumericVector<Number> & initial_solution(solution());
921 313848 : if (_predictor.get())
922 : {
923 387 : if (_predictor->shouldApply())
924 : {
925 1115 : TIME_SECTION("applyPredictor", 2, "Applying Predictor");
926 :
927 223 : _predictor->apply(initial_solution);
928 223 : _fe_problem.predictorCleanup(initial_solution);
929 223 : }
930 : else
931 164 : _console << " Skipping predictor this step" << std::endl;
932 : }
933 :
934 : // do nodal BC
935 : {
936 1569240 : TIME_SECTION("initialBCs", 2, "Applying BCs To Initial Condition");
937 :
938 313848 : const ConstBndNodeRange & bnd_nodes = _fe_problem.getCurrentAlgebraicBndNodeRange();
939 15392653 : for (const auto & bnode : bnd_nodes)
940 : {
941 15078805 : BoundaryID boundary_id = bnode->_bnd_id;
942 15078805 : Node * node = bnode->_node;
943 :
944 15078805 : if (node->processor_id() == processor_id())
945 : {
946 : // reinit variables in nodes
947 11559322 : _fe_problem.reinitNodeFace(node, boundary_id, 0);
948 :
949 11559322 : if (_preset_nodal_bcs.hasActiveBoundaryObjects(boundary_id))
950 : {
951 3634329 : const auto & preset_bcs = _preset_nodal_bcs.getActiveBoundaryObjects(boundary_id);
952 7923110 : for (const auto & preset_bc : preset_bcs)
953 4288781 : preset_bc->computeValue(initial_solution);
954 : }
955 11559322 : if (_ad_preset_nodal_bcs.hasActiveBoundaryObjects(boundary_id))
956 : {
957 36720 : const auto & preset_bcs_res = _ad_preset_nodal_bcs.getActiveBoundaryObjects(boundary_id);
958 77278 : for (const auto & preset_bc : preset_bcs_res)
959 40558 : preset_bc->computeValue(initial_solution);
960 : }
961 : }
962 : }
963 313848 : }
964 :
965 313848 : _sys.solution->close();
966 313848 : update();
967 :
968 : // Set constraint secondary values
969 313848 : setConstraintSecondaryValues(initial_solution, false);
970 :
971 313848 : if (_fe_problem.getDisplacedProblem())
972 35761 : setConstraintSecondaryValues(initial_solution, true);
973 313848 : }
974 :
975 : void
976 121 : NonlinearSystemBase::setPredictor(std::shared_ptr<Predictor> predictor)
977 : {
978 121 : _predictor = predictor;
979 121 : }
980 :
981 : void
982 6188103 : NonlinearSystemBase::subdomainSetup(SubdomainID subdomain, THREAD_ID tid)
983 : {
984 6188103 : SolverSystem::subdomainSetup();
985 :
986 6188103 : _kernels.subdomainSetup(subdomain, tid);
987 6188103 : _nodal_kernels.subdomainSetup(subdomain, tid);
988 6188103 : _element_dampers.subdomainSetup(subdomain, tid);
989 6188103 : _nodal_dampers.subdomainSetup(subdomain, tid);
990 6188103 : }
991 :
992 : NumericVector<Number> &
993 31305 : NonlinearSystemBase::getResidualTimeVector()
994 : {
995 31305 : if (!_Re_time)
996 : {
997 31199 : _Re_time_tag = _fe_problem.addVectorTag("TIME");
998 :
999 : // Most applications don't need the expense of ghosting
1000 31199 : ParallelType ptype = _need_residual_ghosted ? GHOSTED : PARALLEL;
1001 31199 : _Re_time = &addVector(_Re_time_tag, false, ptype);
1002 : }
1003 106 : else if (_need_residual_ghosted && _Re_time->type() == PARALLEL)
1004 : {
1005 0 : const auto vector_name = _subproblem.vectorTagName(_Re_time_tag);
1006 :
1007 : // If an application changes its mind, the libMesh API lets us
1008 : // change the vector.
1009 0 : _Re_time = &system().add_vector(vector_name, false, GHOSTED);
1010 0 : }
1011 :
1012 31305 : return *_Re_time;
1013 : }
1014 :
1015 : NumericVector<Number> &
1016 93698 : NonlinearSystemBase::getResidualNonTimeVector()
1017 : {
1018 93698 : if (!_Re_non_time)
1019 : {
1020 62186 : _Re_non_time_tag = _fe_problem.addVectorTag("NONTIME");
1021 :
1022 : // Most applications don't need the expense of ghosting
1023 62186 : ParallelType ptype = _need_residual_ghosted ? GHOSTED : PARALLEL;
1024 62186 : _Re_non_time = &addVector(_Re_non_time_tag, false, ptype);
1025 : }
1026 31512 : else if (_need_residual_ghosted && _Re_non_time->type() == PARALLEL)
1027 : {
1028 0 : const auto vector_name = _subproblem.vectorTagName(_Re_non_time_tag);
1029 :
1030 : // If an application changes its mind, the libMesh API lets us
1031 : // change the vector.
1032 0 : _Re_non_time = &system().add_vector(vector_name, false, GHOSTED);
1033 0 : }
1034 :
1035 93698 : return *_Re_non_time;
1036 : }
1037 :
1038 : NumericVector<Number> &
1039 0 : NonlinearSystemBase::residualVector(TagID tag)
1040 : {
1041 0 : mooseDeprecated("Please use getVector()");
1042 0 : switch (tag)
1043 : {
1044 0 : case 0:
1045 0 : return getResidualNonTimeVector();
1046 :
1047 0 : case 1:
1048 0 : return getResidualTimeVector();
1049 :
1050 0 : default:
1051 0 : mooseError("The required residual vector is not available");
1052 : }
1053 : }
1054 :
1055 : void
1056 23409 : NonlinearSystemBase::enforceNodalConstraintsResidual(NumericVector<Number> & residual)
1057 : {
1058 23409 : THREAD_ID tid = 0; // constraints are going to be done single-threaded
1059 23409 : residual.close();
1060 23409 : if (_constraints.hasActiveNodalConstraints())
1061 : {
1062 602 : const auto & ncs = _constraints.getActiveNodalConstraints();
1063 1204 : for (const auto & nc : ncs)
1064 : {
1065 602 : std::vector<dof_id_type> & secondary_node_ids = nc->getSecondaryNodeId();
1066 602 : std::vector<dof_id_type> & primary_node_ids = nc->getPrimaryNodeId();
1067 :
1068 602 : if ((secondary_node_ids.size() > 0) && (primary_node_ids.size() > 0))
1069 : {
1070 602 : _fe_problem.reinitNodes(primary_node_ids, tid);
1071 602 : _fe_problem.reinitNodesNeighbor(secondary_node_ids, tid);
1072 602 : nc->computeResidual(residual);
1073 : }
1074 : }
1075 602 : _fe_problem.addCachedResidualDirectly(residual, tid);
1076 602 : residual.close();
1077 : }
1078 23409 : }
1079 :
1080 : void
1081 3667 : NonlinearSystemBase::enforceNodalConstraintsJacobian()
1082 : {
1083 3667 : if (!hasMatrix(systemMatrixTag()))
1084 0 : mooseError(" A system matrix is required");
1085 :
1086 3667 : auto & jacobian = getMatrix(systemMatrixTag());
1087 3667 : THREAD_ID tid = 0; // constraints are going to be done single-threaded
1088 :
1089 3667 : if (_constraints.hasActiveNodalConstraints())
1090 : {
1091 121 : const auto & ncs = _constraints.getActiveNodalConstraints();
1092 242 : for (const auto & nc : ncs)
1093 : {
1094 121 : std::vector<dof_id_type> & secondary_node_ids = nc->getSecondaryNodeId();
1095 121 : std::vector<dof_id_type> & primary_node_ids = nc->getPrimaryNodeId();
1096 :
1097 121 : if ((secondary_node_ids.size() > 0) && (primary_node_ids.size() > 0))
1098 : {
1099 121 : _fe_problem.reinitNodes(primary_node_ids, tid);
1100 121 : _fe_problem.reinitNodesNeighbor(secondary_node_ids, tid);
1101 121 : nc->computeJacobian(jacobian);
1102 : }
1103 : }
1104 121 : _fe_problem.addCachedJacobian(tid);
1105 : }
1106 3667 : }
1107 :
1108 : void
1109 108178 : NonlinearSystemBase::reinitNodeFace(const Node & secondary_node,
1110 : const BoundaryID secondary_boundary,
1111 : const PenetrationInfo & info,
1112 : const bool displaced)
1113 : {
1114 115408 : auto & subproblem = displaced ? static_cast<SubProblem &>(*_fe_problem.getDisplacedProblem())
1115 165882 : : static_cast<SubProblem &>(_fe_problem);
1116 :
1117 108178 : const Elem * primary_elem = info._elem;
1118 108178 : unsigned int primary_side = info._side_num;
1119 108178 : std::vector<Point> points;
1120 108178 : points.push_back(info._closest_point);
1121 :
1122 : // *These next steps MUST be done in this order!*
1123 : // ADL: This is a Chesterton's fence situation. I don't know which calls exactly the above comment
1124 : // is referring to. If I had to guess I would guess just the reinitNodeFace and prepareAssembly
1125 : // calls since the former will size the variable's dof indices and then the latter will resize the
1126 : // residual/Jacobian based off the variable's cached dof indices size
1127 :
1128 : // This reinits the variables that exist on the secondary node
1129 108178 : _fe_problem.reinitNodeFace(&secondary_node, secondary_boundary, 0);
1130 :
1131 : // This will set aside residual and jacobian space for the variables that have dofs on
1132 : // the secondary node
1133 108178 : _fe_problem.prepareAssembly(0);
1134 :
1135 108178 : _fe_problem.setNeighborSubdomainID(primary_elem, 0);
1136 :
1137 : //
1138 : // Reinit material on undisplaced mesh
1139 : //
1140 :
1141 : const Elem * const undisplaced_primary_elem =
1142 108178 : displaced ? _mesh.elemPtr(primary_elem->id()) : primary_elem;
1143 : const Point undisplaced_primary_physical_point =
1144 0 : [&points, displaced, primary_elem, undisplaced_primary_elem]()
1145 : {
1146 108178 : if (displaced)
1147 : {
1148 : const Point reference_point =
1149 57704 : FEMap::inverse_map(primary_elem->dim(), primary_elem, points[0]);
1150 57704 : return FEMap::map(primary_elem->dim(), undisplaced_primary_elem, reference_point);
1151 : }
1152 : else
1153 : // If our penetration locator is on the reference mesh, then our undisplaced
1154 : // physical point is simply the point coming from the penetration locator
1155 50474 : return points[0];
1156 108178 : }();
1157 :
1158 216356 : _fe_problem.reinitNeighborPhys(
1159 : undisplaced_primary_elem, primary_side, {undisplaced_primary_physical_point}, 0);
1160 : // Stateful material properties are only initialized for neighbor material data for internal faces
1161 : // for discontinuous Galerkin methods or for conforming interfaces for interface kernels. We don't
1162 : // have either of those use cases here where we likely have disconnected meshes
1163 108178 : _fe_problem.reinitMaterialsNeighbor(primary_elem->subdomain_id(), 0, /*swap_stateful=*/false);
1164 :
1165 : // Reinit points for constraint enforcement
1166 108178 : if (displaced)
1167 57704 : subproblem.reinitNeighborPhys(primary_elem, primary_side, points, 0);
1168 108178 : }
1169 :
1170 : void
1171 349609 : NonlinearSystemBase::setConstraintSecondaryValues(NumericVector<Number> & solution, bool displaced)
1172 : {
1173 :
1174 : if (displaced)
1175 : mooseAssert(_fe_problem.getDisplacedProblem(),
1176 : "If we're calling this method with displaced = true, then we better well have a "
1177 : "displaced problem");
1178 71522 : auto & subproblem = displaced ? static_cast<SubProblem &>(*_fe_problem.getDisplacedProblem())
1179 385370 : : static_cast<SubProblem &>(_fe_problem);
1180 349609 : const auto & penetration_locators = subproblem.geomSearchData()._penetration_locators;
1181 :
1182 349609 : bool constraints_applied = false;
1183 :
1184 409830 : for (const auto & it : penetration_locators)
1185 : {
1186 60221 : PenetrationLocator & pen_loc = *(it.second);
1187 :
1188 60221 : std::vector<dof_id_type> & secondary_nodes = pen_loc._nearest_node._secondary_nodes;
1189 :
1190 60221 : BoundaryID secondary_boundary = pen_loc._secondary_boundary;
1191 60221 : BoundaryID primary_boundary = pen_loc._primary_boundary;
1192 :
1193 60221 : if (_constraints.hasActiveNodeFaceConstraints(secondary_boundary, displaced))
1194 : {
1195 : const auto & constraints =
1196 2977 : _constraints.getActiveNodeFaceConstraints(secondary_boundary, displaced);
1197 2977 : std::unordered_set<unsigned int> needed_mat_props;
1198 5954 : for (const auto & constraint : constraints)
1199 : {
1200 2977 : const auto & mp_deps = constraint->getMatPropDependencies();
1201 2977 : needed_mat_props.insert(mp_deps.begin(), mp_deps.end());
1202 : }
1203 2977 : _fe_problem.setActiveMaterialProperties(needed_mat_props, /*tid=*/0);
1204 :
1205 42140 : for (unsigned int i = 0; i < secondary_nodes.size(); i++)
1206 : {
1207 39163 : dof_id_type secondary_node_num = secondary_nodes[i];
1208 39163 : Node & secondary_node = _mesh.nodeRef(secondary_node_num);
1209 :
1210 39163 : if (secondary_node.processor_id() == processor_id())
1211 : {
1212 30944 : if (pen_loc._penetration_info[secondary_node_num])
1213 : {
1214 30944 : PenetrationInfo & info = *pen_loc._penetration_info[secondary_node_num];
1215 :
1216 30944 : reinitNodeFace(secondary_node, secondary_boundary, info, displaced);
1217 :
1218 61888 : for (const auto & nfc : constraints)
1219 : {
1220 30944 : if (nfc->isExplicitConstraint())
1221 28816 : continue;
1222 : // Return if this constraint does not correspond to the primary-secondary pair
1223 : // prepared by the outer loops.
1224 : // This continue statement is required when, e.g. one secondary surface constrains
1225 : // more than one primary surface.
1226 4256 : if (nfc->secondaryBoundary() != secondary_boundary ||
1227 2128 : nfc->primaryBoundary() != primary_boundary)
1228 0 : continue;
1229 :
1230 2128 : if (nfc->shouldApply())
1231 : {
1232 2128 : constraints_applied = true;
1233 2128 : nfc->computeSecondaryValue(solution);
1234 : }
1235 :
1236 2128 : if (nfc->hasWritableCoupledVariables())
1237 : {
1238 0 : Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
1239 0 : for (auto * var : nfc->getWritableCoupledVariables())
1240 : {
1241 0 : if (var->isNodalDefined())
1242 0 : var->insert(_fe_problem.getAuxiliarySystem().solution());
1243 : }
1244 0 : }
1245 : }
1246 : }
1247 : }
1248 : }
1249 2977 : }
1250 : }
1251 :
1252 : // go over NodeELemConstraints
1253 349609 : std::set<dof_id_type> unique_secondary_node_ids;
1254 :
1255 785458 : for (const auto & secondary_id : _mesh.meshSubdomains())
1256 : {
1257 1165798 : for (const auto & primary_id : _mesh.meshSubdomains())
1258 : {
1259 729949 : if (_constraints.hasActiveNodeElemConstraints(secondary_id, primary_id, displaced))
1260 : {
1261 : const auto & constraints =
1262 180 : _constraints.getActiveNodeElemConstraints(secondary_id, primary_id, displaced);
1263 :
1264 : // get unique set of ids of all nodes on current block
1265 180 : unique_secondary_node_ids.clear();
1266 180 : const MeshBase & meshhelper = _mesh.getMesh();
1267 360 : for (const auto & elem : as_range(meshhelper.active_subdomain_elements_begin(secondary_id),
1268 17340 : meshhelper.active_subdomain_elements_end(secondary_id)))
1269 : {
1270 49140 : for (auto & n : elem->node_ref_range())
1271 40740 : unique_secondary_node_ids.insert(n.id());
1272 180 : }
1273 :
1274 14040 : for (auto secondary_node_id : unique_secondary_node_ids)
1275 : {
1276 13860 : Node & secondary_node = _mesh.nodeRef(secondary_node_id);
1277 :
1278 : // check if secondary node is on current processor
1279 13860 : if (secondary_node.processor_id() == processor_id())
1280 : {
1281 : // This reinits the variables that exist on the secondary node
1282 11088 : _fe_problem.reinitNodeFace(&secondary_node, secondary_id, 0);
1283 :
1284 : // This will set aside residual and jacobian space for the variables that have dofs
1285 : // on the secondary node
1286 11088 : _fe_problem.prepareAssembly(0);
1287 :
1288 23584 : for (const auto & nec : constraints)
1289 : {
1290 12496 : if (nec->shouldApply())
1291 : {
1292 5680 : constraints_applied = true;
1293 5680 : nec->computeSecondaryValue(solution);
1294 : }
1295 : }
1296 : }
1297 : }
1298 : }
1299 : }
1300 : }
1301 :
1302 : // See if constraints were applied anywhere
1303 349609 : _communicator.max(constraints_applied);
1304 :
1305 349609 : if (constraints_applied)
1306 : {
1307 719 : solution.close();
1308 719 : update();
1309 : }
1310 349609 : }
1311 :
1312 : void
1313 28287 : NonlinearSystemBase::constraintResiduals(NumericVector<Number> & residual, bool displaced)
1314 : {
1315 : // Make sure the residual is in a good state
1316 28287 : residual.close();
1317 :
1318 : if (displaced)
1319 : mooseAssert(_fe_problem.getDisplacedProblem(),
1320 : "If we're calling this method with displaced = true, then we better well have a "
1321 : "displaced problem");
1322 9756 : auto & subproblem = displaced ? static_cast<SubProblem &>(*_fe_problem.getDisplacedProblem())
1323 33165 : : static_cast<SubProblem &>(_fe_problem);
1324 28287 : const auto & penetration_locators = subproblem.geomSearchData()._penetration_locators;
1325 :
1326 : bool constraints_applied;
1327 28287 : bool residual_has_inserted_values = false;
1328 28287 : if (!_assemble_constraints_separately)
1329 28287 : constraints_applied = false;
1330 52353 : for (const auto & it : penetration_locators)
1331 : {
1332 24066 : if (_assemble_constraints_separately)
1333 : {
1334 : // Reset the constraint_applied flag before each new constraint, as they need to be
1335 : // assembled separately
1336 0 : constraints_applied = false;
1337 : }
1338 24066 : PenetrationLocator & pen_loc = *(it.second);
1339 :
1340 24066 : std::vector<dof_id_type> & secondary_nodes = pen_loc._nearest_node._secondary_nodes;
1341 :
1342 24066 : BoundaryID secondary_boundary = pen_loc._secondary_boundary;
1343 24066 : BoundaryID primary_boundary = pen_loc._primary_boundary;
1344 :
1345 24066 : bool has_writable_variables(false);
1346 :
1347 24066 : if (_constraints.hasActiveNodeFaceConstraints(secondary_boundary, displaced))
1348 : {
1349 : const auto & constraints =
1350 14004 : _constraints.getActiveNodeFaceConstraints(secondary_boundary, displaced);
1351 :
1352 99926 : for (unsigned int i = 0; i < secondary_nodes.size(); i++)
1353 : {
1354 85922 : dof_id_type secondary_node_num = secondary_nodes[i];
1355 85922 : Node & secondary_node = _mesh.nodeRef(secondary_node_num);
1356 :
1357 85922 : if (secondary_node.processor_id() == processor_id())
1358 : {
1359 73050 : if (pen_loc._penetration_info[secondary_node_num])
1360 : {
1361 73050 : PenetrationInfo & info = *pen_loc._penetration_info[secondary_node_num];
1362 :
1363 73050 : reinitNodeFace(secondary_node, secondary_boundary, info, displaced);
1364 :
1365 146100 : for (const auto & nfc : constraints)
1366 : {
1367 : // Return if this constraint does not correspond to the primary-secondary pair
1368 : // prepared by the outer loops.
1369 : // This continue statement is required when, e.g. one secondary surface constrains
1370 : // more than one primary surface.
1371 146100 : if (nfc->secondaryBoundary() != secondary_boundary ||
1372 73050 : nfc->primaryBoundary() != primary_boundary)
1373 0 : continue;
1374 :
1375 73050 : if (nfc->shouldApply())
1376 : {
1377 44234 : constraints_applied = true;
1378 44234 : nfc->computeResidual();
1379 :
1380 44234 : if (nfc->overwriteSecondaryResidual())
1381 : {
1382 : // The below will actually overwrite the residual for every single dof that
1383 : // lives on the node. We definitely don't want to do that!
1384 : // _fe_problem.setResidual(residual, 0);
1385 :
1386 44234 : const auto & secondary_var = nfc->variable();
1387 44234 : const auto & secondary_dofs = secondary_var.dofIndices();
1388 : mooseAssert(secondary_dofs.size() == secondary_var.count(),
1389 : "We are on a node so there should only be one dof per variable (for "
1390 : "an ArrayVariable we should have a number of dofs equal to the "
1391 : "number of components");
1392 :
1393 : // Assume that if the user is overwriting the secondary residual, then they are
1394 : // supplying residuals that do not correspond to their other physics
1395 : // (e.g. Kernels), hence we should not apply a scalingFactor that is normally
1396 : // based on the order of their other physics (e.g. Kernels)
1397 88468 : std::vector<Number> values = {nfc->secondaryResidual()};
1398 44234 : residual.insert(values, secondary_dofs);
1399 44234 : residual_has_inserted_values = true;
1400 44234 : }
1401 : else
1402 0 : _fe_problem.cacheResidual(0);
1403 44234 : _fe_problem.cacheResidualNeighbor(0);
1404 : }
1405 73050 : if (nfc->hasWritableCoupledVariables())
1406 : {
1407 28816 : Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
1408 28816 : has_writable_variables = true;
1409 57632 : for (auto * var : nfc->getWritableCoupledVariables())
1410 : {
1411 28816 : if (var->isNodalDefined())
1412 28816 : var->insert(_fe_problem.getAuxiliarySystem().solution());
1413 : }
1414 28816 : }
1415 : }
1416 : }
1417 : }
1418 : }
1419 : }
1420 24066 : _communicator.max(has_writable_variables);
1421 :
1422 24066 : if (has_writable_variables)
1423 : {
1424 : // Explicit contact dynamic constraints write to auxiliary variables and update the old
1425 : // displacement solution on the constraint boundaries. Close solutions and update system
1426 : // accordingly.
1427 2401 : _fe_problem.getAuxiliarySystem().solution().close();
1428 2401 : _fe_problem.getAuxiliarySystem().system().update();
1429 2401 : solutionOld().close();
1430 : }
1431 :
1432 24066 : if (_assemble_constraints_separately)
1433 : {
1434 : // Make sure that secondary contribution to primary are assembled, and ghosts have been
1435 : // exchanged, as current primaries might become secondaries on next iteration and will need to
1436 : // contribute their former secondaries' contributions to the future primaries. See if
1437 : // constraints were applied anywhere
1438 0 : _communicator.max(constraints_applied);
1439 :
1440 0 : if (constraints_applied)
1441 : {
1442 : // If any of the above constraints inserted values in the residual, it needs to be
1443 : // assembled before adding the cached residuals below.
1444 0 : _communicator.max(residual_has_inserted_values);
1445 0 : if (residual_has_inserted_values)
1446 : {
1447 0 : residual.close();
1448 0 : residual_has_inserted_values = false;
1449 : }
1450 0 : _fe_problem.addCachedResidualDirectly(residual, 0);
1451 0 : residual.close();
1452 :
1453 0 : if (_need_residual_ghosted)
1454 0 : *_residual_ghosted = residual;
1455 : }
1456 : }
1457 : }
1458 28287 : if (!_assemble_constraints_separately)
1459 : {
1460 28287 : _communicator.max(constraints_applied);
1461 :
1462 28287 : if (constraints_applied)
1463 : {
1464 : // If any of the above constraints inserted values in the residual, it needs to be assembled
1465 : // before adding the cached residuals below.
1466 10864 : _communicator.max(residual_has_inserted_values);
1467 10864 : if (residual_has_inserted_values)
1468 10864 : residual.close();
1469 :
1470 10864 : _fe_problem.addCachedResidualDirectly(residual, 0);
1471 10864 : residual.close();
1472 :
1473 10864 : if (_need_residual_ghosted)
1474 10864 : *_residual_ghosted = residual;
1475 : }
1476 : }
1477 :
1478 : // go over element-element constraint interface
1479 28287 : THREAD_ID tid = 0;
1480 28287 : const auto & element_pair_locators = subproblem.geomSearchData()._element_pair_locators;
1481 28287 : for (const auto & it : element_pair_locators)
1482 : {
1483 0 : ElementPairLocator & elem_pair_loc = *(it.second);
1484 :
1485 0 : if (_constraints.hasActiveElemElemConstraints(it.first, displaced))
1486 : {
1487 : // ElemElemConstraint objects
1488 : const auto & _element_constraints =
1489 0 : _constraints.getActiveElemElemConstraints(it.first, displaced);
1490 :
1491 : // go over pair elements
1492 : const std::list<std::pair<const Elem *, const Elem *>> & elem_pairs =
1493 0 : elem_pair_loc.getElemPairs();
1494 0 : for (const auto & pr : elem_pairs)
1495 : {
1496 0 : const Elem * elem1 = pr.first;
1497 0 : const Elem * elem2 = pr.second;
1498 :
1499 0 : if (elem1->processor_id() != processor_id())
1500 0 : continue;
1501 :
1502 0 : const ElementPairInfo & info = elem_pair_loc.getElemPairInfo(pr);
1503 :
1504 : // for each element process constraints on the
1505 0 : for (const auto & ec : _element_constraints)
1506 : {
1507 0 : _fe_problem.setCurrentSubdomainID(elem1, tid);
1508 0 : subproblem.reinitElemPhys(elem1, info._elem1_constraint_q_point, tid);
1509 0 : _fe_problem.setNeighborSubdomainID(elem2, tid);
1510 0 : subproblem.reinitNeighborPhys(elem2, info._elem2_constraint_q_point, tid);
1511 :
1512 0 : ec->prepareShapes(ec->variable().number());
1513 0 : ec->prepareNeighborShapes(ec->variable().number());
1514 :
1515 0 : ec->reinit(info);
1516 0 : ec->computeResidual();
1517 0 : _fe_problem.cacheResidual(tid);
1518 0 : _fe_problem.cacheResidualNeighbor(tid);
1519 : }
1520 0 : _fe_problem.addCachedResidual(tid);
1521 : }
1522 : }
1523 : }
1524 :
1525 : // go over NodeElemConstraints
1526 28287 : std::set<dof_id_type> unique_secondary_node_ids;
1527 :
1528 28287 : constraints_applied = false;
1529 28287 : residual_has_inserted_values = false;
1530 28287 : bool has_writable_variables = false;
1531 97398 : for (const auto & secondary_id : _mesh.meshSubdomains())
1532 : {
1533 296138 : for (const auto & primary_id : _mesh.meshSubdomains())
1534 : {
1535 227027 : if (_constraints.hasActiveNodeElemConstraints(secondary_id, primary_id, displaced))
1536 : {
1537 : const auto & constraints =
1538 360 : _constraints.getActiveNodeElemConstraints(secondary_id, primary_id, displaced);
1539 :
1540 : // get unique set of ids of all nodes on current block
1541 360 : unique_secondary_node_ids.clear();
1542 360 : const MeshBase & meshhelper = _mesh.getMesh();
1543 720 : for (const auto & elem : as_range(meshhelper.active_subdomain_elements_begin(secondary_id),
1544 17880 : meshhelper.active_subdomain_elements_end(secondary_id)))
1545 : {
1546 98280 : for (auto & n : elem->node_ref_range())
1547 81480 : unique_secondary_node_ids.insert(n.id());
1548 360 : }
1549 :
1550 28080 : for (auto secondary_node_id : unique_secondary_node_ids)
1551 : {
1552 27720 : Node & secondary_node = _mesh.nodeRef(secondary_node_id);
1553 : // check if secondary node is on current processor
1554 27720 : if (secondary_node.processor_id() == processor_id())
1555 : {
1556 : // This reinits the variables that exist on the secondary node
1557 22176 : _fe_problem.reinitNodeFace(&secondary_node, secondary_id, 0);
1558 :
1559 : // This will set aside residual and jacobian space for the variables that have dofs
1560 : // on the secondary node
1561 22176 : _fe_problem.prepareAssembly(0);
1562 :
1563 47168 : for (const auto & nec : constraints)
1564 : {
1565 24992 : if (nec->shouldApply())
1566 : {
1567 11360 : constraints_applied = true;
1568 11360 : nec->computeResidual();
1569 :
1570 11360 : if (nec->overwriteSecondaryResidual())
1571 : {
1572 0 : _fe_problem.setResidual(residual, 0);
1573 0 : residual_has_inserted_values = true;
1574 : }
1575 : else
1576 11360 : _fe_problem.cacheResidual(0);
1577 11360 : _fe_problem.cacheResidualNeighbor(0);
1578 : }
1579 24992 : if (nec->hasWritableCoupledVariables())
1580 : {
1581 352 : Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
1582 352 : has_writable_variables = true;
1583 704 : for (auto * var : nec->getWritableCoupledVariables())
1584 : {
1585 352 : if (var->isNodalDefined())
1586 352 : var->insert(_fe_problem.getAuxiliarySystem().solution());
1587 : }
1588 352 : }
1589 : }
1590 22176 : _fe_problem.addCachedResidual(0);
1591 : }
1592 : }
1593 : }
1594 : }
1595 : }
1596 28287 : _communicator.max(constraints_applied);
1597 :
1598 28287 : if (constraints_applied)
1599 : {
1600 : // If any of the above constraints inserted values in the residual, it needs to be assembled
1601 : // before adding the cached residuals below.
1602 360 : _communicator.max(residual_has_inserted_values);
1603 360 : if (residual_has_inserted_values)
1604 0 : residual.close();
1605 :
1606 360 : _fe_problem.addCachedResidualDirectly(residual, 0);
1607 360 : residual.close();
1608 :
1609 360 : if (_need_residual_ghosted)
1610 360 : *_residual_ghosted = residual;
1611 : }
1612 28287 : _communicator.max(has_writable_variables);
1613 :
1614 28287 : if (has_writable_variables)
1615 : {
1616 : // Explicit contact dynamic constraints write to auxiliary variables and update the old
1617 : // displacement solution on the constraint boundaries. Close solutions and update system
1618 : // accordingly.
1619 20 : _fe_problem.getAuxiliarySystem().solution().close();
1620 20 : _fe_problem.getAuxiliarySystem().system().update();
1621 20 : solutionOld().close();
1622 : }
1623 :
1624 : // We may have additional tagged vectors that also need to be accumulated
1625 28287 : _fe_problem.addCachedResidual(0);
1626 28287 : }
1627 :
1628 : void
1629 4497 : NonlinearSystemBase::overwriteNodeFace(NumericVector<Number> & soln)
1630 : {
1631 : // Overwrite results from integrator in case we have explicit dynamics contact constraints
1632 4497 : auto & subproblem = _fe_problem.getDisplacedProblem()
1633 6898 : ? static_cast<SubProblem &>(*_fe_problem.getDisplacedProblem())
1634 6898 : : static_cast<SubProblem &>(_fe_problem);
1635 4497 : const auto & penetration_locators = subproblem.geomSearchData()._penetration_locators;
1636 :
1637 6898 : for (const auto & it : penetration_locators)
1638 : {
1639 2401 : PenetrationLocator & pen_loc = *(it.second);
1640 :
1641 2401 : const auto & secondary_nodes = pen_loc._nearest_node._secondary_nodes;
1642 2401 : const BoundaryID secondary_boundary = pen_loc._secondary_boundary;
1643 2401 : const BoundaryID primary_boundary = pen_loc._primary_boundary;
1644 :
1645 2401 : if (_constraints.hasActiveNodeFaceConstraints(secondary_boundary, true))
1646 : {
1647 : const auto & constraints =
1648 2401 : _constraints.getActiveNodeFaceConstraints(secondary_boundary, true);
1649 39217 : for (const auto i : index_range(secondary_nodes))
1650 : {
1651 36816 : const auto secondary_node_num = secondary_nodes[i];
1652 36816 : const Node & secondary_node = _mesh.nodeRef(secondary_node_num);
1653 :
1654 36816 : if (secondary_node.processor_id() == processor_id())
1655 28816 : if (pen_loc._penetration_info[secondary_node_num])
1656 57632 : for (const auto & nfc : constraints)
1657 : {
1658 28816 : if (!nfc->isExplicitConstraint())
1659 0 : continue;
1660 :
1661 : // Return if this constraint does not correspond to the primary-secondary pair
1662 : // prepared by the outer loops.
1663 : // This continue statement is required when, e.g. one secondary surface constrains
1664 : // more than one primary surface.
1665 57632 : if (nfc->secondaryBoundary() != secondary_boundary ||
1666 28816 : nfc->primaryBoundary() != primary_boundary)
1667 0 : continue;
1668 :
1669 28816 : nfc->overwriteBoundaryVariables(soln, secondary_node);
1670 : }
1671 : }
1672 : }
1673 : }
1674 4497 : soln.close();
1675 4497 : }
1676 :
1677 : void
1678 3389487 : NonlinearSystemBase::residualSetup()
1679 : {
1680 10168461 : TIME_SECTION("residualSetup", 3);
1681 :
1682 3389487 : SolverSystem::residualSetup();
1683 :
1684 7082915 : for (THREAD_ID tid = 0; tid < libMesh::n_threads(); tid++)
1685 : {
1686 3693428 : _kernels.residualSetup(tid);
1687 3693428 : _nodal_kernels.residualSetup(tid);
1688 3693428 : _dirac_kernels.residualSetup(tid);
1689 3693428 : if (_doing_dg)
1690 35297 : _dg_kernels.residualSetup(tid);
1691 3693428 : _interface_kernels.residualSetup(tid);
1692 3693428 : _element_dampers.residualSetup(tid);
1693 3693428 : _nodal_dampers.residualSetup(tid);
1694 3693428 : _integrated_bcs.residualSetup(tid);
1695 : }
1696 3389487 : _scalar_kernels.residualSetup();
1697 3389487 : _constraints.residualSetup();
1698 3389487 : _general_dampers.residualSetup();
1699 3389487 : _nodal_bcs.residualSetup();
1700 :
1701 : // Avoid recursion
1702 3389487 : if (this == &_fe_problem.currentNonlinearSystem())
1703 3296947 : _fe_problem.residualSetup();
1704 3389487 : _app.solutionInvalidity().resetSolutionInvalidCurrentIteration();
1705 3389487 : }
1706 :
1707 : void
1708 3293480 : NonlinearSystemBase::computeResidualInternal(const std::set<TagID> & tags)
1709 : {
1710 : parallel_object_only();
1711 :
1712 9880440 : TIME_SECTION("computeResidualInternal", 3);
1713 :
1714 3293480 : residualSetup();
1715 :
1716 3293480 : const auto vector_tag_data = _fe_problem.getVectorTags(tags);
1717 :
1718 : // Residual contributions from UOs - for now this is used for ray tracing
1719 : // and ray kernels that contribute to the residual (think line sources)
1720 3293480 : std::vector<UserObject *> uos;
1721 3293480 : _fe_problem.theWarehouse()
1722 6586960 : .query()
1723 3293480 : .condition<AttribSystem>("UserObject")
1724 3293480 : .condition<AttribExecOns>(EXEC_PRE_KERNELS)
1725 3293480 : .queryInto(uos);
1726 3293480 : for (auto & uo : uos)
1727 0 : uo->residualSetup();
1728 3293480 : for (auto & uo : uos)
1729 : {
1730 0 : uo->initialize();
1731 0 : uo->execute();
1732 0 : uo->finalize();
1733 : }
1734 :
1735 : // reinit scalar variables
1736 6882682 : for (unsigned int tid = 0; tid < libMesh::n_threads(); tid++)
1737 3589202 : _fe_problem.reinitScalars(tid);
1738 :
1739 : // residual contributions from the domain
1740 : PARALLEL_TRY
1741 : {
1742 9880440 : TIME_SECTION("Kernels", 3 /*, "Computing Kernels"*/);
1743 :
1744 3293480 : const ConstElemRange & elem_range = _fe_problem.getCurrentAlgebraicElementRange();
1745 :
1746 3293480 : ComputeResidualThread cr(_fe_problem, tags);
1747 3293480 : Threads::parallel_reduce(elem_range, cr);
1748 :
1749 : // We pass face information directly to FV residual objects for their evaluation. Consequently
1750 : // we must make sure to do separate threaded loops for 1) undisplaced face information objects
1751 : // and undisplaced residual objects and 2) displaced face information objects and displaced
1752 : // residual objects
1753 : using FVRange = StoredRange<MooseMesh::const_face_info_iterator, const FaceInfo *>;
1754 3293456 : if (_fe_problem.haveFV())
1755 : {
1756 : ComputeFVFluxResidualThread<FVRange> fvr(
1757 150558 : _fe_problem, this->number(), tags, /*on_displaced=*/false);
1758 150558 : FVRange faces(_fe_problem.mesh().ownedFaceInfoBegin(), _fe_problem.mesh().ownedFaceInfoEnd());
1759 150558 : Threads::parallel_reduce(faces, fvr);
1760 150558 : }
1761 3293448 : if (auto displaced_problem = _fe_problem.getDisplacedProblem();
1762 3293448 : displaced_problem && displaced_problem->haveFV())
1763 : {
1764 : ComputeFVFluxResidualThread<FVRange> fvr(
1765 0 : _fe_problem, this->number(), tags, /*on_displaced=*/true);
1766 0 : FVRange faces(displaced_problem->mesh().ownedFaceInfoBegin(),
1767 0 : displaced_problem->mesh().ownedFaceInfoEnd());
1768 0 : Threads::parallel_reduce(faces, fvr);
1769 3293448 : }
1770 :
1771 3293448 : unsigned int n_threads = libMesh::n_threads();
1772 6882610 : for (unsigned int i = 0; i < n_threads;
1773 : i++) // Add any cached residuals that might be hanging around
1774 3589162 : _fe_problem.addCachedResidual(i);
1775 3293448 : }
1776 3293448 : PARALLEL_CATCH;
1777 :
1778 : // residual contributions from the scalar kernels
1779 : PARALLEL_TRY
1780 : {
1781 : // do scalar kernels (not sure how to thread this)
1782 3293327 : if (_scalar_kernels.hasActiveObjects())
1783 : {
1784 221043 : TIME_SECTION("ScalarKernels", 3 /*, "Computing ScalarKernels"*/);
1785 :
1786 : MooseObjectWarehouse<ScalarKernelBase> * scalar_kernel_warehouse;
1787 : // This code should be refactored once we can do tags for scalar
1788 : // kernels
1789 : // Should redo this based on Warehouse
1790 73681 : if (!tags.size() || tags.size() == _fe_problem.numVectorTags(Moose::VECTOR_TAG_RESIDUAL))
1791 73660 : scalar_kernel_warehouse = &_scalar_kernels;
1792 21 : else if (tags.size() == 1)
1793 : scalar_kernel_warehouse =
1794 12 : &(_scalar_kernels.getVectorTagObjectWarehouse(*(tags.begin()), 0));
1795 : else
1796 : // scalar_kernels is not threading
1797 9 : scalar_kernel_warehouse = &(_scalar_kernels.getVectorTagsObjectWarehouse(tags, 0));
1798 :
1799 73681 : bool have_scalar_contributions = false;
1800 73681 : const auto & scalars = scalar_kernel_warehouse->getActiveObjects();
1801 270946 : for (const auto & scalar_kernel : scalars)
1802 : {
1803 197265 : scalar_kernel->reinit();
1804 197265 : const std::vector<dof_id_type> & dof_indices = scalar_kernel->variable().dofIndices();
1805 197265 : const DofMap & dof_map = scalar_kernel->variable().dofMap();
1806 197265 : const dof_id_type first_dof = dof_map.first_dof();
1807 197265 : const dof_id_type end_dof = dof_map.end_dof();
1808 227734 : for (dof_id_type dof : dof_indices)
1809 : {
1810 197842 : if (dof >= first_dof && dof < end_dof)
1811 : {
1812 167373 : scalar_kernel->computeResidual();
1813 167373 : have_scalar_contributions = true;
1814 167373 : break;
1815 : }
1816 : }
1817 : }
1818 73681 : if (have_scalar_contributions)
1819 64576 : _fe_problem.addResidualScalar();
1820 73681 : }
1821 : }
1822 3293327 : PARALLEL_CATCH;
1823 :
1824 : // residual contributions from Block NodalKernels
1825 : PARALLEL_TRY
1826 : {
1827 3293327 : if (_nodal_kernels.hasActiveBlockObjects())
1828 : {
1829 46788 : TIME_SECTION("NodalKernels", 3 /*, "Computing NodalKernels"*/);
1830 :
1831 15596 : ComputeNodalKernelsThread cnk(_fe_problem, _nodal_kernels, tags);
1832 :
1833 15596 : const ConstNodeRange & range = _fe_problem.getCurrentAlgebraicNodeRange();
1834 :
1835 15596 : if (range.begin() != range.end())
1836 : {
1837 15596 : _fe_problem.reinitNode(*range.begin(), 0);
1838 :
1839 15596 : Threads::parallel_reduce(range, cnk);
1840 :
1841 15596 : unsigned int n_threads = libMesh::n_threads();
1842 38526 : for (unsigned int i = 0; i < n_threads;
1843 : i++) // Add any cached residuals that might be hanging around
1844 22930 : _fe_problem.addCachedResidual(i);
1845 : }
1846 15596 : }
1847 : }
1848 3293327 : PARALLEL_CATCH;
1849 :
1850 3293327 : if (_fe_problem.computingScalingResidual())
1851 : // We computed the volumetric objects. We can return now before we get into
1852 : // any strongly enforced constraint conditions or penalty-type objects
1853 : // (DGKernels, IntegratedBCs, InterfaceKernels, Constraints)
1854 50 : return;
1855 :
1856 : // residual contributions from boundary NodalKernels
1857 : PARALLEL_TRY
1858 : {
1859 3293277 : if (_nodal_kernels.hasActiveBoundaryObjects())
1860 : {
1861 3012 : TIME_SECTION("NodalKernelBCs", 3 /*, "Computing NodalKernelBCs"*/);
1862 :
1863 1004 : ComputeNodalKernelBcsThread cnk(_fe_problem, _nodal_kernels, tags);
1864 :
1865 1004 : const ConstBndNodeRange & bnd_node_range = _fe_problem.getCurrentAlgebraicBndNodeRange();
1866 :
1867 1004 : Threads::parallel_reduce(bnd_node_range, cnk);
1868 :
1869 1004 : unsigned int n_threads = libMesh::n_threads();
1870 2094 : for (unsigned int i = 0; i < n_threads;
1871 : i++) // Add any cached residuals that might be hanging around
1872 1090 : _fe_problem.addCachedResidual(i);
1873 1004 : }
1874 : }
1875 3293277 : PARALLEL_CATCH;
1876 :
1877 3293277 : mortarConstraints(Moose::ComputeType::Residual, tags, {});
1878 :
1879 3293277 : if (_residual_copy.get())
1880 : {
1881 0 : _Re_non_time->close();
1882 0 : _Re_non_time->localize(*_residual_copy);
1883 : }
1884 :
1885 3293277 : if (_need_residual_ghosted)
1886 : {
1887 13234 : _Re_non_time->close();
1888 13234 : *_residual_ghosted = *_Re_non_time;
1889 13234 : _residual_ghosted->close();
1890 : }
1891 :
1892 3293277 : PARALLEL_TRY { computeDiracContributions(tags, false); }
1893 3293265 : PARALLEL_CATCH;
1894 :
1895 3293265 : if (_fe_problem._has_constraints)
1896 : {
1897 23409 : PARALLEL_TRY { enforceNodalConstraintsResidual(*_Re_non_time); }
1898 23409 : PARALLEL_CATCH;
1899 23409 : _Re_non_time->close();
1900 : }
1901 :
1902 : // Add in Residual contributions from other Constraints
1903 3293265 : if (_fe_problem._has_constraints)
1904 : {
1905 : PARALLEL_TRY
1906 : {
1907 : // Undisplaced Constraints
1908 23409 : constraintResiduals(*_Re_non_time, false);
1909 :
1910 : // Displaced Constraints
1911 23409 : if (_fe_problem.getDisplacedProblem())
1912 4878 : constraintResiduals(*_Re_non_time, true);
1913 :
1914 23409 : if (_fe_problem.computingNonlinearResid())
1915 10705 : _constraints.residualEnd();
1916 : }
1917 23409 : PARALLEL_CATCH;
1918 23409 : _Re_non_time->close();
1919 : }
1920 :
1921 : // Accumulate the occurrence of solution invalid warnings for the current iteration cumulative
1922 : // counters
1923 3293265 : _app.solutionInvalidity().syncIteration();
1924 3293265 : _app.solutionInvalidity().solutionInvalidAccumulation();
1925 3293778 : }
1926 :
1927 : void
1928 3467 : NonlinearSystemBase::computeResidualAndJacobianInternal(const std::set<TagID> & vector_tags,
1929 : const std::set<TagID> & matrix_tags)
1930 : {
1931 10401 : TIME_SECTION("computeResidualAndJacobianInternal", 3);
1932 :
1933 : // Make matrix ready to use
1934 3467 : activateAllMatrixTags();
1935 :
1936 10401 : for (auto tag : matrix_tags)
1937 : {
1938 6934 : if (!hasMatrix(tag))
1939 3467 : continue;
1940 :
1941 3467 : auto & jacobian = getMatrix(tag);
1942 : // Necessary for speed
1943 3467 : if (auto petsc_matrix = dynamic_cast<PetscMatrix<Number> *>(&jacobian))
1944 : {
1945 3467 : LibmeshPetscCall(MatSetOption(petsc_matrix->mat(),
1946 : MAT_KEEP_NONZERO_PATTERN, // This is changed in 3.1
1947 : PETSC_TRUE));
1948 3467 : if (!_fe_problem.errorOnJacobianNonzeroReallocation())
1949 0 : LibmeshPetscCall(
1950 : MatSetOption(petsc_matrix->mat(), MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE));
1951 3467 : if (_fe_problem.ignoreZerosInJacobian())
1952 0 : LibmeshPetscCall(MatSetOption(static_cast<PetscMatrix<Number> &>(jacobian).mat(),
1953 : MAT_IGNORE_ZERO_ENTRIES,
1954 : PETSC_TRUE));
1955 : }
1956 : }
1957 :
1958 3467 : residualSetup();
1959 :
1960 : // Residual contributions from UOs - for now this is used for ray tracing
1961 : // and ray kernels that contribute to the residual (think line sources)
1962 3467 : std::vector<UserObject *> uos;
1963 3467 : _fe_problem.theWarehouse()
1964 6934 : .query()
1965 3467 : .condition<AttribSystem>("UserObject")
1966 3467 : .condition<AttribExecOns>(EXEC_PRE_KERNELS)
1967 3467 : .queryInto(uos);
1968 3467 : for (auto & uo : uos)
1969 0 : uo->residualSetup();
1970 3467 : for (auto & uo : uos)
1971 : {
1972 0 : uo->initialize();
1973 0 : uo->execute();
1974 0 : uo->finalize();
1975 : }
1976 :
1977 : // reinit scalar variables
1978 7273 : for (unsigned int tid = 0; tid < libMesh::n_threads(); tid++)
1979 3806 : _fe_problem.reinitScalars(tid);
1980 :
1981 : // residual contributions from the domain
1982 : PARALLEL_TRY
1983 : {
1984 10401 : TIME_SECTION("Kernels", 3 /*, "Computing Kernels"*/);
1985 :
1986 3467 : const ConstElemRange & elem_range = _fe_problem.getCurrentAlgebraicElementRange();
1987 :
1988 3467 : ComputeResidualAndJacobianThread crj(_fe_problem, vector_tags, matrix_tags);
1989 3467 : Threads::parallel_reduce(elem_range, crj);
1990 :
1991 : using FVRange = StoredRange<MooseMesh::const_face_info_iterator, const FaceInfo *>;
1992 3467 : if (_fe_problem.haveFV())
1993 : {
1994 : ComputeFVFluxRJThread<FVRange> fvrj(
1995 1451 : _fe_problem, this->number(), vector_tags, matrix_tags, /*on_displaced=*/false);
1996 1451 : FVRange faces(_fe_problem.mesh().ownedFaceInfoBegin(), _fe_problem.mesh().ownedFaceInfoEnd());
1997 1451 : Threads::parallel_reduce(faces, fvrj);
1998 1451 : }
1999 3467 : if (auto displaced_problem = _fe_problem.getDisplacedProblem();
2000 3467 : displaced_problem && displaced_problem->haveFV())
2001 : {
2002 : ComputeFVFluxRJThread<FVRange> fvr(
2003 0 : _fe_problem, this->number(), vector_tags, matrix_tags, /*on_displaced=*/true);
2004 0 : FVRange faces(displaced_problem->mesh().ownedFaceInfoBegin(),
2005 0 : displaced_problem->mesh().ownedFaceInfoEnd());
2006 0 : Threads::parallel_reduce(faces, fvr);
2007 3467 : }
2008 :
2009 3467 : mortarConstraints(Moose::ComputeType::ResidualAndJacobian, vector_tags, matrix_tags);
2010 :
2011 3467 : unsigned int n_threads = libMesh::n_threads();
2012 7273 : for (unsigned int i = 0; i < n_threads;
2013 : i++) // Add any cached residuals that might be hanging around
2014 : {
2015 3806 : _fe_problem.addCachedResidual(i);
2016 3806 : _fe_problem.addCachedJacobian(i);
2017 : }
2018 3467 : }
2019 3467 : PARALLEL_CATCH;
2020 3467 : }
2021 :
2022 : void
2023 0 : NonlinearSystemBase::computeNodalBCs(NumericVector<Number> & residual)
2024 : {
2025 0 : _nl_vector_tags.clear();
2026 :
2027 0 : const auto & residual_vector_tags = _fe_problem.getVectorTags(Moose::VECTOR_TAG_RESIDUAL);
2028 0 : for (const auto & residual_vector_tag : residual_vector_tags)
2029 0 : _nl_vector_tags.insert(residual_vector_tag._id);
2030 :
2031 0 : associateVectorToTag(residual, residualVectorTag());
2032 0 : computeNodalBCs(residual, _nl_vector_tags);
2033 0 : disassociateVectorFromTag(residual, residualVectorTag());
2034 0 : }
2035 :
2036 : void
2037 0 : NonlinearSystemBase::computeNodalBCs(NumericVector<Number> & residual, const std::set<TagID> & tags)
2038 : {
2039 0 : associateVectorToTag(residual, residualVectorTag());
2040 :
2041 0 : computeNodalBCs(tags);
2042 :
2043 0 : disassociateVectorFromTag(residual, residualVectorTag());
2044 0 : }
2045 :
2046 : void
2047 3293265 : NonlinearSystemBase::computeNodalBCs(const std::set<TagID> & tags)
2048 : {
2049 : // We need to close the diag_save_in variables on the aux system before NodalBCBases clear the
2050 : // dofs on boundary nodes
2051 3293265 : if (_has_save_in)
2052 309 : _fe_problem.getAuxiliarySystem().solution().close();
2053 :
2054 : // Select nodal kernels
2055 : MooseObjectWarehouse<NodalBCBase> * nbc_warehouse;
2056 :
2057 3293265 : if (tags.size() == _fe_problem.numVectorTags(Moose::VECTOR_TAG_RESIDUAL) || !tags.size())
2058 3254434 : nbc_warehouse = &_nodal_bcs;
2059 38831 : else if (tags.size() == 1)
2060 18286 : nbc_warehouse = &(_nodal_bcs.getVectorTagObjectWarehouse(*(tags.begin()), 0));
2061 : else
2062 20545 : nbc_warehouse = &(_nodal_bcs.getVectorTagsObjectWarehouse(tags, 0));
2063 :
2064 : // Return early if there is no nodal kernel
2065 3293265 : if (!nbc_warehouse->size())
2066 420152 : return;
2067 :
2068 : PARALLEL_TRY
2069 : {
2070 2873113 : const ConstBndNodeRange & bnd_nodes = _fe_problem.getCurrentAlgebraicBndNodeRange();
2071 :
2072 2873113 : if (!bnd_nodes.empty())
2073 : {
2074 8618406 : TIME_SECTION("NodalBCs", 3 /*, "Computing NodalBCs"*/);
2075 :
2076 150801504 : for (const auto & bnode : bnd_nodes)
2077 : {
2078 147928702 : BoundaryID boundary_id = bnode->_bnd_id;
2079 147928702 : Node * node = bnode->_node;
2080 :
2081 261696152 : if (node->processor_id() == processor_id() &&
2082 113767450 : nbc_warehouse->hasActiveBoundaryObjects(boundary_id))
2083 : {
2084 : // reinit variables in nodes
2085 58149792 : _fe_problem.reinitNodeFace(node, boundary_id, 0);
2086 :
2087 58149792 : const auto & bcs = nbc_warehouse->getActiveBoundaryObjects(boundary_id);
2088 121908876 : for (const auto & nbc : bcs)
2089 63759084 : if (nbc->shouldApply())
2090 63757680 : nbc->computeResidual();
2091 : }
2092 : }
2093 2872802 : }
2094 : }
2095 2873113 : PARALLEL_CATCH;
2096 :
2097 2873113 : if (_Re_time)
2098 2551133 : _Re_time->close();
2099 2873113 : _Re_non_time->close();
2100 : }
2101 :
2102 : void
2103 3467 : NonlinearSystemBase::computeNodalBCsResidualAndJacobian()
2104 : {
2105 : PARALLEL_TRY
2106 : {
2107 3467 : const ConstBndNodeRange & bnd_nodes = _fe_problem.getCurrentAlgebraicBndNodeRange();
2108 :
2109 3467 : if (!bnd_nodes.empty())
2110 : {
2111 10401 : TIME_SECTION("NodalBCs", 3 /*, "Computing NodalBCs"*/);
2112 :
2113 86555 : for (const auto & bnode : bnd_nodes)
2114 : {
2115 83088 : BoundaryID boundary_id = bnode->_bnd_id;
2116 83088 : Node * node = bnode->_node;
2117 :
2118 83088 : if (node->processor_id() == processor_id())
2119 : {
2120 : // reinit variables in nodes
2121 62704 : _fe_problem.reinitNodeFace(node, boundary_id, 0);
2122 62704 : if (_nodal_bcs.hasActiveBoundaryObjects(boundary_id))
2123 : {
2124 12501 : const auto & bcs = _nodal_bcs.getActiveBoundaryObjects(boundary_id);
2125 25002 : for (const auto & nbc : bcs)
2126 12501 : if (nbc->shouldApply())
2127 12501 : nbc->computeResidualAndJacobian();
2128 : }
2129 : }
2130 : }
2131 3467 : }
2132 : }
2133 3467 : PARALLEL_CATCH;
2134 :
2135 : // Set the cached NodalBCBase values in the Jacobian matrix
2136 3467 : _fe_problem.assembly(0, number()).setCachedJacobian(Assembly::GlobalDataKey{});
2137 3467 : }
2138 :
2139 : void
2140 717 : NonlinearSystemBase::getNodeDofs(dof_id_type node_id, std::vector<dof_id_type> & dofs)
2141 : {
2142 717 : const Node & node = _mesh.nodeRef(node_id);
2143 717 : unsigned int s = number();
2144 717 : if (node.has_dofs(s))
2145 : {
2146 1472 : for (unsigned int v = 0; v < nVariables(); v++)
2147 1472 : for (unsigned int c = 0; c < node.n_comp(s, v); c++)
2148 717 : dofs.push_back(node.dof_number(s, v, c));
2149 : }
2150 717 : }
2151 :
2152 : void
2153 5567 : NonlinearSystemBase::findImplicitGeometricCouplingEntries(
2154 : GeometricSearchData & geom_search_data,
2155 : std::unordered_map<dof_id_type, std::vector<dof_id_type>> & graph)
2156 : {
2157 5567 : const auto & node_to_elem_map = _mesh.nodeToElemMap();
2158 5567 : const auto & nearest_node_locators = geom_search_data._nearest_node_locators;
2159 7696 : for (const auto & it : nearest_node_locators)
2160 : {
2161 2129 : std::vector<dof_id_type> & secondary_nodes = it.second->_secondary_nodes;
2162 :
2163 10915 : for (const auto & secondary_node : secondary_nodes)
2164 : {
2165 8786 : std::set<dof_id_type> unique_secondary_indices;
2166 8786 : std::set<dof_id_type> unique_primary_indices;
2167 :
2168 8786 : auto node_to_elem_pair = node_to_elem_map.find(secondary_node);
2169 8786 : if (node_to_elem_pair != node_to_elem_map.end())
2170 : {
2171 8666 : const std::vector<dof_id_type> & elems = node_to_elem_pair->second;
2172 :
2173 : // Get the dof indices from each elem connected to the node
2174 23314 : for (const auto & cur_elem : elems)
2175 : {
2176 14648 : std::vector<dof_id_type> dof_indices;
2177 14648 : dofMap().dof_indices(_mesh.elemPtr(cur_elem), dof_indices);
2178 :
2179 73240 : for (const auto & dof : dof_indices)
2180 58592 : unique_secondary_indices.insert(dof);
2181 14648 : }
2182 : }
2183 :
2184 8786 : std::vector<dof_id_type> primary_nodes = it.second->_neighbor_nodes[secondary_node];
2185 :
2186 49576 : for (const auto & primary_node : primary_nodes)
2187 : {
2188 40790 : auto primary_node_to_elem_pair = node_to_elem_map.find(primary_node);
2189 : mooseAssert(primary_node_to_elem_pair != node_to_elem_map.end(),
2190 : "Missing entry in node to elem map");
2191 40790 : const std::vector<dof_id_type> & primary_node_elems = primary_node_to_elem_pair->second;
2192 :
2193 : // Get the dof indices from each elem connected to the node
2194 107078 : for (const auto & cur_elem : primary_node_elems)
2195 : {
2196 66288 : std::vector<dof_id_type> dof_indices;
2197 66288 : dofMap().dof_indices(_mesh.elemPtr(cur_elem), dof_indices);
2198 :
2199 331440 : for (const auto & dof : dof_indices)
2200 265152 : unique_primary_indices.insert(dof);
2201 66288 : }
2202 : }
2203 :
2204 55414 : for (const auto & secondary_id : unique_secondary_indices)
2205 506684 : for (const auto & primary_id : unique_primary_indices)
2206 : {
2207 460056 : graph[secondary_id].push_back(primary_id);
2208 460056 : graph[primary_id].push_back(secondary_id);
2209 : }
2210 8786 : }
2211 : }
2212 :
2213 : // handle node-to-node constraints
2214 5567 : const auto & ncs = _constraints.getActiveNodalConstraints();
2215 5724 : for (const auto & nc : ncs)
2216 : {
2217 157 : std::vector<dof_id_type> primary_dofs;
2218 157 : std::vector<dof_id_type> & primary_node_ids = nc->getPrimaryNodeId();
2219 314 : for (const auto & node_id : primary_node_ids)
2220 : {
2221 157 : Node * node = _mesh.queryNodePtr(node_id);
2222 157 : if (node && node->processor_id() == this->processor_id())
2223 : {
2224 142 : getNodeDofs(node_id, primary_dofs);
2225 : }
2226 : }
2227 :
2228 157 : _communicator.allgather(primary_dofs);
2229 :
2230 157 : std::vector<dof_id_type> secondary_dofs;
2231 157 : std::vector<dof_id_type> & secondary_node_ids = nc->getSecondaryNodeId();
2232 732 : for (const auto & node_id : secondary_node_ids)
2233 : {
2234 575 : Node * node = _mesh.queryNodePtr(node_id);
2235 575 : if (node && node->processor_id() == this->processor_id())
2236 : {
2237 575 : getNodeDofs(node_id, secondary_dofs);
2238 : }
2239 : }
2240 :
2241 157 : _communicator.allgather(secondary_dofs);
2242 :
2243 314 : for (const auto & primary_id : primary_dofs)
2244 870 : for (const auto & secondary_id : secondary_dofs)
2245 : {
2246 713 : graph[primary_id].push_back(secondary_id);
2247 713 : graph[secondary_id].push_back(primary_id);
2248 : }
2249 157 : }
2250 :
2251 : // Make every entry sorted and unique
2252 30319 : for (auto & it : graph)
2253 : {
2254 24752 : std::vector<dof_id_type> & row = it.second;
2255 24752 : std::sort(row.begin(), row.end());
2256 24752 : std::vector<dof_id_type>::iterator uit = std::unique(row.begin(), row.end());
2257 24752 : row.resize(uit - row.begin());
2258 : }
2259 5567 : }
2260 :
2261 : void
2262 4135 : NonlinearSystemBase::addImplicitGeometricCouplingEntries(GeometricSearchData & geom_search_data)
2263 : {
2264 4135 : if (!hasMatrix(systemMatrixTag()))
2265 0 : mooseError("Need a system matrix ");
2266 :
2267 : // At this point, have no idea how to make
2268 : // this work with tag system
2269 4135 : auto & jacobian = getMatrix(systemMatrixTag());
2270 :
2271 4135 : std::unordered_map<dof_id_type, std::vector<dof_id_type>> graph;
2272 :
2273 4135 : findImplicitGeometricCouplingEntries(geom_search_data, graph);
2274 :
2275 28010 : for (const auto & it : graph)
2276 : {
2277 23875 : dof_id_type dof = it.first;
2278 23875 : const auto & row = it.second;
2279 :
2280 281261 : for (const auto & coupled_dof : row)
2281 257386 : jacobian.add(dof, coupled_dof, 0);
2282 : }
2283 4135 : }
2284 :
2285 : void
2286 4763 : NonlinearSystemBase::constraintJacobians(const SparseMatrix<Number> & jacobian_to_view,
2287 : bool displaced)
2288 : {
2289 4763 : if (!hasMatrix(systemMatrixTag()))
2290 0 : mooseError("A system matrix is required");
2291 :
2292 4763 : auto & jacobian = getMatrix(systemMatrixTag());
2293 :
2294 4763 : if (!_fe_problem.errorOnJacobianNonzeroReallocation())
2295 0 : LibmeshPetscCall(MatSetOption(static_cast<PetscMatrix<Number> &>(jacobian).mat(),
2296 : MAT_NEW_NONZERO_ALLOCATION_ERR,
2297 : PETSC_FALSE));
2298 4763 : if (_fe_problem.ignoreZerosInJacobian())
2299 0 : LibmeshPetscCall(MatSetOption(
2300 : static_cast<PetscMatrix<Number> &>(jacobian).mat(), MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
2301 :
2302 4763 : std::vector<numeric_index_type> zero_rows;
2303 :
2304 : if (displaced)
2305 : mooseAssert(_fe_problem.getDisplacedProblem(),
2306 : "If we're calling this method with displaced = true, then we better well have a "
2307 : "displaced problem");
2308 2192 : auto & subproblem = displaced ? static_cast<SubProblem &>(*_fe_problem.getDisplacedProblem())
2309 5859 : : static_cast<SubProblem &>(_fe_problem);
2310 4763 : const auto & penetration_locators = subproblem.geomSearchData()._penetration_locators;
2311 :
2312 : bool constraints_applied;
2313 4763 : if (!_assemble_constraints_separately)
2314 4763 : constraints_applied = false;
2315 6829 : for (const auto & it : penetration_locators)
2316 : {
2317 2066 : if (_assemble_constraints_separately)
2318 : {
2319 : // Reset the constraint_applied flag before each new constraint, as they need to be
2320 : // assembled separately
2321 0 : constraints_applied = false;
2322 : }
2323 2066 : PenetrationLocator & pen_loc = *(it.second);
2324 :
2325 2066 : std::vector<dof_id_type> & secondary_nodes = pen_loc._nearest_node._secondary_nodes;
2326 :
2327 2066 : BoundaryID secondary_boundary = pen_loc._secondary_boundary;
2328 2066 : BoundaryID primary_boundary = pen_loc._primary_boundary;
2329 :
2330 2066 : zero_rows.clear();
2331 2066 : if (_constraints.hasActiveNodeFaceConstraints(secondary_boundary, displaced))
2332 : {
2333 : const auto & constraints =
2334 1128 : _constraints.getActiveNodeFaceConstraints(secondary_boundary, displaced);
2335 :
2336 5726 : for (const auto & secondary_node_num : secondary_nodes)
2337 : {
2338 4598 : Node & secondary_node = _mesh.nodeRef(secondary_node_num);
2339 :
2340 4598 : if (secondary_node.processor_id() == processor_id())
2341 : {
2342 4184 : if (pen_loc._penetration_info[secondary_node_num])
2343 : {
2344 4184 : PenetrationInfo & info = *pen_loc._penetration_info[secondary_node_num];
2345 :
2346 4184 : reinitNodeFace(secondary_node, secondary_boundary, info, displaced);
2347 4184 : _fe_problem.reinitOffDiagScalars(0);
2348 :
2349 8368 : for (const auto & nfc : constraints)
2350 : {
2351 4184 : if (nfc->isExplicitConstraint())
2352 0 : continue;
2353 : // Return if this constraint does not correspond to the primary-secondary pair
2354 : // prepared by the outer loops.
2355 : // This continue statement is required when, e.g. one secondary surface constrains
2356 : // more than one primary surface.
2357 8368 : if (nfc->secondaryBoundary() != secondary_boundary ||
2358 4184 : nfc->primaryBoundary() != primary_boundary)
2359 0 : continue;
2360 :
2361 4184 : nfc->_jacobian = &jacobian_to_view;
2362 :
2363 4184 : if (nfc->shouldApply())
2364 : {
2365 4184 : constraints_applied = true;
2366 :
2367 4184 : nfc->prepareShapes(nfc->variable().number());
2368 4184 : nfc->prepareNeighborShapes(nfc->variable().number());
2369 :
2370 4184 : nfc->computeJacobian();
2371 :
2372 4184 : if (nfc->overwriteSecondaryJacobian())
2373 : {
2374 : // Add this variable's dof's row to be zeroed
2375 4184 : zero_rows.push_back(nfc->variable().nodalDofIndex());
2376 : }
2377 :
2378 4184 : std::vector<dof_id_type> secondary_dofs(1, nfc->variable().nodalDofIndex());
2379 :
2380 : // Assume that if the user is overwriting the secondary Jacobian, then they are
2381 : // supplying Jacobians that do not correspond to their other physics
2382 : // (e.g. Kernels), hence we should not apply a scalingFactor that is normally
2383 : // based on the order of their other physics (e.g. Kernels)
2384 : Real scaling_factor =
2385 4184 : nfc->overwriteSecondaryJacobian() ? 1. : nfc->variable().scalingFactor();
2386 :
2387 : // Cache the jacobian block for the secondary side
2388 8368 : nfc->addJacobian(_fe_problem.assembly(0, number()),
2389 4184 : nfc->_Kee,
2390 : secondary_dofs,
2391 4184 : nfc->_connected_dof_indices,
2392 : scaling_factor);
2393 :
2394 : // Cache Ken, Kne, Knn
2395 4184 : if (nfc->addCouplingEntriesToJacobian())
2396 : {
2397 : // Make sure we use a proper scaling factor (e.g. don't use an interior scaling
2398 : // factor when we're overwriting secondary stuff)
2399 8368 : nfc->addJacobian(_fe_problem.assembly(0, number()),
2400 4184 : nfc->_Ken,
2401 : secondary_dofs,
2402 4184 : nfc->primaryVariable().dofIndicesNeighbor(),
2403 : scaling_factor);
2404 :
2405 : // Use _connected_dof_indices to get all the correct columns
2406 8368 : nfc->addJacobian(_fe_problem.assembly(0, number()),
2407 4184 : nfc->_Kne,
2408 4184 : nfc->primaryVariable().dofIndicesNeighbor(),
2409 4184 : nfc->_connected_dof_indices,
2410 4184 : nfc->primaryVariable().scalingFactor());
2411 :
2412 : // We've handled Ken and Kne, finally handle Knn
2413 4184 : _fe_problem.cacheJacobianNeighbor(0);
2414 : }
2415 :
2416 : // Do the off-diagonals next
2417 4184 : const std::vector<MooseVariableFEBase *> coupled_vars = nfc->getCoupledMooseVars();
2418 8368 : for (const auto & jvar : coupled_vars)
2419 : {
2420 : // Only compute jacobians for nonlinear variables
2421 4184 : if (jvar->kind() != Moose::VAR_SOLVER)
2422 0 : continue;
2423 :
2424 : // Only compute Jacobian entries if this coupling is being used by the
2425 : // preconditioner
2426 4256 : if (nfc->variable().number() == jvar->number() ||
2427 144 : !_fe_problem.areCoupled(
2428 72 : nfc->variable().number(), jvar->number(), this->number()))
2429 4112 : continue;
2430 :
2431 : // Need to zero out the matrices first
2432 72 : _fe_problem.prepareAssembly(0);
2433 :
2434 72 : nfc->prepareShapes(nfc->variable().number());
2435 72 : nfc->prepareNeighborShapes(jvar->number());
2436 :
2437 72 : nfc->computeOffDiagJacobian(jvar->number());
2438 :
2439 : // Cache the jacobian block for the secondary side
2440 144 : nfc->addJacobian(_fe_problem.assembly(0, number()),
2441 72 : nfc->_Kee,
2442 : secondary_dofs,
2443 72 : nfc->_connected_dof_indices,
2444 : scaling_factor);
2445 :
2446 : // Cache Ken, Kne, Knn
2447 72 : if (nfc->addCouplingEntriesToJacobian())
2448 : {
2449 : // Make sure we use a proper scaling factor (e.g. don't use an interior scaling
2450 : // factor when we're overwriting secondary stuff)
2451 144 : nfc->addJacobian(_fe_problem.assembly(0, number()),
2452 72 : nfc->_Ken,
2453 : secondary_dofs,
2454 72 : jvar->dofIndicesNeighbor(),
2455 : scaling_factor);
2456 :
2457 : // Use _connected_dof_indices to get all the correct columns
2458 144 : nfc->addJacobian(_fe_problem.assembly(0, number()),
2459 72 : nfc->_Kne,
2460 72 : nfc->variable().dofIndicesNeighbor(),
2461 72 : nfc->_connected_dof_indices,
2462 72 : nfc->variable().scalingFactor());
2463 :
2464 : // We've handled Ken and Kne, finally handle Knn
2465 72 : _fe_problem.cacheJacobianNeighbor(0);
2466 : }
2467 : }
2468 4184 : }
2469 : }
2470 : }
2471 : }
2472 : }
2473 : }
2474 2066 : if (_assemble_constraints_separately)
2475 : {
2476 : // See if constraints were applied anywhere
2477 0 : _communicator.max(constraints_applied);
2478 :
2479 0 : if (constraints_applied)
2480 : {
2481 0 : LibmeshPetscCall(MatSetOption(static_cast<PetscMatrix<Number> &>(jacobian).mat(),
2482 : MAT_KEEP_NONZERO_PATTERN, // This is changed in 3.1
2483 : PETSC_TRUE));
2484 :
2485 0 : jacobian.close();
2486 0 : jacobian.zero_rows(zero_rows, 0.0);
2487 0 : jacobian.close();
2488 0 : _fe_problem.addCachedJacobian(0);
2489 0 : jacobian.close();
2490 : }
2491 : }
2492 : }
2493 4763 : if (!_assemble_constraints_separately)
2494 : {
2495 : // See if constraints were applied anywhere
2496 4763 : _communicator.max(constraints_applied);
2497 :
2498 4763 : if (constraints_applied)
2499 : {
2500 1054 : LibmeshPetscCall(MatSetOption(static_cast<PetscMatrix<Number> &>(jacobian).mat(),
2501 : MAT_KEEP_NONZERO_PATTERN, // This is changed in 3.1
2502 : PETSC_TRUE));
2503 :
2504 1054 : jacobian.close();
2505 1054 : jacobian.zero_rows(zero_rows, 0.0);
2506 1054 : jacobian.close();
2507 1054 : _fe_problem.addCachedJacobian(0);
2508 1054 : jacobian.close();
2509 : }
2510 : }
2511 :
2512 4763 : THREAD_ID tid = 0;
2513 : // go over element-element constraint interface
2514 4763 : const auto & element_pair_locators = subproblem.geomSearchData()._element_pair_locators;
2515 4763 : for (const auto & it : element_pair_locators)
2516 : {
2517 0 : ElementPairLocator & elem_pair_loc = *(it.second);
2518 :
2519 0 : if (_constraints.hasActiveElemElemConstraints(it.first, displaced))
2520 : {
2521 : // ElemElemConstraint objects
2522 : const auto & _element_constraints =
2523 0 : _constraints.getActiveElemElemConstraints(it.first, displaced);
2524 :
2525 : // go over pair elements
2526 : const std::list<std::pair<const Elem *, const Elem *>> & elem_pairs =
2527 0 : elem_pair_loc.getElemPairs();
2528 0 : for (const auto & pr : elem_pairs)
2529 : {
2530 0 : const Elem * elem1 = pr.first;
2531 0 : const Elem * elem2 = pr.second;
2532 :
2533 0 : if (elem1->processor_id() != processor_id())
2534 0 : continue;
2535 :
2536 0 : const ElementPairInfo & info = elem_pair_loc.getElemPairInfo(pr);
2537 :
2538 : // for each element process constraints on the
2539 0 : for (const auto & ec : _element_constraints)
2540 : {
2541 0 : _fe_problem.setCurrentSubdomainID(elem1, tid);
2542 0 : subproblem.reinitElemPhys(elem1, info._elem1_constraint_q_point, tid);
2543 0 : _fe_problem.setNeighborSubdomainID(elem2, tid);
2544 0 : subproblem.reinitNeighborPhys(elem2, info._elem2_constraint_q_point, tid);
2545 :
2546 0 : ec->prepareShapes(ec->variable().number());
2547 0 : ec->prepareNeighborShapes(ec->variable().number());
2548 :
2549 0 : ec->reinit(info);
2550 0 : ec->computeJacobian();
2551 0 : _fe_problem.cacheJacobian(tid);
2552 0 : _fe_problem.cacheJacobianNeighbor(tid);
2553 : }
2554 0 : _fe_problem.addCachedJacobian(tid);
2555 : }
2556 : }
2557 : }
2558 :
2559 : // go over NodeElemConstraints
2560 4763 : std::set<dof_id_type> unique_secondary_node_ids;
2561 4763 : constraints_applied = false;
2562 20461 : for (const auto & secondary_id : _mesh.meshSubdomains())
2563 : {
2564 77282 : for (const auto & primary_id : _mesh.meshSubdomains())
2565 : {
2566 61584 : if (_constraints.hasActiveNodeElemConstraints(secondary_id, primary_id, displaced))
2567 : {
2568 : const auto & constraints =
2569 180 : _constraints.getActiveNodeElemConstraints(secondary_id, primary_id, displaced);
2570 :
2571 : // get unique set of ids of all nodes on current block
2572 180 : unique_secondary_node_ids.clear();
2573 180 : const MeshBase & meshhelper = _mesh.getMesh();
2574 360 : for (const auto & elem : as_range(meshhelper.active_subdomain_elements_begin(secondary_id),
2575 8940 : meshhelper.active_subdomain_elements_end(secondary_id)))
2576 : {
2577 49140 : for (auto & n : elem->node_ref_range())
2578 40740 : unique_secondary_node_ids.insert(n.id());
2579 180 : }
2580 :
2581 14040 : for (auto secondary_node_id : unique_secondary_node_ids)
2582 : {
2583 13860 : const Node & secondary_node = _mesh.nodeRef(secondary_node_id);
2584 : // check if secondary node is on current processor
2585 13860 : if (secondary_node.processor_id() == processor_id())
2586 : {
2587 : // This reinits the variables that exist on the secondary node
2588 11088 : _fe_problem.reinitNodeFace(&secondary_node, secondary_id, 0);
2589 :
2590 : // This will set aside residual and jacobian space for the variables that have dofs
2591 : // on the secondary node
2592 11088 : _fe_problem.prepareAssembly(0);
2593 11088 : _fe_problem.reinitOffDiagScalars(0);
2594 :
2595 23584 : for (const auto & nec : constraints)
2596 : {
2597 12496 : if (nec->shouldApply())
2598 : {
2599 5680 : constraints_applied = true;
2600 :
2601 5680 : nec->_jacobian = &jacobian_to_view;
2602 5680 : nec->prepareShapes(nec->variable().number());
2603 5680 : nec->prepareNeighborShapes(nec->variable().number());
2604 :
2605 5680 : nec->computeJacobian();
2606 :
2607 5680 : if (nec->overwriteSecondaryJacobian())
2608 : {
2609 : // Add this variable's dof's row to be zeroed
2610 0 : zero_rows.push_back(nec->variable().nodalDofIndex());
2611 : }
2612 :
2613 5680 : std::vector<dof_id_type> secondary_dofs(1, nec->variable().nodalDofIndex());
2614 :
2615 : // Cache the jacobian block for the secondary side
2616 11360 : nec->addJacobian(_fe_problem.assembly(0, number()),
2617 5680 : nec->_Kee,
2618 : secondary_dofs,
2619 5680 : nec->_connected_dof_indices,
2620 5680 : nec->variable().scalingFactor());
2621 :
2622 : // Cache the jacobian block for the primary side
2623 11360 : nec->addJacobian(_fe_problem.assembly(0, number()),
2624 5680 : nec->_Kne,
2625 5680 : nec->primaryVariable().dofIndicesNeighbor(),
2626 5680 : nec->_connected_dof_indices,
2627 5680 : nec->primaryVariable().scalingFactor());
2628 :
2629 5680 : _fe_problem.cacheJacobian(0);
2630 5680 : _fe_problem.cacheJacobianNeighbor(0);
2631 :
2632 : // Do the off-diagonals next
2633 5680 : const std::vector<MooseVariableFEBase *> coupled_vars = nec->getCoupledMooseVars();
2634 11440 : for (const auto & jvar : coupled_vars)
2635 : {
2636 : // Only compute jacobians for nonlinear variables
2637 5760 : if (jvar->kind() != Moose::VAR_SOLVER)
2638 80 : continue;
2639 :
2640 : // Only compute Jacobian entries if this coupling is being used by the
2641 : // preconditioner
2642 6320 : if (nec->variable().number() == jvar->number() ||
2643 1280 : !_fe_problem.areCoupled(
2644 640 : nec->variable().number(), jvar->number(), this->number()))
2645 5040 : continue;
2646 :
2647 : // Need to zero out the matrices first
2648 640 : _fe_problem.prepareAssembly(0);
2649 :
2650 640 : nec->prepareShapes(nec->variable().number());
2651 640 : nec->prepareNeighborShapes(jvar->number());
2652 :
2653 640 : nec->computeOffDiagJacobian(jvar->number());
2654 :
2655 : // Cache the jacobian block for the secondary side
2656 1280 : nec->addJacobian(_fe_problem.assembly(0, number()),
2657 640 : nec->_Kee,
2658 : secondary_dofs,
2659 640 : nec->_connected_dof_indices,
2660 640 : nec->variable().scalingFactor());
2661 :
2662 : // Cache the jacobian block for the primary side
2663 1280 : nec->addJacobian(_fe_problem.assembly(0, number()),
2664 640 : nec->_Kne,
2665 640 : nec->variable().dofIndicesNeighbor(),
2666 640 : nec->_connected_dof_indices,
2667 640 : nec->variable().scalingFactor());
2668 :
2669 640 : _fe_problem.cacheJacobian(0);
2670 640 : _fe_problem.cacheJacobianNeighbor(0);
2671 : }
2672 5680 : }
2673 : }
2674 : }
2675 : }
2676 : }
2677 : }
2678 : }
2679 : // See if constraints were applied anywhere
2680 4763 : _communicator.max(constraints_applied);
2681 :
2682 4763 : if (constraints_applied)
2683 : {
2684 180 : LibmeshPetscCall(MatSetOption(static_cast<PetscMatrix<Number> &>(jacobian).mat(),
2685 : MAT_KEEP_NONZERO_PATTERN, // This is changed in 3.1
2686 : PETSC_TRUE));
2687 :
2688 180 : jacobian.close();
2689 180 : jacobian.zero_rows(zero_rows, 0.0);
2690 180 : jacobian.close();
2691 180 : _fe_problem.addCachedJacobian(0);
2692 180 : jacobian.close();
2693 : }
2694 4763 : }
2695 :
2696 : void
2697 519689 : NonlinearSystemBase::computeScalarKernelsJacobians(const std::set<TagID> & tags)
2698 : {
2699 : MooseObjectWarehouse<ScalarKernelBase> * scalar_kernel_warehouse;
2700 :
2701 519689 : if (!tags.size() || tags.size() == _fe_problem.numMatrixTags())
2702 510118 : scalar_kernel_warehouse = &_scalar_kernels;
2703 9571 : else if (tags.size() == 1)
2704 8319 : scalar_kernel_warehouse = &(_scalar_kernels.getMatrixTagObjectWarehouse(*(tags.begin()), 0));
2705 : else
2706 1252 : scalar_kernel_warehouse = &(_scalar_kernels.getMatrixTagsObjectWarehouse(tags, 0));
2707 :
2708 : // Compute the diagonal block for scalar variables
2709 519689 : if (scalar_kernel_warehouse->hasActiveObjects())
2710 : {
2711 17554 : const auto & scalars = scalar_kernel_warehouse->getActiveObjects();
2712 :
2713 17554 : _fe_problem.reinitScalars(/*tid=*/0);
2714 :
2715 17554 : _fe_problem.reinitOffDiagScalars(/*_tid*/ 0);
2716 :
2717 17554 : bool have_scalar_contributions = false;
2718 62904 : for (const auto & kernel : scalars)
2719 : {
2720 45350 : if (!kernel->computesJacobian())
2721 0 : continue;
2722 :
2723 45350 : kernel->reinit();
2724 45350 : const std::vector<dof_id_type> & dof_indices = kernel->variable().dofIndices();
2725 45350 : const DofMap & dof_map = kernel->variable().dofMap();
2726 45350 : const dof_id_type first_dof = dof_map.first_dof();
2727 45350 : const dof_id_type end_dof = dof_map.end_dof();
2728 51509 : for (dof_id_type dof : dof_indices)
2729 : {
2730 45392 : if (dof >= first_dof && dof < end_dof)
2731 : {
2732 39233 : kernel->computeJacobian();
2733 39233 : _fe_problem.addJacobianOffDiagScalar(kernel->variable().number());
2734 39233 : have_scalar_contributions = true;
2735 39233 : break;
2736 : }
2737 : }
2738 : }
2739 :
2740 17554 : if (have_scalar_contributions)
2741 15622 : _fe_problem.addJacobianScalar();
2742 : }
2743 519689 : }
2744 :
2745 : void
2746 535749 : NonlinearSystemBase::jacobianSetup()
2747 : {
2748 535749 : SolverSystem::jacobianSetup();
2749 :
2750 1120944 : for (THREAD_ID tid = 0; tid < libMesh::n_threads(); tid++)
2751 : {
2752 585195 : _kernels.jacobianSetup(tid);
2753 585195 : _nodal_kernels.jacobianSetup(tid);
2754 585195 : _dirac_kernels.jacobianSetup(tid);
2755 585195 : if (_doing_dg)
2756 2948 : _dg_kernels.jacobianSetup(tid);
2757 585195 : _interface_kernels.jacobianSetup(tid);
2758 585195 : _element_dampers.jacobianSetup(tid);
2759 585195 : _nodal_dampers.jacobianSetup(tid);
2760 585195 : _integrated_bcs.jacobianSetup(tid);
2761 : }
2762 535749 : _scalar_kernels.jacobianSetup();
2763 535749 : _constraints.jacobianSetup();
2764 535749 : _general_dampers.jacobianSetup();
2765 535749 : _nodal_bcs.jacobianSetup();
2766 :
2767 : // Avoid recursion
2768 535749 : if (this == &_fe_problem.currentNonlinearSystem())
2769 519689 : _fe_problem.jacobianSetup();
2770 535749 : _app.solutionInvalidity().resetSolutionInvalidCurrentIteration();
2771 535749 : }
2772 :
2773 : void
2774 519689 : NonlinearSystemBase::computeJacobianInternal(const std::set<TagID> & tags)
2775 : {
2776 1559067 : TIME_SECTION("computeJacobianInternal", 3);
2777 :
2778 519689 : _fe_problem.setCurrentNonlinearSystem(number());
2779 :
2780 : // Make matrix ready to use
2781 519689 : activateAllMatrixTags();
2782 :
2783 1551240 : for (auto tag : tags)
2784 : {
2785 1031551 : if (!hasMatrix(tag))
2786 510118 : continue;
2787 :
2788 521433 : auto & jacobian = getMatrix(tag);
2789 : // Necessary for speed
2790 521433 : if (auto petsc_matrix = dynamic_cast<PetscMatrix<Number> *>(&jacobian))
2791 : {
2792 520147 : LibmeshPetscCall(MatSetOption(petsc_matrix->mat(),
2793 : MAT_KEEP_NONZERO_PATTERN, // This is changed in 3.1
2794 : PETSC_TRUE));
2795 520147 : if (!_fe_problem.errorOnJacobianNonzeroReallocation())
2796 13371 : LibmeshPetscCall(
2797 : MatSetOption(petsc_matrix->mat(), MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE));
2798 520147 : if (_fe_problem.ignoreZerosInJacobian())
2799 0 : LibmeshPetscCall(MatSetOption(static_cast<PetscMatrix<Number> &>(jacobian).mat(),
2800 : MAT_IGNORE_ZERO_ENTRIES,
2801 : PETSC_TRUE));
2802 : }
2803 : }
2804 :
2805 519689 : jacobianSetup();
2806 :
2807 : // Jacobian contributions from UOs - for now this is used for ray tracing
2808 : // and ray kernels that contribute to the Jacobian (think line sources)
2809 519689 : std::vector<UserObject *> uos;
2810 519689 : _fe_problem.theWarehouse()
2811 1039378 : .query()
2812 519689 : .condition<AttribSystem>("UserObject")
2813 519689 : .condition<AttribExecOns>(EXEC_PRE_KERNELS)
2814 519689 : .queryInto(uos);
2815 519689 : for (auto & uo : uos)
2816 0 : uo->jacobianSetup();
2817 519689 : for (auto & uo : uos)
2818 : {
2819 0 : uo->initialize();
2820 0 : uo->execute();
2821 0 : uo->finalize();
2822 : }
2823 :
2824 : // reinit scalar variables
2825 1087451 : for (unsigned int tid = 0; tid < libMesh::n_threads(); tid++)
2826 567762 : _fe_problem.reinitScalars(tid);
2827 :
2828 : PARALLEL_TRY
2829 : {
2830 : // We would like to compute ScalarKernels, block NodalKernels, FVFluxKernels, and mortar objects
2831 : // up front because we want these included whether we are computing an ordinary Jacobian or a
2832 : // Jacobian for determining variable scaling factors
2833 519689 : computeScalarKernelsJacobians(tags);
2834 :
2835 : // Block restricted Nodal Kernels
2836 519689 : if (_nodal_kernels.hasActiveBlockObjects())
2837 : {
2838 3998 : ComputeNodalKernelJacobiansThread cnkjt(_fe_problem, *this, _nodal_kernels, tags);
2839 3998 : const ConstNodeRange & range = _fe_problem.getCurrentAlgebraicNodeRange();
2840 3998 : Threads::parallel_reduce(range, cnkjt);
2841 :
2842 3998 : unsigned int n_threads = libMesh::n_threads();
2843 9262 : for (unsigned int i = 0; i < n_threads;
2844 : i++) // Add any cached jacobians that might be hanging around
2845 5264 : _fe_problem.assembly(i, number()).addCachedJacobian(Assembly::GlobalDataKey{});
2846 3998 : }
2847 :
2848 : using FVRange = StoredRange<MooseMesh::const_face_info_iterator, const FaceInfo *>;
2849 519689 : if (_fe_problem.haveFV())
2850 : {
2851 : // the same loop works for both residual and jacobians because it keys
2852 : // off of FEProblem's _currently_computing_jacobian parameter
2853 : ComputeFVFluxJacobianThread<FVRange> fvj(
2854 37586 : _fe_problem, this->number(), tags, /*on_displaced=*/false);
2855 37586 : FVRange faces(_fe_problem.mesh().ownedFaceInfoBegin(), _fe_problem.mesh().ownedFaceInfoEnd());
2856 37586 : Threads::parallel_reduce(faces, fvj);
2857 37586 : }
2858 519689 : if (auto displaced_problem = _fe_problem.getDisplacedProblem();
2859 519689 : displaced_problem && displaced_problem->haveFV())
2860 : {
2861 : ComputeFVFluxJacobianThread<FVRange> fvr(
2862 0 : _fe_problem, this->number(), tags, /*on_displaced=*/true);
2863 0 : FVRange faces(displaced_problem->mesh().ownedFaceInfoBegin(),
2864 0 : displaced_problem->mesh().ownedFaceInfoEnd());
2865 0 : Threads::parallel_reduce(faces, fvr);
2866 519689 : }
2867 :
2868 519689 : mortarConstraints(Moose::ComputeType::Jacobian, {}, tags);
2869 :
2870 : // Get our element range for looping over
2871 519689 : const ConstElemRange & elem_range = _fe_problem.getCurrentAlgebraicElementRange();
2872 :
2873 519689 : if (_fe_problem.computingScalingJacobian())
2874 : {
2875 : // Only compute Jacobians corresponding to the diagonals of volumetric compute objects
2876 : // because this typically gives us a good representation of the physics. NodalBCs and
2877 : // Constraints can introduce dramatically different scales (often order unity).
2878 : // IntegratedBCs and/or InterfaceKernels may use penalty factors. DGKernels may be ok, but
2879 : // they are almost always used in conjunction with Kernels
2880 602 : ComputeJacobianForScalingThread cj(_fe_problem, tags);
2881 602 : Threads::parallel_reduce(elem_range, cj);
2882 602 : unsigned int n_threads = libMesh::n_threads();
2883 1263 : for (unsigned int i = 0; i < n_threads;
2884 : i++) // Add any Jacobian contributions still hanging around
2885 661 : _fe_problem.addCachedJacobian(i);
2886 :
2887 : // Check whether any exceptions were thrown and propagate this information for parallel
2888 : // consistency before
2889 : // 1) we do parallel communication when closing tagged matrices
2890 : // 2) early returning before reaching our PARALLEL_CATCH below
2891 602 : _fe_problem.checkExceptionAndStopSolve();
2892 :
2893 602 : closeTaggedMatrices(tags);
2894 :
2895 602 : return;
2896 602 : }
2897 :
2898 519087 : switch (_fe_problem.coupling())
2899 : {
2900 430269 : case Moose::COUPLING_DIAG:
2901 : {
2902 430269 : ComputeJacobianThread cj(_fe_problem, tags);
2903 430269 : Threads::parallel_reduce(elem_range, cj);
2904 :
2905 430261 : unsigned int n_threads = libMesh::n_threads();
2906 902236 : for (unsigned int i = 0; i < n_threads;
2907 : i++) // Add any Jacobian contributions still hanging around
2908 471975 : _fe_problem.addCachedJacobian(i);
2909 :
2910 : // Boundary restricted Nodal Kernels
2911 430261 : if (_nodal_kernels.hasActiveBoundaryObjects())
2912 : {
2913 46 : ComputeNodalKernelBCJacobiansThread cnkjt(_fe_problem, *this, _nodal_kernels, tags);
2914 46 : const ConstBndNodeRange & bnd_range = _fe_problem.getCurrentAlgebraicBndNodeRange();
2915 :
2916 46 : Threads::parallel_reduce(bnd_range, cnkjt);
2917 46 : unsigned int n_threads = libMesh::n_threads();
2918 96 : for (unsigned int i = 0; i < n_threads;
2919 : i++) // Add any cached jacobians that might be hanging around
2920 50 : _fe_problem.assembly(i, number()).addCachedJacobian(Assembly::GlobalDataKey{});
2921 46 : }
2922 430261 : }
2923 430261 : break;
2924 :
2925 88818 : default:
2926 : case Moose::COUPLING_CUSTOM:
2927 : {
2928 88818 : ComputeFullJacobianThread cj(_fe_problem, tags);
2929 88818 : Threads::parallel_reduce(elem_range, cj);
2930 88818 : unsigned int n_threads = libMesh::n_threads();
2931 :
2932 183931 : for (unsigned int i = 0; i < n_threads; i++)
2933 95117 : _fe_problem.addCachedJacobian(i);
2934 :
2935 : // Boundary restricted Nodal Kernels
2936 88814 : if (_nodal_kernels.hasActiveBoundaryObjects())
2937 : {
2938 10 : ComputeNodalKernelBCJacobiansThread cnkjt(_fe_problem, *this, _nodal_kernels, tags);
2939 10 : const ConstBndNodeRange & bnd_range = _fe_problem.getCurrentAlgebraicBndNodeRange();
2940 :
2941 10 : Threads::parallel_reduce(bnd_range, cnkjt);
2942 10 : unsigned int n_threads = libMesh::n_threads();
2943 21 : for (unsigned int i = 0; i < n_threads;
2944 : i++) // Add any cached jacobians that might be hanging around
2945 11 : _fe_problem.assembly(i, number()).addCachedJacobian(Assembly::GlobalDataKey{});
2946 10 : }
2947 88818 : }
2948 88814 : break;
2949 : }
2950 :
2951 519075 : computeDiracContributions(tags, true);
2952 :
2953 : static bool first = true;
2954 :
2955 : // This adds zeroes into geometric coupling entries to ensure they stay in the matrix
2956 1037526 : if ((_fe_problem.restoreOriginalNonzeroPattern() || first) &&
2957 518455 : _add_implicit_geometric_coupling_entries_to_jacobian)
2958 : {
2959 3051 : first = false;
2960 3051 : addImplicitGeometricCouplingEntries(_fe_problem.geomSearchData());
2961 :
2962 3051 : if (_fe_problem.getDisplacedProblem())
2963 1084 : addImplicitGeometricCouplingEntries(_fe_problem.getDisplacedProblem()->geomSearchData());
2964 : }
2965 : }
2966 519071 : PARALLEL_CATCH;
2967 :
2968 : // Have no idea how to have constraints work
2969 : // with the tag system
2970 : PARALLEL_TRY
2971 : {
2972 : // Add in Jacobian contributions from other Constraints
2973 518969 : if (_fe_problem._has_constraints && tags.count(systemMatrixTag()))
2974 : {
2975 : // Some constraints need to be able to read values from the Jacobian, which requires that it
2976 : // be closed/assembled
2977 3667 : auto & system_matrix = getMatrix(systemMatrixTag());
2978 : #if PETSC_RELEASE_GREATER_EQUALS(3, 23, 0)
2979 : SparseMatrix<Number> * view_jac_ptr;
2980 3667 : std::unique_ptr<SparseMatrix<Number>> hash_copy;
2981 3667 : if (system_matrix.use_hash_table())
2982 : {
2983 2309 : hash_copy = libMesh::cast_ref<PetscMatrix<Number> &>(system_matrix).copy_from_hash();
2984 2309 : view_jac_ptr = hash_copy.get();
2985 : }
2986 : else
2987 1358 : view_jac_ptr = &system_matrix;
2988 3667 : auto & jacobian_to_view = *view_jac_ptr;
2989 : #else
2990 : auto & jacobian_to_view = system_matrix;
2991 : #endif
2992 3667 : if (&jacobian_to_view == &system_matrix)
2993 1358 : system_matrix.close();
2994 :
2995 : // Nodal Constraints
2996 3667 : enforceNodalConstraintsJacobian();
2997 :
2998 : // Undisplaced Constraints
2999 3667 : constraintJacobians(jacobian_to_view, false);
3000 :
3001 : // Displaced Constraints
3002 3667 : if (_fe_problem.getDisplacedProblem())
3003 1096 : constraintJacobians(jacobian_to_view, true);
3004 3667 : }
3005 : }
3006 518969 : PARALLEL_CATCH;
3007 :
3008 : // We need to close the save_in variables on the aux system before NodalBCBases clear the dofs
3009 : // on boundary nodes
3010 518969 : if (_has_diag_save_in)
3011 185 : _fe_problem.getAuxiliarySystem().solution().close();
3012 :
3013 : PARALLEL_TRY
3014 : {
3015 : MooseObjectWarehouse<NodalBCBase> * nbc_warehouse;
3016 : // Select nodal kernels
3017 518969 : if (tags.size() == _fe_problem.numMatrixTags() || !tags.size())
3018 509408 : nbc_warehouse = &_nodal_bcs;
3019 9561 : else if (tags.size() == 1)
3020 8309 : nbc_warehouse = &(_nodal_bcs.getMatrixTagObjectWarehouse(*(tags.begin()), 0));
3021 : else
3022 1252 : nbc_warehouse = &(_nodal_bcs.getMatrixTagsObjectWarehouse(tags, 0));
3023 :
3024 518969 : if (nbc_warehouse->hasActiveObjects())
3025 : {
3026 : // We may be switching from add to set. Moreover, we rely on a call to MatZeroRows to enforce
3027 : // the nodal boundary condition constraints which requires that the matrix be truly assembled
3028 : // as opposed to just flushed. Consequently we can't do the following despite any desire to
3029 : // keep our initial sparsity pattern honored (see https://gitlab.com/petsc/petsc/-/issues/852)
3030 : //
3031 : // flushTaggedMatrices(tags);
3032 427014 : closeTaggedMatrices(tags);
3033 :
3034 : // Cache the information about which BCs are coupled to which
3035 : // variables, so we don't have to figure it out for each node.
3036 427014 : std::map<std::string, std::set<unsigned int>> bc_involved_vars;
3037 427014 : const std::set<BoundaryID> & all_boundary_ids = _mesh.getBoundaryIDs();
3038 2147328 : for (const auto & bid : all_boundary_ids)
3039 : {
3040 : // Get reference to all the NodalBCs for this ID. This is only
3041 : // safe if there are NodalBCBases there to be gotten...
3042 1720314 : if (nbc_warehouse->hasActiveBoundaryObjects(bid))
3043 : {
3044 844916 : const auto & bcs = nbc_warehouse->getActiveBoundaryObjects(bid);
3045 1774540 : for (const auto & bc : bcs)
3046 : {
3047 : const std::vector<MooseVariableFEBase *> & coupled_moose_vars =
3048 929624 : bc->getCoupledMooseVars();
3049 :
3050 : // Create the set of "involved" MOOSE nonlinear vars, which includes all coupled vars
3051 : // and the BC's own variable
3052 929624 : std::set<unsigned int> & var_set = bc_involved_vars[bc->name()];
3053 987896 : for (const auto & coupled_var : coupled_moose_vars)
3054 58272 : if (coupled_var->kind() == Moose::VAR_SOLVER)
3055 56694 : var_set.insert(coupled_var->number());
3056 :
3057 929624 : var_set.insert(bc->variable().number());
3058 : }
3059 : }
3060 : }
3061 :
3062 : // reinit scalar variables again. This reinit does not re-fill any of the scalar variable
3063 : // solution arrays because that was done above. It only will reorder the derivative
3064 : // information for AD calculations to be suitable for NodalBC calculations
3065 892338 : for (unsigned int tid = 0; tid < libMesh::n_threads(); tid++)
3066 465324 : _fe_problem.reinitScalars(tid, true);
3067 :
3068 : // Get variable coupling list. We do all the NodalBCBase stuff on
3069 : // thread 0... The couplingEntries() data structure determines
3070 : // which variables are "coupled" as far as the preconditioner is
3071 : // concerned, not what variables a boundary condition specifically
3072 : // depends on.
3073 427014 : auto & coupling_entries = _fe_problem.couplingEntries(/*_tid=*/0, this->number());
3074 :
3075 : // Compute Jacobians for NodalBCBases
3076 427014 : const ConstBndNodeRange & bnd_nodes = _fe_problem.getCurrentAlgebraicBndNodeRange();
3077 22372888 : for (const auto & bnode : bnd_nodes)
3078 : {
3079 21945874 : BoundaryID boundary_id = bnode->_bnd_id;
3080 21945874 : Node * node = bnode->_node;
3081 :
3082 32188157 : if (nbc_warehouse->hasActiveBoundaryObjects(boundary_id) &&
3083 10242283 : node->processor_id() == processor_id())
3084 : {
3085 7987793 : _fe_problem.reinitNodeFace(node, boundary_id, 0);
3086 :
3087 7987793 : const auto & bcs = nbc_warehouse->getActiveBoundaryObjects(boundary_id);
3088 16991327 : for (const auto & bc : bcs)
3089 : {
3090 : // Get the set of involved MOOSE vars for this BC
3091 9003534 : std::set<unsigned int> & var_set = bc_involved_vars[bc->name()];
3092 :
3093 : // Loop over all the variables whose Jacobian blocks are
3094 : // actually being computed, call computeOffDiagJacobian()
3095 : // for each one which is actually coupled (otherwise the
3096 : // value is zero.)
3097 22632240 : for (const auto & it : coupling_entries)
3098 : {
3099 13628706 : unsigned int ivar = it.first->number(), jvar = it.second->number();
3100 :
3101 : // We are only going to call computeOffDiagJacobian() if:
3102 : // 1.) the BC's variable is ivar
3103 : // 2.) jvar is "involved" with the BC (including jvar==ivar), and
3104 : // 3.) the BC should apply.
3105 13628706 : if ((bc->variable().number() == ivar) && var_set.count(jvar) && bc->shouldApply())
3106 9092918 : bc->computeOffDiagJacobian(jvar);
3107 : }
3108 :
3109 9003534 : const auto & coupled_scalar_vars = bc->getCoupledMooseScalarVars();
3110 9003905 : for (const auto & jvariable : coupled_scalar_vars)
3111 371 : if (hasScalarVariable(jvariable->name()))
3112 371 : bc->computeOffDiagJacobianScalar(jvariable->number());
3113 : }
3114 : }
3115 : } // end loop over boundary nodes
3116 :
3117 : // Set the cached NodalBCBase values in the Jacobian matrix
3118 427014 : _fe_problem.assembly(0, number()).setCachedJacobian(Assembly::GlobalDataKey{});
3119 427014 : }
3120 : }
3121 518969 : PARALLEL_CATCH;
3122 :
3123 518969 : closeTaggedMatrices(tags);
3124 :
3125 : // We need to close the save_in variables on the aux system before NodalBCBases clear the dofs
3126 : // on boundary nodes
3127 518969 : if (_has_nodalbc_diag_save_in)
3128 23 : _fe_problem.getAuxiliarySystem().solution().close();
3129 :
3130 518969 : if (hasDiagSaveIn())
3131 185 : _fe_problem.getAuxiliarySystem().update();
3132 :
3133 : // Accumulate the occurrence of solution invalid warnings for the current iteration cumulative
3134 : // counters
3135 518969 : _app.solutionInvalidity().syncIteration();
3136 518969 : _app.solutionInvalidity().solutionInvalidAccumulation();
3137 520385 : }
3138 :
3139 : void
3140 0 : NonlinearSystemBase::computeJacobian(SparseMatrix<Number> & jacobian)
3141 : {
3142 0 : _nl_matrix_tags.clear();
3143 :
3144 0 : auto & tags = _fe_problem.getMatrixTags();
3145 :
3146 0 : for (auto & tag : tags)
3147 0 : _nl_matrix_tags.insert(tag.second);
3148 :
3149 0 : computeJacobian(jacobian, _nl_matrix_tags);
3150 0 : }
3151 :
3152 : void
3153 0 : NonlinearSystemBase::computeJacobian(SparseMatrix<Number> & jacobian, const std::set<TagID> & tags)
3154 : {
3155 0 : associateMatrixToTag(jacobian, systemMatrixTag());
3156 :
3157 0 : computeJacobianTags(tags);
3158 :
3159 0 : disassociateMatrixFromTag(jacobian, systemMatrixTag());
3160 0 : }
3161 :
3162 : void
3163 519689 : NonlinearSystemBase::computeJacobianTags(const std::set<TagID> & tags)
3164 : {
3165 1559067 : TIME_SECTION("computeJacobianTags", 5);
3166 :
3167 519689 : FloatingPointExceptionGuard fpe_guard(_app);
3168 :
3169 : try
3170 : {
3171 519689 : computeJacobianInternal(tags);
3172 : }
3173 106 : catch (MooseException & e)
3174 : {
3175 : // The buck stops here, we have already handled the exception by
3176 : // calling stopSolve(), it is now up to PETSc to return a
3177 : // "diverged" reason during the next solve.
3178 102 : }
3179 519681 : }
3180 :
3181 : void
3182 228 : NonlinearSystemBase::computeJacobianBlocks(std::vector<JacobianBlock *> & blocks)
3183 : {
3184 228 : _nl_matrix_tags.clear();
3185 :
3186 228 : auto & tags = _fe_problem.getMatrixTags();
3187 684 : for (auto & tag : tags)
3188 456 : _nl_matrix_tags.insert(tag.second);
3189 :
3190 228 : computeJacobianBlocks(blocks, _nl_matrix_tags);
3191 228 : }
3192 :
3193 : void
3194 588 : NonlinearSystemBase::computeJacobianBlocks(std::vector<JacobianBlock *> & blocks,
3195 : const std::set<TagID> & tags)
3196 : {
3197 1764 : TIME_SECTION("computeJacobianBlocks", 3);
3198 588 : FloatingPointExceptionGuard fpe_guard(_app);
3199 :
3200 2100 : for (unsigned int i = 0; i < blocks.size(); i++)
3201 : {
3202 1512 : SparseMatrix<Number> & jacobian = blocks[i]->_jacobian;
3203 :
3204 1512 : LibmeshPetscCall(MatSetOption(static_cast<PetscMatrix<Number> &>(jacobian).mat(),
3205 : MAT_KEEP_NONZERO_PATTERN, // This is changed in 3.1
3206 : PETSC_TRUE));
3207 1512 : if (!_fe_problem.errorOnJacobianNonzeroReallocation())
3208 0 : LibmeshPetscCall(MatSetOption(static_cast<PetscMatrix<Number> &>(jacobian).mat(),
3209 : MAT_NEW_NONZERO_ALLOCATION_ERR,
3210 : PETSC_TRUE));
3211 :
3212 1512 : jacobian.zero();
3213 : }
3214 :
3215 1233 : for (unsigned int tid = 0; tid < libMesh::n_threads(); tid++)
3216 645 : _fe_problem.reinitScalars(tid);
3217 :
3218 : PARALLEL_TRY
3219 : {
3220 588 : const ConstElemRange & elem_range = _fe_problem.getCurrentAlgebraicElementRange();
3221 588 : ComputeJacobianBlocksThread cjb(_fe_problem, blocks, tags);
3222 588 : Threads::parallel_reduce(elem_range, cjb);
3223 588 : }
3224 588 : PARALLEL_CATCH;
3225 :
3226 2100 : for (unsigned int i = 0; i < blocks.size(); i++)
3227 1512 : blocks[i]->_jacobian.close();
3228 :
3229 2100 : for (unsigned int i = 0; i < blocks.size(); i++)
3230 : {
3231 1512 : libMesh::System & precond_system = blocks[i]->_precond_system;
3232 1512 : SparseMatrix<Number> & jacobian = blocks[i]->_jacobian;
3233 :
3234 1512 : unsigned int ivar = blocks[i]->_ivar;
3235 1512 : unsigned int jvar = blocks[i]->_jvar;
3236 :
3237 : // Dirichlet BCs
3238 1512 : std::vector<numeric_index_type> zero_rows;
3239 : PARALLEL_TRY
3240 : {
3241 1512 : const ConstBndNodeRange & bnd_nodes = _fe_problem.getCurrentAlgebraicBndNodeRange();
3242 49712 : for (const auto & bnode : bnd_nodes)
3243 : {
3244 48200 : BoundaryID boundary_id = bnode->_bnd_id;
3245 48200 : Node * node = bnode->_node;
3246 :
3247 48200 : if (_nodal_bcs.hasActiveBoundaryObjects(boundary_id))
3248 : {
3249 41544 : const auto & bcs = _nodal_bcs.getActiveBoundaryObjects(boundary_id);
3250 :
3251 41544 : if (node->processor_id() == processor_id())
3252 : {
3253 32468 : _fe_problem.reinitNodeFace(node, boundary_id, 0);
3254 :
3255 160190 : for (const auto & bc : bcs)
3256 127722 : if (bc->variable().number() == ivar && bc->shouldApply())
3257 : {
3258 : // The first zero is for the variable number... there is only one variable in
3259 : // each mini-system The second zero only works with Lagrange elements!
3260 47568 : zero_rows.push_back(node->dof_number(precond_system.number(), 0, 0));
3261 : }
3262 : }
3263 : }
3264 : }
3265 : }
3266 1512 : PARALLEL_CATCH;
3267 :
3268 1512 : jacobian.close();
3269 :
3270 : // This zeroes the rows corresponding to Dirichlet BCs and puts a 1.0 on the diagonal
3271 1512 : if (ivar == jvar)
3272 1396 : jacobian.zero_rows(zero_rows, 1.0);
3273 : else
3274 116 : jacobian.zero_rows(zero_rows, 0.0);
3275 :
3276 1512 : jacobian.close();
3277 1512 : }
3278 588 : }
3279 :
3280 : void
3281 365967 : NonlinearSystemBase::updateActive(THREAD_ID tid)
3282 : {
3283 365967 : _element_dampers.updateActive(tid);
3284 365967 : _nodal_dampers.updateActive(tid);
3285 365967 : _integrated_bcs.updateActive(tid);
3286 365967 : _dg_kernels.updateActive(tid);
3287 365967 : _interface_kernels.updateActive(tid);
3288 365967 : _dirac_kernels.updateActive(tid);
3289 365967 : _kernels.updateActive(tid);
3290 365967 : _nodal_kernels.updateActive(tid);
3291 365967 : if (tid == 0)
3292 : {
3293 336220 : _general_dampers.updateActive();
3294 336220 : _nodal_bcs.updateActive();
3295 336220 : _preset_nodal_bcs.updateActive();
3296 336220 : _ad_preset_nodal_bcs.updateActive();
3297 336220 : _constraints.updateActive();
3298 336220 : _scalar_kernels.updateActive();
3299 : }
3300 365967 : }
3301 :
3302 : Real
3303 1420 : NonlinearSystemBase::computeDamping(const NumericVector<Number> & solution,
3304 : const NumericVector<Number> & update)
3305 : {
3306 : // Default to no damping
3307 1420 : Real damping = 1.0;
3308 1420 : bool has_active_dampers = false;
3309 :
3310 : try
3311 : {
3312 1420 : if (_element_dampers.hasActiveObjects())
3313 : {
3314 : PARALLEL_TRY
3315 : {
3316 2450 : TIME_SECTION("computeDampers", 3, "Computing Dampers");
3317 490 : has_active_dampers = true;
3318 490 : *_increment_vec = update;
3319 490 : ComputeElemDampingThread cid(_fe_problem, *this);
3320 490 : Threads::parallel_reduce(_fe_problem.getCurrentAlgebraicElementRange(), cid);
3321 490 : damping = std::min(cid.damping(), damping);
3322 490 : }
3323 490 : PARALLEL_CATCH;
3324 : }
3325 :
3326 1370 : if (_nodal_dampers.hasActiveObjects())
3327 : {
3328 : PARALLEL_TRY
3329 : {
3330 2620 : TIME_SECTION("computeDamping::element", 3, "Computing Element Damping");
3331 :
3332 524 : has_active_dampers = true;
3333 524 : *_increment_vec = update;
3334 524 : ComputeNodalDampingThread cndt(_fe_problem, *this);
3335 524 : Threads::parallel_reduce(_fe_problem.getCurrentAlgebraicNodeRange(), cndt);
3336 524 : damping = std::min(cndt.damping(), damping);
3337 524 : }
3338 524 : PARALLEL_CATCH;
3339 : }
3340 :
3341 1314 : if (_general_dampers.hasActiveObjects())
3342 : {
3343 : PARALLEL_TRY
3344 : {
3345 2020 : TIME_SECTION("computeDamping::general", 3, "Computing General Damping");
3346 :
3347 404 : has_active_dampers = true;
3348 404 : const auto & gdampers = _general_dampers.getActiveObjects();
3349 808 : for (const auto & damper : gdampers)
3350 : {
3351 404 : Real gd_damping = damper->computeDamping(solution, update);
3352 : try
3353 : {
3354 404 : damper->checkMinDamping(gd_damping);
3355 : }
3356 8 : catch (MooseException & e)
3357 : {
3358 16 : _fe_problem.setException(e.what());
3359 8 : }
3360 404 : damping = std::min(gd_damping, damping);
3361 : }
3362 404 : }
3363 404 : PARALLEL_CATCH;
3364 : }
3365 : }
3366 114 : catch (MooseException & e)
3367 : {
3368 : // The buck stops here, we have already handled the exception by
3369 : // calling stopSolve(), it is now up to PETSc to return a
3370 : // "diverged" reason during the next solve.
3371 114 : }
3372 :
3373 1420 : _communicator.min(damping);
3374 :
3375 1420 : if (has_active_dampers && damping < 1.0)
3376 995 : _console << " Damping factor: " << damping << std::endl;
3377 :
3378 1420 : return damping;
3379 : }
3380 :
3381 : void
3382 3812352 : NonlinearSystemBase::computeDiracContributions(const std::set<TagID> & tags, bool is_jacobian)
3383 : {
3384 3812352 : _fe_problem.clearDiracInfo();
3385 :
3386 3812352 : std::set<const Elem *> dirac_elements;
3387 :
3388 3812352 : if (_dirac_kernels.hasActiveObjects())
3389 : {
3390 192085 : TIME_SECTION("computeDirac", 3, "Computing DiracKernels");
3391 :
3392 : // TODO: Need a threading fix... but it's complicated!
3393 79882 : for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
3394 : {
3395 41481 : const auto & dkernels = _dirac_kernels.getActiveObjects(tid);
3396 96376 : for (const auto & dkernel : dkernels)
3397 : {
3398 54911 : dkernel->clearPoints();
3399 54911 : dkernel->addPoints();
3400 : }
3401 : }
3402 :
3403 38401 : ComputeDiracThread cd(_fe_problem, tags, is_jacobian);
3404 :
3405 38401 : _fe_problem.getDiracElements(dirac_elements);
3406 :
3407 38401 : DistElemRange range(dirac_elements.begin(), dirac_elements.end(), 1);
3408 : // TODO: Make Dirac work thread!
3409 : // Threads::parallel_reduce(range, cd);
3410 :
3411 38401 : cd(range);
3412 38401 : }
3413 3812336 : }
3414 :
3415 : NumericVector<Number> &
3416 0 : NonlinearSystemBase::residualCopy()
3417 : {
3418 0 : if (!_residual_copy.get())
3419 0 : _residual_copy = NumericVector<Number>::build(_communicator);
3420 :
3421 0 : return *_residual_copy;
3422 : }
3423 :
3424 : NumericVector<Number> &
3425 360 : NonlinearSystemBase::residualGhosted()
3426 : {
3427 360 : _need_residual_ghosted = true;
3428 360 : if (!_residual_ghosted)
3429 : {
3430 : // The first time we realize we need a ghosted residual vector,
3431 : // we add it.
3432 576 : _residual_ghosted = &addVector("residual_ghosted", false, GHOSTED);
3433 :
3434 : // If we've already realized we need time and/or non-time
3435 : // residual vectors, but we haven't yet realized they need to be
3436 : // ghosted, fix that now.
3437 : //
3438 : // If an application changes its mind, the libMesh API lets us
3439 : // change the vector.
3440 288 : if (_Re_time)
3441 : {
3442 50 : const auto vector_name = _subproblem.vectorTagName(_Re_time_tag);
3443 50 : _Re_time = &system().add_vector(vector_name, false, GHOSTED);
3444 50 : }
3445 288 : if (_Re_non_time)
3446 : {
3447 288 : const auto vector_name = _subproblem.vectorTagName(_Re_non_time_tag);
3448 288 : _Re_non_time = &system().add_vector(vector_name, false, GHOSTED);
3449 288 : }
3450 : }
3451 360 : return *_residual_ghosted;
3452 : }
3453 :
3454 : void
3455 68579 : NonlinearSystemBase::augmentSparsity(SparsityPattern::Graph & sparsity,
3456 : std::vector<dof_id_type> & n_nz,
3457 : std::vector<dof_id_type> & n_oz)
3458 : {
3459 68579 : if (_add_implicit_geometric_coupling_entries_to_jacobian)
3460 : {
3461 1231 : _fe_problem.updateGeomSearch();
3462 :
3463 1231 : std::unordered_map<dof_id_type, std::vector<dof_id_type>> graph;
3464 :
3465 1231 : findImplicitGeometricCouplingEntries(_fe_problem.geomSearchData(), graph);
3466 :
3467 1231 : if (_fe_problem.getDisplacedProblem())
3468 201 : findImplicitGeometricCouplingEntries(_fe_problem.getDisplacedProblem()->geomSearchData(),
3469 : graph);
3470 :
3471 1231 : const dof_id_type first_dof_on_proc = dofMap().first_dof(processor_id());
3472 1231 : const dof_id_type end_dof_on_proc = dofMap().end_dof(processor_id());
3473 :
3474 : // The total number of dofs on and off processor
3475 1231 : const dof_id_type n_dofs_on_proc = dofMap().n_local_dofs();
3476 1231 : const dof_id_type n_dofs_not_on_proc = dofMap().n_dofs() - dofMap().n_local_dofs();
3477 :
3478 2088 : for (const auto & git : graph)
3479 : {
3480 857 : dof_id_type dof = git.first;
3481 857 : dof_id_type local_dof = dof - first_dof_on_proc;
3482 :
3483 857 : if (dof < first_dof_on_proc || dof >= end_dof_on_proc)
3484 126 : continue;
3485 :
3486 731 : const auto & row = git.second;
3487 :
3488 731 : SparsityPattern::Row & sparsity_row = sparsity[local_dof];
3489 :
3490 731 : unsigned int original_row_length = sparsity_row.size();
3491 :
3492 731 : sparsity_row.insert(sparsity_row.end(), row.begin(), row.end());
3493 :
3494 1462 : SparsityPattern::sort_row(
3495 731 : sparsity_row.begin(), sparsity_row.begin() + original_row_length, sparsity_row.end());
3496 :
3497 : // Fix up nonzero arrays
3498 4807 : for (const auto & coupled_dof : row)
3499 : {
3500 4076 : if (coupled_dof < first_dof_on_proc || coupled_dof >= end_dof_on_proc)
3501 : {
3502 1224 : if (n_oz[local_dof] < n_dofs_not_on_proc)
3503 612 : n_oz[local_dof]++;
3504 : }
3505 : else
3506 : {
3507 3464 : if (n_nz[local_dof] < n_dofs_on_proc)
3508 3464 : n_nz[local_dof]++;
3509 : }
3510 : }
3511 : }
3512 1231 : }
3513 68579 : }
3514 :
3515 : void
3516 0 : NonlinearSystemBase::setSolutionUDot(const NumericVector<Number> & u_dot)
3517 : {
3518 0 : *_u_dot = u_dot;
3519 0 : }
3520 :
3521 : void
3522 0 : NonlinearSystemBase::setSolutionUDotDot(const NumericVector<Number> & u_dotdot)
3523 : {
3524 0 : *_u_dotdot = u_dotdot;
3525 0 : }
3526 :
3527 : void
3528 0 : NonlinearSystemBase::setSolutionUDotOld(const NumericVector<Number> & u_dot_old)
3529 : {
3530 0 : *_u_dot_old = u_dot_old;
3531 0 : }
3532 :
3533 : void
3534 0 : NonlinearSystemBase::setSolutionUDotDotOld(const NumericVector<Number> & u_dotdot_old)
3535 : {
3536 0 : *_u_dotdot_old = u_dotdot_old;
3537 0 : }
3538 :
3539 : void
3540 14633 : NonlinearSystemBase::setPreconditioner(std::shared_ptr<MoosePreconditioner> pc)
3541 : {
3542 14633 : if (_preconditioner.get() != nullptr)
3543 7 : mooseError("More than one active Preconditioner detected");
3544 :
3545 14626 : _preconditioner = pc;
3546 14626 : }
3547 :
3548 : MoosePreconditioner const *
3549 54384 : NonlinearSystemBase::getPreconditioner() const
3550 : {
3551 54384 : return _preconditioner.get();
3552 : }
3553 :
3554 : void
3555 322 : NonlinearSystemBase::setupDampers()
3556 : {
3557 322 : _increment_vec = &_sys.add_vector("u_increment", true, GHOSTED);
3558 322 : }
3559 :
3560 : void
3561 1753 : NonlinearSystemBase::reinitIncrementAtQpsForDampers(THREAD_ID /*tid*/,
3562 : const std::set<MooseVariable *> & damped_vars)
3563 : {
3564 3529 : for (const auto & var : damped_vars)
3565 1776 : var->computeIncrementAtQps(*_increment_vec);
3566 1753 : }
3567 :
3568 : void
3569 7441 : NonlinearSystemBase::reinitIncrementAtNodeForDampers(THREAD_ID /*tid*/,
3570 : const std::set<MooseVariable *> & damped_vars)
3571 : {
3572 14882 : for (const auto & var : damped_vars)
3573 7441 : var->computeIncrementAtNode(*_increment_vec);
3574 7441 : }
3575 :
3576 : void
3577 42945 : NonlinearSystemBase::checkKernelCoverage(const std::set<SubdomainID> & mesh_subdomains) const
3578 : {
3579 : // Obtain all blocks and variables covered by all kernels
3580 42945 : std::set<SubdomainID> input_subdomains;
3581 42945 : std::set<std::string> kernel_variables;
3582 :
3583 42945 : bool global_kernels_exist = false;
3584 42945 : global_kernels_exist |= _scalar_kernels.hasActiveObjects();
3585 42945 : global_kernels_exist |= _nodal_kernels.hasActiveObjects();
3586 :
3587 42945 : _kernels.subdomainsCovered(input_subdomains, kernel_variables);
3588 42945 : _dg_kernels.subdomainsCovered(input_subdomains, kernel_variables);
3589 42945 : _nodal_kernels.subdomainsCovered(input_subdomains, kernel_variables);
3590 42945 : _scalar_kernels.subdomainsCovered(input_subdomains, kernel_variables);
3591 42945 : _constraints.subdomainsCovered(input_subdomains, kernel_variables);
3592 :
3593 42945 : if (_fe_problem.haveFV())
3594 : {
3595 3525 : std::vector<FVElementalKernel *> fv_elemental_kernels;
3596 3525 : _fe_problem.theWarehouse()
3597 7050 : .query()
3598 3525 : .template condition<AttribSystem>("FVElementalKernel")
3599 3525 : .queryInto(fv_elemental_kernels);
3600 :
3601 7533 : for (auto fv_kernel : fv_elemental_kernels)
3602 : {
3603 4008 : if (fv_kernel->blockRestricted())
3604 1972 : for (auto block_id : fv_kernel->blockIDs())
3605 1103 : input_subdomains.insert(block_id);
3606 : else
3607 3139 : global_kernels_exist = true;
3608 4008 : kernel_variables.insert(fv_kernel->variable().name());
3609 :
3610 : // Check for lagrange multiplier
3611 4008 : if (dynamic_cast<FVScalarLagrangeMultiplierConstraint *>(fv_kernel))
3612 268 : kernel_variables.insert(dynamic_cast<FVScalarLagrangeMultiplierConstraint *>(fv_kernel)
3613 268 : ->lambdaVariable()
3614 134 : .name());
3615 : }
3616 :
3617 3525 : std::vector<FVFluxKernel *> fv_flux_kernels;
3618 3525 : _fe_problem.theWarehouse()
3619 7050 : .query()
3620 3525 : .template condition<AttribSystem>("FVFluxKernel")
3621 3525 : .queryInto(fv_flux_kernels);
3622 :
3623 8792 : for (auto fv_kernel : fv_flux_kernels)
3624 : {
3625 5267 : if (fv_kernel->blockRestricted())
3626 2350 : for (auto block_id : fv_kernel->blockIDs())
3627 1250 : input_subdomains.insert(block_id);
3628 : else
3629 4167 : global_kernels_exist = true;
3630 5267 : kernel_variables.insert(fv_kernel->variable().name());
3631 : }
3632 :
3633 3525 : std::vector<FVInterfaceKernel *> fv_interface_kernels;
3634 3525 : _fe_problem.theWarehouse()
3635 7050 : .query()
3636 3525 : .template condition<AttribSystem>("FVInterfaceKernel")
3637 3525 : .queryInto(fv_interface_kernels);
3638 :
3639 3830 : for (auto fvik : fv_interface_kernels)
3640 305 : if (auto scalar_fvik = dynamic_cast<FVScalarLagrangeMultiplierInterface *>(fvik))
3641 14 : kernel_variables.insert(scalar_fvik->lambdaVariable().name());
3642 :
3643 3525 : std::vector<FVFluxBC *> fv_flux_bcs;
3644 3525 : _fe_problem.theWarehouse()
3645 7050 : .query()
3646 3525 : .template condition<AttribSystem>("FVFluxBC")
3647 3525 : .queryInto(fv_flux_bcs);
3648 :
3649 5555 : for (auto fvbc : fv_flux_bcs)
3650 2030 : if (auto scalar_fvbc = dynamic_cast<FVBoundaryScalarLagrangeMultiplierConstraint *>(fvbc))
3651 14 : kernel_variables.insert(scalar_fvbc->lambdaVariable().name());
3652 3525 : }
3653 :
3654 : // Check kernel coverage of subdomains (blocks) in your mesh
3655 42945 : if (!global_kernels_exist)
3656 : {
3657 38942 : std::set<SubdomainID> difference;
3658 38942 : std::set_difference(mesh_subdomains.begin(),
3659 : mesh_subdomains.end(),
3660 : input_subdomains.begin(),
3661 : input_subdomains.end(),
3662 : std::inserter(difference, difference.end()));
3663 :
3664 : // there supposed to be no kernels on this lower-dimensional subdomain
3665 39180 : for (const auto & id : _mesh.interiorLowerDBlocks())
3666 238 : difference.erase(id);
3667 39180 : for (const auto & id : _mesh.boundaryLowerDBlocks())
3668 238 : difference.erase(id);
3669 :
3670 38942 : if (!difference.empty())
3671 : {
3672 : std::vector<SubdomainID> difference_vec =
3673 8 : std::vector<SubdomainID>(difference.begin(), difference.end());
3674 8 : std::vector<SubdomainName> difference_names = _mesh.getSubdomainNames(difference_vec);
3675 8 : std::stringstream missing_block_names;
3676 8 : std::copy(difference_names.begin(),
3677 : difference_names.end(),
3678 8 : std::ostream_iterator<std::string>(missing_block_names, " "));
3679 8 : std::stringstream missing_block_ids;
3680 8 : std::copy(difference.begin(),
3681 : difference.end(),
3682 8 : std::ostream_iterator<unsigned int>(missing_block_ids, " "));
3683 :
3684 8 : mooseError("Each subdomain must contain at least one Kernel.\nThe following block(s) lack an "
3685 8 : "active kernel: " +
3686 8 : missing_block_names.str(),
3687 : " (ids: ",
3688 8 : missing_block_ids.str(),
3689 : ")");
3690 0 : }
3691 38934 : }
3692 :
3693 : // Check kernel use of variables
3694 42937 : std::set<VariableName> variables(getVariableNames().begin(), getVariableNames().end());
3695 :
3696 42937 : std::set<VariableName> difference;
3697 42937 : std::set_difference(variables.begin(),
3698 : variables.end(),
3699 : kernel_variables.begin(),
3700 : kernel_variables.end(),
3701 : std::inserter(difference, difference.end()));
3702 :
3703 : // skip checks for varaibles defined on lower-dimensional subdomain
3704 42937 : std::set<VariableName> vars(difference);
3705 43300 : for (auto & var_name : vars)
3706 : {
3707 363 : auto blks = getSubdomainsForVar(var_name);
3708 744 : for (const auto & id : blks)
3709 381 : if (_mesh.interiorLowerDBlocks().count(id) > 0 || _mesh.boundaryLowerDBlocks().count(id) > 0)
3710 381 : difference.erase(var_name);
3711 363 : }
3712 :
3713 42937 : if (!difference.empty())
3714 : {
3715 8 : std::stringstream missing_kernel_vars;
3716 8 : std::copy(difference.begin(),
3717 : difference.end(),
3718 8 : std::ostream_iterator<std::string>(missing_kernel_vars, " "));
3719 8 : mooseError("Each variable must be referenced by at least one active Kernel.\nThe following "
3720 8 : "variable(s) lack an active kernel: " +
3721 8 : missing_kernel_vars.str());
3722 0 : }
3723 42929 : }
3724 :
3725 : bool
3726 28854 : NonlinearSystemBase::containsTimeKernel()
3727 : {
3728 28854 : auto & time_kernels = _kernels.getVectorTagObjectWarehouse(timeVectorTag(), 0);
3729 :
3730 28854 : return time_kernels.hasActiveObjects();
3731 : }
3732 :
3733 : std::vector<std::string>
3734 0 : NonlinearSystemBase::timeKernelVariableNames()
3735 : {
3736 0 : std::vector<std::string> variable_names;
3737 0 : const auto & time_kernels = _kernels.getVectorTagObjectWarehouse(timeVectorTag(), 0);
3738 0 : if (time_kernels.hasActiveObjects())
3739 0 : for (const auto & kernel : time_kernels.getObjects())
3740 0 : variable_names.push_back(kernel->variable().name());
3741 :
3742 0 : return variable_names;
3743 0 : }
3744 :
3745 : bool
3746 28897 : NonlinearSystemBase::needBoundaryMaterialOnSide(BoundaryID bnd_id, THREAD_ID tid) const
3747 : {
3748 28897 : return _integrated_bcs.hasActiveBoundaryObjects(bnd_id, tid);
3749 : }
3750 :
3751 : bool
3752 1478 : NonlinearSystemBase::needInterfaceMaterialOnSide(BoundaryID bnd_id, THREAD_ID tid) const
3753 : {
3754 1478 : return _interface_kernels.hasActiveBoundaryObjects(bnd_id, tid);
3755 : }
3756 :
3757 : bool
3758 12872 : NonlinearSystemBase::needSubdomainMaterialOnSide(SubdomainID /*subdomain_id*/,
3759 : THREAD_ID /*tid*/) const
3760 : {
3761 12872 : return _doing_dg;
3762 : }
3763 :
3764 : bool
3765 0 : NonlinearSystemBase::doingDG() const
3766 : {
3767 0 : return _doing_dg;
3768 : }
3769 :
3770 : void
3771 556 : NonlinearSystemBase::setPreviousNewtonSolution(const NumericVector<Number> & soln)
3772 : {
3773 556 : if (hasVector(Moose::PREVIOUS_NL_SOLUTION_TAG))
3774 556 : getVector(Moose::PREVIOUS_NL_SOLUTION_TAG) = soln;
3775 556 : }
3776 :
3777 : void
3778 3816433 : NonlinearSystemBase::mortarConstraints(const Moose::ComputeType compute_type,
3779 : const std::set<TagID> & vector_tags,
3780 : const std::set<TagID> & matrix_tags)
3781 : {
3782 : parallel_object_only();
3783 :
3784 : try
3785 : {
3786 3825552 : for (auto & map_pr : _undisplaced_mortar_functors)
3787 9119 : map_pr.second(compute_type, vector_tags, matrix_tags);
3788 :
3789 3821121 : for (auto & map_pr : _displaced_mortar_functors)
3790 4688 : map_pr.second(compute_type, vector_tags, matrix_tags);
3791 : }
3792 0 : catch (MetaPhysicL::LogicError &)
3793 : {
3794 0 : mooseError(
3795 : "We caught a MetaPhysicL error in NonlinearSystemBase::mortarConstraints. This is very "
3796 : "likely due to AD not having a sufficiently large derivative container size. Please run "
3797 : "MOOSE configure with the '--with-derivative-size=<n>' option");
3798 0 : }
3799 3816433 : }
3800 :
3801 : void
3802 439 : NonlinearSystemBase::setupScalingData()
3803 : {
3804 439 : if (_auto_scaling_initd)
3805 0 : return;
3806 :
3807 : // Want the libMesh count of variables, not MOOSE, e.g. I don't care about array variable counts
3808 439 : const auto n_vars = system().n_vars();
3809 :
3810 439 : if (_scaling_group_variables.empty())
3811 : {
3812 427 : _var_to_group_var.reserve(n_vars);
3813 427 : _num_scaling_groups = n_vars;
3814 :
3815 1128 : for (const auto var_number : make_range(n_vars))
3816 701 : _var_to_group_var.emplace(var_number, var_number);
3817 : }
3818 : else
3819 : {
3820 12 : std::set<unsigned int> var_numbers, var_numbers_covered, var_numbers_not_covered;
3821 48 : for (const auto var_number : make_range(n_vars))
3822 36 : var_numbers.insert(var_number);
3823 :
3824 12 : _num_scaling_groups = _scaling_group_variables.size();
3825 :
3826 24 : for (const auto group_index : index_range(_scaling_group_variables))
3827 48 : for (const auto & var_name : _scaling_group_variables[group_index])
3828 : {
3829 36 : if (!hasVariable(var_name) && !hasScalarVariable(var_name))
3830 0 : mooseError("'",
3831 : var_name,
3832 : "', provided to the 'scaling_group_variables' parameter, does not exist in "
3833 : "the nonlinear system.");
3834 :
3835 : const MooseVariableBase & var =
3836 36 : hasVariable(var_name)
3837 24 : ? static_cast<MooseVariableBase &>(getVariable(0, var_name))
3838 60 : : static_cast<MooseVariableBase &>(getScalarVariable(0, var_name));
3839 36 : auto map_pair = _var_to_group_var.emplace(var.number(), group_index);
3840 36 : if (!map_pair.second)
3841 0 : mooseError("Variable ", var_name, " is contained in multiple scaling grouplings");
3842 36 : var_numbers_covered.insert(var.number());
3843 : }
3844 :
3845 12 : std::set_difference(var_numbers.begin(),
3846 : var_numbers.end(),
3847 : var_numbers_covered.begin(),
3848 : var_numbers_covered.end(),
3849 : std::inserter(var_numbers_not_covered, var_numbers_not_covered.begin()));
3850 :
3851 12 : _num_scaling_groups = _scaling_group_variables.size() + var_numbers_not_covered.size();
3852 :
3853 12 : auto index = static_cast<unsigned int>(_scaling_group_variables.size());
3854 12 : for (auto var_number : var_numbers_not_covered)
3855 0 : _var_to_group_var.emplace(var_number, index++);
3856 12 : }
3857 :
3858 439 : _variable_autoscaled.resize(n_vars, true);
3859 439 : const auto & number_to_var_map = _vars[0].numberToVariableMap();
3860 :
3861 439 : if (_ignore_variables_for_autoscaling.size())
3862 50 : for (const auto i : index_range(_variable_autoscaled))
3863 40 : if (std::find(_ignore_variables_for_autoscaling.begin(),
3864 : _ignore_variables_for_autoscaling.end(),
3865 80 : libmesh_map_find(number_to_var_map, i)->name()) !=
3866 80 : _ignore_variables_for_autoscaling.end())
3867 20 : _variable_autoscaled[i] = false;
3868 :
3869 439 : _auto_scaling_initd = true;
3870 : }
3871 :
3872 : bool
3873 1112 : NonlinearSystemBase::computeScaling()
3874 : {
3875 1112 : if (_compute_scaling_once && _computed_scaling)
3876 460 : return true;
3877 :
3878 652 : _console << "\nPerforming automatic scaling calculation\n" << std::endl;
3879 :
3880 3260 : TIME_SECTION("computeScaling", 3, "Computing Automatic Scaling");
3881 :
3882 : // It's funny but we need to assemble our vector of scaling factors here otherwise we will be
3883 : // applying scaling factors of 0 during Assembly of our scaling Jacobian
3884 652 : assembleScalingVector();
3885 :
3886 : // container for repeated access of element global dof indices
3887 652 : std::vector<dof_id_type> dof_indices;
3888 :
3889 652 : if (!_auto_scaling_initd)
3890 439 : setupScalingData();
3891 :
3892 652 : std::vector<Real> inverse_scaling_factors(_num_scaling_groups, 0);
3893 652 : std::vector<Real> resid_inverse_scaling_factors(_num_scaling_groups, 0);
3894 652 : std::vector<Real> jac_inverse_scaling_factors(_num_scaling_groups, 0);
3895 652 : auto & dof_map = dofMap();
3896 :
3897 : // what types of scaling do we want?
3898 652 : bool jac_scaling = _resid_vs_jac_scaling_param < 1. - TOLERANCE;
3899 652 : bool resid_scaling = _resid_vs_jac_scaling_param > TOLERANCE;
3900 :
3901 652 : const NumericVector<Number> & scaling_residual = RHS();
3902 :
3903 652 : if (jac_scaling)
3904 : {
3905 : // if (!_auto_scaling_initd)
3906 : // We need to reinit this when the number of dofs changes
3907 : // but there is no good way to track that
3908 : // In theory, it is the job of libmesh system to track this,
3909 : // but this special matrix is not owned by libMesh system
3910 : // Let us reinit eveytime since it is not expensive
3911 : {
3912 602 : auto init_vector = NumericVector<Number>::build(this->comm());
3913 602 : init_vector->init(system().n_dofs(), system().n_local_dofs(), /*fast=*/false, PARALLEL);
3914 :
3915 602 : _scaling_matrix->clear();
3916 602 : _scaling_matrix->init(*init_vector);
3917 602 : }
3918 :
3919 602 : _fe_problem.computingScalingJacobian(true);
3920 : // Dispatch to derived classes to ensure that we use the correct matrix tag
3921 602 : computeScalingJacobian();
3922 602 : _fe_problem.computingScalingJacobian(false);
3923 : }
3924 :
3925 652 : if (resid_scaling)
3926 : {
3927 50 : _fe_problem.computingScalingResidual(true);
3928 50 : _fe_problem.computingNonlinearResid(true);
3929 : // Dispatch to derived classes to ensure that we use the correct vector tag
3930 50 : computeScalingResidual();
3931 50 : _fe_problem.computingNonlinearResid(false);
3932 50 : _fe_problem.computingScalingResidual(false);
3933 : }
3934 :
3935 : // Did something bad happen during residual/Jacobian scaling computation?
3936 652 : if (_fe_problem.getFailNextNonlinearConvergenceCheck())
3937 0 : return false;
3938 :
3939 228330 : auto examine_dof_indices = [this,
3940 : jac_scaling,
3941 : resid_scaling,
3942 : &dof_map,
3943 : &jac_inverse_scaling_factors,
3944 : &resid_inverse_scaling_factors,
3945 : &scaling_residual](const auto & dof_indices, const auto var_number)
3946 : {
3947 1136309 : for (auto dof_index : dof_indices)
3948 907979 : if (dof_map.local_index(dof_index))
3949 : {
3950 903220 : if (jac_scaling)
3951 : {
3952 : // For now we will use the diagonal for determining scaling
3953 899103 : auto mat_value = (*_scaling_matrix)(dof_index, dof_index);
3954 899103 : auto & factor = jac_inverse_scaling_factors[_var_to_group_var[var_number]];
3955 899103 : factor = std::max(factor, std::abs(mat_value));
3956 : }
3957 903220 : if (resid_scaling)
3958 : {
3959 4117 : auto vec_value = scaling_residual(dof_index);
3960 4117 : auto & factor = resid_inverse_scaling_factors[_var_to_group_var[var_number]];
3961 4117 : factor = std::max(factor, std::abs(vec_value));
3962 : }
3963 : }
3964 228330 : };
3965 :
3966 : // Compute our scaling factors for the spatial field variables
3967 82245 : for (const auto & elem : _fe_problem.getCurrentAlgebraicElementRange())
3968 310836 : for (const auto i : make_range(system().n_vars()))
3969 229243 : if (_variable_autoscaled[i] && system().variable_type(i).family != SCALAR)
3970 : {
3971 228237 : dof_map.dof_indices(elem, dof_indices, i);
3972 228237 : examine_dof_indices(dof_indices, i);
3973 : }
3974 :
3975 1712 : for (const auto i : make_range(system().n_vars()))
3976 1060 : if (_variable_autoscaled[i] && system().variable_type(i).family == SCALAR)
3977 : {
3978 93 : dof_map.SCALAR_dof_indices(dof_indices, i);
3979 93 : examine_dof_indices(dof_indices, i);
3980 : }
3981 :
3982 652 : if (resid_scaling)
3983 50 : _communicator.max(resid_inverse_scaling_factors);
3984 652 : if (jac_scaling)
3985 602 : _communicator.max(jac_inverse_scaling_factors);
3986 :
3987 652 : if (jac_scaling && resid_scaling)
3988 0 : for (MooseIndex(inverse_scaling_factors) i = 0; i < inverse_scaling_factors.size(); ++i)
3989 : {
3990 : // Be careful not to take log(0)
3991 0 : if (!resid_inverse_scaling_factors[i])
3992 : {
3993 0 : if (!jac_inverse_scaling_factors[i])
3994 0 : inverse_scaling_factors[i] = 1;
3995 : else
3996 0 : inverse_scaling_factors[i] = jac_inverse_scaling_factors[i];
3997 : }
3998 0 : else if (!jac_inverse_scaling_factors[i])
3999 : // We know the resid is not zero
4000 0 : inverse_scaling_factors[i] = resid_inverse_scaling_factors[i];
4001 : else
4002 0 : inverse_scaling_factors[i] =
4003 0 : std::exp(_resid_vs_jac_scaling_param * std::log(resid_inverse_scaling_factors[i]) +
4004 0 : (1 - _resid_vs_jac_scaling_param) * std::log(jac_inverse_scaling_factors[i]));
4005 0 : }
4006 652 : else if (jac_scaling)
4007 602 : inverse_scaling_factors = jac_inverse_scaling_factors;
4008 50 : else if (resid_scaling)
4009 50 : inverse_scaling_factors = resid_inverse_scaling_factors;
4010 : else
4011 0 : mooseError("We shouldn't be calling this routine if we're not performing any scaling");
4012 :
4013 : // We have to make sure that our scaling values are not zero
4014 1688 : for (auto & scaling_factor : inverse_scaling_factors)
4015 1036 : if (scaling_factor == 0)
4016 40 : scaling_factor = 1;
4017 :
4018 : // Now flatten the group scaling factors to the individual variable scaling factors
4019 652 : std::vector<Real> flattened_inverse_scaling_factors(system().n_vars());
4020 1712 : for (const auto i : index_range(flattened_inverse_scaling_factors))
4021 1060 : flattened_inverse_scaling_factors[i] = inverse_scaling_factors[_var_to_group_var[i]];
4022 :
4023 : // Now set the scaling factors for the variables
4024 652 : applyScalingFactors(flattened_inverse_scaling_factors);
4025 652 : if (auto displaced_problem = _fe_problem.getDisplacedProblem().get())
4026 63 : displaced_problem->systemBaseNonlinear(number()).applyScalingFactors(
4027 : flattened_inverse_scaling_factors);
4028 :
4029 652 : _computed_scaling = true;
4030 652 : return true;
4031 652 : }
4032 :
4033 : void
4034 314500 : NonlinearSystemBase::assembleScalingVector()
4035 : {
4036 943500 : if (!hasVector("scaling_factors"))
4037 : // No variables have indicated they need scaling
4038 313950 : return;
4039 :
4040 1100 : auto & scaling_vector = getVector("scaling_factors");
4041 :
4042 550 : const auto & lm_mesh = _mesh.getMesh();
4043 550 : const auto & dof_map = dofMap();
4044 :
4045 550 : const auto & field_variables = _vars[0].fieldVariables();
4046 550 : const auto & scalar_variables = _vars[0].scalars();
4047 :
4048 550 : std::vector<dof_id_type> dof_indices;
4049 :
4050 550 : for (const Elem * const elem :
4051 13672 : as_range(lm_mesh.active_local_elements_begin(), lm_mesh.active_local_elements_end()))
4052 31020 : for (const auto * const field_var : field_variables)
4053 : {
4054 18448 : const auto & factors = field_var->arrayScalingFactor();
4055 36896 : for (const auto i : make_range(field_var->count()))
4056 : {
4057 18448 : dof_map.dof_indices(elem, dof_indices, field_var->number() + i);
4058 76448 : for (const auto dof : dof_indices)
4059 58000 : scaling_vector.set(dof, factors[i]);
4060 : }
4061 550 : }
4062 :
4063 662 : for (const auto * const scalar_var : scalar_variables)
4064 : {
4065 : mooseAssert(scalar_var->count() == 1,
4066 : "Scalar variables should always have only one component.");
4067 112 : dof_map.SCALAR_dof_indices(dof_indices, scalar_var->number());
4068 224 : for (const auto dof : dof_indices)
4069 112 : scaling_vector.set(dof, scalar_var->scalingFactor());
4070 : }
4071 :
4072 : // Parallel assemble
4073 550 : scaling_vector.close();
4074 :
4075 550 : if (auto * displaced_problem = _fe_problem.getDisplacedProblem().get())
4076 : // copy into the corresponding displaced system vector because they should be the exact same
4077 0 : displaced_problem->systemBaseNonlinear(number()).getVector("scaling_factors") = scaling_vector;
4078 550 : }
4079 :
4080 : bool
4081 313848 : NonlinearSystemBase::preSolve()
4082 : {
4083 : // Clear the iteration counters
4084 313848 : _current_l_its.clear();
4085 313848 : _current_nl_its = 0;
4086 :
4087 : // Initialize the solution vector using a predictor and known values from nodal bcs
4088 313848 : setInitialSolution();
4089 :
4090 : // Now that the initial solution has ben set, potentially perform a residual/Jacobian evaluation
4091 : // to determine variable scaling factors
4092 313848 : if (_automatic_scaling)
4093 : {
4094 1112 : const bool scaling_succeeded = computeScaling();
4095 1112 : if (!scaling_succeeded)
4096 0 : return false;
4097 : }
4098 :
4099 : // We do not know a priori what variable a global degree of freedom corresponds to, so we need a
4100 : // map from global dof to scaling factor. We just use a ghosted NumericVector for that mapping
4101 313848 : assembleScalingVector();
4102 :
4103 313848 : return true;
4104 : }
4105 :
4106 : void
4107 5612 : NonlinearSystemBase::destroyColoring()
4108 : {
4109 5612 : if (matrixFromColoring())
4110 117 : LibmeshPetscCall(MatFDColoringDestroy(&_fdcoloring));
4111 5612 : }
4112 :
4113 : FieldSplitPreconditionerBase &
4114 0 : NonlinearSystemBase::getFieldSplitPreconditioner()
4115 : {
4116 0 : if (!_fsp)
4117 0 : mooseError("No field split preconditioner is present for this system");
4118 :
4119 0 : return *_fsp;
4120 : }
|