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