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