From 01b4bb257a897fb96fc66b3d3e30229272ca4ad1 Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Mon, 9 Oct 2023 11:16:46 -0600 Subject: [PATCH 01/31] Initial commit with: - Initial explicit dynamics action - Initial explicit dynamics node-face normal constraint (#25666). --- framework/src/systems/NonlinearSystemBase.C | 2 +- .../contact/explicit_dynamics/contact_stub.i | 302 +++++++++++++ .../contact/explicit_dynamics/first_test.i | 302 +++++++++++++ .../explicit_dynamics/gold/first_test_out.csv | 54 +++ modules/contact/explicit_dynamics/tests | 11 + .../actions/ExplicitDynamicsContactAction.h | 65 +++ .../ExplicitDynamicsContactConstraint.h | 124 ++++++ .../actions/ExplicitDynamicsContactAction.C | 326 ++++++++++++++ modules/contact/src/base/ContactApp.C | 1 + .../ExplicitDynamicsContactConstraint.C | 404 ++++++++++++++++++ 10 files changed, 1590 insertions(+), 1 deletion(-) create mode 100644 modules/contact/explicit_dynamics/contact_stub.i create mode 100644 modules/contact/explicit_dynamics/first_test.i create mode 100644 modules/contact/explicit_dynamics/gold/first_test_out.csv create mode 100644 modules/contact/explicit_dynamics/tests create mode 100644 modules/contact/include/actions/ExplicitDynamicsContactAction.h create mode 100644 modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h create mode 100644 modules/contact/src/actions/ExplicitDynamicsContactAction.C create mode 100644 modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C diff --git a/framework/src/systems/NonlinearSystemBase.C b/framework/src/systems/NonlinearSystemBase.C index 60b2ba2fd2ca..64eb081cce82 100644 --- a/framework/src/systems/NonlinearSystemBase.C +++ b/framework/src/systems/NonlinearSystemBase.C @@ -2788,7 +2788,7 @@ NonlinearSystemBase::computeJacobianInternal(const std::set & tags) PARALLEL_TRY { // Add in Jacobian contributions from other Constraints - if (_fe_problem._has_constraints) + if (_fe_problem._has_constraints && tags.count(systemMatrixTag())) { // Some constraints need values from the Jacobian closeTaggedMatrices(tags); diff --git a/modules/contact/explicit_dynamics/contact_stub.i b/modules/contact/explicit_dynamics/contact_stub.i new file mode 100644 index 000000000000..545c3972f2f2 --- /dev/null +++ b/modules/contact/explicit_dynamics/contact_stub.i @@ -0,0 +1,302 @@ +# One element test to test the central difference time integrator in 3D. +[GlobalParams] + displacements = 'disp_x disp_y disp_z' +[] + +[Mesh] + [block_one] + type = GeneratedMeshGenerator + dim = 3 + nx = 2 + ny = 2 + nz = 2 + xmin = 4.5 + xmax = 5.5 + ymin = 4.5 + ymax = 5.5 + zmin = 0.001 + zmax = 1.001 + boundary_name_prefix = 'ball' + [] + [block_two] + type = GeneratedMeshGenerator + dim = 3 + nx = 2 + ny = 2 + nz = 2 + xmin = 0.0 + xmax = 10 + ymin = 0.0 + ymax = 10 + zmin = -2 + zmax = 0 + boundary_name_prefix = 'base' + boundary_id_offset = 10 + [] + [block_one_id] + type = SubdomainIDGenerator + input = block_one + subdomain_id = 1 + [] + [block_two_id] + type = SubdomainIDGenerator + input = block_two + subdomain_id = 2 + [] + [combine] + type = MeshCollectionGenerator + inputs = ' block_one_id block_two_id' + [] +[] + +[Variables] + [disp_x] + [] + [disp_y] + [] + [disp_z] + [] +[] + +[AuxVariables] + [vel_x] + [] + [accel_x] + [] + [vel_y] + [] + [accel_y] + [] + [vel_z] + [] + [accel_z] + [] + [stress_zz] + [] + [strain_zz] + [] +[] + +[AuxKernels] + [stress_zz] + type = RankTwoAux + rank_two_tensor = stress + index_i = 2 + index_j = 2 + variable = stress_zz + [] + [strain_zz] + type = RankTwoAux + rank_two_tensor = elastic_strain + index_i = 2 + index_j = 2 + variable = strain_zz + [] + [accel_x] + type = TestNewmarkTI + variable = accel_x + displacement = disp_x + first = false + [] + [vel_x] + type = TestNewmarkTI + variable = vel_x + displacement = disp_x + [] + [accel_y] + type = TestNewmarkTI + variable = accel_y + displacement = disp_y + first = false + [] + [vel_y] + type = TestNewmarkTI + variable = vel_y + displacement = disp_x + [] + [accel_z] + type = TestNewmarkTI + variable = accel_z + displacement = disp_z + first = false + [] + [vel_z] + type = TestNewmarkTI + variable = vel_z + displacement = disp_z + [] +[] + +[Kernels] + [DynamicTensorMechanics] + displacements = 'disp_x disp_y disp_z' + volumetric_locking_correction = true + stiffness_damping_coefficient = 0.04 + # generate_output = 'stress_zz strain_zz' + [] + [inertia_x] + type = InertialForce + variable = disp_x + [] + [inertia_y] + type = InertialForce + variable = disp_y + [] + [inertia_z] + type = InertialForce + variable = disp_z + [] +[] + +[Functions] + [dispz] + type = ParsedFunction + expression = if(t<1.0e3,-0.01*t,0) + [] + [push] + type = ParsedFunction + expression = if(t<10.0,0.01*t,0.1) + [] +[] + +[BCs] + [z_front] + type = FunctionDirichletBC + variable = disp_z + boundary = 'ball_front' + function = dispz + preset = false + [] + [x_front] + type = DirichletBC + variable = disp_x + boundary = 'ball_front' + preset = false + value = 0.0 + [] + [y_front] + type = DirichletBC + variable = disp_y + boundary = 'ball_front' + preset = false + value = 0.0 + [] + [x_fixed] + type = DirichletBC + variable = disp_x + boundary = 'base_back' + preset = false + value = 0.0 + [] + [y_fixed] + type = DirichletBC + variable = disp_y + boundary = 'base_back' + preset = false + value = 0.0 + [] + [z_fixed] + type = DirichletBC + variable = disp_z + boundary = 'base_back' + preset = false + value = 0.0 + [] +[] + +[ExplicitDynamicsContact] + [my_contact] + model = frictionless + # formulation = penalty + primary = base_front + secondary = ball_back + # penalty = 1e+05 + # normalize_penalty = true + [] +[] + +# [Controls] +# [mycontrol] +# type = TimePeriod +# disable_objects = 'BCs/z_bot' +# start_time = 1.0e-3 +# end_time = 1.0e9 +# execute_on = 'INITIAL TIMESTEP_END' +# [] +# [] + +[Materials] + [elasticity_tensor_block_one] + type = ComputeIsotropicElasticityTensor + youngs_modulus = 1e3 + poissons_ratio = 0.0 + block = 1 + [] + [elasticity_tensor_block_two] + type = ComputeIsotropicElasticityTensor + youngs_modulus = 1e6 + poissons_ratio = 0.0 + block = 2 + [] + [strain_block] + type = ComputeIncrementalSmallStrain + displacements = 'disp_x disp_y disp_z' + implicit = false + [] + [stress_block] + type = ComputeFiniteStrainElasticStress + [] + [density] + type = GenericConstantMaterial + prop_names = density + prop_values = 1e4 + [] + [wave_speed] + type = WaveSpeed + [] +[] + +[Executioner] + type = Transient + start_time = -0.01 + end_time = 24.0 + dt = 0.005 + timestep_tolerance = 1e-6 + + [TimeIntegrator] + type = CentralDifference + [] +[] + +[Postprocessors] + [accel_58z] + type = NodalVariableValue + nodeid = 1 + variable = accel_z + [] + [vel_58z] + type = NodalVariableValue + nodeid = 1 + variable = vel_z + [] + [disp_58z] + type = NodalVariableValue + nodeid = 1 + variable = disp_z + [] + [critical_time_step] + type = CriticalTimeStep + [] + [contact_pressure_max] + type = NodalExtremeValue + variable = contact_pressure + block = '1 2' + value_type = max + [] +[] + +[Outputs] + # interval = 50 + exodus = true + csv = true +[] diff --git a/modules/contact/explicit_dynamics/first_test.i b/modules/contact/explicit_dynamics/first_test.i new file mode 100644 index 000000000000..c7a25fc38e33 --- /dev/null +++ b/modules/contact/explicit_dynamics/first_test.i @@ -0,0 +1,302 @@ +# One element test to test the central difference time integrator in 3D. +[GlobalParams] + displacements = 'disp_x disp_y disp_z' +[] + +[Mesh] + [block_one] + type = GeneratedMeshGenerator + dim = 3 + nx = 2 + ny = 2 + nz = 2 + xmin = 4.5 + xmax = 5.5 + ymin = 4.5 + ymax = 5.5 + zmin = 0.0001 + zmax = 1.0001 + boundary_name_prefix = 'ball' + [] + [block_two] + type = GeneratedMeshGenerator + dim = 3 + nx = 2 + ny = 2 + nz = 2 + xmin = 0.0 + xmax = 10 + ymin = 0.0 + ymax = 10 + zmin = -2 + zmax = 0 + boundary_name_prefix = 'base' + boundary_id_offset = 10 + [] + [block_one_id] + type = SubdomainIDGenerator + input = block_one + subdomain_id = 1 + [] + [block_two_id] + type = SubdomainIDGenerator + input = block_two + subdomain_id = 2 + [] + [combine] + type = MeshCollectionGenerator + inputs = ' block_one_id block_two_id' + [] +[] + +[Variables] + [disp_x] + [] + [disp_y] + [] + [disp_z] + [] +[] + +[AuxVariables] + [vel_x] + [] + [accel_x] + [] + [vel_y] + [] + [accel_y] + [] + [vel_z] + [] + [accel_z] + [] + [stress_zz] + [] + [strain_zz] + [] +[] + +[AuxKernels] + [stress_zz] + type = RankTwoAux + rank_two_tensor = stress + index_i = 2 + index_j = 2 + variable = stress_zz + [] + [strain_zz] + type = RankTwoAux + rank_two_tensor = elastic_strain + index_i = 2 + index_j = 2 + variable = strain_zz + [] + [accel_x] + type = TestNewmarkTI + variable = accel_x + displacement = disp_x + first = false + [] + [vel_x] + type = TestNewmarkTI + variable = vel_x + displacement = disp_x + [] + [accel_y] + type = TestNewmarkTI + variable = accel_y + displacement = disp_y + first = false + [] + [vel_y] + type = TestNewmarkTI + variable = vel_y + displacement = disp_x + [] + [accel_z] + type = TestNewmarkTI + variable = accel_z + displacement = disp_z + first = false + [] + [vel_z] + type = TestNewmarkTI + variable = vel_z + displacement = disp_z + [] +[] + +[Kernels] + [DynamicTensorMechanics] + displacements = 'disp_x disp_y disp_z' + volumetric_locking_correction = true + stiffness_damping_coefficient = 0.04 + # generate_output = 'stress_zz strain_zz' + [] + [inertia_x] + type = InertialForce + variable = disp_x + [] + [inertia_y] + type = InertialForce + variable = disp_y + [] + [inertia_z] + type = InertialForce + variable = disp_z + [] +[] + +[Functions] + [dispz] + type = ParsedFunction + expression = if(t<1.0e3,-0.01*t,0) + [] + [push] + type = ParsedFunction + expression = if(t<10.0,0.01*t,0.1) + [] +[] + +[BCs] + [z_front] + type = FunctionDirichletBC + variable = disp_z + boundary = 'ball_front' + function = dispz + preset = false + [] + [x_front] + type = DirichletBC + variable = disp_x + boundary = 'ball_front' + preset = false + value = 0.0 + [] + [y_front] + type = DirichletBC + variable = disp_y + boundary = 'ball_front' + preset = false + value = 0.0 + [] + [x_fixed] + type = DirichletBC + variable = disp_x + boundary = 'base_back' + preset = false + value = 0.0 + [] + [y_fixed] + type = DirichletBC + variable = disp_y + boundary = 'base_back' + preset = false + value = 0.0 + [] + [z_fixed] + type = DirichletBC + variable = disp_z + boundary = 'base_back' + preset = false + value = 0.0 + [] +[] + +[ExplicitDynamicsContact] + [my_contact] + model = frictionless + # formulation = penalty + primary = base_front + secondary = ball_back + # penalty = 1e+05 + # normalize_penalty = true + [] +[] + +# [Controls] +# [mycontrol] +# type = TimePeriod +# disable_objects = 'BCs/z_bot' +# start_time = 1.0e-3 +# end_time = 1.0e9 +# execute_on = 'INITIAL TIMESTEP_END' +# [] +# [] + +[Materials] + [elasticity_tensor_block_one] + type = ComputeIsotropicElasticityTensor + youngs_modulus = 1e3 + poissons_ratio = 0.0 + block = 1 + [] + [elasticity_tensor_block_two] + type = ComputeIsotropicElasticityTensor + youngs_modulus = 1e6 + poissons_ratio = 0.0 + block = 2 + [] + [strain_block] + type = ComputeIncrementalSmallStrain + displacements = 'disp_x disp_y disp_z' + implicit = false + [] + [stress_block] + type = ComputeFiniteStrainElasticStress + [] + [density] + type = GenericConstantMaterial + prop_names = density + prop_values = 1e4 + [] + [wave_speed] + type = WaveSpeed + [] +[] + +[Executioner] + type = Transient + start_time = -0.01 + end_time = 0.25 + dt = 0.005 + timestep_tolerance = 1e-6 + + [TimeIntegrator] + type = CentralDifference + [] +[] + +[Postprocessors] + [accel_58z] + type = NodalVariableValue + nodeid = 1 + variable = accel_z + [] + [vel_58z] + type = NodalVariableValue + nodeid = 1 + variable = vel_z + [] + [disp_58z] + type = NodalVariableValue + nodeid = 1 + variable = disp_z + [] + [critical_time_step] + type = CriticalTimeStep + [] + [contact_pressure_max] + type = NodalExtremeValue + variable = contact_pressure + block = '1 2' + value_type = max + [] +[] + +[Outputs] + # interval = 50 + exodus = true + csv = true +[] diff --git a/modules/contact/explicit_dynamics/gold/first_test_out.csv b/modules/contact/explicit_dynamics/gold/first_test_out.csv new file mode 100644 index 000000000000..8faf552b51fa --- /dev/null +++ b/modules/contact/explicit_dynamics/gold/first_test_out.csv @@ -0,0 +1,54 @@ +time,accel_58z,contact_pressure_max,critical_time_step,disp_58z,vel_58z +-0.01,0,0,0,0,0 +-0.005,0,0,0.1,7.1428571428571e-06,0 +0,0.28571428571429,0,0.1,-1.4273832748933e-08,0.00071428571428571 +0.005,-0.57199952473853,0,0.1,-7.1596363437208e-06,-1.4273832748933e-06 +0.01,0.00047073858536669,0,0.1,-1.4291770020591e-05,-0.0014302493486578 +0.015,0.00052915336407702,0,0.1,-2.1409216967926e-05,-0.0014277496187842 +0.02,0.00058746918137703,0,0.1,-2.8510521909652e-05,-0.0014249580624206 +0.025,0.00064568022437881,0,0.1,-3.5594232334055e-05,-0.0014218751889062 +0.03,0.0007037806929369,0,0.1,-4.2658898638455e-05,-0.0014185015366129 +0.035,0.00076176480009714,0,0.1,-4.9703074273542e-05,-0.0014148376728803 +0.04,0.00081962677255106,0,0.1,-5.6725315887351e-05,-0.0014108841939487 +0.045,0.00087736085107866,0,0.1,-6.3724183468886e-05,-0.0014066417248896 +0.05,0.00093496129099528,0,0.1,-7.0698240491355e-05,-0.0014021109195344 +0.055,0.00099242236259982,0,0.1,-7.7646054055035e-05,-0.0013972924604004 +0.06,0.0010497383516112,0,0.1,-8.4566195029724e-05,-0.0013921870586149 +0.065,0.0011069035596071,0,0.1,-9.1457238196801e-05,-0.0013867954538368 +0.07,0.0011639123044733,0,0.1,-9.8317762390858e-05,-0.0013811184141766 +0.075,0.0012207589208208,0,0.1,-0.0001051463506409,-0.0013751567361134 +0.08,0.0012774377604406,0.082341610254479,0.1,-0.00011194159031113,-0.0013689112444103 +0.085,0.0013339431927144,0.19101404878775,0.1,-0.00011870207077947,-0.0013623827920274 +0.09,0.0013903680754374,0.29901128340337,0.1,-0.000125426380983,-0.001355572013857 +0.095,0.0014468105927516,0.40624378043303,0.1,-0.00013211310720354,-0.0013484790671865 +0.1,0.0015033593191535,0.51262279737801,0.1,-0.0001387608308863,-0.0013411036424068 +0.105,0.0015601015116037,0.61806047588701,0.1,-0.00014536812639074,-0.0013334449903299 +0.11,0.0016171271325011,0.72246991323486,0.1,-0.0001519335590144,-0.0013255019187196 +0.115,0.001674515230965,0.82576527398727,0.1,-0.00015845568288214,-0.0013172728128109 +0.12,0.0017323502374635,0.92786186707797,0.1,-0.00016493303893838,-0.0013087556491399 +0.125,0.0017907124595868,1.0286762300661,0.1,-0.00017136415299712,-0.0012999479923972 +0.13,0.0018496798999412,1.1281262115668,0.1,-0.00017774753385423,-0.0012908470114984 +0.135,0.0019093280652566,1.2261310518263,0.1,-0.00018408167146683,-0.0012814494915854 +0.14,0.001969729780531,1.3226114614042,0.1,-0.00019036503520421,-0.001271751846971 +0.145,0.0020309550085944,1.4174896979197,0.1,-0.00019659607217471,-0.0012617501349981 +0.15,0.0020930706754688,1.5106896408102,0.1,-0.00020277320563266,-0.001251440070788 +0.155,0.0021561405017217,1.602136864045,0.1,-0.00020889483346961,-0.001240817042845 +0.16,0.0022202248402281,1.6917587067307,0.1,-0.00021495932679354,-0.0012298761294901 +0.165,0.0022853805205843,1.7794843415407,0.1,-0.00022096502859997,-0.0012186121160881 +0.17,0.002351660700413,1.8652448408947,0.1,-0.00022691025253829,-0.0012070195130356 +0.175,0.0024191147238834,1.9489732408148,0.1,-0.00023279328177693,-0.0011950925744749 +0.18,0.0024877879876864,2.0306046023756,0.1,-0.00023861236797019,-0.0011828253176959 +0.185,0.0025577218146179,2.1100760706723,0.1,-0.00024436573033008,-0.0011702115431902 +0.19,0.0026289533351361,2.1873269312209,0.1,-0.00025005155480555,-0.0011572448553158 +0.195,0.0027015153769366,2.2622986637121,0.1,-0.00025566799337195,-0.0011439186835356 +0.2,0.0027754363628673,2.3349349930359,0.1,-0.00026121316343291,-0.0011302263041861 +0.205,0.0028507402172954,2.4051819374974,0.1,-0.00026668514733684,-0.0011161608627357 +0.21,0.0029274462810618,2.4729878541477,0.1,-0.0002720819920099,-0.0011017153964898 +0.215,0.0030055692352613,2.5383034811523,0.1,-0.00027740170870711,-0.001086882857699 +0.22,0.0030851190338586,2.6010819771283,0.1,-0.00028264227288318,-0.0010716561370262 +0.225,0.003166100845367,2.6612789573813,0.1,-0.00028780162418416,-0.0010560280873281 +0.23,0.0032485150035862,2.71885252698,0.1,-0.00029287766656096,-0.0010399915477058 +0.235,0.0033323569676,2.7737633106092,0.1,-0.00029786826850548,-0.0010235393677778 +0.24,0.0034176172909284,2.8259744791508,0.1,-0.00030277126341,-0.0010066644321315 +0.245,0.0035042816000971,2.8754517729438,0.1,-0.00030758445004995,-0.0009893596849039 +0.25,0.0035923305824689,2.9221635216841,0.1,-0.00031230559319032,-0.00097161815444751 diff --git a/modules/contact/explicit_dynamics/tests b/modules/contact/explicit_dynamics/tests new file mode 100644 index 000000000000..792017fb2956 --- /dev/null +++ b/modules/contact/explicit_dynamics/tests @@ -0,0 +1,11 @@ +[Tests] + issues = '#0000' + design = '/Contact/index.md' + [block_penalty] + type = 'CSVDiff' + input = 'first_test.i' + csvdiff = 'first_test_out.csv' + requirement = 'The system shall be able to solve a simple few-element normal contact problem ' + 'using explicit dynamics.' + [] +[] diff --git a/modules/contact/include/actions/ExplicitDynamicsContactAction.h b/modules/contact/include/actions/ExplicitDynamicsContactAction.h new file mode 100644 index 000000000000..4b8af4bf90c5 --- /dev/null +++ b/modules/contact/include/actions/ExplicitDynamicsContactAction.h @@ -0,0 +1,65 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#pragma once + +#include "Action.h" +#include "MooseTypes.h" +#include "MooseEnum.h" + +enum class ExplicitDynamicsContactModel +{ + FRICTIONLESS +}; + +/** + * Action class for creating constraints, kernels, and user objects necessary for mechanical + * contact. + */ +class ExplicitDynamicsContactAction : public Action +{ +public: + static InputParameters validParams(); + + ExplicitDynamicsContactAction(const InputParameters & params); + + virtual void act() override; + + /** + * Get contact model + * @return enum + */ + static MooseEnum getModelEnum(); + + /** + * Define parameters used by multiple contact objects + * @return InputParameters object populated with common parameters + */ + static InputParameters commonParameters(); + +protected: + /// Primary/Secondary boundary name pairs for mechanical contact + std::vector> _boundary_pairs; + + /// Contact model type enum + const ExplicitDynamicsContactModel _model; + + /// Type that we use in Actions for declaring coupling + typedef std::vector CoupledName; + +private: + /** + * Generate constraints for node to face contact + */ + void addNodeFaceContact(); + /** + * Add single contact pressure auxiliary kernel for various contact action objects + */ + void addContactPressureAuxKernel(); +}; diff --git a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h new file mode 100644 index 000000000000..7794d8263440 --- /dev/null +++ b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h @@ -0,0 +1,124 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#pragma once + +// MOOSE includes +#include "NodeFaceConstraint.h" +#include "PenetrationLocator.h" + +// Forward Declarations +class ContactLineSearchBase; +enum class ExplicitDynamicsContactModel; + +/** + * A MechanicalContactConstraint forces the value of a variable to be the same on both sides of an + * interface. + */ +class ExplicitDynamicsContactConstraint : public NodeFaceConstraint +{ +public: + static InputParameters validParams(); + + ExplicitDynamicsContactConstraint(const InputParameters & parameters); + + virtual void timestepSetup() override; + virtual void jacobianSetup() override {} + virtual void residualEnd() override; + + virtual void updateContactStatefulData(bool beginning_of_step); + virtual Real computeQpSecondaryValue() override; + virtual Real computeQpResidual(Moose::ConstraintType type) override; + + /** + * Computes the jacobian for the current element. + */ + virtual void computeJacobian() override {} + + /** + * Compute off-diagonal Jacobian entries + * @param jvar The index of the coupled variable + */ + virtual void computeOffDiagJacobian(unsigned int /*jvar*/) override {} + + virtual Real computeQpJacobian(Moose::ConstraintJacobianType /*type*/) override { return 0.0; } + + /** + * Compute off-diagonal Jacobian entries + * @param type The type of coupling + * @param jvar The index of the coupled variable + */ + virtual Real computeQpOffDiagJacobian(Moose::ConstraintJacobianType /*type*/, + unsigned int /*jvar*/) override + { + return 0.0; + } + + /** + * Determine whether the coupled variable is one of the displacement variables, + * and find its component + * @param var_num The number of the variable to be checked + * @param component The component index computed in this routine + * @return bool indicating whether the coupled variable is one of the displacement variables + */ + bool getCoupledVarComponent(unsigned int var_num, unsigned int & component); + + bool shouldApply() override; + void computeContactForce(const Node & node, PenetrationInfo * pinfo, bool update_contact_set); + + /** + * Return false so that the nonlinear system does not try to add Jacobian entries + * from the contact forces. + * @return bool indicating whether we need to couple Jacobian entries + */ + virtual bool addCouplingEntriesToJacobian() override { return false; } + +protected: + MooseSharedPointer _displaced_problem; + Real gapOffset(const Node & node); + Real nodalArea(const Node & node); + Real getPenalty(const Node & node); + + const unsigned int _component; + const ExplicitDynamicsContactModel _model; + const bool _normalize_penalty; + + const Real _tension_release; + const Real _capture_tolerance; + const unsigned int _stick_lock_iterations; + const Real _stick_unlock_factor; + bool _update_stateful_data; + + NumericVector & _residual_copy; + // std::map _point_to_info; + + const unsigned int _mesh_dimension; + + std::vector _vars; + std::vector _var_objects; + + /// gap offset from either secondary, primary or both + const bool _has_secondary_gap_offset; + const MooseVariable * const _secondary_gap_offset_var; + const bool _has_mapped_primary_gap_offset; + const MooseVariable * const _mapped_primary_gap_offset_var; + + MooseVariable * _nodal_area_var; + SystemBase & _aux_system; + const NumericVector * const _aux_solution; + + ContactLineSearchBase * _contact_linesearch; + std::set _current_contact_state; + std::set _old_contact_state; + + const bool _print_contact_nodes; + static Threads::spin_mutex _contact_set_mutex; + + const static unsigned int _no_iterations; +}; diff --git a/modules/contact/src/actions/ExplicitDynamicsContactAction.C b/modules/contact/src/actions/ExplicitDynamicsContactAction.C new file mode 100644 index 000000000000..3b8d980d711e --- /dev/null +++ b/modules/contact/src/actions/ExplicitDynamicsContactAction.C @@ -0,0 +1,326 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#include "ExplicitDynamicsContactAction.h" + +#include "Factory.h" +#include "FEProblem.h" +#include "Conversion.h" +#include "AddVariableAction.h" +#include "NonlinearSystemBase.h" +#include "Parser.h" + +#include "NanoflannMeshAdaptor.h" +#include "PointListAdaptor.h" + +#include +#include +#include +#include + +#include "libmesh/petsc_nonlinear_solver.h" +#include "libmesh/string_to_enum.h" + +// Counter for naming auxiliary kernels +static unsigned int ed_contact_auxkernel_counter = 0; + +// Counter for naming nodal area user objects +static unsigned int ed_contact_userobject_counter = 0; + +// Counter for distinct contact action objects +static unsigned int ed_contact_action_counter = 0; + +registerMooseAction("ContactApp", ExplicitDynamicsContactAction, "add_aux_variable"); +registerMooseAction("ContactApp", ExplicitDynamicsContactAction, "add_contact_aux_variable"); +registerMooseAction("ContactApp", ExplicitDynamicsContactAction, "add_aux_kernel"); + +registerMooseAction("ContactApp", ExplicitDynamicsContactAction, "add_constraint"); +registerMooseAction("ContactApp", ExplicitDynamicsContactAction, "add_user_object"); + +InputParameters +ExplicitDynamicsContactAction::validParams() +{ + InputParameters params = Action::validParams(); + params += ExplicitDynamicsContactAction::commonParameters(); + + params.addParam>( + "primary", "The list of boundary IDs referring to primary sidesets"); + params.addParam>( + "secondary", "The list of boundary IDs referring to secondary sidesets"); + params.addParam>( + "displacements", + "The displacements appropriate for the simulation geometry and coordinate system"); + params.addParam("friction_coefficient", 0, "The friction coefficient"); + params.addParam( + "model", ExplicitDynamicsContactAction::getModelEnum(), "The contact model to use"); + params.addParam("tangential_tolerance", + "Tangential distance to extend edges of contact surfaces"); + params.addClassDescription("Sets up all objects needed for mechanical contact enforcement"); + params.addParam>( + "extra_vector_tags", + "The tag names for extra vectors that residual data should be saved into"); + params.addParam>( + "absolute_value_vector_tags", + "The tags for the vectors this residual object should fill with the " + "absolute value of the residual contribution"); + + params.addParam("secondary_gap_offset", + "Offset to gap distance from secondary side"); + params.addParam("mapped_primary_gap_offset", + "Offset to gap distance mapped from primary side"); + + return params; +} + +ExplicitDynamicsContactAction::ExplicitDynamicsContactAction(const InputParameters & params) + : Action(params), + _boundary_pairs(getParam("primary", "secondary")), + _model(getParam("model").getEnum()) +{ +} + +void +ExplicitDynamicsContactAction::act() +{ + // proform problem checks/corrections once during the first feasible task + if (_current_task == "add_contact_aux_variable") + { + if (!_problem->getDisplacedProblem()) + mooseError( + "Contact requires updated coordinates. Use the 'displacements = ...' parameter in the " + "Mesh block."); + + // It is risky to apply this optimization to contact problems + // since the problem configuration may be changed during Jacobian + // evaluation. We therefore turn it off for all contact problems so that + // PETSc-3.8.4 or higher will have the same behavior as PETSc-3.8.3. + if (!_problem->isSNESMFReuseBaseSetbyUser()) + _problem->setSNESMFReuseBase(false, false); + } + + addNodeFaceContact(); + + if (_current_task == "add_aux_kernel") + { // Add ContactPenetrationAuxAction. + if (!_problem->getDisplacedProblem()) + mooseError("Contact requires updated coordinates. Use the 'displacements = ...' line in the " + "Mesh block."); + // Create auxiliary kernels for each contact pairs + for (const auto & contact_pair : _boundary_pairs) + { + { + InputParameters params = _factory.getValidParams("PenetrationAux"); + params.applyParameters(parameters(), + {"secondary_gap_offset", "mapped_primary_gap_offset", "order"}); + + std::vector displacements = + getParam>("displacements"); + const auto order = _problem->systemBaseNonlinear() + .system() + .variable_type(displacements[0]) + .order.get_order(); + + params.set("order") = Utility::enum_to_string(OrderWrapper{order}); + params.set("execute_on") = {EXEC_INITIAL, EXEC_LINEAR}; + params.set>("boundary") = {contact_pair.second}; + params.set("paired_boundary") = contact_pair.first; + params.set("variable") = "penetration"; + if (isParamValid("secondary_gap_offset")) + params.set>("secondary_gap_offset") = { + getParam("secondary_gap_offset")}; + if (isParamValid("mapped_primary_gap_offset")) + params.set>("mapped_primary_gap_offset") = { + getParam("mapped_primary_gap_offset")}; + params.set("use_displaced_mesh") = true; + std::string name = _name + "_contact_" + Moose::stringify(ed_contact_auxkernel_counter++); + + _problem->addAuxKernel("PenetrationAux", name, params); + } + } + + addContactPressureAuxKernel(); + } + + if (_current_task == "add_contact_aux_variable") + { + std::vector displacements = getParam>("displacements"); + const auto order = + _problem->systemBaseNonlinear().system().variable_type(displacements[0]).order.get_order(); + // Add ContactPenetrationVarAction + { + auto var_params = _factory.getValidParams("MooseVariable"); + var_params.set("order") = Utility::enum_to_string(OrderWrapper{order}); + var_params.set("family") = "LAGRANGE"; + + _problem->addAuxVariable("MooseVariable", "penetration", var_params); + } + // Add ContactPressureVarAction + { + auto var_params = _factory.getValidParams("MooseVariable"); + var_params.set("order") = Utility::enum_to_string(OrderWrapper{order}); + var_params.set("family") = "LAGRANGE"; + + _problem->addAuxVariable("MooseVariable", "contact_pressure", var_params); + } + // Add nodal area contact variable + { + auto var_params = _factory.getValidParams("MooseVariable"); + var_params.set("order") = Utility::enum_to_string(OrderWrapper{order}); + var_params.set("family") = "LAGRANGE"; + + _problem->addAuxVariable("MooseVariable", "nodal_area", var_params); + } + } + + if (_current_task == "add_user_object") + { + auto var_params = _factory.getValidParams("NodalArea"); + + // Get secondary_boundary_vector from possibly updated set from the + // ContactAction constructor cleanup + const auto actions = _awh.getActions(); + + std::vector secondary_boundary_vector; + for (const auto * const action : actions) + for (const auto j : index_range(action->_boundary_pairs)) + secondary_boundary_vector.push_back(action->_boundary_pairs[j].second); + + var_params.set>("boundary") = secondary_boundary_vector; + var_params.set>("variable") = {"nodal_area"}; + + mooseAssert(_problem, "Problem pointer is NULL"); + var_params.set("execute_on", true) = {EXEC_INITIAL, EXEC_TIMESTEP_BEGIN}; + var_params.set("use_displaced_mesh") = true; + + _problem->addUserObject("NodalArea", + "nodal_area_object_" + + Moose::stringify(ed_contact_userobject_counter++), + var_params); + } +} + +void +ExplicitDynamicsContactAction::addContactPressureAuxKernel() +{ + // Add ContactPressureAux: Only one object for all contact pairs + // if (_formulation != ContactFormulation::MORTAR) + const auto actions = _awh.getActions(); + + // Increment counter for contact action objects + ed_contact_action_counter++; + // Add auxiliary kernel if we are the last contact action object. + if (ed_contact_action_counter == actions.size()) + { + std::vector boundary_vector; + std::vector pair_boundary_vector; + + for (const auto * const action : actions) + for (const auto j : index_range(action->_boundary_pairs)) + { + boundary_vector.push_back(action->_boundary_pairs[j].second); + pair_boundary_vector.push_back(action->_boundary_pairs[j].first); + } + + InputParameters params = _factory.getValidParams("ContactPressureAux"); + params.applyParameters(parameters(), {"order"}); + + std::vector displacements = getParam>("displacements"); + const auto order = + _problem->systemBaseNonlinear().system().variable_type(displacements[0]).order.get_order(); + + params.set("order") = Utility::enum_to_string(OrderWrapper{order}); + params.set>("boundary") = boundary_vector; + params.set>("paired_boundary") = pair_boundary_vector; + params.set("variable") = "contact_pressure"; + params.addRequiredCoupledVar("nodal_area", "The nodal area"); + params.set>("nodal_area") = {"nodal_area"}; + params.set("use_displaced_mesh") = true; + + std::string name = _name + "_contact_pressure"; + params.set("execute_on", + true) = {EXEC_NONLINEAR, EXEC_TIMESTEP_END, EXEC_TIMESTEP_BEGIN}; + _problem->addAuxKernel("ContactPressureAux", name, params); + } +} + +void +ExplicitDynamicsContactAction::addNodeFaceContact() +{ + if (_current_task != "add_constraint") + return; + + std::string action_name = MooseUtils::shortName(name()); + std::vector displacements = getParam>("displacements"); + const unsigned int ndisp = displacements.size(); + + std::string constraint_type; + + constraint_type = "ExplicitDynamicsContactConstraint"; + + InputParameters params = _factory.getValidParams(constraint_type); + + params.applyParameters(parameters(), + {"displacements", + "secondary_gap_offset", + "mapped_primary_gap_offset", + "primary", + "secondary"}); + + const auto order = + _problem->systemBaseNonlinear().system().variable_type(displacements[0]).order.get_order(); + + params.set>("displacements") = displacements; + params.set("use_displaced_mesh") = true; + params.set("order") = Utility::enum_to_string(OrderWrapper{order}); + + for (const auto & contact_pair : _boundary_pairs) + { + params.set>("nodal_area") = {"nodal_area"}; + params.set("boundary") = contact_pair.first; + if (isParamValid("secondary_gap_offset")) + params.set>("secondary_gap_offset") = { + getParam("secondary_gap_offset")}; + if (isParamValid("mapped_primary_gap_offset")) + params.set>("mapped_primary_gap_offset") = { + getParam("mapped_primary_gap_offset")}; + + for (unsigned int i = 0; i < ndisp; ++i) + { + std::string name = action_name + "_constraint_" + Moose::stringify(contact_pair, "_") + "_" + + Moose::stringify(i); + + params.set("component") = i; + + params.set("primary") = contact_pair.first; + params.set("secondary") = contact_pair.second; + params.set("variable") = displacements[i]; + params.set>("primary_variable") = {displacements[i]}; + params.applySpecificParameters(parameters(), + {"extra_vector_tags", "absolute_value_vector_tags"}); + _problem->addConstraint(constraint_type, name, params); + } + } +} + +MooseEnum +ExplicitDynamicsContactAction::getModelEnum() +{ + return MooseEnum("frictionless", "frictionless"); +} + +InputParameters +ExplicitDynamicsContactAction::commonParameters() +{ + InputParameters params = emptyInputParameters(); + + params.addParam( + "model", ExplicitDynamicsContactAction::getModelEnum(), "The contact model to use"); + + return params; +} diff --git a/modules/contact/src/base/ContactApp.C b/modules/contact/src/base/ContactApp.C index 392018048d8a..fc6d705ecfec 100644 --- a/modules/contact/src/base/ContactApp.C +++ b/modules/contact/src/base/ContactApp.C @@ -37,6 +37,7 @@ static void associateSyntaxInner(Syntax & syntax, ActionFactory & /*action_factory*/) { registerSyntax("ContactAction", "Contact/*"); + registerSyntax("ExplicitDynamicsContactAction", "ExplicitDynamicsContact/*"); registerTask("output_penetration_info_vars", false); registerTask("add_contact_aux_variable", false); diff --git a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C new file mode 100644 index 000000000000..b3263a304b6d --- /dev/null +++ b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C @@ -0,0 +1,404 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +// MOOSE includes +#include "ExplicitDynamicsContactConstraint.h" +#include "FEProblem.h" +#include "DisplacedProblem.h" +#include "AuxiliarySystem.h" +#include "PenetrationLocator.h" +#include "NearestNodeLocator.h" +#include "SystemBase.h" +#include "Assembly.h" +#include "MooseMesh.h" +#include "MathUtils.h" +#include "Executioner.h" +#include "AddVariableAction.h" +#include "ContactLineSearchBase.h" +#include "ExplicitDynamicsContactAction.h" + +#include "libmesh/string_to_enum.h" +#include "libmesh/sparse_matrix.h" + +registerMooseObject("ContactApp", ExplicitDynamicsContactConstraint); + +const unsigned int ExplicitDynamicsContactConstraint::_no_iterations = 0; + +InputParameters +ExplicitDynamicsContactConstraint::validParams() +{ + InputParameters params = NodeFaceConstraint::validParams(); + params += ExplicitDynamicsContactAction::commonParameters(); + + params.addRequiredParam("boundary", "The primary boundary"); + params.addParam("secondary", "The secondary boundary"); + params.addRequiredParam("component", + "An integer corresponding to the direction " + "the variable this constraint acts on. (0 for x, " + "1 for y, 2 for z)"); + + params.addCoupledVar( + "displacements", + "The displacements appropriate for the simulation geometry and coordinate system"); + + params.addCoupledVar("secondary_gap_offset", "offset to the gap distance from secondary side"); + params.addCoupledVar("mapped_primary_gap_offset", + "offset to the gap distance mapped from primary side"); + params.addRequiredCoupledVar("nodal_area", "The nodal area"); + + params.set("use_displaced_mesh") = true; + params.addParam( + "penalty", + 1e8, + "The penalty to apply. This can vary depending on the stiffness of your materials"); + params.addParam("penalty_multiplier", + 1.0, + "The growth factor for the penalty applied at the end of each augmented " + "Lagrange update iteration"); + params.addParam("friction_coefficient", 0, "The friction coefficient"); + params.addParam("tangential_tolerance", + "Tangential distance to extend edges of contact surfaces"); + params.addParam( + "capture_tolerance", 0, "Normal distance from surface within which nodes are captured"); + + params.addParam("tension_release", + 0.0, + "Tension release threshold. A node in contact " + "will not be released if its tensile load is below " + "this value. No tension release if negative."); + + params.addParam( + "normalize_penalty", + false, + "Whether to normalize the penalty parameter with the nodal area for penalty contact."); + params.addParam("stick_lock_iterations", + std::numeric_limits::max(), + "Number of times permitted to switch between sticking and slipping " + "in a solution before locking node in a sticked state."); + params.addParam("stick_unlock_factor", + 1.5, + "Factor by which frictional capacity must be " + "exceeded to permit stick-locked node to slip " + "again."); + params.addParam( + "print_contact_nodes", false, "Whether to print the number of nodes in contact."); + + params.addClassDescription( + "Apply non-penetration constraints on the mechanical deformation " + "using a node on face, primary/secondary algorithm, and multiple options " + "for the physical behavior on the interface and the mathematical " + "formulation for constraint enforcement"); + + return params; +} + +Threads::spin_mutex ExplicitDynamicsContactConstraint::_contact_set_mutex; + +ExplicitDynamicsContactConstraint::ExplicitDynamicsContactConstraint( + const InputParameters & parameters) + : NodeFaceConstraint(parameters), + _displaced_problem(parameters.get("_fe_problem_base")->getDisplacedProblem()), + _component(getParam("component")), + _model(getParam("model").getEnum()), + _normalize_penalty(getParam("normalize_penalty")), + _tension_release(getParam("tension_release")), + _capture_tolerance(getParam("capture_tolerance")), + _stick_lock_iterations(getParam("stick_lock_iterations")), + _stick_unlock_factor(getParam("stick_unlock_factor")), + _update_stateful_data(true), + _residual_copy(_sys.residualGhosted()), + _mesh_dimension(_mesh.dimension()), + _vars(3, libMesh::invalid_uint), + _var_objects(3, nullptr), + _has_secondary_gap_offset(isCoupled("secondary_gap_offset")), + _secondary_gap_offset_var(_has_secondary_gap_offset ? getVar("secondary_gap_offset", 0) + : nullptr), + _has_mapped_primary_gap_offset(isCoupled("mapped_primary_gap_offset")), + _mapped_primary_gap_offset_var( + _has_mapped_primary_gap_offset ? getVar("mapped_primary_gap_offset", 0) : nullptr), + _nodal_area_var(getVar("nodal_area", 0)), + _aux_system(_nodal_area_var->sys()), + _aux_solution(_aux_system.currentSolution()), + _contact_linesearch(dynamic_cast(_subproblem.getLineSearch())), + _print_contact_nodes(getParam("print_contact_nodes")) +{ + _overwrite_secondary_residual = false; + + if (isParamValid("displacements")) + { + // modern parameter scheme for displacements + for (unsigned int i = 0; i < coupledComponents("displacements"); ++i) + { + _vars[i] = coupled("displacements", i); + _var_objects[i] = getVar("displacements", i); + } + } + + mooseInfo("This is the constructor of the explicit dynamics contact constraint."); + + if (parameters.isParamValid("tangential_tolerance")) + _penetration_locator.setTangentialTolerance(getParam("tangential_tolerance")); + + if (parameters.isParamValid("normal_smoothing_distance")) + _penetration_locator.setNormalSmoothingDistance(getParam("normal_smoothing_distance")); + + if (parameters.isParamValid("normal_smoothing_method")) + _penetration_locator.setNormalSmoothingMethod( + parameters.get("normal_smoothing_method")); +} + +void +ExplicitDynamicsContactConstraint::timestepSetup() +{ + if (_component == 0) + { + updateContactStatefulData(/* beginning_of_step = */ true); + _update_stateful_data = false; + + if (_contact_linesearch) + _contact_linesearch->reset(); + } +} + +void +ExplicitDynamicsContactConstraint::updateContactStatefulData(bool beginning_of_step) +{ + for (auto & [secondary_node_num, pinfo] : _penetration_locator._penetration_info) + { + if (!pinfo) + continue; + + const Node & node = _mesh.nodeRef(secondary_node_num); + if (node.n_comp(_sys.number(), _vars[_component]) < 1) + continue; + + if (beginning_of_step) + { + if (_app.getExecutioner()->lastSolveConverged()) + { + pinfo->_contact_force_old = pinfo->_contact_force; + pinfo->_accumulated_slip_old = pinfo->_accumulated_slip; + pinfo->_frictional_energy_old = pinfo->_frictional_energy; + pinfo->_mech_status_old = pinfo->_mech_status; + } + else if (pinfo->_mech_status_old == PenetrationInfo::MS_NO_CONTACT && + pinfo->_mech_status != PenetrationInfo::MS_NO_CONTACT) + { + // The penetration info object could be based on a bad state so delete it + delete pinfo; + pinfo = nullptr; + continue; + } + + pinfo->_locked_this_step = 0; + pinfo->_stick_locked_this_step = 0; + pinfo->_starting_elem = pinfo->_elem; + pinfo->_starting_side_num = pinfo->_side_num; + pinfo->_starting_closest_point_ref = pinfo->_closest_point_ref; + } + pinfo->_incremental_slip_prev_iter = pinfo->_incremental_slip; + } +} + +bool +ExplicitDynamicsContactConstraint::shouldApply() +{ + bool in_contact = false; + + std::map::iterator found = + _penetration_locator._penetration_info.find(_current_node->id()); + if (found != _penetration_locator._penetration_info.end()) + { + PenetrationInfo * pinfo = found->second; + if (pinfo != nullptr) + { + // This computes the contact force once per constraint, rather than once per quad point + // and for both primary and secondary cases. + if (_component == 0) + computeContactForce(*_current_node, pinfo, true); + + if (pinfo->isCaptured()) + { + in_contact = true; + + Threads::spin_mutex::scoped_lock lock(_contact_set_mutex); + _current_contact_state.insert(_current_node->id()); + } + } + } + + return in_contact; +} + +void +ExplicitDynamicsContactConstraint::computeContactForce(const Node & node, + PenetrationInfo * pinfo, + bool update_contact_set) +{ + // Build up residual vector + RealVectorValue res_vec; + for (unsigned int i = 0; i < _mesh_dimension; ++i) + { + dof_id_type dof_number = node.dof_number(0, _vars[i], 0); + res_vec(i) = _residual_copy(dof_number) / _var_objects[i]->scalingFactor(); + } + + RealVectorValue distance_vec(node - pinfo->_closest_point); + if (distance_vec.norm() != 0) + distance_vec += gapOffset(node) * pinfo->_normal * distance_vec.unit() * distance_vec.unit(); + + const Real gap_size = -1.0 * pinfo->_normal * distance_vec; + + // This is for preventing an increment of pinfo->_locked_this_step for nodes that are + // captured and released in this function + bool newly_captured = false; + + // Capture nodes that are newly in contact + if (update_contact_set && !pinfo->isCaptured() && + MooseUtils::absoluteFuzzyGreaterEqual(gap_size, 0.0, _capture_tolerance)) + { + newly_captured = true; + pinfo->capture(); + + // Increment the lock count every time the node comes back into contact from not being in + // contact. + ++pinfo->_locked_this_step; + } + + if (!pinfo->isCaptured()) + return; + + const Real penalty = getPenalty(node); + + RealVectorValue pen_force(penalty * distance_vec); + switch (_model) + { + case ExplicitDynamicsContactModel::FRICTIONLESS: + // pinfo->_contact_force = -pinfo->_normal * (pinfo->_normal * res_vec); + pinfo->_contact_force = pinfo->_normal * (pinfo->_normal * pen_force); + break; + + default: + mooseError("Invalid or unavailable contact model"); + break; + } + + // Release (TODO: Revise lines until end of routine) + if (update_contact_set && pinfo->isCaptured() && !newly_captured && _tension_release >= 0.0 && + (_contact_linesearch ? true : pinfo->_locked_this_step < 2)) + { + const Real contact_pressure = -(pinfo->_normal * pinfo->_contact_force) / nodalArea(node); + if (-contact_pressure >= _tension_release) + { + pinfo->release(); + pinfo->_contact_force.zero(); + } + } +} + +Real +ExplicitDynamicsContactConstraint::computeQpSecondaryValue() +{ + return _u_secondary[_qp]; +} + +Real +ExplicitDynamicsContactConstraint::computeQpResidual(Moose::ConstraintType type) +{ + PenetrationInfo * pinfo = _penetration_locator._penetration_info[_current_node->id()]; + Real resid = pinfo->_contact_force(_component); + switch (type) + { + case Moose::Secondary: + return _test_secondary[_i][_qp] * resid; + + case Moose::Primary: + return _test_primary[_i][_qp] * -resid; + } + + return 0.0; +} + +Real +ExplicitDynamicsContactConstraint::gapOffset(const Node & node) +{ + Real val = 0; + + if (_has_secondary_gap_offset) + val += _secondary_gap_offset_var->getNodalValue(node); + + if (_has_mapped_primary_gap_offset) + val += _mapped_primary_gap_offset_var->getNodalValue(node); + + return val; +} + +Real +ExplicitDynamicsContactConstraint::nodalArea(const Node & node) +{ + dof_id_type dof = node.dof_number(_aux_system.number(), _nodal_area_var->number(), 0); + + Real area = (*_aux_solution)(dof); + if (area == 0.0) + { + if (_t_step > 1) + mooseError("Zero nodal area found"); + else + area = 1.0; // Avoid divide by zero during initialization + } + + return area; +} + +Real +ExplicitDynamicsContactConstraint::getPenalty(const Node & /*node*/) +{ + return 1.0e3; +} + +bool +ExplicitDynamicsContactConstraint::getCoupledVarComponent(unsigned int var_num, + unsigned int & component) +{ + component = std::numeric_limits::max(); + bool coupled_var_is_disp_var = false; + for (const auto i : make_range(Moose::dim)) + { + if (var_num == _vars[i]) + { + coupled_var_is_disp_var = true; + component = i; + break; + } + } + + return coupled_var_is_disp_var; +} + +void +ExplicitDynamicsContactConstraint::residualEnd() +{ + if (_component == 0 && (_print_contact_nodes || _contact_linesearch)) + { + _communicator.set_union(_current_contact_state); + if (_print_contact_nodes) + { + if (_current_contact_state == _old_contact_state) + _console << "Unchanged contact state. " << _current_contact_state.size() + << " nodes in contact.\n"; + else + _console << "Changed contact state. " << _current_contact_state.size() + << " nodes in contact.\n"; + } + if (_contact_linesearch) + _contact_linesearch->insertSet(_current_contact_state); + _old_contact_state.swap(_current_contact_state); + _current_contact_state.clear(); + } +} From 29c256d3842fa52b9a8d3d6284d255d09cc08c46 Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Thu, 19 Oct 2023 16:08:58 -0600 Subject: [PATCH 02/31] Add wave speed as a material property --- .../include/materials/WaveSpeed.h | 39 ++++++++++++++++ .../solid_mechanics/src/materials/WaveSpeed.C | 45 +++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 modules/solid_mechanics/include/materials/WaveSpeed.h create mode 100644 modules/solid_mechanics/src/materials/WaveSpeed.C diff --git a/modules/solid_mechanics/include/materials/WaveSpeed.h b/modules/solid_mechanics/include/materials/WaveSpeed.h new file mode 100644 index 000000000000..83d959a7e698 --- /dev/null +++ b/modules/solid_mechanics/include/materials/WaveSpeed.h @@ -0,0 +1,39 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#pragma once + +#include "Material.h" + +/** + * This material computes the wave speed for dynamic simulations using + * the Young's modulus (or equivalent metric) and the density. + * This object can be used, for example, in contact algorithms for + * explicit dynamics. + */ +class WaveSpeed : public Material +{ +public: + static InputParameters validParams(); + + WaveSpeed(const InputParameters & parameters); + +protected: + virtual void computeQpProperties(); + +private: + /// The wave speed material generated here + MaterialProperty & _wave_speed; + + /// Density of the material + const MaterialProperty & _material_density; + + /// Effective stiffness of element: function of material properties and element size + const MaterialProperty & _effective_stiffness; +}; diff --git a/modules/solid_mechanics/src/materials/WaveSpeed.C b/modules/solid_mechanics/src/materials/WaveSpeed.C new file mode 100644 index 000000000000..dce67514b129 --- /dev/null +++ b/modules/solid_mechanics/src/materials/WaveSpeed.C @@ -0,0 +1,45 @@ +//* This file is part of the MOOSE framework +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#include "WaveSpeed.h" +#include "libmesh/utility.h" +#include "RankTwoTensor.h" +#include "RankFourTensor.h" +#include "SymmetricRankTwoTensor.h" +#include "SymmetricRankFourTensor.h" + +registerMooseObject("SolidMechanicsApp", WaveSpeed); + +InputParameters +WaveSpeed::validParams() +{ + InputParameters params = Material::validParams(); + params.addParam("base_name", + "Optional parameter that allows the user to define " + "multiple mechanics material systems on the same " + "block, i.e. for multiple phases"); + + return params; +} + +WaveSpeed::WaveSpeed(const InputParameters & parameters) + : Material(parameters), + _wave_speed(declareProperty("wave_speed")), + _material_density(getMaterialPropertyByName("density")), + _effective_stiffness(getMaterialPropertyByName("effective_stiffness")) +{ +} + +void +WaveSpeed::computeQpProperties() +{ + // Effective stiffness is sqrt(equivalent_youngs_modulus) + _wave_speed[_qp] = _effective_stiffness[_qp] / std::sqrt(_material_density[_qp]); +} From bb206bcb9bfe69eab6cfdc415a94f5112ea839e6 Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Thu, 19 Oct 2023 17:54:49 -0600 Subject: [PATCH 03/31] WIP iterative contact dynamics --- .../actions/ExplicitDynamicsContactAction.h | 7 +- .../ExplicitDynamicsContactConstraint.h | 33 ++++++--- .../ExplicitDynamicsContactConstraint.C | 74 ++++++------------- 3 files changed, 48 insertions(+), 66 deletions(-) diff --git a/modules/contact/include/actions/ExplicitDynamicsContactAction.h b/modules/contact/include/actions/ExplicitDynamicsContactAction.h index 4b8af4bf90c5..b8dc8fe2c458 100644 --- a/modules/contact/include/actions/ExplicitDynamicsContactAction.h +++ b/modules/contact/include/actions/ExplicitDynamicsContactAction.h @@ -13,9 +13,9 @@ #include "MooseTypes.h" #include "MooseEnum.h" -enum class ExplicitDynamicsContactModel -{ - FRICTIONLESS +enum class ExplicitDynamicsContactModel { + FRICTIONLESS, + FRICTIONLESS_ITERATION }; /** @@ -23,6 +23,7 @@ enum class ExplicitDynamicsContactModel * contact. */ class ExplicitDynamicsContactAction : public Action + { public: static InputParameters validParams(); diff --git a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h index 7794d8263440..d8b293d7e1d0 100644 --- a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h +++ b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h @@ -12,16 +12,16 @@ // MOOSE includes #include "NodeFaceConstraint.h" #include "PenetrationLocator.h" +#include "TwoMaterialPropertyInterface.h" // Forward Declarations -class ContactLineSearchBase; enum class ExplicitDynamicsContactModel; /** - * A MechanicalContactConstraint forces the value of a variable to be the same on both sides of an - * interface. + * A ExplicitDynamicsContactConstraint does mechanical contact for explicit dynamics simulations. */ -class ExplicitDynamicsContactConstraint : public NodeFaceConstraint +class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, + public TwoMaterialPropertyInterface { public: static InputParameters validParams(); @@ -80,6 +80,22 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint virtual bool addCouplingEntriesToJacobian() override { return false; } protected: + /** + * Determine "Lagrange multipliers" from the iterative solution of the impact problem. + * @param node The number of the variable to be checked + * @param pinfo The component index computed in this routine + */ + void solveImpactEquations(const Node & node, PenetrationInfo * pinfo); + + const std::set getBoundaryIDs() const + { + std::set result; + result.insert(_primary); + result.insert(_secondary); + + return result; + } + MooseSharedPointer _displaced_problem; Real gapOffset(const Node & node); Real nodalArea(const Node & node); @@ -91,19 +107,13 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint const Real _tension_release; const Real _capture_tolerance; - const unsigned int _stick_lock_iterations; - const Real _stick_unlock_factor; bool _update_stateful_data; - NumericVector & _residual_copy; - // std::map _point_to_info; - const unsigned int _mesh_dimension; std::vector _vars; std::vector _var_objects; - /// gap offset from either secondary, primary or both const bool _has_secondary_gap_offset; const MooseVariable * const _secondary_gap_offset_var; const bool _has_mapped_primary_gap_offset; @@ -113,7 +123,6 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint SystemBase & _aux_system; const NumericVector * const _aux_solution; - ContactLineSearchBase * _contact_linesearch; std::set _current_contact_state; std::set _old_contact_state; @@ -121,4 +130,6 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint static Threads::spin_mutex _contact_set_mutex; const static unsigned int _no_iterations; + + const MaterialProperty & _wave_speed; }; diff --git a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C index b3263a304b6d..8e93bd7a7d80 100644 --- a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C +++ b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C @@ -35,6 +35,7 @@ ExplicitDynamicsContactConstraint::validParams() { InputParameters params = NodeFaceConstraint::validParams(); params += ExplicitDynamicsContactAction::commonParameters(); + params += TwoMaterialPropertyInterface::validParams(); params.addRequiredParam("boundary", "The primary boundary"); params.addParam("secondary", "The secondary boundary"); @@ -42,59 +43,41 @@ ExplicitDynamicsContactConstraint::validParams() "An integer corresponding to the direction " "the variable this constraint acts on. (0 for x, " "1 for y, 2 for z)"); - params.addCoupledVar( "displacements", "The displacements appropriate for the simulation geometry and coordinate system"); - params.addCoupledVar("secondary_gap_offset", "offset to the gap distance from secondary side"); params.addCoupledVar("mapped_primary_gap_offset", "offset to the gap distance mapped from primary side"); params.addRequiredCoupledVar("nodal_area", "The nodal area"); - params.set("use_displaced_mesh") = true; params.addParam( "penalty", 1e8, "The penalty to apply. This can vary depending on the stiffness of your materials"); - params.addParam("penalty_multiplier", - 1.0, - "The growth factor for the penalty applied at the end of each augmented " - "Lagrange update iteration"); params.addParam("friction_coefficient", 0, "The friction coefficient"); params.addParam("tangential_tolerance", "Tangential distance to extend edges of contact surfaces"); params.addParam( "capture_tolerance", 0, "Normal distance from surface within which nodes are captured"); - params.addParam("tension_release", 0.0, "Tension release threshold. A node in contact " "will not be released if its tensile load is below " "this value. No tension release if negative."); - params.addParam( "normalize_penalty", false, "Whether to normalize the penalty parameter with the nodal area for penalty contact."); - params.addParam("stick_lock_iterations", - std::numeric_limits::max(), - "Number of times permitted to switch between sticking and slipping " - "in a solution before locking node in a sticked state."); - params.addParam("stick_unlock_factor", - 1.5, - "Factor by which frictional capacity must be " - "exceeded to permit stick-locked node to slip " - "again."); params.addParam( "print_contact_nodes", false, "Whether to print the number of nodes in contact."); - params.addClassDescription( "Apply non-penetration constraints on the mechanical deformation " "using a node on face, primary/secondary algorithm, and multiple options " "for the physical behavior on the interface and the mathematical " "formulation for constraint enforcement"); - + params.addParam( + "wave_speed", 0.0, "The wave speed used to solve the impact problem node-wise."); return params; } @@ -103,16 +86,14 @@ Threads::spin_mutex ExplicitDynamicsContactConstraint::_contact_set_mutex; ExplicitDynamicsContactConstraint::ExplicitDynamicsContactConstraint( const InputParameters & parameters) : NodeFaceConstraint(parameters), + TwoMaterialPropertyInterface(this, Moose::EMPTY_BLOCK_IDS, getBoundaryIDs()), _displaced_problem(parameters.get("_fe_problem_base")->getDisplacedProblem()), _component(getParam("component")), _model(getParam("model").getEnum()), _normalize_penalty(getParam("normalize_penalty")), _tension_release(getParam("tension_release")), _capture_tolerance(getParam("capture_tolerance")), - _stick_lock_iterations(getParam("stick_lock_iterations")), - _stick_unlock_factor(getParam("stick_unlock_factor")), _update_stateful_data(true), - _residual_copy(_sys.residualGhosted()), _mesh_dimension(_mesh.dimension()), _vars(3, libMesh::invalid_uint), _var_objects(3, nullptr), @@ -125,8 +106,8 @@ ExplicitDynamicsContactConstraint::ExplicitDynamicsContactConstraint( _nodal_area_var(getVar("nodal_area", 0)), _aux_system(_nodal_area_var->sys()), _aux_solution(_aux_system.currentSolution()), - _contact_linesearch(dynamic_cast(_subproblem.getLineSearch())), - _print_contact_nodes(getParam("print_contact_nodes")) + _print_contact_nodes(getParam("print_contact_nodes")), + _wave_speed(getMaterialProperty("wave_speed")) { _overwrite_secondary_residual = false; @@ -160,9 +141,6 @@ ExplicitDynamicsContactConstraint::timestepSetup() { updateContactStatefulData(/* beginning_of_step = */ true); _update_stateful_data = false; - - if (_contact_linesearch) - _contact_linesearch->reset(); } } @@ -190,14 +168,13 @@ ExplicitDynamicsContactConstraint::updateContactStatefulData(bool beginning_of_s else if (pinfo->_mech_status_old == PenetrationInfo::MS_NO_CONTACT && pinfo->_mech_status != PenetrationInfo::MS_NO_CONTACT) { + mooseWarning("Previous step did not converge. Check results"); // The penetration info object could be based on a bad state so delete it - delete pinfo; - pinfo = nullptr; - continue; + // delete pinfo; + // pinfo = nullptr; + // continue; } - pinfo->_locked_this_step = 0; - pinfo->_stick_locked_this_step = 0; pinfo->_starting_elem = pinfo->_elem; pinfo->_starting_side_num = pinfo->_side_num; pinfo->_starting_closest_point_ref = pinfo->_closest_point_ref; @@ -241,14 +218,6 @@ ExplicitDynamicsContactConstraint::computeContactForce(const Node & node, PenetrationInfo * pinfo, bool update_contact_set) { - // Build up residual vector - RealVectorValue res_vec; - for (unsigned int i = 0; i < _mesh_dimension; ++i) - { - dof_id_type dof_number = node.dof_number(0, _vars[i], 0); - res_vec(i) = _residual_copy(dof_number) / _var_objects[i]->scalingFactor(); - } - RealVectorValue distance_vec(node - pinfo->_closest_point); if (distance_vec.norm() != 0) distance_vec += gapOffset(node) * pinfo->_normal * distance_vec.unit() * distance_vec.unit(); @@ -265,10 +234,6 @@ ExplicitDynamicsContactConstraint::computeContactForce(const Node & node, { newly_captured = true; pinfo->capture(); - - // Increment the lock count every time the node comes back into contact from not being in - // contact. - ++pinfo->_locked_this_step; } if (!pinfo->isCaptured()) @@ -283,15 +248,15 @@ ExplicitDynamicsContactConstraint::computeContactForce(const Node & node, // pinfo->_contact_force = -pinfo->_normal * (pinfo->_normal * res_vec); pinfo->_contact_force = pinfo->_normal * (pinfo->_normal * pen_force); break; - + case ExplicitDynamicsContactModel::FRICTIONLESS_ITERATION: + solveImpactEquations(node, pinfo); + break; default: mooseError("Invalid or unavailable contact model"); break; } - // Release (TODO: Revise lines until end of routine) - if (update_contact_set && pinfo->isCaptured() && !newly_captured && _tension_release >= 0.0 && - (_contact_linesearch ? true : pinfo->_locked_this_step < 2)) + if (update_contact_set && pinfo->isCaptured() && !newly_captured && _tension_release >= 0.0) { const Real contact_pressure = -(pinfo->_normal * pinfo->_contact_force) / nodalArea(node); if (-contact_pressure >= _tension_release) @@ -302,6 +267,12 @@ ExplicitDynamicsContactConstraint::computeContactForce(const Node & node, } } +void +ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & /*node*/, + PenetrationInfo * /*pinfo*/) +{ +} + Real ExplicitDynamicsContactConstraint::computeQpSecondaryValue() { @@ -384,7 +355,7 @@ ExplicitDynamicsContactConstraint::getCoupledVarComponent(unsigned int var_num, void ExplicitDynamicsContactConstraint::residualEnd() { - if (_component == 0 && (_print_contact_nodes || _contact_linesearch)) + if (_component == 0 && (_print_contact_nodes)) { _communicator.set_union(_current_contact_state); if (_print_contact_nodes) @@ -396,8 +367,7 @@ ExplicitDynamicsContactConstraint::residualEnd() _console << "Changed contact state. " << _current_contact_state.size() << " nodes in contact.\n"; } - if (_contact_linesearch) - _contact_linesearch->insertSet(_current_contact_state); + _old_contact_state.swap(_current_contact_state); _current_contact_state.clear(); } From d47cecdb89258f417f68bc068c08011523635f36 Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Wed, 8 Nov 2023 13:37:21 -0700 Subject: [PATCH 04/31] Update calls to system base with new API --- .../actions/ExplicitDynamicsContactAction.C | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/modules/contact/src/actions/ExplicitDynamicsContactAction.C b/modules/contact/src/actions/ExplicitDynamicsContactAction.C index 3b8d980d711e..b783fdb11c20 100644 --- a/modules/contact/src/actions/ExplicitDynamicsContactAction.C +++ b/modules/contact/src/actions/ExplicitDynamicsContactAction.C @@ -121,7 +121,7 @@ ExplicitDynamicsContactAction::act() std::vector displacements = getParam>("displacements"); - const auto order = _problem->systemBaseNonlinear() + const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0) .system() .variable_type(displacements[0]) .order.get_order(); @@ -150,8 +150,10 @@ ExplicitDynamicsContactAction::act() if (_current_task == "add_contact_aux_variable") { std::vector displacements = getParam>("displacements"); - const auto order = - _problem->systemBaseNonlinear().system().variable_type(displacements[0]).order.get_order(); + const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0) + .system() + .variable_type(displacements[0]) + .order.get_order(); // Add ContactPenetrationVarAction { auto var_params = _factory.getValidParams("MooseVariable"); @@ -231,8 +233,10 @@ ExplicitDynamicsContactAction::addContactPressureAuxKernel() params.applyParameters(parameters(), {"order"}); std::vector displacements = getParam>("displacements"); - const auto order = - _problem->systemBaseNonlinear().system().variable_type(displacements[0]).order.get_order(); + const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0) + .system() + .variable_type(displacements[0]) + .order.get_order(); params.set("order") = Utility::enum_to_string(OrderWrapper{order}); params.set>("boundary") = boundary_vector; @@ -272,8 +276,10 @@ ExplicitDynamicsContactAction::addNodeFaceContact() "primary", "secondary"}); - const auto order = - _problem->systemBaseNonlinear().system().variable_type(displacements[0]).order.get_order(); + const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0) + .system() + .variable_type(displacements[0]) + .order.get_order(); params.set>("displacements") = displacements; params.set("use_displaced_mesh") = true; From 47dfe7b98ec181e88717a60a847965da4e92ae8c Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Fri, 10 Nov 2023 13:03:25 -0700 Subject: [PATCH 05/31] Neighbor material in node-face explicit dynamics constraint --- framework/src/systems/NonlinearSystemBase.C | 25 +++++ .../contact/explicit_dynamics/first_test.i | 30 ++--- .../explicit_dynamics/gold/first_test_out.csv | 68 +++++------ .../ExplicitDynamicsContactConstraint.h | 16 ++- .../actions/ExplicitDynamicsContactAction.C | 106 +++++++++++++++--- .../ExplicitDynamicsContactConstraint.C | 25 ++++- 6 files changed, 191 insertions(+), 79 deletions(-) diff --git a/framework/src/systems/NonlinearSystemBase.C b/framework/src/systems/NonlinearSystemBase.C index 64eb081cce82..353980b96e69 100644 --- a/framework/src/systems/NonlinearSystemBase.C +++ b/framework/src/systems/NonlinearSystemBase.C @@ -1137,6 +1137,14 @@ NonlinearSystemBase::setConstraintSecondaryValues(NumericVector & soluti // reinit variables on the primary element's face at the contact point _fe_problem.setNeighborSubdomainID(primary_elem, 0); subproblem.reinitNeighborPhys(primary_elem, primary_side, points, 0); + // _fe_problem.reinitNeighbor(primary_elem, primary_side, 0); + _fe_problem.reinitNeighborFaceRef( + primary_elem, primary_side, primary_boundary, TOLERANCE, &points, nullptr, 0); + + // Two material interface relies on reiniting materials boundary + // _fe_problem.reinitMaterialsBoundary(primary_boundary, 0); + _fe_problem.reinitMaterialsNeighbor(primary_elem->subdomain_id(), 0); + // _fe_problem.reinitMaterialsInterface(primary_boundary, 0); for (const auto & nfc : constraints) { @@ -1287,6 +1295,14 @@ NonlinearSystemBase::constraintResiduals(NumericVector & residual, bool // reinit variables on the primary element's face at the contact point _fe_problem.setNeighborSubdomainID(primary_elem, 0); subproblem.reinitNeighborPhys(primary_elem, primary_side, points, 0); + // _fe_problem.reinitNeighbor(primary_elem, primary_side, 0); + _fe_problem.reinitNeighborFaceRef( + primary_elem, primary_side, primary_boundary, TOLERANCE, &points, nullptr, 0); + + // Two material interface relies on reiniting materials boundary + _fe_problem.reinitMaterialsBoundary(primary_boundary, 0); + _fe_problem.reinitMaterialsNeighbor(primary_elem->subdomain_id(), 0); + // _fe_problem.reinitMaterialsInterface(primary_boundary, 0); for (const auto & nfc : constraints) { @@ -2181,6 +2197,15 @@ NonlinearSystemBase::constraintJacobians(bool displaced) // reinit variables on the primary element's face at the contact point _fe_problem.setNeighborSubdomainID(primary_elem, 0); subproblem.reinitNeighborPhys(primary_elem, primary_side, points, 0); + // _fe_problem.reinitNeighbor(primary_elem, primary_side, 0); + _fe_problem.reinitNeighborFaceRef( + primary_elem, primary_side, primary_boundary, TOLERANCE, &points, nullptr, 0); + + // Two material interface relies on reiniting materials boundary + _fe_problem.reinitMaterialsBoundary(primary_boundary, 0); + _fe_problem.reinitMaterialsNeighbor(primary_elem->subdomain_id(), 0); + // _fe_problem.reinitMaterialsInterface(primary_boundary, 0); + for (const auto & nfc : constraints) { // Return if this constraint does not correspond to the primary-secondary pair diff --git a/modules/contact/explicit_dynamics/first_test.i b/modules/contact/explicit_dynamics/first_test.i index c7a25fc38e33..e66349967b5c 100644 --- a/modules/contact/explicit_dynamics/first_test.i +++ b/modules/contact/explicit_dynamics/first_test.i @@ -78,20 +78,20 @@ [] [AuxKernels] - [stress_zz] - type = RankTwoAux - rank_two_tensor = stress - index_i = 2 - index_j = 2 - variable = stress_zz - [] - [strain_zz] - type = RankTwoAux - rank_two_tensor = elastic_strain - index_i = 2 - index_j = 2 - variable = strain_zz - [] + # [stress_zz] + # type = RankTwoAux + # rank_two_tensor = stress + # index_i = 2 + # index_j = 2 + # variable = stress_zz + # [] + # [strain_zz] + # type = RankTwoAux + # rank_two_tensor = elastic_strain + # index_i = 2 + # index_j = 2 + # variable = strain_zz + # [] [accel_x] type = TestNewmarkTI variable = accel_x @@ -132,7 +132,7 @@ displacements = 'disp_x disp_y disp_z' volumetric_locking_correction = true stiffness_damping_coefficient = 0.04 - # generate_output = 'stress_zz strain_zz' + #generate_output = 'stress_zz strain_zz' [] [inertia_x] type = InertialForce diff --git a/modules/contact/explicit_dynamics/gold/first_test_out.csv b/modules/contact/explicit_dynamics/gold/first_test_out.csv index 8faf552b51fa..1ad910c48d74 100644 --- a/modules/contact/explicit_dynamics/gold/first_test_out.csv +++ b/modules/contact/explicit_dynamics/gold/first_test_out.csv @@ -18,37 +18,37 @@ time,accel_58z,contact_pressure_max,critical_time_step,disp_58z,vel_58z 0.07,0.0011639123044733,0,0.1,-9.8317762390858e-05,-0.0013811184141766 0.075,0.0012207589208208,0,0.1,-0.0001051463506409,-0.0013751567361134 0.08,0.0012774377604406,0.082341610254479,0.1,-0.00011194159031113,-0.0013689112444103 -0.085,0.0013339431927144,0.19101404878775,0.1,-0.00011870207077947,-0.0013623827920274 -0.09,0.0013903680754374,0.29901128340337,0.1,-0.000125426380983,-0.001355572013857 -0.095,0.0014468105927516,0.40624378043303,0.1,-0.00013211310720354,-0.0013484790671865 -0.1,0.0015033593191535,0.51262279737801,0.1,-0.0001387608308863,-0.0013411036424068 -0.105,0.0015601015116037,0.61806047588701,0.1,-0.00014536812639074,-0.0013334449903299 -0.11,0.0016171271325011,0.72246991323486,0.1,-0.0001519335590144,-0.0013255019187196 -0.115,0.001674515230965,0.82576527398727,0.1,-0.00015845568288214,-0.0013172728128109 -0.12,0.0017323502374635,0.92786186707797,0.1,-0.00016493303893838,-0.0013087556491399 -0.125,0.0017907124595868,1.0286762300661,0.1,-0.00017136415299712,-0.0012999479923972 -0.13,0.0018496798999412,1.1281262115668,0.1,-0.00017774753385423,-0.0012908470114984 -0.135,0.0019093280652566,1.2261310518263,0.1,-0.00018408167146683,-0.0012814494915854 -0.14,0.001969729780531,1.3226114614042,0.1,-0.00019036503520421,-0.001271751846971 -0.145,0.0020309550085944,1.4174896979197,0.1,-0.00019659607217471,-0.0012617501349981 -0.15,0.0020930706754688,1.5106896408102,0.1,-0.00020277320563266,-0.001251440070788 -0.155,0.0021561405017217,1.602136864045,0.1,-0.00020889483346961,-0.001240817042845 -0.16,0.0022202248402281,1.6917587067307,0.1,-0.00021495932679354,-0.0012298761294901 -0.165,0.0022853805205843,1.7794843415407,0.1,-0.00022096502859997,-0.0012186121160881 -0.17,0.002351660700413,1.8652448408947,0.1,-0.00022691025253829,-0.0012070195130356 -0.175,0.0024191147238834,1.9489732408148,0.1,-0.00023279328177693,-0.0011950925744749 -0.18,0.0024877879876864,2.0306046023756,0.1,-0.00023861236797019,-0.0011828253176959 -0.185,0.0025577218146179,2.1100760706723,0.1,-0.00024436573033008,-0.0011702115431902 -0.19,0.0026289533351361,2.1873269312209,0.1,-0.00025005155480555,-0.0011572448553158 -0.195,0.0027015153769366,2.2622986637121,0.1,-0.00025566799337195,-0.0011439186835356 -0.2,0.0027754363628673,2.3349349930359,0.1,-0.00026121316343291,-0.0011302263041861 -0.205,0.0028507402172954,2.4051819374974,0.1,-0.00026668514733684,-0.0011161608627357 -0.21,0.0029274462810618,2.4729878541477,0.1,-0.0002720819920099,-0.0011017153964898 -0.215,0.0030055692352613,2.5383034811523,0.1,-0.00027740170870711,-0.001086882857699 -0.22,0.0030851190338586,2.6010819771283,0.1,-0.00028264227288318,-0.0010716561370262 -0.225,0.003166100845367,2.6612789573813,0.1,-0.00028780162418416,-0.0010560280873281 -0.23,0.0032485150035862,2.71885252698,0.1,-0.00029287766656096,-0.0010399915477058 -0.235,0.0033323569676,2.7737633106092,0.1,-0.00029786826850548,-0.0010235393677778 -0.24,0.0034176172909284,2.8259744791508,0.1,-0.00030277126341,-0.0010066644321315 -0.245,0.0035042816000971,2.8754517729438,0.1,-0.00030758445004995,-0.0009893596849039 -0.25,0.0035923305824689,2.9221635216841,0.1,-0.00031230559319032,-0.00097161815444751 +0.085,0.0013339431927144,0.19101387324202,0.1,-0.00011870207078074,-0.0013623827920274 +0.09,0.0013903680245986,0.29901054033757,0.1,-0.00012542638098813,-0.0013555720139841 +0.095,0.0014468104891853,0.40624188233201,0.1,-0.0001321131072124,-0.0013484790676996 +0.1,0.0015033593243614,0.51261898060212,0.1,-0.00013876083088694,-0.0013411036431658 +0.105,0.0015601019895492,0.6180538180028,0.1,-0.00014536812645012,-0.001333444989881 +0.11,0.0016171244542279,0.72245936777424,0.1,-0.00015193355923856,-0.0013255019237715 +0.115,0.0016745109896756,0.82574968049562,0.1,-0.00015845568342789,-0.0013172728351618 +0.12,0.0017323439642806,0.92783996910971,0.1,-0.00016493304002615,-0.0013087556977769 +0.125,0.0017907036428894,1.0286466920336,0.1,-0.00017136415492464,-0.001299948078759 +0.13,0.0018496679904682,1.1280876343615,0.1,-0.00017774753701113,-0.0012908471496756 +0.135,0.0019093124805714,1.2260819871573,0.1,-0.00018408167634989,-0.001281449698498 +0.14,0.0019697099088594,1.3225504248213,0.1,-0.00019036504243335,-0.0012717521425244 +0.145,0.0020309302120678,1.4174151805125,0.1,-0.00019659608250949,-0.0012617505422221 +0.15,0.0020930402927933,1.5106001195908,0.1,-0.00020277321998937,-0.0012514406159599 +0.155,0.0021561038503095,1.6020308110413,0.1,-0.0002088948529388,-0.0012408177556022 +0.16,0.0022201812178383,1.6916345968322,0.1,-0.00021495935265808,-0.0012298770429318 +0.165,0.002285329206499,1.7793406591494,0.1,-0.00022096506235345,-0.001218613266871 +0.17,0.0023516009562059,1.8650800854465,0.1,-0.00022691029590397,-0.0012070209414642 +0.175,0.0024190457938264,1.9487859312431,0.1,-0.00023279333672702,-0.0011950943245891 +0.18,0.0024877090988292,2.0303932805973,0.1,-0.00023861243674566,-0.0011828274373575 +0.185,0.0025576321765986,2.1098393041781,0.1,-0.0002443658154608,-0.0011702140841689 +0.19,0.0026288521397656,2.1870633148575,0.1,-0.000250051659131,-0.001157247873378 +0.195,0.0027014017975942,2.2620068207421,0.1,-0.00025566812006236,-0.0011439222385346 +0.2,0.0027753095537485,2.334613575563,0.1,-0.00026121331601091,-0.0011302304601562 +0.205,0.0028505993125661,2.4048296263426,0.1,-0.0002666853296996,-0.0011161656879905 +0.21,0.0029272903939357,2.4726033582571,0.1,-0.00027208220845188,-0.0011017209637242 +0.215,0.0030053974570555,2.5378855366174,0.1,-0.00027740196394332,-0.0010868892440967 +0.22,0.0030849304330396,2.6006293458916,0.1,-0.0002826425720731,-0.0010716634243715 +0.225,0.0031658944666291,2.6607904256972,0.1,-0.0002878019729562,-0.0010560363621223 +0.23,0.0032482898669819,2.7183269036938,0.1,-0.00029287807103761,-0.0010400009012883 +0.235,0.0033321120677422,2.7731994253133,0.1,-0.00029786873532912,-0.0010235498964515 +0.24,0.0034173515962963,2.8253711802679,0.1,-0.00030277179976931,-0.0010066762372914 +0.245,0.0035039940524356,2.8748079257844,0.1,-0.00030758506370709,-0.00098937287316955 +0.25,0.0035920200963356,2.9214780065164,0.1,-0.00031230629250873,-0.00097163283779764 diff --git a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h index d8b293d7e1d0..7bb199bb8e8d 100644 --- a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h +++ b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h @@ -13,7 +13,6 @@ #include "NodeFaceConstraint.h" #include "PenetrationLocator.h" #include "TwoMaterialPropertyInterface.h" - // Forward Declarations enum class ExplicitDynamicsContactModel; @@ -87,14 +86,10 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, */ void solveImpactEquations(const Node & node, PenetrationInfo * pinfo); - const std::set getBoundaryIDs() const - { - std::set result; - result.insert(_primary); - result.insert(_secondary); + /// the union of the secondary and primary boundary ids + std::set _boundary_ids; - return result; - } + const std::set & getBoundaryIDs(); MooseSharedPointer _displaced_problem; Real gapOffset(const Node & node); @@ -120,6 +115,9 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, const MooseVariable * const _mapped_primary_gap_offset_var; MooseVariable * _nodal_area_var; + const MooseVariable * _nodal_density_var; + const MooseVariable * _nodal_wave_speed_var; + SystemBase & _aux_system; const NumericVector * const _aux_solution; @@ -131,5 +129,5 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, const static unsigned int _no_iterations; - const MaterialProperty & _wave_speed; + const MaterialProperty & _neighbor_density; }; diff --git a/modules/contact/src/actions/ExplicitDynamicsContactAction.C b/modules/contact/src/actions/ExplicitDynamicsContactAction.C index b783fdb11c20..ed0feb34b98d 100644 --- a/modules/contact/src/actions/ExplicitDynamicsContactAction.C +++ b/modules/contact/src/actions/ExplicitDynamicsContactAction.C @@ -178,32 +178,101 @@ ExplicitDynamicsContactAction::act() _problem->addAuxVariable("MooseVariable", "nodal_area", var_params); } + // Add nodal density variable + { + auto var_params = _factory.getValidParams("MooseVariable"); + var_params.set("order") = Utility::enum_to_string(OrderWrapper{order}); + var_params.set("family") = "LAGRANGE"; + + _problem->addAuxVariable("MooseVariable", "nodal_density", var_params); + } + // Add nodal wave speed + { + auto var_params = _factory.getValidParams("MooseVariable"); + var_params.set("order") = Utility::enum_to_string(OrderWrapper{order}); + var_params.set("family") = "LAGRANGE"; + + _problem->addAuxVariable("MooseVariable", "nodal_wave_speed", var_params); + } } if (_current_task == "add_user_object") { - auto var_params = _factory.getValidParams("NodalArea"); + { + auto var_params = _factory.getValidParams("NodalArea"); - // Get secondary_boundary_vector from possibly updated set from the - // ContactAction constructor cleanup - const auto actions = _awh.getActions(); + // Get secondary_boundary_vector from possibly updated set from the + // ContactAction constructor cleanup + const auto actions = _awh.getActions(); - std::vector secondary_boundary_vector; - for (const auto * const action : actions) - for (const auto j : index_range(action->_boundary_pairs)) - secondary_boundary_vector.push_back(action->_boundary_pairs[j].second); + std::vector secondary_boundary_vector; + for (const auto * const action : actions) + for (const auto j : index_range(action->_boundary_pairs)) + secondary_boundary_vector.push_back(action->_boundary_pairs[j].second); - var_params.set>("boundary") = secondary_boundary_vector; - var_params.set>("variable") = {"nodal_area"}; + var_params.set>("boundary") = secondary_boundary_vector; + var_params.set>("variable") = {"nodal_area"}; - mooseAssert(_problem, "Problem pointer is NULL"); - var_params.set("execute_on", true) = {EXEC_INITIAL, EXEC_TIMESTEP_BEGIN}; - var_params.set("use_displaced_mesh") = true; + mooseAssert(_problem, "Problem pointer is NULL"); + var_params.set("execute_on", true) = {EXEC_INITIAL, EXEC_TIMESTEP_BEGIN}; + var_params.set("use_displaced_mesh") = true; - _problem->addUserObject("NodalArea", - "nodal_area_object_" + - Moose::stringify(ed_contact_userobject_counter++), - var_params); + _problem->addUserObject("NodalArea", + "nodal_area_object_" + + Moose::stringify(ed_contact_userobject_counter), + var_params); + } + // Add nodal density and nodal wave speed user + { + // Add nodal density + auto var_params = _factory.getValidParams("NodalDensity"); + + // Get secondary_boundary_vector from possibly updated set from the + // ContactAction constructor cleanup + const auto actions = _awh.getActions(); + + std::vector secondary_boundary_vector; + for (const auto * const action : actions) + for (const auto j : index_range(action->_boundary_pairs)) + secondary_boundary_vector.push_back(action->_boundary_pairs[j].second); + + var_params.set>("boundary") = secondary_boundary_vector; + var_params.set>("variable") = {"nodal_density"}; + + mooseAssert(_problem, "Problem pointer is NULL"); + var_params.set("execute_on", true) = {EXEC_INITIAL, EXEC_TIMESTEP_BEGIN}; + var_params.set("use_displaced_mesh") = true; + + _problem->addUserObject("NodalDensity", + "nodal_density_object_" + + Moose::stringify(ed_contact_userobject_counter), + var_params); + } + { + // Add wave speed + auto var_params = _factory.getValidParams("NodalWaveSpeed"); + + // Get secondary_boundary_vector from possibly updated set from the + // ContactAction constructor cleanup + const auto actions = _awh.getActions(); + + std::vector secondary_boundary_vector; + for (const auto * const action : actions) + for (const auto j : index_range(action->_boundary_pairs)) + secondary_boundary_vector.push_back(action->_boundary_pairs[j].second); + + var_params.set>("boundary") = secondary_boundary_vector; + var_params.set>("variable") = {"nodal_wave_speed"}; + + mooseAssert(_problem, "Problem pointer is NULL"); + var_params.set("execute_on", true) = {EXEC_INITIAL, EXEC_TIMESTEP_BEGIN}; + var_params.set("use_displaced_mesh") = true; + + _problem->addUserObject("NodalWaveSpeed", + "nodal_wavespeed_object_" + + Moose::stringify(ed_contact_userobject_counter++), + var_params); + } } } @@ -288,6 +357,9 @@ ExplicitDynamicsContactAction::addNodeFaceContact() for (const auto & contact_pair : _boundary_pairs) { params.set>("nodal_area") = {"nodal_area"}; + params.set>("nodal_density") = {"nodal_density"}; + params.set>("nodal_wave_speed") = {"nodal_wave_speed"}; + params.set("boundary") = contact_pair.first; if (isParamValid("secondary_gap_offset")) params.set>("secondary_gap_offset") = { diff --git a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C index 8e93bd7a7d80..6bcd2c5fd4ff 100644 --- a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C +++ b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C @@ -49,7 +49,9 @@ ExplicitDynamicsContactConstraint::validParams() params.addCoupledVar("secondary_gap_offset", "offset to the gap distance from secondary side"); params.addCoupledVar("mapped_primary_gap_offset", "offset to the gap distance mapped from primary side"); - params.addRequiredCoupledVar("nodal_area", "The nodal area"); + params.addRequiredCoupledVar("nodal_area", "The nodal area."); + params.addRequiredCoupledVar("nodal_density", "The nodal density."); + params.addRequiredCoupledVar("nodal_wave_speed", "The nodal wave speed."); params.set("use_displaced_mesh") = true; params.addParam( "penalty", @@ -104,10 +106,12 @@ ExplicitDynamicsContactConstraint::ExplicitDynamicsContactConstraint( _mapped_primary_gap_offset_var( _has_mapped_primary_gap_offset ? getVar("mapped_primary_gap_offset", 0) : nullptr), _nodal_area_var(getVar("nodal_area", 0)), + _nodal_density_var(getVar("nodal_density", 0)), + _nodal_wave_speed_var(getVar("nodal_wave_speed", 0)), _aux_system(_nodal_area_var->sys()), _aux_solution(_aux_system.currentSolution()), _print_contact_nodes(getParam("print_contact_nodes")), - _wave_speed(getMaterialProperty("wave_speed")) + _neighbor_density(getNeighborMaterialPropertyByName("density")) { _overwrite_secondary_residual = false; @@ -142,6 +146,8 @@ ExplicitDynamicsContactConstraint::timestepSetup() updateContactStatefulData(/* beginning_of_step = */ true); _update_stateful_data = false; } + + Moose::out << "Quadrature point in time step setup: " << _qp << "\n"; } void @@ -268,9 +274,11 @@ ExplicitDynamicsContactConstraint::computeContactForce(const Node & node, } void -ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & /*node*/, - PenetrationInfo * /*pinfo*/) +ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & node, PenetrationInfo * pinfo) { + const auto nodal_area = nodalArea(node); + + pinfo->_contact_force = pinfo->_normal * (pinfo->_normal * nodal_area); } Real @@ -372,3 +380,12 @@ ExplicitDynamicsContactConstraint::residualEnd() _current_contact_state.clear(); } } + +const std::set & +ExplicitDynamicsContactConstraint::getBoundaryIDs() +{ + _boundary_ids.clear(); + _boundary_ids.insert(_primary); + _boundary_ids.insert(_secondary); + return _boundary_ids; +} From 835e4045f0683a8c28c2f6041a32ac7c401b99f2 Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Mon, 13 Nov 2023 16:03:42 -0700 Subject: [PATCH 06/31] Add necessary material properties --- .../include/userobjects/NodalDensity.h | 44 +++++++ .../include/userobjects/NodalWaveSpeed.h | 44 +++++++ .../contact/src/userobjects/NodalDensity.C | 114 ++++++++++++++++++ .../contact/src/userobjects/NodalWaveSpeed.C | 112 +++++++++++++++++ 4 files changed, 314 insertions(+) create mode 100644 modules/contact/include/userobjects/NodalDensity.h create mode 100644 modules/contact/include/userobjects/NodalWaveSpeed.h create mode 100644 modules/contact/src/userobjects/NodalDensity.C create mode 100644 modules/contact/src/userobjects/NodalWaveSpeed.C diff --git a/modules/contact/include/userobjects/NodalDensity.h b/modules/contact/include/userobjects/NodalDensity.h new file mode 100644 index 000000000000..81d884c67381 --- /dev/null +++ b/modules/contact/include/userobjects/NodalDensity.h @@ -0,0 +1,44 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#pragma once + +#include "SideIntegralVariableUserObject.h" + +class NodalDensity : public SideIntegralVariableUserObject +{ +public: + static InputParameters validParams(); + + NodalDensity(const InputParameters & parameters); + virtual ~NodalDensity(); + + virtual void threadJoin(const UserObject & uo); + + virtual void initialize(); + virtual void execute(); + virtual void finalize(); + + Real nodalDensity(const Node * node) const; + +protected: + virtual Real computeQpIntegral(); + + std::map _node_densities; + + std::map _commMap; + std::vector _commVec; + + const VariablePhiValue & _phi; + + SystemBase & _system; + NumericVector & _aux_solution; + + const MaterialProperty & _density; +}; diff --git a/modules/contact/include/userobjects/NodalWaveSpeed.h b/modules/contact/include/userobjects/NodalWaveSpeed.h new file mode 100644 index 000000000000..ea22aec5abf3 --- /dev/null +++ b/modules/contact/include/userobjects/NodalWaveSpeed.h @@ -0,0 +1,44 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#pragma once + +#include "SideIntegralVariableUserObject.h" + +class NodalWaveSpeed : public SideIntegralVariableUserObject +{ +public: + static InputParameters validParams(); + + NodalWaveSpeed(const InputParameters & parameters); + virtual ~NodalWaveSpeed(); + + virtual void threadJoin(const UserObject & uo); + + virtual void initialize(); + virtual void execute(); + virtual void finalize(); + + Real nodalWaveSpeed(const Node * node) const; + +protected: + virtual Real computeQpIntegral(); + + std::map _node_wave_speeds; + + std::map _commMap; + std::vector _commVec; + + const VariablePhiValue & _phi; + + SystemBase & _system; + NumericVector & _aux_solution; + + const MaterialProperty & _wave_speed; +}; diff --git a/modules/contact/src/userobjects/NodalDensity.C b/modules/contact/src/userobjects/NodalDensity.C new file mode 100644 index 000000000000..ebac64fe4fd6 --- /dev/null +++ b/modules/contact/src/userobjects/NodalDensity.C @@ -0,0 +1,114 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#include "NodalDensity.h" + +// MOOSE includes +#include "MooseVariable.h" +#include "SystemBase.h" + +#include "libmesh/numeric_vector.h" +#include "libmesh/quadrature.h" + +registerMooseObject("ContactApp", NodalDensity); + +InputParameters +NodalDensity::validParams() +{ + InputParameters params = SideIntegralVariableUserObject::validParams(); + params.set("execute_on") = EXEC_LINEAR; + params.addClassDescription("Compute the tributary densities for nodes on a surface"); + return params; +} + +NodalDensity::NodalDensity(const InputParameters & parameters) + : SideIntegralVariableUserObject(parameters), + _phi(_variable->phiFace()), + _system(_variable->sys()), + _aux_solution(_system.solution()), + _density(getMaterialProperty("density")) +{ +} + +NodalDensity::~NodalDensity() {} + +void +NodalDensity::threadJoin(const UserObject & fred) +{ + const NodalDensity & na = dynamic_cast(fred); + + std::map::const_iterator it = na._node_densities.begin(); + const std::map::const_iterator it_end = na._node_densities.end(); + for (; it != it_end; ++it) + { + _node_densities[it->first] += it->second; + } +} + +Real +NodalDensity::computeQpIntegral() +{ + return 1; +} + +void +NodalDensity::initialize() +{ + _node_densities.clear(); +} + +void +NodalDensity::execute() +{ + std::vector node_densities(_phi.size()); + for (unsigned qp(0); qp < _qrule->n_points(); ++qp) + for (unsigned j(0); j < _phi.size(); ++j) + node_densities[j] += (_phi[j][qp] * _density[qp]); + + for (unsigned j(0); j < _phi.size(); ++j) + { + const Real density = node_densities[j]; + if (density != 0) + _node_densities[_current_elem->node_ptr(j)] += density; + } +} + +void +NodalDensity::finalize() +{ + + const std::map::iterator it_end = _node_densities.end(); + for (std::map::iterator it = _node_densities.begin(); it != it_end; ++it) + { + const Node * const node = it->first; + dof_id_type dof = node->dof_number(_system.number(), _variable->number(), 0); + _aux_solution.set(dof, 0); + } + _aux_solution.close(); + + for (std::map::iterator it = _node_densities.begin(); it != it_end; ++it) + { + const Node * const node = it->first; + dof_id_type dof = node->dof_number(_system.number(), _variable->number(), 0); + _aux_solution.add(dof, it->second); + } + _aux_solution.close(); +} + +Real +NodalDensity::nodalDensity(const Node * node) const +{ + std::map::const_iterator it = _node_densities.find(node); + Real retVal(0); + if (it != _node_densities.end()) + { + retVal = it->second; + } + return retVal; +} diff --git a/modules/contact/src/userobjects/NodalWaveSpeed.C b/modules/contact/src/userobjects/NodalWaveSpeed.C new file mode 100644 index 000000000000..ce61d2843a1c --- /dev/null +++ b/modules/contact/src/userobjects/NodalWaveSpeed.C @@ -0,0 +1,112 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#include "NodalWaveSpeed.h" + +// MOOSE includes +#include "MooseVariable.h" +#include "SystemBase.h" + +#include "libmesh/numeric_vector.h" +#include "libmesh/quadrature.h" + +registerMooseObject("ContactApp", NodalWaveSpeed); + +InputParameters +NodalWaveSpeed::validParams() +{ + InputParameters params = SideIntegralVariableUserObject::validParams(); + params.set("execute_on") = EXEC_LINEAR; + params.addClassDescription("Compute the tributary wave speeds for nodes on a surface"); + return params; +} + +NodalWaveSpeed::NodalWaveSpeed(const InputParameters & parameters) + : SideIntegralVariableUserObject(parameters), + _phi(_variable->phiFace()), + _system(_variable->sys()), + _aux_solution(_system.solution()), + _wave_speed(getMaterialProperty("wave_speed")) +{ +} + +NodalWaveSpeed::~NodalWaveSpeed() {} + +void +NodalWaveSpeed::threadJoin(const UserObject & fred) +{ + const NodalWaveSpeed & na = dynamic_cast(fred); + + std::map::const_iterator it = na._node_wave_speeds.begin(); + const std::map::const_iterator it_end = na._node_wave_speeds.end(); + for (; it != it_end; ++it) + _node_wave_speeds[it->first] += it->second; +} + +Real +NodalWaveSpeed::computeQpIntegral() +{ + return 1; +} + +void +NodalWaveSpeed::initialize() +{ + _node_wave_speeds.clear(); +} + +void +NodalWaveSpeed::execute() +{ + std::vector node_wave_speeds(_phi.size()); + for (unsigned qp(0); qp < _qrule->n_points(); ++qp) + for (unsigned j(0); j < _phi.size(); ++j) + node_wave_speeds[j] += (_phi[j][qp] * _wave_speed[qp]); + + for (unsigned j(0); j < _phi.size(); ++j) + { + const Real wave_speed = node_wave_speeds[j]; + if (wave_speed != 0) + _node_wave_speeds[_current_elem->node_ptr(j)] += wave_speed; + } +} + +void +NodalWaveSpeed::finalize() +{ + + const std::map::iterator it_end = _node_wave_speeds.end(); + for (std::map::iterator it = _node_wave_speeds.begin(); it != it_end; ++it) + { + const Node * const node = it->first; + dof_id_type dof = node->dof_number(_system.number(), _variable->number(), 0); + _aux_solution.set(dof, 0); + } + _aux_solution.close(); + + for (std::map::iterator it = _node_wave_speeds.begin(); it != it_end; ++it) + { + const Node * const node = it->first; + dof_id_type dof = node->dof_number(_system.number(), _variable->number(), 0); + _aux_solution.add(dof, it->second); + } + _aux_solution.close(); +} + +Real +NodalWaveSpeed::nodalWaveSpeed(const Node * node) const +{ + std::map::const_iterator it = _node_wave_speeds.find(node); + Real retVal(0); + if (it != _node_wave_speeds.end()) + { + retVal = it->second; + } + return retVal; +} From cb98b48fe2aa5c7098cac13349c3c14a1448614e Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Mon, 13 Nov 2023 16:24:41 -0800 Subject: [PATCH 07/31] Mods to avoid OOB access --- framework/include/constraints/NodeFaceConstraint.h | 11 +++++++++++ framework/src/systems/NonlinearSystemBase.C | 14 +++++++++++--- .../ExplicitDynamicsContactConstraint.h | 8 ++++++++ .../ExplicitDynamicsContactConstraint.C | 2 ++ 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/framework/include/constraints/NodeFaceConstraint.h b/framework/include/constraints/NodeFaceConstraint.h index 2c5b4fdccb5c..620d558dd536 100644 --- a/framework/include/constraints/NodeFaceConstraint.h +++ b/framework/include/constraints/NodeFaceConstraint.h @@ -115,6 +115,8 @@ class NodeFaceConstraint : public Constraint, void residualSetup() override; + virtual const std::set & getMatPropDependencies() const; + protected: /** * Compute the value the secondary node should have at the beginning of a timestep. @@ -287,6 +289,9 @@ class NodeFaceConstraint : public Constraint, /// The value of the secondary residual Real _secondary_residual; + /// An empty material property dependency set for use with \p getMatPropDependencies + const std::set _empty_mat_prop_deps; + public: std::vector _connected_dof_indices; @@ -310,3 +315,9 @@ class NodeFaceConstraint : public Constraint, /// one associated with interior physics DenseMatrix _Ken; }; + +inline const std::set & +NodeFaceConstraint::getMatPropDependencies() const +{ + return _empty_mat_prop_deps; +} diff --git a/framework/src/systems/NonlinearSystemBase.C b/framework/src/systems/NonlinearSystemBase.C index 353980b96e69..87addda8035a 100644 --- a/framework/src/systems/NonlinearSystemBase.C +++ b/framework/src/systems/NonlinearSystemBase.C @@ -1111,6 +1111,13 @@ NonlinearSystemBase::setConstraintSecondaryValues(NumericVector & soluti { const auto & constraints = _constraints.getActiveNodeFaceConstraints(secondary_boundary, displaced); + std::set needed_mat_props; + for (const auto & constraint : constraints) + { + const auto & mp_deps = constraint->getMatPropDependencies(); + needed_mat_props.insert(mp_deps.begin(), mp_deps.end()); + } + _fe_problem.setActiveMaterialProperties(needed_mat_props, /*tid=*/0); for (unsigned int i = 0; i < secondary_nodes.size(); i++) { @@ -1136,10 +1143,11 @@ NonlinearSystemBase::setConstraintSecondaryValues(NumericVector & soluti // reinit variables on the primary element's face at the contact point _fe_problem.setNeighborSubdomainID(primary_elem, 0); - subproblem.reinitNeighborPhys(primary_elem, primary_side, points, 0); + _fe_problem.reinitNeighborPhys( + _mesh.elemPtr(primary_elem->id()), primary_side, points, 0); // _fe_problem.reinitNeighbor(primary_elem, primary_side, 0); - _fe_problem.reinitNeighborFaceRef( - primary_elem, primary_side, primary_boundary, TOLERANCE, &points, nullptr, 0); + // _fe_problem.reinitNeighborFaceRef( + // primary_elem, primary_side, primary_boundary, TOLERANCE, &points, nullptr, 0); // Two material interface relies on reiniting materials boundary // _fe_problem.reinitMaterialsBoundary(primary_boundary, 0); diff --git a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h index 7bb199bb8e8d..45b9e2c7c21b 100644 --- a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h +++ b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h @@ -78,6 +78,8 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, */ virtual bool addCouplingEntriesToJacobian() override { return false; } + virtual const std::set & getMatPropDependencies() const override; + protected: /** * Determine "Lagrange multipliers" from the iterative solution of the impact problem. @@ -131,3 +133,9 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, const MaterialProperty & _neighbor_density; }; + +inline const std::set & +ExplicitDynamicsContactConstraint::getMatPropDependencies() const +{ + return TwoMaterialPropertyInterface::getMatPropDependencies(); +} diff --git a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C index 6bcd2c5fd4ff..6e19c52320a3 100644 --- a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C +++ b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C @@ -201,6 +201,8 @@ ExplicitDynamicsContactConstraint::shouldApply() PenetrationInfo * pinfo = found->second; if (pinfo != nullptr) { + _neighbor_density[0]; + // This computes the contact force once per constraint, rather than once per quad point // and for both primary and secondary cases. if (_component == 0) From 7436d6a961dd034d2638a690f76195dff75077e7 Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Wed, 15 Nov 2023 14:54:37 -0700 Subject: [PATCH 08/31] Enable (hopefully) neighbor materials in explicit contact constraint --- .../include/constraints/NodeFaceConstraint.h | 5 ++ .../src/constraints/NodeFaceConstraint.C | 8 +++ framework/src/systems/NonlinearSystemBase.C | 26 ++----- .../explicit_dynamics/gold/first_test_out.csv | 68 +++++++++---------- .../actions/ExplicitDynamicsContactAction.h | 7 +- .../ExplicitDynamicsContactConstraint.h | 9 ++- .../ExplicitDynamicsContactConstraint.C | 24 ++----- 7 files changed, 66 insertions(+), 81 deletions(-) diff --git a/framework/include/constraints/NodeFaceConstraint.h b/framework/include/constraints/NodeFaceConstraint.h index 620d558dd536..b819cd619ff6 100644 --- a/framework/include/constraints/NodeFaceConstraint.h +++ b/framework/include/constraints/NodeFaceConstraint.h @@ -221,6 +221,8 @@ class NodeFaceConstraint : public Constraint, return coupledNeighborSecond(var_name, comp); } + const std::set & getBoundaryIDs(); + /// Boundary ID for the secondary surface unsigned int _secondary; /// Boundary ID for the primary surface @@ -231,6 +233,9 @@ class NodeFaceConstraint : public Constraint, const MooseArray & _primary_q_point; const QBase * const & _primary_qrule; + /// the union of the secondary and primary boundary ids + std::set _boundary_ids; + public: PenetrationLocator & _penetration_locator; diff --git a/framework/src/constraints/NodeFaceConstraint.C b/framework/src/constraints/NodeFaceConstraint.C index 0ed1340d4507..fc9152871d61 100644 --- a/framework/src/constraints/NodeFaceConstraint.C +++ b/framework/src/constraints/NodeFaceConstraint.C @@ -284,3 +284,11 @@ NodeFaceConstraint::overwriteSecondaryResidual() { return _overwrite_secondary_residual; } + +const std::set & +NodeFaceConstraint::getBoundaryIDs() +{ + _boundary_ids.insert(_primary); + _boundary_ids.insert(_secondary); + return _boundary_ids; +} diff --git a/framework/src/systems/NonlinearSystemBase.C b/framework/src/systems/NonlinearSystemBase.C index 87addda8035a..b185a12d0cd1 100644 --- a/framework/src/systems/NonlinearSystemBase.C +++ b/framework/src/systems/NonlinearSystemBase.C @@ -1143,16 +1143,10 @@ NonlinearSystemBase::setConstraintSecondaryValues(NumericVector & soluti // reinit variables on the primary element's face at the contact point _fe_problem.setNeighborSubdomainID(primary_elem, 0); + subproblem.reinitNeighborPhys(primary_elem, primary_side, points, 0); _fe_problem.reinitNeighborPhys( _mesh.elemPtr(primary_elem->id()), primary_side, points, 0); - // _fe_problem.reinitNeighbor(primary_elem, primary_side, 0); - // _fe_problem.reinitNeighborFaceRef( - // primary_elem, primary_side, primary_boundary, TOLERANCE, &points, nullptr, 0); - - // Two material interface relies on reiniting materials boundary - // _fe_problem.reinitMaterialsBoundary(primary_boundary, 0); _fe_problem.reinitMaterialsNeighbor(primary_elem->subdomain_id(), 0); - // _fe_problem.reinitMaterialsInterface(primary_boundary, 0); for (const auto & nfc : constraints) { @@ -1303,14 +1297,9 @@ NonlinearSystemBase::constraintResiduals(NumericVector & residual, bool // reinit variables on the primary element's face at the contact point _fe_problem.setNeighborSubdomainID(primary_elem, 0); subproblem.reinitNeighborPhys(primary_elem, primary_side, points, 0); - // _fe_problem.reinitNeighbor(primary_elem, primary_side, 0); - _fe_problem.reinitNeighborFaceRef( - primary_elem, primary_side, primary_boundary, TOLERANCE, &points, nullptr, 0); - - // Two material interface relies on reiniting materials boundary - _fe_problem.reinitMaterialsBoundary(primary_boundary, 0); + _fe_problem.reinitNeighborPhys( + _mesh.elemPtr(primary_elem->id()), primary_side, points, 0); _fe_problem.reinitMaterialsNeighbor(primary_elem->subdomain_id(), 0); - // _fe_problem.reinitMaterialsInterface(primary_boundary, 0); for (const auto & nfc : constraints) { @@ -2205,14 +2194,9 @@ NonlinearSystemBase::constraintJacobians(bool displaced) // reinit variables on the primary element's face at the contact point _fe_problem.setNeighborSubdomainID(primary_elem, 0); subproblem.reinitNeighborPhys(primary_elem, primary_side, points, 0); - // _fe_problem.reinitNeighbor(primary_elem, primary_side, 0); - _fe_problem.reinitNeighborFaceRef( - primary_elem, primary_side, primary_boundary, TOLERANCE, &points, nullptr, 0); - - // Two material interface relies on reiniting materials boundary - _fe_problem.reinitMaterialsBoundary(primary_boundary, 0); + _fe_problem.reinitNeighborPhys( + _mesh.elemPtr(primary_elem->id()), primary_side, points, 0); _fe_problem.reinitMaterialsNeighbor(primary_elem->subdomain_id(), 0); - // _fe_problem.reinitMaterialsInterface(primary_boundary, 0); for (const auto & nfc : constraints) { diff --git a/modules/contact/explicit_dynamics/gold/first_test_out.csv b/modules/contact/explicit_dynamics/gold/first_test_out.csv index 1ad910c48d74..8faf552b51fa 100644 --- a/modules/contact/explicit_dynamics/gold/first_test_out.csv +++ b/modules/contact/explicit_dynamics/gold/first_test_out.csv @@ -18,37 +18,37 @@ time,accel_58z,contact_pressure_max,critical_time_step,disp_58z,vel_58z 0.07,0.0011639123044733,0,0.1,-9.8317762390858e-05,-0.0013811184141766 0.075,0.0012207589208208,0,0.1,-0.0001051463506409,-0.0013751567361134 0.08,0.0012774377604406,0.082341610254479,0.1,-0.00011194159031113,-0.0013689112444103 -0.085,0.0013339431927144,0.19101387324202,0.1,-0.00011870207078074,-0.0013623827920274 -0.09,0.0013903680245986,0.29901054033757,0.1,-0.00012542638098813,-0.0013555720139841 -0.095,0.0014468104891853,0.40624188233201,0.1,-0.0001321131072124,-0.0013484790676996 -0.1,0.0015033593243614,0.51261898060212,0.1,-0.00013876083088694,-0.0013411036431658 -0.105,0.0015601019895492,0.6180538180028,0.1,-0.00014536812645012,-0.001333444989881 -0.11,0.0016171244542279,0.72245936777424,0.1,-0.00015193355923856,-0.0013255019237715 -0.115,0.0016745109896756,0.82574968049562,0.1,-0.00015845568342789,-0.0013172728351618 -0.12,0.0017323439642806,0.92783996910971,0.1,-0.00016493304002615,-0.0013087556977769 -0.125,0.0017907036428894,1.0286466920336,0.1,-0.00017136415492464,-0.001299948078759 -0.13,0.0018496679904682,1.1280876343615,0.1,-0.00017774753701113,-0.0012908471496756 -0.135,0.0019093124805714,1.2260819871573,0.1,-0.00018408167634989,-0.001281449698498 -0.14,0.0019697099088594,1.3225504248213,0.1,-0.00019036504243335,-0.0012717521425244 -0.145,0.0020309302120678,1.4174151805125,0.1,-0.00019659608250949,-0.0012617505422221 -0.15,0.0020930402927933,1.5106001195908,0.1,-0.00020277321998937,-0.0012514406159599 -0.155,0.0021561038503095,1.6020308110413,0.1,-0.0002088948529388,-0.0012408177556022 -0.16,0.0022201812178383,1.6916345968322,0.1,-0.00021495935265808,-0.0012298770429318 -0.165,0.002285329206499,1.7793406591494,0.1,-0.00022096506235345,-0.001218613266871 -0.17,0.0023516009562059,1.8650800854465,0.1,-0.00022691029590397,-0.0012070209414642 -0.175,0.0024190457938264,1.9487859312431,0.1,-0.00023279333672702,-0.0011950943245891 -0.18,0.0024877090988292,2.0303932805973,0.1,-0.00023861243674566,-0.0011828274373575 -0.185,0.0025576321765986,2.1098393041781,0.1,-0.0002443658154608,-0.0011702140841689 -0.19,0.0026288521397656,2.1870633148575,0.1,-0.000250051659131,-0.001157247873378 -0.195,0.0027014017975942,2.2620068207421,0.1,-0.00025566812006236,-0.0011439222385346 -0.2,0.0027753095537485,2.334613575563,0.1,-0.00026121331601091,-0.0011302304601562 -0.205,0.0028505993125661,2.4048296263426,0.1,-0.0002666853296996,-0.0011161656879905 -0.21,0.0029272903939357,2.4726033582571,0.1,-0.00027208220845188,-0.0011017209637242 -0.215,0.0030053974570555,2.5378855366174,0.1,-0.00027740196394332,-0.0010868892440967 -0.22,0.0030849304330396,2.6006293458916,0.1,-0.0002826425720731,-0.0010716634243715 -0.225,0.0031658944666291,2.6607904256972,0.1,-0.0002878019729562,-0.0010560363621223 -0.23,0.0032482898669819,2.7183269036938,0.1,-0.00029287807103761,-0.0010400009012883 -0.235,0.0033321120677422,2.7731994253133,0.1,-0.00029786873532912,-0.0010235498964515 -0.24,0.0034173515962963,2.8253711802679,0.1,-0.00030277179976931,-0.0010066762372914 -0.245,0.0035039940524356,2.8748079257844,0.1,-0.00030758506370709,-0.00098937287316955 -0.25,0.0035920200963356,2.9214780065164,0.1,-0.00031230629250873,-0.00097163283779764 +0.085,0.0013339431927144,0.19101404878775,0.1,-0.00011870207077947,-0.0013623827920274 +0.09,0.0013903680754374,0.29901128340337,0.1,-0.000125426380983,-0.001355572013857 +0.095,0.0014468105927516,0.40624378043303,0.1,-0.00013211310720354,-0.0013484790671865 +0.1,0.0015033593191535,0.51262279737801,0.1,-0.0001387608308863,-0.0013411036424068 +0.105,0.0015601015116037,0.61806047588701,0.1,-0.00014536812639074,-0.0013334449903299 +0.11,0.0016171271325011,0.72246991323486,0.1,-0.0001519335590144,-0.0013255019187196 +0.115,0.001674515230965,0.82576527398727,0.1,-0.00015845568288214,-0.0013172728128109 +0.12,0.0017323502374635,0.92786186707797,0.1,-0.00016493303893838,-0.0013087556491399 +0.125,0.0017907124595868,1.0286762300661,0.1,-0.00017136415299712,-0.0012999479923972 +0.13,0.0018496798999412,1.1281262115668,0.1,-0.00017774753385423,-0.0012908470114984 +0.135,0.0019093280652566,1.2261310518263,0.1,-0.00018408167146683,-0.0012814494915854 +0.14,0.001969729780531,1.3226114614042,0.1,-0.00019036503520421,-0.001271751846971 +0.145,0.0020309550085944,1.4174896979197,0.1,-0.00019659607217471,-0.0012617501349981 +0.15,0.0020930706754688,1.5106896408102,0.1,-0.00020277320563266,-0.001251440070788 +0.155,0.0021561405017217,1.602136864045,0.1,-0.00020889483346961,-0.001240817042845 +0.16,0.0022202248402281,1.6917587067307,0.1,-0.00021495932679354,-0.0012298761294901 +0.165,0.0022853805205843,1.7794843415407,0.1,-0.00022096502859997,-0.0012186121160881 +0.17,0.002351660700413,1.8652448408947,0.1,-0.00022691025253829,-0.0012070195130356 +0.175,0.0024191147238834,1.9489732408148,0.1,-0.00023279328177693,-0.0011950925744749 +0.18,0.0024877879876864,2.0306046023756,0.1,-0.00023861236797019,-0.0011828253176959 +0.185,0.0025577218146179,2.1100760706723,0.1,-0.00024436573033008,-0.0011702115431902 +0.19,0.0026289533351361,2.1873269312209,0.1,-0.00025005155480555,-0.0011572448553158 +0.195,0.0027015153769366,2.2622986637121,0.1,-0.00025566799337195,-0.0011439186835356 +0.2,0.0027754363628673,2.3349349930359,0.1,-0.00026121316343291,-0.0011302263041861 +0.205,0.0028507402172954,2.4051819374974,0.1,-0.00026668514733684,-0.0011161608627357 +0.21,0.0029274462810618,2.4729878541477,0.1,-0.0002720819920099,-0.0011017153964898 +0.215,0.0030055692352613,2.5383034811523,0.1,-0.00027740170870711,-0.001086882857699 +0.22,0.0030851190338586,2.6010819771283,0.1,-0.00028264227288318,-0.0010716561370262 +0.225,0.003166100845367,2.6612789573813,0.1,-0.00028780162418416,-0.0010560280873281 +0.23,0.0032485150035862,2.71885252698,0.1,-0.00029287766656096,-0.0010399915477058 +0.235,0.0033323569676,2.7737633106092,0.1,-0.00029786826850548,-0.0010235393677778 +0.24,0.0034176172909284,2.8259744791508,0.1,-0.00030277126341,-0.0010066644321315 +0.245,0.0035042816000971,2.8754517729438,0.1,-0.00030758445004995,-0.0009893596849039 +0.25,0.0035923305824689,2.9221635216841,0.1,-0.00031230559319032,-0.00097161815444751 diff --git a/modules/contact/include/actions/ExplicitDynamicsContactAction.h b/modules/contact/include/actions/ExplicitDynamicsContactAction.h index b8dc8fe2c458..78d171f30856 100644 --- a/modules/contact/include/actions/ExplicitDynamicsContactAction.h +++ b/modules/contact/include/actions/ExplicitDynamicsContactAction.h @@ -13,9 +13,10 @@ #include "MooseTypes.h" #include "MooseEnum.h" -enum class ExplicitDynamicsContactModel { - FRICTIONLESS, - FRICTIONLESS_ITERATION +enum class ExplicitDynamicsContactModel +{ + FRICTIONLESS, + FRICTIONLESS_BALANCE }; /** diff --git a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h index 45b9e2c7c21b..2ab768b160d7 100644 --- a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h +++ b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h @@ -88,11 +88,6 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, */ void solveImpactEquations(const Node & node, PenetrationInfo * pinfo); - /// the union of the secondary and primary boundary ids - std::set _boundary_ids; - - const std::set & getBoundaryIDs(); - MooseSharedPointer _displaced_problem; Real gapOffset(const Node & node); Real nodalArea(const Node & node); @@ -131,7 +126,11 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, const static unsigned int _no_iterations; + /// Density material for neighbor projection const MaterialProperty & _neighbor_density; + + /// Wave speed material for neighbor projection + const MaterialProperty & _neighbor_wave_speed; }; inline const std::set & diff --git a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C index 6e19c52320a3..4416d7ce5aef 100644 --- a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C +++ b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C @@ -78,8 +78,6 @@ ExplicitDynamicsContactConstraint::validParams() "using a node on face, primary/secondary algorithm, and multiple options " "for the physical behavior on the interface and the mathematical " "formulation for constraint enforcement"); - params.addParam( - "wave_speed", 0.0, "The wave speed used to solve the impact problem node-wise."); return params; } @@ -111,7 +109,8 @@ ExplicitDynamicsContactConstraint::ExplicitDynamicsContactConstraint( _aux_system(_nodal_area_var->sys()), _aux_solution(_aux_system.currentSolution()), _print_contact_nodes(getParam("print_contact_nodes")), - _neighbor_density(getNeighborMaterialPropertyByName("density")) + _neighbor_density(getNeighborMaterialPropertyByName("density")), + _neighbor_wave_speed(getNeighborMaterialPropertyByName("wave_speed")) { _overwrite_secondary_residual = false; @@ -146,8 +145,6 @@ ExplicitDynamicsContactConstraint::timestepSetup() updateContactStatefulData(/* beginning_of_step = */ true); _update_stateful_data = false; } - - Moose::out << "Quadrature point in time step setup: " << _qp << "\n"; } void @@ -201,8 +198,6 @@ ExplicitDynamicsContactConstraint::shouldApply() PenetrationInfo * pinfo = found->second; if (pinfo != nullptr) { - _neighbor_density[0]; - // This computes the contact force once per constraint, rather than once per quad point // and for both primary and secondary cases. if (_component == 0) @@ -253,10 +248,9 @@ ExplicitDynamicsContactConstraint::computeContactForce(const Node & node, switch (_model) { case ExplicitDynamicsContactModel::FRICTIONLESS: - // pinfo->_contact_force = -pinfo->_normal * (pinfo->_normal * res_vec); pinfo->_contact_force = pinfo->_normal * (pinfo->_normal * pen_force); break; - case ExplicitDynamicsContactModel::FRICTIONLESS_ITERATION: + case ExplicitDynamicsContactModel::FRICTIONLESS_BALANCE: solveImpactEquations(node, pinfo); break; default: @@ -278,6 +272,9 @@ ExplicitDynamicsContactConstraint::computeContactForce(const Node & node, void ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & node, PenetrationInfo * pinfo) { + // Stab at momentum balance, uncoupled normal pressure + // See Heinstein et al, 2000, Contact-impact modeling in explicit transient dynamics. + const auto nodal_area = nodalArea(node); pinfo->_contact_force = pinfo->_normal * (pinfo->_normal * nodal_area); @@ -382,12 +379,3 @@ ExplicitDynamicsContactConstraint::residualEnd() _current_contact_state.clear(); } } - -const std::set & -ExplicitDynamicsContactConstraint::getBoundaryIDs() -{ - _boundary_ids.clear(); - _boundary_ids.insert(_primary); - _boundary_ids.insert(_secondary); - return _boundary_ids; -} From f87ff84b166e528cf7e38ba7ad3079abce714c00 Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Thu, 16 Nov 2023 12:31:36 -0700 Subject: [PATCH 09/31] Implement momentum-balance explicit dynamics contact force --- .../ExplicitDynamicsContactConstraint.h | 14 +++++ .../actions/ExplicitDynamicsContactAction.C | 14 ++++- .../ExplicitDynamicsContactConstraint.C | 55 ++++++++++++++++++- 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h index 2ab768b160d7..0bee8cc8647f 100644 --- a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h +++ b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h @@ -131,6 +131,20 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, /// Wave speed material for neighbor projection const MaterialProperty & _neighbor_wave_speed; + + /// X component of velocity at the contacting node + const VariableValue & _vel_x; + /// Y component of velocity at the contacting node + const VariableValue & _vel_y; + /// Z component of velocity at the contacting node + const VariableValue & _vel_z; + + /// X component of velocity at the closest point + const VariableValue & _neighbor_vel_x; + /// Y component of velocity at the closest point + const VariableValue & _neighbor_vel_y; + /// Z component of velocity at the closest point + const VariableValue & _neighbor_vel_z; }; inline const std::set & diff --git a/modules/contact/src/actions/ExplicitDynamicsContactAction.C b/modules/contact/src/actions/ExplicitDynamicsContactAction.C index ed0feb34b98d..0805b215261c 100644 --- a/modules/contact/src/actions/ExplicitDynamicsContactAction.C +++ b/modules/contact/src/actions/ExplicitDynamicsContactAction.C @@ -360,6 +360,13 @@ ExplicitDynamicsContactAction::addNodeFaceContact() params.set>("nodal_density") = {"nodal_density"}; params.set>("nodal_wave_speed") = {"nodal_wave_speed"}; + if (isParamValid("vel_x")) + params.set>("vel_x") = getParam>("vel_x"); + if (isParamValid("vel_y")) + params.set>("vel_y") = getParam>("vel_y"); + if (isParamValid("vel_z")) + params.set>("vel_z") = getParam>("vel_z"); + params.set("boundary") = contact_pair.first; if (isParamValid("secondary_gap_offset")) params.set>("secondary_gap_offset") = { @@ -389,7 +396,7 @@ ExplicitDynamicsContactAction::addNodeFaceContact() MooseEnum ExplicitDynamicsContactAction::getModelEnum() { - return MooseEnum("frictionless", "frictionless"); + return MooseEnum("frictionless frictionless_balance", "frictionless"); } InputParameters @@ -400,5 +407,10 @@ ExplicitDynamicsContactAction::commonParameters() params.addParam( "model", ExplicitDynamicsContactAction::getModelEnum(), "The contact model to use"); + // Gap rate input + params.addCoupledVar("vel_x", "x-component of velocity"); + params.addCoupledVar("vel_y", "y-component of velocity"); + params.addCoupledVar("vel_z", "z-component of velocity"); + return params; } diff --git a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C index 4416d7ce5aef..50b31324e3e9 100644 --- a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C +++ b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C @@ -78,6 +78,7 @@ ExplicitDynamicsContactConstraint::validParams() "using a node on face, primary/secondary algorithm, and multiple options " "for the physical behavior on the interface and the mathematical " "formulation for constraint enforcement"); + return params; } @@ -110,7 +111,14 @@ ExplicitDynamicsContactConstraint::ExplicitDynamicsContactConstraint( _aux_solution(_aux_system.currentSolution()), _print_contact_nodes(getParam("print_contact_nodes")), _neighbor_density(getNeighborMaterialPropertyByName("density")), - _neighbor_wave_speed(getNeighborMaterialPropertyByName("wave_speed")) + _neighbor_wave_speed(getNeighborMaterialPropertyByName("wave_speed")), + _vel_x(isCoupled("vel_x") ? coupledValue("vel_x") : _zero), + _vel_y(isCoupled("vel_y") ? coupledValue("vel_y") : _zero), + _vel_z((_mesh.dimension() == 3 && isCoupled("vel_z")) ? coupledValue("vel_z") : _zero), + _neighbor_vel_x(isCoupled("vel_x") ? coupledNeighborValue("vel_x") : _zero), + _neighbor_vel_y(isCoupled("vel_y") ? coupledNeighborValue("vel_y") : _zero), + _neighbor_vel_z((_mesh.dimension() == 3 && isCoupled("vel_z")) ? coupledNeighborValue("vel_z") + : _zero) { _overwrite_secondary_residual = false; @@ -135,6 +143,18 @@ ExplicitDynamicsContactConstraint::ExplicitDynamicsContactConstraint( if (parameters.isParamValid("normal_smoothing_method")) _penetration_locator.setNormalSmoothingMethod( parameters.get("normal_smoothing_method")); + + if (_model == ExplicitDynamicsContactModel::FRICTIONLESS_BALANCE) + { + bool is_correct = + (isCoupled("vel_x") && isCoupled("vel_y") && _mesh.dimension() == 2) || + (isCoupled("vel_x") && isCoupled("vel_y") && isCoupled("vel_z") && _mesh.dimension() == 3); + + if (!is_correct) + paramError("vel_x", + "Velocities vel_x and vel_y (also vel_z in three dimensions) need to be provided " + "for the 'balance' option of solving normal contact in explicit dynamics."); + } } void @@ -245,6 +265,7 @@ ExplicitDynamicsContactConstraint::computeContactForce(const Node & node, const Real penalty = getPenalty(node); RealVectorValue pen_force(penalty * distance_vec); + switch (_model) { case ExplicitDynamicsContactModel::FRICTIONLESS: @@ -275,9 +296,39 @@ ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & node, Penet // Stab at momentum balance, uncoupled normal pressure // See Heinstein et al, 2000, Contact-impact modeling in explicit transient dynamics. + // Secondary surface const auto nodal_area = nodalArea(node); - pinfo->_contact_force = pinfo->_normal * (pinfo->_normal * nodal_area); + dof_id_type dof_wave_speed = + node.dof_number(_aux_system.number(), _nodal_wave_speed_var->number(), 0); + const Real wave_speed_secondary = (*_aux_solution)(dof_wave_speed); + + dof_id_type dof_density = node.dof_number(_aux_system.number(), _nodal_density_var->number(), 0); + const Real density_secondary = (*_aux_solution)(dof_density); + + // Primary surface + _neighbor_density[0]; + _neighbor_wave_speed[0]; + + Real contact_pressure_balance(0.0); + Real gap_rate(0.0); + + contact_pressure_balance = + density_secondary * _neighbor_density[0] * wave_speed_secondary * _neighbor_wave_speed[0]; + contact_pressure_balance /= + (density_secondary * wave_speed_secondary + _neighbor_density[0] * _neighbor_wave_speed[0]); + contact_pressure_balance *= nodal_area; + + RealVectorValue secondary_velocity( + _vel_x[0], _vel_y[0], _mesh.dimension() == 3 ? _vel_z[0] : 0.0); + RealVectorValue closest_point_velocity( + _neighbor_vel_x[0], _neighbor_vel_y[0], _mesh.dimension() == 3 ? _neighbor_vel_z[0] : 0.0); + gap_rate = pinfo->_normal * (secondary_velocity - closest_point_velocity); + contact_pressure_balance *= gap_rate; + + // The gap rate can probably be saved in pinfo by adequately evaluating the velocity variable at + // the corresponding points. Let's do that. + pinfo->_contact_force = pinfo->_normal * contact_pressure_balance; } Real From 076f8aec12e89d7f5f8b1fa4abe489877b663cb7 Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Tue, 21 Nov 2023 13:35:01 -0700 Subject: [PATCH 10/31] Fix nodal calculations --- modules/contact/src/userobjects/NodalDensity.C | 2 +- modules/contact/src/userobjects/NodalWaveSpeed.C | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/contact/src/userobjects/NodalDensity.C b/modules/contact/src/userobjects/NodalDensity.C index ebac64fe4fd6..3bc60e55e75d 100644 --- a/modules/contact/src/userobjects/NodalDensity.C +++ b/modules/contact/src/userobjects/NodalDensity.C @@ -75,7 +75,7 @@ NodalDensity::execute() { const Real density = node_densities[j]; if (density != 0) - _node_densities[_current_elem->node_ptr(j)] += density; + _node_densities[_current_elem->node_ptr(j)] = density; } } diff --git a/modules/contact/src/userobjects/NodalWaveSpeed.C b/modules/contact/src/userobjects/NodalWaveSpeed.C index ce61d2843a1c..c306df4d1d43 100644 --- a/modules/contact/src/userobjects/NodalWaveSpeed.C +++ b/modules/contact/src/userobjects/NodalWaveSpeed.C @@ -73,7 +73,7 @@ NodalWaveSpeed::execute() { const Real wave_speed = node_wave_speeds[j]; if (wave_speed != 0) - _node_wave_speeds[_current_elem->node_ptr(j)] += wave_speed; + _node_wave_speeds[_current_elem->node_ptr(j)] = wave_speed; } } From 7e9143dff54ae33b2c596eff00899639abe7c548 Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Tue, 21 Nov 2023 13:39:36 -0700 Subject: [PATCH 11/31] Make NodeFace writeable friendly. Pull variables into constraint. --- framework/src/interfaces/Coupleable.C | 63 ++++---- framework/src/systems/NonlinearSystemBase.C | 21 +++ framework/src/variables/MooseVariableData.C | 12 ++ .../ExplicitDynamicsContactConstraint.h | 13 +- .../ExplicitDynamicsContactConstraint.C | 149 +++++++++++++++--- 5 files changed, 208 insertions(+), 50 deletions(-) diff --git a/framework/src/interfaces/Coupleable.C b/framework/src/interfaces/Coupleable.C index ef8d2e69a8e1..84b11c410790 100644 --- a/framework/src/interfaces/Coupleable.C +++ b/framework/src/interfaces/Coupleable.C @@ -21,6 +21,7 @@ #include "AuxKernel.h" #include "ElementUserObject.h" #include "NodalUserObject.h" +#include "NodeFaceConstraint.h" Coupleable::Coupleable(const MooseObject * moose_object, bool nodal, bool is_fv) : _c_parameters(moose_object->parameters()), @@ -862,12 +863,13 @@ Coupleable::writableVariable(const std::string & var_name, unsigned int comp) const auto * aux = dynamic_cast(this); const auto * euo = dynamic_cast(this); const auto * nuo = dynamic_cast(this); + const auto * nfc = dynamic_cast(this); - if (!aux && !euo && !nuo) - mooseError("writableVariable() can only be called from AuxKernels, ElementUserObjects, or " - "NodalUserObjects. '", + if (!aux && !euo && !nuo && !nfc) + mooseError("writableVariable() can only be called from AuxKernels, ElementUserObjects, " + "NodalUserObjects, or NodeFaceConstraints. '", _obj->name(), - "' is neither of those."); + "' is none of those."); if (aux && !aux->isNodal() && var->isNodal()) mooseError("The elemental AuxKernel '", @@ -931,35 +933,42 @@ Coupleable::checkWritableVar(MooseWritableVariable * var) { // check block restrictions for compatibility const auto * br = dynamic_cast(this); - if (!var->hasBlocks(br->blockIDs())) - mooseError("The variable '", - var->name(), - "' must be defined on all blocks '", - _obj->name(), - "' is defined on"); - // make sure only one object can access a variable - for (const auto & ci : _obj->getMooseApp().getInterfaceObjects()) - if (ci != this && ci->_writable_coupled_variables[_c_tid].count(var)) - { - // if both this and ci are block restrictable then we check if the block restrictions - // are not overlapping. If they don't we permit the call. - const auto * br_other = dynamic_cast(ci); - if (br && br_other && br->blockRestricted() && br_other->blockRestricted() && - !MooseUtils::setsIntersect(br->blockIDs(), br_other->blockIDs())) - continue; - - mooseError("'", - ci->_obj->name(), - "' already obtained a writable reference to '", + if (!br) + mooseWarning("This object '", _obj->name(), "'is not of the block restrictable type."); + else + { + if (!var->hasBlocks(br->blockIDs())) + mooseError("The variable '", var->name(), - "'. Only one object can obtain such a reference per variable and subdomain in a " - "simulation."); - } + "' must be defined on all blocks '", + _obj->name(), + "' is defined on"); + // make sure only one object can access a variable + for (const auto & ci : _obj->getMooseApp().getInterfaceObjects()) + if (ci != this && ci->_writable_coupled_variables[_c_tid].count(var)) + { + // if both this and ci are block restrictable then we check if the block restrictions + // are not overlapping. If they don't we permit the call. + const auto * br_other = dynamic_cast(ci); + if (br && br_other && br->blockRestricted() && br_other->blockRestricted() && + !MooseUtils::setsIntersect(br->blockIDs(), br_other->blockIDs())) + continue; + + mooseError("'", + ci->_obj->name(), + "' already obtained a writable reference to '", + var->name(), + "'. Only one object can obtain such a reference per variable and subdomain in a " + "simulation."); + } + } // var is unique across threads, so we could forego having a separate set per thread, but we // need quick access to the list of all variables that need to be inserted into the solution // vector by a given thread. + + Moose::out << "Inserting variable... " << var->name() << " in checkWritableVar. \n"; _writable_coupled_variables[_c_tid].insert(var); } diff --git a/framework/src/systems/NonlinearSystemBase.C b/framework/src/systems/NonlinearSystemBase.C index b185a12d0cd1..7558911fa2ce 100644 --- a/framework/src/systems/NonlinearSystemBase.C +++ b/framework/src/systems/NonlinearSystemBase.C @@ -1163,6 +1163,17 @@ NonlinearSystemBase::setConstraintSecondaryValues(NumericVector & soluti constraints_applied = true; nfc->computeSecondaryValue(solution); } + + if (nfc->hasWritableCoupledVariables()) + { + Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx); + for (auto * var : nfc->getWritableCoupledVariables()) + { + var->insert(_fe_problem.getAuxiliarySystem().solution()); + } + _fe_problem.getAuxiliarySystem().solution().close(); + _fe_problem.getAuxiliarySystem().system().update(); + } } } } @@ -1341,6 +1352,16 @@ NonlinearSystemBase::constraintResiduals(NumericVector & residual, bool _fe_problem.cacheResidual(0); _fe_problem.cacheResidualNeighbor(0); } + if (nfc->hasWritableCoupledVariables()) + { + Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx); + for (auto * var : nfc->getWritableCoupledVariables()) + { + var->insert(_fe_problem.getAuxiliarySystem().solution()); + } + _fe_problem.getAuxiliarySystem().solution().close(); + _fe_problem.getAuxiliarySystem().system().update(); + } } } } diff --git a/framework/src/variables/MooseVariableData.C b/framework/src/variables/MooseVariableData.C index b678d15f16c9..fcde1c8161fb 100644 --- a/framework/src/variables/MooseVariableData.C +++ b/framework/src/variables/MooseVariableData.C @@ -1305,13 +1305,25 @@ MooseVariableData::setDofValue(const OutputData & value, unsigned in dof_values[index] = value; _has_dof_values = true; + Moose::out << "MooseVariableData::setDofValue: " << value << " and index: " << index + << "\n"; auto & u = _vector_tag_u[_solution_tag]; for (unsigned int qp = 0; qp < u.size(); qp++) { + Moose::out << "Making that modification\n"; u[qp] = (*_phi)[0][qp] * dof_values[0]; + Moose::out << " dof_values[0] is: " << dof_values[0] << "\n"; + Moose::out << "First u of " << qp << " is: " << u[qp] << "\n"; + for (unsigned int i = 1; i < dof_values.size(); i++) + { u[qp] += (*_phi)[i][qp] * dof_values[i]; + Moose::out << " dof_values[i] is: " << dof_values[i] << "\n"; + Moose::out << "Second u of " << qp << " is: " << u[qp] << "\n"; + } } + + Moose::out << "Final is (of 0): " << u[0] << "\n"; } template diff --git a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h index 0bee8cc8647f..351eee06d49d 100644 --- a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h +++ b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h @@ -13,6 +13,8 @@ #include "NodeFaceConstraint.h" #include "PenetrationLocator.h" #include "TwoMaterialPropertyInterface.h" +#include "Coupleable.h" + // Forward Declarations enum class ExplicitDynamicsContactModel; @@ -85,8 +87,11 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, * Determine "Lagrange multipliers" from the iterative solution of the impact problem. * @param node The number of the variable to be checked * @param pinfo The component index computed in this routine + * @param distance_gap The gap distance at the constraint node */ - void solveImpactEquations(const Node & node, PenetrationInfo * pinfo); + void solveImpactEquations(const Node & node, + PenetrationInfo * pinfo, + const RealVectorValue & distance_gap); MooseSharedPointer _displaced_problem; Real gapOffset(const Node & node); @@ -133,11 +138,11 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, const MaterialProperty & _neighbor_wave_speed; /// X component of velocity at the contacting node - const VariableValue & _vel_x; + MooseWritableVariable * _vel_x; /// Y component of velocity at the contacting node - const VariableValue & _vel_y; + MooseWritableVariable * _vel_y; /// Z component of velocity at the contacting node - const VariableValue & _vel_z; + MooseWritableVariable * _vel_z; /// X component of velocity at the closest point const VariableValue & _neighbor_vel_x; diff --git a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C index 50b31324e3e9..896d591ae044 100644 --- a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C +++ b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C @@ -26,6 +26,8 @@ #include "libmesh/string_to_enum.h" #include "libmesh/sparse_matrix.h" +#include "UpdateNodalAuxVariable.h" + registerMooseObject("ContactApp", ExplicitDynamicsContactConstraint); const unsigned int ExplicitDynamicsContactConstraint::_no_iterations = 0; @@ -73,6 +75,8 @@ ExplicitDynamicsContactConstraint::validParams() "Whether to normalize the penalty parameter with the nodal area for penalty contact."); params.addParam( "print_contact_nodes", false, "Whether to print the number of nodes in contact."); + // params.addParam>( + // "vel_uos", "List of nodal user objects to update the velocity vector."); params.addClassDescription( "Apply non-penetration constraints on the mechanical deformation " "using a node on face, primary/secondary algorithm, and multiple options " @@ -112,9 +116,9 @@ ExplicitDynamicsContactConstraint::ExplicitDynamicsContactConstraint( _print_contact_nodes(getParam("print_contact_nodes")), _neighbor_density(getNeighborMaterialPropertyByName("density")), _neighbor_wave_speed(getNeighborMaterialPropertyByName("wave_speed")), - _vel_x(isCoupled("vel_x") ? coupledValue("vel_x") : _zero), - _vel_y(isCoupled("vel_y") ? coupledValue("vel_y") : _zero), - _vel_z((_mesh.dimension() == 3 && isCoupled("vel_z")) ? coupledValue("vel_z") : _zero), + _vel_x(&writableVariable("vel_x")), + _vel_y(&writableVariable("vel_y")), + _vel_z(&writableVariable("vel_z")), _neighbor_vel_x(isCoupled("vel_x") ? coupledNeighborValue("vel_x") : _zero), _neighbor_vel_y(isCoupled("vel_y") ? coupledNeighborValue("vel_y") : _zero), _neighbor_vel_z((_mesh.dimension() == 3 && isCoupled("vel_z")) ? coupledNeighborValue("vel_z") @@ -272,7 +276,7 @@ ExplicitDynamicsContactConstraint::computeContactForce(const Node & node, pinfo->_contact_force = pinfo->_normal * (pinfo->_normal * pen_force); break; case ExplicitDynamicsContactModel::FRICTIONLESS_BALANCE: - solveImpactEquations(node, pinfo); + solveImpactEquations(node, pinfo, distance_vec); break; default: mooseError("Invalid or unavailable contact model"); @@ -291,7 +295,9 @@ ExplicitDynamicsContactConstraint::computeContactForce(const Node & node, } void -ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & node, PenetrationInfo * pinfo) +ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & node, + PenetrationInfo * pinfo, + const RealVectorValue & /*distance_gap*/) { // Stab at momentum balance, uncoupled normal pressure // See Heinstein et al, 2000, Contact-impact modeling in explicit transient dynamics. @@ -306,29 +312,134 @@ ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & node, Penet dof_id_type dof_density = node.dof_number(_aux_system.number(), _nodal_density_var->number(), 0); const Real density_secondary = (*_aux_solution)(dof_density); - // Primary surface - _neighbor_density[0]; - _neighbor_wave_speed[0]; - Real contact_pressure_balance(0.0); + Real mass_contact_pressure(0.0); + Real gap_rate(0.0); + // Real gap(0.0); - contact_pressure_balance = + mass_contact_pressure = density_secondary * _neighbor_density[0] * wave_speed_secondary * _neighbor_wave_speed[0]; - contact_pressure_balance /= + mass_contact_pressure /= (density_secondary * wave_speed_secondary + _neighbor_density[0] * _neighbor_wave_speed[0]); - contact_pressure_balance *= nodal_area; + mass_contact_pressure *= nodal_area; + + // Prepare equilibrium loop + + bool is_converged(false); + const unsigned int max_no_iterations(20000); + unsigned int iteration_no(0); + + // for (unsigned int i = 0; i < _ndisp; ++i) + // { + // // translational velocities and accelerations + // unsigned int dof_index_0 = node[0]->dof_number(nonlinear_sys.number(), _disp_num[i], 0); + // _vel_0(i) = vel(dof_index_0); + // } + + Real velocity_x = dynamic_cast *>(_vel_x)->getNodalValue(node); + Real velocity_y = dynamic_cast *>(_vel_y)->getNodalValue(node); + Real velocity_z = dynamic_cast *>(_vel_z)->getNodalValue(node); + + Real n_velocity_x = _neighbor_vel_x[0]; + Real n_velocity_y = _neighbor_vel_y[0]; + Real n_velocity_z = _neighbor_vel_z[0]; + + Real contact_pressure_old(0.0); + + // Mass proxy for secondary node. + const Real mass_proxy = density_secondary * wave_speed_secondary * _dt * nodal_area; + const Real n_mass_proxy = _neighbor_density[0] * _neighbor_wave_speed[0] * _dt * nodal_area; + + while (!is_converged && iteration_no < max_no_iterations) + { + // Start a loop until we converge on normal contact forces + RealVectorValue secondary_velocity( + velocity_x, velocity_y, _mesh.dimension() == 3 ? velocity_z : 0.0); + RealVectorValue closest_point_velocity( + n_velocity_x, n_velocity_y, _mesh.dimension() == 3 ? n_velocity_z : 0.0); + gap_rate = pinfo->_normal * (secondary_velocity - closest_point_velocity); + + contact_pressure_balance = mass_contact_pressure * gap_rate; + + velocity_x = velocity_x - _dt / mass_proxy * pinfo->_normal(0) * + (contact_pressure_balance - contact_pressure_old); + velocity_y = velocity_y - _dt / mass_proxy * pinfo->_normal(1) * + (contact_pressure_balance - contact_pressure_old); + velocity_z = velocity_z - _dt / mass_proxy * pinfo->_normal(2) * + (contact_pressure_balance - contact_pressure_old); + + n_velocity_x = n_velocity_x - _dt / n_mass_proxy * pinfo->_normal(0) * + (contact_pressure_balance - contact_pressure_old); + n_velocity_y = n_velocity_y - _dt / n_mass_proxy * pinfo->_normal(1) * + (contact_pressure_balance - contact_pressure_old); + n_velocity_z = n_velocity_z - _dt / n_mass_proxy * pinfo->_normal(2) * + (contact_pressure_balance - contact_pressure_old); + + // Convergence check: + const Real relative_error = + (contact_pressure_balance - contact_pressure_old) / contact_pressure_balance; + const Real absolute_error = std::abs(contact_pressure_balance - contact_pressure_old); + + // Moose::out << "For error, current vs old is: " << contact_pressure_balance << ", " + // << contact_pressure_old << "\n"; + + contact_pressure_old = contact_pressure_balance; + + if (std::abs(relative_error) < TOLERANCE || absolute_error < TOLERANCE) + { + Moose::out << "Relative error of loop: " << std::abs(relative_error) << "\n"; + is_converged = true; + } + else + iteration_no++; + } + + auto & u_dot = *_sys.solutionUDot(); + dof_id_type dof_x = node.dof_number(_sys.number(), _var_objects[0]->number(), 0); + dof_id_type dof_y = node.dof_number(_sys.number(), _var_objects[1]->number(), 0); + dof_id_type dof_z = node.dof_number(_sys.number(), _var_objects[2]->number(), 0); + + Moose::out << "Dofs set up in this nodal constraint are: " << dof_x << ", " << dof_y << ", and " + << dof_z << "\n"; + + // Set velocities on contact interface according to local, converged solution. + u_dot.set(dof_x, velocity_x); + u_dot.set(dof_y, velocity_y); + u_dot.set(dof_z, velocity_z); + u_dot.close(); + + Moose::out << "Total iterations: " << iteration_no << "\n"; + Moose::out << "vel z *before* local update: " + << dynamic_cast *>(_vel_z)->getNodalValue(node) << "\n"; + Moose::out << "vel z locally after update update: " << velocity_z << "\n"; + + // NonlinearSystemBase & nonlinear_sys = _fe_problem.getNonlinearSystemBase(_sys.number()); + + if (true) + { + numeric_index_type dof_index_x = node.dof_number(_aux_system.number(), _vel_x->number(), 0); + numeric_index_type dof_index_y = node.dof_number(_aux_system.number(), _vel_y->number(), 0); + numeric_index_type dof_index_z = node.dof_number(_aux_system.number(), _vel_z->number(), 0); + + _vel_x->setNodalValue(velocity_x, dof_index_x); + _vel_y->setNodalValue(velocity_y, dof_index_y); + _vel_z->setNodalValue(velocity_z, dof_index_z); + + // vel(dof_index_x) = velocity_x; + // vel(dof_index_y) = velocity_y; + // vel(dof_index_z) = velocity_z; + } - RealVectorValue secondary_velocity( - _vel_x[0], _vel_y[0], _mesh.dimension() == 3 ? _vel_z[0] : 0.0); - RealVectorValue closest_point_velocity( - _neighbor_vel_x[0], _neighbor_vel_y[0], _mesh.dimension() == 3 ? _neighbor_vel_z[0] : 0.0); - gap_rate = pinfo->_normal * (secondary_velocity - closest_point_velocity); - contact_pressure_balance *= gap_rate; + Moose::out << "vel z *after* local update: " + << dynamic_cast *>(_vel_z)->nodalValue() << "\n"; // The gap rate can probably be saved in pinfo by adequately evaluating the velocity variable at // the corresponding points. Let's do that. - pinfo->_contact_force = pinfo->_normal * contact_pressure_balance; + if (contact_pressure_balance < 0.0) + pinfo->_contact_force = pinfo->_normal * contact_pressure_balance; + else + pinfo->_contact_force = 0.0; } Real From 0675e22c697a92e35831df824dac4986f1f61f9b Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Wed, 29 Nov 2023 10:38:43 -0700 Subject: [PATCH 12/31] Implement balance-momentum equations for explicit contact (#25666) Kinematic constraint works for primary surface being rigid and works also in parallel. Requires verification and adding documentation. --- .../include/constraints/NodeFaceConstraint.h | 9 +- .../include/systems/NonlinearSystemBase.h | 3 + .../src/constraints/NodeFaceConstraint.C | 8 +- framework/src/interfaces/Coupleable.C | 67 +++--- framework/src/systems/NonlinearSystemBase.C | 22 +- framework/src/variables/MooseVariableData.C | 11 - .../actions/ExplicitDynamicsContactAction.md | 7 + .../source/userobjects/NodalDensity.md | 13 + .../source/userobjects/NodalWaveSpeed.md | 13 + .../contact/explicit_dynamics/first_test.i | 43 +--- .../explicit_dynamics/gold/first_test_out.csv | 108 ++++----- .../explicit_dynamics/gold/test_balance_out.e | Bin 0 -> 216924 bytes .../{contact_stub.i => test_balance.i} | 126 ++++++---- modules/contact/explicit_dynamics/tests | 14 +- .../ExplicitDynamicsContactConstraint.h | 17 +- .../actions/ExplicitDynamicsContactAction.C | 17 +- .../ExplicitDynamicsContactConstraint.C | 222 +++++++----------- .../contact/src/userobjects/NodalDensity.C | 10 +- .../contact/src/userobjects/NodalWaveSpeed.C | 11 +- .../doc/content/source/materials/WaveSpeed.md | 15 ++ 20 files changed, 369 insertions(+), 367 deletions(-) create mode 100644 modules/contact/doc/content/source/actions/ExplicitDynamicsContactAction.md create mode 100644 modules/contact/doc/content/source/userobjects/NodalDensity.md create mode 100644 modules/contact/doc/content/source/userobjects/NodalWaveSpeed.md create mode 100644 modules/contact/explicit_dynamics/gold/test_balance_out.e rename modules/contact/explicit_dynamics/{contact_stub.i => test_balance.i} (70%) create mode 100644 modules/solid_mechanics/doc/content/source/materials/WaveSpeed.md diff --git a/framework/include/constraints/NodeFaceConstraint.h b/framework/include/constraints/NodeFaceConstraint.h index b819cd619ff6..4444af425fe8 100644 --- a/framework/include/constraints/NodeFaceConstraint.h +++ b/framework/include/constraints/NodeFaceConstraint.h @@ -117,6 +117,11 @@ class NodeFaceConstraint : public Constraint, virtual const std::set & getMatPropDependencies() const; + /** + * Virtual method to avoid initial setups. + */ + virtual bool isExplicitConstraint() const { return false; } + protected: /** * Compute the value the secondary node should have at the beginning of a timestep. @@ -221,7 +226,7 @@ class NodeFaceConstraint : public Constraint, return coupledNeighborSecond(var_name, comp); } - const std::set & getBoundaryIDs(); + const std::set & buildBoundaryIDs(); /// Boundary ID for the secondary surface unsigned int _secondary; @@ -237,6 +242,8 @@ class NodeFaceConstraint : public Constraint, std::set _boundary_ids; public: + const std::set getSecondaryConnectecBlocks() const; + PenetrationLocator & _penetration_locator; protected: diff --git a/framework/include/systems/NonlinearSystemBase.h b/framework/include/systems/NonlinearSystemBase.h index 3048a69f6fc4..23c18cfd0cf3 100644 --- a/framework/include/systems/NonlinearSystemBase.h +++ b/framework/include/systems/NonlinearSystemBase.h @@ -983,4 +983,7 @@ class NonlinearSystemBase : public SystemBase, public PerfGraphInterface /// The number of scaling groups std::size_t _num_scaling_groups; + + /// Mutex for auxiliary variables + static Threads::spin_mutex writable_variable_mutex; }; diff --git a/framework/src/constraints/NodeFaceConstraint.C b/framework/src/constraints/NodeFaceConstraint.C index fc9152871d61..d9d6c10ac463 100644 --- a/framework/src/constraints/NodeFaceConstraint.C +++ b/framework/src/constraints/NodeFaceConstraint.C @@ -286,9 +286,15 @@ NodeFaceConstraint::overwriteSecondaryResidual() } const std::set & -NodeFaceConstraint::getBoundaryIDs() +NodeFaceConstraint::buildBoundaryIDs() { _boundary_ids.insert(_primary); _boundary_ids.insert(_secondary); return _boundary_ids; } + +const std::set +NodeFaceConstraint::getSecondaryConnectecBlocks() const +{ + return _mesh.getBoundaryConnectedBlocks(_secondary); +} diff --git a/framework/src/interfaces/Coupleable.C b/framework/src/interfaces/Coupleable.C index 84b11c410790..a72d559fc20e 100644 --- a/framework/src/interfaces/Coupleable.C +++ b/framework/src/interfaces/Coupleable.C @@ -931,44 +931,51 @@ Coupleable::writableCoupledValue(const std::string & var_name, unsigned int comp void Coupleable::checkWritableVar(MooseWritableVariable * var) { - // check block restrictions for compatibility + // check domain restrictions for compatibility const auto * br = dynamic_cast(this); + const auto * nfc = dynamic_cast(this); - if (!br) - mooseWarning("This object '", _obj->name(), "'is not of the block restrictable type."); - else - { - if (!var->hasBlocks(br->blockIDs())) - mooseError("The variable '", + // mooseWarning("This object '", _obj->name(), "'is not of the block restrictable type."); + + if (br && !var->hasBlocks(br->blockIDs())) + mooseError("The variable '", + var->name(), + "' must be defined on all blocks '", + _obj->name(), + "' is defined on."); + + if (nfc && !var->hasBlocks(nfc->getSecondaryConnectecBlocks())) + mooseError("The variable '", + var->name(), + " must be defined on all blocks '", + _obj->name(), + "'s secondary surface is defined on."); + + // make sure only one object can access a variable + for (const auto & ci : _obj->getMooseApp().getInterfaceObjects()) + if (ci != this && ci->_writable_coupled_variables[_c_tid].count(var)) + { + // if both this and ci are block restrictable then we check if the block restrictions + // are not overlapping. If they don't we permit the call. + const auto * br_other = dynamic_cast(ci); + if (br && br_other && br->blockRestricted() && br_other->blockRestricted() && + !MooseUtils::setsIntersect(br->blockIDs(), br_other->blockIDs())) + continue; + else if (nfc) + continue; + + mooseError("'", + ci->_obj->name(), + "' already obtained a writable reference to '", var->name(), - "' must be defined on all blocks '", - _obj->name(), - "' is defined on"); + "'. Only one object can obtain such a reference per variable and subdomain in a " + "simulation."); + } - // make sure only one object can access a variable - for (const auto & ci : _obj->getMooseApp().getInterfaceObjects()) - if (ci != this && ci->_writable_coupled_variables[_c_tid].count(var)) - { - // if both this and ci are block restrictable then we check if the block restrictions - // are not overlapping. If they don't we permit the call. - const auto * br_other = dynamic_cast(ci); - if (br && br_other && br->blockRestricted() && br_other->blockRestricted() && - !MooseUtils::setsIntersect(br->blockIDs(), br_other->blockIDs())) - continue; - - mooseError("'", - ci->_obj->name(), - "' already obtained a writable reference to '", - var->name(), - "'. Only one object can obtain such a reference per variable and subdomain in a " - "simulation."); - } - } // var is unique across threads, so we could forego having a separate set per thread, but we // need quick access to the list of all variables that need to be inserted into the solution // vector by a given thread. - Moose::out << "Inserting variable... " << var->name() << " in checkWritableVar. \n"; _writable_coupled_variables[_c_tid].insert(var); } diff --git a/framework/src/systems/NonlinearSystemBase.C b/framework/src/systems/NonlinearSystemBase.C index 7558911fa2ce..188b77e16530 100644 --- a/framework/src/systems/NonlinearSystemBase.C +++ b/framework/src/systems/NonlinearSystemBase.C @@ -1150,6 +1150,8 @@ NonlinearSystemBase::setConstraintSecondaryValues(NumericVector & soluti for (const auto & nfc : constraints) { + if (nfc->isExplicitConstraint()) + continue; // Return if this constraint does not correspond to the primary-secondary pair // prepared by the outer loops. // This continue statement is required when, e.g. one secondary surface constrains @@ -1169,10 +1171,9 @@ NonlinearSystemBase::setConstraintSecondaryValues(NumericVector & soluti Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx); for (auto * var : nfc->getWritableCoupledVariables()) { - var->insert(_fe_problem.getAuxiliarySystem().solution()); + if (var->isNodalDefined()) + var->insert(_fe_problem.getAuxiliarySystem().solution()); } - _fe_problem.getAuxiliarySystem().solution().close(); - _fe_problem.getAuxiliarySystem().system().update(); } } } @@ -1357,16 +1358,23 @@ NonlinearSystemBase::constraintResiduals(NumericVector & residual, bool Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx); for (auto * var : nfc->getWritableCoupledVariables()) { - var->insert(_fe_problem.getAuxiliarySystem().solution()); + if (var->isNodalDefined()) + var->insert(_fe_problem.getAuxiliarySystem().solution()); } - _fe_problem.getAuxiliarySystem().solution().close(); - _fe_problem.getAuxiliarySystem().system().update(); } } } } } } + const bool has_explicit_contact(true); + if (has_explicit_contact) + { + _fe_problem.getAuxiliarySystem().solution().close(); + _fe_problem.getAuxiliarySystem().system().update(); + solutionOld().close(); + } + if (_assemble_constraints_separately) { // Make sure that secondary contribution to primary are assembled, and ghosts have been @@ -2221,6 +2229,8 @@ NonlinearSystemBase::constraintJacobians(bool displaced) for (const auto & nfc : constraints) { + if (nfc->isExplicitConstraint()) + continue; // Return if this constraint does not correspond to the primary-secondary pair // prepared by the outer loops. // This continue statement is required when, e.g. one secondary surface constrains diff --git a/framework/src/variables/MooseVariableData.C b/framework/src/variables/MooseVariableData.C index fcde1c8161fb..6def8d6dad45 100644 --- a/framework/src/variables/MooseVariableData.C +++ b/framework/src/variables/MooseVariableData.C @@ -1305,25 +1305,14 @@ MooseVariableData::setDofValue(const OutputData & value, unsigned in dof_values[index] = value; _has_dof_values = true; - Moose::out << "MooseVariableData::setDofValue: " << value << " and index: " << index - << "\n"; auto & u = _vector_tag_u[_solution_tag]; for (unsigned int qp = 0; qp < u.size(); qp++) { - Moose::out << "Making that modification\n"; u[qp] = (*_phi)[0][qp] * dof_values[0]; - Moose::out << " dof_values[0] is: " << dof_values[0] << "\n"; - Moose::out << "First u of " << qp << " is: " << u[qp] << "\n"; for (unsigned int i = 1; i < dof_values.size(); i++) - { u[qp] += (*_phi)[i][qp] * dof_values[i]; - Moose::out << " dof_values[i] is: " << dof_values[i] << "\n"; - Moose::out << "Second u of " << qp << " is: " << u[qp] << "\n"; - } } - - Moose::out << "Final is (of 0): " << u[0] << "\n"; } template diff --git a/modules/contact/doc/content/source/actions/ExplicitDynamicsContactAction.md b/modules/contact/doc/content/source/actions/ExplicitDynamicsContactAction.md new file mode 100644 index 000000000000..d73eec344a8f --- /dev/null +++ b/modules/contact/doc/content/source/actions/ExplicitDynamicsContactAction.md @@ -0,0 +1,7 @@ +# Contact Action + +!syntax description /Contact/ExplicitDynamicsContactAction + +## Description + +ExplicitDynamicsContactAction is a MOOSE action that constructs objects needed for mechanical contact enforcement for explicit dynamics finite element simulations. diff --git a/modules/contact/doc/content/source/userobjects/NodalDensity.md b/modules/contact/doc/content/source/userobjects/NodalDensity.md new file mode 100644 index 000000000000..b419993047ef --- /dev/null +++ b/modules/contact/doc/content/source/userobjects/NodalDensity.md @@ -0,0 +1,13 @@ +# NodalDensity + +## Description + +The `NodalDensity` object computes a nodally assigned value of the +density material property. It is used by explicit dynamics contact. + + +!syntax parameters /UserObjects/NodalDensity + +!syntax inputs /UserObjects/NodalDensity + +!syntax children /UserObjects/NodalDensity diff --git a/modules/contact/doc/content/source/userobjects/NodalWaveSpeed.md b/modules/contact/doc/content/source/userobjects/NodalWaveSpeed.md new file mode 100644 index 000000000000..6262337715f0 --- /dev/null +++ b/modules/contact/doc/content/source/userobjects/NodalWaveSpeed.md @@ -0,0 +1,13 @@ +# NodalWaveSpeed + +## Description + +The `NodalWaveSpeed` object computes a nodally assigned value of the +wave speed material property. + + +!syntax parameters /UserObjects/NodalWaveSpeed + +!syntax inputs /UserObjects/NodalWaveSpeed + +!syntax children /UserObjects/NodalWaveSpeed diff --git a/modules/contact/explicit_dynamics/first_test.i b/modules/contact/explicit_dynamics/first_test.i index e66349967b5c..ab3e0c7fa979 100644 --- a/modules/contact/explicit_dynamics/first_test.i +++ b/modules/contact/explicit_dynamics/first_test.i @@ -47,6 +47,7 @@ type = MeshCollectionGenerator inputs = ' block_one_id block_two_id' [] + allow_renumbering = false [] [Variables] @@ -71,27 +72,9 @@ [] [accel_z] [] - [stress_zz] - [] - [strain_zz] - [] [] [AuxKernels] - # [stress_zz] - # type = RankTwoAux - # rank_two_tensor = stress - # index_i = 2 - # index_j = 2 - # variable = stress_zz - # [] - # [strain_zz] - # type = RankTwoAux - # rank_two_tensor = elastic_strain - # index_i = 2 - # index_j = 2 - # variable = strain_zz - # [] [accel_x] type = TestNewmarkTI variable = accel_x @@ -207,24 +190,11 @@ [ExplicitDynamicsContact] [my_contact] model = frictionless - # formulation = penalty primary = base_front secondary = ball_back - # penalty = 1e+05 - # normalize_penalty = true [] [] -# [Controls] -# [mycontrol] -# type = TimePeriod -# disable_objects = 'BCs/z_bot' -# start_time = 1.0e-3 -# end_time = 1.0e9 -# execute_on = 'INITIAL TIMESTEP_END' -# [] -# [] - [Materials] [elasticity_tensor_block_one] type = ComputeIsotropicElasticityTensor @@ -269,16 +239,6 @@ [] [Postprocessors] - [accel_58z] - type = NodalVariableValue - nodeid = 1 - variable = accel_z - [] - [vel_58z] - type = NodalVariableValue - nodeid = 1 - variable = vel_z - [] [disp_58z] type = NodalVariableValue nodeid = 1 @@ -296,7 +256,6 @@ [] [Outputs] - # interval = 50 exodus = true csv = true [] diff --git a/modules/contact/explicit_dynamics/gold/first_test_out.csv b/modules/contact/explicit_dynamics/gold/first_test_out.csv index 8faf552b51fa..70f6348dbac6 100644 --- a/modules/contact/explicit_dynamics/gold/first_test_out.csv +++ b/modules/contact/explicit_dynamics/gold/first_test_out.csv @@ -1,54 +1,54 @@ -time,accel_58z,contact_pressure_max,critical_time_step,disp_58z,vel_58z --0.01,0,0,0,0,0 --0.005,0,0,0.1,7.1428571428571e-06,0 -0,0.28571428571429,0,0.1,-1.4273832748933e-08,0.00071428571428571 -0.005,-0.57199952473853,0,0.1,-7.1596363437208e-06,-1.4273832748933e-06 -0.01,0.00047073858536669,0,0.1,-1.4291770020591e-05,-0.0014302493486578 -0.015,0.00052915336407702,0,0.1,-2.1409216967926e-05,-0.0014277496187842 -0.02,0.00058746918137703,0,0.1,-2.8510521909652e-05,-0.0014249580624206 -0.025,0.00064568022437881,0,0.1,-3.5594232334055e-05,-0.0014218751889062 -0.03,0.0007037806929369,0,0.1,-4.2658898638455e-05,-0.0014185015366129 -0.035,0.00076176480009714,0,0.1,-4.9703074273542e-05,-0.0014148376728803 -0.04,0.00081962677255106,0,0.1,-5.6725315887351e-05,-0.0014108841939487 -0.045,0.00087736085107866,0,0.1,-6.3724183468886e-05,-0.0014066417248896 -0.05,0.00093496129099528,0,0.1,-7.0698240491355e-05,-0.0014021109195344 -0.055,0.00099242236259982,0,0.1,-7.7646054055035e-05,-0.0013972924604004 -0.06,0.0010497383516112,0,0.1,-8.4566195029724e-05,-0.0013921870586149 -0.065,0.0011069035596071,0,0.1,-9.1457238196801e-05,-0.0013867954538368 -0.07,0.0011639123044733,0,0.1,-9.8317762390858e-05,-0.0013811184141766 -0.075,0.0012207589208208,0,0.1,-0.0001051463506409,-0.0013751567361134 -0.08,0.0012774377604406,0.082341610254479,0.1,-0.00011194159031113,-0.0013689112444103 -0.085,0.0013339431927144,0.19101404878775,0.1,-0.00011870207077947,-0.0013623827920274 -0.09,0.0013903680754374,0.29901128340337,0.1,-0.000125426380983,-0.001355572013857 -0.095,0.0014468105927516,0.40624378043303,0.1,-0.00013211310720354,-0.0013484790671865 -0.1,0.0015033593191535,0.51262279737801,0.1,-0.0001387608308863,-0.0013411036424068 -0.105,0.0015601015116037,0.61806047588701,0.1,-0.00014536812639074,-0.0013334449903299 -0.11,0.0016171271325011,0.72246991323486,0.1,-0.0001519335590144,-0.0013255019187196 -0.115,0.001674515230965,0.82576527398727,0.1,-0.00015845568288214,-0.0013172728128109 -0.12,0.0017323502374635,0.92786186707797,0.1,-0.00016493303893838,-0.0013087556491399 -0.125,0.0017907124595868,1.0286762300661,0.1,-0.00017136415299712,-0.0012999479923972 -0.13,0.0018496798999412,1.1281262115668,0.1,-0.00017774753385423,-0.0012908470114984 -0.135,0.0019093280652566,1.2261310518263,0.1,-0.00018408167146683,-0.0012814494915854 -0.14,0.001969729780531,1.3226114614042,0.1,-0.00019036503520421,-0.001271751846971 -0.145,0.0020309550085944,1.4174896979197,0.1,-0.00019659607217471,-0.0012617501349981 -0.15,0.0020930706754688,1.5106896408102,0.1,-0.00020277320563266,-0.001251440070788 -0.155,0.0021561405017217,1.602136864045,0.1,-0.00020889483346961,-0.001240817042845 -0.16,0.0022202248402281,1.6917587067307,0.1,-0.00021495932679354,-0.0012298761294901 -0.165,0.0022853805205843,1.7794843415407,0.1,-0.00022096502859997,-0.0012186121160881 -0.17,0.002351660700413,1.8652448408947,0.1,-0.00022691025253829,-0.0012070195130356 -0.175,0.0024191147238834,1.9489732408148,0.1,-0.00023279328177693,-0.0011950925744749 -0.18,0.0024877879876864,2.0306046023756,0.1,-0.00023861236797019,-0.0011828253176959 -0.185,0.0025577218146179,2.1100760706723,0.1,-0.00024436573033008,-0.0011702115431902 -0.19,0.0026289533351361,2.1873269312209,0.1,-0.00025005155480555,-0.0011572448553158 -0.195,0.0027015153769366,2.2622986637121,0.1,-0.00025566799337195,-0.0011439186835356 -0.2,0.0027754363628673,2.3349349930359,0.1,-0.00026121316343291,-0.0011302263041861 -0.205,0.0028507402172954,2.4051819374974,0.1,-0.00026668514733684,-0.0011161608627357 -0.21,0.0029274462810618,2.4729878541477,0.1,-0.0002720819920099,-0.0011017153964898 -0.215,0.0030055692352613,2.5383034811523,0.1,-0.00027740170870711,-0.001086882857699 -0.22,0.0030851190338586,2.6010819771283,0.1,-0.00028264227288318,-0.0010716561370262 -0.225,0.003166100845367,2.6612789573813,0.1,-0.00028780162418416,-0.0010560280873281 -0.23,0.0032485150035862,2.71885252698,0.1,-0.00029287766656096,-0.0010399915477058 -0.235,0.0033323569676,2.7737633106092,0.1,-0.00029786826850548,-0.0010235393677778 -0.24,0.0034176172909284,2.8259744791508,0.1,-0.00030277126341,-0.0010066644321315 -0.245,0.0035042816000971,2.8754517729438,0.1,-0.00030758445004995,-0.0009893596849039 -0.25,0.0035923305824689,2.9221635216841,0.1,-0.00031230559319032,-0.00097161815444751 +time,contact_pressure_max,critical_time_step,disp_58z +-0.01,0,0,0 +-0.005,0,0.1,7.1428571428571e-06 +0,0,0.1,-1.4287635400646e-08 +0.005,0,0.1,-7.1596363851879e-06 +0.01,0,0.1,-1.4291770007043e-05 +0.015,0,0.1,-2.1409216834347e-05 +0.02,0,0.1,-2.8510521584756e-05 +0.025,0,0.1,-3.5594231740244e-05 +0.03,0,0.1,-4.2658897691867e-05 +0.035,0,0.1,-4.9703069387686e-05 +0.04,0,0.1,-5.6725303226426e-05 +0.045,0,0.1,-6.3724158948958e-05 +0.05,0,0.1,-7.0698199781591e-05 +0.055,0,0.1,-7.7645992578983e-05 +0.06,0,0.1,-8.4566107966647e-05 +0.065,0,0.1,-9.1457120483056e-05 +0.07,0,0.1,-9.8317608721322e-05 +0.075,0,0.1,-0.00010514615547046 +0.08,0.082342600269299,0.1,-0.0001119413563371 +0.085,0.19106600107269,0.1,-0.00011870180328649 +0.09,0.2992319466837,0.1,-0.00012542609282485 +0.095,0.40681732201413,0.1,-0.00013211282617698 +0.1,0.51379916468013,0.1,-0.00013876060942624 +0.105,0.62015466406831,0.1,-0.00014536805364947 +0.11,0.72586115989849,0.1,-0.00015193377505034 +0.115,0.83089614035709,0.1,-0.00015845639509274 +0.12,0.93523723989044,0.1,-0.00016493454063544 +0.125,1.0388622367508,0.1,-0.00017136684406851 +0.13,1.1417490503861,0.1,-0.00017775194345166 +0.135,1.2438757387596,0.1,-0.0001840884826543 +0.14,1.345220495677,0.1,-0.00019037511149702 +0.145,1.445762257348,0.1,-0.00019661048589375 +0.15,1.5454790652346,0.1,-0.00020279326799438 +0.155,1.6443494690639,0.1,-0.00020892212632706 +0.16,1.7423521982298,0.1,-0.00021499573594003 +0.165,1.8394661076241,0.1,-0.00022101277854242 +0.17,1.9356701759766,0.1,-0.00022697194264373 +0.175,2.0309435044819,0.1,-0.00023287192369192 +0.18,2.1252653157298,0.1,-0.00023871142420983 +0.185,2.2186149529507,0.1,-0.0002444891539297 +0.19,2.3109718795812,0.1,-0.00025020382992607 +0.195,2.4023156791516,0.1,-0.00025585417674664 +0.2,2.492626055489,0.1,-0.00026143892654142 +0.205,2.5818828332289,0.1,-0.00026695681919008 +0.21,2.6700659586206,0.1,-0.00027240660242766 +0.215,2.7571555006078,0.1,-0.00027778703196863 +0.22,2.8431316521658,0.1,-0.00028309687162956 +0.225,2.9279747318706,0.1,-0.00028833489345041 +0.23,3.0116651856744,0.1,-0.00029349987781456 +0.235,3.094183588863,0.1,-0.0002985906135678 +0.24,3.1755106481672,0.1,-0.00030360589813622 +0.245,3.2556272040036,0.1,-0.00030854446430282 +0.25,3.3345170057449,0.1,-0.00031340512549616 diff --git a/modules/contact/explicit_dynamics/gold/test_balance_out.e b/modules/contact/explicit_dynamics/gold/test_balance_out.e new file mode 100644 index 0000000000000000000000000000000000000000..16f4ce67c9fb523c0d2a2082a946dac0e4a6d1e2 GIT binary patch literal 216924 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 literal 0 HcmV?d00001 diff --git a/modules/contact/explicit_dynamics/contact_stub.i b/modules/contact/explicit_dynamics/test_balance.i similarity index 70% rename from modules/contact/explicit_dynamics/contact_stub.i rename to modules/contact/explicit_dynamics/test_balance.i index 545c3972f2f2..a7757c4ea539 100644 --- a/modules/contact/explicit_dynamics/contact_stub.i +++ b/modules/contact/explicit_dynamics/test_balance.i @@ -1,21 +1,22 @@ # One element test to test the central difference time integrator in 3D. [GlobalParams] displacements = 'disp_x disp_y disp_z' + volumetric_locking_correction = true [] [Mesh] [block_one] type = GeneratedMeshGenerator dim = 3 - nx = 2 - ny = 2 - nz = 2 + nx = 3 + ny = 3 + nz = 3 xmin = 4.5 xmax = 5.5 ymin = 4.5 ymax = 5.5 - zmin = 0.001 - zmax = 1.001 + zmin = 0.0001 + zmax = 1.0001 boundary_name_prefix = 'ball' [] [block_two] @@ -59,6 +60,8 @@ [] [AuxVariables] + [gap_rate] + [] [vel_x] [] [accel_x] @@ -72,8 +75,12 @@ [accel_z] [] [stress_zz] + family = MONOMIAL + order = CONSTANT [] [strain_zz] + family = MONOMIAL + order = CONSTANT [] [] @@ -84,10 +91,11 @@ index_i = 2 index_j = 2 variable = stress_zz + execute_on = 'TIMESTEP_END' [] [strain_zz] type = RankTwoAux - rank_two_tensor = elastic_strain + rank_two_tensor = mechanical_strain index_i = 2 index_j = 2 variable = strain_zz @@ -97,33 +105,39 @@ variable = accel_x displacement = disp_x first = false + execute_on = 'LINEAR TIMESTEP_BEGIN TIMESTEP_END' [] [vel_x] type = TestNewmarkTI variable = vel_x displacement = disp_x + execute_on = 'LINEAR TIMESTEP_BEGIN TIMESTEP_END' [] [accel_y] type = TestNewmarkTI variable = accel_y displacement = disp_y first = false + execute_on = 'LINEAR TIMESTEP_BEGIN TIMESTEP_END' [] [vel_y] type = TestNewmarkTI variable = vel_y displacement = disp_x + execute_on = 'LINEAR TIMESTEP_BEGIN TIMESTEP_END' [] [accel_z] type = TestNewmarkTI variable = accel_z displacement = disp_z first = false + execute_on = 'LINEAR TIMESTEP_BEGIN TIMESTEP_END' [] [vel_z] type = TestNewmarkTI variable = vel_z displacement = disp_z + execute_on = 'LINEAR TIMESTEP_BEGIN TIMESTEP_END' [] [] @@ -131,8 +145,8 @@ [DynamicTensorMechanics] displacements = 'disp_x disp_y disp_z' volumetric_locking_correction = true - stiffness_damping_coefficient = 0.04 - # generate_output = 'stress_zz strain_zz' + stiffness_damping_coefficient = 0.001 + generate_output = 'stress_zz strain_zz' [] [inertia_x] type = InertialForce @@ -148,25 +162,22 @@ [] [] -[Functions] - [dispz] - type = ParsedFunction - expression = if(t<1.0e3,-0.01*t,0) - [] - [push] - type = ParsedFunction - expression = if(t<10.0,0.01*t,0.1) +[Kernels] + [gravity] + type = Gravity + variable = disp_z + value = -981.0 [] [] [BCs] - [z_front] - type = FunctionDirichletBC - variable = disp_z - boundary = 'ball_front' - function = dispz - preset = false - [] + # [z_front] + # type = FunctionDirichletBC + # variable = disp_z + # boundary = 'ball_front' + # function = dispz + # preset = false + # [] [x_front] type = DirichletBC variable = disp_x @@ -202,72 +213,93 @@ preset = false value = 0.0 [] + [z_fixed_front] + type = DirichletBC + variable = disp_z + boundary = 'base_front' + preset = false + value = 0.0 + [] [] [ExplicitDynamicsContact] [my_contact] - model = frictionless - # formulation = penalty + model = frictionless_balance primary = base_front secondary = ball_back - # penalty = 1e+05 - # normalize_penalty = true + vel_x = 'vel_x' + vel_y = 'vel_y' + vel_z = 'vel_z' [] [] -# [Controls] -# [mycontrol] -# type = TimePeriod -# disable_objects = 'BCs/z_bot' -# start_time = 1.0e-3 -# end_time = 1.0e9 -# execute_on = 'INITIAL TIMESTEP_END' -# [] -# [] - [Materials] [elasticity_tensor_block_one] type = ComputeIsotropicElasticityTensor - youngs_modulus = 1e3 + youngs_modulus = 1e6 poissons_ratio = 0.0 block = 1 + outputs = 'exodus' + output_properties = __all__ [] [elasticity_tensor_block_two] type = ComputeIsotropicElasticityTensor - youngs_modulus = 1e6 + youngs_modulus = 1e10 poissons_ratio = 0.0 block = 2 + outputs = 'exodus' + output_properties = __all__ [] [strain_block] - type = ComputeIncrementalSmallStrain + type = ComputeFiniteStrain # ComputeIncrementalSmallStrain displacements = 'disp_x disp_y disp_z' implicit = false [] [stress_block] type = ComputeFiniteStrainElasticStress [] - [density] + [density_one] + type = GenericConstantMaterial + prop_names = density + prop_values = 1e1 + outputs = 'exodus' + output_properties = 'density' + block = '1' + [] + [density_two] type = GenericConstantMaterial prop_names = density - prop_values = 1e4 + prop_values = 1e6 + outputs = 'exodus' + output_properties = 'density' + block = '2' [] [wave_speed] type = WaveSpeed + outputs = 'exodus' + output_properties = 'wave_speed' [] [] [Executioner] type = Transient start_time = -0.01 - end_time = 24.0 - dt = 0.005 + end_time = -0.0075 # 10 + dt = 0.00001 timestep_tolerance = 1e-6 [TimeIntegrator] type = CentralDifference + solve_type = lumped [] [] +[Outputs] + interval = 50 + exodus = true + csv = true +[] + [Postprocessors] [accel_58z] type = NodalVariableValue @@ -294,9 +326,3 @@ value_type = max [] [] - -[Outputs] - # interval = 50 - exodus = true - csv = true -[] diff --git a/modules/contact/explicit_dynamics/tests b/modules/contact/explicit_dynamics/tests index 792017fb2956..67676c49b319 100644 --- a/modules/contact/explicit_dynamics/tests +++ b/modules/contact/explicit_dynamics/tests @@ -1,11 +1,21 @@ [Tests] - issues = '#0000' - design = '/Contact/index.md' + issues = '#25666' + design = 'ExplicitDynamicsContactConstraint.md' [block_penalty] type = 'CSVDiff' input = 'first_test.i' csvdiff = 'first_test_out.csv' + abs_zero = 1.0e-4 + rel_err = 5.0e-5 requirement = 'The system shall be able to solve a simple few-element normal contact problem ' 'using explicit dynamics.' [] + [test_balance] + type = 'Exodiff' + input = 'test_balance.i' + exodiff = 'test_balance_out.e' + abs_zero = 1.0e-4 + requirement = 'The system shall be able to solve a simple few-element normal contact problem ' + 'using explicit dynamics solving uncoupled, local equations of momentum balance.' + [] [] diff --git a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h index 351eee06d49d..fd0d2d691f30 100644 --- a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h +++ b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h @@ -72,7 +72,7 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, bool shouldApply() override; void computeContactForce(const Node & node, PenetrationInfo * pinfo, bool update_contact_set); - + virtual bool isExplicitConstraint() const override { return true; } /** * Return false so that the nonlinear system does not try to add Jacobian entries * from the contact forces. @@ -102,8 +102,6 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, const ExplicitDynamicsContactModel _model; const bool _normalize_penalty; - const Real _tension_release; - const Real _capture_tolerance; bool _update_stateful_data; const unsigned int _mesh_dimension; @@ -123,26 +121,21 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, SystemBase & _aux_system; const NumericVector * const _aux_solution; - std::set _current_contact_state; - std::set _old_contact_state; - const bool _print_contact_nodes; static Threads::spin_mutex _contact_set_mutex; const static unsigned int _no_iterations; + NumericVector & _residual_copy; + /// Density material for neighbor projection const MaterialProperty & _neighbor_density; /// Wave speed material for neighbor projection const MaterialProperty & _neighbor_wave_speed; - /// X component of velocity at the contacting node - MooseWritableVariable * _vel_x; - /// Y component of velocity at the contacting node - MooseWritableVariable * _vel_y; - /// Z component of velocity at the contacting node - MooseWritableVariable * _vel_z; + /// Nodal gap rate (output for debugging or analyst perusal) + MooseWritableVariable * _gap_rate; /// X component of velocity at the closest point const VariableValue & _neighbor_vel_x; diff --git a/modules/contact/src/actions/ExplicitDynamicsContactAction.C b/modules/contact/src/actions/ExplicitDynamicsContactAction.C index 0805b215261c..f8d4f2df6056 100644 --- a/modules/contact/src/actions/ExplicitDynamicsContactAction.C +++ b/modules/contact/src/actions/ExplicitDynamicsContactAction.C @@ -170,6 +170,13 @@ ExplicitDynamicsContactAction::act() _problem->addAuxVariable("MooseVariable", "contact_pressure", var_params); } + // Add gap rate for output + { + auto var_params = _factory.getValidParams("MooseVariable"); + var_params.set("order") = Utility::enum_to_string(OrderWrapper{order}); + var_params.set("family") = "LAGRANGE"; + _problem->addAuxVariable("MooseVariable", "gap_rate", var_params); + } // Add nodal area contact variable { auto var_params = _factory.getValidParams("MooseVariable"); @@ -367,6 +374,8 @@ ExplicitDynamicsContactAction::addNodeFaceContact() if (isParamValid("vel_z")) params.set>("vel_z") = getParam>("vel_z"); + params.set>("gap_rate") = {"gap_rate"}; + params.set("boundary") = contact_pair.first; if (isParamValid("secondary_gap_offset")) params.set>("secondary_gap_offset") = { @@ -408,9 +417,11 @@ ExplicitDynamicsContactAction::commonParameters() "model", ExplicitDynamicsContactAction::getModelEnum(), "The contact model to use"); // Gap rate input - params.addCoupledVar("vel_x", "x-component of velocity"); - params.addCoupledVar("vel_y", "y-component of velocity"); - params.addCoupledVar("vel_z", "z-component of velocity"); + params.addCoupledVar("vel_x", "x-component of velocity."); + params.addCoupledVar("vel_y", "y-component of velocity."); + params.addCoupledVar("vel_z", "z-component of velocity."); + // Gap rate output + params.addCoupledVar("gap_rate", "Gap rate for output."); return params; } diff --git a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C index 896d591ae044..9febeec1d97e 100644 --- a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C +++ b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C @@ -26,8 +26,6 @@ #include "libmesh/string_to_enum.h" #include "libmesh/sparse_matrix.h" -#include "UpdateNodalAuxVariable.h" - registerMooseObject("ContactApp", ExplicitDynamicsContactConstraint); const unsigned int ExplicitDynamicsContactConstraint::_no_iterations = 0; @@ -62,27 +60,17 @@ ExplicitDynamicsContactConstraint::validParams() params.addParam("friction_coefficient", 0, "The friction coefficient"); params.addParam("tangential_tolerance", "Tangential distance to extend edges of contact surfaces"); - params.addParam( - "capture_tolerance", 0, "Normal distance from surface within which nodes are captured"); - params.addParam("tension_release", - 0.0, - "Tension release threshold. A node in contact " - "will not be released if its tensile load is below " - "this value. No tension release if negative."); params.addParam( "normalize_penalty", false, "Whether to normalize the penalty parameter with the nodal area for penalty contact."); params.addParam( "print_contact_nodes", false, "Whether to print the number of nodes in contact."); - // params.addParam>( - // "vel_uos", "List of nodal user objects to update the velocity vector."); params.addClassDescription( "Apply non-penetration constraints on the mechanical deformation " "using a node on face, primary/secondary algorithm, and multiple options " "for the physical behavior on the interface and the mathematical " "formulation for constraint enforcement"); - return params; } @@ -91,13 +79,11 @@ Threads::spin_mutex ExplicitDynamicsContactConstraint::_contact_set_mutex; ExplicitDynamicsContactConstraint::ExplicitDynamicsContactConstraint( const InputParameters & parameters) : NodeFaceConstraint(parameters), - TwoMaterialPropertyInterface(this, Moose::EMPTY_BLOCK_IDS, getBoundaryIDs()), + TwoMaterialPropertyInterface(this, Moose::EMPTY_BLOCK_IDS, buildBoundaryIDs()), _displaced_problem(parameters.get("_fe_problem_base")->getDisplacedProblem()), _component(getParam("component")), _model(getParam("model").getEnum()), _normalize_penalty(getParam("normalize_penalty")), - _tension_release(getParam("tension_release")), - _capture_tolerance(getParam("capture_tolerance")), _update_stateful_data(true), _mesh_dimension(_mesh.dimension()), _vars(3, libMesh::invalid_uint), @@ -114,11 +100,10 @@ ExplicitDynamicsContactConstraint::ExplicitDynamicsContactConstraint( _aux_system(_nodal_area_var->sys()), _aux_solution(_aux_system.currentSolution()), _print_contact_nodes(getParam("print_contact_nodes")), + _residual_copy(_sys.residualGhosted()), _neighbor_density(getNeighborMaterialPropertyByName("density")), _neighbor_wave_speed(getNeighborMaterialPropertyByName("wave_speed")), - _vel_x(&writableVariable("vel_x")), - _vel_y(&writableVariable("vel_y")), - _vel_z(&writableVariable("vel_z")), + _gap_rate(&writableVariable("gap_rate")), _neighbor_vel_x(isCoupled("vel_x") ? coupledNeighborValue("vel_x") : _zero), _neighbor_vel_y(isCoupled("vel_y") ? coupledNeighborValue("vel_y") : _zero), _neighbor_vel_z((_mesh.dimension() == 3 && isCoupled("vel_z")) ? coupledNeighborValue("vel_z") @@ -136,8 +121,6 @@ ExplicitDynamicsContactConstraint::ExplicitDynamicsContactConstraint( } } - mooseInfo("This is the constructor of the explicit dynamics contact constraint."); - if (parameters.isParamValid("tangential_tolerance")) _penetration_locator.setTangentialTolerance(getParam("tangential_tolerance")); @@ -197,9 +180,9 @@ ExplicitDynamicsContactConstraint::updateContactStatefulData(bool beginning_of_s { mooseWarning("Previous step did not converge. Check results"); // The penetration info object could be based on a bad state so delete it - // delete pinfo; - // pinfo = nullptr; - // continue; + delete pinfo; + pinfo = nullptr; + continue; } pinfo->_starting_elem = pinfo->_elem; @@ -213,6 +196,9 @@ ExplicitDynamicsContactConstraint::updateContactStatefulData(bool beginning_of_s bool ExplicitDynamicsContactConstraint::shouldApply() { + if (_current_node->processor_id() != _fe_problem.processor_id()) + return false; + bool in_contact = false; std::map::iterator found = @@ -228,12 +214,7 @@ ExplicitDynamicsContactConstraint::shouldApply() computeContactForce(*_current_node, pinfo, true); if (pinfo->isCaptured()) - { in_contact = true; - - Threads::spin_mutex::scoped_lock lock(_contact_set_mutex); - _current_contact_state.insert(_current_node->id()); - } } } @@ -257,7 +238,7 @@ ExplicitDynamicsContactConstraint::computeContactForce(const Node & node, // Capture nodes that are newly in contact if (update_contact_set && !pinfo->isCaptured() && - MooseUtils::absoluteFuzzyGreaterEqual(gap_size, 0.0, _capture_tolerance)) + MooseUtils::absoluteFuzzyGreaterEqual(gap_size, 0.0, 0.0)) { newly_captured = true; pinfo->capture(); @@ -283,10 +264,10 @@ ExplicitDynamicsContactConstraint::computeContactForce(const Node & node, break; } - if (update_contact_set && pinfo->isCaptured() && !newly_captured && _tension_release >= 0.0) + if (update_contact_set && pinfo->isCaptured() && !newly_captured) { const Real contact_pressure = -(pinfo->_normal * pinfo->_contact_force) / nodalArea(node); - if (-contact_pressure >= _tension_release) + if (-contact_pressure >= 0.0) { pinfo->release(); pinfo->_contact_force.zero(); @@ -297,12 +278,11 @@ ExplicitDynamicsContactConstraint::computeContactForce(const Node & node, void ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & node, PenetrationInfo * pinfo, - const RealVectorValue & /*distance_gap*/) + const RealVectorValue & distance_gap) { - // Stab at momentum balance, uncoupled normal pressure + // Momentum balance, uncoupled normal pressure // See Heinstein et al, 2000, Contact-impact modeling in explicit transient dynamics. - // Secondary surface const auto nodal_area = nodalArea(node); dof_id_type dof_wave_speed = @@ -312,11 +292,12 @@ ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & node, dof_id_type dof_density = node.dof_number(_aux_system.number(), _nodal_density_var->number(), 0); const Real density_secondary = (*_aux_solution)(dof_density); - Real contact_pressure_balance(0.0); Real mass_contact_pressure(0.0); Real gap_rate(0.0); - // Real gap(0.0); + Real gap(0.0); + + gap = distance_gap * pinfo->_normal; mass_contact_pressure = density_secondary * _neighbor_density[0] * wave_speed_secondary * _neighbor_wave_speed[0]; @@ -324,120 +305,104 @@ ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & node, (density_secondary * wave_speed_secondary + _neighbor_density[0] * _neighbor_wave_speed[0]); mass_contact_pressure *= nodal_area; - // Prepare equilibrium loop + dof_id_type dof_x = node.dof_number(_sys.number(), _var_objects[0]->number(), 0); + dof_id_type dof_y = node.dof_number(_sys.number(), _var_objects[1]->number(), 0); + dof_id_type dof_z = node.dof_number(_sys.number(), _var_objects[2]->number(), 0); - bool is_converged(false); - const unsigned int max_no_iterations(20000); - unsigned int iteration_no(0); + auto & u_dot = *_sys.solutionUDot(); + auto & u_old = _sys.solutionOld(); + auto & u_older = _sys.solutionOlder(); - // for (unsigned int i = 0; i < _ndisp; ++i) - // { - // // translational velocities and accelerations - // unsigned int dof_index_0 = node[0]->dof_number(nonlinear_sys.number(), _disp_num[i], 0); - // _vel_0(i) = vel(dof_index_0); - // } + // Mass proxy for secondary node. + const Real mass_proxy = density_secondary * wave_speed_secondary * _dt * nodal_area; - Real velocity_x = dynamic_cast *>(_vel_x)->getNodalValue(node); - Real velocity_y = dynamic_cast *>(_vel_y)->getNodalValue(node); - Real velocity_z = dynamic_cast *>(_vel_z)->getNodalValue(node); + // Include effects of other forces: + // Initial guess: v_{n-1/2} + dt * M^{-1} * (F^{ext} - F^{int}) + Real velocity_x = u_dot(dof_x) + _dt / mass_proxy * _residual_copy(dof_x); + Real velocity_y = u_dot(dof_y) + _dt / mass_proxy * _residual_copy(dof_y); + Real velocity_z = u_dot(dof_z) + _dt / mass_proxy * _residual_copy(dof_z); Real n_velocity_x = _neighbor_vel_x[0]; Real n_velocity_y = _neighbor_vel_y[0]; Real n_velocity_z = _neighbor_vel_z[0]; - Real contact_pressure_old(0.0); + RealVectorValue secondary_velocity( + velocity_x, velocity_y, _mesh.dimension() == 3 ? velocity_z : 0.0); + RealVectorValue closest_point_velocity( + n_velocity_x, n_velocity_y, _mesh.dimension() == 3 ? n_velocity_z : 0.0); + gap_rate = pinfo->_normal * (secondary_velocity - closest_point_velocity); - // Mass proxy for secondary node. - const Real mass_proxy = density_secondary * wave_speed_secondary * _dt * nodal_area; - const Real n_mass_proxy = _neighbor_density[0] * _neighbor_wave_speed[0] * _dt * nodal_area; + // Prepare equilibrium loop + bool is_converged(false); + unsigned int iteration_no(0); + const unsigned int max_no_iterations(20000); + + // Initialize augmented iteration variable + Real gap_rate_old(0.0); + Real force_increment(0.0); + Real force_increment_old(0.0); + Real lambda_iteration(0); while (!is_converged && iteration_no < max_no_iterations) { // Start a loop until we converge on normal contact forces - RealVectorValue secondary_velocity( - velocity_x, velocity_y, _mesh.dimension() == 3 ? velocity_z : 0.0); - RealVectorValue closest_point_velocity( - n_velocity_x, n_velocity_y, _mesh.dimension() == 3 ? n_velocity_z : 0.0); + gap_rate_old = gap_rate; gap_rate = pinfo->_normal * (secondary_velocity - closest_point_velocity); + force_increment_old = force_increment; - contact_pressure_balance = mass_contact_pressure * gap_rate; + force_increment = mass_contact_pressure * gap_rate; - velocity_x = velocity_x - _dt / mass_proxy * pinfo->_normal(0) * - (contact_pressure_balance - contact_pressure_old); - velocity_y = velocity_y - _dt / mass_proxy * pinfo->_normal(1) * - (contact_pressure_balance - contact_pressure_old); - velocity_z = velocity_z - _dt / mass_proxy * pinfo->_normal(2) * - (contact_pressure_balance - contact_pressure_old); + velocity_x -= _dt / mass_proxy * (pinfo->_normal(0) * (force_increment)); + velocity_y -= _dt / mass_proxy * (pinfo->_normal(1) * (force_increment)); + velocity_z -= _dt / mass_proxy * (pinfo->_normal(2) * (force_increment)); - n_velocity_x = n_velocity_x - _dt / n_mass_proxy * pinfo->_normal(0) * - (contact_pressure_balance - contact_pressure_old); - n_velocity_y = n_velocity_y - _dt / n_mass_proxy * pinfo->_normal(1) * - (contact_pressure_balance - contact_pressure_old); - n_velocity_z = n_velocity_z - _dt / n_mass_proxy * pinfo->_normal(2) * - (contact_pressure_balance - contact_pressure_old); + // Let's not modify the neighbor velocity, but apply the corresponding force. + // n_velocity_x = n_velocity_x; + // n_velocity_y = n_velocity_y; + // n_velocity_z = n_velocity_z; - // Convergence check: - const Real relative_error = - (contact_pressure_balance - contact_pressure_old) / contact_pressure_balance; - const Real absolute_error = std::abs(contact_pressure_balance - contact_pressure_old); + secondary_velocity = {velocity_x, velocity_y, _mesh.dimension() == 3 ? velocity_z : 0.0}; + closest_point_velocity = { + n_velocity_x, n_velocity_y, _mesh.dimension() == 3 ? n_velocity_z : 0.0}; - // Moose::out << "For error, current vs old is: " << contact_pressure_balance << ", " - // << contact_pressure_old << "\n"; + // Convergence check + lambda_iteration += force_increment; - contact_pressure_old = contact_pressure_balance; + const Real relative_error = (force_increment - force_increment_old) / force_increment; + const Real absolute_error = std::abs(force_increment); - if (std::abs(relative_error) < TOLERANCE || absolute_error < TOLERANCE) - { - Moose::out << "Relative error of loop: " << std::abs(relative_error) << "\n"; + if (std::abs(relative_error) < TOLERANCE || absolute_error < TOLERANCE || + (gap_rate_old) * (gap_rate) < 0.0) is_converged = true; - } else iteration_no++; } - auto & u_dot = *_sys.solutionUDot(); - dof_id_type dof_x = node.dof_number(_sys.number(), _var_objects[0]->number(), 0); - dof_id_type dof_y = node.dof_number(_sys.number(), _var_objects[1]->number(), 0); - dof_id_type dof_z = node.dof_number(_sys.number(), _var_objects[2]->number(), 0); - - Moose::out << "Dofs set up in this nodal constraint are: " << dof_x << ", " << dof_y << ", and " - << dof_z << "\n"; - - // Set velocities on contact interface according to local, converged solution. - u_dot.set(dof_x, velocity_x); - u_dot.set(dof_y, velocity_y); - u_dot.set(dof_z, velocity_z); - u_dot.close(); - - Moose::out << "Total iterations: " << iteration_no << "\n"; - Moose::out << "vel z *before* local update: " - << dynamic_cast *>(_vel_z)->getNodalValue(node) << "\n"; - Moose::out << "vel z locally after update update: " << velocity_z << "\n"; - - // NonlinearSystemBase & nonlinear_sys = _fe_problem.getNonlinearSystemBase(_sys.number()); - - if (true) + const bool print_debug(false); + if (print_debug) { - numeric_index_type dof_index_x = node.dof_number(_aux_system.number(), _vel_x->number(), 0); - numeric_index_type dof_index_y = node.dof_number(_aux_system.number(), _vel_y->number(), 0); - numeric_index_type dof_index_z = node.dof_number(_aux_system.number(), _vel_z->number(), 0); + _console << "Results of momentun balance iterations for node: " << _current_node->id() << "\n"; + _console << "Total number of iterations: " << iteration_no << "\n"; + _console << "Final normal gap rate (dynamic constraint): " << gap_rate << "\n"; + _console << "Velocity x of secondary node: " << velocity_x << "\n"; + _console << "Velocity y of secondary node: " << velocity_y << "\n"; + _console << "Velocity z of secondary node: " << velocity_z << "\n"; + _console << "Final normal contact force: " << lambda_iteration << "\n"; + } - _vel_x->setNodalValue(velocity_x, dof_index_x); - _vel_y->setNodalValue(velocity_y, dof_index_y); - _vel_z->setNodalValue(velocity_z, dof_index_z); + // Set velocities on contact interface according to local, converged solution. + // Note: Since MOOSE solves for displacements, we need to back calculate the displacements and + // cannot rely on the central difference stepper to operate on rate variables (!) - // vel(dof_index_x) = velocity_x; - // vel(dof_index_y) = velocity_y; - // vel(dof_index_z) = velocity_z; - } + // Discount effects of other forces + u_old.set(dof_x, u_older(dof_x) + velocity_x * _dt); + u_old.set(dof_y, u_older(dof_y) + velocity_y * _dt); + u_old.set(dof_z, u_older(dof_z) + velocity_z * _dt); - Moose::out << "vel z *after* local update: " - << dynamic_cast *>(_vel_z)->nodalValue() << "\n"; + _gap_rate->setNodalValue(gap_rate); - // The gap rate can probably be saved in pinfo by adequately evaluating the velocity variable at - // the corresponding points. Let's do that. - if (contact_pressure_balance < 0.0) - pinfo->_contact_force = pinfo->_normal * contact_pressure_balance; + if (lambda_iteration < 0.0) + pinfo->_contact_force = pinfo->_normal * lambda_iteration; else pinfo->_contact_force = 0.0; } @@ -453,10 +418,11 @@ ExplicitDynamicsContactConstraint::computeQpResidual(Moose::ConstraintType type) { PenetrationInfo * pinfo = _penetration_locator._penetration_info[_current_node->id()]; Real resid = pinfo->_contact_force(_component); + // Maybe only apply force on the primary since we are kinematically enforcing the secondary. switch (type) { case Moose::Secondary: - return _test_secondary[_i][_qp] * resid; + return _test_secondary[_i][_qp] * 0.0; case Moose::Primary: return _test_primary[_i][_qp] * -resid; @@ -524,20 +490,4 @@ ExplicitDynamicsContactConstraint::getCoupledVarComponent(unsigned int var_num, void ExplicitDynamicsContactConstraint::residualEnd() { - if (_component == 0 && (_print_contact_nodes)) - { - _communicator.set_union(_current_contact_state); - if (_print_contact_nodes) - { - if (_current_contact_state == _old_contact_state) - _console << "Unchanged contact state. " << _current_contact_state.size() - << " nodes in contact.\n"; - else - _console << "Changed contact state. " << _current_contact_state.size() - << " nodes in contact.\n"; - } - - _old_contact_state.swap(_current_contact_state); - _current_contact_state.clear(); - } } diff --git a/modules/contact/src/userobjects/NodalDensity.C b/modules/contact/src/userobjects/NodalDensity.C index 3bc60e55e75d..0b1eb9324bb1 100644 --- a/modules/contact/src/userobjects/NodalDensity.C +++ b/modules/contact/src/userobjects/NodalDensity.C @@ -88,15 +88,7 @@ NodalDensity::finalize() { const Node * const node = it->first; dof_id_type dof = node->dof_number(_system.number(), _variable->number(), 0); - _aux_solution.set(dof, 0); - } - _aux_solution.close(); - - for (std::map::iterator it = _node_densities.begin(); it != it_end; ++it) - { - const Node * const node = it->first; - dof_id_type dof = node->dof_number(_system.number(), _variable->number(), 0); - _aux_solution.add(dof, it->second); + _aux_solution.set(dof, it->second); } _aux_solution.close(); } diff --git a/modules/contact/src/userobjects/NodalWaveSpeed.C b/modules/contact/src/userobjects/NodalWaveSpeed.C index c306df4d1d43..37a83f521816 100644 --- a/modules/contact/src/userobjects/NodalWaveSpeed.C +++ b/modules/contact/src/userobjects/NodalWaveSpeed.C @@ -80,21 +80,12 @@ NodalWaveSpeed::execute() void NodalWaveSpeed::finalize() { - const std::map::iterator it_end = _node_wave_speeds.end(); for (std::map::iterator it = _node_wave_speeds.begin(); it != it_end; ++it) { const Node * const node = it->first; dof_id_type dof = node->dof_number(_system.number(), _variable->number(), 0); - _aux_solution.set(dof, 0); - } - _aux_solution.close(); - - for (std::map::iterator it = _node_wave_speeds.begin(); it != it_end; ++it) - { - const Node * const node = it->first; - dof_id_type dof = node->dof_number(_system.number(), _variable->number(), 0); - _aux_solution.add(dof, it->second); + _aux_solution.set(dof, it->second); } _aux_solution.close(); } diff --git a/modules/solid_mechanics/doc/content/source/materials/WaveSpeed.md b/modules/solid_mechanics/doc/content/source/materials/WaveSpeed.md new file mode 100644 index 000000000000..d505b574422a --- /dev/null +++ b/modules/solid_mechanics/doc/content/source/materials/WaveSpeed.md @@ -0,0 +1,15 @@ +# Wave Speed + +!syntax description /Materials/WaveSpeed + +## Description + +The material `WaveSpeed` computes an approximate local +wave speed for use in explicit dynamic simulations. + + +!syntax parameters /Materials/WaveSpeed + +!syntax inputs /Materials/WaveSpeed + +!syntax children /Materials/WaveSpeed From d5a58b9d59d414ccd37625b4ac3bc9f934afc8a0 Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Tue, 5 Dec 2023 16:28:26 -0700 Subject: [PATCH 13/31] Add heavy test --- .../explicit_dynamics/gold/first_test_out.csv | 70 ++-- .../explicit_dynamics/gold/settlement_out.e | Bin 0 -> 160388 bytes .../explicit_dynamics/gold/test_balance_out.e | Bin 216924 -> 216924 bytes .../contact/explicit_dynamics/settlement.i | 339 ++++++++++++++++++ modules/contact/explicit_dynamics/tests | 10 + .../ExplicitDynamicsContactConstraint.C | 2 +- 6 files changed, 385 insertions(+), 36 deletions(-) create mode 100644 modules/contact/explicit_dynamics/gold/settlement_out.e create mode 100644 modules/contact/explicit_dynamics/settlement.i diff --git a/modules/contact/explicit_dynamics/gold/first_test_out.csv b/modules/contact/explicit_dynamics/gold/first_test_out.csv index 70f6348dbac6..b44898b00aa4 100644 --- a/modules/contact/explicit_dynamics/gold/first_test_out.csv +++ b/modules/contact/explicit_dynamics/gold/first_test_out.csv @@ -17,38 +17,38 @@ time,contact_pressure_max,critical_time_step,disp_58z 0.065,0,0.1,-9.1457120483056e-05 0.07,0,0.1,-9.8317608721322e-05 0.075,0,0.1,-0.00010514615547046 -0.08,0.082342600269299,0.1,-0.0001119413563371 -0.085,0.19106600107269,0.1,-0.00011870180328649 -0.09,0.2992319466837,0.1,-0.00012542609282485 -0.095,0.40681732201413,0.1,-0.00013211282617698 -0.1,0.51379916468013,0.1,-0.00013876060942624 -0.105,0.62015466406831,0.1,-0.00014536805364947 -0.11,0.72586115989849,0.1,-0.00015193377505034 -0.115,0.83089614035709,0.1,-0.00015845639509274 -0.12,0.93523723989044,0.1,-0.00016493454063544 -0.125,1.0388622367508,0.1,-0.00017136684406851 -0.13,1.1417490503861,0.1,-0.00017775194345166 -0.135,1.2438757387596,0.1,-0.0001840884826543 -0.14,1.345220495677,0.1,-0.00019037511149702 -0.145,1.445762257348,0.1,-0.00019661048589375 -0.15,1.5454790652346,0.1,-0.00020279326799438 -0.155,1.6443494690639,0.1,-0.00020892212632706 -0.16,1.7423521982298,0.1,-0.00021499573594003 -0.165,1.8394661076241,0.1,-0.00022101277854242 -0.17,1.9356701759766,0.1,-0.00022697194264373 -0.175,2.0309435044819,0.1,-0.00023287192369192 -0.18,2.1252653157298,0.1,-0.00023871142420983 -0.185,2.2186149529507,0.1,-0.0002444891539297 -0.19,2.3109718795812,0.1,-0.00025020382992607 -0.195,2.4023156791516,0.1,-0.00025585417674664 -0.2,2.492626055489,0.1,-0.00026143892654142 -0.205,2.5818828332289,0.1,-0.00026695681919008 -0.21,2.6700659586206,0.1,-0.00027240660242766 -0.215,2.7571555006078,0.1,-0.00027778703196863 -0.22,2.8431316521658,0.1,-0.00028309687162956 -0.225,2.9279747318706,0.1,-0.00028833489345041 -0.23,3.0116651856744,0.1,-0.00029349987781456 -0.235,3.094183588863,0.1,-0.0002985906135678 -0.24,3.1755106481672,0.1,-0.00030360589813622 -0.245,3.2556272040036,0.1,-0.00030854446430282 -0.25,3.3345170057449,0.1,-0.00031340512549616 +0.08,0.082342600269299,0.1,-0.00011194135660093 +0.085,0.19101520520849,0.1,-0.00011870180198318 +0.09,0.2990125934109,0.1,-0.00012542608101362 +0.095,0.40624522737138,0.1,-0.00013211278042684 +0.1,0.51262435889857,0.1,-0.00013876048209793 +0.105,0.61806212050844,0.1,-0.00014536776088066 +0.11,0.72247161511198,0.1,-0.0001519331824919 +0.115,0.82576700393275,0.1,-0.0001584553014484 +0.12,0.92786359262147,0.1,-0.0001649326590604 +0.125,1.0286779155446,0.1,-0.00017136378148647 +0.13,1.1281278182246,0.1,-0.00017774717785316 +0.135,1.2261325379052,0.1,-0.00018408133844316 +0.14,1.3226127822114,0.1,-0.00019036473295566 +0.145,1.4174920367121,0.1,-0.00019659580884224 +0.15,1.5106936533543,0.1,-0.00020277298972196 +0.155,1.6021427551876,0.1,-0.00020889467387898 +0.16,1.6917666866988,0.1,-0.00021495923284641 +0.165,1.7794946285915,0.1,-0.00022096501007964 +0.17,1.8652576643615,0.1,-0.00022691031972277 +0.175,1.9489888444021,0.1,-0.00023279344547155 +0.18,2.0306232475643,0.1,-0.00023861263953621 +0.185,2.1100980401007,0.1,-0.00024436612170748 +0.19,2.1873525319252,0.1,-0.0002500520785289 +0.195,2.2623282301271,0.1,-0.00025566866257851 +0.2,2.3349688896757,0.1,-0.00026121399186243 +0.205,2.4052205612584,0.1,-0.00026668614932302 +0.21,2.4730316361914,0.1,-0.00027208318246378 +0.215,2.5383528883463,0.1,-0.00027740310309278 +0.22,2.6011375130337,0.1,-0.00028264388718642 +0.225,2.6613411627903,0.1,-0.00028780347487477 +0.23,2.7189219800124,0.1,-0.00029287977054953 +0.235,2.7738406263866,0.1,-0.00029787064309539 +0.24,2.8260603090661,0.1,-0.00030277392624536 +0.245,2.8755468035504,0.1,-0.00030758742177669 +0.25,2.9222732166101,0.1,-0.00031230889093653 diff --git a/modules/contact/explicit_dynamics/gold/settlement_out.e b/modules/contact/explicit_dynamics/gold/settlement_out.e new file mode 100644 index 0000000000000000000000000000000000000000..eeb982b496aec40aad0ab45545b3fa638eaaf303 GIT binary patch literal 160388 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 B-u3_h literal 0 HcmV?d00001 diff --git a/modules/contact/explicit_dynamics/gold/test_balance_out.e b/modules/contact/explicit_dynamics/gold/test_balance_out.e index 16f4ce67c9fb523c0d2a2082a946dac0e4a6d1e2..1fc05bd786a792656931cfbcdb2f23f9e7f02f3b 100644 GIT binary patch delta 23918 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 diff --git a/modules/contact/explicit_dynamics/settlement.i b/modules/contact/explicit_dynamics/settlement.i new file mode 100644 index 000000000000..3814c07212a5 --- /dev/null +++ b/modules/contact/explicit_dynamics/settlement.i @@ -0,0 +1,339 @@ +# One element test to test the central difference time integrator in 3D. +[GlobalParams] + displacements = 'disp_x disp_y disp_z' + volumetric_locking_correction = true +[] + +[Mesh] + [block_one] + type = GeneratedMeshGenerator + dim = 3 + nx = 3 + ny = 3 + nz = 3 + xmin = 4.5 + xmax = 5.5 + ymin = 4.5 + ymax = 5.5 + zmin = 0.0001 + zmax = 1.0001 + boundary_name_prefix = 'ball' + [] + [block_two] + type = GeneratedMeshGenerator + dim = 3 + nx = 2 + ny = 2 + nz = 2 + xmin = 0.0 + xmax = 10 + ymin = 0.0 + ymax = 10 + zmin = -2 + zmax = 0 + boundary_name_prefix = 'base' + boundary_id_offset = 10 + [] + [block_one_id] + type = SubdomainIDGenerator + input = block_one + subdomain_id = 1 + [] + [block_two_id] + type = SubdomainIDGenerator + input = block_two + subdomain_id = 2 + [] + [combine] + type = MeshCollectionGenerator + inputs = ' block_one_id block_two_id' + [] +[] + + +[AuxVariables] + [pid] + family = MONOMIAL + order = CONSTANT + [] +[] + +[AuxKernels] + [pid_aux] + type = ProcessorIDAux + variable = pid + execute_on = 'INITIAL' + [] +[] + + +[Variables] + [disp_x] + [] + [disp_y] + [] + [disp_z] + [] +[] + +[AuxVariables] + [gap_rate] + [] + [vel_x] + [] + [accel_x] + [] + [vel_y] + [] + [accel_y] + [] + [vel_z] + [] + [accel_z] + [] + [stress_zz] + family = MONOMIAL + order = CONSTANT + [] + [strain_zz] + family = MONOMIAL + order = CONSTANT + [] +[] + +[AuxKernels] + [stress_zz] + type = RankTwoAux + rank_two_tensor = stress + index_i = 2 + index_j = 2 + variable = stress_zz + execute_on = 'TIMESTEP_END' + [] + [strain_zz] + type = RankTwoAux + rank_two_tensor = mechanical_strain + index_i = 2 + index_j = 2 + variable = strain_zz + [] + [accel_x] + type = TestNewmarkTI + variable = accel_x + displacement = disp_x + first = false + execute_on = 'LINEAR TIMESTEP_BEGIN TIMESTEP_END' + [] + [vel_x] + type = TestNewmarkTI + variable = vel_x + displacement = disp_x + execute_on = 'LINEAR TIMESTEP_BEGIN TIMESTEP_END' + [] + [accel_y] + type = TestNewmarkTI + variable = accel_y + displacement = disp_y + first = false + execute_on = 'LINEAR TIMESTEP_BEGIN TIMESTEP_END' + [] + [vel_y] + type = TestNewmarkTI + variable = vel_y + displacement = disp_x + execute_on = 'LINEAR TIMESTEP_BEGIN TIMESTEP_END' + [] + [accel_z] + type = TestNewmarkTI + variable = accel_z + displacement = disp_z + first = false + execute_on = 'LINEAR TIMESTEP_BEGIN TIMESTEP_END' + [] + [vel_z] + type = TestNewmarkTI + variable = vel_z + displacement = disp_z + execute_on = 'LINEAR TIMESTEP_BEGIN TIMESTEP_END' + [] +[] + +[Kernels] + [DynamicTensorMechanics] + displacements = 'disp_x disp_y disp_z' + volumetric_locking_correction = true + stiffness_damping_coefficient = 0.001 + generate_output = 'stress_zz strain_zz' + [] + [inertia_x] + type = InertialForce + variable = disp_x + [] + [inertia_y] + type = InertialForce + variable = disp_y + [] + [inertia_z] + type = InertialForce + variable = disp_z + [] +[] + +[Kernels] + [gravity] + type = Gravity + variable = disp_z + value = -981.0 + [] +[] + +[BCs] + [x_front] + type = DirichletBC + variable = disp_x + boundary = 'ball_front' + preset = false + value = 0.0 + [] + [y_front] + type = DirichletBC + variable = disp_y + boundary = 'ball_front' + preset = false + value = 0.0 + [] + [x_fixed] + type = DirichletBC + variable = disp_x + boundary = 'base_back' + preset = false + value = 0.0 + [] + [y_fixed] + type = DirichletBC + variable = disp_y + boundary = 'base_back' + preset = false + value = 0.0 + [] + [z_fixed] + type = DirichletBC + variable = disp_z + boundary = 'base_back' + preset = false + value = 0.0 + [] + [z_fixed_front] + type = DirichletBC + variable = disp_z + boundary = 'base_front' + preset = false + value = 0.0 + [] +[] + +[ExplicitDynamicsContact] + [my_contact] + model = frictionless_balance + primary = base_front + secondary = ball_back + vel_x = 'vel_x' + vel_y = 'vel_y' + vel_z = 'vel_z' + [] +[] + +[Materials] + [elasticity_tensor_block_one] + type = ComputeIsotropicElasticityTensor + youngs_modulus = 1e6 + poissons_ratio = 0.0 + block = 1 + outputs = 'exodus' + output_properties = __all__ + [] + [elasticity_tensor_block_two] + type = ComputeIsotropicElasticityTensor + youngs_modulus = 1e10 + poissons_ratio = 0.0 + block = 2 + outputs = 'exodus' + output_properties = __all__ + [] + [strain_block] + type = ComputeFiniteStrain # ComputeIncrementalSmallStrain + displacements = 'disp_x disp_y disp_z' + implicit = false + [] + [stress_block] + type = ComputeFiniteStrainElasticStress + [] + [density_one] + type = GenericConstantMaterial + prop_names = density + prop_values = 1e1 + outputs = 'exodus' + output_properties = 'density' + block = '1' + [] + [density_two] + type = GenericConstantMaterial + prop_names = density + prop_values = 1e6 + outputs = 'exodus' + output_properties = 'density' + block = '2' + [] + [wave_speed] + type = WaveSpeed + outputs = 'exodus' + output_properties = 'wave_speed' + [] +[] + +[Executioner] + type = Transient + start_time = -0.01 + end_time = 0.15 + dt = 0.0002 + timestep_tolerance = 1e-6 + + [TimeIntegrator] + type = CentralDifference + solve_type = lumped + [] +[] + +[Outputs] + interval = 10 + exodus = true + csv = true + execute_on = 'FINAL' +[] + +[Postprocessors] + [accel_58z] + type = NodalVariableValue + nodeid = 1 + variable = accel_z + [] + [vel_58z] + type = NodalVariableValue + nodeid = 1 + variable = vel_z + [] + [disp_58z] + type = NodalVariableValue + nodeid = 1 + variable = disp_z + [] + [critical_time_step] + type = CriticalTimeStep + [] + [contact_pressure_max] + type = NodalExtremeValue + variable = contact_pressure + block = '1 2' + value_type = max + [] +[] diff --git a/modules/contact/explicit_dynamics/tests b/modules/contact/explicit_dynamics/tests index 67676c49b319..4f6f3e95dd0c 100644 --- a/modules/contact/explicit_dynamics/tests +++ b/modules/contact/explicit_dynamics/tests @@ -18,4 +18,14 @@ requirement = 'The system shall be able to solve a simple few-element normal contact problem ' 'using explicit dynamics solving uncoupled, local equations of momentum balance.' [] + [settlement] + type = 'Exodiff' + input = 'settlement.i' + exodiff = 'settlement_out.e' + abs_zero = 1.0e-4 + requirement = 'The system shall be able to solve a simple few-element normal contact problem ' + 'using explicit dynamics solving uncoupled, local equations of momentum balance ' + 'during an impact-settling under gravity acceleration.' + heavy = true + [] [] diff --git a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C index 9febeec1d97e..4cceaf292c2b 100644 --- a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C +++ b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C @@ -422,7 +422,7 @@ ExplicitDynamicsContactConstraint::computeQpResidual(Moose::ConstraintType type) switch (type) { case Moose::Secondary: - return _test_secondary[_i][_qp] * 0.0; + return _test_secondary[_i][_qp] * resid; case Moose::Primary: return _test_primary[_i][_qp] * -resid; From 7d5014529e8b6bd2cdf2cba5da7d2f23790cc396 Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Fri, 8 Dec 2023 12:01:34 -0700 Subject: [PATCH 14/31] Remove unused gap variable --- .../src/constraints/ExplicitDynamicsContactConstraint.C | 7 +++---- test/tests/auxkernels/nodal_aux_var/tests | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C index 4cceaf292c2b..0ca88602233a 100644 --- a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C +++ b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C @@ -278,7 +278,7 @@ ExplicitDynamicsContactConstraint::computeContactForce(const Node & node, void ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & node, PenetrationInfo * pinfo, - const RealVectorValue & distance_gap) + const RealVectorValue & /*distance_gap*/) { // Momentum balance, uncoupled normal pressure // See Heinstein et al, 2000, Contact-impact modeling in explicit transient dynamics. @@ -295,9 +295,8 @@ ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & node, Real mass_contact_pressure(0.0); Real gap_rate(0.0); - Real gap(0.0); - - gap = distance_gap * pinfo->_normal; + // Real gap(0.0); + // gap = distance_gap * pinfo->_normal; mass_contact_pressure = density_secondary * _neighbor_density[0] * wave_speed_secondary * _neighbor_wave_speed[0]; diff --git a/test/tests/auxkernels/nodal_aux_var/tests b/test/tests/auxkernels/nodal_aux_var/tests index 22851278b5f5..8fabddc8dcc6 100644 --- a/test/tests/auxkernels/nodal_aux_var/tests +++ b/test/tests/auxkernels/nodal_aux_var/tests @@ -34,8 +34,8 @@ [multi_update_error] type = RunException input = multi_update_var_error.i - expect_err = "writableVariable\(\) can only be called from AuxKernels, ElementUserObjects, or " - "NodalUserObjects\. 'all' is neither of those\." + expect_err = "writableVariable\(\) can only be called from AuxKernels, ElementUserObjects, " + "NodalUserObjects, or NodeFaceConstraints\. 'all' is none of those\." issues = '#22563' allow_deprecated = true requirement = "Writing to auxiliary variables shall be limited to use from AuxKernels, " From 6c870029a9671595a5ccbf5f4e9346102c8b1a61 Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Tue, 2 Jan 2024 14:53:40 -0700 Subject: [PATCH 15/31] Fix documentation --- modules/contact/doc/content/bib/contact.bib | 33 ++++++++++++------- .../actions/ExplicitDynamicsContactAction.md | 4 +-- .../ExplicitDynamicsContactConstraint.md | 17 ++++++++++ 3 files changed, 40 insertions(+), 14 deletions(-) create mode 100644 modules/contact/doc/content/source/constraints/ExplicitDynamicsContactConstraint.md diff --git a/modules/contact/doc/content/bib/contact.bib b/modules/contact/doc/content/bib/contact.bib index ed25918f3af1..2d19fac638e9 100644 --- a/modules/contact/doc/content/bib/contact.bib +++ b/modules/contact/doc/content/bib/contact.bib @@ -57,18 +57,18 @@ @techreport{jiang2021efficient } @article{DEMKOWICZ2018102, -title = {Key results from irradiation and post-irradiation examination of {AGR}-1 {UCO} {TRISO} fuel}, -journal = {Nuclear Engineering and Design}, -volume = {329}, -pages = {102-109}, -year = {2018}, -note = {The Best of HTR 2016: International Topical Meeting on High Temperature Reactor Technology}, -issn = {0029-5493}, -doi = {https://doi.org/10.1016/j.nucengdes.2017.09.005}, -url = {https://www.sciencedirect.com/science/article/pii/S0029549317304351}, -author = {Paul A. Demkowicz and John D. Hunn and David A. Petti and Robert N. Morris}, -keywords = {TRISO, AGR-1, UCO, Fuel irradiation, Post-irradiation examination, Fuel performance}, -abstract = {The AGR-1 irradiation experiment was performed as the first test of tristructural isotropic (TRISO) fuel in the US Advanced Gas Reactor Fuel Development and Qualification Program. The experiment consisted of 72 right cylinder fuel compacts containing approximately 3×105 coated fuel particles with uranium oxide/uranium carbide (UCO) fuel kernels. The fuel was irradiated in the Advanced Test Reactor for a total of 620 effective full power days. Fuel burnup ranged from 11.3 to 19.6\% fissions per initial metal atom and time-average, volume-average irradiation temperatures of the individual compacts ranged from 955 to 1136°C. This paper focuses on key results from the irradiation and post-irradiation examination, which revealed a robust fuel with excellent performance characteristics under the conditions tested and have significantly improved the understanding of UCO coated particle fuel irradiation behavior. The fuel exhibited zero TRISO coating failures (failure of all three dense coating layers) during irradiation and post-irradiation safety testing at temperatures up to 1700°C. Advanced PIE methods have allowed particles with SiC coating failure that were discovered to be present in a very-low population to be isolated and meticulously examined, which has elucidated the specific causes of SiC failure in these specimens. The level of fission product release from the fuel during irradiation and post-irradiation safety testing has been studied in detail. Results indicated very low release of krypton and cesium through intact SiC and modest release of europium and strontium, while also confirming the potential for significant silver release through the coatings depending on irradiation conditions. Focused study of fission products within the coating layers of irradiated particles down to nanometer length scales has provided new insights into fission product transport through the coating layers and the role various fission products may have on coating integrity. The broader implications of these results and the application of lessons learned from AGR-1 to fuel fabrication and post-irradiation examination for subsequent fuel irradiation experiments as part of the US fuel program are also discussed.} + title = {Key results from irradiation and post-irradiation examination of {AGR}-1 {UCO} {TRISO} fuel}, + journal = {Nuclear Engineering and Design}, + volume = {329}, + pages = {102-109}, + year = {2018}, + note = {The Best of HTR 2016: International Topical Meeting on High Temperature Reactor Technology}, + issn = {0029-5493}, + doi = {https://doi.org/10.1016/j.nucengdes.2017.09.005}, + url = {https://www.sciencedirect.com/science/article/pii/S0029549317304351}, + author = {Paul A. Demkowicz and John D. Hunn and David A. Petti and Robert N. Morris}, + keywords = {TRISO, AGR-1, UCO, Fuel irradiation, Post-irradiation examination, Fuel performance}, + abstract = {The AGR-1 irradiation experiment was performed as the first test of tristructural isotropic (TRISO) fuel in the US Advanced Gas Reactor Fuel Development and Qualification Program. The experiment consisted of 72 right cylinder fuel compacts containing approximately 3×105 coated fuel particles with uranium oxide/uranium carbide (UCO) fuel kernels. The fuel was irradiated in the Advanced Test Reactor for a total of 620 effective full power days. Fuel burnup ranged from 11.3 to 19.6\% fissions per initial metal atom and time-average, volume-average irradiation temperatures of the individual compacts ranged from 955 to 1136°C. This paper focuses on key results from the irradiation and post-irradiation examination, which revealed a robust fuel with excellent performance characteristics under the conditions tested and have significantly improved the understanding of UCO coated particle fuel irradiation behavior. The fuel exhibited zero TRISO coating failures (failure of all three dense coating layers) during irradiation and post-irradiation safety testing at temperatures up to 1700°C. Advanced PIE methods have allowed particles with SiC coating failure that were discovered to be present in a very-low population to be isolated and meticulously examined, which has elucidated the specific causes of SiC failure in these specimens. The level of fission product release from the fuel during irradiation and post-irradiation safety testing has been studied in detail. Results indicated very low release of krypton and cesium through intact SiC and modest release of europium and strontium, while also confirming the potential for significant silver release through the coatings depending on irradiation conditions. Focused study of fission products within the coating layers of irradiated particles down to nanometer length scales has provided new insights into fission product transport through the coating layers and the role various fission products may have on coating integrity. The broader implications of these results and the application of lessons learned from AGR-1 to fuel fabrication and post-irradiation examination for subsequent fuel irradiation experiments as part of the US fuel program are also discussed.} } @techreport{martin2022implement, title={Implement and test {3D} mortar contact in BISON}, @@ -122,5 +122,14 @@ @article{benzeggagh1996measurement number={4}, pages={439--449}, year={1996}, + +@article{heinstein2000contact, + title={Contact—impact modeling in explicit transient dynamics}, + author={Heinstein, Martin W and Mello, Frank J and Attaway, Stephen W and Laursen, Tod A}, + journal={Computer Methods in Applied Mechanics and Engineering}, + volume={187}, + number={3-4}, + pages={621--640}, + year={2000}, publisher={Elsevier} } diff --git a/modules/contact/doc/content/source/actions/ExplicitDynamicsContactAction.md b/modules/contact/doc/content/source/actions/ExplicitDynamicsContactAction.md index d73eec344a8f..14c85a910184 100644 --- a/modules/contact/doc/content/source/actions/ExplicitDynamicsContactAction.md +++ b/modules/contact/doc/content/source/actions/ExplicitDynamicsContactAction.md @@ -1,6 +1,6 @@ -# Contact Action +# ExplicitDynamicsContact Action -!syntax description /Contact/ExplicitDynamicsContactAction +!syntax description /ExplicitDynamicsContact/ExplicitDynamicsContactAction ## Description diff --git a/modules/contact/doc/content/source/constraints/ExplicitDynamicsContactConstraint.md b/modules/contact/doc/content/source/constraints/ExplicitDynamicsContactConstraint.md new file mode 100644 index 000000000000..c8d65c4c42d6 --- /dev/null +++ b/modules/contact/doc/content/source/constraints/ExplicitDynamicsContactConstraint.md @@ -0,0 +1,17 @@ +# ExplicitDynamicsContactConstraint + +!syntax description /Constraints/ExplicitDynamicsContactConstraint + +This object implements node-face constraints for the enforcement of normal +mechanical contact in explicit dynamics. Surrogate balance-momentum equations are +solved at each node on the secondary surface using density and wave speed +material properties and the velocities of the two surfaces in contact. + +For relevant, general equations, see [!citep](heinstein2000contact), in particular, +Equations (15), (21), (26) and (29). + +!syntax parameters /Constraints/ExplicitDynamicsContactConstraint + +!syntax inputs /Constraints/ExplicitDynamicsContactConstraint + +!syntax children /Constraints/ExplicitDynamicsContactConstraint From 5cb31328836b5377a12013c45e63b476970c9060 Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Tue, 2 Jan 2024 16:27:41 -0700 Subject: [PATCH 16/31] Move test folder and remove unnecessary mutex. Fix website. Only update auxiliary system in node-face constraints if we are writing to variables. --- framework/include/systems/NonlinearSystemBase.h | 3 --- framework/src/systems/NonlinearSystemBase.C | 8 ++++++-- .../actions/ExplicitDynamicsContactAction.md | 4 +++- .../ExplicitDynamicsContactConstraint.md | 2 +- .../contact/doc/content/syntax/Contact/index.md | 4 ++++ .../syntax/ExplicitDynamicsContact/index.md | 16 ++++++++++++++++ .../src/actions/ExplicitDynamicsContactAction.C | 3 ++- .../tests}/explicit_dynamics/first_test.i | 0 .../explicit_dynamics/gold/first_test_out.csv | 0 .../explicit_dynamics/gold/settlement_out.e | Bin .../explicit_dynamics/gold/test_balance_out.e | Bin .../tests}/explicit_dynamics/settlement.i | 0 .../tests}/explicit_dynamics/test_balance.i | 1 + .../{ => test/tests}/explicit_dynamics/tests | 0 14 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 modules/contact/doc/content/syntax/ExplicitDynamicsContact/index.md rename modules/contact/{ => test/tests}/explicit_dynamics/first_test.i (100%) rename modules/contact/{ => test/tests}/explicit_dynamics/gold/first_test_out.csv (100%) rename modules/contact/{ => test/tests}/explicit_dynamics/gold/settlement_out.e (100%) rename modules/contact/{ => test/tests}/explicit_dynamics/gold/test_balance_out.e (100%) rename modules/contact/{ => test/tests}/explicit_dynamics/settlement.i (100%) rename modules/contact/{ => test/tests}/explicit_dynamics/test_balance.i (99%) rename modules/contact/{ => test/tests}/explicit_dynamics/tests (100%) diff --git a/framework/include/systems/NonlinearSystemBase.h b/framework/include/systems/NonlinearSystemBase.h index 23c18cfd0cf3..3048a69f6fc4 100644 --- a/framework/include/systems/NonlinearSystemBase.h +++ b/framework/include/systems/NonlinearSystemBase.h @@ -983,7 +983,4 @@ class NonlinearSystemBase : public SystemBase, public PerfGraphInterface /// The number of scaling groups std::size_t _num_scaling_groups; - - /// Mutex for auxiliary variables - static Threads::spin_mutex writable_variable_mutex; }; diff --git a/framework/src/systems/NonlinearSystemBase.C b/framework/src/systems/NonlinearSystemBase.C index 188b77e16530..da5ad648f7c2 100644 --- a/framework/src/systems/NonlinearSystemBase.C +++ b/framework/src/systems/NonlinearSystemBase.C @@ -1275,6 +1275,8 @@ NonlinearSystemBase::constraintResiduals(NumericVector & residual, bool BoundaryID secondary_boundary = pen_loc._secondary_boundary; BoundaryID primary_boundary = pen_loc._primary_boundary; + bool has_writable_variables(false); + if (_constraints.hasActiveNodeFaceConstraints(secondary_boundary, displaced)) { const auto & constraints = @@ -1356,6 +1358,7 @@ NonlinearSystemBase::constraintResiduals(NumericVector & residual, bool if (nfc->hasWritableCoupledVariables()) { Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx); + has_writable_variables = true; for (auto * var : nfc->getWritableCoupledVariables()) { if (var->isNodalDefined()) @@ -1367,8 +1370,9 @@ NonlinearSystemBase::constraintResiduals(NumericVector & residual, bool } } } - const bool has_explicit_contact(true); - if (has_explicit_contact) + _communicator.max(has_writable_variables); + + if (has_writable_variables) { _fe_problem.getAuxiliarySystem().solution().close(); _fe_problem.getAuxiliarySystem().system().update(); diff --git a/modules/contact/doc/content/source/actions/ExplicitDynamicsContactAction.md b/modules/contact/doc/content/source/actions/ExplicitDynamicsContactAction.md index 14c85a910184..67a8eaf13cc2 100644 --- a/modules/contact/doc/content/source/actions/ExplicitDynamicsContactAction.md +++ b/modules/contact/doc/content/source/actions/ExplicitDynamicsContactAction.md @@ -1,7 +1,9 @@ # ExplicitDynamicsContact Action -!syntax description /ExplicitDynamicsContact/ExplicitDynamicsContactAction +!syntax description /Contact/ContactAction + ## Description ExplicitDynamicsContactAction is a MOOSE action that constructs objects needed for mechanical contact enforcement for explicit dynamics finite element simulations. + diff --git a/modules/contact/doc/content/source/constraints/ExplicitDynamicsContactConstraint.md b/modules/contact/doc/content/source/constraints/ExplicitDynamicsContactConstraint.md index c8d65c4c42d6..49b6501e6480 100644 --- a/modules/contact/doc/content/source/constraints/ExplicitDynamicsContactConstraint.md +++ b/modules/contact/doc/content/source/constraints/ExplicitDynamicsContactConstraint.md @@ -7,7 +7,7 @@ mechanical contact in explicit dynamics. Surrogate balance-momentum equations ar solved at each node on the secondary surface using density and wave speed material properties and the velocities of the two surfaces in contact. -For relevant, general equations, see [!citep](heinstein2000contact), in particular, +For relevant equations, see [!citep](heinstein2000contact), in particular, Equations (15), (21), (26) and (29). !syntax parameters /Constraints/ExplicitDynamicsContactConstraint diff --git a/modules/contact/doc/content/syntax/Contact/index.md b/modules/contact/doc/content/syntax/Contact/index.md index ca6d72adfcef..eaf04997c3e5 100644 --- a/modules/contact/doc/content/syntax/Contact/index.md +++ b/modules/contact/doc/content/syntax/Contact/index.md @@ -101,6 +101,10 @@ The multiple contact pairs feature is not yet available for mortar contact. !listing test/tests/multiple_contact_pairs/multiple_pairs.i block=Contact +## Explicit dynamics + +For explicit dynamics contact setup, the action [ExplicitDynamicsContactAction](/actions/ExplicitDynamicsContactAction.md) is to be used. + ## Example Input syntax id=example Node/face frictionless contact: diff --git a/modules/contact/doc/content/syntax/ExplicitDynamicsContact/index.md b/modules/contact/doc/content/syntax/ExplicitDynamicsContact/index.md new file mode 100644 index 000000000000..31e762f675ad --- /dev/null +++ b/modules/contact/doc/content/syntax/ExplicitDynamicsContact/index.md @@ -0,0 +1,16 @@ +# ExplicitDynamicsContact + +## Description + +The `ExplicitDynamicsContact` block can be used to specify parameters related to mechanical contact enforcement in +MOOSE simulations. The [ExplicitDynamicsContact](/actions/ExplicitDynamicsContactAction.md) is associated with this +input block, and is the class that performs the associated model setup tasks. For details on the enforcement of contact +constraints in explicit dynamics simulations, see [ExplicitDynamicsContactConstraint](/constraints/ExplicitDynamicsContactConstraint.md) + +Currently, only normal contact is supported with explicit dynamics. + +!syntax parameters /ExplicitDynamicsContact/ExplicitDynamicsContactAction + +!syntax inputs /ExplicitDynamicsContact/ExplicitDynamicsContactAction + +!syntax children /ExplicitDynamicsContact/ExplicitDynamicsContactAction diff --git a/modules/contact/src/actions/ExplicitDynamicsContactAction.C b/modules/contact/src/actions/ExplicitDynamicsContactAction.C index f8d4f2df6056..9902cfd1c76f 100644 --- a/modules/contact/src/actions/ExplicitDynamicsContactAction.C +++ b/modules/contact/src/actions/ExplicitDynamicsContactAction.C @@ -61,7 +61,8 @@ ExplicitDynamicsContactAction::validParams() "model", ExplicitDynamicsContactAction::getModelEnum(), "The contact model to use"); params.addParam("tangential_tolerance", "Tangential distance to extend edges of contact surfaces"); - params.addClassDescription("Sets up all objects needed for mechanical contact enforcement"); + params.addClassDescription("Sets up all objects needed for mechanical contact enforcement in " + "explicit dynamics simulations."); params.addParam>( "extra_vector_tags", "The tag names for extra vectors that residual data should be saved into"); diff --git a/modules/contact/explicit_dynamics/first_test.i b/modules/contact/test/tests/explicit_dynamics/first_test.i similarity index 100% rename from modules/contact/explicit_dynamics/first_test.i rename to modules/contact/test/tests/explicit_dynamics/first_test.i diff --git a/modules/contact/explicit_dynamics/gold/first_test_out.csv b/modules/contact/test/tests/explicit_dynamics/gold/first_test_out.csv similarity index 100% rename from modules/contact/explicit_dynamics/gold/first_test_out.csv rename to modules/contact/test/tests/explicit_dynamics/gold/first_test_out.csv diff --git a/modules/contact/explicit_dynamics/gold/settlement_out.e b/modules/contact/test/tests/explicit_dynamics/gold/settlement_out.e similarity index 100% rename from modules/contact/explicit_dynamics/gold/settlement_out.e rename to modules/contact/test/tests/explicit_dynamics/gold/settlement_out.e diff --git a/modules/contact/explicit_dynamics/gold/test_balance_out.e b/modules/contact/test/tests/explicit_dynamics/gold/test_balance_out.e similarity index 100% rename from modules/contact/explicit_dynamics/gold/test_balance_out.e rename to modules/contact/test/tests/explicit_dynamics/gold/test_balance_out.e diff --git a/modules/contact/explicit_dynamics/settlement.i b/modules/contact/test/tests/explicit_dynamics/settlement.i similarity index 100% rename from modules/contact/explicit_dynamics/settlement.i rename to modules/contact/test/tests/explicit_dynamics/settlement.i diff --git a/modules/contact/explicit_dynamics/test_balance.i b/modules/contact/test/tests/explicit_dynamics/test_balance.i similarity index 99% rename from modules/contact/explicit_dynamics/test_balance.i rename to modules/contact/test/tests/explicit_dynamics/test_balance.i index a7757c4ea539..bb4f287f53fc 100644 --- a/modules/contact/explicit_dynamics/test_balance.i +++ b/modules/contact/test/tests/explicit_dynamics/test_balance.i @@ -48,6 +48,7 @@ type = MeshCollectionGenerator inputs = ' block_one_id block_two_id' [] + allow_renumbering = false [] [Variables] diff --git a/modules/contact/explicit_dynamics/tests b/modules/contact/test/tests/explicit_dynamics/tests similarity index 100% rename from modules/contact/explicit_dynamics/tests rename to modules/contact/test/tests/explicit_dynamics/tests From e3e67d3204dbf91e927a884fafe08e7cc0b73f6e Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Thu, 4 Jan 2024 08:50:00 -0700 Subject: [PATCH 17/31] Fix critical time step postprocessor threading behavior. When multiple threads provide values for the critical values for the time step, pick the minimum value. Otherwise: - It's numerically inaccurate, only the minimum value would guarantee stability. - The behavior would be inconsistent with parallel processing --- modules/solid_mechanics/src/postprocessors/CriticalTimeStep.C | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/solid_mechanics/src/postprocessors/CriticalTimeStep.C b/modules/solid_mechanics/src/postprocessors/CriticalTimeStep.C index f42fec5591b7..a7390b0a72b2 100644 --- a/modules/solid_mechanics/src/postprocessors/CriticalTimeStep.C +++ b/modules/solid_mechanics/src/postprocessors/CriticalTimeStep.C @@ -83,5 +83,5 @@ void CriticalTimeStep::threadJoin(const UserObject & y) { const auto & pps = static_cast(y); - _critical_time += pps._critical_time; + _critical_time = std::min(pps._critical_time, _critical_time); } From 584e25a74989a413a712619bdff9eef59967b551 Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Fri, 19 Jan 2024 14:48:01 -0700 Subject: [PATCH 18/31] Update contact boundary displacements with new step. The previous strategy modified previous displacements to produce a velocity that would fulfil the dynamic contact constraints. That resulted in very wrong internal force state in the new step, which _created_ energy. This commit attempts to directly write the displacement variables in the new step. --- .../include/constraints/NodeFaceConstraint.h | 8 ++- .../include/systems/NonlinearSystemBase.h | 6 ++ framework/src/interfaces/Coupleable.C | 2 - framework/src/systems/NonlinearSystemBase.C | 52 ++++++++++++++ .../timeintegrators/ActuallyExplicitEuler.C | 3 + .../ExplicitDynamicsContactConstraint.h | 6 ++ .../ExplicitDynamicsContactConstraint.C | 67 +++++++++++++------ 7 files changed, 119 insertions(+), 25 deletions(-) diff --git a/framework/include/constraints/NodeFaceConstraint.h b/framework/include/constraints/NodeFaceConstraint.h index 4444af425fe8..26121e1b6411 100644 --- a/framework/include/constraints/NodeFaceConstraint.h +++ b/framework/include/constraints/NodeFaceConstraint.h @@ -118,10 +118,16 @@ class NodeFaceConstraint : public Constraint, virtual const std::set & getMatPropDependencies() const; /** - * Virtual method to avoid initial setups. + * Whether (contact) constraint is of 'explicit dynamics' type. */ virtual bool isExplicitConstraint() const { return false; } + /** + * Allows for overwriting boundary variables (explicit dynamics contact). + */ + virtual void overwriteBoundaryVariables(NumericVector & /*soln*/, + const Node & /*secondary_node*/) const {}; + protected: /** * Compute the value the secondary node should have at the beginning of a timestep. diff --git a/framework/include/systems/NonlinearSystemBase.h b/framework/include/systems/NonlinearSystemBase.h index 3048a69f6fc4..c59afeebc73e 100644 --- a/framework/include/systems/NonlinearSystemBase.h +++ b/framework/include/systems/NonlinearSystemBase.h @@ -361,6 +361,12 @@ class NonlinearSystemBase : public SystemBase, public PerfGraphInterface virtual void setSolution(const NumericVector & soln); + /** + * Called from explicit time stepping to overwrite boundary positions (explicit dynamics) + * @param soln Solution of current time step. + */ + virtual void overwriteContact(NumericVector & soln); + /** * Update active objects of Warehouses owned by NonlinearSystemBase */ diff --git a/framework/src/interfaces/Coupleable.C b/framework/src/interfaces/Coupleable.C index a72d559fc20e..73cbe52c086a 100644 --- a/framework/src/interfaces/Coupleable.C +++ b/framework/src/interfaces/Coupleable.C @@ -935,8 +935,6 @@ Coupleable::checkWritableVar(MooseWritableVariable * var) const auto * br = dynamic_cast(this); const auto * nfc = dynamic_cast(this); - // mooseWarning("This object '", _obj->name(), "'is not of the block restrictable type."); - if (br && !var->hasBlocks(br->blockIDs())) mooseError("The variable '", var->name(), diff --git a/framework/src/systems/NonlinearSystemBase.C b/framework/src/systems/NonlinearSystemBase.C index da5ad648f7c2..29a378990f8a 100644 --- a/framework/src/systems/NonlinearSystemBase.C +++ b/framework/src/systems/NonlinearSystemBase.C @@ -1553,6 +1553,58 @@ NonlinearSystemBase::constraintResiduals(NumericVector & residual, bool _fe_problem.addCachedResidual(0); } +void +NonlinearSystemBase::overwriteContact(NumericVector & soln) +{ + // Overwrite results from integrator in case we have explicit dynamics contact constraints + auto & subproblem = _fe_problem.getDisplacedProblem() + ? static_cast(*_fe_problem.getDisplacedProblem()) + : static_cast(_fe_problem); + const auto & penetration_locators = subproblem.geomSearchData()._penetration_locators; + + for (const auto & it : penetration_locators) + { + PenetrationLocator & pen_loc = *(it.second); + + std::vector & secondary_nodes = pen_loc._nearest_node._secondary_nodes; + BoundaryID secondary_boundary = pen_loc._secondary_boundary; + BoundaryID primary_boundary = pen_loc._primary_boundary; + + if (_constraints.hasActiveNodeFaceConstraints(secondary_boundary, true)) + { + const auto & constraints = + _constraints.getActiveNodeFaceConstraints(secondary_boundary, true); + for (unsigned int i = 0; i < secondary_nodes.size(); i++) + { + dof_id_type secondary_node_num = secondary_nodes[i]; + Node & secondary_node = _mesh.nodeRef(secondary_node_num); + + if (secondary_node.processor_id() == processor_id()) + { + if (pen_loc._penetration_info[secondary_node_num]) + { + for (const auto & nfc : constraints) + { + if (!nfc->isExplicitConstraint()) + continue; + + // Return if this constraint does not correspond to the primary-secondary pair + // prepared by the outer loops. + // This continue statement is required when, e.g. one secondary surface constrains + // more than one primary surface. + if (nfc->secondaryBoundary() != secondary_boundary || + nfc->primaryBoundary() != primary_boundary) + continue; + + nfc->overwriteBoundaryVariables(soln, secondary_node); + } + } + } + } + } + } +} + void NonlinearSystemBase::residualSetup() { diff --git a/framework/src/timeintegrators/ActuallyExplicitEuler.C b/framework/src/timeintegrators/ActuallyExplicitEuler.C index 0d9b2754b12e..ad986bf742f5 100644 --- a/framework/src/timeintegrators/ActuallyExplicitEuler.C +++ b/framework/src/timeintegrators/ActuallyExplicitEuler.C @@ -95,6 +95,9 @@ ActuallyExplicitEuler::solve() *_nonlinear_implicit_system->solution = _nl.solutionOld(); *_nonlinear_implicit_system->solution += _solution_update; + _nl.overwriteContact(*_nonlinear_implicit_system->solution); + _nonlinear_implicit_system->solution->close(); + // Enforce contraints on the solution DofMap & dof_map = _nonlinear_implicit_system->get_dof_map(); dof_map.enforce_constraints_exactly(*_nonlinear_implicit_system, diff --git a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h index fd0d2d691f30..22d2b1d0c5e6 100644 --- a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h +++ b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h @@ -82,6 +82,9 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, virtual const std::set & getMatPropDependencies() const override; + virtual void overwriteBoundaryVariables(NumericVector & soln, + const Node & secondary_node) const override; + protected: /** * Determine "Lagrange multipliers" from the iterative solution of the impact problem. @@ -143,6 +146,9 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, const VariableValue & _neighbor_vel_y; /// Z component of velocity at the closest point const VariableValue & _neighbor_vel_z; + +private: + std::unordered_map _dof_to_velocity; }; inline const std::set & diff --git a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C index 0ca88602233a..8abbc2510614 100644 --- a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C +++ b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C @@ -151,6 +151,7 @@ ExplicitDynamicsContactConstraint::timestepSetup() { updateContactStatefulData(/* beginning_of_step = */ true); _update_stateful_data = false; + _dof_to_velocity.clear(); } } @@ -295,8 +296,6 @@ ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & node, Real mass_contact_pressure(0.0); Real gap_rate(0.0); - // Real gap(0.0); - // gap = distance_gap * pinfo->_normal; mass_contact_pressure = density_secondary * _neighbor_density[0] * wave_speed_secondary * _neighbor_wave_speed[0]; @@ -310,7 +309,8 @@ ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & node, auto & u_dot = *_sys.solutionUDot(); auto & u_old = _sys.solutionOld(); - auto & u_older = _sys.solutionOlder(); + // auto & u_older = _sys.solutionOlder(); + auto & u_old_old_old = _sys.solutionState(3); // Mass proxy for secondary node. const Real mass_proxy = density_secondary * wave_speed_secondary * _dt * nodal_area; @@ -356,6 +356,7 @@ ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & node, velocity_z -= _dt / mass_proxy * (pinfo->_normal(2) * (force_increment)); // Let's not modify the neighbor velocity, but apply the corresponding force. + // TODO: Update for multi-body impacts // n_velocity_x = n_velocity_x; // n_velocity_y = n_velocity_y; // n_velocity_z = n_velocity_z; @@ -370,33 +371,32 @@ ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & node, const Real relative_error = (force_increment - force_increment_old) / force_increment; const Real absolute_error = std::abs(force_increment); - if (std::abs(relative_error) < TOLERANCE || absolute_error < TOLERANCE || + if (std::abs(relative_error) < TOLERANCE * TOLERANCE || absolute_error < TOLERANCE || (gap_rate_old) * (gap_rate) < 0.0) is_converged = true; else iteration_no++; } - const bool print_debug(false); - if (print_debug) - { - _console << "Results of momentun balance iterations for node: " << _current_node->id() << "\n"; - _console << "Total number of iterations: " << iteration_no << "\n"; - _console << "Final normal gap rate (dynamic constraint): " << gap_rate << "\n"; - _console << "Velocity x of secondary node: " << velocity_x << "\n"; - _console << "Velocity y of secondary node: " << velocity_y << "\n"; - _console << "Velocity z of secondary node: " << velocity_z << "\n"; - _console << "Final normal contact force: " << lambda_iteration << "\n"; - } - - // Set velocities on contact interface according to local, converged solution. - // Note: Since MOOSE solves for displacements, we need to back calculate the displacements and - // cannot rely on the central difference stepper to operate on rate variables (!) + // const bool print_debug(false); + // if (print_debug) + // { + // _console << "Results of momentun balance iterations for node: " << _current_node->id() << + // "\n"; _console << "Total number of iterations: " << iteration_no << "\n"; _console << "Final + // normal gap rate (dynamic constraint): " << gap_rate << "\n"; _console << "Velocity x of + // secondary node: " << velocity_x << "\n"; _console << "Velocity y of secondary node: " << + // velocity_y << "\n"; _console << "Velocity z of secondary node: " << velocity_z << "\n"; + // _console << "Final normal contact force: " << lambda_iteration << "\n"; + // } // Discount effects of other forces - u_old.set(dof_x, u_older(dof_x) + velocity_x * _dt); - u_old.set(dof_y, u_older(dof_y) + velocity_y * _dt); - u_old.set(dof_z, u_older(dof_z) + velocity_z * _dt); + u_old.set(dof_x, u_old_old_old(dof_x) + 2.0 * velocity_x * _dt); + u_old.set(dof_y, u_old_old_old(dof_y) + 2.0 * velocity_y * _dt); + u_old.set(dof_z, u_old_old_old(dof_z) + 2.0 * velocity_z * _dt); + + _dof_to_velocity[dof_x] = u_old_old_old(dof_x) + 2.0 * velocity_x * _dt; + _dof_to_velocity[dof_y] = u_old_old_old(dof_y) + 2.0 * velocity_y * _dt; + _dof_to_velocity[dof_z] = u_old_old_old(dof_z) + 2.0 * velocity_z * _dt; _gap_rate->setNodalValue(gap_rate); @@ -490,3 +490,26 @@ void ExplicitDynamicsContactConstraint::residualEnd() { } + +void +ExplicitDynamicsContactConstraint::overwriteBoundaryVariables(NumericVector & soln, + const Node & secondary_node) const +{ + if (_component == 0) + { + dof_id_type dof_x = secondary_node.dof_number(_sys.number(), _var_objects[0]->number(), 0); + dof_id_type dof_y = secondary_node.dof_number(_sys.number(), _var_objects[1]->number(), 0); + dof_id_type dof_z = secondary_node.dof_number(_sys.number(), _var_objects[2]->number(), 0); + + if (_dof_to_velocity.find(dof_x) != _dof_to_velocity.end()) + { + const auto & velocity_x = libmesh_map_find(_dof_to_velocity, dof_x); + const auto & velocity_y = libmesh_map_find(_dof_to_velocity, dof_y); + const auto & velocity_z = libmesh_map_find(_dof_to_velocity, dof_z); + + soln.set(dof_x, velocity_x); + soln.set(dof_y, velocity_y); + soln.set(dof_z, velocity_z); + } + } +} From bdd2cf3ed1fe8d0307f37c4611fca601dbf5774b Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Mon, 22 Jan 2024 10:46:04 -0700 Subject: [PATCH 19/31] Include option to overwrite current solution. This option is more consistent with the internal energy of the solid. I.e., it overwrites the position of the contacting boundaries with the result of the iterative contact algorithm. However, it sometimes results in some sort of element inversion or impossible mappings. This needs to be investigated/debugged. It may be due to MOOSE's order of operations. --- framework/src/systems/NonlinearSystemBase.C | 1 + .../ExplicitDynamicsContactConstraint.h | 10 +- .../actions/ExplicitDynamicsContactAction.C | 9 + .../ExplicitDynamicsContactConstraint.C | 80 ++- .../test/tests/explicit_dynamics/first_test.i | 1 + .../explicit_dynamics/gold/first_test_out.csv | 102 ++-- .../explicit_dynamics/gold/settlement_out.csv | 501 ++++++++++++++++++ .../explicit_dynamics/gold/settlement_out.e | Bin 160388 -> 0 bytes .../explicit_dynamics/gold/test_balance_out.e | Bin 216924 -> 218948 bytes .../test/tests/explicit_dynamics/settlement.i | 107 +++- .../test/tests/explicit_dynamics/tests | 7 +- 11 files changed, 687 insertions(+), 131 deletions(-) create mode 100644 modules/contact/test/tests/explicit_dynamics/gold/settlement_out.csv delete mode 100644 modules/contact/test/tests/explicit_dynamics/gold/settlement_out.e diff --git a/framework/src/systems/NonlinearSystemBase.C b/framework/src/systems/NonlinearSystemBase.C index 29a378990f8a..d15a5196454b 100644 --- a/framework/src/systems/NonlinearSystemBase.C +++ b/framework/src/systems/NonlinearSystemBase.C @@ -1603,6 +1603,7 @@ NonlinearSystemBase::overwriteContact(NumericVector & soln) } } } + soln.close(); } void diff --git a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h index 22d2b1d0c5e6..d66ee71b8a29 100644 --- a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h +++ b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h @@ -31,7 +31,7 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, virtual void timestepSetup() override; virtual void jacobianSetup() override {} - virtual void residualEnd() override; + virtual void residualEnd() override {} virtual void updateContactStatefulData(bool beginning_of_step); virtual Real computeQpSecondaryValue() override; @@ -103,7 +103,6 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, const unsigned int _component; const ExplicitDynamicsContactModel _model; - const bool _normalize_penalty; bool _update_stateful_data; @@ -123,9 +122,9 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, SystemBase & _aux_system; const NumericVector * const _aux_solution; + const Real _penalty; const bool _print_contact_nodes; - static Threads::spin_mutex _contact_set_mutex; const static unsigned int _no_iterations; @@ -147,8 +146,11 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, /// Z component of velocity at the closest point const VariableValue & _neighbor_vel_z; + /// Whether to overwrite contact boundary nodal solution + const bool _overwrite_current_solution; + private: - std::unordered_map _dof_to_velocity; + std::unordered_map _dof_to_position; }; inline const std::set & diff --git a/modules/contact/src/actions/ExplicitDynamicsContactAction.C b/modules/contact/src/actions/ExplicitDynamicsContactAction.C index 9902cfd1c76f..9662f728de10 100644 --- a/modules/contact/src/actions/ExplicitDynamicsContactAction.C +++ b/modules/contact/src/actions/ExplicitDynamicsContactAction.C @@ -61,6 +61,11 @@ ExplicitDynamicsContactAction::validParams() "model", ExplicitDynamicsContactAction::getModelEnum(), "The contact model to use"); params.addParam("tangential_tolerance", "Tangential distance to extend edges of contact surfaces"); + params.addParam("penalty", 1e8, "Penalty factor for normal contact."); + params.addParam("overwrite_current_solution", + false, + "Whether to overwrite the position of contact boundaries with the velocity " + "computed with the contact algorithm."); params.addClassDescription("Sets up all objects needed for mechanical contact enforcement in " "explicit dynamics simulations."); params.addParam>( @@ -84,6 +89,7 @@ ExplicitDynamicsContactAction::ExplicitDynamicsContactAction(const InputParamete _boundary_pairs(getParam("primary", "secondary")), _model(getParam("model").getEnum()) { + mooseWarning("Verification of explicit dynamics capabilities is an ongoing effort."); } void @@ -360,6 +366,9 @@ ExplicitDynamicsContactAction::addNodeFaceContact() params.set>("displacements") = displacements; params.set("use_displaced_mesh") = true; + params.set("overwrite_current_solution") = getParam("overwrite_current_solution"); + params.set("penalty") = getParam("penalty"); + params.set("order") = Utility::enum_to_string(OrderWrapper{order}); for (const auto & contact_pair : _boundary_pairs) diff --git a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C index 8abbc2510614..20521c480760 100644 --- a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C +++ b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C @@ -53,19 +53,19 @@ ExplicitDynamicsContactConstraint::validParams() params.addRequiredCoupledVar("nodal_density", "The nodal density."); params.addRequiredCoupledVar("nodal_wave_speed", "The nodal wave speed."); params.set("use_displaced_mesh") = true; - params.addParam( - "penalty", - 1e8, - "The penalty to apply. This can vary depending on the stiffness of your materials"); + params.addParam("penalty", + 1e8, + "The penalty to apply. Its optimal value can vary depending on the " + "stiffness of the materials"); params.addParam("friction_coefficient", 0, "The friction coefficient"); params.addParam("tangential_tolerance", "Tangential distance to extend edges of contact surfaces"); - params.addParam( - "normalize_penalty", - false, - "Whether to normalize the penalty parameter with the nodal area for penalty contact."); params.addParam( "print_contact_nodes", false, "Whether to print the number of nodes in contact."); + params.addParam("overwrite_current_solution", + false, + "Whether to overwrite the position of contact boundaries with the velocity " + "computed with the contact algorithm."); params.addClassDescription( "Apply non-penetration constraints on the mechanical deformation " "using a node on face, primary/secondary algorithm, and multiple options " @@ -74,8 +74,6 @@ ExplicitDynamicsContactConstraint::validParams() return params; } -Threads::spin_mutex ExplicitDynamicsContactConstraint::_contact_set_mutex; - ExplicitDynamicsContactConstraint::ExplicitDynamicsContactConstraint( const InputParameters & parameters) : NodeFaceConstraint(parameters), @@ -83,7 +81,6 @@ ExplicitDynamicsContactConstraint::ExplicitDynamicsContactConstraint( _displaced_problem(parameters.get("_fe_problem_base")->getDisplacedProblem()), _component(getParam("component")), _model(getParam("model").getEnum()), - _normalize_penalty(getParam("normalize_penalty")), _update_stateful_data(true), _mesh_dimension(_mesh.dimension()), _vars(3, libMesh::invalid_uint), @@ -99,6 +96,7 @@ ExplicitDynamicsContactConstraint::ExplicitDynamicsContactConstraint( _nodal_wave_speed_var(getVar("nodal_wave_speed", 0)), _aux_system(_nodal_area_var->sys()), _aux_solution(_aux_system.currentSolution()), + _penalty(getParam("penalty")), _print_contact_nodes(getParam("print_contact_nodes")), _residual_copy(_sys.residualGhosted()), _neighbor_density(getNeighborMaterialPropertyByName("density")), @@ -107,7 +105,8 @@ ExplicitDynamicsContactConstraint::ExplicitDynamicsContactConstraint( _neighbor_vel_x(isCoupled("vel_x") ? coupledNeighborValue("vel_x") : _zero), _neighbor_vel_y(isCoupled("vel_y") ? coupledNeighborValue("vel_y") : _zero), _neighbor_vel_z((_mesh.dimension() == 3 && isCoupled("vel_z")) ? coupledNeighborValue("vel_z") - : _zero) + : _zero), + _overwrite_current_solution(getParam("overwrite_current_solution")) { _overwrite_secondary_residual = false; @@ -151,7 +150,7 @@ ExplicitDynamicsContactConstraint::timestepSetup() { updateContactStatefulData(/* beginning_of_step = */ true); _update_stateful_data = false; - _dof_to_velocity.clear(); + _dof_to_position.clear(); } } @@ -309,7 +308,6 @@ ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & node, auto & u_dot = *_sys.solutionUDot(); auto & u_old = _sys.solutionOld(); - // auto & u_older = _sys.solutionOlder(); auto & u_old_old_old = _sys.solutionState(3); // Mass proxy for secondary node. @@ -378,32 +376,17 @@ ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & node, iteration_no++; } - // const bool print_debug(false); - // if (print_debug) - // { - // _console << "Results of momentun balance iterations for node: " << _current_node->id() << - // "\n"; _console << "Total number of iterations: " << iteration_no << "\n"; _console << "Final - // normal gap rate (dynamic constraint): " << gap_rate << "\n"; _console << "Velocity x of - // secondary node: " << velocity_x << "\n"; _console << "Velocity y of secondary node: " << - // velocity_y << "\n"; _console << "Velocity z of secondary node: " << velocity_z << "\n"; - // _console << "Final normal contact force: " << lambda_iteration << "\n"; - // } - - // Discount effects of other forces + _gap_rate->setNodalValue(gap_rate); + u_old.set(dof_x, u_old_old_old(dof_x) + 2.0 * velocity_x * _dt); u_old.set(dof_y, u_old_old_old(dof_y) + 2.0 * velocity_y * _dt); u_old.set(dof_z, u_old_old_old(dof_z) + 2.0 * velocity_z * _dt); - _dof_to_velocity[dof_x] = u_old_old_old(dof_x) + 2.0 * velocity_x * _dt; - _dof_to_velocity[dof_y] = u_old_old_old(dof_y) + 2.0 * velocity_y * _dt; - _dof_to_velocity[dof_z] = u_old_old_old(dof_z) + 2.0 * velocity_z * _dt; - - _gap_rate->setNodalValue(gap_rate); + _dof_to_position[dof_x] = u_old_old_old(dof_x) + 2.0 * velocity_x * _dt; + _dof_to_position[dof_y] = u_old_old_old(dof_y) + 2.0 * velocity_y * _dt; + _dof_to_position[dof_z] = u_old_old_old(dof_z) + 2.0 * velocity_z * _dt; - if (lambda_iteration < 0.0) - pinfo->_contact_force = pinfo->_normal * lambda_iteration; - else - pinfo->_contact_force = 0.0; + pinfo->_contact_force = pinfo->_normal * lambda_iteration; } Real @@ -415,9 +398,10 @@ ExplicitDynamicsContactConstraint::computeQpSecondaryValue() Real ExplicitDynamicsContactConstraint::computeQpResidual(Moose::ConstraintType type) { + // We use kinematic contact. But adding the residual helps avoid element inversion. PenetrationInfo * pinfo = _penetration_locator._penetration_info[_current_node->id()]; Real resid = pinfo->_contact_force(_component); - // Maybe only apply force on the primary since we are kinematically enforcing the secondary. + switch (type) { case Moose::Secondary: @@ -464,7 +448,8 @@ ExplicitDynamicsContactConstraint::nodalArea(const Node & node) Real ExplicitDynamicsContactConstraint::getPenalty(const Node & /*node*/) { - return 1.0e3; + // TODO: Include normalized penalty values. + return _penalty; } bool @@ -486,30 +471,25 @@ ExplicitDynamicsContactConstraint::getCoupledVarComponent(unsigned int var_num, return coupled_var_is_disp_var; } -void -ExplicitDynamicsContactConstraint::residualEnd() -{ -} - void ExplicitDynamicsContactConstraint::overwriteBoundaryVariables(NumericVector & soln, const Node & secondary_node) const { - if (_component == 0) + if (_component == 0 && _overwrite_current_solution) { dof_id_type dof_x = secondary_node.dof_number(_sys.number(), _var_objects[0]->number(), 0); dof_id_type dof_y = secondary_node.dof_number(_sys.number(), _var_objects[1]->number(), 0); dof_id_type dof_z = secondary_node.dof_number(_sys.number(), _var_objects[2]->number(), 0); - if (_dof_to_velocity.find(dof_x) != _dof_to_velocity.end()) + if (_dof_to_position.find(dof_x) != _dof_to_position.end()) { - const auto & velocity_x = libmesh_map_find(_dof_to_velocity, dof_x); - const auto & velocity_y = libmesh_map_find(_dof_to_velocity, dof_y); - const auto & velocity_z = libmesh_map_find(_dof_to_velocity, dof_z); + const auto & position_x = libmesh_map_find(_dof_to_position, dof_x); + const auto & position_y = libmesh_map_find(_dof_to_position, dof_y); + const auto & position_z = libmesh_map_find(_dof_to_position, dof_z); - soln.set(dof_x, velocity_x); - soln.set(dof_y, velocity_y); - soln.set(dof_z, velocity_z); + soln.set(dof_x, position_x); + soln.set(dof_y, position_y); + soln.set(dof_z, position_z); } } } diff --git a/modules/contact/test/tests/explicit_dynamics/first_test.i b/modules/contact/test/tests/explicit_dynamics/first_test.i index ab3e0c7fa979..6cc9e4050351 100644 --- a/modules/contact/test/tests/explicit_dynamics/first_test.i +++ b/modules/contact/test/tests/explicit_dynamics/first_test.i @@ -192,6 +192,7 @@ model = frictionless primary = base_front secondary = ball_back + penalty = 1.0e3 [] [] diff --git a/modules/contact/test/tests/explicit_dynamics/gold/first_test_out.csv b/modules/contact/test/tests/explicit_dynamics/gold/first_test_out.csv index b44898b00aa4..2b1b456dea73 100644 --- a/modules/contact/test/tests/explicit_dynamics/gold/first_test_out.csv +++ b/modules/contact/test/tests/explicit_dynamics/gold/first_test_out.csv @@ -1,54 +1,54 @@ time,contact_pressure_max,critical_time_step,disp_58z -0.01,0,0,0 -0.005,0,0.1,7.1428571428571e-06 -0,0,0.1,-1.4287635400646e-08 -0.005,0,0.1,-7.1596363851879e-06 -0.01,0,0.1,-1.4291770007043e-05 -0.015,0,0.1,-2.1409216834347e-05 -0.02,0,0.1,-2.8510521584756e-05 -0.025,0,0.1,-3.5594231740244e-05 -0.03,0,0.1,-4.2658897691867e-05 -0.035,0,0.1,-4.9703069387686e-05 -0.04,0,0.1,-5.6725303226426e-05 -0.045,0,0.1,-6.3724158948958e-05 -0.05,0,0.1,-7.0698199781591e-05 -0.055,0,0.1,-7.7645992578983e-05 -0.06,0,0.1,-8.4566107966647e-05 -0.065,0,0.1,-9.1457120483056e-05 -0.07,0,0.1,-9.8317608721322e-05 -0.075,0,0.1,-0.00010514615547046 -0.08,0.082342600269299,0.1,-0.00011194135660093 -0.085,0.19101520520849,0.1,-0.00011870180198318 -0.09,0.2990125934109,0.1,-0.00012542608101362 -0.095,0.40624522737138,0.1,-0.00013211278042684 -0.1,0.51262435889857,0.1,-0.00013876048209793 -0.105,0.61806212050844,0.1,-0.00014536776088066 -0.11,0.72247161511198,0.1,-0.0001519331824919 -0.115,0.82576700393275,0.1,-0.0001584553014484 -0.12,0.92786359262147,0.1,-0.0001649326590604 -0.125,1.0286779155446,0.1,-0.00017136378148647 -0.13,1.1281278182246,0.1,-0.00017774717785316 -0.135,1.2261325379052,0.1,-0.00018408133844316 -0.14,1.3226127822114,0.1,-0.00019036473295566 -0.145,1.4174920367121,0.1,-0.00019659580884224 -0.15,1.5106936533543,0.1,-0.00020277298972196 -0.155,1.6021427551876,0.1,-0.00020889467387898 -0.16,1.6917666866988,0.1,-0.00021495923284641 -0.165,1.7794946285915,0.1,-0.00022096501007964 -0.17,1.8652576643615,0.1,-0.00022691031972277 -0.175,1.9489888444021,0.1,-0.00023279344547155 -0.18,2.0306232475643,0.1,-0.00023861263953621 -0.185,2.1100980401007,0.1,-0.00024436612170748 -0.19,2.1873525319252,0.1,-0.0002500520785289 -0.195,2.2623282301271,0.1,-0.00025566866257851 -0.2,2.3349688896757,0.1,-0.00026121399186243 -0.205,2.4052205612584,0.1,-0.00026668614932302 -0.21,2.4730316361914,0.1,-0.00027208318246378 -0.215,2.5383528883463,0.1,-0.00027740310309278 -0.22,2.6011375130337,0.1,-0.00028264388718642 -0.225,2.6613411627903,0.1,-0.00028780347487477 -0.23,2.7189219800124,0.1,-0.00029287977054953 -0.235,2.7738406263866,0.1,-0.00029787064309539 -0.24,2.8260603090661,0.1,-0.00030277392624536 -0.245,2.8755468035504,0.1,-0.00030758742177669 -0.25,2.9222732166101,0.1,-0.00031230889093653 +0,0,0.1,-1.4273832748933e-08 +0.005,0,0.1,-7.1596363437208e-06 +0.01,0,0.1,-1.4291770020591e-05 +0.015,0,0.1,-2.1409216967926e-05 +0.02,0,0.1,-2.8510521909652e-05 +0.025,0,0.1,-3.5594232334055e-05 +0.03,0,0.1,-4.2658898638455e-05 +0.035,0,0.1,-4.9703074273542e-05 +0.04,0,0.1,-5.6725315887351e-05 +0.045,0,0.1,-6.3724183468886e-05 +0.05,0,0.1,-7.0698240491355e-05 +0.055,0,0.1,-7.7646054055035e-05 +0.06,0,0.1,-8.4566195029724e-05 +0.065,0,0.1,-9.1457238196801e-05 +0.07,0,0.1,-9.8317762390858e-05 +0.075,0,0.1,-0.0001051463506409 +0.08,0.082341610254479,0.1,-0.00011194159031113 +0.085,0.19101404878775,0.1,-0.00011870207077947 +0.09,0.29901128340337,0.1,-0.000125426380983 +0.095,0.40624378043303,0.1,-0.00013211310720354 +0.1,0.51262279737801,0.1,-0.0001387608308863 +0.105,0.61806047588701,0.1,-0.00014536812639074 +0.11,0.72246991323486,0.1,-0.0001519335590144 +0.115,0.82576527398727,0.1,-0.00015845568288214 +0.12,0.92786186707797,0.1,-0.00016493303893838 +0.125,1.0286762300661,0.1,-0.00017136415299712 +0.13,1.1281262115668,0.1,-0.00017774753385423 +0.135,1.2261310518263,0.1,-0.00018408167146683 +0.14,1.3226114614042,0.1,-0.00019036503520421 +0.145,1.4174896979197,0.1,-0.00019659607217471 +0.15,1.5106896408102,0.1,-0.00020277320563266 +0.155,1.602136864045,0.1,-0.00020889483346961 +0.16,1.6917587067307,0.1,-0.00021495932679354 +0.165,1.7794843415407,0.1,-0.00022096502859997 +0.17,1.8652448408947,0.1,-0.00022691025253829 +0.175,1.9489732408148,0.1,-0.00023279328177693 +0.18,2.0306046023756,0.1,-0.00023861236797019 +0.185,2.1100760706723,0.1,-0.00024436573033008 +0.19,2.1873269312209,0.1,-0.00025005155480555 +0.195,2.2622986637121,0.1,-0.00025566799337195 +0.2,2.3349349930359,0.1,-0.00026121316343291 +0.205,2.4051819374974,0.1,-0.00026668514733684 +0.21,2.4729878541477,0.1,-0.0002720819920099 +0.215,2.5383034811523,0.1,-0.00027740170870711 +0.22,2.6010819771283,0.1,-0.00028264227288318 +0.225,2.6612789573813,0.1,-0.00028780162418416 +0.23,2.71885252698,0.1,-0.00029287766656096 +0.235,2.7737633106092,0.1,-0.00029786826850548 +0.24,2.8259744791508,0.1,-0.00030277126341 +0.245,2.8754517729438,0.1,-0.00030758445004995 +0.25,2.9221635216841,0.1,-0.00031230559319032 diff --git a/modules/contact/test/tests/explicit_dynamics/gold/settlement_out.csv b/modules/contact/test/tests/explicit_dynamics/gold/settlement_out.csv new file mode 100644 index 000000000000..6568e095a333 --- /dev/null +++ b/modules/contact/test/tests/explicit_dynamics/gold/settlement_out.csv @@ -0,0 +1,501 @@ +time,accel_58z,contact_pressure_max,critical_time_step,penetration_max,total_elastic_energy_one,total_elastic_energy_two,total_kinetic_energy_one,total_kinetic_energy_two,vel_58z +-0.0099,0,0,0.0010540925533895,0,0,0,0,0,0 +-0.0098,-98.1,0,0.0010540925533895,0,1.781116844959e-37,0.96236099958246,0.000120295125,801.9675,-0.004905 +-0.0097,-98.1,0,0.0010540925533895,0,1.4573277753141e-36,8.6485504916941,0.001082656125,7207.125410525,-0.014715 +-0.0096,-98.1,0,0.0010540925533895,0,8.3541446114842e-36,34.541194768951,0.003007378125,19977.114129753,-0.024525 +-0.0095,-98.1,0,0.0010540925533895,0,2.5844049500121e-35,95.794404433412,0.005894461125,39063.502158656,-0.034335 +-0.0094,-98.1,0,0.0010540925533895,0,7.4476225388437e-35,215.17866252072,0.009743905125,64413.029301542,-0.044145 +-0.0093,-98.1,0,0.0010540925533895,0,1.7210582184592e-34,421.02034699428,0.014555710125,95967.668473748,-0.053955 +-0.0092,-98.1,0,0.0010540925533895,0,3.5044503813815e-34,747.1359635386,0.020329876125,133664.69107201,-0.063765 +-0.0091,-98.1,0,0.0010540925533895,0,7.7735058605238e-34,1232.7611686589,0.027066403125,177436.73584089,-0.073575 +-0.009,-98.1,0,0.0010540925533895,0,1.3692667873462e-33,1922.4746672498,0.034765291125,227211.88116635,-0.083385 +-0.0089,-98.1,0,0.0010540925533895,0,2.337393888942e-33,2866.1170727541,0.043426540125,282913.72072507,-0.093195 +-0.0088,-98.1,0,0.0010540925533895,0,3.7943586570234e-33,4118.7048216479,0.053050150125,344461.44241535,-0.103005 +-0.0087,-98.1,0,0.0010540925533895,0,5.6986784997406e-33,5740.3392382769,0.063636121125,411769.9104938,-0.112815 +-0.0086,-98.1,0,0.0010540925533895,0,8.5505357949379e-33,7796.1108492496,0.075184453125,484749.75083908,-0.122625 +-0.0085,-98.1,255.29624192439,0.0010540925533895,3.005e-06,0.0010524253619969,10355.999050492,0.087695146125,563307.43926213,-0.132435 +-0.0084,-2593.6222548453,0,0.0010540925533895,0,0.0012141220033913,13494.767233573,0.078858940273919,647345.39278014,-0.0021511127422708 +-0.0083,-219.13897997677,177.16573219322,0.0010540925533895,4.2153897997733e-06,0.0047907599426476,17291.85348094,0.11129780497153,736762.0637695,-0.13848694899883 +-0.0082,-2819.9049482976,0,0.0010540925533895,0,0.0053644676230803,21831.256943579,0.10076583137468,831452.03691089,-0.013465247414893 +-0.0081,-480.7845465123,171.09459808932,0.0010540925533895,6.8318454651292e-06,0.011905080352586,27201.420017388,0.13650205866371,931306.12883803,-0.15156922732559 +-0.008,-3113.6679174579,0,0.0010540925533895,0,0.013125375043761,33495.106437541,0.1238158600564,1036211.4903995,-0.02815339587292 +-0.0079,-797.49786642521,188.26405319555,0.0010540925533895,9.9989786642575e-06,0.023049987630682,40809.275413699,0.16336439961183,1146051.7114414,-0.16740489332123 +-0.0078,-3439.8343221833,0,0.0010540925533895,0,0.025175454170005,49244.951930786,0.14819613515841,1260706.9280174,-0.044461716109198 +-0.0077,-1143.2453562107,214.00566822372,0.0010540925533895,1.345645356212e-05,0.038921457036751,58907.09334429,0.19175248011969,1380053.9319305,-0.1846922678105 +-0.0076,-3787.5022905755,0,0.0010540925533895,1.6400229057989e-06,0.042235260586696,69904.452399124,0.17380734052929,1503966.2825092,-0.061845114528819 +-0.0075,-1509.815798213,243.89584655901,0.0010540925533895,1.7122157982152e-05,0.060258843616392,82349.43680726,0.22146144369272,1632314.4205209,-0.2030207899106 +-0.0074,-4152.9898766558,0,0.0010540925533895,5.2948987666134e-06,0.065062198277194,96357.965518564,0.20046162212737,1764965.7841226,-0.080119493832843 +-0.0073,-1894.3047068216,276.59312584058,0.0010540925533895,2.0967047068268e-05,0.087832303144384,112049.32182196,0.25227004298658,1901784.9267467,-0.22224523534102 +-0.0072,-4534.7266182932,0,0.0010540925533895,9.1122661830142e-06,0.094437530858825,129546.00341868,0.22795161909199,2042633.6368221,-0.099206330914724 +-0.0071,-2295.3373393337,311.68217062793,0.0010540925533895,2.4977373393391e-05,0.12242857527922,148973.56960829,0.28396071117236,2187371.0592264,-0.24229686696662 +-0.007,-4931.7054023079,0,0.0010540925533895,1.3082054023148e-05,0.13115258715255,170460.48573187,0.25607338544935,2335853.8183656,-0.11905527011547 +-0.0069,-2711.9226874286,349.01914107051,0.0010540925533895,2.9143226874339e-05,0.16483717790665,194137.96501787,0.31632608525018,2487936.1427752,-0.26312613437135 +-0.0068,-5343.013339655,0,0.0010540925533895,1.7195133396633e-05,0.17599532494562,220139.80797804,0.28463352335473,2643469.9911397,-0.13962066698283 +-0.0067,-3143.1077326639,388.53020151316,0.0010540925533895,3.3455077326684e-05,0.21583718763555,248602.23950278,0.34917062554515,2802305.1796213,-0.28468538663311 +-0.0066,-5767.6950480445,0,0.0010540925533895,2.1441950480543e-05,0.22973741068704,279663.74380485,0.3134508183715,2964289.5103924,-0.16085475240232 +-0.0065,-3587.8798581393,430.14871581002,0.0010540925533895,3.7902798581466e-05,0.27618460916603,313464.8973642,0.3823104480795,3129268.9012642,-0.30692399290687 +-0.0064,-6204.7211970146,0,0.0010540925533895,2.5812211970219e-05,0.29312187878193,350148.20002739,0.34235596462534,3297087.5163032,-0.18270605985084 +-0.0063,-4045.1485907721,473.79608766397,0.0010540925533895,4.2475485907757e-05,0.34660038936977,389857.90441137,0.41557257653988,3467587.8973268,-0.3297874295385 +-0.0062,-6652.9915296152,0,0.0010540925533895,3.0294915296235e-05,0.36685146644485,432739.84377273,0.37119072283343,3640611.0961708,-0.20511957648088 +-0.0061,-4513.7535060383,519.37702291003,0.0010540925533895,4.7161535060435e-05,0.4277591817999,478941.25848906,0.44879412760881,3815996.8076179,-0.3532176753018 +-0.006,-7111.3492197817,2.641371605338,0.0010540925533895,3.4878492197902e-05,0.45157775202421,528610.62131825,0.39980705059281,3993583.5028812,-0.22803746098921 +-0.0059,-4990.8933771332,566.00436430764,0.0010540925533895,5.1932933771361e-05,0.52027899518641,581897.46158388,0.48177643061621,4173208.5635304,-0.37707466885653 +-0.0058,-7577.7924055481,42.424859477522,0.0010540925533895,3.9542924055569e-05,0.54788874347855,638952.18844745,0.42804172031166,4354708.4157549,-0.25135962027754 +-0.0057,-5453.9064475648,603.16158410301,0.0010540925533895,5.6563064475688e-05,0.62470566937753,699925.91342309,0.51370569371882,4537918.6648535,-0.4002253223781 +-0.0056,-8040.1465526121,78.619466056368,0.0010540925533895,4.4166465526204e-05,0.65625824267341,764970.27229433,0.45538556372489,4722674.2298425,-0.27447732763076 +-0.0055,-5916.7570809426,639.19710893246,0.0010540925533895,6.1191570809466e-05,0.74141190684154,834237.24658647,0.54453125625921,4908809.4780753,-0.42336785404698 +-0.0054,-8503.6712692849,114.70890738048,0.0010540925533895,4.8801712692937e-05,0.77699446583935,907878.98475347,0.48158545902803,5096158.3597638,-0.29765356346441 +-0.0053,-6381.0176873865,675.53169961682,0.0010540925533895,6.5834176873887e-05,0.87063712411596,986047.62323679,0.57405586660786,5284554.5422966,-0.44658088436917 +-0.0052,-8968.3807254761,151.23917025099,0.0010540925533895,5.3448807254857e-05,0.9102868563496,1068895.1075493,0.5064638169053,5473831.5442458,-0.32088903627398 +-0.0051,-6846.0470791014,712.33962252001,0.0010540925533895,7.0484470791045e-05,1.0125078637335,1156573.013544,0.60211576861168,5663822.8689582,-0.4698323539549 +-0.005,-9433.3630760179,188.2268367689,0.0010540925533895,5.8098630760266e-05,1.0562141051262,1249232.3690193,0.52987336938859,5854362.1376243,-0.34413815380108 +-0.0049,-7310.8263344535,749.57039537094,0.0010540925533895,7.5132263344593e-05,1.1670410464862,1347023.4758184,0.62857007156627,6045283.2217217,-0.49307131672249 +-0.0048,-9897.5629676177,225.59344938749,0.0010540925533895,6.274062967627e-05,1.2147454759552,1450095.7325717,0.55168928802655,6236420.3747288,-0.36734814838108 +-0.0047,-7774.2936368182,787.13400585177,0.0010540925533895,7.9766936368216e-05,1.3341448025557,1558597.4582373,0.65329903379152,6427608.3630079,-0.51624468184072 +-0.0046,-10359.925597431,263.24492528322,0.0010540925533895,6.7364255974427e-05,1.3857417047392,1672675.7165926,0.57180774888541,6618682.5957551,-0.39046627987176 +-0.0045,-8235.406571018,824.93523124617,0.0010540925533895,8.4378065710212e-05,1.5136196606973,1792476.1418239,0.67620368683983,6809479.2539174,-0.5393003285507 +-0.0044,-10819.423680306,301.08655565628,0.0010540925533895,7.1959236803197e-05,1.5689565839647,1918142.7653618,0.59014547311057,6999835.4179774,-0.41344118401553 +-0.0043,-8693.1536241371,862.88027261167,0.0010540925533895,8.8955536241435e-05,1.7051605236809,2049817.8441137,0.69720550267737,7189589.1945098,-0.56218768120664 +-0.0042,-11275.062069505,339.02595987675,0.0010540925533895,7.6515620695171e-05,1.7640393483954,2187641.6902344,0.60663928259772,7378579.8414114,-0.43622310347547 +-0.0041,-9146.5556952554,900.8780468858,0.0010540925533895,9.3489556952616e-05,1.9083594259854,2331752.5025809,0.71624590440778,7566647.8917112,-0.58485778476255 +-0.004,-11725.877858238,376.97360087871,0.0010540925533895,8.1023778582523e-05,1.970537812118,2482286.1999933,0.62124551335042,7753635.2758664,-0.45876389291214 +-0.0039,-9594.6655882901,938.84032346156,0.0010540925533895,9.7970655882949e-05,2.1227090138328,2639376.2565382,0.73328560917716,7939385.442454,-0.60726327941427 +-0.0038,-12170.939661778,414.84273096784,0.0010540925533895,8.5474396617924e-05,2.1879022000444,2803153.5388615,0.63393927370481,8123743.4771661,-0.48101698308913 +-0.0037,-10036.567292355,976.6815731149,0.0010540925533895,0.00010238967292359,2.3476066989172,2973746.1457723,0.74830382208281,8306556.2200224,-0.62935836461749 +-0.0036,-12609.347012671,452.54919544189,0.0010540925533895,8.9858470126805e-05,2.4154896338254,3151279.2502055,0.64471356184035,8487672.3807116,-0.50293735063382 +-0.0035,-10471.375557057,1014.3187561989,0.0010540925533895,0.00010673775557062,2.5823594537954,3335874.943682,0.76129730339874,8666942.6519788,-0.6510987778526 +-0.0034,-13040.230152048,490.01122460583,0.0010540925533895,9.4167301520618e-05,2.6525692460269,3527652.0834068,0.65357826251555,8844219.8209754,-0.52448150760267 +-0.0033,-10898.235915575,1051.6711306491,0.0010540925533895,0.0001110063591558,2.8261892271625,3726726.1421212,0.77227933056401,9019358.8784892,-0.67244179577847 +-0.0032,-13462.750287496,527.14926603095,0.0010540925533895,9.8392502875107e-05,2.8983279029399,3933209.0608365,0.66055904377975,9192217.1259789,-0.54560751437508 +-0.0031,-11316.325170315,1088.6601134852,0.0010540925533895,0.00011518725170322,3.0782389600507,4147209.1045754,0.78127857478772,9362654.2803332,-0.69334625851547 +-0.003,-13876.100293132,563.88587829795,0.0010540925533895,0.00010252600293147,3.1518765159847,4368830.7212217,0.66569617372595,9530532.5762817,-0.56627501465688 +-0.0029,-11724.852288951,1125.2092078302,0.0010540925533895,0.00011927252288956,3.3375791806047,4598174.4036166,0.78833791036458,9695716.8663846,-0.71377261444726 +-0.0028,-14279.50578022,600.14569130206,0.0010540925533895,0.00010656005780236,3.412256915955,4835336.554994,0.6690432759653,9858074.7185318,-0.58644528901129 +-0.0027,-12123.059626076,1161.2439955173,0.0010540925533895,0.00012325459626078,3.6032151472884,5080409.3578772,0.79351317300172,10017476.510881,-0.73368298130348 +-0.0026,-14672.226445401,635.85542936181,0.0010540925533895,0.00011048726445413,3.6784492550718,5333480.6465297,0.6706660407791,10173795.524175,-0.60608132227035 +-0.0025,-12510.22437326,1196.6921884687,0.0010540925533895,0.00012712624373264,3.8740944998879,5594633.7830794,0.79687188170749,10326908.031361,-0.75304121866269 +-0.0024,-15053.557598118,670.94398820657,0.0010540925533895,0.00011430057598131,3.9493798906501,5863947.5373961,0.67064090707817,10476693.384475,-0.62514787990623 +-0.0023,-12885.660140373,1231.4837284895,0.0010540925533895,0.0001308806014038,4.1491153660255,6141495.97083,0.79849193718759,10623034.098706,-0.77181300701835 +-0.0022,-15422.831773312,705.34255454414,0.0010540925533895,0.00011799331773329,4.2239296924761,6427348.3239084,0.66905372855241,10765815.933605,-0.64361158866593 +-0.0021,-13248.718578783,1265.5509238004,0.0010540925533895,0.00013451118578791,4.427134859348,6721568.90806,0.79846030829106,10904927.971372,-0.78996592893883 +-0.002,-15779.420345514,738.98475639914,0.0010540925533895,0.00012155920345534,4.5009427048985,7024217.0014752,0.66599843583879,11040262.692176,-0.66144101727603 +-0.0019,-13598.790968839,1298.8286104467,0.0010540925533895,0.0001380119096885,4.7069778950471,7335346.7491669,0.79687171689512,11171716.046465,-0.8074695484416 +-0.0018,-16122.735073581,771.80683268138,0.0010540925533895,0.00012499235073605,4.7792350849404,7655007.0673169,0.66157570524057,11299187.524202,-0.67860675367944 +-0.0017,-13935.309707949,1331.2543276487,0.0010540925533895,0.0001413770970796,4.9874462396096,7983241.5519861,0.79382733072286,11422580.221009,-0.82429548539708 +-0.0016,-16452.229519447,803.74781170207,0.0010540925533895,0.00012828729519471,5.0576042300107,8320088.3922488,0.65589164350419,11541800.901153,-0.69508147597273 +-0.0015,-14257.74964958,1362.7684974329,0.0010540925533895,0.00014460149649591,5.267327705088,8665580.2878198,0.78943347292245,11656760.057363,-0.84041748247863 +-0.0014,-16767.40029811,834.74968970248,0.0010540925533895,0.00013143900298134,5.3348380033553,9019744.371253,0.6490564973851,11767371.967414,-0.71084001490589 +-0.0013,-14565.629257128,1393.3146005034,0.0010540925533895,0.0001476802925714,5.5454053939759,9382602.1347455,0.78380035676994,11873554.747473,-0.85581146285601 +-0.0012,-17067.788129133,864.75760216419,0.0010540925533895,0.00013444288129158,5.6097239623753,9754169.3616229,0.6411833961633,11975230.402152,-0.72585940645707 +-0.0011,-14858.511548752,1422.8393417673,0.0010540925533895,0.00015060911548764,5.8204668989916,10134456.062543,0.7770408535172,12072324.87127,-0.87045557743724 +-0.001,-17352.978671267,893.71998220276,0.0010540925533895,0.00013729478671295,5.8810584943182,10523466.416478,0.63238713484159,12164768.073265,-0.74011893356375 +-0.00090000000000001,-15136.004819923,1451.2928007586,0.0010540925533895,0.00015338404819936,6.0913133626341,10921198.716501,0.76926930114874,12252493.945269,-0.88433024099578 +-0.00080000000000001,-17622.603131573,921.5887018951,0.0010540925533895,0.00013999103131597,6.1476557654863,11327645.320434,0.62278300541304,12335440.479802,-0.75360015657906 +-0.00070000000000001,-15397.763139276,1478.6285634101,0.0010540925533895,0.00015600163139289,6.3567683040962,11742792.606383,0.76060036156097,12413549.758085,-0.8974181569634 +-0.00060000000000001,-17876.338648426,948.31919378787,0.0010540925533895,0.00014252838648454,6.4083563937668,12166620.933188,0.61248568326182,12486767.979946,-0.76628693242174 +-0.00050000000000001,-15643.486620424,1504.8038330211,0.0010540925533895,0.00015845886620438,6.6156861257161,12599104.605824,0.75114793339748,12555045.49032,-0.90970433102077 +-0.00040000000000001,-18113.908453995,973.87055089526,0.0010540925533895,0.00014490408454022,6.6620357596972,13040211.845771,0.60160817541038,12618336.802334,-0.77816542270021 +-0.00030000000000001,-15872.921477857,1529.779519337,0.0010540925533895,0.0001607532147787,6.8669602173474,13489904.766363,0.74102412741721,12676600.616958,-0.92117607389245 +-0.00020000000000001,-18335.081826364,998.20560462474,0.0010540925533895,0.00014711581826391,6.907611878112,13948139.353154,0.59026083690859,12729799.839244,-0.78922409131866 +-0.00010000000000001,-16085.859878909,1553.5203054561,0.0010540925533895,0.00016288259878925,7.1095305844792,14414865.449289,0.73033831081867,12777901.59113,-0.931822993945 +-5.4481159167397e-18,-18539.673844865,1021.2909807158,0.0010540925533895,0.00014916173844898,7.1440527603668,14890026.74588,0.57855046115937,12820877.220829,-0.79945369224371 +9.9999999999995e-05,-16282.139606424,1575.994693107,0.0010540925533895,0.00016484539606444,7.3423909343551,15373560.777438,0.71919622637631,12858702.308804,-0.94163698032075 +0.00019999999999999,-18727.544963977,1043.0971339988,0.0010540925533895,0.00015104044964011,7.3703832058821,15865398.922292,0.56657944936521,12891356.67033,-0.80884724819935 +0.00029999999999999,-16461.643548148,1597.1750272101,0.0010540925533895,0.00016664043548168,7.5645951633922,16365466.408038,0.70769919156345,12918824.354673,-0.95061217740692 +0.00039999999999999,-18898.600422322,1063.5983630433,0.0010540925533895,0.00015275100422357,7.5856909709936,16873682.321987,0.55444506357902,12941093.640876,-0.81740002111659 +0.00049999999999999,-16624.299029554,1617.0375010956,0.0010540925533895,0.00016826699029575,7.7752631986163,17389959.626607,0.6959433820551,12958157.030193,-0.95874495147722 +0.00059999999999999,-19052.789503391,1082.772806287,0.0010540925533895,0.00015429289503425,7.7891322726069,17914205.179946,0.54223876704114,12970011.235172,-0.82510947517003 +0.00069999999999999,-16770.077006582,1635.5621439055,0.0010540925533895,0.00016972477006602,7.973586155398,18446319.760977,0.68401920312547,12976657.165421,-0.9660338503286 +0.00079999999999999,-19190.104664277,1100.6024211737,0.0010540925533895,0.00015566604664314,7.9799365936651,18986198.099906,0.53004565461133,12978099.910071,-0.83197523321436 +0.00089999999999999,-16898.991134173,1652.7327918844,0.0010540925533895,0.00017101391134196,8.1588307832292,19533728.913353,0.67201075151559,12974348.716974,-0.97247955670817 +0.00099999999999999,-19310.580547784,1117.072948027,0.0010540925533895,0.00015687080547819,8.1574107667995,20088794.944395,0.51794397517419,12965416.968665,-0.83799902738971 +0.0011,-17011.096725488,1668.5370452182,0.0010540925533895,0.00017213496725508,8.3303431805041,20651273.007448,0.65999536935967,12951322.1551,-0.97808483627389 +0.0012,-19414.292892191,1132.1738603493,0.0010540925533895,0.00015790792892227,8.3209423215623,21221034.03793,0.50600474692987,12932085.843241,-0.84318464461009 +0.0013,-17106.489615357,1682.9662121088,0.0010540925533895,0.00017308889615374,8.4875517681126,21797943.146641,0.64804329074811,12907733.643486,-0.98285448076734 +0.0014,-19501.357351614,1145.8983031334,0.0010540925533895,0.00015877857351648,8.4700020892493,22381859.678871,0.49429146550823,12878295.173019,-0.84753786758125 +0.0015,-17185.304940307,1696.0152416102,0.0010540925533895,0.00017387704940325,8.6299695199947,22972637.278122,0.63621738050448,12843804.016087,-0.98679524701481 +0.0016,-19571.928238535,1158.2430207192,0.0010540925533895,0.00015948428238569,8.6041460673928,23570123.954418,0.4828599038767,12804297.681281,-0.85106641192729 +0.0017,-17247.715846002,1707.682646677,0.0010540925533895,0.00017450115846023,8.7571954566152,24174162.157149,0.62457296377164,12759817.555838,-0.98991579229959 +0.0018,-19626.197198735,1169.2082755097,0.0010540925533895,0.00016002697198773,8.7230165535095,24784588.852359,0.47175800208114,12710408.857041,-0.85377985993732 +0.0019,-17293.932131712,1717.9704187435,0.0010540925533895,0.00017496332131734,8.8689154145079,25401235.60446,0.61315774406976,12656120.580737,-0.99222660658506 +0.002,-19664.391827581,1178.7977588107,0.0010540925533895,0.00016040891827617,8.8263425645774,26023928.662228,0.46102584397269,12597005.447049,-0.85568959137963 +0.0021,-17324.198840118,1726.8839349122,0.0010540925533895,0.00017526598840137,8.9649021116081,26652489.049101,0.60201180661407,12533119.84333,-0.99373994200536 +0.0022,-19686.774235404,1187.0184948611,0.0010540925533895,0.00016063274235438,8.9139395649546,27286732.657621,0.45069571725298,12464523.764413,-0.85680871177079 +0.0023,-17338.794799719,1734.4318588364,0.0010540925533895,0.00017541194799739,9.0450145340264,27926470.347991,0.59116770288044,12391280.750209,-0.99446973998538 +0.0024,-19693.639568753,1193.8807389512,0.0010540925533895,0.00016070139568791,8.9857085310784,28571508.050639,0.44079225242447,12313457.820735,-0.85715197843825 +0.0025,-17338.031126106,1740.626036075,0.0010540925533895,0.0001754043112613,9.1091966752003,29221646.872733,0.58065061169246,12231125.408609,-0.99443155630474 +0.0026,-19685.314493365,1199.3978704377,0.0010540925533895,0.00016061814493404,9.041634386245,29876683.208511,0.43133263557261,12144357.28909,-0.85673572466885 +0.0027,-17322.249687612,1745.4813846946,0.0010540925533895,0.00017524649687634,9.1574756630248,30536408.853397,0.57047857147707,12053230.507734,-0.99364248438 +0.0028,-19662.155643965,1203.5862813215,0.0010540925533895,0.00016038655644005,9.0817838431286,31200611.12175,0.42232688933402,11957825.305711,-0.85557778219885 +0.0029,-17291.821540085,1749.0157817278,0.0010540925533895,0.00017494221540109,9.1899593146038,31869072.968193,0.56066277780714,11858225.042879,-0.99212107700365 +0.003,-19624.548045467,1206.465260962,0.0010540925533895,0.00016001048045508,9.1063026954383,32541573.112408,0.41377821592891,11754516.118668,-0.85369740227397 +0.0031,-17247.145335194,1751.2499459906,0.0010540925533895,0.00017449545335218,9.2068331617011,33217886.167276,0.55120793991503,11646787.89085,-0.98988726675907 +0.0032,-19572.903509651,1208.056877396,0.0010540925533895,0.00015949403509696,9.1154126032553,33897782.770305,0.40568339575159,11535132.59227,-0.85111517548321 +0.0033,-17188.645706063,1752.2073177302,0.0010540925533895,0.00017391045706089,9.2083569928316,34581029.718189,0.54211268952362,11419645.245615,-0.98696228530255 +0.0034,-19507.659011067,1208.3858556618,0.0010540925533895,0.00015884159011113,9.1094074191735,35267390.104424,0.39803323472513,11300423.576286,-0.84785295055401 +0.0035,-17116.771633944,1751.9139354338,0.0010540925533895,0.00017319171633972,9.1948609602277,35956623.459862,0.53337003509973,11177567.923467,-0.98336858169659 +0.0036,-19429.275045692,1207.4794535079,0.0010540925533895,0.00015805775045738,9.0886491043862,36648485.896085,0.39081305342762,11051181.149455,-0.84393375228528 +0.0037,-17031.994799317,1750.3983101964,0.0010540925533895,0.00017234394799345,9.1667413016564,37342730.251485,0.5249678544804,10921368.547338,-0.97912973996521 +0.0038,-19338.233975689,1205.3673347563,0.0010540925533895,0.00015714733975735,9.053563285338,38039106.239943,0.38400321088693,10788237.747095,-0.8393816987851 +0.0039,-16934.807920744,1747.6912978658,0.0010540925533895,0.00017137207920772,9.1244557282907,38737360.601985,0.51688941875745,10651898.620209,-0.97427039603653 +0.004,-19235.038363577,1202.0814406261,0.0010540925533895,0.00015611538363627,9.0046345025365,39437237.258283,0.37757965591526,10512463.182866,-0.83422191817954 +0.0041,-16825.723084734,1743.8259692791,0.0010540925533895,0.00017028123084766,9.0685185305541,40138477.465401,0.50911394032018,10370045.497829,-0.96881615423604 +0.0042,-19120.209299034,1197.6558592745,0.0010540925533895,0.00015496709299088,8.9424012035845,40840819.973662,0.37151449890584,10224761.575062,-0.82848046495239 +0.0043,-16705.270069878,1738.8374788465,0.0010540925533895,0.00016907670069913,8.999495454076,41544001.186971,0.50161713804727,10076729.271206,-0.96279350349325 +0.0044,-18994.284721546,1192.1266937748,0.0010540925533895,0.000153707847216,8.8674505324769,42247755.324536,0.36577659713557,9926068.1879674,-0.82218423607803 +0.0045,-16573.994668427,1732.7629316769,0.0010540925533895,0.0001677639466846,8.9179983976598,42951814.584311,0.49437181279634,9772899.5695294,-0.95622973342065 +0.0046,-18857.817742199,1185.5319288167,0.0010540925533895,0.0001523431774225,8.780412966759,43655909.30805,0.36033214681168,9617346.1990519,-0.81536088711065 +0.0047,-16432.457008645,1725.6412495593,0.0010540925533895,0.00016634857008679,8.8246799844837,44359768.147845,0.48734842656541,9459532.2943587,-0.94915285043158 +0.0048,-18711.37496785,1177.9112963571,0.0010540925533895,0.00015087874967906,8.6819568532152,45063118.23401,0.35514527534528,9299583.4028941,-0.80803874839325 +0.0049,-16281.229881222,1717.5130359974,0.0010540925533895,0.00016483629881259,8.7202280566303,45765685.344197,0.48051567897599,9137626.2960358,-0.94159149406042 +0.005,-18555.534831017,1169.3061404242,0.0010540925533895,0.00014932034831077,8.5727828914815,46467194.073602,0.35017862764257,8973788.8628529,-0.8002467415516 +0.0051,-16120.897073,1708.4204405407,0.0010540925533895,0.00016323297073042,8.6053601415595,47167368.00613,0.47384107506057,8808200.0033931,-0.9335748536493 +0.0052,-18390.885928769,1159.7592813971,0.0010540925533895,0.00014767385928829,8.4536186132493,47865929.886394,0.34539394055418,8640989.5215887,-0.79201429643917 +0.0053,-15952.051711434,1698.4070227067,0.0010540925533895,0.00016154451711473,8.4808179372388,48562601.792405,0.46729147870942,8472288.0178693,-0.92513258557099 +0.0054,-18218.025373964,1149.3148799726,0.0010540925533895,0.00014594525374026,8.3252129027057,49257105.308845,0.34075260001608,8302226.781565,-0.78337126869897 +0.0055,-15775.294622997,1687.5176157275,0.0010540925533895,0.00015977694623036,8.3473618604561,49949161.700755,0.46083364654168,8130937.6831894,-0.9162947311491 +0.0056,-18037.557162175,1138.0183010778,0.0010540925533895,0.00014414057162237,8.1883306014781,50638492.08755,0.33621617584337,7958553.066688,-0.77434785810953 +0.0057,-15591.23270899,1675.7981903674,0.0010540925533895,0.00015793632709029,8.2057657003019,51324817.617175,0.45443473741367,7785205.6417389,-0.90709163544876 +0.0058,-17850.090557584,1125.9159780374,0.0010540925533895,0.00014226590557648,8.0437472386757,52007859.640334,0.33174692959714,7611028.3761911,-0.76497452788 +0.0059,-15400.477341908,1663.2957191309,0.0010540925533895,0.00015602877341948,8.056811416023,52687339.884605,0.44806279324162,7436154.3887264,-0.89755386709464 +0.006,-17656.238501105,1113.0552772106,0.0010540925533895,0.00014032738501169,7.892243923729,53362980.628342,0.32730829142327,7260716.8418286,-0.75528192505604 +0.0061,-15203.642785665,1650.0580410929,0.0010540925533895,0.00015406042785708,7.9012841154117,54034504.874227,0.44168718730971,7084848.8351459,-0.88771213928247 +0.0062,-17456.616043938,1099.4843634251,0.0010540925533895,0.00013833116044006,7.7346024365656,54701636.522337,0.32286530226214,6908683.2993287,-0.74530080219772 +0.0063,-15001.344642832,1636.1337276447,0.0010540925533895,0.00015203744642876,7.7399672466663,55364100.542589,0.43527903673643,6732352.8904271,-0.87759723214083 +0.0064,-17251.838809682,1085.2520664301,0.0010540925533895,0.00013628338809752,7.571600546361,56021623.146467,0.31838501833807,6555989.8849292,-0.73506194048493 +0.0065,-14794.198331939,1621.5719494162,0.0010540925533895,0.00014996598331986,7.5736380332561,56673931.957874,0.4288115762873,6379726.0755226,-0.86723991659617 +0.0066,-17042.521488023,1070.4077487017,0.0010540925533895,0.00013419021488095,7.4040075866247,57320756.182986,0.3138368753558,6203692.6676577,-0.72459607440199 +0.0067,-14582.817597844,1606.4223446777,0.0010540925533895,0.00014785217597894,7.4030631778061,57961826.778998,0.42226049123867,6028020.1769945,-0.85667087989141 +0.0068,-16829.276362905,1055.0011748082,0.0010540925533895,0.00013205776362981,7.2325803108262,58596876.621622,0.30919301034925,5852838.3278098,-0.71393381814606 +0.0069,-14367.813058011,1590.7348894356,0.0010540925533895,0.00014570213058063,7.2289948574114,59225640.671236,0.41560420751485,5678275.9524428,-0.84592065289974 +0.007,-16612.711878024,1039.0823826444,0.0010540925533895,0.00012989211878101,7.058059049118,59847856.137511,0.30442853964583,5504460.8918551,-0.70310559390206 +0.0071,-14149.790787445,1574.5597695436,0.0010540925533895,0.00014352190787498,7.0521670291226,60463262.64249,0.40882413783186,5331519.8973803,-0.83501953937141 +0.0072,-16393.431242262,1022.7015567829,0.0010540925533895,0.00012769931242339,6.8811641830571,61071602.381896,0.2995217919151,5159578.5337369,-0.69214156211393 +0.0073,-13929.35094486,1557.9472550394,0.0010540925533895,0.0001413175094491,6.8732920606966,61672620.284623,0.40190488308112,4988761.0833772,-0.82399754724216 +0.0074,-16172.031077602,1005.9089041715,0.0010540925533895,0.00012548531077679,6.7025929515601,62266064.170273,0.29445449576883,4819190.4522423,-0.68107155388095 +0.0075,-13707.086442542,1540.9475769846,0.0010540925533895,0.00013909486442595,6.6930576980448,62851684.904626,0.39483438867421,4650988.0769948,-0.81288432212628 +0.0076,-15949.100111915,988.75453246132,0.0010540925533895,0.00012325600111994,6.5230165977234,63429236.552927,0.2892119218581,4484273.8337958,-0.66992500559663 +0.0077,-13483.581662227,1523.6108070306,0.0010540925533895,0.00013685981662282,6.5121243772451,63998476.530888,0.3876040560373,4319165.9486933,-0.80170908311051 +0.0078,-15725.217918807,971.28833115567,0.0010540925533895,0.00012101717918888,6.343077862585,64559165.753312,0.28378297987091,4155780.9096878,-0.65873089594123 +0.0079,-13259.411219062,1505.9867399212,0.0010540925533895,0.00013461811219117,6.3311228854827,65111068.7802,0.38020880988818,3994233.3805386,-0.79050056095223 +0.008,-15500.953706606,953.55985582783,0.0010540925533895,0.00011877453706691,6.1633888284909,65653953.960261,0.2781602712714,3834636.1163709,-0.64751768533118 +0.0081,-13035.138775715,1488.124779209,0.0010540925533895,0.00013237538775772,6.1506523719246,66187593.571733,0.37264712235291,3677099.881148,-0.77928693878486 +0.0082,-15276.86515837,935.61821560463,0.0010540925533895,0.00011653365158456,5.984529111419,66711763.960405,0.27234009902793,3521733.3670627,-0.63631325791943 +0.0083,-12811.315908383,1470.0738262986,0.0010540925533895,0.00013013715908441,5.9712787063059,67226245.67473,0.36492099536727,3368643.115907,-0.76809579541826 +0.0084,-15053.497324669,917.51196414069,0.0010540925533895,0.00011429997324757,5.8070443984875,67730823.597964,0.26632243595733,3217933.4424751,-0.62514486623438 +0.0085,-12588.481026378,1451.8821731092,0.0010540925533895,0.00012790881026438,5.7935331799749,68225287.077229,0.35703590317268,3069706.3600521,-0.75695405131801 +0.0086,-14831.381570668,899.28899421039,0.0010540925533895,0.00011207881570757,5.6314453239297,68709430.049412,0.2601108536582,2924061.5080401,-0.61403907853432 +0.0087,-12367.15834676,1433.5973984417,0.0010540925533895,0.0001256955834682,5.617911541278,69183051.163805,0.34900069704213,2781096.0817707,-0.74588791733707 +0.0088,-14611.03457895,880.99643615894,0.0010540925533895,0.00010987534579042,5.4582066740662,69645953.901427,0.25371241431991,2640904.7645507,-0.60302172894845 +0.0089,-12147.856925339,1415.2662682975,0.0010540925533895,0.00012350256925403,5.4448733545493,70097946.690928,0.34082747466753,2503579.6619889,-0.73492284626603 +0.009,-14392.957409305,862.68056034282,0.0010540925533895,0.00010769457409395,5.2877669092959,70538843.020996,0.24713752796882,2369210.2386431,-0.59211787046621 +0.0091,-11931.069745205,1396.9346402358,0.0010540925533895,0.00012133469745266,5.2748416695571,70968461.54924,0.33253141689309,2237883.2570337,-0.72408348725931 +0.0092,-14177.634616516,844.38668368041,0.0010540925533895,0.00010554134616606,5.1205279888433,71386626.207384,0.24039977795454,2109682.7190585,-0.58135173082678 +0.0093,-11717.272863753,1378.64737198,0.0010540925533895,0.00011919672863814,5.1082029861042,71793166.302807,0.3241305947038,1984689.8098497,-0.71339364318672 +0.0094,-13965.533427118,826.1590805146,0.0010540925533895,0.0001034203342721,4.9568554819625,72187916.616303,0.23351571768074,1862982.8441055,-0.57074667135688 +0.0095,-11506.924619058,1360.4482343341,0.0010540925533895,0.0001170932461912,4.9453074965659,72570717.496017,0.31564574955523,1744637.2149312,-0.70287623095195 +0.0096,-13757.102975813,808.04089782962,0.0010540925533895,0.00010133602975905,4.7970789475272,72941414.947491,0.22650464174894,1629725.3452196,-0.56032514879166 +0.0097,-11300.464896252,1342.3798285662,0.0010540925533895,0.00011502864896314,4.7864695875139,73299860.719784,0.3071000502787,1518316.6415994,-0.69255324481162 +0.0098,-13552.773602161,790.07407499807,0.0010540925533895,9.9292736022552e-05,4.6414925624181,73645912.387574,0.21938833480944,1410477.4509791,-0.55010868010906 +0.0099,-11098.314454405,1324.4835083584,0.0010540925533895,0.0001130071445447,4.6319685801746,73979433.429258,0.29851882989816,1306271.01971,-0.68244572271928 +0.01,-13352.956207988,772.29926812711,0.0010540925533895,9.7294562080825e-05,4.4903559778628,74300293.30092,0.21219080150294,1205757.4553902,-0.54011781040041 +0.0101,-10900.87431429,1306.7993063862,0.0010540925533895,0.00011103274314354,4.482049688359,74608367.506217,0.28992930576682,1108993.6913308,-0.6725737157135 +0.0102,-13158.041675753,754.7557790621,0.0010540925533895,9.5345416758471e-05,4.3438953819014,74903537.662054,0.20493798092629,1016033.4537003,-0.53037208378866 +0.0103,-10708.525207208,1289.3658656588,0.0010540925533895,0.0001091092520727,4.3369251716391,75185691.560079,0.28136028646188,926927.23136452,-0.6629562603594 +0.0104,-12968.400348042,737.4814891822,0.0010540925533895,9.3449003481362e-05,4.202304745415,75454723.223945,0.19765744907204,841722.24843485,-0.52089001740311 +0.0105,-10521.62708494,1272.2203756129,0.0010540925533895,0.00010724027085003,4.1967756609378,75710532.962283,0.27284186887208,760462.43953463,-0.65361135424597 +0.0106,-12784.381568148,720.51279799016,0.0010540925533895,9.1608815682451e-05,4.065747228681,75953027.417409,0.19037811267234,683188.42779459,-0.51168907840843 +0.0107,-10340.518690743,1255.3985130891,0.0010540925533895,0.0001054291869081,4.0617516333535,76182119.609707,0.26440512887872,609937.50558294,-0.64455593453614 +0.0108,-12606.313281567,703.88456656279,0.0010540925533895,8.9828132816636e-05,3.9343567251862,76397728.977659,0.183129897825,540743.61797454,-0.50278566407939 +0.0109,-10165.517191128,1238.9343881547,0.0010540925533895,0.00010367917191196,3.9319750129223,76599781.413559,0.25608180895829,475637.34896183,-0.63580585955536 +0.011,-12434.501698117,687.63006590004,0.0010540925533895,8.8110016982171e-05,3.808239519439,76788209.294842,0.17594343669747,414645.91040742,-0.49419508490691 +0.0111,-9996.9178680728,1222.8604948894,0.0010540925533895,0.00010199317868144,3.8075408741473,76962951.511058,0.2479040059399,357793.13373599,-0.62737589340259 +0.0112,-12269.231014255,671.78093021113,0.0010540925533895,8.6457310143571e-05,3.687476035752,77123953.486444,0.16884975549144,305099.46436154,-0.48593155071382 +0.0113,-9834.993871134,1207.2076670633,0.0010540925533895,0.00010037393871207,3.6885192254553,77271167.198145,0.23990386202093,256581.95884359,-0.61927969355564 +0.0114,-12110.763194941,656.36711509351,0.0010540925533895,8.4872631950459e-05,3.5721226554027,77404551.190044,0.16187996671337,212254.28476315,-0.47800815974812 +0.0115,-9679.9960288469,1192.0050387838,0.0010540925533895,9.8823960289207e-05,3.5749568502875,77524070.582209,0.23211326199633,172126.72330814,-0.61152980144128 +0.0116,-11959.337814447,641.41686069109,0.0010540925533895,8.3358378145543e-05,3.4622135802287,77629697.076001,0.15506496863283,136206.17455534,-0.47043689072343 +0.0117,-9532.1527185835,1177.2800100525,0.0010540925533895,9.73455271866e-05,3.4668791842566,77721408.954787,0.22456353947828,104496.16543372,-0.60413763592809 +0.0118,-11815.171955178,626.95665974452,0.0010540925533895,8.1916719552857e-05,3.3577627215229,77799191.080343,0.14843515462759,76996.86035213,-0.46322859776001 +0.0119,-9391.6697940399,1163.0582172491,0.0010540925533895,9.594069794114e-05,3.3642922077115,77863034.884919,0.21728519469386,53705.074472252,-0.5971134897009 +0.012,-11678.460163595,613.01123053294,0.0010540925533895,8.0549601637051e-05,3.2587655940841,77912938.359016,0.14202013491066,34614.289605595,-0.45639300818086 +0.0121,-9258.7305692746,1149.3635085196,0.0010540925533895,9.4611305693515e-05,3.2671843341009,77948906.034844,0.21030762623182,19714.672711274,-0.59046652846263 +0.0122,-11549.374462169,599.60349470159,0.0010540925533895,7.9258744622811e-05,3.1652011964031,77970948.965567,0.13584847291674,8993.0969695049,-0.44993872310958 +0.0123,-9133.4958582239,1136.2179239775,0.0010540925533895,9.3358958583011e-05,3.175528275728,77979084.700312,0.20365887888633,2433.1654036208,-0.58420479291008 +0.0124,-11428.064416156,586.75455991226,0.0010540925533895,7.8045644162682e-05,3.0770338592233,77973337.254982,0.12994743839368,15.237021522945,-0.44387322080895 +0.0125,-9016.1040683639,1123.641680732,0.0010540925533895,9.2185040684415e-05,3.0892828697965,77953737.078915,0.19736540950703,1716.4554456217,-0.57833520341707 +0.0126,-11314.657253883,574.4837072297,0.0010540925533895,7.691157253997e-05,2.9942150460826,77920321.017436,0.12434277900202,7510.7799984425,-0.43820286269529 +0.0127,-8906.6713472719,1111.6531626774,0.0010540925533895,9.1090713473533e-05,3.0083948490695,77873132.270322,0.19145187251841,17369.019209222,-0.57286356736246 +0.0128,-11209.25803917,562.8083832446,0.0010540925533895,7.585758039284e-05,2.9166850908944,77812220.346262,0.11905851197682,31258.866705016,-0.43293290195964 +0.0129,-8805.2917805315,1100.2689148828,0.0010540925533895,9.0076917806147e-05,2.9328005429451,77737641.013308,0.18594092651741,49144.939448123,-0.56779458902543 +0.013,-11111.949894378,551.74419680995,0.0010540925533895,7.488449894494e-05,2.8443748591608,77649456.245413,0.11411673715163,70988.818279856,-0.42806749472007 +0.0131,-8712.0376395071,1089.5036426576,0.0010540925533895,8.9144376395882e-05,2.8624274963241,77547734.165099,0.18085306310419,96749.090729013,-0.5631318819742 +0.0132,-11022.794272462,541.30492031039,0.0010540925533895,7.3992942725816e-05,2.7772073209917,77432548.982296,0.10953747238741,126381.39604189,-0.42360971362426 +0.0133,-8626.9596772942,1079.3702150743,0.0010540925533895,8.8293596773783e-05,2.7971959952366,77303980.929414,0.17620645884295,159838.4723888,-0.55887798386355 +0.0134,-10941.831276375,531.50249540729,0.0010540925533895,7.3183312764943e-05,2.7150990257181,77162116.192731,0.10533851219482,197070.2062007,-0.41956156381994 +0.0135,-8550.0874711568,1069.87967288,0.0010540925533895,8.7524874712406e-05,2.7370204898276,77007046.840122,0.17201685099452,238023.68358813,-0.55503437355666 +0.0136,-10869.080024033,522.34704310826,0.0010540925533895,7.2455800241511e-05,2.6579614695284,76838870.745256,0.10153531008383,282643.24379274,-0.41592400120287 +0.0137,-8481.4298096151,1061.0412407617,0.0010540925533895,8.6838298096991e-05,2.681810906942,76657691.508252,0.1682974374128,330870.5346209,-0.55160149047957 +0.0138,-10804.539056998,513.84687808282,0.0010540925533895,7.1810390571188e-05,2.6057023491913,76463618.372942,0.098140884928005,382644.56980677,-0.41269695285112 +0.0139,-8420.9751223118,1052.8623437489,0.0010540925533895,8.6233751223983e-05,2.6314738461728,76256766.14075,0.16505880075136,437901.78825172,-0.54857875611439 +0.014,-10748.186790987,506.00852710998,0.0010540925533895,7.1246867911081e-05,2.5582266965513,76037255.081306,0.095165751391203,496576.11508484,-0.40987933955058 +0.0141,-8368.6919506979,1045.3486276718,0.0010540925533895,8.5710919507834e-05,2.5859136548563,75805210.839863,0.16230885689216,558599.02448846,-0.54596459753369 +0.0142,-10699.982006172,498.8367514781,0.0010540925533895,7.0764820062951e-05,2.5154378900735,75560764.341593,0.092617874232104,623899.60423187,-0.40746910030984 +0.0143,-8324.5294575574,1038.5039836049,0.0010540925533895,8.5269294576432e-05,2.5450333790562,75304051.692823,0.16005282728198,692404.62185419,-0.54375647287665 +0.0144,-10659.864375286,492.33457328638,0.0010540925533895,7.0363643754081e-05,2.4772385412637,75035214.079341,0.090502646084839,764038.59243749,-0.40546321876554 +0.0145,-8288.4179732389,1032.3305760153,0.0010540925533895,8.4908179733257e-05,2.5087355891097,74754397.661827,0.1582932346498,838723.84790916,-0.54195089866071 +0.0146,-10627.755027397,486.50330542853,0.0010540925533895,7.0042550275248e-05,2.4435312552792,74461753.468497,0.088822888105891,916380.60781205,-0.40385775137111 +0.0147,-8260.2695765554,1026.8288746386,0.0010540925533895,8.4626695766458e-05,2.4769230797599,74157437.285066,0.15702992137798,996927.05148038,-0.54054347882653 +0.0148,-10603.557145261,481.34258516086,0.0010540925533895,6.9800571453863e-05,2.4142192664649,73841609.542082,0.087578872686511,1080279.3915577,-0.4026478572643 +0.0149,-8239.9787081162,1021.9976897792,0.0010540925533895,8.4423787082026e-05,2.4494994462798,73514435.199814,0.15626008961702,1166351.9487926,-0.53952893540456 +0.015,-10587.156593975,476.85041106371,0.0010540925533895,6.9636565940996e-05,2.3892069508934,73176083.630647,0.086768367252747,1255057.2280483,-0.40182782970003 +0.0151,-8227.4228139238,1017.8342110077,0.0010540925533895,8.4298228140107e-05,2.426369539293,72826728.499232,0.15597836206452,1346305.9954588,-0.53890114069492 +0.0152,-10578.422578845,473.0231832851,0.0010540925533895,6.9549225789677e-05,2.368400219233,72466547.640372,0.086386698016656,1440007.3566669,-0.40139112894354 +0.0153,-8222.4630170183,1014.3340489769,0.0010540925533895,8.4248630171064e-05,2.4074398021975,72095722.93479,0.15617686217871,1536068.8360769,-0.53865315084964 +0.0154,-10577.208330098,469.85574684663,0.0010540925533895,6.9537083302265e-05,2.3517067944298,71714440.182903,0.086426832398114,1634396.457053,-0.40133041650618 +0.0155,-8224.9448148741,1011.4912802995,0.0010540925533895,8.4273448149646e-05,2.3926184962036,71322888.976629,0.15684531246296,1734894.8229982,-0.53877724074242 +0.0156,-10583.351812259,467.34143785979,0.0010540925533895,6.959851812389e-05,2.33903637973,70921262.569421,0.086879478714606,1837467.1992437,-0.40163759061428 +0.0157,-8234.6988002978,1009.2984951914,0.0010540925533895,8.437098800392e-05,2.3818158189867,70509757.744551,0.15797114934374,1942015.5956792,-0.53926494001359 +0.0158,-10596.676455866,465.47213252578,0.0010540925533895,6.9731764559993e-05,2.3303007235174,70088574.681785,0.087733201629172,2048440.8500581,-0.40230382279459 +0.0159,-8251.5414035529,1007.7468478417,0.0010540925533895,8.4539414036475e-05,2.3749439238464,69657916.822556,0.15953965306828,2156642.7119052,-0.54010707017634 +0.016,-10616.991909248,464.23829865309,0.0010540925533895,6.993491909383e-05,2.3254135882557,69217990.733727,0.088974551760442,2266519.9269588,-0.40331959546372 +0.0161,-8275.2756533698,1006.8261092202,0.0010540925533895,8.4776756534659e-05,2.3719168470207,68769005.970048,0.16153409097001,2377970.3220783,-0.54129378266717 +0.0162,-10644.094808061,463.62904960202,0.0010540925533895,7.0205948081984e-05,2.3242906315461,68311174.935432,0.090588207790325,2490890.8905457,-0.40467474040437 +0.0163,-8305.6919545947,1006.5247221852,0.0010540925533895,8.5080919546936e-05,2.3726503514661,67844712.743123,0.16393587239246,2605177.8776927,-0.5428145977284 +0.0164,-10677.769560307,463.63220040276,0.0010540925533895,7.0542695604452e-05,2.3268492078897,67369837.074904,0.092557129355565,2720726.8667837,-0.40635847801669 +0.0165,-8342.5688801746,1006.8298587269,0.0010540925533895,8.5449688802723e-05,2.3770616959289,66886768.039416,0.16672471352231,2837432.865084,-0.54465844400739 +0.0166,-10717.789144514,464.23432590284,0.0010540925533895,7.094289144653e-05,2.333008100222,66395728.029703,0.094862718977861,2955190.3900442,-0.40835945722708 +0.0167,-8385.6739751562,1007.7274790995,0.0010540925533895,8.5880739752541e-05,2.3850693385621,65896941.580124,0.16987881035867,3073893.5555319,-0.54681369875646 +0.0168,-10763.915918867,465.42082074724,0.0010540925533895,7.1404159190061e-05,2.3426871906365,65390635.222683,0.0974849912742,3193436.158041,-0.41066579594471 +0.0169,-8434.7645705287,1009.202392729,0.0010540925533895,8.6371645706268e-05,2.3965925846204,64877037.342919,0.17337501804394,3313711.7628098,-0.54926822852507 +0.017,-10815.902438974,467.17596099481,0.0010540925533895,7.1924024391122e-05,2.3558070799509,64356378.035476,0.10040274769204,3434613.7897796,-0.41326512195011 +0.0171,-8489.5886045739,1011.2383206375,0.0010540925533895,8.6919886046722e-05,2.4115511879587,63828888.9594,0.1771890347918,3556035.5993265,-0.55200943022732 +0.0172,-10873.492282092,469.4829671917,0.0010540925533895,7.2499922822318e-05,2.3722886658964,63294803.193357,0.10359375503513,3677870.5776969,-0.41614461410598 +0.0173,-8549.8854495892,1013.8179592617,0.0010540925533895,8.7522854496863e-05,2.4298649161175,62754355.090794,0.18129558867769,3800012.2220811,-0.55502427247808 +0.0174,-10936.420875596,472.32406870907,0.0010540925533895,7.3129208757355e-05,2.3920526897162,62207780.135198,0.10703492608033,3922354.2252583,-0.41929104378122 +0.0175,-8615.386741746,1016.9230454087,0.0010540925533895,8.8177867418427e-05,2.4514530887431,61655314.795546,0.18566862559991,4044790.5597469,-0.5582993370859 +0.0176,-11004.416327532,475.68056915319,0.0010540925533895,7.3809163276702e-05,2.4150192608748,61097196.38205,0.11070250063676,4167215.5613944,-0.42269081637804 +0.0177,-8685.8172120136,1020.5344222269,0.0010540925533895,8.8882172121117e-05,2.4762340989497,60533662.902268,0.19028149677789,4289524.012343,-0.56182086059927 +0.0178,-11077.200257151,479.53291269453,0.0010540925533895,7.4537002572926e-05,2.4411073693865,59964952.917783,0.11457222546144,4411611.2233063,-0.42633001285899 +0.0179,-8760.8955159712,1024.632105958,0.0010540925533895,8.9632955160707e-05,2.5041249269889,59391305.401395,0.19510714422256,4533373.115095,-0.56557477579714 +0.018,-11154.488623338,483.86075104759,0.0010540925533895,7.5309886234801e-05,2.470234394986,58812959.595103,0.11861953152169,4654706.2993293,-0.43019443116834 +0.0181,-8840.3350605403,1029.1953533048,0.0010540925533895,9.0427350606379e-05,2.5350406552678,58230154.868802,0.20011828269888,4775508.1582757,-0.56954675302559 +0.0182,-11235.992548918,488.64301102858,0.0010540925533895,7.6124925490611e-05,2.5023156219969,57643130.579964,0.12281970718129,4895676.9237491,-0.43426962744737 +0.0183,-8923.8448255703,1034.2027292443,0.0010540925533895,9.1262448256732e-05,2.5688939933448,57052125.934248,0.20528757678946,5015111.7550203,-0.57372224127708 +0.0184,-11321.419138871,493.85796242886,0.0010540925533895,7.6979191390164e-05,2.5372637683035,56457379.847245,0.12714806598323,5133712.8156715,-0.43854095694501 +0.0185,-9011.1301784089,1039.6321750684,0.0010540925533895,9.2135301785121e-05,2.6055948210492,55859130.807387,0.21058781177167,5251381.3493403,-0.578086508919 +0.0186,-11410.472290547,499.48328604281,0.0010540925533895,7.7869722906932e-05,2.5749885363137,55257616.740164,0.13158010780714,5368019.7542991,-0.44299361452884 +0.0187,-9101.8936794916,1045.4610765166,0.0010540925533895,9.304293679594e-05,2.6450497573184,54653074.873687,0.2159920571292,5483531.6568128,-0.58262468397312 +0.0188,-11502.85349403,505.4961416993,0.0010540925533895,7.8793534941786e-05,2.6153961932162,54045741.605731,0.13609167229074,5597821.983222,-0.44761267470297 +0.0189,-9195.8358772266,1051.666331821,0.0010540925533895,9.3982358773285e-05,2.6871617617341,53435852.372328,0.2214738216339,5710797.0306987,-0.58732179385986 +0.019,-11598.262620869,511.87323609293,0.0010540925533895,7.9747626210144e-05,2.6583891872037,52823641.518008,0.14065908352213,5822364.5366239,-0.45238313104494 +0.0191,-9292.6560903591,1058.2244194319,0.0010540925533895,9.4950560904572e-05,2.7318297750861,52209342.167747,0.22700719905427,5932433.7465366,-0.59216280451647 +0.0192,-11696.398699475,518.59089030027,0.0010540925533895,8.0728986996203e-05,2.703865805647,51593186.100744,0.14525928513111,6040915.4806055,-0.45728993497526 +0.0193,-9392.053176152,1065.1114653897,0.0010540925533895,9.5944531762527e-05,2.7789484045858,50975403.62608,0.2325670036704,6147722.1985773,-0.59713265880611 +0.0194,-11796.9606755,525.62510673327,0.0010540925533895,8.1734606756485e-05,2.7517198804951,50356223.46035,0.14986996503166,6252768.063154,-0.46231803377649 +0.0195,-9493.726282801,1072.3033100255,0.0010540925533895,9.6961262829077e-05,2.8284076586223,49735872.607349,0.23812889490228,6355969.0017564,-0.60221631413855 +0.0196,-11899.648155692,532.95163545084,0.0010540925533895,8.2761481558452e-05,2.8018405454209,49114576.239875,0.1544696691951,6457242.7666302,-0.4674524077861 +0.0197,-9597.3755845357,1079.77557396,0.0010540925533895,9.7997755846444e-05,2.8800927351955,48492557.583742,0.24366949048432,6556508.9932537,-0.60739877922527 +0.0198,-12004.162133688,540.54603964559,0.0010540925533895,8.3806621338443e-05,2.8541120484769,47870037.804064,0.15903790395806,6653689.257006,-0.47267810668592 +0.0199,-9702.7029979291,1087.5037231916,0.0010540925533895,9.9051029980379e-05,2.9338838673932,47247235.89387,0.24916646774544,6748707.1280594,-0.61266514989493 +0.02,-12110.205696335,548.38376014575,0.0010540925533895,8.4867056964899e-05,2.908413623243,46624368.565139,0.16355522649587,6841488.2244571,-0.4779802848183 +0.0201,-9809.4128780955,1095.4631331313,0.0010540925533895,0.00010011812878206,2.989656228499,46001650.142314,0.25459865268179,6931960.2633434,-0.61800064390324 +0.0202,-12217.484709226,556.44017882837,0.0010540925533895,8.5939847093848e-05,2.9646194206751,45379292.458337,0.16800332321542,7020053.1103102,-0.48334423546284 +0.0203,-9917.2126934476,1103.6291514652,0.0010540925533895,0.00010119612693561,3.0472798985418,44757504.753306,0.25994609662809,7105698.8268294,-0.62339063467083 +0.0204,-12325.708480214,564.69068074811,0.0010540925533895,8.7022084803735e-05,3.0225985030863,44136493.575771,0.17236507594136,7188831.715742,-0.48875542401228 +0.0205,-10025.813677831,1111.9771596983,0.0010540925533895,0.00010228213677941,3.1066198933322,43516462.68675,0.26519014045692,7269388.3647726,-0.62882068388998 +0.0206,-12434.590399681,573.11071491375,0.0010540925533895,8.81109039984e-05,3.0822149009366,42897612.96652,0.17662461588593,7347307.6880443,-0.49419951998565 +0.0207,-10134.931458865,1120.4826332499,0.0010540925533895,0.00010337331458978,3.16753625628,42280142.32422,0.27031346634558,7422530.9655695,-0.63427657294168 +0.0208,-12543.848556563,581.67585357382,0.0010540925533895,8.9203485567209e-05,3.1433277323645,41664245.610321,0.18076736550415,7495001.8806907,-0.49966242782972 +0.0209,-10244.28666154,1129.1211999974,0.0010540925533895,0.0001044668666165,3.2298842125602,41050114.532013,0.27530013726294,7564666.5554515,-0.63974433307541 +0.021,-12653.206329052,590.36184983417,0.0010540925533895,9.0297063292131e-05,3.2057913846853,40437937.571559,0.18478006844046,7631473.5838774,-0.50513031645421 +0.0211,-10353.60548602,1137.8686971449,0.0010540925533895,0.00010556005486134,3.293514384501,39827899.907623,0.28013562442882,7695374.0631481,-0.6452102742994 +0.0212,-12762.392949165,599.14469362212,0.0010540925533895,9.13889294933e-05,3.2694557563933,39220183.339674,0.18865080787312,7756321.6226443,-0.51058964745984 +0.0213,-10462.620258868,1146.7012262978,0.0010540925533895,0.00010665020258985,3.3582730663977,38614966.215436,0.28480682309569,7814272.450855,-0.65066101294181 +0.0214,-12871.144040251,608.00066574781,0.0010540925533895,9.2476440404156e-05,3.3341665575722,38012423.361454,0.19236901365162,7869185.3201325,-0.51602720201417 +0.0215,-10571.069956872,1155.595206666,0.0010540925533895,0.00010773469956987,3.4240025563489,37412726.016804,0.28930205708965,7921021.6092832,-0.65608349784198 +0.0216,-12979.20212679,616.90639010328,0.0010540925533895,9.3557021269583e-05,3.3997656660133,36816041.76997,0.19592545870895,7969745.3239846,-0.52143010634116 +0.0217,-10678.700702773,1164.5274263135,0.0010540925533895,0.00010881100702891,3.4905415421237,36222534.498907,0.29361107262904,8015323.1150225,-0.66146503513703 +0.0218,-13086.317115797,625.83888379476,0.0010540925533895,9.4628171159645e-05,3.4660915357918,35632364.314334,0.19931224530274,8057724.2943407,-0.52678585579152 +0.0219,-10785.266232359,1173.4750913106,0.0010540925533895,0.00010987666232479,3.5577255375511,35045687.506255,0.29772502200956,8096920.8489002,-0.66679331161631 +0.022,-13192.24674927,634.77560523175,0.0010540925533895,9.568746749439e-05,3.5329796545526,34462656.493732,0.20252278170843,8132887.4523452,-0.53208233746517 +0.0221,-10890.528332311,1182.4158727828,0.0010540925533895,0.00011092928332431,3.6253873654491,33883419.777943,0.30163643780848,8165601.474475,-0.67205641661388 +0.0222,-13296.757027237,643.69450000025,0.0010540925533895,9.6732570274052e-05,3.600263045313,33308121.898509,0.20555175004412,8195042.9885246,-0.53730785136353 +0.0223,-10994.257248453,1191.3279517515,0.0010540925533895,0.00011196657248572,3.693357682693,32736903.393119,0.3053391983141,8221194.7762544,-0.67724286242102 +0.0224,-13399.622600964,652.57404453246,0.0010540925533895,9.7761226011346e-05,3.6677728081984,32169900.760457,0.20839506595563,8244042.3308559,-0.54245113004989 +0.0225,-11096.232064011,1200.1900617071,0.0010540925533895,0.00011298632064131,3.7614655426634,31607246.426433,0.30882848493201,8263573.8576789,-0.6823416031989 +0.0226,-13500.627136074,661.39328747613,0.0010540925533895,9.8771271362439e-05,3.7353386971974,31049068.713713,0.21104983093053,8279780.2727883,-0.54750135680538 +0.0227,-11196.241047628,1208.9815288305,0.0010540925533895,0.00011398641047748,3.8295389900149,30495491.814578,0.31210073235439,8292655.1993601,-0.68734205237974 +0.0228,-13599.563645306,670.13188871105,0.0010540925533895,9.9760636454768e-05,3.8027897267551,29946635.767049,0.2135142780401,8302194.9619294,-0.55244818226698 +0.0229,-11294.08197097,1217.6823098988,0.0010540925533895,0.00011496481971091,3.8974056824667,29402616.434353,0.31515357230673,8308398.5785017,-0.69223409854683 +0.023,-13696.234790805,678.77015598519,0.0010540925533895,0.00010072734790976,3.8699548028115,28863545.487645,0.21578771192961,8311267.7505439,-0.55728173954196 +0.0231,-11389.56239579,1226.2730277105,0.0010540925533895,0.00011591962395907,3.964893534139,28329530.392023,0.31798577170322,8310806.8508706,-0.69700811978779 +0.0232,-13790.453155876,687.28907910869,0.0010540925533895,0.00010166953156046,3.9366633727399,27800674.395795,0.21787044389132,8307022.9094443,-0.56199265779553 +0.0233,-11482.49993046,1234.7350041393,0.0010540925533895,0.00011684899930576,4.0318313748306,27277076.523022,0.32059716605178,8299925.5971089,-0.7016549965213 +0.0234,-13882.041486192,695.67036170648,0.0010540925533895,0.00010258541486361,4.0027460885516,26758831.569262,0.21976372285739,8289527.2072773,-0.56657207431134 +0.0235,-11572.722456,1243.0502906419,0.0010540925533895,0.00011775122456119,4.0980496195837,26246030.100543,0.32298858894991,8275842.6355979,-0.7061661227983 +0.0236,-13970.832900589,703.89645048143,0.0010540925533895,0.0001034733290076,4.0680354777002,25738758.455505,0.22146966314804,8258889.3576212,-0.5710116450312 +0.0237,-11660.068321763,1251.2016963202,0.0010540925533895,0.00011862468321883,4.1633809428664,25237098.75073,0.32516179850681,8238687.4044951,-0.71053341608643 +0.0238,-14056.671071591,711.95056196708,0.0010540925533895,0.00010433171071765,4.1323666158381,24741128.889175,0.22299116979715,8215259.3367147,-0.57530355358129 +0.0239,-11744.38651094,1259.1728134597,0.0010540925533895,0.00011946786511061,4.2276609517615,24250922.571727,0.32711940151082,8188630.2159559,-0.71474932554525 +0.024,-14139.410375914,719.81670679325,0.0010540925533895,0.00010515910376086,4.195577795962,23766549.311827,0.22433186226039,8158827.575023,-0.57944051879747 +0.0241,-11825.536776193,1266.9480405532,0.0010540925533895,0.00012027936776316,4.290728852652,23288074.453117,0.32886477614093,8125881.3859414,-0.71880683880792 +0.0242,-14218.916015218,727.47971144473,0.0010540925533895,0.00010595416015394,4.2575111885054,22815559.190102,0.22549599728336,8089824.026229,-0.58341580076266 +0.0243,-11903.389745729,1274.5126028236,0.0010540925533895,0.00012105789745855,4.3524281060445,22349060.591751,0.3304019939916,8050690.2433799,-0.7226994872847 +0.0244,-14295.064107555,734.92523747087,0.0010540925533895,0.00010671564107733,4.3180134871177,21888631.628038,0.22648839167823,8008517.1175962,-0.58722320537952 +0.0245,-11977.82700025,1281.8525702173,0.0010540925533895,0.00012180227000377,4.4126070643766,21434321.199343,0.33173574214795,7963344.0228047,-0.72642135001074 +0.0246,-14367.741749882,742.13979823757,0.0010540925533895,0.00010744241750061,4.3769365350909,20986174.168681,0.22731434571752,7915212.5859969,-0.5908570874959 +0.0247,-12048.741121237,1288.9548729275,0.0010540925533895,0.00012251141121364,4.4711195878934,20544231.396721,0.33287124600648,7864166.6449303,-0.72996705606004 +0.0248,-14436.847052187,749.11077319653,0.0010540925533895,0.0001081334705237,4.4341379276539,20108529.77953,0.22797956781294,7810252.2042327,-0.59431235261118 +0.0249,-12116.035711075,1295.8073144456,0.0010540925533895,0.00012318435711206,4.5278256339624,19679102.289005,0.33381419349313,7753517.3899506,-0.73333178555194 +0.025,-14502.289143739,755.82641966832,0.0010540925533895,0.00010878789143922,4.4894815856599,19255978.015917,0.22849010109867,7694012.402583,-0.59758445718876 +0.0251,-12179.625385645,1302.3985821132,0.0010540925533895,0.00012382025385776,4.5825918155139,18839182.215541,0.33457066128292,7631789.4686466,-0.73651126928043 +0.0252,-14563.988152126,762.27588220796,0.0010540925533895,0.00010940488152316,4.542838296519,18428736.355806,0.22885225249102,7566902.7908144,-0.60066940760814 +0.0253,-12239.435739996,1308.718255336,0.0010540925533895,0.00012441835740131,4.6352919246384,18024658.167888,0.33514704357281,7499408.4966753,-0.73950178699796 +0.0254,-14621.875155734,768.44919956351,0.0010540925533895,0.00010998375155925,4.5940862185973,17626961.699199,0.22907252473894,7429364.5861602,-0.60356375778854 +0.0255,-12295.403287818,1314.7568113247,0.0010540925533895,0.00012497803287956,4.6858074177531,17235657.368737,0.33554998390416,7356830.877681,-0.74230016438906 +0.0256,-14675.892110357,774.33730929294,0.0010540925533895,0.0001105239211055,4.6431113456792,16850752.024674,0.22915755192681,7281868.9530332,-0.60626460551969 +0.0257,-12347.475375419,1320.5056285599,0.0010540925533895,0.0001254987537556,4.7340278591405,16472249.004179,0.33578631047596,7204542.1011087,-0.74490376876911 +0.0258,-14725.99175074,779.93205002886,0.0010540925533895,0.00011102491750934,4.6898079285054,16100148.195368,0.22911403883202,7124915.2604701,-0.60876958753886 +0.0259,-12395.610071032,1325.9569879234,0.0010540925533895,0.00012598010071172,4.7798513200751,15734446.101335,0.33586297533076,7043054.9608353,-0.74731050354975 +0.026,-14772.137467839,785.22616148247,0.0010540925533895,0.00011148637468037,4.7340788508103,15375135.906185,0.22894870448098,6959029.2635255,-0.61107687339385 +0.0261,-12439.77603026,1331.1040715916,0.0010540925533895,0.000126421760304,4.8231847311893,15022207.543009,0.33578699773664,6872907.7009266,-0.74951880151117 +0.0262,-14814.303162689,790.21328225244,0.0010540925533895,0.00011190803162885,4.7758359577208,14675647.763715,0.22866823018792,6784761.2150176,-0.61318515813635 +0.0263,-12479.952338536,1335.9409597276,0.0010540925533895,0.00012682352338678,4.8639441861495,14335440.210664,0.33556541202862,6694662.0950177,-0.75152761692494 +0.0264,-14852.473077678,794.88794543995,0.0010540925533895,0.00011228973077878,4.8150003348081,14001565.490015,0.22827921230149,6602683.9142069,-0.61509365388581 +0.0265,-12516.128331502,1340.4626250419,0.0010540925533895,0.00012718528331648,4.9020551951697,13674001.246718,0.33520522011554,6508901.4659721,-0.75333641657323 +0.0266,-14886.641606288,799.24557216815,0.0010540925533895,0.00011263141606488,4.8515025365296,13352722.241084,0.22778811982608,6413390.6991342,-0.61680208031631 +0.0267,-12548.303394268,1344.6649252878,0.0010540925533895,0.0001275070339441,4.937452887311,13037700.426848,0.33471334879898,6316228.652611,-0.75494516971151 +0.0268,-14916.813082108,803.28246307502,0.0010540925533895,0.00011293313082308,4.8852827632293,12728905.030641,0.2272012570257,6217493.3894688,-0.61831065410734 +0.0269,-12576.486740445,1348.5445937384,0.0010540925533895,0.00012778886740588,4.9700821609613,12426302.632815,0.33409661199416,6117263.9304207,-0.75635433702034 +0.027,-14943.00154822,806.99578783169,0.0010540925533895,0.00011319501548419,4.916290986299,12129857.249528,0.22652473106526,6015620.1868244,-0.61962007741292 +0.0271,-12600.69717207,1352.0992277294,0.0010540925533895,0.00012803097172213,4.9998977823133,11839530.416005,0.33336167788994,5912642.8932368,-0.75756485860158 +0.0272,-14965.230507893,810.3835727564,0.0010540925533895,0.00011341730508093,4.9444870215205,11555281.27092,0.22576442468786,5808413.5395793,-0.62073152539661 +0.0273,-12620.962821323,1355.3272753347,0.0010540925533895,0.00012823362821467,5.0268644320676,11277066.641789,0.3325150410311,5703014.3029722,-0.75857814106422 +0.0274,-14983.532657668,813.44468661443,0.0010540925533895,0.00011360032657871,4.9698405510145,11004841.131318,0.22492597387749,5596527.979291,-0.62164663288533 +0.0275,-12637.320875165,1358.2280202789,0.0010540925533895,0.00012839720875308,5.0509567009904,10738557.204617,0.33156299925709,5489037.9145027,-0.7593960437563 +0.0276,-14997.949603825,816.17882463208,0.0010540925533895,0.00011374449604025,4.992331094615,10478165.277188,0.22401475040803,5380627.9358365,-0.62236748019321 +0.0277,-12649.817283898,1360.8015650859,0.0010540925533895,0.0001285221728404,5.072159035338,10223613.80364,0.33051163538535,5271382.2828451,-0.76002086419294 +0.0278,-15008.531563366,818.58649092878,0.0010540925533895,0.00011385031563567,5.0119479318542,9974849.3670053,0.22303584913499,5161385.5384123,-0.62289657817025 +0.0279,-12658.506454745,1363.0488126516,0.0010540925533895,0.00012860906454888,5.0904656335103,9731816.7686116,0.32936680348403,5050722.5597621,-0.76045532273531 +0.028,-15015.337050541,820.66897925119,0.0010540925533895,0.00011391837050744,5.0286899760905,9494459.1184185,0.22199407984518,4939478.4095236,-0.62323685252901 +0.0281,-12663.450931555,1364.9714462532,0.0010540925533895,0.000128658509317,5.1058802956431,9262717.925729,0.32813411954107,4827738.286908,-0.7607025465758 +0.0282,-15018.432550024,822.42835228966,0.0010540925533895,0.00011394932550223,5.0425656026411,9036533.1902025,0.2208939634424,4715587.4590519,-0.6233916275032 +0.0283,-12664.721061669,1366.5719080464,0.0010540925533895,0.0001286712106181,5.1184162281477,8815843.4930999,0.3268189562986,4603111.1925792,-0.76076605308149 +0.0284,-15017.892177843,823.86741953148,0.0010540925533895,0.00011394392178046,5.0535924330635,8600586.0886571,0.21973973221512,4490394.685439,-0.62336460889416 +0.0285,-12662.394651116,1367.8533762877,0.0010540925533895,0.0001286479465126,5.1280958054955,8390696.9955294,0.32542644199413,4377522.9990691,-0.7606497325538 +0.0286,-15013.797331178,824.98971380208,0.0010540925533895,0.00011390297331382,5.0617970780113,8186111.0882119,0.21853533390094,4264580.9909406,-0.6231598665609 +0.0287,-12656.556609195,1368.8197411541,0.0010540925533895,0.00012858956609343,5.1349502917914,7986762.1883693,0.32396146271964,4151653.2475354,-0.76035783045776 +0.0288,-15006.236328078,825.79946654863,0.0010540925533895,0.00011382736328285,5.0672148413166,7792583.1559831,0.21728443924045,4038824.0178073,-0.62278181640595 +0.0289,-12647.298583575,1369.4755794464,0.0010540925533895,0.00012849698583722,5.1390195249092,7603505.9802575,0.32242866808959,3926177.1471792,-0.75989492917675 +0.029,-14995.30403827,826.30158197373,0.0010540925533895,0.00011371804038478,5.0698893881673,7419461.8701892,0.21599045269108,3813796.0121266,-0.62223520191554 +0.0291,-12634.718586969,1369.8261281274,0.0010540925533895,0.00012837118587116,5.1403515661465,7240381.3447315,0.32083247988819,3701763.4553977,-0.75926592934641 +0.0292,-14981.101506109,826.50161010176,0.0010540925533895,0.00011357601506319,5.0698723804079,7066194.3224816,0.21465652595613,3590161.7219182,-0.62152507530751 +0.0293,-12618.920616565,1369.8772568692,0.0010540925533895,0.00012821320616715,5.1390023185189,6896830.2108077,0.31917710335545,3479072.3954306,-0.75847603082622 +0.0294,-14963.735566793,826.40571887551,0.0010540925533895,0.00011340235567004,5.0672230821577,6732217.9943495,0.2132855739727,3368576.3359151,-0.62065677834173 +0.0295,-12600.014267214,1369.6354396465,0.0010540925533895,0.00012802414267365,5.1350351169434,6572286.3228138,0.31746654075766,3258753.6178385,-0.75753071335865 +0.0296,-14943.318456905,826.02066535372,0.0010540925533895,0.00011319818457119,5.062007939028,6416963.5980023,0.21188029299378,3149683.4692783,-0.6196359228473 +0.0297,-12578.114339503,1369.1077255018,0.0010540925533895,0.00012780514339654,5.1285202936495,6266178.0599827,0.31570460688583,3041444.2119661,-0.75643571697308 +0.0298,-14919.967420377,825.35376611392,0.0010540925533895,0.00011296467420592,5.0543001343175,6119857.8723621,0.21044318039755,2934113.2022951,-0.61846837102095 +0.0299,-12553.340443783,1368.3017085499,0.0010540925533895,0.00012755740443936,5.1195347222256,5977931.2065603,0.31389494612204,2827766.7733348,-0.75519702218709 +0.03,-14893.804310928,824.41286696776,0.0010540925533895,0.00011270304311144,5.0441791256032,5840326.3250406,0.2089765558565,2722480.1778955,-0.61716021554847 +0.0301,-12525.816601184,1367.2254973231,0.0010540925533895,0.00012728216601339,5.1081613437433,5706971.6634271,0.31204105071555,2618327.532683,-0.75382083005715 +0.0302,-14864.95519201,823.20631202942,0.0010540925533895,0.0001124145519223,5.0317301651704,5577795.9114325,0.20748258350325,2515381.7635849,-0.6157177596026 +0.0303,-12495.670842674,1365.8876835476,0.0010540925533895,0.00012698070842832,5.0944886784008,5452728.0925534,0.31014627991859,2413714.5521276,-0.75231354213161 +0.0304,-14833.549935337,821.74291227712,0.0010540925533895,0.00011210049935556,5.0170438077074,5331697.6424415,0.20596329473915,2313396.2831407,-0.61414749676895 +0.0305,-12463.034807175,1364.2973104173,0.0010540925533895,0.00012665434807335,5.0786103261063,5214634.4859228,0.3082138796378,2214495.9936681,-0.75068174035665 +0.0306,-14799.721818932,820.03191368025,0.0010540925533895,0.00011176221819155,5.0002154086571,5101469.1125864,0.20442061134041,2117081.3231588,-0.6124560909487 +0.0307,-12428.043339731,1362.4638405062,0.0010540925533895,0.00012630443339895,5.0606244593662,4992132.6508831,0.30624700227241,2021218.4649755,-0.74893216698446 +0.0308,-14763.607125755,818.08296494575,0.0010540925533895,0.00011140107125985,4.9813446165537,4886556.94069,0.20285636853301,1926972.1192513,-0.61065035628989 +0.0309,-12390.834090711,1360.3971233394,0.0010540925533895,0.00012593234090876,5.0406333117736,4784674.6042849,0.30424872642363,1834405.4471284,-0.74707170453341 +0.031,-14725.344743824,815.90608501915,0.0010540925533895,0.00011101844744054,4.9605348625762,4686419.1156594,0.2012723377233,1743580.0264102,-0.60873723719335 +0.0311,-12351.547116991,1358.1073627445,0.0010540925533895,0.00012553947117157,5.0187426652796,4591724.8681508,0.30222207617898,1654555.8086552,-0.74510735584743 +0.0312,-14685.075768791,813.51163041834,0.0010540925533895,0.00011061575769024,4.9378928504491,4500527.2403146,0.19967024859084,1567391.0777427,-0.6067237884417 +0.0313,-12310.324486054,1355.6050840837,0.0010540925533895,0.00012512724486222,4.9950613393182,4412762.6600044,0.30017003969479,1482142.4099362,-0.74304622430054 +0.0314,-14642.943109887,810.91026242573,0.0010540925533895,0.0001101944311012,4.9135280496772,4328368.6666168,0.19805181027165,1398864.6354715,-0.60461715549651 +0.0315,-12267.3098839,1352.9011014007,0.0010540925533895,0.0001246970988407,4.9697006847061,4247283.9714443,0.29809558682191,1317610.8016935,-0.74089549419286 +0.0316,-14599.09110011,808.11291431077,0.0010540925533895,0.00010975591100345,4.8875521949578,4169448.5161009,0.19641873138344,1238432.1377655,-0.60242455500769 +0.0317,-12222.648227626,1350.0064846167,0.0010540925533895,0.00012425048227797,4.9427740850819,4094803.5289901,0.29600168554467,1161378.0209717,-0.73866241137916 +0.0318,-14553.665111516,805.13075858084,0.0010540925533895,0.00010930165111754,4.8600787944412,4023291.5797539,0.19477273866947,1086495.9446353,-0.60015325557798 +0.0319,-12176.48528353,1346.9325268111,0.0010540925533895,0.00012378885283702,4.9143964684718,3954856.6316866,0.29389131702764,1013831.4876702,-0.73635426417431 +0.032,-14506.81117644,801.9751743883,0.0010540925533895,0.00010883311176677,4.8312226493276,3889444.0920727,0.19311559406374,943428.28578352,-0.59781055882418 +0.0321,-12128.967291538,1343.6907116855,0.0010540925533895,0.00012331367291713,4.8846838313782,3827000.8604107,0.29176748909111,875328.00434719,-0.73397836457474 +0.0322,-14458.675615416,798.65771515373,0.0010540925533895,0.00010835175615657,4.8010993870973,3767475.3745005,0.19144911000679,809570.3129521,-0.59540378077303 +0.0323,-12080.240596744,1340.2926812818,0.0010540925533895,0.0001228264059692,4.8537527775954,3710817.6543575,0.28963324796344,746192.8616589,-0.73154202983499 +0.0324,-14409.404672616,795.19007647793,0.0010540925533895,0.0001078590467286,4.7698250104641,3656979.3439357,0.18977516287075,685231.25895767,-0.59294023363302 +0.0325,-12030.451288773,1336.7502040452,0.0010540925533895,0.00012232851288949,4.8217200737352,3605913.7506276,0.28749168818605,626719.05144736,-0.72905256443642 +0.0326,-14359.144159424,791.58406441244,0.0010540925533895,0.00010735644159666,4.7375154639304,3557575.8825208,0.18809570437729,570687.70524407,-0.59042720797341 +0.0327,-11979.744849689,1333.075143236,0.0010540925533895,0.00012182144849864,4.7887022232439,3511922.4833933,0.2853459605737,517166.58912647,-0.72651724248222 +0.0328,-14308.039106933,787.85156416526,0.0010540925533895,0.00010684539107177,4.7042862196057,3468912.0654221,0.18641277092327,466182.95942451,-0.5878719553489 +0.0329,-11928.26581113,1329.2794258671,0.0010540925533895,0.00012130665811308,4.7548150604647,3428504.9395988,0.28319927816126,417761.94665686,-0.72394329055429 +0.033,-14256.233427978,784.0045092896,0.0010540925533895,0.00010632733428225,4.6702518837314,3390663.2438275,0.18472849075403,371926.54392056,-0.58528167140113 +0.0331,-11876.1574213,1325.375012121,0.0010540925533895,0.00012078557421482,4.7201733660793,3355350.9686977,0.28105492009462,328697.59703535,-0.72133787106277 +0.0332,-14203.869589305,780.0548514677,0.0010540925533895,0.00010580369589558,4.6355258251308,3322533.9809229,0.18304508895297,288093.79644325,-0.58266347946753 +0.0333,-11823.561322365,1321.3738653892,0.0010540925533895,0.00012025961322548,4.6848905050454,3292180.0444318,0.27891623344922,250131.67086319,-0.71870806611598 +0.0334,-14151.088294464,776.01453085234,0.0010540925533895,0.00010527588294711,4.6002198265844,3264258.8391144,0.18136489024106,214825.58269846,-0.58002441472549 +0.0335,-11770.617238881,1317.287922927,0.0010540925533895,0.00011973017239061,4.6490780879224,3238741.9772048,0.27678663298647,182187.7251939,-0.7160608619418 +0.0336,-14098.028177958,771.89544716397,0.0010540925533895,0.00010474528178205,4.5644437599172,3215603.0173148,0.17969031960619,152228.12133789,-0.57737140890016 +0.0337,-11717.462677727,1313.1290672763,0.0010540925533895,0.00011919862677905,4.6128456562697,3194817.476106,0.2746695988809,124954.62450312,-0.71340313388406 +0.0338,-14044.825511172,767.70943145434,0.0010540925533895,0.00010421325511419,4.528305285364,3176362.8376081,0.17802390080558,100372.92081878,-0.5747112755609 +0.0339,-11664.232640032,1308.9090983822,0.0010540925533895,0.0001186663264021,4.5763003925807,3160218.5601851,0.27256867247547,78486.533265203,-0.71074163199931 +0.034,-13991.613920509,763.46821870266,0.0010540925533895,0.00010368113920756,4.4919095755799,3146366.0811589,0.17636825280731,59296.827480925,-0.57205069602774 +0.0341,-11611.059345543,1304.6397065604,0.0010540925533895,0.00011813459345721,4.5395468550278,3134788.8190892,0.27048745014273,42803.019270597,-0.70808296727488 +0.0342,-13938.524118154,759.18342121935,0.0010540925533895,0.00010315024118402,4.4553590644654,3125472.1737337,0.17472608425799,29002.183800987,-0.56939620591002 +0.0343,-11558.07196982,1300.3324463249,0.0010540925533895,0.00011760471969999,4.5026867370874,3118403.5236867,0.26842957534958,17889.26647098,-0.7054335984887 +0.0344,-13885.683645871,754.86650294092,0.0010540925533895,0.0001026218364612,4.4187532207777,3113572.2217199,0.17310018608205,9457.0954402078,-0.56675418229588 +0.0345,-11505.396394631,1295.9987110948,0.0010540925533895,0.0001170779639481,4.4658186519371,3110969.5878306,0.26639872904151,3696.3957996355,-0.70279981972925 +0.0346,-13833.216632163,750.52875465471,0.0010540925533895,0.00010209716632412,4.382188346335,3110588.9000323,0.1714934223372,595.80536623842,-0.56413083161051 +0.0347,-11453.15497187,1291.6497088767,0.0010540925533895,0.00011655554972047,4.4290379413438,3112425.3828862,0.2643986184781,141.89208267701,-0.70018774859116 +0.0348,-13781.243563074,746.18127017453,0.0010540925533895,0.00010157743563323,4.3457573984424,3116476.1938088,0.16990871946347,2319.1730016175,-0.56153217815605 +0.0349,-11401.466301248,1287.2964389118,0.0010540925533895,0.00011603866301429,4.3924365085961,3122740.4071775,0.26243296466555,7110.1348332076,-0.69760331506005 +0.035,-13729.88106693,741.83492353462,0.0010540925533895,0.00010106381067178,4.3095498360227,3131218.9962538,0.16834905407926,14495.256033049,-0.55896405334884 +0.0351,-11350.445022069,1282.9496693442,0.0010540925533895,0.00011552845022249,4.3561026748955,3141914.8129544,0.26050548854382,24453.030406841,-0.69505225110109 +0.0352,-13679.241713211,737.50034719516,0.0010540925533895,0.00010055741713464,4.2736514887871,3154832.5654997,0.16681743948632,36959.992206791,-0.55643208566294 +0.0353,-11300.201619192,1278.6199159583,0.0010540925533895,0.00011502601619374,4.3201210584781,3169978.7939674,0.25861989609622,51990.742693763,-0.69254008095723 +0.0354,-13629.43382577,733.18791133478,0.0010540925533895,0.00010005933826025,4.2381444486611,3187361.8437855,0.16531691105754,69517.978138082,-0.55394169129087 +0.0355,-11250.84224344,1274.3174219678,0.0010540925533895,0.00011453242243626,4.2845724756235,3206991.8371956,0.25677986255671,89512.51923086,-0.69007211216962 +0.0356,-13580.561310491,728.90770422778,0.0010540925533895,9.9570613107479e-05,4.2031069825634,3228880.6427249,0.16385051068557,111943.34187672,-0.55149806552694 +0.0357,-11202.468546525,1270.0521389249,0.0010540925533895,0.00011404868546709,4.2495338626013,3253041.8427003,0.25498901589617,136777.60933779,-0.68765342732386 +0.0358,-13532.723497553,724.66951372035,0.0010540925533895,9.9092234978095e-05,4.1686134655402,3279490.6988482,0.16242127047789,163980.70569784,-0.54910617488006 +0.0359,-11155.177530581,1265.8337087724,0.0010540925533895,0.00011357577530768,4.215078217515,3308244.1160101,0.25325091977156,193516.27061459,-0.68528887652664 +0.036,-13486.014998325,720.48280988282,0.0010540925533895,9.8625149985809e-05,4.1347343331727,3339320.6040273,0.16103219588442,225346.23532725,-0.54677074991869 +0.0361,-11109.061412392,1261.6714470107,0.0010540925533895,0.00011311461412574,4.1812745609241,3372740.2378305,0.25156905612559,259430.85988557,-0.68298307061722 +0.0362,-13440.525576937,716.35672876448,0.0010540925533895,9.8170255771933e-05,4.1015360521094,3408524.6157776,0.15968624844643,295728.77156554,-0.54449627884929 +0.0363,-11064.207502258,1257.5743270602,0.0010540925533895,0.00011266607502441,4.1481879140712,3446696.8162875,0.24994680762069,334197.00443662,-0.6807403751105 +0.0364,-13396.340036537,712.30005736228,0.0010540925533895,9.7728400367925e-05,4.0690811075153,3487281.3528214,0.15838632835334,374791.04004408,-0.54228700182931 +0.0365,-11020.698097573,1253.5509657704,0.0010540925533895,0.00011223098097757,4.1158792934826,3530304.1272445,0.24838744009263,417464.8491694,-0.67856490487623 +0.0366,-13353.538120163,708.32121974879,0.0010540925533895,9.7300381204228e-05,4.0374280061995,3575792.3816393,0.15713525699113,462170.93463132,-0.54014690601058 +0.0367,-10978.610390957,1249.609610149,0.0010540925533895,0.00011181010391146,4.0844057206885,3623774.6485982,0.24689408520197,508860.37508898,-0.67646051954542 +0.0368,-13312.194426185,704.4282643867,0.0010540925533895,9.6886944264463e-05,4.0066312941458,3674280.7000652,0.15593575966218,557482.86980811,-0.53807972131172 +0.0369,-10938.016392924,1245.7581252402,0.0010540925533895,0.00011140416393111,4.0538202457828,3727341.4947691,0.24546972345739,607986.78435091,-0.67443081964376 +0.037,-13272.378338185,700.62885264274,0.0010540925533895,9.6488783384442e-05,3.9767415871656,3782989.1243056,0.15479044864866,660319.19714898,-0.53608891691174 +0.0371,-10898.982868911,1242.0039832436,0.0010540925533895,0.00011101382869096,4.0241719835385,3841256.7579196,0.24411716777716,714425.94691898,-0.67247914344308 +0.0372,-13234.15396914,696.93024850336,0.0010540925533895,9.6106539694002e-05,3.9478056133914,3902178.586048,0.15370180678514,770251.68087948,-0.53417769845949 +0.0373,-10861.571290544,1238.3542538054,0.0010540925533895,0.0001106397129073,3.9955061607988,3965789.7626745,0.24283904774616,827739.90372745,-0.67060856452474 +0.0374,-13197.580119753,693.33930946221,0.0010540925533895,9.5740801200122e-05,3.9198662663362,4032126.3465593,0.15267217169608,886833.02733215,-0.53234900599013 +0.0375,-10825.837800959,1234.8155955049,0.0010540925533895,0.00011028237801144,3.9678641738841,4101225.2413951,0.24163779471648,947472.42110372,-0.66882189004545 +0.0376,-13162.710250696,689.86247863557,0.0010540925533895,9.5392102509565e-05,3.8929626672719,4173124.1349537,0.15170372084338,1009598.4629936,-0.5306055125373 +0.0377,-10791.833193887,1231.3942485364,0.0010540925533895,0.00010994233194074,3.9412836547765,4247861.4372798,0.24051562788682,1073150.5910839,-0.66712165969184 +0.0378,-13129.592468558,686.50577802788,0.0010540925533895,9.5060924688211e-05,3.8671302357129,4325476.2179925,0.15079845751819,1138067.3557209,-0.5289496234304 +0.0379,-10759.602906367,1228.0960285841,0.0010540925533895,0.00010962002906556,3.9157985448929,4406008.1427539,0.23947454148658,1204286.4721511,-0.66551014531586 +0.038,-13098.269525213,683.27480299126,0.0010540925533895,9.4747695254756e-05,3.8424007668267,4489497.4089721,0.1499581978986,1271744.8736125,-0.52738347626316 +0.0381,-10729.187024703,1224.9263218393,0.0010540925533895,0.00010931587024893,3.8914391752883,4575984.680788,0.238516293174,1340378.7648408,-0.66398935123265 +0.0382,-13068.778830333,680.17471785291,0.0010540925533895,9.4452788305986e-05,3.8188025146533,4665511.0234211,0.14918455928094,1410123.6759421,-0.52590894151916 +0.0383,-10700.620303434,1221.8900812139,0.0010540925533895,0.00010903020303624,3.8682323521991,4758117.8369281,0.23764239374769,1480914.5165909,-0.66256101516919 +0.0384,-13041.152476734,677.21025267605,0.0010540925533895,9.4176524770022e-05,3.7963602800671,4853846.7894424,0.1484789495801,1552685.6305069,-0.52452762383923 +0.0385,-10673.932196953,1218.9918236671,0.0010540925533895,0.00010876332197145,3.8462014468919,4952739.7499513,0.23685409825346,1625370.8501659,-0.66122660984509 +0.0386,-13015.417278197,674.38570117062,0.0010540925533895,9.3919172784653e-05,3.7750955024813,5054838.7206803,0.14784255817824,1698903.5517019,-0.5232408639124 +0.0387,-10649.146903447,1216.23562866,0.0010540925533895,0.00010851546903639,3.8253664888472,5160185.7691415,0.23615239855669,1773216.7099531,-0.6599873451698 +0.0388,-12991.594819421,671.70491971385,0.0010540925533895,9.368094819691e-05,3.7550263543628,5268822.9599172,0.1472763481887,1848242.9536101,-0.52204974097361 +0.0389,-10626.283420756,1213.625137748,0.0010540925533895,0.00010828683420948,3.8057442613862,5380792.2862383,0.23553801743326,1923914.6204194,-0.65884417103524 +0.039,-12969.701517698,669.17132744786,0.0010540925533895,9.3462015179695e-05,3.736167837705,5496135.6014133,0.14678105018502,2000163.8123995,-0.52095507588747 +0.0391,-10605.355613775,1211.1635552101,0.0010540925533895,0.00010807755613972,3.7873483989196,5614894.5501845,0.23501140421942,2076922.4510238,-0.65779778068619 +0.0392,-12949.748695932,666.78790747705,0.0010540925533895,9.3262486962069e-05,3.7185318816752,5737110.5000719,0.1463571574331,2154122.3323291,-0.51995743479919 +0.0393,-10586.372292986,1208.8536497631,0.0010540925533895,0.00010788772293183,3.7701894850759,5862824.4727532,0.23457273204502,2231695.1819024,-0.65684861464672 +0.0394,-12931.742666555,664.55720907219,0.0010540925533895,9.3082426668308e-05,3.7021274407397,5992077.0755672,0.14600492264702,2309572.7097051,-0.51905713333035 +0.0395,-10569.337303652,1206.6977572849,0.0010540925533895,0.00010771737303848,3.7542751510494,6124908.4331807,0.23422189665994,2387686.6646911,-0.65599686518 +0.0396,-12915.684825918,662.48135091801,0.0010540925533895,9.2921848261947e-05,3.6869605926453,6261358.1194921,0.14572435627723,2465968.8891745,-0.5182542412985 +0.0397,-10554.249625285,1204.6977845881,0.0010540925533895,0.00010756649625482,3.7396101735871,6401465.0898404,0.23395851685094,2544351.3729064,-0.65524248126167 +0.0398,-12901.57175866,660.56202533344,0.0010540925533895,9.2780717589343e-05,3.6730346357222,6545267.6135576,0.14551522632458,2622766.3068176,-0.51754858793563 +0.0399,-10541.103480848,1202.8552140872,0.0010540925533895,0.00010743503481046,3.7261965721163,6692803.206958,0.23378193643059,2701146.1363856,-0.6545851740398 +0.04,-12889.395351654,658.8005034539,0.0010540925533895,9.2658953519004e-05,3.6603501850511,6844108.5667918,0.14537705966158,2779423.6145911,-0.51693976758449 diff --git a/modules/contact/test/tests/explicit_dynamics/gold/settlement_out.e b/modules/contact/test/tests/explicit_dynamics/gold/settlement_out.e deleted file mode 100644 index eeb982b496aec40aad0ab45545b3fa638eaaf303..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160388 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 B-u3_h diff --git a/modules/contact/test/tests/explicit_dynamics/gold/test_balance_out.e b/modules/contact/test/tests/explicit_dynamics/gold/test_balance_out.e index 1fc05bd786a792656931cfbcdb2f23f9e7f02f3b..da141fca639894e6595b61142c6b2c3f93e30bfb 100644 GIT binary patch delta 33229 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 Sgb!=sqSti$H29uOANUR0gJu5! literal 216924 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 Date: Tue, 23 Jan 2024 17:56:17 -0700 Subject: [PATCH 20/31] Remove dead code, add coverage, cleanup --- .../ExplicitDynamicsContactConstraint.h | 9 --------- .../include/userobjects/NodalDensity.h | 2 -- .../include/userobjects/NodalWaveSpeed.h | 2 -- .../ExplicitDynamicsContactConstraint.C | 19 ------------------- .../contact/src/userobjects/NodalDensity.C | 12 ------------ .../contact/src/userobjects/NodalWaveSpeed.C | 12 ------------ .../tests/explicit_dynamics/test_balance.i | 9 +-------- .../lumped/3D/3d_lumped_explicit.i | 3 +++ 8 files changed, 4 insertions(+), 64 deletions(-) diff --git a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h index d66ee71b8a29..7eb114cc3b19 100644 --- a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h +++ b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h @@ -61,15 +61,6 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, return 0.0; } - /** - * Determine whether the coupled variable is one of the displacement variables, - * and find its component - * @param var_num The number of the variable to be checked - * @param component The component index computed in this routine - * @return bool indicating whether the coupled variable is one of the displacement variables - */ - bool getCoupledVarComponent(unsigned int var_num, unsigned int & component); - bool shouldApply() override; void computeContactForce(const Node & node, PenetrationInfo * pinfo, bool update_contact_set); virtual bool isExplicitConstraint() const override { return true; } diff --git a/modules/contact/include/userobjects/NodalDensity.h b/modules/contact/include/userobjects/NodalDensity.h index 81d884c67381..6c4c6d5d2c87 100644 --- a/modules/contact/include/userobjects/NodalDensity.h +++ b/modules/contact/include/userobjects/NodalDensity.h @@ -25,8 +25,6 @@ class NodalDensity : public SideIntegralVariableUserObject virtual void execute(); virtual void finalize(); - Real nodalDensity(const Node * node) const; - protected: virtual Real computeQpIntegral(); diff --git a/modules/contact/include/userobjects/NodalWaveSpeed.h b/modules/contact/include/userobjects/NodalWaveSpeed.h index ea22aec5abf3..1efadd6d4be8 100644 --- a/modules/contact/include/userobjects/NodalWaveSpeed.h +++ b/modules/contact/include/userobjects/NodalWaveSpeed.h @@ -25,8 +25,6 @@ class NodalWaveSpeed : public SideIntegralVariableUserObject virtual void execute(); virtual void finalize(); - Real nodalWaveSpeed(const Node * node) const; - protected: virtual Real computeQpIntegral(); diff --git a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C index 20521c480760..61b73b953a32 100644 --- a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C +++ b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C @@ -452,25 +452,6 @@ ExplicitDynamicsContactConstraint::getPenalty(const Node & /*node*/) return _penalty; } -bool -ExplicitDynamicsContactConstraint::getCoupledVarComponent(unsigned int var_num, - unsigned int & component) -{ - component = std::numeric_limits::max(); - bool coupled_var_is_disp_var = false; - for (const auto i : make_range(Moose::dim)) - { - if (var_num == _vars[i]) - { - coupled_var_is_disp_var = true; - component = i; - break; - } - } - - return coupled_var_is_disp_var; -} - void ExplicitDynamicsContactConstraint::overwriteBoundaryVariables(NumericVector & soln, const Node & secondary_node) const diff --git a/modules/contact/src/userobjects/NodalDensity.C b/modules/contact/src/userobjects/NodalDensity.C index 0b1eb9324bb1..7564af9ced71 100644 --- a/modules/contact/src/userobjects/NodalDensity.C +++ b/modules/contact/src/userobjects/NodalDensity.C @@ -92,15 +92,3 @@ NodalDensity::finalize() } _aux_solution.close(); } - -Real -NodalDensity::nodalDensity(const Node * node) const -{ - std::map::const_iterator it = _node_densities.find(node); - Real retVal(0); - if (it != _node_densities.end()) - { - retVal = it->second; - } - return retVal; -} diff --git a/modules/contact/src/userobjects/NodalWaveSpeed.C b/modules/contact/src/userobjects/NodalWaveSpeed.C index 37a83f521816..02146483fcba 100644 --- a/modules/contact/src/userobjects/NodalWaveSpeed.C +++ b/modules/contact/src/userobjects/NodalWaveSpeed.C @@ -89,15 +89,3 @@ NodalWaveSpeed::finalize() } _aux_solution.close(); } - -Real -NodalWaveSpeed::nodalWaveSpeed(const Node * node) const -{ - std::map::const_iterator it = _node_wave_speeds.find(node); - Real retVal(0); - if (it != _node_wave_speeds.end()) - { - retVal = it->second; - } - return retVal; -} diff --git a/modules/contact/test/tests/explicit_dynamics/test_balance.i b/modules/contact/test/tests/explicit_dynamics/test_balance.i index bb4f287f53fc..01d46b98d459 100644 --- a/modules/contact/test/tests/explicit_dynamics/test_balance.i +++ b/modules/contact/test/tests/explicit_dynamics/test_balance.i @@ -172,13 +172,6 @@ [] [BCs] - # [z_front] - # type = FunctionDirichletBC - # variable = disp_z - # boundary = 'ball_front' - # function = dispz - # preset = false - # [] [x_front] type = DirichletBC variable = disp_x @@ -252,7 +245,7 @@ output_properties = __all__ [] [strain_block] - type = ComputeFiniteStrain # ComputeIncrementalSmallStrain + type = ComputeFiniteStrain displacements = 'disp_x disp_y disp_z' implicit = false [] diff --git a/modules/solid_mechanics/test/tests/central_difference/lumped/3D/3d_lumped_explicit.i b/modules/solid_mechanics/test/tests/central_difference/lumped/3D/3d_lumped_explicit.i index cd69504fb729..4b6a1bfe20d5 100644 --- a/modules/solid_mechanics/test/tests/central_difference/lumped/3D/3d_lumped_explicit.i +++ b/modules/solid_mechanics/test/tests/central_difference/lumped/3D/3d_lumped_explicit.i @@ -157,6 +157,9 @@ prop_names = density prop_values = 1e4 [../] + [wave_speed] + type = WaveSpeed + [] [] [Executioner] From 9ac457f76e12f22266b6045a94b745382f26229a Mon Sep 17 00:00:00 2001 From: "Antonio M. Recuero" Date: Mon, 29 Jan 2024 09:25:49 -0700 Subject: [PATCH 21/31] Reinit material for contact constraints on undisplaced mesh. This is needed for explicit dynamics contact constraints which use material properties on both secondary node and primary projection point. --- framework/include/base/Assembly.h | 3 ++ framework/include/problems/DisplacedProblem.h | 5 ++ framework/include/problems/SubProblem.h | 3 ++ framework/src/base/Assembly.C | 47 +++++++++-------- framework/src/problems/SubProblem.C | 8 +++ framework/src/systems/NonlinearSystemBase.C | 50 ++++++++++++++++--- modules/contact/doc/content/bib/contact.bib | 2 +- 7 files changed, 87 insertions(+), 31 deletions(-) diff --git a/framework/include/base/Assembly.h b/framework/include/base/Assembly.h index 1f56f7b324b3..88680bea4f29 100644 --- a/framework/include/base/Assembly.h +++ b/framework/include/base/Assembly.h @@ -714,6 +714,9 @@ class Assembly */ void reinitNeighborAtPhysical(const Elem * neighbor, const std::vector & physical_points); + /** + * Reinitializes the neighbor side using reference coordinates. + */ void reinitNeighbor(const Elem * neighbor, const std::vector & reference_points); /** diff --git a/framework/include/problems/DisplacedProblem.h b/framework/include/problems/DisplacedProblem.h index a61d9ce8fdcd..ed495e61d01e 100644 --- a/framework/include/problems/DisplacedProblem.h +++ b/framework/include/problems/DisplacedProblem.h @@ -216,6 +216,11 @@ class DisplacedProblem : public SubProblem virtual void reinitNodesNeighbor(const std::vector & nodes, const THREAD_ID tid) override; virtual void reinitNeighbor(const Elem * elem, unsigned int side, const THREAD_ID tid) override; + virtual void getNeighborPoints(const Elem * /*neighbor*/, + const std::vector * /*neighbor_reference_points*/, + Point & /*neighbor_physical_points*/) override + { + } /** * reinitialize neighbor routine diff --git a/framework/include/problems/SubProblem.h b/framework/include/problems/SubProblem.h index 0c32aa82aa06..1bc5512c22ef 100644 --- a/framework/include/problems/SubProblem.h +++ b/framework/include/problems/SubProblem.h @@ -396,6 +396,9 @@ class SubProblem : public Problem unsigned int neighbor_side, const std::vector & physical_points, const THREAD_ID tid) = 0; + virtual void getNeighborPoints(const Elem * neighbor, + const std::vector * neighbor_reference_points, + Point & neighbor_physical_points); virtual void reinitNeighborPhys(const Elem * neighbor, const std::vector & physical_points, const THREAD_ID tid) = 0; diff --git a/framework/src/base/Assembly.C b/framework/src/base/Assembly.C index a3e3992cf98b..6174466ad68c 100644 --- a/framework/src/base/Assembly.C +++ b/framework/src/base/Assembly.C @@ -1867,8 +1867,8 @@ Assembly::reinitFVFace(const FaceInfo & fi) if (_current_qrule_face != qrules(dim).fv_face.get()) { setFaceQRule(qrules(dim).fv_face.get(), dim); - // The order of the element that is used for initing here doesn't matter since this will just be - // used for constant monomials (which only need a single integration point) + // The order of the element that is used for initing here doesn't matter since this will just + // be used for constant monomials (which only need a single integration point) if (dim == 3) _current_qrule_face->init(QUAD4); else @@ -1881,9 +1881,9 @@ Assembly::reinitFVFace(const FaceInfo & fi) "Our finite volume quadrature rule should always yield a single point"); // We've initialized the reference points. Now we need to compute the physical location of the - // quadrature points. We do not do any FE initialization so we cannot simply copy over FE results - // like we do in reinitFEFace. Instead we handle the computation of the physical locations - // manually + // quadrature points. We do not do any FE initialization so we cannot simply copy over FE + // results like we do in reinitFEFace. Instead we handle the computation of the physical + // locations manually _current_q_points_face.resize(1); const auto & ref_points = _current_qrule_face->get_points(); const auto & ref_point = ref_points[0]; @@ -2344,12 +2344,12 @@ Assembly::reinitLowerDElem(const Elem * elem, if (pts && !weights) { - // We only have dummy weights so the JxWs computed during our FE reinits are meaningless and we - // cannot use them + // We only have dummy weights so the JxWs computed during our FE reinits are meaningless and + // we cannot use them if (_subproblem.getCoordSystem(elem->subdomain_id()) == Moose::CoordinateSystemType::COORD_XYZ) - // We are in a Cartesian coordinate system and we can just use the element volume method which - // has fast computation for certain element types + // We are in a Cartesian coordinate system and we can just use the element volume method + // which has fast computation for certain element types _current_lower_d_elem_volume = elem->volume(); else // We manually compute the volume taking the curvilinear coordinate transformations into @@ -2422,8 +2422,10 @@ Assembly::reinitNeighborAtPhysical(const Elem * neighbor, "If reinitializing with more than one point, then I am dubious of your use case. Perhaps " "you are performing a DG type method and you are reinitializing using points from the " "element face. In such a case your neighbor JxW must have its index order 'match' the " - "element JxW index order, e.g. imagining a vertical 1D face with two quadrature points, if " - "index 0 for elem JxW corresponds to the 'top' quadrature point, then index 0 for neighbor " + "element JxW index order, e.g. imagining a vertical 1D face with two quadrature points, " + "if " + "index 0 for elem JxW corresponds to the 'top' quadrature point, then index 0 for " + "neighbor " "JxW must also correspond to the 'top' quadrature point. And libMesh/MOOSE has no way to " "guarantee that with multiple quadrature points."); @@ -2844,11 +2846,12 @@ Assembly::prepareLowerD() for (MooseIndex(_jacobian_block_lower_used) tag = 0; tag < _jacobian_block_lower_used.size(); tag++) { - // To cover all possible cases we should have 9 combinations below for every 2-permutation of - // Lower,Secondary,Primary. However, 4 cases will in general be covered by calls to prepare() - // and prepareNeighbor(). These calls will cover SecondarySecondary (ElementElement), - // SecondaryPrimary (ElementNeighbor), PrimarySecondary (NeighborElement), and PrimaryPrimary - // (NeighborNeighbor). With these covered we only need to prepare the 5 remaining below + // To cover all possible cases we should have 9 combinations below for every 2-permutation + // of Lower,Secondary,Primary. However, 4 cases will in general be covered by calls to + // prepare() and prepareNeighbor(). These calls will cover SecondarySecondary + // (ElementElement), SecondaryPrimary (ElementNeighbor), PrimarySecondary (NeighborElement), + // and PrimaryPrimary (NeighborNeighbor). With these covered we only need to prepare the 5 + // remaining below // derivatives w.r.t. lower dimensional residuals jacobianBlockMortar(Moose::LowerLower, vi, vj, LocalDataKey{}, tag) @@ -4787,8 +4790,8 @@ Assembly::adCurvatures() const _calculate_curvatures = true; const Order helper_order = _mesh.hasSecondOrderElements() ? SECOND : FIRST; const FEType helper_type(helper_order, LAGRANGE); - // Must prerequest the second derivatives. Sadly because there is only one _need_second_derivative - // map for both volumetric and face FE objects we must request both here + // Must prerequest the second derivatives. Sadly because there is only one + // _need_second_derivative map for both volumetric and face FE objects we must request both here feSecondPhi(helper_type); feSecondPhiFace(helper_type); return _ad_curvatures; @@ -4820,8 +4823,8 @@ Assembly::helpersRequestData() for (unsigned int dim = 0; dim < _mesh_dimension; dim++) { - // We need these computations in order to compute correct lower-d element volumes in curvilinear - // coordinates + // We need these computations in order to compute correct lower-d element volumes in + // curvilinear coordinates _holder_fe_lower_helper[dim]->get_xyz(); _holder_fe_lower_helper[dim]->get_JxW(); } @@ -4868,8 +4871,8 @@ Assembly::havePRefinement(const std::vector & disable_p_refinement_for helper_container[dim] = unique_helper.get(); // If the user did not request the helper type then we should erase it from our FE container - // so that they're not penalized (in the "we should be able to do p-refinement sense") for our - // perhaps silly helpers + // so that they're not penalized (in the "we should be able to do p-refinement sense") for + // our perhaps silly helpers if (!user_added_helper_type) { auto & fe_container_dim = libmesh_map_find(fe_container, dim); diff --git a/framework/src/problems/SubProblem.C b/framework/src/problems/SubProblem.C index a5b9a08a97b0..f5dc674f8405 100644 --- a/framework/src/problems/SubProblem.C +++ b/framework/src/problems/SubProblem.C @@ -867,6 +867,14 @@ SubProblem::reinitElemFaceRef(const Elem * elem, current_assembly.prepareResidual(); } +void +SubProblem::getNeighborPoints(const Elem * neighbor, + const std::vector * neighbor_reference_points, + Point & neighbor_physical_points) +{ + neighbor_physical_points = FEMap::map(neighbor->dim(), neighbor, (*neighbor_reference_points)[0]); +} + void SubProblem::reinitNeighborFaceRef(const Elem * neighbor_elem, unsigned int neighbor_side, diff --git a/framework/src/systems/NonlinearSystemBase.C b/framework/src/systems/NonlinearSystemBase.C index d15a5196454b..2bf25691d6bc 100644 --- a/framework/src/systems/NonlinearSystemBase.C +++ b/framework/src/systems/NonlinearSystemBase.C @@ -96,6 +96,7 @@ #include "libmesh/petsc_matrix.h" #include "libmesh/default_coupling.h" #include "libmesh/diagonal_matrix.h" +#include "libmesh/fe_interface.h" #include @@ -1141,13 +1142,23 @@ NonlinearSystemBase::setConstraintSecondaryValues(NumericVector & soluti std::vector points; points.push_back(info._closest_point); - // reinit variables on the primary element's face at the contact point + // Reinit variables and materials on the primary element's face at the contact point _fe_problem.setNeighborSubdomainID(primary_elem, 0); - subproblem.reinitNeighborPhys(primary_elem, primary_side, points, 0); + + // Reinit material on undisplaced mesh + std::vector reference_points; + FEInterface::inverse_map( + primary_elem->dim(), FEType(), primary_elem, points, reference_points); + Point neighbor_physical_points; + _fe_problem.getNeighborPoints( + _mesh.elemPtr(primary_elem->id()), &reference_points, neighbor_physical_points); _fe_problem.reinitNeighborPhys( - _mesh.elemPtr(primary_elem->id()), primary_side, points, 0); + _mesh.elemPtr(primary_elem->id()), primary_side, {neighbor_physical_points}, 0); _fe_problem.reinitMaterialsNeighbor(primary_elem->subdomain_id(), 0); + // Reinit points for constraint enforcement + subproblem.reinitNeighborPhys(primary_elem, primary_side, points, 0); + for (const auto & nfc : constraints) { if (nfc->isExplicitConstraint()) @@ -1308,13 +1319,23 @@ NonlinearSystemBase::constraintResiduals(NumericVector & residual, bool std::vector points; points.push_back(info._closest_point); - // reinit variables on the primary element's face at the contact point + // Reinit variables and materials on the primary element's face at the contact point _fe_problem.setNeighborSubdomainID(primary_elem, 0); - subproblem.reinitNeighborPhys(primary_elem, primary_side, points, 0); + + // Reinit material on undisplaced mesh + std::vector reference_points; + FEInterface::inverse_map( + primary_elem->dim(), FEType(), primary_elem, points, reference_points); + Point neighbor_physical_points; + _fe_problem.getNeighborPoints( + _mesh.elemPtr(primary_elem->id()), &reference_points, neighbor_physical_points); _fe_problem.reinitNeighborPhys( - _mesh.elemPtr(primary_elem->id()), primary_side, points, 0); + _mesh.elemPtr(primary_elem->id()), primary_side, {neighbor_physical_points}, 0); _fe_problem.reinitMaterialsNeighbor(primary_elem->subdomain_id(), 0); + // Reinit points for constraint enforcement + subproblem.reinitNeighborPhys(primary_elem, primary_side, points, 0); + for (const auto & nfc : constraints) { // Return if this constraint does not correspond to the primary-secondary pair @@ -2279,11 +2300,24 @@ NonlinearSystemBase::constraintJacobians(bool displaced) // reinit variables on the primary element's face at the contact point _fe_problem.setNeighborSubdomainID(primary_elem, 0); - subproblem.reinitNeighborPhys(primary_elem, primary_side, points, 0); + + // Reinit variables and materials on the primary element's face at the contact point + _fe_problem.setNeighborSubdomainID(primary_elem, 0); + + // Reinit material on undisplaced mesh + std::vector reference_points; + FEInterface::inverse_map( + primary_elem->dim(), FEType(), primary_elem, points, reference_points); + Point neighbor_physical_points; + _fe_problem.getNeighborPoints( + _mesh.elemPtr(primary_elem->id()), &reference_points, neighbor_physical_points); _fe_problem.reinitNeighborPhys( - _mesh.elemPtr(primary_elem->id()), primary_side, points, 0); + _mesh.elemPtr(primary_elem->id()), primary_side, {neighbor_physical_points}, 0); _fe_problem.reinitMaterialsNeighbor(primary_elem->subdomain_id(), 0); + // Reinit points for constraint enforcement + subproblem.reinitNeighborPhys(primary_elem, primary_side, points, 0); + for (const auto & nfc : constraints) { if (nfc->isExplicitConstraint()) diff --git a/modules/contact/doc/content/bib/contact.bib b/modules/contact/doc/content/bib/contact.bib index 2d19fac638e9..9a65fd48271f 100644 --- a/modules/contact/doc/content/bib/contact.bib +++ b/modules/contact/doc/content/bib/contact.bib @@ -122,7 +122,7 @@ @article{benzeggagh1996measurement number={4}, pages={439--449}, year={1996}, - +} @article{heinstein2000contact, title={Contact—impact modeling in explicit transient dynamics}, author={Heinstein, Martin W and Mello, Frank J and Attaway, Stephen W and Laursen, Tod A}, From f5afd2b79834b610791f810eff8f3728b523c834 Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Sat, 3 Feb 2024 09:20:24 -0700 Subject: [PATCH 22/31] Clean up and add coverage for 'overwrite' option --- framework/include/constraints/NodeFaceConstraint.h | 2 +- framework/include/problems/DisplacedProblem.h | 2 +- framework/include/problems/SubProblem.h | 8 +++++++- framework/src/constraints/NodeFaceConstraint.C | 2 +- framework/src/interfaces/Coupleable.C | 2 +- framework/src/problems/SubProblem.C | 4 ++-- .../doc/content/source/userobjects/NodalDensity.md | 2 +- .../doc/content/source/userobjects/NodalWaveSpeed.md | 2 +- modules/contact/include/userobjects/NodalDensity.h | 2 -- modules/contact/include/userobjects/NodalWaveSpeed.h | 2 -- .../src/actions/ExplicitDynamicsContactAction.C | 7 ++++++- .../constraints/ExplicitDynamicsContactConstraint.C | 7 +++---- modules/contact/src/userobjects/NodalDensity.C | 6 ------ modules/contact/src/userobjects/NodalWaveSpeed.C | 6 ------ .../gold/test_balance_overwrite_out.csv | 7 +++++++ modules/contact/test/tests/explicit_dynamics/tests | 11 +++++++++++ 16 files changed, 42 insertions(+), 30 deletions(-) create mode 100644 modules/contact/test/tests/explicit_dynamics/gold/test_balance_overwrite_out.csv diff --git a/framework/include/constraints/NodeFaceConstraint.h b/framework/include/constraints/NodeFaceConstraint.h index 26121e1b6411..ffda01e2a657 100644 --- a/framework/include/constraints/NodeFaceConstraint.h +++ b/framework/include/constraints/NodeFaceConstraint.h @@ -248,7 +248,7 @@ class NodeFaceConstraint : public Constraint, std::set _boundary_ids; public: - const std::set getSecondaryConnectecBlocks() const; + const std::set getSecondaryConnectedBlocks() const; PenetrationLocator & _penetration_locator; diff --git a/framework/include/problems/DisplacedProblem.h b/framework/include/problems/DisplacedProblem.h index ed495e61d01e..2e485fa9ad3f 100644 --- a/framework/include/problems/DisplacedProblem.h +++ b/framework/include/problems/DisplacedProblem.h @@ -218,7 +218,7 @@ class DisplacedProblem : public SubProblem virtual void reinitNeighbor(const Elem * elem, unsigned int side, const THREAD_ID tid) override; virtual void getNeighborPoints(const Elem * /*neighbor*/, const std::vector * /*neighbor_reference_points*/, - Point & /*neighbor_physical_points*/) override + Point & /*neighbor_physical_point*/) override { } diff --git a/framework/include/problems/SubProblem.h b/framework/include/problems/SubProblem.h index 1bc5512c22ef..9814bba0a3a1 100644 --- a/framework/include/problems/SubProblem.h +++ b/framework/include/problems/SubProblem.h @@ -396,9 +396,15 @@ class SubProblem : public Problem unsigned int neighbor_side, const std::vector & physical_points, const THREAD_ID tid) = 0; + /** + * Populates a Point object with physical coordinates in the undisplaced configuration. + * @param[in] neighbor The neighbor element + * @param[in] neighbor_reference_points A vector with the reference point in the neighbor element + * @param[out] neighbor_physical_point The physical point in the undisplaced mesh + */ virtual void getNeighborPoints(const Elem * neighbor, const std::vector * neighbor_reference_points, - Point & neighbor_physical_points); + Point & neighbor_physical_point); virtual void reinitNeighborPhys(const Elem * neighbor, const std::vector & physical_points, const THREAD_ID tid) = 0; diff --git a/framework/src/constraints/NodeFaceConstraint.C b/framework/src/constraints/NodeFaceConstraint.C index d9d6c10ac463..24ac536a98dd 100644 --- a/framework/src/constraints/NodeFaceConstraint.C +++ b/framework/src/constraints/NodeFaceConstraint.C @@ -294,7 +294,7 @@ NodeFaceConstraint::buildBoundaryIDs() } const std::set -NodeFaceConstraint::getSecondaryConnectecBlocks() const +NodeFaceConstraint::getSecondaryConnectedBlocks() const { return _mesh.getBoundaryConnectedBlocks(_secondary); } diff --git a/framework/src/interfaces/Coupleable.C b/framework/src/interfaces/Coupleable.C index 73cbe52c086a..64ec434d0d8d 100644 --- a/framework/src/interfaces/Coupleable.C +++ b/framework/src/interfaces/Coupleable.C @@ -942,7 +942,7 @@ Coupleable::checkWritableVar(MooseWritableVariable * var) _obj->name(), "' is defined on."); - if (nfc && !var->hasBlocks(nfc->getSecondaryConnectecBlocks())) + if (nfc && !var->hasBlocks(nfc->getSecondaryConnectedBlocks())) mooseError("The variable '", var->name(), " must be defined on all blocks '", diff --git a/framework/src/problems/SubProblem.C b/framework/src/problems/SubProblem.C index f5dc674f8405..f81fce7593bc 100644 --- a/framework/src/problems/SubProblem.C +++ b/framework/src/problems/SubProblem.C @@ -870,9 +870,9 @@ SubProblem::reinitElemFaceRef(const Elem * elem, void SubProblem::getNeighborPoints(const Elem * neighbor, const std::vector * neighbor_reference_points, - Point & neighbor_physical_points) + Point & neighbor_physical_point) { - neighbor_physical_points = FEMap::map(neighbor->dim(), neighbor, (*neighbor_reference_points)[0]); + neighbor_physical_point = FEMap::map(neighbor->dim(), neighbor, (*neighbor_reference_points)[0]); } void diff --git a/modules/contact/doc/content/source/userobjects/NodalDensity.md b/modules/contact/doc/content/source/userobjects/NodalDensity.md index b419993047ef..cb06e150a453 100644 --- a/modules/contact/doc/content/source/userobjects/NodalDensity.md +++ b/modules/contact/doc/content/source/userobjects/NodalDensity.md @@ -2,7 +2,7 @@ ## Description -The `NodalDensity` object computes a nodally assigned value of the +The `NodalDensity` object computes a nodal value of the density material property. It is used by explicit dynamics contact. diff --git a/modules/contact/doc/content/source/userobjects/NodalWaveSpeed.md b/modules/contact/doc/content/source/userobjects/NodalWaveSpeed.md index 6262337715f0..ea2650336b25 100644 --- a/modules/contact/doc/content/source/userobjects/NodalWaveSpeed.md +++ b/modules/contact/doc/content/source/userobjects/NodalWaveSpeed.md @@ -2,7 +2,7 @@ ## Description -The `NodalWaveSpeed` object computes a nodally assigned value of the +The `NodalWaveSpeed` object computes a nodal value of the wave speed material property. diff --git a/modules/contact/include/userobjects/NodalDensity.h b/modules/contact/include/userobjects/NodalDensity.h index 6c4c6d5d2c87..d0c49e8740e2 100644 --- a/modules/contact/include/userobjects/NodalDensity.h +++ b/modules/contact/include/userobjects/NodalDensity.h @@ -26,8 +26,6 @@ class NodalDensity : public SideIntegralVariableUserObject virtual void finalize(); protected: - virtual Real computeQpIntegral(); - std::map _node_densities; std::map _commMap; diff --git a/modules/contact/include/userobjects/NodalWaveSpeed.h b/modules/contact/include/userobjects/NodalWaveSpeed.h index 1efadd6d4be8..31b2ec7aef9b 100644 --- a/modules/contact/include/userobjects/NodalWaveSpeed.h +++ b/modules/contact/include/userobjects/NodalWaveSpeed.h @@ -26,8 +26,6 @@ class NodalWaveSpeed : public SideIntegralVariableUserObject virtual void finalize(); protected: - virtual Real computeQpIntegral(); - std::map _node_wave_speeds; std::map _commMap; diff --git a/modules/contact/src/actions/ExplicitDynamicsContactAction.C b/modules/contact/src/actions/ExplicitDynamicsContactAction.C index 9662f728de10..3030496cdc3d 100644 --- a/modules/contact/src/actions/ExplicitDynamicsContactAction.C +++ b/modules/contact/src/actions/ExplicitDynamicsContactAction.C @@ -89,6 +89,12 @@ ExplicitDynamicsContactAction::ExplicitDynamicsContactAction(const InputParamete _boundary_pairs(getParam("primary", "secondary")), _model(getParam("model").getEnum()) { + // The resulting velocity of the contact algorithm is applied, as the code stands, by modifying + // the old position. This causes artifacts in the internal forces as old position, new position, + // and velocities are not fully consistent. Results improve with shorter time steps, but it would + // be best to modify all the solution arrays for consistency or replace the way the explicit + // system is solved to obtain accelerations (a = M^{-1}F) such that only velocities on the + // contacting boundary need to be updated with the contact algorithm output. mooseWarning("Verification of explicit dynamics capabilities is an ongoing effort."); } @@ -294,7 +300,6 @@ void ExplicitDynamicsContactAction::addContactPressureAuxKernel() { // Add ContactPressureAux: Only one object for all contact pairs - // if (_formulation != ContactFormulation::MORTAR) const auto actions = _awh.getActions(); // Increment counter for contact action objects diff --git a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C index 61b73b953a32..3dc34c200307 100644 --- a/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C +++ b/modules/contact/src/constraints/ExplicitDynamicsContactConstraint.C @@ -67,10 +67,8 @@ ExplicitDynamicsContactConstraint::validParams() "Whether to overwrite the position of contact boundaries with the velocity " "computed with the contact algorithm."); params.addClassDescription( - "Apply non-penetration constraints on the mechanical deformation " - "using a node on face, primary/secondary algorithm, and multiple options " - "for the physical behavior on the interface and the mathematical " - "formulation for constraint enforcement"); + "Apply non-penetration constraints on the mechanical deformation in explicit dynamics " + "using a node on face formulation by solving uncoupled momentum-balance equations."); return params; } @@ -392,6 +390,7 @@ ExplicitDynamicsContactConstraint::solveImpactEquations(const Node & node, Real ExplicitDynamicsContactConstraint::computeQpSecondaryValue() { + // Not used in current implementation. return _u_secondary[_qp]; } diff --git a/modules/contact/src/userobjects/NodalDensity.C b/modules/contact/src/userobjects/NodalDensity.C index 7564af9ced71..ffa4c20889da 100644 --- a/modules/contact/src/userobjects/NodalDensity.C +++ b/modules/contact/src/userobjects/NodalDensity.C @@ -51,12 +51,6 @@ NodalDensity::threadJoin(const UserObject & fred) } } -Real -NodalDensity::computeQpIntegral() -{ - return 1; -} - void NodalDensity::initialize() { diff --git a/modules/contact/src/userobjects/NodalWaveSpeed.C b/modules/contact/src/userobjects/NodalWaveSpeed.C index 02146483fcba..781acc5a9359 100644 --- a/modules/contact/src/userobjects/NodalWaveSpeed.C +++ b/modules/contact/src/userobjects/NodalWaveSpeed.C @@ -49,12 +49,6 @@ NodalWaveSpeed::threadJoin(const UserObject & fred) _node_wave_speeds[it->first] += it->second; } -Real -NodalWaveSpeed::computeQpIntegral() -{ - return 1; -} - void NodalWaveSpeed::initialize() { diff --git a/modules/contact/test/tests/explicit_dynamics/gold/test_balance_overwrite_out.csv b/modules/contact/test/tests/explicit_dynamics/gold/test_balance_overwrite_out.csv new file mode 100644 index 000000000000..cd966428496c --- /dev/null +++ b/modules/contact/test/tests/explicit_dynamics/gold/test_balance_overwrite_out.csv @@ -0,0 +1,7 @@ +time,accel_58z,contact_pressure_max,critical_time_step,disp_58z,vel_58z +-0.01,0,0,0,0,0 +-0.0095,-1812.9790008838,0,0.0010540925533895,-9.8209767074621e-05,-0.02734245372604 +-0.009,-8.9446679230054e-09,0,0.0010540925533895,-9.9831442361891e-05,4.4723339615027e-14 +-0.0085000000000001,-3714.2374511323,0,0.0010540925533895,-9.9831442361873e-05,-0.018571187255662 +-0.0080000000000001,-5.4210108624275e-10,0,0.0010540925533895,-0.00010029707701866,2.7105054312138e-15 +-0.0075000000000001,-1.775381057445e-08,0,0.0010540925533895,-9.9831442361821e-05,8.8769052872251e-14 diff --git a/modules/contact/test/tests/explicit_dynamics/tests b/modules/contact/test/tests/explicit_dynamics/tests index eb7de17baa8d..956d2b1b7eed 100644 --- a/modules/contact/test/tests/explicit_dynamics/tests +++ b/modules/contact/test/tests/explicit_dynamics/tests @@ -20,6 +20,17 @@ requirement = 'The system shall be able to solve a simple few-element normal contact problem ' 'using explicit dynamics solving uncoupled, local equations of momentum balance.' [] + [test_balance_overwrite] + type = 'CSVDiff' + input = 'test_balance.i' + csvdiff = 'test_balance_overwrite_out.csv' + cli_args = 'ExplicitDynamicsContact/my_contact/overwrite_current_solution=true Outputs/file_base=test_balance_overwrite_out' + abs_zero = 1.0e-4 + allow_warnings = true + requirement = 'The system shall be able to solve a simple few-element normal contact problem ' + 'using explicit dynamics solving uncoupled, local equations of momentum balance ' + ' and overwrite boundary variables after applying the time stepper scheme.' + [] [settlement] type = 'CSVDiff' input = 'settlement.i' From b89e804c006026d7b59c947ebd27dee53796a3a2 Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Mon, 19 Feb 2024 16:43:03 -0700 Subject: [PATCH 23/31] Create framework test to cover overwriting of solution vector. Test object encapsulates basic functionality that allows a node face constraint to overwrite the solution with an explicit integrator in the framework. This feature is used in the contact module. --- .../constraints/ExplicitDynamicsOverwrite.h | 94 ++++++++ .../constraints/ExplicitDynamicsOverwrite.C | 184 +++++++++++++++ .../gold/test_balance_out.e | Bin 0 -> 147312 bytes .../overwrite_variables/test_balance.i | 217 ++++++++++++++++++ .../constraints/overwrite_variables/tests | 12 + 5 files changed, 507 insertions(+) create mode 100644 test/include/constraints/ExplicitDynamicsOverwrite.h create mode 100644 test/src/constraints/ExplicitDynamicsOverwrite.C create mode 100644 test/tests/constraints/overwrite_variables/gold/test_balance_out.e create mode 100644 test/tests/constraints/overwrite_variables/test_balance.i create mode 100644 test/tests/constraints/overwrite_variables/tests diff --git a/test/include/constraints/ExplicitDynamicsOverwrite.h b/test/include/constraints/ExplicitDynamicsOverwrite.h new file mode 100644 index 000000000000..7421df044f8c --- /dev/null +++ b/test/include/constraints/ExplicitDynamicsOverwrite.h @@ -0,0 +1,94 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#pragma once + +// MOOSE includes +#include "NodeFaceConstraint.h" +#include "PenetrationLocator.h" +#include "Coupleable.h" + +// Forward Declarations +enum class ExplicitDynamicsContactModel; + +/** + * Method to test overwriting of solution variables from NonlinearSystemBase. + */ +class ExplicitDynamicsOverwrite : public NodeFaceConstraint +{ +public: + static InputParameters validParams(); + + ExplicitDynamicsOverwrite(const InputParameters & parameters); + + virtual void timestepSetup() override; + virtual void jacobianSetup() override {} + virtual void residualEnd() override {} + + virtual Real computeQpSecondaryValue() override; + virtual Real computeQpResidual(Moose::ConstraintType type) override; + + /** + * Computes the jacobian for the current element. + */ + virtual void computeJacobian() override {} + + /** + * Compute off-diagonal Jacobian entries + * @param jvar The index of the coupled variable + */ + virtual void computeOffDiagJacobian(unsigned int /*jvar*/) override {} + + virtual Real computeQpJacobian(Moose::ConstraintJacobianType /*type*/) override { return 0.0; } + + /** + * Compute off-diagonal Jacobian entries + * @param type The type of coupling + * @param jvar The index of the coupled variable + */ + virtual Real computeQpOffDiagJacobian(Moose::ConstraintJacobianType /*type*/, + unsigned int /*jvar*/) override + { + return 0.0; + } + + bool shouldApply() override; + void computeContactForce(const Node & node, PenetrationInfo * pinfo); + virtual bool isExplicitConstraint() const override { return true; } + /** + * Return false so that the nonlinear system does not try to add Jacobian entries + * from the contact forces. + * @return bool indicating whether we need to couple Jacobian entries + */ + virtual bool addCouplingEntriesToJacobian() override { return false; } + + virtual void overwriteBoundaryVariables(NumericVector & soln, + const Node & secondary_node) const override; + +protected: + Real gapOffset(const Node & node); + + const unsigned int _component; + + const unsigned int _mesh_dimension; + + std::vector _vars; + std::vector _var_objects; + + const bool _has_secondary_gap_offset; + const MooseVariable * const _secondary_gap_offset_var; + const bool _has_mapped_primary_gap_offset; + const MooseVariable * const _mapped_primary_gap_offset_var; + + /// Whether to overwrite contact boundary nodal solution + const bool _overwrite_current_solution; + +private: + std::unordered_map _dof_to_gap; +}; diff --git a/test/src/constraints/ExplicitDynamicsOverwrite.C b/test/src/constraints/ExplicitDynamicsOverwrite.C new file mode 100644 index 000000000000..b268de6d059b --- /dev/null +++ b/test/src/constraints/ExplicitDynamicsOverwrite.C @@ -0,0 +1,184 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +// MOOSE includes +#include "ExplicitDynamicsOverwrite.h" +#include "FEProblem.h" +#include "DisplacedProblem.h" +#include "PenetrationLocator.h" +#include "NearestNodeLocator.h" +#include "SystemBase.h" +#include "Assembly.h" +#include "MooseMesh.h" +#include "MathUtils.h" +#include "Executioner.h" +#include "AddVariableAction.h" + +#include "libmesh/string_to_enum.h" +#include "libmesh/sparse_matrix.h" + +registerMooseObject("MooseApp", ExplicitDynamicsOverwrite); + +InputParameters +ExplicitDynamicsOverwrite::validParams() +{ + InputParameters params = NodeFaceConstraint::validParams(); + + params.addParam("model", + MooseEnum("frictionless frictionless_balance", "frictionless"), + "The contact model to use"); + params.addCoupledVar("vel_x", "x-component of velocity."); + params.addCoupledVar("vel_y", "y-component of velocity."); + params.addCoupledVar("vel_z", "z-component of velocity."); + + params.addRequiredParam("boundary", "The primary boundary"); + params.addParam("secondary", "The secondary boundary"); + params.addRequiredParam("component", + "An integer corresponding to the direction " + "the variable this constraint acts on. (0 for x, " + "1 for y, 2 for z)"); + params.addCoupledVar( + "displacements", + "The displacements appropriate for the simulation geometry and coordinate system"); + params.addCoupledVar("secondary_gap_offset", "offset to the gap distance from secondary side"); + params.addCoupledVar("mapped_primary_gap_offset", + "offset to the gap distance mapped from primary side"); + params.set("use_displaced_mesh") = true; + params.addParam("friction_coefficient", 0, "The friction coefficient"); + params.addParam("tangential_tolerance", + "Tangential distance to extend edges of contact surfaces"); + params.addClassDescription( + "Apply non-penetration constraints on the mechanical deformation in explicit dynamics " + "using a node on face formulation by solving uncoupled momentum-balance equations."); + return params; +} + +ExplicitDynamicsOverwrite::ExplicitDynamicsOverwrite(const InputParameters & parameters) + : NodeFaceConstraint(parameters), + _component(getParam("component")), + _mesh_dimension(_mesh.dimension()), + _vars(3, libMesh::invalid_uint), + _var_objects(3, nullptr), + _has_secondary_gap_offset(isCoupled("secondary_gap_offset")), + _secondary_gap_offset_var(_has_secondary_gap_offset ? getVar("secondary_gap_offset", 0) + : nullptr), + _has_mapped_primary_gap_offset(isCoupled("mapped_primary_gap_offset")), + _mapped_primary_gap_offset_var( + _has_mapped_primary_gap_offset ? getVar("mapped_primary_gap_offset", 0) : nullptr), + _overwrite_current_solution(true) +{ + _overwrite_secondary_residual = false; + + if (isParamValid("displacements")) + { + // modern parameter scheme for displacements + for (unsigned int i = 0; i < coupledComponents("displacements"); ++i) + { + _vars[i] = coupled("displacements", i); + _var_objects[i] = getVar("displacements", i); + } + } + + bool is_correct = + (isCoupled("vel_x") && isCoupled("vel_y") && _mesh.dimension() == 2) || + (isCoupled("vel_x") && isCoupled("vel_y") && isCoupled("vel_z") && _mesh.dimension() == 3); + + if (!is_correct) + paramError("vel_x", + "Velocities vel_x and vel_y (also vel_z in three dimensions) need to be provided " + "for the 'balance' option of solving normal contact in explicit dynamics."); +} + +void +ExplicitDynamicsOverwrite::timestepSetup() +{ + if (_component == 0) + _dof_to_gap.clear(); +} + +bool +ExplicitDynamicsOverwrite::shouldApply() +{ + if (_current_node->processor_id() != _fe_problem.processor_id()) + return false; + + std::map::iterator found = + _penetration_locator._penetration_info.find(_current_node->id()); + if (found != _penetration_locator._penetration_info.end()) + { + PenetrationInfo * pinfo = found->second; + if (pinfo != nullptr) + if (_component == 0) + computeContactForce(*_current_node, pinfo); + } + return false; +} + +void +ExplicitDynamicsOverwrite::computeContactForce(const Node & node, PenetrationInfo * pinfo) +{ + pinfo->_contact_force.zero(); + RealVectorValue distance_vec(node - pinfo->_closest_point); + if (distance_vec.norm() != 0) + distance_vec += gapOffset(node) * pinfo->_normal * distance_vec.unit() * distance_vec.unit(); + + const Real gap_size = -1.0 * pinfo->_normal * distance_vec; + + if (!pinfo->isCaptured() && MooseUtils::absoluteFuzzyGreaterEqual(gap_size, 0.0, 0.0)) + { + pinfo->capture(); + } + dof_id_type dof_x = node.dof_number(_sys.number(), _var_objects[0]->number(), 0); + + _dof_to_gap[dof_x] = gap_size; +} + +Real +ExplicitDynamicsOverwrite::gapOffset(const Node & node) +{ + Real val = 0; + + if (_has_secondary_gap_offset) + val += _secondary_gap_offset_var->getNodalValue(node); + + if (_has_mapped_primary_gap_offset) + val += _mapped_primary_gap_offset_var->getNodalValue(node); + + return val; +} + +Real +ExplicitDynamicsOverwrite::computeQpSecondaryValue() +{ + // Not used in current implementation. + return -9999.9; +} + +Real +ExplicitDynamicsOverwrite::computeQpResidual(Moose::ConstraintType /*type*/) +{ + return 0.0; +} + +void +ExplicitDynamicsOverwrite::overwriteBoundaryVariables(NumericVector & soln, + const Node & secondary_node) const +{ + dof_id_type dof_x = secondary_node.dof_number(_sys.number(), _var_objects[0]->number(), 0); + + if (_component == 0 && _overwrite_current_solution && libmesh_map_find(_dof_to_gap, dof_x) >= 0.0) + { + dof_id_type dof_z = secondary_node.dof_number(_sys.number(), _var_objects[2]->number(), 0); + // Blatantly attached the 'cube' bottom surface to zero. + // This doesn't do anything physical, just overwrites the z displacement to give the appearance + // of contact . + if (_dof_to_gap.find(dof_x) != _dof_to_gap.end()) + soln.set(dof_z, 0.0); + } +} diff --git a/test/tests/constraints/overwrite_variables/gold/test_balance_out.e b/test/tests/constraints/overwrite_variables/gold/test_balance_out.e new file mode 100644 index 0000000000000000000000000000000000000000..a9f92080e6e81bd94780529ef7fba42f8e3437ae GIT binary patch literal 147312 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 A6951J literal 0 HcmV?d00001 diff --git a/test/tests/constraints/overwrite_variables/test_balance.i b/test/tests/constraints/overwrite_variables/test_balance.i new file mode 100644 index 000000000000..5e2b0b276e37 --- /dev/null +++ b/test/tests/constraints/overwrite_variables/test_balance.i @@ -0,0 +1,217 @@ +# Test to exemplify the use of overwriting of variables in the framework. +[GlobalParams] + displacements = 'disp_x disp_y disp_z' + diffusivity = 1e3 + use_displaced_mesh = true +[] + +[Mesh] + [block_one] + type = GeneratedMeshGenerator + dim = 3 + nx = 3 + ny = 3 + nz = 3 + xmin = 4.5 + xmax = 5.5 + ymin = 4.5 + ymax = 5.5 + zmin = 0.0001 + zmax = 1.0001 + boundary_name_prefix = 'ball' + [] + [block_two] + type = GeneratedMeshGenerator + dim = 3 + nx = 2 + ny = 2 + nz = 2 + xmin = 0.0 + xmax = 10 + ymin = 0.0 + ymax = 10 + zmin = -2 + zmax = 0 + boundary_name_prefix = 'base' + boundary_id_offset = 10 + [] + [block_one_id] + type = SubdomainIDGenerator + input = block_one + subdomain_id = 1 + [] + [block_two_id] + type = SubdomainIDGenerator + input = block_two + subdomain_id = 2 + [] + [combine] + type = MeshCollectionGenerator + inputs = ' block_one_id block_two_id' + [] + allow_renumbering = false +[] + +[Problem] + kernel_coverage_check = false +[] + +[Variables] + [disp_x] + [] + [disp_y] + [] + [disp_z] + [] +[] + +[AuxVariables] + [gap_rate] + [] + [vel_x] + [] + [accel_x] + [] + [vel_y] + [] + [accel_y] + [] + [vel_z] + [] + [accel_z] + [] +[] + +[Kernels] + [disp_x] + type = MatDiffusion + variable = disp_x + [] + [disp_y] + type = MatDiffusion + variable = disp_y + [] + [disp_z] + type = MatDiffusion + variable = disp_z + [] + [vel_x] + type = TimeDerivative + variable = disp_x + [] + [vel_y] + type = TimeDerivative + variable = disp_y + [] + [vel_z] + type = TimeDerivative + variable = disp_z + [] + [source_m] + type = BodyForce + variable = disp_z + value = -100 + [] +[] + +[BCs] + [x_front] + type = DirichletBC + variable = disp_x + boundary = 'ball_front' + preset = false + value = 0.0 + [] + [y_front] + type = DirichletBC + variable = disp_y + boundary = 'ball_front' + preset = false + value = 0.0 + [] + [x_fixed] + type = DirichletBC + variable = disp_x + boundary = 'base_back' + value = 0.0 + preset = true + [] + [y_fixed] + type = DirichletBC + variable = disp_y + boundary = 'base_back' + value = 0.0 + preset = true + [] + [z_fixed] + type = DirichletBC + variable = disp_z + boundary = 'base_back' + value = 0.0 + preset = true + [] + [z_fixed_front] + type = DirichletBC + variable = disp_z + boundary = 'base_front' + value = 0.0 + preset = true + [] +[] + +[Constraints] + [overwrite] + type = ExplicitDynamicsOverwrite + model = frictionless_balance + primary = base_front + secondary = ball_back + vel_x = 'vel_x' + vel_y = 'vel_y' + vel_z = 'vel_z' + primary_variable = disp_x + boundary = 'base_front' + component = 0 + variable = disp_x + [] +[] + +[Materials] + [density_one] + type = GenericConstantMaterial + prop_names = density + prop_values = 1e5 + outputs = 'exodus' + output_properties = 'density' + block = '1' + [] + [density_two] + type = GenericConstantMaterial + prop_names = density + prop_values = 1e5 + outputs = 'exodus' + output_properties = 'density' + block = '2' + [] +[] + +[Executioner] + type = Transient + start_time = -0.01 + end_time = -0.008 + dt = 1.0e-5 + timestep_tolerance = 1e-6 + + [TimeIntegrator] + type = CentralDifference + solve_type = lumped + [] +[] + +[Outputs] + interval = 50 + exodus = true + csv = true +[] + +[Postprocessors] +[] diff --git a/test/tests/constraints/overwrite_variables/tests b/test/tests/constraints/overwrite_variables/tests new file mode 100644 index 000000000000..a71907f74e85 --- /dev/null +++ b/test/tests/constraints/overwrite_variables/tests @@ -0,0 +1,12 @@ +[Tests] + issues = '#25666' + design = 'NonlinearSystemBase.md' + [test_balance] + type = 'Exodiff' + input = 'test_balance.i' + exodiff = 'test_balance_out.e' + abs_zero = 1.0e-4 + allow_test_objects = true + requirement = 'The system shall be able to allow overwriting of solution variables in node-face constraint objects.' + [] +[] From 142a6a981e821fbff10b9057ff707b4026b2d392 Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Mon, 19 Feb 2024 19:54:11 -0700 Subject: [PATCH 24/31] Convert mat prop dependencies to unordered set --- .../include/constraints/NodeFaceConstraint.h | 6 +++--- framework/src/systems/NonlinearSystemBase.C | 2 +- .../ExplicitDynamicsContactConstraint.h | 4 ++-- .../constraints/ExplicitDynamicsOverwrite.h | 3 +++ .../constraints/ExplicitDynamicsOverwrite.C | 7 +++++-- .../gold/test_balance_out.e | Bin 147312 -> 147392 bytes .../overwrite_variables/test_balance.i | 5 +++-- 7 files changed, 17 insertions(+), 10 deletions(-) diff --git a/framework/include/constraints/NodeFaceConstraint.h b/framework/include/constraints/NodeFaceConstraint.h index ffda01e2a657..7a6a657fa315 100644 --- a/framework/include/constraints/NodeFaceConstraint.h +++ b/framework/include/constraints/NodeFaceConstraint.h @@ -115,7 +115,7 @@ class NodeFaceConstraint : public Constraint, void residualSetup() override; - virtual const std::set & getMatPropDependencies() const; + virtual const std::unordered_set & getMatPropDependencies() const; /** * Whether (contact) constraint is of 'explicit dynamics' type. @@ -308,7 +308,7 @@ class NodeFaceConstraint : public Constraint, Real _secondary_residual; /// An empty material property dependency set for use with \p getMatPropDependencies - const std::set _empty_mat_prop_deps; + const std::unordered_set _empty_mat_prop_deps; public: std::vector _connected_dof_indices; @@ -334,7 +334,7 @@ class NodeFaceConstraint : public Constraint, DenseMatrix _Ken; }; -inline const std::set & +inline const std::unordered_set & NodeFaceConstraint::getMatPropDependencies() const { return _empty_mat_prop_deps; diff --git a/framework/src/systems/NonlinearSystemBase.C b/framework/src/systems/NonlinearSystemBase.C index 2bf25691d6bc..2acec027d160 100644 --- a/framework/src/systems/NonlinearSystemBase.C +++ b/framework/src/systems/NonlinearSystemBase.C @@ -1112,7 +1112,7 @@ NonlinearSystemBase::setConstraintSecondaryValues(NumericVector & soluti { const auto & constraints = _constraints.getActiveNodeFaceConstraints(secondary_boundary, displaced); - std::set needed_mat_props; + std::unordered_set needed_mat_props; for (const auto & constraint : constraints) { const auto & mp_deps = constraint->getMatPropDependencies(); diff --git a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h index 7eb114cc3b19..b2a57608ae44 100644 --- a/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h +++ b/modules/contact/include/constraints/ExplicitDynamicsContactConstraint.h @@ -71,7 +71,7 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, */ virtual bool addCouplingEntriesToJacobian() override { return false; } - virtual const std::set & getMatPropDependencies() const override; + virtual const std::unordered_set & getMatPropDependencies() const override; virtual void overwriteBoundaryVariables(NumericVector & soln, const Node & secondary_node) const override; @@ -144,7 +144,7 @@ class ExplicitDynamicsContactConstraint : public NodeFaceConstraint, std::unordered_map _dof_to_position; }; -inline const std::set & +inline const std::unordered_set & ExplicitDynamicsContactConstraint::getMatPropDependencies() const { return TwoMaterialPropertyInterface::getMatPropDependencies(); diff --git a/test/include/constraints/ExplicitDynamicsOverwrite.h b/test/include/constraints/ExplicitDynamicsOverwrite.h index 7421df044f8c..2b7f79ae9c6b 100644 --- a/test/include/constraints/ExplicitDynamicsOverwrite.h +++ b/test/include/constraints/ExplicitDynamicsOverwrite.h @@ -89,6 +89,9 @@ class ExplicitDynamicsOverwrite : public NodeFaceConstraint /// Whether to overwrite contact boundary nodal solution const bool _overwrite_current_solution; + /// Nodal gap rate (exercise writable variables from framework) + MooseWritableVariable * _gap_rate; + private: std::unordered_map _dof_to_gap; }; diff --git a/test/src/constraints/ExplicitDynamicsOverwrite.C b/test/src/constraints/ExplicitDynamicsOverwrite.C index b268de6d059b..c349dd18dbae 100644 --- a/test/src/constraints/ExplicitDynamicsOverwrite.C +++ b/test/src/constraints/ExplicitDynamicsOverwrite.C @@ -53,6 +53,7 @@ ExplicitDynamicsOverwrite::validParams() params.addParam("friction_coefficient", 0, "The friction coefficient"); params.addParam("tangential_tolerance", "Tangential distance to extend edges of contact surfaces"); + params.addCoupledVar("gap_rate", "Gap rate for output (writable auxiliary variable)."); params.addClassDescription( "Apply non-penetration constraints on the mechanical deformation in explicit dynamics " "using a node on face formulation by solving uncoupled momentum-balance equations."); @@ -71,7 +72,8 @@ ExplicitDynamicsOverwrite::ExplicitDynamicsOverwrite(const InputParameters & par _has_mapped_primary_gap_offset(isCoupled("mapped_primary_gap_offset")), _mapped_primary_gap_offset_var( _has_mapped_primary_gap_offset ? getVar("mapped_primary_gap_offset", 0) : nullptr), - _overwrite_current_solution(true) + _overwrite_current_solution(true), + _gap_rate(&writableVariable("gap_rate")) { _overwrite_secondary_residual = false; @@ -137,6 +139,7 @@ ExplicitDynamicsOverwrite::computeContactForce(const Node & node, PenetrationInf dof_id_type dof_x = node.dof_number(_sys.number(), _var_objects[0]->number(), 0); _dof_to_gap[dof_x] = gap_size; + _gap_rate->setNodalValue(-1.23456); } Real @@ -177,7 +180,7 @@ ExplicitDynamicsOverwrite::overwriteBoundaryVariables(NumericVector & so dof_id_type dof_z = secondary_node.dof_number(_sys.number(), _var_objects[2]->number(), 0); // Blatantly attached the 'cube' bottom surface to zero. // This doesn't do anything physical, just overwrites the z displacement to give the appearance - // of contact . + // of contact. if (_dof_to_gap.find(dof_x) != _dof_to_gap.end()) soln.set(dof_z, 0.0); } diff --git a/test/tests/constraints/overwrite_variables/gold/test_balance_out.e b/test/tests/constraints/overwrite_variables/gold/test_balance_out.e index a9f92080e6e81bd94780529ef7fba42f8e3437ae..539cfc1825ce519a1ea35e8878def2686d44973c 100644 GIT binary patch delta 1201 zcmezHkK@39jt%H~^!Q>bqwU=Y^6ajO_2b0eL$sLnf1kD*IOtum1XUv#9K~QA!13@*WlMa(PgyNWD z88&AKowZff&&bbB)h|j-E=?`U*AGf9$S=+;$uG)GE!NM?&o55ZFG(#fnQRuI9ZTSI ztPqx3RGgWgr{I~Fmam`;GEiB0@7MkcxjM!JTU zlM^LGH>(HoFiM1!rYg9lCIRIX3=OOd46KZe!RmlUC``@{l0;TuU}F2}bqJZ5`FjlON~= zPE}xJ-E5uzKY%?wu^_%Ev1D@KQ3(zxJ9V;Ss@C-Vyo|w{dk?fRZB98nm2vWee=3{v zjv7AOzF;-u3cdRMpAQ`T({E%kK=kCs3z|TGydVJ|*uLT^qk+(nbh!mH(-3v}35B8R Ka)}d+5{v+XcX8|h delta 426 zcmZ9C-777Ob^g{uu?^ z5Et8H3ENd0G~!^ysdH*vm5c$aqw|iSzgY>! z35m;+@sf$`a}(K3=w<{em>w+yvCG&ylu^kWl)U~>lN@UDeRX)#AYplL<^RANYWDvG zXZwA22TqGcSlB4SRBVk8zfNLQ6LEOnPR%-qbT3}I18_8~ zLmhk3JUqKq-y@h=?`4ap%N#YkpobNorvb^ViROx-8^dXpUJF22%f;bC_Gt&U8?zZ5 YMoP1<4?t|+zQS|AE>`_{A`c>b01^F@L;wH) diff --git a/test/tests/constraints/overwrite_variables/test_balance.i b/test/tests/constraints/overwrite_variables/test_balance.i index 5e2b0b276e37..01a5f3ae2d95 100644 --- a/test/tests/constraints/overwrite_variables/test_balance.i +++ b/test/tests/constraints/overwrite_variables/test_balance.i @@ -66,8 +66,6 @@ [] [AuxVariables] - [gap_rate] - [] [vel_x] [] [accel_x] @@ -80,6 +78,8 @@ [] [accel_z] [] + [gap_rate] + [] [] [Kernels] @@ -172,6 +172,7 @@ boundary = 'base_front' component = 0 variable = disp_x + gap_rate = gap_rate [] [] From f597a967b9f6cec13b41843a32ede656130ae41e Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Tue, 20 Feb 2024 17:12:29 -0800 Subject: [PATCH 25/31] NodeFaceConstraint updates - More doxygen - Reorder data members for public-protected-private - friend class NonlinearSystemBase - No more public penetration info --- .../include/constraints/NodeFaceConstraint.h | 27 +++++++++++++------ .../src/constraints/NodeFaceConstraint.C | 2 +- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/framework/include/constraints/NodeFaceConstraint.h b/framework/include/constraints/NodeFaceConstraint.h index 7a6a657fa315..3d6f8fa04670 100644 --- a/framework/include/constraints/NodeFaceConstraint.h +++ b/framework/include/constraints/NodeFaceConstraint.h @@ -115,6 +115,9 @@ class NodeFaceConstraint : public Constraint, void residualSetup() override; + /** + * @returns material property dependencies + */ virtual const std::unordered_set & getMatPropDependencies() const; /** @@ -126,7 +129,14 @@ class NodeFaceConstraint : public Constraint, * Allows for overwriting boundary variables (explicit dynamics contact). */ virtual void overwriteBoundaryVariables(NumericVector & /*soln*/, - const Node & /*secondary_node*/) const {}; + const Node & /*secondary_node*/) const + { + } + + /** + * @returns IDs of the subdomains that connect to the secondary boundary + */ + std::set getSecondaryConnectedBlocks() const; protected: /** @@ -232,12 +242,16 @@ class NodeFaceConstraint : public Constraint, return coupledNeighborSecond(var_name, comp); } + /** + * Builds the \p _boundary_ids data member and returns it + * @returns a set containing the secondary and primary boundary IDs + */ const std::set & buildBoundaryIDs(); /// Boundary ID for the secondary surface - unsigned int _secondary; + BoundaryID _secondary; /// Boundary ID for the primary surface - unsigned int _primary; + BoundaryID _primary; MooseVariable & _var; @@ -247,12 +261,8 @@ class NodeFaceConstraint : public Constraint, /// the union of the secondary and primary boundary ids std::set _boundary_ids; -public: - const std::set getSecondaryConnectedBlocks() const; - PenetrationLocator & _penetration_locator; -protected: /// current node being processed const Node * const & _current_node; const Elem * const & _current_primary; @@ -310,7 +320,6 @@ class NodeFaceConstraint : public Constraint, /// An empty material property dependency set for use with \p getMatPropDependencies const std::unordered_set _empty_mat_prop_deps; -public: std::vector _connected_dof_indices; /// The Jacobian corresponding to the derivatives of the neighbor/primary residual with respect to @@ -332,6 +341,8 @@ class NodeFaceConstraint : public Constraint, /// overwriting the secondary residual we traditionally want to use a different scaling factor from the /// one associated with interior physics DenseMatrix _Ken; + + friend class NonlinearSystemBase; }; inline const std::unordered_set & diff --git a/framework/src/constraints/NodeFaceConstraint.C b/framework/src/constraints/NodeFaceConstraint.C index 24ac536a98dd..fbf9b7622714 100644 --- a/framework/src/constraints/NodeFaceConstraint.C +++ b/framework/src/constraints/NodeFaceConstraint.C @@ -293,7 +293,7 @@ NodeFaceConstraint::buildBoundaryIDs() return _boundary_ids; } -const std::set +std::set NodeFaceConstraint::getSecondaryConnectedBlocks() const { return _mesh.getBoundaryConnectedBlocks(_secondary); From 226bc8d6cbc428e6559c63abc5d134f82e4ba8c4 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Tue, 20 Feb 2024 17:14:18 -0800 Subject: [PATCH 26/31] Add reinitNodeFaceFace method to NonlinearSystemBase And remove the getNeighborPoints method --- framework/include/problems/DisplacedProblem.h | 5 - framework/include/problems/SubProblem.h | 9 -- .../include/systems/NonlinearSystemBase.h | 6 + framework/src/problems/SubProblem.C | 8 -- framework/src/systems/NonlinearSystemBase.C | 133 +++++++----------- 5 files changed, 60 insertions(+), 101 deletions(-) diff --git a/framework/include/problems/DisplacedProblem.h b/framework/include/problems/DisplacedProblem.h index 2e485fa9ad3f..a61d9ce8fdcd 100644 --- a/framework/include/problems/DisplacedProblem.h +++ b/framework/include/problems/DisplacedProblem.h @@ -216,11 +216,6 @@ class DisplacedProblem : public SubProblem virtual void reinitNodesNeighbor(const std::vector & nodes, const THREAD_ID tid) override; virtual void reinitNeighbor(const Elem * elem, unsigned int side, const THREAD_ID tid) override; - virtual void getNeighborPoints(const Elem * /*neighbor*/, - const std::vector * /*neighbor_reference_points*/, - Point & /*neighbor_physical_point*/) override - { - } /** * reinitialize neighbor routine diff --git a/framework/include/problems/SubProblem.h b/framework/include/problems/SubProblem.h index 9814bba0a3a1..0c32aa82aa06 100644 --- a/framework/include/problems/SubProblem.h +++ b/framework/include/problems/SubProblem.h @@ -396,15 +396,6 @@ class SubProblem : public Problem unsigned int neighbor_side, const std::vector & physical_points, const THREAD_ID tid) = 0; - /** - * Populates a Point object with physical coordinates in the undisplaced configuration. - * @param[in] neighbor The neighbor element - * @param[in] neighbor_reference_points A vector with the reference point in the neighbor element - * @param[out] neighbor_physical_point The physical point in the undisplaced mesh - */ - virtual void getNeighborPoints(const Elem * neighbor, - const std::vector * neighbor_reference_points, - Point & neighbor_physical_point); virtual void reinitNeighborPhys(const Elem * neighbor, const std::vector & physical_points, const THREAD_ID tid) = 0; diff --git a/framework/include/systems/NonlinearSystemBase.h b/framework/include/systems/NonlinearSystemBase.h index c59afeebc73e..7001fffae5a4 100644 --- a/framework/include/systems/NonlinearSystemBase.h +++ b/framework/include/systems/NonlinearSystemBase.h @@ -44,6 +44,7 @@ class Split; class KernelBase; class BoundaryCondition; class ResidualObject; +class PenetrationInfo; // libMesh forward declarations namespace libMesh @@ -769,6 +770,11 @@ class NonlinearSystemBase : public SystemBase, public PerfGraphInterface NumericVector & solutionInternal() const override { return *_sys.solution; } + /** + * Reinitialize quantities on the primary side of a secondary node primary face pair + */ + void reinitNodeFaceFace(const PenetrationInfo & info, const bool displaced); + /// solution vector from nonlinear solver const NumericVector * _current_solution; /// ghosted form of the residual diff --git a/framework/src/problems/SubProblem.C b/framework/src/problems/SubProblem.C index f81fce7593bc..a5b9a08a97b0 100644 --- a/framework/src/problems/SubProblem.C +++ b/framework/src/problems/SubProblem.C @@ -867,14 +867,6 @@ SubProblem::reinitElemFaceRef(const Elem * elem, current_assembly.prepareResidual(); } -void -SubProblem::getNeighborPoints(const Elem * neighbor, - const std::vector * neighbor_reference_points, - Point & neighbor_physical_point) -{ - neighbor_physical_point = FEMap::map(neighbor->dim(), neighbor, (*neighbor_reference_points)[0]); -} - void SubProblem::reinitNeighborFaceRef(const Elem * neighbor_elem, unsigned int neighbor_side, diff --git a/framework/src/systems/NonlinearSystemBase.C b/framework/src/systems/NonlinearSystemBase.C index 2acec027d160..a4090d3ea865 100644 --- a/framework/src/systems/NonlinearSystemBase.C +++ b/framework/src/systems/NonlinearSystemBase.C @@ -1085,6 +1085,51 @@ NonlinearSystemBase::enforceNodalConstraintsJacobian() } } +void +NonlinearSystemBase::reinitNodeFaceFace(const PenetrationInfo & info, const bool displaced) +{ + auto & subproblem = displaced ? static_cast(*_fe_problem.getDisplacedProblem()) + : static_cast(_fe_problem); + + const Elem * primary_elem = info._elem; + unsigned int primary_side = info._side_num; + + std::vector points; + points.push_back(info._closest_point); + + // Reinit variables and materials on the primary element's face at the contact point + _fe_problem.setNeighborSubdomainID(primary_elem, 0); + + // + // Reinit material on undisplaced mesh + // + + const Elem * const undisplaced_primary_elem = + displaced ? _mesh.elemPtr(primary_elem->id()) : primary_elem; + const Point undisplaced_primary_physical_point = + [&points, displaced, primary_elem, undisplaced_primary_elem]() + { + if (displaced) + { + const Point reference_point = + FEMap::inverse_map(primary_elem->dim(), primary_elem, points[0]); + return FEMap::map(primary_elem->dim(), undisplaced_primary_elem, reference_point); + } + else + // If our penetration locator is on the reference mesh, then our undisplaced + // physical point is simply the point coming from the penetration locator + return points[0]; + }(); + + _fe_problem.reinitNeighborPhys( + undisplaced_primary_elem, primary_side, {undisplaced_primary_physical_point}, 0); + _fe_problem.reinitMaterialsNeighbor(primary_elem->subdomain_id(), 0); + + // Reinit points for constraint enforcement + if (displaced) + subproblem.reinitNeighborPhys(primary_elem, primary_side, points, 0); +} + void NonlinearSystemBase::setConstraintSecondaryValues(NumericVector & solution, bool displaced) { @@ -1131,33 +1176,12 @@ NonlinearSystemBase::setConstraintSecondaryValues(NumericVector & soluti { PenetrationInfo & info = *pen_loc._penetration_info[secondary_node_num]; - const Elem * primary_elem = info._elem; - unsigned int primary_side = info._side_num; - // reinit variables at the node _fe_problem.reinitNodeFace(&secondary_node, secondary_boundary, 0); _fe_problem.prepareAssembly(0); - std::vector points; - points.push_back(info._closest_point); - - // Reinit variables and materials on the primary element's face at the contact point - _fe_problem.setNeighborSubdomainID(primary_elem, 0); - - // Reinit material on undisplaced mesh - std::vector reference_points; - FEInterface::inverse_map( - primary_elem->dim(), FEType(), primary_elem, points, reference_points); - Point neighbor_physical_points; - _fe_problem.getNeighborPoints( - _mesh.elemPtr(primary_elem->id()), &reference_points, neighbor_physical_points); - _fe_problem.reinitNeighborPhys( - _mesh.elemPtr(primary_elem->id()), primary_side, {neighbor_physical_points}, 0); - _fe_problem.reinitMaterialsNeighbor(primary_elem->subdomain_id(), 0); - - // Reinit points for constraint enforcement - subproblem.reinitNeighborPhys(primary_elem, primary_side, points, 0); + reinitNodeFaceFace(info, displaced); for (const auto & nfc : constraints) { @@ -1304,9 +1328,6 @@ NonlinearSystemBase::constraintResiduals(NumericVector & residual, bool { PenetrationInfo & info = *pen_loc._penetration_info[secondary_node_num]; - const Elem * primary_elem = info._elem; - unsigned int primary_side = info._side_num; - // *These next steps MUST be done in this order!* // This reinits the variables that exist on the secondary node @@ -1316,25 +1337,7 @@ NonlinearSystemBase::constraintResiduals(NumericVector & residual, bool // the secondary node _fe_problem.prepareAssembly(0); - std::vector points; - points.push_back(info._closest_point); - - // Reinit variables and materials on the primary element's face at the contact point - _fe_problem.setNeighborSubdomainID(primary_elem, 0); - - // Reinit material on undisplaced mesh - std::vector reference_points; - FEInterface::inverse_map( - primary_elem->dim(), FEType(), primary_elem, points, reference_points); - Point neighbor_physical_points; - _fe_problem.getNeighborPoints( - _mesh.elemPtr(primary_elem->id()), &reference_points, neighbor_physical_points); - _fe_problem.reinitNeighborPhys( - _mesh.elemPtr(primary_elem->id()), primary_side, {neighbor_physical_points}, 0); - _fe_problem.reinitMaterialsNeighbor(primary_elem->subdomain_id(), 0); - - // Reinit points for constraint enforcement - subproblem.reinitNeighborPhys(primary_elem, primary_side, points, 0); + reinitNodeFaceFace(info, displaced); for (const auto & nfc : constraints) { @@ -1587,23 +1590,21 @@ NonlinearSystemBase::overwriteContact(NumericVector & soln) { PenetrationLocator & pen_loc = *(it.second); - std::vector & secondary_nodes = pen_loc._nearest_node._secondary_nodes; - BoundaryID secondary_boundary = pen_loc._secondary_boundary; - BoundaryID primary_boundary = pen_loc._primary_boundary; + const auto & secondary_nodes = pen_loc._nearest_node._secondary_nodes; + const BoundaryID secondary_boundary = pen_loc._secondary_boundary; + const BoundaryID primary_boundary = pen_loc._primary_boundary; if (_constraints.hasActiveNodeFaceConstraints(secondary_boundary, true)) { const auto & constraints = _constraints.getActiveNodeFaceConstraints(secondary_boundary, true); - for (unsigned int i = 0; i < secondary_nodes.size(); i++) + for (const auto i : index_range(secondary_nodes)) { - dof_id_type secondary_node_num = secondary_nodes[i]; - Node & secondary_node = _mesh.nodeRef(secondary_node_num); + const auto secondary_node_num = secondary_nodes[i]; + const Node & secondary_node = _mesh.nodeRef(secondary_node_num); if (secondary_node.processor_id() == processor_id()) - { if (pen_loc._penetration_info[secondary_node_num]) - { for (const auto & nfc : constraints) { if (!nfc->isExplicitConstraint()) @@ -1619,8 +1620,6 @@ NonlinearSystemBase::overwriteContact(NumericVector & soln) nfc->overwriteBoundaryVariables(soln, secondary_node); } - } - } } } } @@ -2286,37 +2285,13 @@ NonlinearSystemBase::constraintJacobians(bool displaced) { PenetrationInfo & info = *pen_loc._penetration_info[secondary_node_num]; - const Elem * primary_elem = info._elem; - unsigned int primary_side = info._side_num; - // reinit variables at the node _fe_problem.reinitNodeFace(&secondary_node, secondary_boundary, 0); _fe_problem.prepareAssembly(0); _fe_problem.reinitOffDiagScalars(0); - std::vector points; - points.push_back(info._closest_point); - - // reinit variables on the primary element's face at the contact point - _fe_problem.setNeighborSubdomainID(primary_elem, 0); - - // Reinit variables and materials on the primary element's face at the contact point - _fe_problem.setNeighborSubdomainID(primary_elem, 0); - - // Reinit material on undisplaced mesh - std::vector reference_points; - FEInterface::inverse_map( - primary_elem->dim(), FEType(), primary_elem, points, reference_points); - Point neighbor_physical_points; - _fe_problem.getNeighborPoints( - _mesh.elemPtr(primary_elem->id()), &reference_points, neighbor_physical_points); - _fe_problem.reinitNeighborPhys( - _mesh.elemPtr(primary_elem->id()), primary_side, {neighbor_physical_points}, 0); - _fe_problem.reinitMaterialsNeighbor(primary_elem->subdomain_id(), 0); - - // Reinit points for constraint enforcement - subproblem.reinitNeighborPhys(primary_elem, primary_side, points, 0); + reinitNodeFaceFace(info, displaced); for (const auto & nfc : constraints) { From fbdbb1d27376efe641e2546b1db21c35e26bcc93 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Tue, 20 Feb 2024 17:14:55 -0800 Subject: [PATCH 27/31] No extra close in ActuallyExplicitEuler Also no need for overwriteContact to be virtual --- framework/include/systems/NonlinearSystemBase.h | 6 +++--- framework/src/timeintegrators/ActuallyExplicitEuler.C | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/framework/include/systems/NonlinearSystemBase.h b/framework/include/systems/NonlinearSystemBase.h index 7001fffae5a4..1b17747fd1fb 100644 --- a/framework/include/systems/NonlinearSystemBase.h +++ b/framework/include/systems/NonlinearSystemBase.h @@ -363,10 +363,10 @@ class NonlinearSystemBase : public SystemBase, public PerfGraphInterface virtual void setSolution(const NumericVector & soln); /** - * Called from explicit time stepping to overwrite boundary positions (explicit dynamics) - * @param soln Solution of current time step. + * Called from explicit time stepping to overwrite boundary positions (explicit dynamics). This + * will close/assemble the passed-in \p soln after overwrite */ - virtual void overwriteContact(NumericVector & soln); + void overwriteContact(NumericVector & soln); /** * Update active objects of Warehouses owned by NonlinearSystemBase diff --git a/framework/src/timeintegrators/ActuallyExplicitEuler.C b/framework/src/timeintegrators/ActuallyExplicitEuler.C index ad986bf742f5..059b53b66ca1 100644 --- a/framework/src/timeintegrators/ActuallyExplicitEuler.C +++ b/framework/src/timeintegrators/ActuallyExplicitEuler.C @@ -96,7 +96,6 @@ ActuallyExplicitEuler::solve() *_nonlinear_implicit_system->solution += _solution_update; _nl.overwriteContact(*_nonlinear_implicit_system->solution); - _nonlinear_implicit_system->solution->close(); // Enforce contraints on the solution DofMap & dof_map = _nonlinear_implicit_system->get_dof_map(); From 3d1646085c1b536d1157b282e50477bd765e7a8a Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Tue, 20 Feb 2024 22:21:32 -0800 Subject: [PATCH 28/31] reinitNodeFaceFace -> reinitNodeFace This after moving calls to `_fe_problem.reinitNodeFace` into the `NonlinearSystemBase` method such that `NonlinearSystemBase::reinitNodeFace` now does all the reinit'ing necessary for node face constraints --- .../include/systems/NonlinearSystemBase.h | 10 ++++- framework/src/systems/NonlinearSystemBase.C | 45 +++++++++---------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/framework/include/systems/NonlinearSystemBase.h b/framework/include/systems/NonlinearSystemBase.h index 1b17747fd1fb..78f0e6f60e88 100644 --- a/framework/include/systems/NonlinearSystemBase.h +++ b/framework/include/systems/NonlinearSystemBase.h @@ -696,6 +696,8 @@ class NonlinearSystemBase : public SystemBase, public PerfGraphInterface */ void setupDM(); + using SystemBase::reinitNodeFace; + protected: /** * Compute the residual for a given tag @@ -771,9 +773,13 @@ class NonlinearSystemBase : public SystemBase, public PerfGraphInterface NumericVector & solutionInternal() const override { return *_sys.solution; } /** - * Reinitialize quantities on the primary side of a secondary node primary face pair + * Reinitialize quantities such as variables, residuals, Jacobians, materials for node-face + * constraints */ - void reinitNodeFaceFace(const PenetrationInfo & info, const bool displaced); + void reinitNodeFace(const Node & secondary_node, + const BoundaryID secondary_boundary, + const PenetrationInfo & info, + const bool displaced); /// solution vector from nonlinear solver const NumericVector * _current_solution; diff --git a/framework/src/systems/NonlinearSystemBase.C b/framework/src/systems/NonlinearSystemBase.C index a4090d3ea865..a9e426fc9bb1 100644 --- a/framework/src/systems/NonlinearSystemBase.C +++ b/framework/src/systems/NonlinearSystemBase.C @@ -1086,18 +1086,32 @@ NonlinearSystemBase::enforceNodalConstraintsJacobian() } void -NonlinearSystemBase::reinitNodeFaceFace(const PenetrationInfo & info, const bool displaced) +NonlinearSystemBase::reinitNodeFace(const Node & secondary_node, + const BoundaryID secondary_boundary, + const PenetrationInfo & info, + const bool displaced) { auto & subproblem = displaced ? static_cast(*_fe_problem.getDisplacedProblem()) : static_cast(_fe_problem); const Elem * primary_elem = info._elem; unsigned int primary_side = info._side_num; - std::vector points; points.push_back(info._closest_point); - // Reinit variables and materials on the primary element's face at the contact point + // *These next steps MUST be done in this order!* + // ADL: This is a Chesterton's fence situation. I don't know which calls exactly the above comment + // is referring to. If I had to guess I would guess just the reinitNodeFace and prepareAssembly + // calls since the former will size the variable's dof indices and then the latter will resize the + // residual/Jacobian based off the variable's cached dof indices size + + // This reinits the variables that exist on the secondary node + _fe_problem.reinitNodeFace(&secondary_node, secondary_boundary, 0); + + // This will set aside residual and jacobian space for the variables that have dofs on + // the secondary node + _fe_problem.prepareAssembly(0); + _fe_problem.setNeighborSubdomainID(primary_elem, 0); // @@ -1176,12 +1190,7 @@ NonlinearSystemBase::setConstraintSecondaryValues(NumericVector & soluti { PenetrationInfo & info = *pen_loc._penetration_info[secondary_node_num]; - // reinit variables at the node - _fe_problem.reinitNodeFace(&secondary_node, secondary_boundary, 0); - - _fe_problem.prepareAssembly(0); - - reinitNodeFaceFace(info, displaced); + reinitNodeFace(secondary_node, secondary_boundary, info, displaced); for (const auto & nfc : constraints) { @@ -1328,16 +1337,7 @@ NonlinearSystemBase::constraintResiduals(NumericVector & residual, bool { PenetrationInfo & info = *pen_loc._penetration_info[secondary_node_num]; - // *These next steps MUST be done in this order!* - - // This reinits the variables that exist on the secondary node - _fe_problem.reinitNodeFace(&secondary_node, secondary_boundary, 0); - - // This will set aside residual and jacobian space for the variables that have dofs on - // the secondary node - _fe_problem.prepareAssembly(0); - - reinitNodeFaceFace(info, displaced); + reinitNodeFace(secondary_node, secondary_boundary, info, displaced); for (const auto & nfc : constraints) { @@ -2285,14 +2285,9 @@ NonlinearSystemBase::constraintJacobians(bool displaced) { PenetrationInfo & info = *pen_loc._penetration_info[secondary_node_num]; - // reinit variables at the node - _fe_problem.reinitNodeFace(&secondary_node, secondary_boundary, 0); - - _fe_problem.prepareAssembly(0); + reinitNodeFace(secondary_node, secondary_boundary, info, displaced); _fe_problem.reinitOffDiagScalars(0); - reinitNodeFaceFace(info, displaced); - for (const auto & nfc : constraints) { if (nfc->isExplicitConstraint()) From ff53b656e35935a75f28b5402e1935ba26245bc2 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Tue, 20 Feb 2024 22:22:43 -0800 Subject: [PATCH 29/31] Turn off debug/valgrind for slow contact petrov-galerkin tests --- modules/contact/test/tests/3d-mortar-contact/tests | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/contact/test/tests/3d-mortar-contact/tests b/modules/contact/test/tests/3d-mortar-contact/tests index 237c92c253d3..467bbdd64d59 100644 --- a/modules/contact/test/tests/3d-mortar-contact/tests +++ b/modules/contact/test/tests/3d-mortar-contact/tests @@ -20,6 +20,8 @@ csvdiff = 'frictionless-mortar-3d_pg_out.csv' requirement = 'The system shall solve a 3D frictionless bouncing block problem with mortar ' 'constraint using Petrov-Galerkin approach.' + method = '!dbg' + valgrind = 'none' [] [frictionless-mortar-3d-test-derivative-trimming] type = 'RunApp' @@ -105,6 +107,8 @@ 'constraint using the Petrov-Galerkin approach.' rel_err = 1.0e-5 abs_zero = 1.0e-5 + method = '!dbg' + valgrind = 'none' [] [frictional-mortar-3d-function] type = 'Exodiff' From d0cf1f6678ae774f3806c4fe08ec38c19c168c61 Mon Sep 17 00:00:00 2001 From: Antonio Recuero Date: Fri, 23 Feb 2024 15:51:03 -0700 Subject: [PATCH 30/31] Address Alex's comments --- .../include/systems/NonlinearSystemBase.h | 2 +- framework/src/systems/NonlinearSystemBase.C | 5 +++- .../timeintegrators/ActuallyExplicitEuler.C | 6 ++++- .../gold/test_balance_overwrite_short_out.csv | 4 +++ .../gold/test_balance_short_out.e | Bin 0 -> 182856 bytes .../test/tests/explicit_dynamics/tests | 23 ++++++++++++++++++ 6 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 modules/contact/test/tests/explicit_dynamics/gold/test_balance_overwrite_short_out.csv create mode 100644 modules/contact/test/tests/explicit_dynamics/gold/test_balance_short_out.e diff --git a/framework/include/systems/NonlinearSystemBase.h b/framework/include/systems/NonlinearSystemBase.h index 78f0e6f60e88..1df376cd5485 100644 --- a/framework/include/systems/NonlinearSystemBase.h +++ b/framework/include/systems/NonlinearSystemBase.h @@ -366,7 +366,7 @@ class NonlinearSystemBase : public SystemBase, public PerfGraphInterface * Called from explicit time stepping to overwrite boundary positions (explicit dynamics). This * will close/assemble the passed-in \p soln after overwrite */ - void overwriteContact(NumericVector & soln); + void overwriteNodeFace(NumericVector & soln); /** * Update active objects of Warehouses owned by NonlinearSystemBase diff --git a/framework/src/systems/NonlinearSystemBase.C b/framework/src/systems/NonlinearSystemBase.C index a9e426fc9bb1..a8df172fec33 100644 --- a/framework/src/systems/NonlinearSystemBase.C +++ b/framework/src/systems/NonlinearSystemBase.C @@ -1398,6 +1398,9 @@ NonlinearSystemBase::constraintResiduals(NumericVector & residual, bool if (has_writable_variables) { + // Explicit contact dynamic constraints write to auxiliary variables and update the old + // displacement solution on the constraint boundaries. Close solutions and update system + // accordingly. _fe_problem.getAuxiliarySystem().solution().close(); _fe_problem.getAuxiliarySystem().system().update(); solutionOld().close(); @@ -1578,7 +1581,7 @@ NonlinearSystemBase::constraintResiduals(NumericVector & residual, bool } void -NonlinearSystemBase::overwriteContact(NumericVector & soln) +NonlinearSystemBase::overwriteNodeFace(NumericVector & soln) { // Overwrite results from integrator in case we have explicit dynamics contact constraints auto & subproblem = _fe_problem.getDisplacedProblem() diff --git a/framework/src/timeintegrators/ActuallyExplicitEuler.C b/framework/src/timeintegrators/ActuallyExplicitEuler.C index 059b53b66ca1..a5539bd81fab 100644 --- a/framework/src/timeintegrators/ActuallyExplicitEuler.C +++ b/framework/src/timeintegrators/ActuallyExplicitEuler.C @@ -95,7 +95,11 @@ ActuallyExplicitEuler::solve() *_nonlinear_implicit_system->solution = _nl.solutionOld(); *_nonlinear_implicit_system->solution += _solution_update; - _nl.overwriteContact(*_nonlinear_implicit_system->solution); + // Constraints may be solved in an uncoupled way. For example, momentum-balance equations may be + // solved node-wise and then the solution (e.g. velocities or positions)can be applied to those + // nodes without solve such constraints on a system level. This strategy is being used for + // node-face contact in explicit dynamics. + _nl.overwriteNodeFace(*_nonlinear_implicit_system->solution); // Enforce contraints on the solution DofMap & dof_map = _nonlinear_implicit_system->get_dof_map(); diff --git a/modules/contact/test/tests/explicit_dynamics/gold/test_balance_overwrite_short_out.csv b/modules/contact/test/tests/explicit_dynamics/gold/test_balance_overwrite_short_out.csv new file mode 100644 index 000000000000..a836fd38e8d8 --- /dev/null +++ b/modules/contact/test/tests/explicit_dynamics/gold/test_balance_overwrite_short_out.csv @@ -0,0 +1,4 @@ +time,accel_58z,contact_pressure_max,critical_time_step,disp_58z,vel_58z +-0.01,0,0,0,0,0 +-0.0095,-1812.9790008838,0,0.0010540925533895,-9.8209767074621e-05,-0.02734245372604 +-0.009,-8.9446679230054e-09,0,0.0010540925533895,-9.9831442361891e-05,4.4723339615027e-14 diff --git a/modules/contact/test/tests/explicit_dynamics/gold/test_balance_short_out.e b/modules/contact/test/tests/explicit_dynamics/gold/test_balance_short_out.e new file mode 100644 index 0000000000000000000000000000000000000000..7a0733277ccfefff9136ad51f5ef205c57147151 GIT binary patch literal 182856 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 XV Date: Sat, 24 Feb 2024 12:44:21 -0700 Subject: [PATCH 31/31] Address Daniel's review --- .../timeintegrators/ActuallyExplicitEuler.C | 2 +- .../actions/ExplicitDynamicsContactAction.C | 30 +++++++------------ 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/framework/src/timeintegrators/ActuallyExplicitEuler.C b/framework/src/timeintegrators/ActuallyExplicitEuler.C index a5539bd81fab..34bb49f7cc91 100644 --- a/framework/src/timeintegrators/ActuallyExplicitEuler.C +++ b/framework/src/timeintegrators/ActuallyExplicitEuler.C @@ -97,7 +97,7 @@ ActuallyExplicitEuler::solve() // Constraints may be solved in an uncoupled way. For example, momentum-balance equations may be // solved node-wise and then the solution (e.g. velocities or positions)can be applied to those - // nodes without solve such constraints on a system level. This strategy is being used for + // nodes without solving for such constraints on a system level. This strategy is being used for // node-face contact in explicit dynamics. _nl.overwriteNodeFace(*_nonlinear_implicit_system->solution); diff --git a/modules/contact/src/actions/ExplicitDynamicsContactAction.C b/modules/contact/src/actions/ExplicitDynamicsContactAction.C index 3030496cdc3d..b730313d289e 100644 --- a/modules/contact/src/actions/ExplicitDynamicsContactAction.C +++ b/modules/contact/src/actions/ExplicitDynamicsContactAction.C @@ -27,12 +27,6 @@ #include "libmesh/petsc_nonlinear_solver.h" #include "libmesh/string_to_enum.h" -// Counter for naming auxiliary kernels -static unsigned int ed_contact_auxkernel_counter = 0; - -// Counter for naming nodal area user objects -static unsigned int ed_contact_userobject_counter = 0; - // Counter for distinct contact action objects static unsigned int ed_contact_action_counter = 0; @@ -124,6 +118,7 @@ ExplicitDynamicsContactAction::act() if (!_problem->getDisplacedProblem()) mooseError("Contact requires updated coordinates. Use the 'displacements = ...' line in the " "Mesh block."); + unsigned int pair_number(0); // Create auxiliary kernels for each contact pairs for (const auto & contact_pair : _boundary_pairs) { @@ -151,8 +146,8 @@ ExplicitDynamicsContactAction::act() params.set>("mapped_primary_gap_offset") = { getParam("mapped_primary_gap_offset")}; params.set("use_displaced_mesh") = true; - std::string name = _name + "_contact_" + Moose::stringify(ed_contact_auxkernel_counter++); - + std::string name = _name + "_contact_" + Moose::stringify(pair_number); + pair_number++; _problem->addAuxKernel("PenetrationAux", name, params); } } @@ -237,10 +232,8 @@ ExplicitDynamicsContactAction::act() var_params.set("execute_on", true) = {EXEC_INITIAL, EXEC_TIMESTEP_BEGIN}; var_params.set("use_displaced_mesh") = true; - _problem->addUserObject("NodalArea", - "nodal_area_object_" + - Moose::stringify(ed_contact_userobject_counter), - var_params); + _problem->addUserObject( + "NodalArea", "nodal_area_object_" + Moose::stringify(name()), var_params); } // Add nodal density and nodal wave speed user { @@ -263,10 +256,8 @@ ExplicitDynamicsContactAction::act() var_params.set("execute_on", true) = {EXEC_INITIAL, EXEC_TIMESTEP_BEGIN}; var_params.set("use_displaced_mesh") = true; - _problem->addUserObject("NodalDensity", - "nodal_density_object_" + - Moose::stringify(ed_contact_userobject_counter), - var_params); + _problem->addUserObject( + "NodalDensity", "nodal_density_object_" + Moose::stringify(name()), var_params); } { // Add wave speed @@ -288,10 +279,8 @@ ExplicitDynamicsContactAction::act() var_params.set("execute_on", true) = {EXEC_INITIAL, EXEC_TIMESTEP_BEGIN}; var_params.set("use_displaced_mesh") = true; - _problem->addUserObject("NodalWaveSpeed", - "nodal_wavespeed_object_" + - Moose::stringify(ed_contact_userobject_counter++), - var_params); + _problem->addUserObject( + "NodalWaveSpeed", "nodal_wavespeed_object_" + Moose::stringify(name()), var_params); } } } @@ -304,6 +293,7 @@ ExplicitDynamicsContactAction::addContactPressureAuxKernel() // Increment counter for contact action objects ed_contact_action_counter++; + // Add auxiliary kernel if we are the last contact action object. if (ed_contact_action_counter == actions.size()) {