NimbleSM
NimbleSM is a solid mechanics simulation code for dynamic systems
Loading...
Searching...
No Matches
nimble::ContactManager Class Reference

#include <nimble_contact_manager.h>

Inheritance diagram for nimble::ContactManager:
nimble::KokkosContactManager nimble::SerialContactManager

Public Types

enum  ProjectionType { UNKNOWN = 0 , NODE_OR_EDGE = 1 , FACE = 2 }
 
typedef enum nimble::ContactManager::ProjectionType PROJECTION_TYPE
 

Public Member Functions

 ContactManager (std::shared_ptr< ContactInterface > interface, nimble::DataManager &data_manager)
 Constructor.
 
virtual ~ContactManager ()=default
 Default destructor.
 
bool ContactEnabled () const
 Indicates whether contact is enabled or not.
 
void SetPenaltyParameter (double penalty_parameter)
 Set the penalty parameter for enforcing contact.
 
void CreateContactEntities (GenesisMesh const &mesh, nimble::VectorCommunicator &mpi_container, std::vector< int > const &primary_block_ids, std::vector< int > const &secondary_block_ids)
 Create contact entities between different blocks.
 
virtual void InitializeContactVisualization (std::string const &contact_visualization_exodus_file_name)
 Initialize contact visualization output.
 
virtual void ContactVisualizationWriteStep (double time_current)
 Write information about contact.
 
virtual void ComputeContactForce (int step, bool debug_output, nimble::Viewify< 2 > contact_force)
 Compute the contact force.
 
size_t numContactFaces () const
 Returns the number of contact faces.
 
size_t numContactNodes () const
 Returns the number of contact nodes.
 
size_t numActiveContactFaces () const
 Returns the number of contact faces "actively" in collision.
 
size_t numActiveContactNodes () const
 Returns the number of contact nodes "actively" in collision.
 
const std::unordered_map< std::string, double > & getTimers ()
 Return timing information.
 
double GetPenaltyForceParam () const noexcept
 Return the penalty coefficient for enforcing contact force.
 

Static Public Member Functions

static void SkinBlocks (GenesisMesh const &mesh, std::vector< int > const &block_ids, int entity_id_offset, std::vector< std::vector< int > > &skin_faces, std::vector< int > &entity_ids)
 
static void RemoveInternalSkinFaces (GenesisMesh const &mesh, std::vector< std::vector< int > > &faces, std::vector< int > &entity_ids)
 
static void SimpleClosestPointProjectionSingle (const ContactEntity &node, const ContactEntity &tri, PROJECTION_TYPE *projection_type, ContactEntity::vertex *closest_point, double &gap, double *normal, double tolerance=1.e-8)
 Compute the projection of a point onto a triangular face.
 
static void Projection (const ContactEntity &node, const ContactEntity &tri, bool &in, double &gap, double *normal, double *barycentric_coordinates, double tolerance=1.e-8)
 Compute the projection of a point onto a triangular face.
 

Protected Member Functions

void WriteVisualizationData (double t)
 Write visualization data to Exodus file at time t.
 
template<typename ArgT>
void CreateContactNodesAndFaces (std::vector< std::vector< int > > const &primary_skin_faces, std::vector< int > const &primary_skin_entity_ids, std::vector< int > const &secondary_node_ids, std::vector< int > const &secondary_node_entity_ids, std::map< int, double > const &secondary_node_char_lens, ArgT &contact_nodes, ArgT &contact_faces) const
 
void BoundingBox (double &x_min, double &x_max, double &y_min, double &y_max, double &z_min, double &z_max) const
 
double BoundingBoxAverageCharacteristicLengthOverAllRanks () const
 
void ComputeContactForce (int step, bool debug_output)
 
void ApplyDisplacements (nimble_kokkos::DeviceVectorNodeView displacement_d)
 
void GetForces (nimble_kokkos::DeviceVectorNodeView contact_force_d) const
 
void BruteForceBoxIntersectionSearch (std::vector< ContactEntity > const &nodes, std::vector< ContactEntity > const &triangles)
 
void ClosestPointProjection (const ContactEntity *nodes, const ContactEntity *triangles, ContactEntity::vertex *closest_points, PROJECTION_TYPE *projection_types, std::size_t num_elements)
 
void ClosestPointProjectionSingle (const ContactEntity &node, const ContactEntity &tri, ContactEntity::vertex *closest_point, PROJECTION_TYPE *projection_type, double tolerance)
 
const ContactEntitygetContactFace (size_t i_face) const
 Returns a read-only reference to contact face entity.
 
const ContactEntitygetContactNode (size_t i_node) const
 Returns a read-only reference to contact node entity.
 
void ResetContactStatus ()
 Reset the contact status for all entities.
 
void ZeroContactForce ()
 Zero the contact forces.
 
template<typename VecType>
void EnforceNodeFaceInteraction (ContactEntity &node, ContactEntity &face, const double gap, const double direction[3], const double facet_coordinates[3], VecType &full_contact_force) const
 
NIMBLE_INLINE_FUNCTION void startTimer (const std::string &str_val)
 
NIMBLE_INLINE_FUNCTION void stopTimer (const std::string &str_val)
 

Protected Attributes

nimble::DataManagerdata_manager_
 
nimble::TimeKeeper total_search_time
 
nimble::TimeKeeper total_enforcement_time
 
std::size_t total_num_contacts = 0
 
PenaltyContactEnforcement enforcement
 
bool contact_enabled_ = false
 
double penalty_parameter_
 
std::vector< int > node_ids_
 
std::vector< double > model_coord_
 
std::vector< double > coord_
 
std::vector< double > force_
 
double contact_visualization_model_coord_bounding_box_ [6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}
 
nimble::GenesisMesh genesis_mesh_for_contact_visualization_
 
nimble::ExodusOutput exodus_output_for_contact_visualization_
 
nimble_kokkos::DeviceIntegerArrayView node_ids_d_ = nimble_kokkos::DeviceIntegerArrayView("contact node_ids_d", 1)
 
nimble_kokkos::DeviceScalarNodeView model_coord_d_ = nimble_kokkos::DeviceScalarNodeView("contact model_coord_d", 1)
 
nimble_kokkos::DeviceScalarNodeView coord_d_ = nimble_kokkos::DeviceScalarNodeView("contact coord_d", 1)
 
nimble_kokkos::DeviceScalarNodeView force_d_ = nimble_kokkos::DeviceScalarNodeView("contact force_d", 1)
 
nimble_kokkos::DeviceContactEntityArrayView contact_faces_d_
 
nimble_kokkos::DeviceContactEntityArrayView contact_nodes_d_
 
nimble_kokkos::HostContactEntityArrayView contact_faces_h_
 
nimble_kokkos::HostContactEntityArrayView contact_nodes_h_
 
nimble_kokkos::ModelDatamodel_data_ = nullptr
 
std::unordered_map< std::string, double > timers_
 
std::shared_ptr< ContactInterfacecontact_interface
 

Member Typedef Documentation

◆ PROJECTION_TYPE

Member Enumeration Documentation

◆ ProjectionType

