diff --git a/ThirdParty/diy2/vtkdiy2/CMakeLists.txt b/ThirdParty/diy2/vtkdiy2/CMakeLists.txt
index 8031aabe6607882cc40c0ae580452c3517a4d5ec..33c489631955aeef684605d673196649a7ae00ab 100644
--- a/ThirdParty/diy2/vtkdiy2/CMakeLists.txt
+++ b/ThirdParty/diy2/vtkdiy2/CMakeLists.txt
@@ -1,5 +1,3 @@
-if (NOT VTK_INSTALL_NO_DEVELOPMENT)
-  install(DIRECTORY include/vtkdiy2/
-    DESTINATION "${VTK_INSTALL_INCLUDE_DIR}/vtkdiy2"
-    COMPONENT Development)
-endif()
+vtk_module_install_headers(
+  SUBDIR      "vtkdiy2"
+  DIRECTORIES "include")
diff --git a/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/io/shared.hpp b/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/io/shared.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..26d94d6c16da021a2ed5077dedc063cfe89da2f3
--- /dev/null
+++ b/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/io/shared.hpp
@@ -0,0 +1,49 @@
+#ifndef DIY_IO_SHARED_HPP
+#define DIY_IO_SHARED_HPP
+
+#include <sstream>
+#include <fstream>
+#include "../mpi.hpp"
+
+namespace diy
+{
+namespace io
+{
+
+class SharedOutFile: public std::ostringstream
+{
+    public:
+                SharedOutFile(std::string filename, diy::mpi::communicator world, int root = 0):
+                    filename_(filename),
+                    world_(world),
+                    root_(root)                     {}
+
+                ~SharedOutFile()                    { close(); }
+
+        void    close()
+        {
+            auto str = this->str();
+            std::vector<char> contents(str.begin(), str.end());
+            if (world_.rank() == root_)
+            {
+                std::vector<std::vector<char>> all_contents;
+                diy::mpi::gather(world_, contents, all_contents, root_);
+
+                // write the file serially
+                std::ofstream out(filename_);
+                for (auto& contents : all_contents)
+                    out.write(contents.data(), contents.size());
+            } else
+                diy::mpi::gather(world_, contents, root_);
+        }
+
+    private:
+        std::string             filename_;
+        diy::mpi::communicator  world_;
+        int                     root_;
+};
+
+}
+}
+
+#endif
diff --git a/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/master.hpp b/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/master.hpp
index a012df948717a3bb50b7469f8cda2ae7f8ca3b65..5a783e99787c819ab88c02a5d73d5d93e3b1be1e 100644
--- a/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/master.hpp
+++ b/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/master.hpp
@@ -670,6 +670,8 @@ void
 diy::Master::
 iexchange_(const ICallback<Block>& f)
 {
+    auto scoped = prof.scoped("iexchange");
+
     // prepare for next round
     incoming_.erase(exchange_round_);
     ++exchange_round_;
@@ -684,10 +686,13 @@ iexchange_(const ICallback<Block>& f)
     {
         for (size_t i = 0; i < size(); i++)     // for all blocks
         {
+
             icommunicate(&iexchange);            // TODO: separate comm thread std::thread t(icommunicate);
             ProxyWithLink cp = proxy(i, &iexchange);
 
+            prof << "callback";
             bool done = f(block<Block>(i), cp);
+            prof >> "callback";
 
             int nundeq_after = 0;
             int nunenq_after = 0;
@@ -731,6 +736,7 @@ void
 diy::Master::
 comm_exchange(GidSendOrder& gid_order, IExchangeInfo* iexchange)
 {
+    auto scoped = prof.scoped("comm-exchange");
     send_outgoing_queues(gid_order, false, iexchange);
     while(nudge());                   // kick requests
     check_incoming_queues(iexchange);
@@ -832,6 +838,8 @@ void
 diy::Master::
 icommunicate(IExchangeInfo* iexchange)
 {
+    auto scoped = prof.scoped("icommunicate");
+
     log->debug("Entering icommunicate()");
 
     // lock out other threads
@@ -861,6 +869,8 @@ send_outgoing_queues(GidSendOrder&   gid_order,
                      bool            remote,                     // TODO: are remote and iexchange mutually exclusive? If so, use single enum?
                      IExchangeInfo*  iexchange)
 {
+    auto scoped = prof.scoped("send-outgoing-queues");
+
     while (inflight_sends().size() < gid_order.limit && !gid_order.empty())
     {
         int from = gid_order.pop();
@@ -1071,6 +1081,8 @@ void
 diy::Master::
 check_incoming_queues(IExchangeInfo* iexchange)
 {
+    auto scoped = prof.scoped("check-incoming-queues");
+
     mpi::optional<mpi::status> ostatus = comm_.iprobe(mpi::any_source, mpi::any_tag);
     while (ostatus)
     {
diff --git a/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/mpi/io.hpp b/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/mpi/io.hpp
index af52c506387095f27c230cf8d37dc5e62df841dc..1ed9792219aa0a5a4cfff23f8f75869a3a0b7ca9 100644
--- a/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/mpi/io.hpp
+++ b/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/mpi/io.hpp
@@ -74,7 +74,9 @@ file(const communicator& comm__, const std::string& filename, int mode)
 : comm_(comm__)
 {
 #ifndef DIY_NO_MPI
-  MPI_File_open(comm__, const_cast<char*>(filename.c_str()), mode, MPI_INFO_NULL, &fh);
+  int ret = MPI_File_open(comm__, const_cast<char*>(filename.c_str()), mode, MPI_INFO_NULL, &fh);
+  if (ret)
+      throw std::runtime_error("DIY cannot open file: " + filename);
 #else
   DIY_UNUSED(comm__);
   DIY_UNUSED(filename);
diff --git a/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/proxy.hpp b/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/proxy.hpp
index 25eabd41ea03736d5f53e23142a58012624f9c37..1667c2612a3ef6ccff478d05fc1a17ad08cdb0da 100644
--- a/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/proxy.hpp
+++ b/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/proxy.hpp
@@ -167,6 +167,51 @@ namespace diy
       void*             block_;
       Link*             link_;
       IExchangeInfo*    iexchange_;         // not used for iexchange presently, but later could trigger some special behavior
+
+    public:
+      template<class T>
+      void enqueue(const BlockID&     to,
+              const T&                x,
+              void (*save)(BinaryBuffer&, const T&) = &::diy::save<T>) const
+      {
+          diy::Master::Proxy::enqueue(to, x, save);
+          if (iexchange_)
+              master()->icommunicate(iexchange_);
+      }
+
+      template<class T>
+      void enqueue(const BlockID&     to,
+              const T*                x,
+              size_t                  n,
+              void (*save)(BinaryBuffer&, const T&) = &::diy::save<T>) const
+      {
+          diy::Master::Proxy::enqueue(to, x, n, save);
+          if (iexchange_)
+              master()->icommunicate(iexchange_);
+      }
+
+      template<class T>
+      void dequeue(int                from,
+              T&                      x,
+              void (*load)(BinaryBuffer&, T&) = &::diy::load<T>) const
+      {
+          // TODO: uncomment if necessary, try first without icommunicating on dequeue
+//           if (iexchange_)
+//               master()->icommunicate(iexchange_);
+          diy::Master::Proxy::dequeue(from, x, load);
+      }
+
+      template<class T>
+      void dequeue(int                from,
+              T*                      x,
+              size_t                  n,
+              void (*load)(BinaryBuffer&, T&) = &::diy::load<T>) const
+      {
+          // TODO: uncomment if necessary, try first without icommunicating on dequeue
+//           if (iexchange_)
+//               master()->icommunicate(iexchange_);
+          diy::Master::Proxy::dequeue(from, x, n, load);
+      }
   };
 }                                           // diy namespace
 
diff --git a/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/stats.hpp b/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/stats.hpp
index 4866ccfb195bb442d6fd676dfa44c2d68f9a065d..e794ad09b8fdc2e67476a7cc41c8f4ee3fe4a46d 100644
--- a/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/stats.hpp
+++ b/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/stats.hpp
@@ -65,14 +65,18 @@ struct Profiler
     void    enter(std::string name)                     { events.push_back(Event(name, true)); }
     void    exit(std::string name)                      { events.push_back(Event(name, false)); }
 
-    void    output(std::ostream& out)
+    void    output(std::ostream& out, std::string prefix = "")
     {
+        if (!prefix.empty())
+            prefix += " ";
+
         for (size_t i = 0; i < events.size(); ++i)
         {
             const Event& e = events[i];
             auto time = std::chrono::duration_cast<std::chrono::microseconds>(e.stamp - start).count();
 
-            fmt::print(out, "{:02d}:{:02d}:{:02d}.{:06d} {}{}\n",
+            fmt::print(out, "{}{:02d}:{:02d}:{:02d}.{:06d} {}{}\n",
+                            prefix,
                             time/1000000/60/60,
                             time/1000000/60 % 60,
                             time/1000000 % 60,
@@ -103,7 +107,7 @@ struct Profiler
     void    enter(const std::string&)                   {}
     void    exit(const std::string&)                    {}
 
-    void    output(std::ostream&)                       {}
+    void    output(std::ostream&, std::string = "")     {}
     void    clear()                                     {}
 
     Scoped  scoped(std::string)                         { return Scoped(); }
diff --git a/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/version.hpp b/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/version.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2b6812353dc03121d9f6cbd5d1c603762e054458
--- /dev/null
+++ b/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/version.hpp
@@ -0,0 +1,8 @@
+#ifndef DIY_VERSION_HPP
+#define DIY_VERSION_HPP
+
+#define DIY_VERSION_MAJOR 3
+#define DIY_VERSION_MINOR 5
+#define DIY_VERSION_PATCH 0
+
+#endif