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