From affbd7236f8046372030044696c4447c8f656449 Mon Sep 17 00:00:00 2001
From: Yohann Bearzi <yohann.bearzi@kitware.com>
Date: Fri, 23 Jun 2023 14:42:42 -0400
Subject: [PATCH] vtkDIYGhostUtilities: aborting when adjacency map is
 asymmetrical

This scenario can happen if the user provides faulty global point ids.
An asymmetrical adjacency map can cause a crash. So instead, we now
double check symmetry while exchanging ghost data and abort if exchanges
are not symmetrical

Fixes #22191
---
 Parallel/DIY/vtkDIYGhostUtilities.h   |  3 +-
 Parallel/DIY/vtkDIYGhostUtilities.txx | 43 ++++++++++++++++++++++++---
 2 files changed, 41 insertions(+), 5 deletions(-)

diff --git a/Parallel/DIY/vtkDIYGhostUtilities.h b/Parallel/DIY/vtkDIYGhostUtilities.h
index 990511660b8..fafe8b0b48b 100644
--- a/Parallel/DIY/vtkDIYGhostUtilities.h
+++ b/Parallel/DIY/vtkDIYGhostUtilities.h
@@ -973,7 +973,8 @@ protected:
    * This method exchanges ghosts between connected blocks.
    */
   template <class DataSetT>
-  static void ExchangeGhosts(diy::Master& master, std::vector<DataSetT*>& inputs);
+  static bool ExchangeGhosts(diy::Master& master, diy::Assigner& assigner,
+    diy::RegularAllReducePartners& partners, std::vector<DataSetT*>& inputs);
 
   /**
    * This methods allocate a point and cell ghost array and fills it with 0.
diff --git a/Parallel/DIY/vtkDIYGhostUtilities.txx b/Parallel/DIY/vtkDIYGhostUtilities.txx
index 654c367a9ed..a39aa5e8bfa 100644
--- a/Parallel/DIY/vtkDIYGhostUtilities.txx
+++ b/Parallel/DIY/vtkDIYGhostUtilities.txx
@@ -393,7 +393,8 @@ void vtkDIYGhostUtilities::ExchangeBoundingBoxes(
 
 //----------------------------------------------------------------------------
 template <class DataSetT>
-void vtkDIYGhostUtilities::ExchangeGhosts(diy::Master& master, std::vector<DataSetT*>& inputs)
+bool vtkDIYGhostUtilities::ExchangeGhosts(diy::Master& master, diy::Assigner& assigner,
+  diy::RegularAllReducePartners& partners, std::vector<DataSetT*>& inputs)
 {
   using BlockType = typename DataSetTypeToBlockTypeConverter<DataSetT>::BlockType;
 
@@ -411,7 +412,8 @@ void vtkDIYGhostUtilities::ExchangeGhosts(diy::Master& master, std::vector<DataS
 
   master.exchange();
 
-  master.foreach ([](BlockType* block, const diy::Master::ProxyWithLink& cp) {
+  bool error = false;
+  master.foreach ([&error](BlockType* block, const diy::Master::ProxyWithLink& cp) {
     std::vector<int> incoming;
     cp.incoming(incoming);
     for (const int& gid : incoming)
@@ -419,10 +421,37 @@ void vtkDIYGhostUtilities::ExchangeGhosts(diy::Master& master, std::vector<DataS
       // we need this extra check because incoming is not empty when using only one block
       if (!cp.incoming(gid).empty())
       {
-        vtkDIYGhostUtilities::DequeueGhosts(cp, gid, block->BlockStructures.at(gid));
+        auto it = block->BlockStructures.find(gid);
+        if (it == block->BlockStructures.end())
+        {
+          error = true;
+        }
+        else
+        {
+          vtkDIYGhostUtilities::DequeueGhosts(cp, gid, block->BlockStructures.at(gid));
+        }
       }
     }
   });
+
+  diy::reduce(master, assigner, partners,
+    [&error](BlockType*, const diy::ReduceProxy& rp, const diy::RegularAllReducePartners&) {
+      for (int i = 0; i < rp.in_link().size(); ++i)
+      {
+        int gid = rp.in_link().target(i).gid;
+
+        bool receivedError;
+        rp.dequeue(gid, &receivedError, 1);
+        error |= receivedError;
+      }
+
+      for (int i = 0; i < rp.out_link().size(); ++i)
+      {
+        rp.enqueue(rp.out_link().target(i), &error, 1);
+      }
+    });
+
+  return !error;
 }
 
 //----------------------------------------------------------------------------
@@ -678,7 +707,13 @@ int vtkDIYGhostUtilities::GenerateGhostCells(std::vector<DataSetT*>& inputs,
   vtkLogEndScope("Relinking blocks using link map");
 
   vtkLogStartScope(TRACE, "Exchanging ghost data between blocks");
-  vtkDIYGhostUtilities::ExchangeGhosts(master, inputs);
+  if (!vtkDIYGhostUtilities::ExchangeGhosts(master, assigner, partners, inputs))
+  {
+    vtkLog(ERROR,
+      "Could not connect adjacent datasets across partitions."
+        << " This is likely caused by an input with faulty point global ids. Aborting.");
+    return 0;
+  }
   vtkLogEndScope("Exchanging ghost data between blocks");
 
   vtkLogStartScope(TRACE, "Allocating ghosts in outputs");
-- 
GitLab