Enumerator
UNKNOWN 
NODE_OR_EDGE 
FACE 
137 {
138 UNKNOWN = 0,
139 NODE_OR_EDGE = 1,
140 FACE = 2
@ NODE_OR_EDGE
Definition nimble_contact_manager.h:139
@ UNKNOWN
Definition nimble_contact_manager.h:138
@ FACE
Definition nimble_contact_manager.h:140
enum nimble::ContactManager::ProjectionType PROJECTION_TYPE

Constructor & Destructor Documentation

◆ ContactManager()

nimble::ContactManager::ContactManager ( std::shared_ptr< ContactInterface > interface,
nimble::DataManager & data_manager )

Constructor.

180 : data_manager_(data_manager), penalty_parameter_(0.0), contact_interface(std::move(interface))
181{
182}
std::shared_ptr< ContactInterface > contact_interface
Definition nimble_contact_manager.h:475
double penalty_parameter_
Definition nimble_contact_manager.h:441
nimble::DataManager & data_manager_
Definition nimble_contact_manager.h:432

◆ ~ContactManager()

virtual nimble::ContactManager::~ContactManager ( )
virtualdefault

Default destructor.

Member Function Documentation

◆ ApplyDisplacements()

void nimble::ContactManager::ApplyDisplacements ( nimble_kokkos::DeviceVectorNodeView displacement_d)
protected
752{
753 int num_nodes_in_contact_manager = node_ids_d_.extent(0);
754 int num_contact_node_entities = contact_nodes_d_.extent(0);
755 int num_contact_face_entities = contact_faces_d_.extent(0);
756
757 // circumvent lambda *this glitch
763
764 Kokkos::parallel_for(
765 "ContactManager::ApplyDisplacements set coord_d_ vector",
766 num_nodes_in_contact_manager,
767 KOKKOS_LAMBDA(const int i) {
768 int node_id = node_ids(i);
769 coord(3 * i) = model_coord(3 * i) + displacement_d(node_id, 0);
770 coord(3 * i + 1) = model_coord(3 * i + 1) + displacement_d(node_id, 1);
771 coord(3 * i + 2) = model_coord(3 * i + 2) + displacement_d(node_id, 2);
772 });
773
774 Kokkos::parallel_for(
775 "ContactManager::ApplyDisplacements set contact node entity "
776 "displacements",
777 num_contact_node_entities,
778 KOKKOS_LAMBDA(const int i_node) { contact_nodes(i_node).SetCoordinates(coord); });
779
780 Kokkos::parallel_for(
781 "ContactManager::ApplyDisplacements set contact face entity "
782 "displacements",
783 num_contact_face_entities,
784 KOKKOS_LAMBDA(const int i_face) { contact_faces(i_face).SetCoordinates(coord); });
785}
nimble_kokkos::DeviceContactEntityArrayView contact_nodes_d_
Definition nimble_contact_manager.h:459
nimble_kokkos::DeviceIntegerArrayView node_ids_d_
Definition nimble_contact_manager.h:452
nimble_kokkos::DeviceContactEntityArrayView contact_faces_d_
Definition nimble_contact_manager.h:457
nimble_kokkos::DeviceScalarNodeView coord_d_
Definition nimble_contact_manager.h:454
nimble_kokkos::DeviceScalarNodeView model_coord_d_
Definition nimble_contact_manager.h:453
Kokkos::View< nimble::ContactEntity *, nimble_kokkos::kokkos_layout, nimble_kokkos::kokkos_device > DeviceContactEntityArrayView
Definition nimble_kokkos_contact_defs.h:15
Field< FieldType::DeviceScalarNode >::View DeviceScalarNodeView
Definition nimble_kokkos_defs.h:582
Kokkos::View< int *, kokkos_device > DeviceIntegerArrayView
Definition nimble_kokkos_defs.h:606

◆ BoundingBox()

void nimble::ContactManager::BoundingBox ( double & x_min,
double & x_max,
double & y_min,
double & y_max,
double & z_min,
double & z_max ) const
protected
1210{
1211 double big = std::numeric_limits<double>::max();
1212
1213 nimble_kokkos::DeviceScalarNodeView contact_bounding_box_d("contact_bounding_box_d", 6);
1214 nimble_kokkos::HostScalarNodeView contact_bounding_box_h("contact_bounding_box_h", 6);
1215 contact_bounding_box_h(0) = big; // x_min
1216 contact_bounding_box_h(1) = -1.0 * big; // x_max
1217 contact_bounding_box_h(2) = big; // y_min
1218 contact_bounding_box_h(3) = -1.0 * big; // y_max
1219 contact_bounding_box_h(4) = big; // z_min
1220 contact_bounding_box_h(5) = -1.0 * big; // z_max
1221 Kokkos::deep_copy(contact_bounding_box_d, contact_bounding_box_h);
1222
1224 auto contact_vector_size = static_cast<int>(coord_d.extent(0) / 3);
1225
1226 Kokkos::parallel_for(
1227 "Contact Bounding Box", contact_vector_size, KOKKOS_LAMBDA(const int i) {
1228 double x = coord_d(3 * i);
1229 double y = coord_d(3 * i + 1);
1230 double z = coord_d(3 * i + 2);
1231 Kokkos::atomic_min_fetch(&contact_bounding_box_d(0), x);
1232 Kokkos::atomic_max_fetch(&contact_bounding_box_d(1), x);
1233 Kokkos::atomic_min_fetch(&contact_bounding_box_d(2), y);
1234 Kokkos::atomic_max_fetch(&contact_bounding_box_d(3), y);
1235 Kokkos::atomic_min_fetch(&contact_bounding_box_d(4), z);
1236 Kokkos::atomic_max_fetch(&contact_bounding_box_d(5), z);
1237 });
1238
1239 Kokkos::deep_copy(contact_bounding_box_h, contact_bounding_box_d);
1240 x_min = contact_bounding_box_h(0);
1241 x_max = contact_bounding_box_h(1);
1242 y_min = contact_bounding_box_h(2);
1243 y_max = contact_bounding_box_h(3);
1244 z_min = contact_bounding_box_h(4);
1245 z_max = contact_bounding_box_h(5);
1246}
Field< FieldType::HostScalarNode >::View HostScalarNodeView
Definition nimble_kokkos_defs.h:570

◆ BoundingBoxAverageCharacteristicLengthOverAllRanks()

double nimble::ContactManager::BoundingBoxAverageCharacteristicLengthOverAllRanks ( ) const
protected
1250{
1251 double x_min, x_max, y_min, y_max, z_min, z_max;
1252 BoundingBox(x_min, x_max, y_min, y_max, z_min, z_max);
1253 double longest_edge = x_max - x_min;
1254 if ((y_max - y_min) > longest_edge) { longest_edge = y_max - y_min; }
1255 if ((z_max - z_min) > longest_edge) { longest_edge = z_max - z_min; }
1256 double ave_characteristic_length = longest_edge;
1257#ifdef NIMBLE_HAVE_MPI
1258 int num_ranks;
1259 MPI_Comm_size(MPI_COMM_WORLD, &num_ranks);
1260 MPI_Allreduce(&longest_edge, &ave_characteristic_length, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
1261 ave_characteristic_length /= num_ranks;
1262#endif
1263 return ave_characteristic_length;
1264}
void BoundingBox(double &x_min, double &x_max, double &y_min, double &y_max, double &z_min, double &z_max) const
Definition nimble_contact_manager.cc:1208

◆ BruteForceBoxIntersectionSearch()

void nimble::ContactManager::BruteForceBoxIntersectionSearch ( std::vector< ContactEntity > const & nodes,
std::vector< ContactEntity > const & triangles )
protected
1270{
1271 for (int i_node = 0; i_node < nodes.size(); i_node++) {
1272 for (int i_tri = 0; i_tri < triangles.size(); i_tri++) {
1273 // ContactEntity.get_x_min()
1274 }
1275 }
1276}

◆ ClosestPointProjection()

void nimble::ContactManager::ClosestPointProjection ( const ContactEntity * nodes,
const ContactEntity * triangles,
ContactEntity::vertex * closest_points,
PROJECTION_TYPE * projection_types,
std::size_t num_elements )
protected
1285{
1286 // Wolfgang Heidrich, 2005, Computing the Barycentric Coordinates of a
1287 // Projected Point, Journal of Graphics Tools, pp 9-12, 10(3).
1288
1289 double tol = 1.0e-16;
1290
1291 for (unsigned int i_proj = 0; i_proj < num_elements; i_proj++) {
1292 ContactEntity const& node = nodes[i_proj];
1293 ContactEntity const& tri = triangles[i_proj];
1294
1295 ClosestPointProjectionSingle(node, tri, &closest_points[i_proj], &projection_types[i_proj], tol);
1296 }
1297}
void ClosestPointProjectionSingle(const ContactEntity &node, const ContactEntity &tri, ContactEntity::vertex *closest_point, PROJECTION_TYPE *projection_type, double tolerance)
Definition nimble_contact_manager.cc:1300

◆ ClosestPointProjectionSingle()

void nimble::ContactManager::ClosestPointProjectionSingle ( const ContactEntity & node,
const ContactEntity & tri,
ContactEntity::vertex * closest_point,
PROJECTION_TYPE * projection_type,
double tolerance )
protected
1306{
1307 double p[3];
1308 p[0] = node.coord_1_x_;
1309 p[1] = node.coord_1_y_;
1310 p[2] = node.coord_1_z_;
1311
1312 double p1[3];
1313 p1[0] = tri.coord_1_x_;
1314 p1[1] = tri.coord_1_y_;
1315 p1[2] = tri.coord_1_z_;
1316
1317 double p2[3];
1318 p2[0] = tri.coord_2_x_;
1319 p2[1] = tri.coord_2_y_;
1320 p2[2] = tri.coord_2_z_;
1321
1322 double p3[3];
1323 p3[0] = tri.coord_3_x_;
1324 p3[1] = tri.coord_3_y_;
1325 p3[2] = tri.coord_3_z_;
1326
1327 double u[3], v[3], w[3];
1328 for (int i = 0; i < 3; i++) {
1329 u[i] = p2[i] - p1[i];
1330 v[i] = p3[i] - p1[i];
1331 w[i] = p[i] - p1[i];
1332 }
1333
1334 double n[3];
1335 CrossProduct(u, v, n);
1336
1337 double n_squared = n[0] * n[0] + n[1] * n[1] + n[2] * n[2];
1338
1339 double cross[3];
1340 CrossProduct(u, w, cross);
1341 double gamma = (cross[0] * n[0] + cross[1] * n[1] + cross[2] * n[2]) / n_squared;
1342
1343 CrossProduct(w, v, cross);
1344 double beta = (cross[0] * n[0] + cross[1] * n[1] + cross[2] * n[2]) / n_squared;
1345
1346 double alpha = 1 - gamma - beta;
1347
1348 bool alpha_is_zero, alpha_in_range;
1349 (alpha > -tol && alpha < tol) ? alpha_is_zero = true : alpha_is_zero = false;
1350 (alpha > -tol && alpha < 1.0 + tol) ? alpha_in_range = true : alpha_in_range = false;
1351
1352 bool beta_is_zero, beta_in_range;
1353 (beta > -tol && beta < tol) ? beta_is_zero = true : beta_is_zero = false;
1354 (beta > -tol && beta < 1.0 + tol) ? beta_in_range = true : beta_in_range = false;
1355
1356 bool gamma_is_zero, gamma_in_range;
1357 (gamma > -tol && gamma < tol) ? gamma_is_zero = true : gamma_is_zero = false;
1358 (gamma > -tol && gamma < 1.0 + tol) ? gamma_in_range = true : gamma_in_range = false;
1359
1360 if (alpha_in_range && beta_in_range && gamma_in_range) {
1361 closest_point->coords_[0] = alpha * p1[0] + beta * p2[0] + gamma * p3[0];
1362 closest_point->coords_[1] = alpha * p1[1] + beta * p2[1] + gamma * p3[1];
1363 closest_point->coords_[2] = alpha * p1[2] + beta * p2[2] + gamma * p3[2];
1364 if (alpha_is_zero || beta_is_zero || gamma_is_zero) {
1365 *projection_type = PROJECTION_TYPE::NODE_OR_EDGE;
1366 } else {
1367 *projection_type = PROJECTION_TYPE::FACE;
1368 }
1369 } else {
1370 *projection_type = PROJECTION_TYPE::NODE_OR_EDGE;
1371
1372 double x = p1[0] - p[0];
1373 double y = p1[1] - p[1];
1374 double z = p1[2] - p[2];
1375 double distance_squared = x * x + y * y + z * z;
1376
1377 double min_distance_squared = distance_squared;
1378 double min_distance_squared_t;
1379 int min_case = 1;
1380
1381 x = p2[0] - p[0];
1382 y = p2[1] - p[1];
1383 z = p2[2] - p[2];
1384 distance_squared = x * x + y * y + z * z;
1385 if (distance_squared < min_distance_squared) {
1386 min_distance_squared = distance_squared;
1387 min_case = 2;
1388 }
1389
1390 x = p3[0] - p[0];
1391 y = p3[1] - p[1];
1392 z = p3[2] - p[2];
1393 distance_squared = x * x + y * y + z * z;
1394 if (distance_squared < min_distance_squared) {
1395 min_distance_squared = distance_squared;
1396 min_case = 3;
1397 }
1398
1399 double t = PointEdgeClosestPointFindT(p1, p2, p);
1400 if (t > 0.0 && t < 1.0) {
1401 distance_squared = PointEdgeClosestPointFindDistanceSquared(p1, p2, p, t);
1402 if (distance_squared < min_distance_squared) {
1403 min_distance_squared = distance_squared;
1404 min_distance_squared_t = t;
1405 min_case = 4;
1406 }
1407 }
1408
1409 t = PointEdgeClosestPointFindT(p2, p3, p);
1410 if (t > 0.0 && t < 1.0) {
1411 distance_squared = PointEdgeClosestPointFindDistanceSquared(p2, p3, p, t);
1412 if (distance_squared < min_distance_squared) {
1413 min_distance_squared = distance_squared;
1414 min_distance_squared_t = t;
1415 min_case = 5;
1416 }
1417 }
1418
1419 t = PointEdgeClosestPointFindT(p3, p1, p);
1420 if (t > 0.0 && t < 1.0) {
1421 distance_squared = PointEdgeClosestPointFindDistanceSquared(p3, p1, p, t);
1422 if (distance_squared < min_distance_squared) {
1423 min_distance_squared = distance_squared;
1424 min_distance_squared_t = t;
1425 min_case = 6;
1426 }
1427 }
1428
1429 switch (min_case) {
1430 default:
1431 case 1:
1432 closest_point->coords_[0] = p1[0];
1433 closest_point->coords_[1] = p1[1];
1434 closest_point->coords_[2] = p1[2];
1435 break;
1436 case 2:
1437 closest_point->coords_[0] = p2[0];
1438 closest_point->coords_[1] = p2[1];
1439 closest_point->coords_[2] = p2[2];
1440 break;
1441 case 3:
1442 closest_point->coords_[0] = p3[0];
1443 closest_point->coords_[1] = p3[1];
1444 closest_point->coords_[2] = p3[2];
1445 break;
1446 case 4:
1447 closest_point->coords_[0] = p1[0] + (p2[0] - p1[0]) * min_distance_squared_t;
1448 closest_point->coords_[1] = p1[1] + (p2[1] - p1[1]) * min_distance_squared_t;
1449 closest_point->coords_[2] = p1[2] + (p2[2] - p1[2]) * min_distance_squared_t;
1450 break;
1451 case 5:
1452 closest_point->coords_[0] = p2[0] + (p3[0] - p2[0]) * min_distance_squared_t;
1453 closest_point->coords_[1] = p2[1] + (p3[1] - p2[1]) * min_distance_squared_t;
1454 closest_point->coords_[2] = p2[2] + (p3[2] - p2[2]) * min_distance_squared_t;
1455 break;
1456 case 6:
1457 closest_point->coords_[0] = p3[0] + (p1[0] - p3[0]) * min_distance_squared_t;
1458 closest_point->coords_[1] = p3[1] + (p1[1] - p3[1]) * min_distance_squared_t;
1459 closest_point->coords_[2] = p3[2] + (p1[2] - p3[2]) * min_distance_squared_t;
1460 break;
1461 }
1462 }
1463}
NIMBLE_INLINE_FUNCTION double PointEdgeClosestPointFindDistanceSquared(double const p1[], double const p2[], double const p[], double t)
Definition nimble_contact_entity.h:100
NIMBLE_INLINE_FUNCTION double PointEdgeClosestPointFindT(double const p1[], double const p2[], double const p[])
Definition nimble_contact_entity.h:92
NIMBLE_INLINE_FUNCTION void CrossProduct(const ScalarT *const u, const ScalarT *const v, ScalarT *const result)
Definition nimble_utils.h:395

◆ ComputeContactForce() [1/2]

void nimble::ContactManager::ComputeContactForce ( int step,
bool debug_output )
inlineprotected
340 {
341 if (penalty_parameter_ <= 0.0) {
342 throw std::invalid_argument("\nError in ComputeContactForce(), invalid penalty_parameter.\n");
343 }
344 double background_grid_cell_size = BoundingBoxAverageCharacteristicLengthOverAllRanks();
345#ifdef NIMBLE_HAVE_KOKKOS
347#endif
348 }
nimble_kokkos::DeviceScalarNodeView force_d_
Definition nimble_contact_manager.h:455
double BoundingBoxAverageCharacteristicLengthOverAllRanks() const
Definition nimble_contact_manager.cc:1249

◆ ComputeContactForce() [2/2]

void nimble::ContactManager::ComputeContactForce ( int step,
bool debug_output,
nimble::Viewify< 2 > contact_force )
virtual

Compute the contact force.

Parameters
data_manager
step
debug_output
contact_force

Reimplemented in nimble::SerialContactManager.

397{
398 if (penalty_parameter_ <= 0.0) {
399 throw std::invalid_argument("\nError in ComputeContactForce(), invalid penalty_parameter.\n");
400 }
401
402 const auto& parser = data_manager_.GetParser();
403
404 std::cout << " Enter if section .. ComputeContactForce \n";
405 auto model_ptr = data_manager_.GetModelData();
406 auto model_data = dynamic_cast<nimble_kokkos::ModelData*>(model_ptr.get());
407 auto field_ids = data_manager_.GetFieldIDs();
408 auto displacement_d = model_data->GetDeviceVectorNodeData(field_ids.displacement);
409 auto contact_force_d = model_data->GetDeviceVectorNodeData(field_ids.contact_force);
410 Kokkos::deep_copy(contact_force_d, (double)(0.0));
411 //
412 ApplyDisplacements(displacement_d);
413 //
414 ComputeContactForce(step, debug_output);
415 //
416 GetForces(contact_force_d);
417 //
418 auto contact_force_h = model_data->GetHostVectorNodeData(field_ids.contact_force);
419 Kokkos::deep_copy(contact_force_h, contact_force_d);
420
421 auto myVectorCommunicator = data_manager_.GetVectorCommunicator();
422 constexpr int vector_dim = 3;
423 myVectorCommunicator->VectorReduction(vector_dim, contact_force_h);
424}
void GetForces(nimble_kokkos::DeviceVectorNodeView contact_force_d) const
Definition nimble_contact_manager.cc:733
void ApplyDisplacements(nimble_kokkos::DeviceVectorNodeView displacement_d)
Definition nimble_contact_manager.cc:751
virtual void ComputeContactForce(int step, bool debug_output, nimble::Viewify< 2 > contact_force)
Compute the contact force.
Definition nimble_contact_manager.cc:396

◆ ContactEnabled()

bool nimble::ContactManager::ContactEnabled ( ) const
inline

Indicates whether contact is enabled or not.

Returns
Boolean for contact
158 {
159 return contact_enabled_;
160 }
bool contact_enabled_
Definition nimble_contact_manager.h:440

◆ ContactVisualizationWriteStep()

void nimble::ContactManager::ContactVisualizationWriteStep ( double time_current)
virtual

Write information about contact.

Parameters
time_currentCurrent time
597{
598 // copy contact entities from device (*d) to host (*h)
599 Kokkos::deep_copy(contact_nodes_h_, contact_nodes_d_);
600 Kokkos::deep_copy(contact_faces_h_, contact_faces_d_);
601 WriteVisualizationData(time_current);
602}
nimble_kokkos::HostContactEntityArrayView contact_nodes_h_
Definition nimble_contact_manager.h:465
void WriteVisualizationData(double t)
Write visualization data to Exodus file at time t.
Definition nimble_contact_manager.cc:605
nimble_kokkos::HostContactEntityArrayView contact_faces_h_
Definition nimble_contact_manager.h:463

◆ CreateContactEntities()

void nimble::ContactManager::CreateContactEntities ( GenesisMesh const & mesh,
nimble::VectorCommunicator & mpi_container,
std::vector< int > const & primary_block_ids,
std::vector< int > const & secondary_block_ids )

Create contact entities between different blocks.

Parameters
mesh
mpi_container
primary_block_ids
secondary_block_ids
190{
191 int mpi_rank = 0;
192 int num_ranks = 1;
193#ifdef NIMBLE_HAVE_MPI
194 MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
195 MPI_Comm_size(MPI_COMM_WORLD, &num_ranks);
196#endif
197
198 contact_enabled_ = true;
199
200 const double* coord_x = mesh.GetCoordinatesX();
201 const double* coord_y = mesh.GetCoordinatesY();
202 const double* coord_z = mesh.GetCoordinatesZ();
203
204 int max_node_global_id = mesh.GetMaxNodeGlobalId();
205#ifdef NIMBLE_HAVE_MPI
206 int global_max_node_global_id = max_node_global_id;
207 MPI_Allreduce(&max_node_global_id, &global_max_node_global_id, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
208 max_node_global_id = global_max_node_global_id;
209#endif
210
211 // find all the element faces on the primary and secondary contact blocks
212 // the entity ids created here will be used downstream for the contact faces
213 // the contact nodes will not use these entity ids, instead they will just use
214 // the exodus id of the node as the entity id
215 std::vector<std::vector<int>> primary_skin_faces, secondary_skin_faces;
216 std::vector<int> primary_skin_entity_ids, secondary_skin_entity_ids;
217 int contact_entity_id_offset = max_node_global_id; // ensure that there are no duplicate entity ids
218 // between nodes and faces
219 SkinBlocks(mesh, primary_block_ids, contact_entity_id_offset, primary_skin_faces, primary_skin_entity_ids);
220 SkinBlocks(mesh, secondary_block_ids, contact_entity_id_offset, secondary_skin_faces, secondary_skin_entity_ids);
221
222 // remove faces that are along partition boundaries
223 RemoveInternalSkinFaces(mesh, primary_skin_faces, primary_skin_entity_ids);
224 RemoveInternalSkinFaces(mesh, secondary_skin_faces, secondary_skin_entity_ids);
225
226 // create a list of ghosted nodes (e.g., nodes that are owned by a different
227 // processor)
228 std::vector<int> partition_boundary_node_local_ids;
229 std::vector<int> min_rank_containing_partition_boundary_nodes;
230 myVecComm.GetPartitionBoundaryNodeLocalIds(
231 partition_boundary_node_local_ids, min_rank_containing_partition_boundary_nodes);
232
233 std::vector<int> ghosted_node_local_ids;
234 for (int i = 0; i < partition_boundary_node_local_ids.size(); i++) {
235 if (min_rank_containing_partition_boundary_nodes[i] != mpi_rank) {
236 ghosted_node_local_ids.push_back(partition_boundary_node_local_ids[i]);
237 }
238 }
239
240 // construct containers for the subset of the model that is involved with
241 // contact this constitutes a submodel that is stored in the ContactManager
242 std::set<int> node_ids_set;
243 for (auto const& face : primary_skin_faces) {
244 for (auto const& id : face) { node_ids_set.insert(id); }
245 }
246 for (auto const& face : secondary_skin_faces) {
247 for (auto const& id : face) { node_ids_set.insert(id); }
248 }
249 node_ids_ = std::vector<int>(node_ids_set.begin(), node_ids_set.end());
250
251 std::map<int, int> genesis_mesh_node_id_to_contact_submodel_id;
252 for (unsigned int i_node = 0; i_node < node_ids_.size(); ++i_node) {
253 genesis_mesh_node_id_to_contact_submodel_id[node_ids_[i_node]] = i_node;
254 }
255
256 // replace the node ids that correspond to the genesis mesh
257 // with node ids that correspond to the contact submodel
258 for (auto& primary_skin_face : primary_skin_faces) {
259 for (int& i_node : primary_skin_face) {
260 int genesis_mesh_node_id = i_node;
261 i_node = genesis_mesh_node_id_to_contact_submodel_id.at(genesis_mesh_node_id);
262 }
263 }
264 for (auto& secondary_skin_face : secondary_skin_faces) {
265 for (int& i_node : secondary_skin_face) {
266 int genesis_mesh_node_id = i_node;
267 i_node = genesis_mesh_node_id_to_contact_submodel_id.at(genesis_mesh_node_id);
268 }
269 }
270
271 // create a list of ghosted nodes in the contact submodel
272 std::vector<int> ghosted_contact_node_ids;
273 for (auto node_id : ghosted_node_local_ids) {
274 auto it = genesis_mesh_node_id_to_contact_submodel_id.find(node_id);
275 if (it != genesis_mesh_node_id_to_contact_submodel_id.end()) { ghosted_contact_node_ids.push_back(it->second); }
276 }
277
278 // allocate data for the contact submodel
279 int array_len = 3 * node_ids_.size();
280 model_coord_.resize(array_len);
281 coord_.resize(array_len);
282 force_.resize(array_len, 0.0);
283 for (unsigned int i_node = 0; i_node < node_ids_.size(); i_node++) {
284 model_coord_[3 * i_node] = coord_[3 * i_node] = coord_x[node_ids_[i_node]];
285 model_coord_[3 * i_node + 1] = coord_[3 * i_node + 1] = coord_y[node_ids_[i_node]];
286 model_coord_[3 * i_node + 2] = coord_[3 * i_node + 2] = coord_z[node_ids_[i_node]];
287 }
288
289 // Store nodes in secondary faces
290 // Create a list of nodes and their characteristic lengths
291 const int* genesis_node_global_ids = mesh.GetNodeGlobalIds();
292 std::vector<int> secondary_node_ids;
293 std::vector<int> secondary_node_entity_ids;
294 std::map<int, double> secondary_node_char_lens;
295 for (auto& face : secondary_skin_faces) {
296 int num_nodes_in_face = static_cast<int>(face.size());
297 // determine a characteristic length based on max edge length
298 double max_edge_length_square = std::numeric_limits<double>::lowest();
299 for (int i = 0; i < num_nodes_in_face; ++i) {
300 int node_id_1 = face[i];
301 int node_id_2 = face[0];
302 if (i + 1 < num_nodes_in_face) { node_id_2 = face[i + 1]; }
303 double edge_length_square =
304 (coord_[3 * node_id_2] - coord_[3 * node_id_1]) * (coord_[3 * node_id_2] - coord_[3 * node_id_1]) +
305 (coord_[3 * node_id_2 + 1] - coord_[3 * node_id_1 + 1]) *
306 (coord_[3 * node_id_2 + 1] - coord_[3 * node_id_1 + 1]) +
307 (coord_[3 * node_id_2 + 2] - coord_[3 * node_id_1 + 2]) *
308 (coord_[3 * node_id_2 + 2] - coord_[3 * node_id_1 + 2]);
309 if (edge_length_square > max_edge_length_square) { max_edge_length_square = edge_length_square; }
310 }
311 double characteristic_length = sqrt(max_edge_length_square);
312 for (int i_node = 0; i_node < num_nodes_in_face; i_node++) {
313 int node_id = face[i_node];
314 // omit ghosted nodes
315 if (std::find(ghosted_contact_node_ids.begin(), ghosted_contact_node_ids.end(), node_id) ==
316 ghosted_contact_node_ids.end()) {
317 if (std::find(secondary_node_ids.begin(), secondary_node_ids.end(), node_id) == secondary_node_ids.end()) {
318 secondary_node_ids.push_back(node_id);
319 // note the mapping from contact manager local id to real FEM mesh
320 // local id to real FEM mesh global id
321 int contact_node_entity_id = genesis_node_global_ids[node_ids_[node_id]] + 1;
322 secondary_node_entity_ids.push_back(contact_node_entity_id);
323 secondary_node_char_lens[node_id] = characteristic_length;
324 } else {
325 // always use the maximum characteristic length
326 // this requires a parallel sync
327 if (secondary_node_char_lens[node_id] < characteristic_length) {
328 secondary_node_char_lens[node_id] = characteristic_length;
329 }
330 }
331 }
332 }
333 }
334
335
336 int num_contact_nodes = secondary_node_ids.size();
337 int num_contact_faces = 4 * primary_skin_faces.size();
338
339 nimble_kokkos::HostIntegerArrayView node_ids_h("contact_node_ids_h", node_ids_.size());
340 for (unsigned int i_node = 0; i_node < node_ids_.size(); i_node++) { node_ids_h[i_node] = node_ids_[i_node]; }
341
342 nimble_kokkos::HostScalarNodeView model_coord_h("contact_model_coord_h", array_len);
343 for (unsigned int i_node = 0; i_node < node_ids_.size(); i_node++) {
344 model_coord_h[3 * i_node] = coord_x[node_ids_[i_node]];
345 model_coord_h[3 * i_node + 1] = coord_y[node_ids_[i_node]];
346 model_coord_h[3 * i_node + 2] = coord_z[node_ids_[i_node]];
347 }
348
349 Kokkos::resize(node_ids_d_, node_ids_.size());
350 Kokkos::resize(model_coord_d_, array_len);
351 Kokkos::resize(coord_d_, array_len);
352 Kokkos::resize(force_d_, array_len);
353
354 Kokkos::deep_copy(node_ids_d_, node_ids_h);
355 Kokkos::deep_copy(model_coord_d_, model_coord_h);
356 Kokkos::deep_copy(coord_d_, model_coord_h);
357 Kokkos::deep_copy(force_d_, 0.0);
358
359 //
360 // Create Kokkos::View objects for contact nodes and faces
361 //
362
363 Kokkos::resize(contact_nodes_h_, num_contact_nodes);
364 Kokkos::resize(contact_faces_h_, num_contact_faces);
366 primary_skin_faces,
367 primary_skin_entity_ids,
368 secondary_node_ids,
369 secondary_node_entity_ids,
370 secondary_node_char_lens,
373
374 Kokkos::resize(contact_nodes_d_, num_contact_nodes);
375 Kokkos::resize(contact_faces_d_, num_contact_faces);
376 Kokkos::deep_copy(contact_nodes_d_, contact_nodes_h_);
377 Kokkos::deep_copy(contact_faces_d_, contact_faces_h_);
378
379#ifdef NIMBLE_HAVE_MPI
380 std::vector<int> input(2);
381 std::vector<int> output(2);
382 input[0] = num_contact_faces;
383 input[1] = num_contact_nodes;
384 MPI_Reduce(input.data(), output.data(), input.size(), MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
385 num_contact_faces = output[0];
386 num_contact_nodes = output[1];
387#endif
388 if (mpi_rank == 0) {
389 std::cout << "Contact initialization:" << std::endl;
390 std::cout << " number of triangular contact facets (primary blocks): " << num_contact_faces << std::endl;
391 std::cout << " number of contact nodes (secondary blocks): " << num_contact_nodes << "\n" << std::endl;
392 }
393}
std::vector< double > coord_
Definition nimble_contact_manager.h:445
std::vector< double > model_coord_
Definition nimble_contact_manager.h:444
static void SkinBlocks(GenesisMesh const &mesh, std::vector< int > const &block_ids, int entity_id_offset, std::vector< std::vector< int > > &skin_faces, std::vector< int > &entity_ids)
Definition nimble_contact_manager.cc:789
std::vector< int > node_ids_
Definition nimble_contact_manager.h:443
void CreateContactNodesAndFaces(std::vector< std::vector< int > > const &primary_skin_faces, std::vector< int > const &primary_skin_entity_ids, std::vector< int > const &secondary_node_ids, std::vector< int > const &secondary_node_entity_ids, std::map< int, double > const &secondary_node_char_lens, ArgT &contact_nodes, ArgT &contact_faces) const
Definition nimble_contact_manager.cc:1045
static void RemoveInternalSkinFaces(GenesisMesh const &mesh, std::vector< std::vector< int > > &faces, std::vector< int > &entity_ids)
Definition nimble_contact_manager.cc:938
std::vector< double > force_
Definition nimble_contact_manager.h:446
Kokkos::View< int *, kokkos_host > HostIntegerArrayView
Definition nimble_kokkos_defs.h:577

◆ CreateContactNodesAndFaces()

template<typename ArgT>
void nimble::ContactManager::CreateContactNodesAndFaces ( std::vector< std::vector< int > > const & primary_skin_faces,
std::vector< int > const & primary_skin_entity_ids,
std::vector< int > const & secondary_node_ids,
std::vector< int > const & secondary_node_entity_ids,
std::map< int, double > const & secondary_node_char_lens,
ArgT & contact_nodes,
ArgT & contact_faces ) const
protected
1053{
1054 int index = 0;
1055
1056 // convert primary faces to triangular facets
1057 for (unsigned int i_face = 0; i_face < primary_skin_faces.size(); i_face++) {
1058 auto face = primary_skin_faces[i_face];
1059
1060 int num_nodes_in_face = static_cast<int>(face.size());
1061 if (num_nodes_in_face != 4) {
1063 "\nError in ContactManager::CreateContactNodesAndFaces(), invalid "
1064 "number of face nodes.\n");
1065 }
1066
1067 // determine a characteristic length based on max edge length
1068 double max_edge_length = std::numeric_limits<double>::lowest();
1069 for (int i = 0; i < num_nodes_in_face; ++i) {
1070 int node_id_1 = face[i];
1071 int node_id_2 = face[0];
1072 if (i + 1 < num_nodes_in_face) { node_id_2 = face[i + 1]; }
1073 double edge_length = sqrt(
1074 (coord_[3 * node_id_2] - coord_[3 * node_id_1]) * (coord_[3 * node_id_2] - coord_[3 * node_id_1]) +
1075 (coord_[3 * node_id_2 + 1] - coord_[3 * node_id_1 + 1]) *
1076 (coord_[3 * node_id_2 + 1] - coord_[3 * node_id_1 + 1]) +
1077 (coord_[3 * node_id_2 + 2] - coord_[3 * node_id_1 + 2]) *
1078 (coord_[3 * node_id_2 + 2] - coord_[3 * node_id_1 + 2]));
1079 if (edge_length > max_edge_length) { max_edge_length = edge_length; }
1080 }
1081 double characteristic_length = max_edge_length;
1082
1083 // create a node at the barycenter of the face
1084 double fictitious_node[3] = {0.0, 0.0, 0.0};
1085 for (int i = 0; i < num_nodes_in_face; ++i) {
1086 int node_id = face[i];
1087 for (int j = 0; j < 3; j++) { fictitious_node[j] += coord_[3 * node_id + j]; }
1088 }
1089 for (double& j : fictitious_node) { j /= num_nodes_in_face; }
1090
1091 // Create a map for transfering displacements and contact forces from the
1092 // nodes on the triangle patch to the contact manager data structures. There
1093 // is a 1-to-1 transfer for the two real nodes, and for the fictitious node
1094 // the mapping applies an equal fraction of the displacement/force at the
1095 // fictitious node to each for four real nodes in the original mesh face
1096 int node_ids_for_fictitious_node[4];
1097 for (int i = 0; i < 4; i++) { node_ids_for_fictitious_node[i] = face[i]; }
1098
1099 double model_coord[9];
1100 int node_id_1, node_id_2, entity_id;
1101
1102 // triangle node_0, node_1, fictitious_node
1103 node_id_1 = face[0];
1104 node_id_2 = face[1];
1105 for (int i = 0; i < 3; ++i) {
1106 model_coord[i] = coord_[3 * node_id_1 + i];
1107 model_coord[3 + i] = coord_[3 * node_id_2 + i];
1108 }
1109 model_coord[6] = fictitious_node[0];
1110 model_coord[7] = fictitious_node[1];
1111 model_coord[8] = fictitious_node[2];
1112 entity_id = primary_skin_entity_ids[i_face];
1113 entity_id |= 0; // triangle ordinal
1114 contact_faces[index] = ContactEntity(
1116 entity_id,
1117 index,
1118 model_coord,
1119 characteristic_length,
1120 node_id_1,
1121 node_id_2,
1122 node_ids_for_fictitious_node);
1123 ++index;
1124
1125 // triangle node_1, node_2, fictitious_node
1126 node_id_1 = face[1];
1127 node_id_2 = face[2];
1128 for (int i = 0; i < 3; ++i) {
1129 model_coord[i] = coord_[3 * node_id_1 + i];
1130 model_coord[3 + i] = coord_[3 * node_id_2 + i];
1131 }
1132 model_coord[6] = fictitious_node[0];
1133 model_coord[7] = fictitious_node[1];
1134 model_coord[8] = fictitious_node[2];
1135 entity_id = primary_skin_entity_ids[i_face];
1136 entity_id |= 1; // triangle ordinal
1137 contact_faces[index] = ContactEntity(
1139 entity_id,
1140 index,
1141 model_coord,
1142 characteristic_length,
1143 node_id_1,
1144 node_id_2,
1145 node_ids_for_fictitious_node);
1146 ++index;
1147
1148 // triangle node_2, node_3, fictitious_node
1149 node_id_1 = face[2];
1150 node_id_2 = face[3];
1151 for (int i = 0; i < 3; ++i) {
1152 model_coord[i] = coord_[3 * node_id_1 + i];
1153 model_coord[3 + i] = coord_[3 * node_id_2 + i];
1154 }
1155 model_coord[6] = fictitious_node[0];
1156 model_coord[7] = fictitious_node[1];
1157 model_coord[8] = fictitious_node[2];
1158 entity_id = primary_skin_entity_ids[i_face];
1159 entity_id |= 2; // triangle ordinal
1160 contact_faces[index] = ContactEntity(
1162 entity_id,
1163 index,
1164 model_coord,
1165 characteristic_length,
1166 node_id_1,
1167 node_id_2,
1168 node_ids_for_fictitious_node);
1169 ++index;
1170
1171 // triangle node_3, node_0, fictitious_node
1172 node_id_1 = face[3];
1173 node_id_2 = face[0];
1174 for (int i = 0; i < 3; ++i) {
1175 model_coord[i] = coord_[3 * node_id_1 + i];
1176 model_coord[3 + i] = coord_[3 * node_id_2 + i];
1177 }
1178 model_coord[6] = fictitious_node[0];
1179 model_coord[7] = fictitious_node[1];
1180 model_coord[8] = fictitious_node[2];
1181 entity_id = primary_skin_entity_ids[i_face];
1182 entity_id |= 3; // triangle ordinal
1183 contact_faces[index] = ContactEntity(
1185 entity_id,
1186 index,
1187 model_coord,
1188 characteristic_length,
1189 node_id_1,
1190 node_id_2,
1191 node_ids_for_fictitious_node);
1192 ++index;
1193 }
1194
1195 // Secondary node entities
1196 for (unsigned int i_node = 0; i_node < secondary_node_ids.size(); ++i_node) {
1197 int node_id = secondary_node_ids.at(i_node);
1198 int entity_id = secondary_node_entity_ids.at(i_node);
1199 double characteristic_length = secondary_node_char_lens.at(node_id);
1200 double model_coord[3];
1201 for (int i = 0; i < 3; ++i) { model_coord[i] = coord_[3 * node_id + i]; }
1202 contact_nodes[i_node] =
1203 ContactEntity(ContactEntity::NODE, entity_id, i_node, model_coord, characteristic_length, node_id);
1204 }
1205}
@ TRIANGLE
Definition nimble_contact_entity.h:122
@ NODE
Definition nimble_contact_entity.h:121
#define NIMBLE_ABORT(...)
Definition nimble_macros.h:87

◆ EnforceNodeFaceInteraction()

template<typename VecType>
void nimble::ContactManager::EnforceNodeFaceInteraction ( ContactEntity & node,
ContactEntity & face,
const double gap,
const double direction[3],
const double facet_coordinates[3],
VecType & full_contact_force ) const
inlineprotected
409 {
410 enforcement.EnforceContact<VecType>(node, face, gap, direction, facet_coordinates, full_contact_force);
411 }
PenaltyContactEnforcement enforcement
Definition nimble_contact_manager.h:438

◆ getContactFace()

const ContactEntity & nimble::ContactManager::getContactFace ( size_t i_face) const
protected

Returns a read-only reference to contact face entity.

Parameters
i_faceIndex of the contact face
Returns
Read-only reference to contact face entity
Note
When using Kokkos, the data is extracted from the "host".
717{
718 return contact_faces_h_(iface);
719}

◆ getContactNode()

const ContactEntity & nimble::ContactManager::getContactNode ( size_t i_node) const
protected

Returns a read-only reference to contact node entity.

Parameters
i_nodeIndex of the contact node
Returns
Read-only reference to contact node entity
Note
When using Kokkos, the data is extracted from the "host".
723{
724 return contact_nodes_h_(inode);
725}

◆ GetForces()

void nimble::ContactManager::GetForces ( nimble_kokkos::DeviceVectorNodeView contact_force_d) const
protected
734{
735 int num_nodes_in_contact_manager = node_ids_d_.extent(0);
736
737 // circumvent lambda *this glitch
740
741 Kokkos::parallel_for(
742 "ContactManager::GetForces", num_nodes_in_contact_manager, KOKKOS_LAMBDA(const int i) {
743 int node_id = node_ids(i);
744 contact_force_d(node_id, 0) = force(3 * i);
745 contact_force_d(node_id, 1) = force(3 * i + 1);
746 contact_force_d(node_id, 2) = force(3 * i + 2);
747 });
748}

◆ GetPenaltyForceParam()

double nimble::ContactManager::GetPenaltyForceParam ( ) const
inlinenoexcept

Return the penalty coefficient for enforcing contact force.

Returns
Penalty value
304 {
305 return enforcement.penalty;
306 }

◆ getTimers()

const std::unordered_map< std::string, double > & nimble::ContactManager::getTimers ( )

Return timing information.

Returns
Reference to map of strings to time value
1666{
1667 timers_.clear();
1668#ifdef NIMBLE_TIME_CONTACT
1669 for (auto st_pair : watch_.timers_) {
1670 auto name = st_pair.first;
1671 timers_.insert(std::make_pair(name, st_pair.second.GetElapsedTime()));
1672 }
1673#endif
1674 return timers_;
1675}
std::unordered_map< std::string, double > timers_
Definition nimble_contact_manager.h:470

◆ InitializeContactVisualization()

void nimble::ContactManager::InitializeContactVisualization ( std::string const & contact_visualization_exodus_file_name)
virtual

Initialize contact visualization output.

Parameters
contact_visualization_exodus_file_nameFilename for output

Initialize Exodus file, and prepare it to contain Contact Visualization data

Parameters
contact_visualization_exodus_file_name
433{
434 // Exodus id convention for contact visualization:
435 //
436 // Both node and face contact entities have a unique, parallel-consistent id
437 // called contact_entity_global_id_. For faces, the contact_entity_global_id_
438 // a bit-wise combination of the global exodus id of the parent element, plus
439 // the face ordinal (1-6), plus the triangle ordinal (1-4). For nodes, the
440 // contact_entity_global_id_ is the exodus global node id for the node in the
441 // original FEM mesh.
442 //
443 // For visualization output, we need unique, parallel-consistent node ids and
444 // element ids. For the faces, the contact_entity_global_id_ is used as the
445 // element id, and the node ids are constructed here. For nodes, the
446 // contact_entity_global_id_ is used for both the node id and the element id
447 // (sphere element containing a single node).
448 //
449 // For the MPI bounding boxes, both the node ids and the element id are
450 // constructed here.
451 //
452 // contact faces:
453 // node ids are (3 * contact_entity_global_id_ + max_contact_entity_id +
454 // 9,
455 // 3 * contact_entity_global_id_ + max_contact_entity_id +
456 // 10, 3 * contact_entity_global_id_ + max_contact_entity_id
457 // + 11)
458 // element id is contact_entity_global_id_
459 // contact nodes
460 // node id is contact_entity_global_id_
461 // element id contact_entity_global_id_
462 // mpi partition bounding box:
463 // nodes id are (3 * max_contact_entity_id + 1,
464 // 3 * max_contact_entity_id + 2,
465 // 3 * max_contact_entity_id + 3,
466 // 3 * max_contact_entity_id + 4,
467 // 3 * max_contact_entity_id + 5,
468 // 3 * max_contact_entity_id + 6,
469 // 3 * max_contact_entity_id + 7,
470 // 3 * max_contact_entity_id + 8)
471 // element id max_contact_entity_id + 1
472
473 // Local constants for readability
474 const size_t nnodes = numContactNodes();
475 const size_t nfaces = numContactFaces();
476 nimble::GenesisMesh& mesh = genesis_mesh_for_contact_visualization_;
477 nimble::ExodusOutput& out = exodus_output_for_contact_visualization_;
478
479 // determine the maximum contact entity global id over all MPI partitions
480 int max_contact_entity_id = 0;
481 for (int i_face = 0; i_face < nfaces; i_face++) {
482 ContactEntity const& face = getContactFace(i_face);
483 if (face.contact_entity_global_id_ > max_contact_entity_id) {
484 max_contact_entity_id = face.contact_entity_global_id_;
485 }
486 }
487 for (int i_node = 0; i_node < nnodes; i_node++) {
488 ContactEntity const& node = getContactNode(i_node);
489 if (node.contact_entity_global_id_ > max_contact_entity_id) {
490 max_contact_entity_id = node.contact_entity_global_id_;
491 }
492 }
493#ifdef NIMBLE_HAVE_MPI
494 int mpi_rank, num_ranks;
495 MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
496 MPI_Comm_size(MPI_COMM_WORLD, &num_ranks);
497 int global_max_contact_entity_id = max_contact_entity_id;
498 MPI_Allreduce(&max_contact_entity_id, &global_max_contact_entity_id, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
499 max_contact_entity_id = global_max_contact_entity_id;
500#endif
501
502 std::vector<int> node_global_id;
503 std::vector<double> node_x;
504 std::vector<double> node_y;
505 std::vector<double> node_z;
506 std::vector<int> elem_global_id;
507 std::vector<int> block_ids;
508 std::map<int, std::string> block_names;
509 std::map<int, std::vector<int>> block_elem_global_ids;
510 std::map<int, int> block_num_nodes_per_elem;
511 std::map<int, std::vector<int>> block_elem_connectivity;
512
513 // first block contains the contact faces
514 int block_id = 1;
515 block_ids.push_back(block_id);
516 block_names[block_id] = "contact_faces";
517 block_elem_global_ids[block_id] = std::vector<int>();
518 block_num_nodes_per_elem[block_id] = 3;
519 block_elem_connectivity[block_id] = std::vector<int>();
520
521 int node_index(0);
522 for (int i_face = 0; i_face < nfaces; i_face++) {
523 ContactEntity const& face = getContactFace(i_face);
524 int contact_entity_global_id = face.contact_entity_global_id_;
525 node_global_id.push_back(3 * contact_entity_global_id + max_contact_entity_id + 9);
526 node_x.push_back(face.coord_1_x_);
527 node_y.push_back(face.coord_1_y_);
528 node_z.push_back(face.coord_1_z_);
529 block_elem_connectivity[block_id].push_back(node_index++);
530 node_global_id.push_back(3 * contact_entity_global_id + max_contact_entity_id + 10);
531 node_x.push_back(face.coord_2_x_);
532 node_y.push_back(face.coord_2_y_);
533 node_z.push_back(face.coord_2_z_);
534 block_elem_connectivity[block_id].push_back(node_index++);
535 node_global_id.push_back(3 * contact_entity_global_id + max_contact_entity_id + 11);
536 node_x.push_back(face.coord_3_x_);
537 node_y.push_back(face.coord_3_y_);
538 node_z.push_back(face.coord_3_z_);
539 block_elem_connectivity[block_id].push_back(node_index++);
540 elem_global_id.push_back(contact_entity_global_id);
541 }
542
543 // second block contains the contact nodes
544 block_id = 2;
545 block_ids.push_back(block_id);
546 block_names[block_id] = "contact_nodes";
547 block_elem_global_ids[block_id] = std::vector<int>();
548 block_num_nodes_per_elem[block_id] = 1;
549 block_elem_connectivity[block_id] = std::vector<int>();
550
551 for (int i_node = 0; i_node < nnodes; i_node++) {
552 ContactEntity const& node = getContactNode(i_node);
553 int contact_entity_global_id = node.contact_entity_global_id_;
554 node_global_id.push_back(contact_entity_global_id);
555 node_x.push_back(node.coord_1_x_);
556 node_y.push_back(node.coord_1_y_);
557 node_z.push_back(node.coord_1_z_);
558 block_elem_connectivity[block_id].push_back(node_index++);
559 elem_global_id.push_back(contact_entity_global_id);
560 }
561
562 // third block is the bounding box for this mpi rank
563 mesh.Initialize(
564 "contact_visualization",
565 node_global_id,
566 node_x,
567 node_y,
568 node_z,
569 elem_global_id,
570 block_ids,
571 block_names,
572 block_elem_global_ids,
573 block_num_nodes_per_elem,
574 block_elem_connectivity);
575
576 out.Initialize(contact_visualization_exodus_file_name, mesh);
577
578 std::vector<std::string> global_data_labels;
579 global_data_labels.emplace_back("num_contacts");
580 std::vector<std::string> node_data_labels_for_output;
581 node_data_labels_for_output.emplace_back("displacement_x");
582 node_data_labels_for_output.emplace_back("displacement_y");
583 node_data_labels_for_output.emplace_back("displacement_z");
584 node_data_labels_for_output.emplace_back("contact_status");
585 std::map<int, std::vector<std::string>> elem_data_labels_for_output;
586 std::map<int, std::vector<std::string>> derived_elem_data_labels;
587 for (auto iblock : block_ids) {
588 elem_data_labels_for_output[iblock] = std::vector<std::string>();
589 derived_elem_data_labels[iblock] = std::vector<std::string>();
590 }
592 mesh, global_data_labels, node_data_labels_for_output, elem_data_labels_for_output, derived_elem_data_labels);
593}
const ContactEntity & getContactNode(size_t i_node) const
Returns a read-only reference to contact node entity.
Definition nimble_contact_manager.cc:722
size_t numContactFaces() const
Returns the number of contact faces.
Definition nimble_contact_manager.cc:680
const ContactEntity & getContactFace(size_t i_face) const
Returns a read-only reference to contact face entity.
Definition nimble_contact_manager.cc:716
nimble::GenesisMesh genesis_mesh_for_contact_visualization_
Definition nimble_contact_manager.h:449
nimble::ExodusOutput exodus_output_for_contact_visualization_
Definition nimble_contact_manager.h:450
size_t numContactNodes() const
Returns the number of contact nodes.
Definition nimble_contact_manager.cc:686
void Initialize(std::string const &filename, GenesisMesh const &genesis_mesh)
Definition nimble_exodus_output.cc:65
void InitializeDatabase(GenesisMesh const &genesis_mesh, std::vector< std::string > const &global_data_names, std::vector< std::string > const &node_data_names, std::map< int, std::vector< std::string > > const &elem_data_names, std::map< int, std::vector< std::string > > const &derived_elem_data_names)
Definition nimble_exodus_output.cc:79
void Initialize(std::string const &file_name, std::vector< int > const &node_global_id, std::vector< double > const &node_x, std::vector< double > const &node_y, std::vector< double > const &node_z, std::vector< int > const &elem_global_id, std::vector< int > const &block_ids, std::map< int, std::string > const &block_names, std::map< int, std::vector< int > > const &block_elem_global_ids, std::map< int, int > const &block_num_nodes_per_elem, std::map< int, std::vector< int > > const &block_elem_connectivity)
Definition nimble_genesis_mesh.cc:430

◆ numActiveContactFaces()

size_t nimble::ContactManager::numActiveContactFaces ( ) const

Returns the number of contact faces "actively" in collision.

Returns
Number of active contact faces
Note
When using Kokkos, the data is extracted from the "host".
693{
694 std::size_t num_contacts = 0;
695 const std::size_t total_num_contact_faces = numContactFaces();
696 for (std::size_t i = 0; i < total_num_contact_faces; ++i) {
697 const auto &myface = getContactFace(i);
698 num_contacts += static_cast<size_t>(myface.contact_status());
699 }
700 return num_contacts;
701}

◆ numActiveContactNodes()

size_t nimble::ContactManager::numActiveContactNodes ( ) const

Returns the number of contact nodes "actively" in collision.

Returns
Number of active contact nodes
Note
When using Kokkos, the data is extracted from the "host".
705{
706 std::size_t num_contacts = 0;
707 const std::size_t total_num_contact_nodes = numContactNodes();
708 for (std::size_t i = 0; i < total_num_contact_nodes; ++i) {
709 const auto &mynode = getContactNode(i);
710 num_contacts += static_cast<size_t>(mynode.contact_status());
711 }
712 return num_contacts;
713}

◆ numContactFaces()

size_t nimble::ContactManager::numContactFaces ( ) const

Returns the number of contact faces.

Returns
Number of contact faces
Note
When using Kokkos, the data is extracted from the "host".
681{
682 return contact_faces_h_.extent(0);
683}

◆ numContactNodes()

size_t nimble::ContactManager::numContactNodes ( ) const

Returns the number of contact nodes.

Returns
Number of contact nodes
Note
When using Kokkos, the data is extracted from the "host".
687{
688 return contact_nodes_h_.extent(0);
689}

◆ Projection()

void nimble::ContactManager::Projection ( const ContactEntity & node,
const ContactEntity & tri,
bool & in,
double & gap,
double * normal,
double * barycentric_coordinates,
double tol = 1.e-8 )
static

Compute the projection of a point onto a triangular face.

Projection of node on the plane defined by the triangular face.

Parameters
[in]nodeNode to project
[in]triFace to project onto
[out]inTrue if node is inside the facet
[out]gapNormal distance when the point projects onto the face
[out]normalUnit normal vector outside of face
[out]barycentric_coordinatesProjection of node on facet
[in]toleranceTolerance to fit into the face (defaut value = 1e-08)
[in]nodeNode to project
[in]triTriangular face defining the plane
[out]inBoolean flag whether projection is inside the triangular face
[out]gap
[out]normalUnit outer normal to the triangular face
[out]barycentric_coordinatesBarycentric coordinates of projection
[in]tolTolerance
Note
Only the coordinates of ContactEntity node are accessed.
1558{
1559 // node
1560 double p[3];
1561 p[0] = node.coord_1_x_;
1562 p[1] = node.coord_1_y_;
1563 p[2] = node.coord_1_z_;
1564 // facet
1565 double p1[3];
1566 p1[0] = tri.coord_1_x_;
1567 p1[1] = tri.coord_1_y_;
1568 p1[2] = tri.coord_1_z_;
1569 double p2[3];
1570 p2[0] = tri.coord_2_x_;
1571 p2[1] = tri.coord_2_y_;
1572 p2[2] = tri.coord_2_z_;
1573 double p3[3];
1574 p3[0] = tri.coord_3_x_;
1575 p3[1] = tri.coord_3_y_;
1576 p3[2] = tri.coord_3_z_;
1577 // u: edge, v: edge, w: vertex to edge
1578 double u[3], v[3], w[3];
1579 for (int i = 0; i < 3; i++) {
1580 u[i] = p2[i] - p1[i];
1581 v[i] = p3[i] - p1[i];
1582 w[i] = p[i] - p1[i];
1583 }
1584 // n: outward non-unit normal
1585 double n[3];
1586 CrossProduct(u, v, n);
1587 double n_squared = n[0] * n[0] + n[1] * n[1] + n[2] * n[2]; // 4 A
1588 // baricentric coordinates on facet [ u, w + a n, n ] = [ u, w, n]
1589 double cross[3];
1590 CrossProduct(u, w, cross);
1591 double alpha3 = (cross[0] * n[0] + cross[1] * n[1] + cross[2] * n[2]) / n_squared;
1592 CrossProduct(w, v, cross);
1593 double alpha2 = (cross[0] * n[0] + cross[1] * n[1] + cross[2] * n[2]) / n_squared;
1594 double alpha1 = 1.0 - alpha2 - alpha3;
1595 // check inside
1596 double tol2 = 1.0 + tol;
1597 bool a1 = (alpha1 > -tol && alpha1 < tol2);
1598 bool a2 = (alpha2 > -tol && alpha2 < tol2);
1599 bool a3 = (alpha3 > -tol && alpha3 < tol2);
1600 in = false;
1601 if (a1 && a2 && a3) {
1602 double xp = alpha1 * p1[0] + alpha2 * p2[0] + alpha3 * p3[0];
1603 double yp = alpha1 * p1[1] + alpha2 * p2[1] + alpha3 * p3[1];
1604 double zp = alpha1 * p1[2] + alpha2 * p2[2] + alpha3 * p3[2];
1605 double dx = node.coord_1_x_ - xp;
1606 double dy = node.coord_1_y_ - yp;
1607 double dz = node.coord_1_z_ - zp;
1608 double s = 1.0 / std::sqrt(n_squared);
1609 normal[0] = n[0] * s;
1610 normal[1] = n[1] * s;
1611 normal[2] = n[2] * s;
1612 gap = dx * normal[0] + dy * normal[1] + dz * normal[2];
1613 barycentric_coordinates[0] = alpha1;
1614 barycentric_coordinates[1] = alpha2;
1615 barycentric_coordinates[2] = alpha3;
1616 if ((gap < 0.0) && (gap > -tri.char_len_)) { // inside but not through
1617 in = true;
1618 }
1619 }
1620}

◆ RemoveInternalSkinFaces()

void nimble::ContactManager::RemoveInternalSkinFaces ( GenesisMesh const & mesh,
std::vector< std::vector< int > > & faces,
std::vector< int > & entity_ids )
static
942{
943#ifdef NIMBLE_HAVE_MPI
944
945 int mpi_rank, num_ranks;
946 MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
947 MPI_Comm_size(MPI_COMM_WORLD, &num_ranks);
948
949 constexpr int num_nodes_in_face = 4;
950
951 const int* genesis_node_global_ids = mesh.GetNodeGlobalIds();
952 std::vector<int> face_global_ids;
953 face_global_ids.reserve(num_nodes_in_face * faces.size());
954 std::map<std::vector<int>, int> faceList;
955 for (int iface = 0; iface < faces.size(); ++iface) {
956 const auto& face = faces[iface];
957 std::vector<int> fvec(num_nodes_in_face, std::numeric_limits<int>::max());
958 for (int ii = 0; ii < face.size(); ++ii) fvec[ii] = genesis_node_global_ids[face[ii]];
959 std::sort(fvec.begin(), fvec.end());
960 for (auto inode : fvec) face_global_ids.push_back(inode);
961 faceList.emplace(std::make_pair(std::move(fvec), iface));
962 }
963
964 std::vector<bool> remove_face_hash(faces.size(), false);
965 std::vector<bool> remove_entity_ids_hash(entity_ids.size(), false);
966 size_t iCountRemovals = 0;
967 const auto bcast_size = static_cast<int>(face_global_ids.size());
968 for (int shift = 1; shift < num_ranks; ++shift) {
969 int target = (mpi_rank + shift) % num_ranks;
970 int source = (mpi_rank + num_ranks - shift) % num_ranks;
971 //
972 int recv_size = 0;
973 MPI_Sendrecv(
974 &bcast_size,
975 1,
976 MPI_INT,
977 target,
978 shift,
979 &recv_size,
980 1,
981 MPI_INT,
982 source,
983 MPI_ANY_TAG,
984 MPI_COMM_WORLD,
985 MPI_STATUS_IGNORE);
986 //
987 std::vector<int> mpi_buffer(recv_size);
988 MPI_Sendrecv(
989 face_global_ids.data(),
990 face_global_ids.size(),
991 MPI_INT,
992 target,
993 shift,
994 mpi_buffer.data(),
995 mpi_buffer.size(),
996 MPI_INT,
997 source,
998 MPI_ANY_TAG,
999 MPI_COMM_WORLD,
1000 MPI_STATUS_IGNORE);
1001 //
1002 int mpi_buffer_num_faces = recv_size / num_nodes_in_face;
1003 //
1004 for (int i_mpi_buff_face = 0; i_mpi_buff_face < mpi_buffer_num_faces; i_mpi_buff_face++) {
1005 const auto* mpi_buff_ptr = &mpi_buffer.at(i_mpi_buff_face * num_nodes_in_face);
1006 //
1007 std::vector<int> tmpList(mpi_buff_ptr, mpi_buff_ptr + num_nodes_in_face);
1008 auto tmpIter = faceList.find(tmpList);
1009 if (tmpIter != faceList.end()) {
1010 remove_face_hash[tmpIter->second] = true;
1011 remove_entity_ids_hash[tmpIter->second] = true;
1012 iCountRemovals += 1;
1013 }
1014 }
1015 }
1016
1017 //
1018 // Update the vector of faces and entity IDs
1019 //
1020
1021 if (iCountRemovals == 0) return;
1022
1023 std::vector<std::vector<int>> new_faces;
1024 new_faces.reserve(faces.size());
1025 for (std::size_t i = 0; i < faces.size(); ++i) {
1026 if (!remove_face_hash[i]) new_faces.emplace_back(std::move(faces[i]));
1027 }
1028
1029 std::vector<int> new_entity_ids;
1030 new_entity_ids.reserve(entity_ids.size());
1031 for (std::size_t i = 0; i < entity_ids.size(); ++i) {
1032 if (!remove_entity_ids_hash[i]) new_entity_ids.emplace_back(entity_ids[i]);
1033 }
1034 std::cout << " Rank " << mpi_rank << " Removed " << remove_face_hash.size() - new_faces.size() << " faces\n";
1035 std::cout << " Rank " << mpi_rank << " Removed " << remove_entity_ids_hash.size() - new_entity_ids.size()
1036 << " faces\n";
1037 std::swap(new_faces, faces);
1038 std::swap(new_entity_ids, entity_ids);
1039
1040#endif
1041}

◆ ResetContactStatus()

void nimble::ContactManager::ResetContactStatus ( )
protected

Reset the contact status for all entities.

1624{
1625 for (size_t jj = 0; jj < contact_faces_d_.extent(0); ++jj) contact_faces_d_(jj).ResetContactData();
1626
1627 for (size_t jj = 0; jj < contact_nodes_d_.extent(0); ++jj) contact_nodes_d_(jj).ResetContactData();
1628}

◆ SetPenaltyParameter()

void nimble::ContactManager::SetPenaltyParameter ( double penalty_parameter)
inline

Set the penalty parameter for enforcing contact.

Parameters
penalty_parameterPenalty value
167 {
168 penalty_parameter_ = penalty_parameter;
170 contact_interface->SetUpPenaltyEnforcement(penalty_parameter_);
171 }

◆ SimpleClosestPointProjectionSingle()

void nimble::ContactManager::SimpleClosestPointProjectionSingle ( const ContactEntity & node,
const ContactEntity & tri,
PROJECTION_TYPE * projection_type,
ContactEntity::vertex * closest_point,
double & gap,
double * normal,
double tolerance = 1.e-8 )
static

Compute the projection of a point onto a triangular face.

Parameters
[in]nodeNode to project
[in]triFace to project onto
[out]projection_typeResult of projection (FACE: success, UNKNOWN: point projects outside the face)
[out]closest_pointProjection
[out]gapNormal distance when the point projects onto the face
[out]normalUnit normal vector outside of face
[in]toleranceTolerance to fit into the face (defaut value = 1e-08)
1475{
1476 // node
1477 double p[3];
1478 p[0] = node.coord_1_x_;
1479 p[1] = node.coord_1_y_;
1480 p[2] = node.coord_1_z_;
1481 // facet
1482 double p1[3];
1483 p1[0] = tri.coord_1_x_;
1484 p1[1] = tri.coord_1_y_;
1485 p1[2] = tri.coord_1_z_;
1486 double p2[3];
1487 p2[0] = tri.coord_2_x_;
1488 p2[1] = tri.coord_2_y_;
1489 p2[2] = tri.coord_2_z_;
1490 double p3[3];
1491 p3[0] = tri.coord_3_x_;
1492 p3[1] = tri.coord_3_y_;
1493 p3[2] = tri.coord_3_z_;
1494 // u: edge, v: edge, w: vertex to edge
1495 double u[3], v[3], w[3];
1496 for (int i = 0; i < 3; i++) {
1497 u[i] = p2[i] - p1[i];
1498 v[i] = p3[i] - p1[i];
1499 w[i] = p[i] - p1[i];
1500 }
1501 // n: outward non-unit normal
1502 double n[3];
1503 CrossProduct(u, v, n);
1504 double n_squared = n[0] * n[0] + n[1] * n[1] + n[2] * n[2]; // 4 A
1505 // baricentric coordinates on facet [ u, w + a n, n ] = [ u, w, n]
1506 double cross[3];
1507 CrossProduct(u, w, cross);
1508 double alpha3 = (cross[0] * n[0] + cross[1] * n[1] + cross[2] * n[2]) / n_squared;
1509 CrossProduct(w, v, cross);
1510 double alpha2 = (cross[0] * n[0] + cross[1] * n[1] + cross[2] * n[2]) / n_squared;
1511 double alpha1 = 1.0 - alpha2 - alpha3;
1512 // check inside
1513 double tol2 = 1.0 + tol;
1514 bool a1 = (alpha1 > -tol && alpha1 < tol2);
1515 bool a2 = (alpha2 > -tol && alpha2 < tol2);
1516 bool a3 = (alpha3 > -tol && alpha3 < tol2);
1517 *projection_type = PROJECTION_TYPE::UNKNOWN; // indicates outside
1518 if (a1 && a2 && a3) {
1519 *projection_type = PROJECTION_TYPE::FACE;
1520 double xp = alpha1 * p1[0] + alpha2 * p2[0] + alpha3 * p3[0];
1521 double yp = alpha1 * p1[1] + alpha2 * p2[1] + alpha3 * p3[1];
1522 double zp = alpha1 * p1[2] + alpha2 * p2[2] + alpha3 * p3[2];
1523 closest_point->coords_[0] = xp;
1524 closest_point->coords_[1] = yp;
1525 closest_point->coords_[2] = zp;
1526 double dx = node.coord_1_x_ - xp;
1527 double dy = node.coord_1_y_ - yp;
1528 double dz = node.coord_1_z_ - zp;
1529 double s = 1.0 / std::sqrt(n_squared);
1530 normal[0] = n[0] * s;
1531 normal[1] = n[1] * s;
1532 normal[2] = n[2] * s;
1533 gap = dx * normal[0] + dy * normal[1] + dz * normal[2];
1534 }
1535}

◆ SkinBlocks()

void nimble::ContactManager::SkinBlocks ( GenesisMesh const & mesh,
std::vector< int > const & block_ids,
int entity_id_offset,
std::vector< std::vector< int > > & skin_faces,
std::vector< int > & entity_ids )
static
795{
796 std::map<std::vector<int>, std::vector<int>> faces;
797 std::map<std::vector<int>, std::vector<int>>::iterator face_it;
798
799 for (auto& block_id : block_ids) {
800 int num_elem_in_block = mesh.GetNumElementsInBlock(block_id);
801 int num_node_per_elem = mesh.GetNumNodesPerElement(block_id);
802 const int* const conn = mesh.GetConnectivity(block_id);
803 std::vector<int> const& elem_global_ids = mesh.GetElementGlobalIdsInBlock(block_id);
804 int conn_index = 0;
805
806 // key is sorted node list for a face
807 int num_node_per_face = 4;
808 std::vector<int> key(num_node_per_face);
809 // value is count, unsorted node list, exodus element id, and face ordinal
810 std::vector<int> value(num_node_per_face + 3);
811
812 for (int i_elem = 0; i_elem < num_elem_in_block; i_elem++) {
813 // switch from 0-based indexing to 1-based indexing
814 // so that the ids will be valid exodus ids in the contact visualization
815 // output
816 int elem_global_id = elem_global_ids[i_elem] + 1;
817
818 // Examine each face, following the Exodus node-ordering convention
819
820 // face 0: 0 1 5 4
821 value[0] = 1;
822 key[0] = value[1] = conn[conn_index + 0];
823 key[1] = value[2] = conn[conn_index + 1];
824 key[2] = value[3] = conn[conn_index + 5];
825 key[3] = value[4] = conn[conn_index + 4];
826 value[5] = elem_global_id;
827 value[6] = 0;
828 std::sort(key.begin(), key.end());
829 face_it = faces.find(key);
830 if (face_it == faces.end())
831 faces[key] = value;
832 else
833 face_it->second[0] += 1;
834
835 // face 1: 1 2 6 5
836 value[0] = 1;
837 key[0] = value[1] = conn[conn_index + 1];
838 key[1] = value[2] = conn[conn_index + 2];
839 key[2] = value[3] = conn[conn_index + 6];
840 key[3] = value[4] = conn[conn_index + 5];
841 value[5] = elem_global_id;
842 value[6] = 1;
843 std::sort(key.begin(), key.end());
844 face_it = faces.find(key);
845 if (face_it == faces.end())
846 faces[key] = value;
847 else
848 face_it->second[0] += 1;
849
850 // face 2: 2 3 7 6
851 value[0] = 1;
852 key[0] = value[1] = conn[conn_index + 2];
853 key[1] = value[2] = conn[conn_index + 3];
854 key[2] = value[3] = conn[conn_index + 7];
855 key[3] = value[4] = conn[conn_index + 6];
856 value[5] = elem_global_id;
857 value[6] = 2;
858 std::sort(key.begin(), key.end());
859 face_it = faces.find(key);
860 if (face_it == faces.end())
861 faces[key] = value;
862 else
863 face_it->second[0] += 1;
864
865 // face 3: 0 4 7 3
866 value[0] = 1;
867 key[0] = value[1] = conn[conn_index + 0];
868 key[1] = value[2] = conn[conn_index + 4];
869 key[2] = value[3] = conn[conn_index + 7];
870 key[3] = value[4] = conn[conn_index + 3];
871 value[5] = elem_global_id;
872 value[6] = 3;
873 std::sort(key.begin(), key.end());
874 face_it = faces.find(key);
875 if (face_it == faces.end())
876 faces[key] = value;
877 else
878 face_it->second[0] += 1;
879
880 // face 4: 0 3 2 1
881 value[0] = 1;
882 key[0] = value[1] = conn[conn_index + 0];
883 key[1] = value[2] = conn[conn_index + 3];
884 key[2] = value[3] = conn[conn_index + 2];
885 key[3] = value[4] = conn[conn_index + 1];
886 value[5] = elem_global_id;
887 value[6] = 4;
888 std::sort(key.begin(), key.end());
889 face_it = faces.find(key);
890 if (face_it == faces.end())
891 faces[key] = value;
892 else
893 face_it->second[0] += 1;
894
895 // face 5: 4 5 6 7
896 value[0] = 1;
897 key[0] = value[1] = conn[conn_index + 4];
898 key[1] = value[2] = conn[conn_index + 5];
899 key[2] = value[3] = conn[conn_index + 6];
900 key[3] = value[4] = conn[conn_index + 7];
901 value[5] = elem_global_id;
902 value[6] = 5;
903 std::sort(key.begin(), key.end());
904 face_it = faces.find(key);
905 if (face_it == faces.end())
906 faces[key] = value;
907 else
908 face_it->second[0] += 1;
909
910 conn_index += num_node_per_elem;
911 }
912 }
913
914 skin_faces.clear();
915 entity_ids.clear();
916 for (auto face : faces) {
917 if (face.second[0] == 1) {
918 std::vector<int> skin_face;
919 for (int i = 0; i < face.second.size() - 3; i++) {
920 int id = face.second.at(i + 1);
921 skin_face.push_back(id);
922 }
923 skin_faces.push_back(skin_face);
924 int entity_id = (face.second[5] + entity_id_offset)
925 << 5; // 59 bits for the genesis element id plus an offset value
926 entity_id |= face.second[6] << 2; // 3 bits for the face ordinal
927 entity_id |= 0; // 2 bits for triangle ordinal (unknown until face is
928 // subdivided downstream)
929 entity_ids.push_back(entity_id);
930 } else if (face.second[0] != 2) {
931 NIMBLE_ABORT("Error in mesh skinning routine, face found more than two times!\n");
932 }
933 }
934}

◆ startTimer()

NIMBLE_INLINE_FUNCTION void nimble::ContactManager::startTimer ( const std::string & str_val)
inlineprotected
416 {
417#ifdef NIMBLE_TIME_CONTACT
418 watch_.Start(str_val);
419#endif
420 }

◆ stopTimer()

NIMBLE_INLINE_FUNCTION void nimble::ContactManager::stopTimer ( const std::string & str_val)
inlineprotected
425 {
426#ifdef NIMBLE_TIME_CONTACT
427 watch_.Stop(str_val);
428#endif
429 }

◆ WriteVisualizationData()

void nimble::ContactManager::WriteVisualizationData ( double t)
protected

Write visualization data to Exodus file at time t.

Parameters
tCurrent time
Note
When using Kokkos, the data is extracted from the "host".
606{
607 nimble::GenesisMesh& mesh = genesis_mesh_for_contact_visualization_;
608 nimble::ExodusOutput& out = exodus_output_for_contact_visualization_;
609
610 std::vector<double> global_data;
611 std::vector<std::vector<double>> node_data_for_output(4);
612 std::map<int, std::vector<std::string>> elem_data_labels_for_output;
613 std::map<int, std::vector<std::vector<double>>> elem_data_for_output;
614 std::map<int, std::vector<std::string>> derived_elem_data_labels;
615 std::map<int, std::vector<std::vector<double>>> derived_elem_data;
616
617 // Get the number of contacts from one block
618 auto num_contacts = numActiveContactFaces();
619 std::cout << "======== vis num_contacts: " << num_contacts << '\n';
620 global_data.push_back(static_cast<double>(num_contacts));
621
622 std::vector<int> const& block_ids = mesh.GetBlockIds();
623 for (auto& block_id : block_ids) {
624 elem_data_labels_for_output[block_id] = std::vector<std::string>();
625 derived_elem_data_labels[block_id] = std::vector<std::string>();
626 }
627
628 // node_data_for_output contains displacement_x, displacement_y,
629 // displacement_z
630 auto num_nodes = mesh.GetNumNodes();
631 for (auto& ndata : node_data_for_output) ndata.resize(num_nodes);
632 const double* model_coord_x = mesh.GetCoordinatesX();
633 const double* model_coord_y = mesh.GetCoordinatesY();
634 const double* model_coord_z = mesh.GetCoordinatesZ();
635
636 size_t nfaces = numContactFaces();
637
638 int node_index(0);
639 for (size_t i_face = 0; i_face < nfaces; i_face++) {
640 ContactEntity const& face = getContactFace(i_face);
641 auto contact_status = static_cast<double>(face.contact_status());
642 node_data_for_output[0][node_index] = face.coord_1_x_ - model_coord_x[node_index];
643 node_data_for_output[1][node_index] = face.coord_1_y_ - model_coord_y[node_index];
644 node_data_for_output[2][node_index] = face.coord_1_z_ - model_coord_z[node_index];
645 node_data_for_output[3][node_index] = contact_status;
646 node_index += 1;
647 node_data_for_output[0][node_index] = face.coord_2_x_ - model_coord_x[node_index];
648 node_data_for_output[1][node_index] = face.coord_2_y_ - model_coord_y[node_index];
649 node_data_for_output[2][node_index] = face.coord_2_z_ - model_coord_z[node_index];
650 node_data_for_output[3][node_index] = contact_status;
651 node_index += 1;
652 node_data_for_output[0][node_index] = face.coord_3_x_ - model_coord_x[node_index];
653 node_data_for_output[1][node_index] = face.coord_3_y_ - model_coord_y[node_index];
654 node_data_for_output[2][node_index] = face.coord_3_z_ - model_coord_z[node_index];
655 node_data_for_output[3][node_index] = contact_status;
656 node_index += 1;
657 }
658
659 const size_t nnodes = numContactNodes();
660 for (size_t i_node = 0; i_node < nnodes; i_node++) {
661 ContactEntity const& node = getContactNode(i_node);
662 node_data_for_output[0][node_index] = node.coord_1_x_ - model_coord_x[node_index];
663 node_data_for_output[1][node_index] = node.coord_1_y_ - model_coord_y[node_index];
664 node_data_for_output[2][node_index] = node.coord_1_z_ - model_coord_z[node_index];
665 node_data_for_output[3][node_index] = static_cast<double>(node.contact_status());
666 node_index += 1;
667 }
668
669 out.WriteStep(
670 t,
671 global_data,
672 node_data_for_output,
673 elem_data_labels_for_output,
674 elem_data_for_output,
675 derived_elem_data_labels,
676 derived_elem_data);
677}
size_t numActiveContactFaces() const
Returns the number of contact faces "actively" in collision.
Definition nimble_contact_manager.cc:692
void WriteStep(double time, std::vector< double > const &global_data, std::vector< std::vector< double > > const &node_data, std::map< int, std::vector< std::string > > const &elem_data_names, std::map< int, std::vector< std::vector< double > > > const &elem_data, std::map< int, std::vector< std::string > > const &derived_elem_data_names, std::map< int, std::vector< std::vector< double > > > const &derived_elem_data)
Definition nimble_exodus_output.cc:315
std::vector< int > GetBlockIds() const
Definition nimble_genesis_mesh.h:150
const double * GetCoordinatesX() const
Definition nimble_genesis_mesh.h:201
const double * GetCoordinatesY() const
Definition nimble_genesis_mesh.h:207
unsigned int GetNumNodes() const
Definition nimble_genesis_mesh.h:90
const double * GetCoordinatesZ() const
Definition nimble_genesis_mesh.h:213

◆ ZeroContactForce()

void nimble::ContactManager::ZeroContactForce ( )
protected

Zero the contact forces.

1632{
1633 for (auto& fval : force_) fval = 0.0;
1634
1635#ifdef NIMBLE_HAVE_KOKKOS
1636 Kokkos::deep_copy(force_d_, 0.0);
1637 //
1639 auto numFaces = contact_faces_d_.extent(0);
1640 Kokkos::parallel_for(
1641 "Zero Face Force", numFaces, KOKKOS_LAMBDA(const int i_face) {
1642 contact_faces(i_face).force_1_x_ = 0.0;
1643 contact_faces(i_face).force_1_y_ = 0.0;
1644 contact_faces(i_face).force_1_z_ = 0.0;
1645 contact_faces(i_face).force_2_x_ = 0.0;
1646 contact_faces(i_face).force_2_y_ = 0.0;
1647 contact_faces(i_face).force_2_z_ = 0.0;
1648 contact_faces(i_face).force_3_x_ = 0.0;
1649 contact_faces(i_face).force_3_y_ = 0.0;
1650 contact_faces(i_face).force_3_z_ = 0.0;
1651 });
1652 //
1654 auto numNodes = contact_nodes_d_.extent(0);
1655 Kokkos::parallel_for(
1656 "Zero Node Force", numNodes, KOKKOS_LAMBDA(const int i_node) {
1657 contact_nodes(i_node).force_1_x_ = 0.0;
1658 contact_nodes(i_node).force_1_y_ = 0.0;
1659 contact_nodes(i_node).force_1_z_ = 0.0;
1660 });
1661#endif
1662}

Member Data Documentation

◆ contact_enabled_

bool nimble::ContactManager::contact_enabled_ = false
protected

◆ contact_faces_d_

nimble_kokkos::DeviceContactEntityArrayView nimble::ContactManager::contact_faces_d_
protected
Initial value:

◆ contact_faces_h_

nimble_kokkos::HostContactEntityArrayView nimble::ContactManager::contact_faces_h_
protected
Initial value:
=
Kokkos::View< nimble::ContactEntity *, nimble_kokkos::kokkos_layout, nimble_kokkos::kokkos_host > HostContactEntityArrayView
Definition nimble_kokkos_contact_defs.h:17

◆ contact_interface

std::shared_ptr<ContactInterface> nimble::ContactManager::contact_interface
protected

◆ contact_nodes_d_

nimble_kokkos::DeviceContactEntityArrayView nimble::ContactManager::contact_nodes_d_
protected
Initial value:

◆ contact_nodes_h_

nimble_kokkos::HostContactEntityArrayView nimble::ContactManager::contact_nodes_h_
protected
Initial value:

◆ contact_visualization_model_coord_bounding_box_

double nimble::ContactManager::contact_visualization_model_coord_bounding_box_[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}
protected
448{0.0, 0.0, 0.0, 0.0, 0.0, 0.0};

◆ coord_

std::vector<double> nimble::ContactManager::coord_
protected

◆ coord_d_

nimble_kokkos::DeviceScalarNodeView nimble::ContactManager::coord_d_ = nimble_kokkos::DeviceScalarNodeView("contact coord_d", 1)
protected

◆ data_manager_

nimble::DataManager& nimble::ContactManager::data_manager_
protected

◆ enforcement

PenaltyContactEnforcement nimble::ContactManager::enforcement
protected

◆ exodus_output_for_contact_visualization_

nimble::ExodusOutput nimble::ContactManager::exodus_output_for_contact_visualization_
protected

◆ force_

std::vector<double> nimble::ContactManager::force_
protected

◆ force_d_

nimble_kokkos::DeviceScalarNodeView nimble::ContactManager::force_d_ = nimble_kokkos::DeviceScalarNodeView("contact force_d", 1)
protected

◆ genesis_mesh_for_contact_visualization_

nimble::GenesisMesh nimble::ContactManager::genesis_mesh_for_contact_visualization_
protected

◆ model_coord_

std::vector<double> nimble::ContactManager::model_coord_
protected

◆ model_coord_d_

nimble_kokkos::DeviceScalarNodeView nimble::ContactManager::model_coord_d_ = nimble_kokkos::DeviceScalarNodeView("contact model_coord_d", 1)
protected

◆ model_data_

nimble_kokkos::ModelData* nimble::ContactManager::model_data_ = nullptr
protected

◆ node_ids_

std::vector<int> nimble::ContactManager::node_ids_
protected

◆ node_ids_d_

nimble_kokkos::DeviceIntegerArrayView nimble::ContactManager::node_ids_d_ = nimble_kokkos::DeviceIntegerArrayView("contact node_ids_d", 1)
protected

◆ penalty_parameter_

double nimble::ContactManager::penalty_parameter_
protected

◆ timers_

std::unordered_map<std::string, double> nimble::ContactManager::timers_
protected

◆ total_enforcement_time

nimble::TimeKeeper nimble::ContactManager::total_enforcement_time
protected

◆ total_num_contacts

std::size_t nimble::ContactManager::total_num_contacts = 0
protected

◆ total_search_time

nimble::TimeKeeper nimble::ContactManager::total_search_time
protected

The documentation for this class was generated from the following files: