1 // Copyright(C) 1999-2021 National Technology & Engineering Solutions
2 // of Sandia, LLC (NTESS).  Under the terms of Contract DE-NA0003525 with
3 // NTESS, the U.S. Government retains certain rights in this software.
4 //
5 // See packages/seacas/LICENSE for details
6 
7 #include <Ioss_CodeTypes.h>
8 #include <Ioss_SmartAssert.h>
9 #include <algorithm>
10 #include <cgns/Iocgns_StructuredZoneData.h>
11 #include <fmt/color.h>
12 #include <fmt/ostream.h>
13 #include <tokenize.h>
14 
15 namespace {
16   struct Range
17   {
RangeRange18     Range(int a, int b) : m_beg(a < b ? a : b), m_end(a < b ? b : a), m_reversed(b < a) {}
19 
beginRange20     int  begin() const { return m_reversed ? m_end : m_beg; }
endRange21     int  end() const { return m_reversed ? m_beg : m_end; }
22     int  m_beg;
23     int  m_end;
24     bool m_reversed;
25   };
26 
overlaps(const Range & a,const Range & b)27   bool overlaps(const Range &a, const Range &b) { return a.m_beg <= b.m_end && b.m_beg <= a.m_end; }
28 
zgc_overlaps(const Iocgns::StructuredZoneData * zone,const Ioss::ZoneConnectivity & zgc)29   bool zgc_overlaps(const Iocgns::StructuredZoneData *zone, const Ioss::ZoneConnectivity &zgc)
30   {
31     // Note that zone range is nodes and m_ordinal[] is cells, so need to add 1 to range.
32     Range z_i(1 + zone->m_offset[0], zone->m_ordinal[0] + zone->m_offset[0] + 1);
33     Range z_j(1 + zone->m_offset[1], zone->m_ordinal[1] + zone->m_offset[1] + 1);
34     Range z_k(1 + zone->m_offset[2], zone->m_ordinal[2] + zone->m_offset[2] + 1);
35 
36     Range gc_i(zgc.m_ownerRangeBeg[0], zgc.m_ownerRangeEnd[0]);
37     Range gc_j(zgc.m_ownerRangeBeg[1], zgc.m_ownerRangeEnd[1]);
38     Range gc_k(zgc.m_ownerRangeBeg[2], zgc.m_ownerRangeEnd[2]);
39 
40     return overlaps(z_i, gc_i) && overlaps(z_j, gc_j) && overlaps(z_k, gc_k);
41   }
42 
zgc_donor_overlaps(const Iocgns::StructuredZoneData * zone,const Ioss::ZoneConnectivity & zgc)43   bool zgc_donor_overlaps(const Iocgns::StructuredZoneData *zone, const Ioss::ZoneConnectivity &zgc)
44   {
45     // Note that zone range is nodes and m_ordinal[] is cells, so need to add 1 to range.
46     Range z_i(1 + zone->m_offset[0], zone->m_ordinal[0] + zone->m_offset[0] + 1);
47     Range z_j(1 + zone->m_offset[1], zone->m_ordinal[1] + zone->m_offset[1] + 1);
48     Range z_k(1 + zone->m_offset[2], zone->m_ordinal[2] + zone->m_offset[2] + 1);
49 
50     Range gc_i(zgc.m_donorRangeBeg[0], zgc.m_donorRangeEnd[0]);
51     Range gc_j(zgc.m_donorRangeBeg[1], zgc.m_donorRangeEnd[1]);
52     Range gc_k(zgc.m_donorRangeBeg[2], zgc.m_donorRangeEnd[2]);
53 
54     return overlaps(z_i, gc_i) && overlaps(z_j, gc_j) && overlaps(z_k, gc_k);
55   }
56 
subset_range(const Range & a,const Range & b)57   Range subset_range(const Range &a, const Range &b)
58   {
59     Range ret(std::max(a.m_beg, b.m_beg), std::min(a.m_end, b.m_end));
60     ret.m_reversed = a.m_reversed || b.m_reversed;
61     return ret;
62   }
63 
zgc_subset_ranges(const Iocgns::StructuredZoneData * zone,Ioss::ZoneConnectivity & zgc)64   void zgc_subset_ranges(const Iocgns::StructuredZoneData *zone, Ioss::ZoneConnectivity &zgc)
65   {
66     if (zgc_overlaps(zone, zgc)) {
67       // NOTE: Updates the range and donor_range in zgc
68 
69       // Note that zone range is nodes and m_ordinal[] is cells, so need to add 1 to range.
70       Range z_i(1 + zone->m_offset[0], zone->m_ordinal[0] + zone->m_offset[0] + 1);
71       Range z_j(1 + zone->m_offset[1], zone->m_ordinal[1] + zone->m_offset[1] + 1);
72       Range z_k(1 + zone->m_offset[2], zone->m_ordinal[2] + zone->m_offset[2] + 1);
73 
74       Range gc_i(zgc.m_ownerRangeBeg[0], zgc.m_ownerRangeEnd[0]);
75       Range gc_j(zgc.m_ownerRangeBeg[1], zgc.m_ownerRangeEnd[1]);
76       Range gc_k(zgc.m_ownerRangeBeg[2], zgc.m_ownerRangeEnd[2]);
77 
78       Range gc_ii = subset_range(z_i, gc_i);
79       Range gc_jj = subset_range(z_j, gc_j);
80       Range gc_kk = subset_range(z_k, gc_k);
81 
82       Ioss::IJK_t range_beg;
83       Ioss::IJK_t range_end;
84       range_beg[0] = gc_ii.begin();
85       range_end[0] = gc_ii.end();
86       range_beg[1] = gc_jj.begin();
87       range_end[1] = gc_jj.end();
88       range_beg[2] = gc_kk.begin();
89       range_end[2] = gc_kk.end();
90 
91       if (zgc.m_sameRange) {
92         zgc.m_ownerRangeBeg = range_beg;
93         zgc.m_ownerRangeEnd = range_end;
94         zgc.m_donorRangeBeg = zgc.m_ownerRangeBeg;
95         zgc.m_donorRangeEnd = zgc.m_ownerRangeEnd;
96       }
97       else {
98         auto d_range_beg    = zgc.transform(range_beg);
99         zgc.m_donorRangeEnd = zgc.transform(range_end);
100         zgc.m_donorRangeBeg = d_range_beg;
101         zgc.m_ownerRangeBeg = range_beg;
102         zgc.m_ownerRangeEnd = range_end;
103       }
104       zgc.m_ownerOffset = {{zone->m_offset[0], zone->m_offset[1], zone->m_offset[2]}};
105       assert(zgc.is_valid());
106       zgc.m_isActive = zgc.has_faces();
107     }
108     else {
109       // This zgc does not overlap on this zone, so set all ranges to 0.
110       // Still need it in list so can write block out correctly in parallel...
111       zgc.m_ownerRangeBeg = {{0, 0, 0}};
112       zgc.m_ownerRangeEnd = {{0, 0, 0}};
113       zgc.m_donorRangeBeg = {{0, 0, 0}};
114       zgc.m_donorRangeEnd = {{0, 0, 0}};
115       zgc.m_isActive      = false;
116     }
117   }
118 
zgc_subset_donor_ranges(const Iocgns::StructuredZoneData * don_zone,Ioss::ZoneConnectivity & zgc)119   void zgc_subset_donor_ranges(const Iocgns::StructuredZoneData *don_zone,
120                                Ioss::ZoneConnectivity &          zgc)
121   {
122     // NOTE: Updates the range and donor_range in zgc
123 
124     // Note that zone range is nodes and m_ordinal[] is cells, so need to add 1 to range.
125     Range z_i(1 + don_zone->m_offset[0], don_zone->m_ordinal[0] + don_zone->m_offset[0] + 1);
126     Range z_j(1 + don_zone->m_offset[1], don_zone->m_ordinal[1] + don_zone->m_offset[1] + 1);
127     Range z_k(1 + don_zone->m_offset[2], don_zone->m_ordinal[2] + don_zone->m_offset[2] + 1);
128 
129     Range gc_i(zgc.m_donorRangeBeg[0], zgc.m_donorRangeEnd[0]);
130     Range gc_j(zgc.m_donorRangeBeg[1], zgc.m_donorRangeEnd[1]);
131     Range gc_k(zgc.m_donorRangeBeg[2], zgc.m_donorRangeEnd[2]);
132 
133     Range gc_ii = subset_range(z_i, gc_i);
134     Range gc_jj = subset_range(z_j, gc_j);
135     Range gc_kk = subset_range(z_k, gc_k);
136 
137     Ioss::IJK_t d_range_beg;
138     Ioss::IJK_t d_range_end;
139     d_range_beg[0] = gc_ii.begin();
140     d_range_end[0] = gc_ii.end();
141     d_range_beg[1] = gc_jj.begin();
142     d_range_end[1] = gc_jj.end();
143     d_range_beg[2] = gc_kk.begin();
144     d_range_end[2] = gc_kk.end();
145 
146     if (zgc.m_sameRange) {
147       zgc.m_donorRangeBeg = d_range_beg;
148       zgc.m_donorRangeEnd = d_range_end;
149       zgc.m_ownerRangeEnd = zgc.m_donorRangeEnd;
150       zgc.m_ownerRangeBeg = zgc.m_donorRangeBeg;
151     }
152     else {
153       auto range_beg      = zgc.inverse_transform(d_range_beg);
154       zgc.m_ownerRangeEnd = zgc.inverse_transform(d_range_end);
155       zgc.m_ownerRangeBeg = range_beg;
156       zgc.m_donorRangeBeg = d_range_beg;
157       zgc.m_donorRangeEnd = d_range_end;
158     }
159     zgc.m_donorOffset = {{don_zone->m_offset[0], don_zone->m_offset[1], don_zone->m_offset[2]}};
160     assert(zgc.is_valid());
161   }
162 
propogate_zgc(Iocgns::StructuredZoneData * parent,Iocgns::StructuredZoneData * child)163   void propogate_zgc(Iocgns::StructuredZoneData *parent, Iocgns::StructuredZoneData *child)
164   {
165     for (auto zgc : parent->m_zoneConnectivity) {
166       if (!zgc.is_from_decomp() || zgc_overlaps(child, zgc)) {
167         // Modify source and donor range to subset it to new block ranges.
168         zgc_subset_ranges(child, zgc);
169         zgc.m_ownerZone = child->m_zone;
170         child->m_zoneConnectivity.push_back(zgc);
171       }
172     }
173   }
174 
175   // Add the zgc corresponding to the new communication path between
176   // two child zones arising from a parent split along ordinal 'ordinal'
add_proc_split_zgc(Iocgns::StructuredZoneData * parent,Iocgns::StructuredZoneData * c1,Iocgns::StructuredZoneData * c2,int ordinal)177   void add_proc_split_zgc(Iocgns::StructuredZoneData *parent, Iocgns::StructuredZoneData *c1,
178                           Iocgns::StructuredZoneData *c2, int ordinal)
179   {
180     Ioss::IJK_t transform{{1, 2, 3}};
181 
182     // Note that range is specified in terms of 'adam' block i,j,k
183     // space which is converted to local block i,j,k space
184     // via the m_offset[] field on the local block.
185     Ioss::IJK_t range_beg{{1 + c1->m_offset[0], 1 + c1->m_offset[1], 1 + c1->m_offset[2]}};
186     Ioss::IJK_t range_end{{c1->m_ordinal[0] + c1->m_offset[0] + 1,
187                            c1->m_ordinal[1] + c1->m_offset[1] + 1,
188                            c1->m_ordinal[2] + c1->m_offset[2] + 1}};
189 
190     Ioss::IJK_t donor_range_beg(range_beg);
191     Ioss::IJK_t donor_range_end(range_end);
192 
193     donor_range_end[ordinal] = donor_range_beg[ordinal] = range_beg[ordinal] = range_end[ordinal];
194 
195     auto c1_base = std::to_string(c1->m_adam->m_zone) + "_" + std::to_string(c1->m_zone);
196     auto c2_base = std::to_string(c2->m_adam->m_zone) + "_" + std::to_string(c2->m_zone);
197 
198     const auto &adam_name = parent->m_adam->m_name;
199 
200     SMART_ASSERT(c1->m_adam->m_zone == c2->m_adam->m_zone)(c1->m_adam->m_zone)(c2->m_adam->m_zone);
201 
202     c1->m_zoneConnectivity.emplace_back(c1_base + "--" + c2_base, c1->m_zone, adam_name, c2->m_zone,
203                                         transform, range_beg, range_end, donor_range_beg,
204                                         donor_range_end, true, true);
205     auto &zgc1         = c1->m_zoneConnectivity.back();
206     zgc1.m_sameRange   = true;
207     zgc1.m_ownerOffset = {{c1->m_offset[0], c1->m_offset[1], c1->m_offset[2]}};
208     zgc1.m_donorOffset = {{c2->m_offset[0], c2->m_offset[1], c2->m_offset[2]}};
209 
210     c2->m_zoneConnectivity.emplace_back(c2_base + "--" + c1_base, c2->m_zone, adam_name, c1->m_zone,
211                                         transform, donor_range_beg, donor_range_end, range_beg,
212                                         range_end, false, true);
213     auto &zgc2         = c2->m_zoneConnectivity.back();
214     zgc2.m_sameRange   = true;
215     zgc2.m_ownerOffset = {{c2->m_offset[0], c2->m_offset[1], c2->m_offset[2]}};
216     zgc2.m_donorOffset = {{c1->m_offset[0], c1->m_offset[1], c1->m_offset[2]}};
217   }
218 } // namespace
219 
220 namespace Iocgns {
221 
StructuredZoneData(int zone,const std::string & nixnjxnk)222   StructuredZoneData::StructuredZoneData(int zone, const std::string &nixnjxnk) : m_zone(zone)
223   {
224     m_name = "zone_" + std::to_string(zone);
225 
226     auto ordinals = Ioss::tokenize(nixnjxnk, "x");
227     SMART_ASSERT(ordinals.size() == 3)(ordinals.size());
228 
229     m_ordinal[0] = std::stoi(ordinals[0]);
230     m_ordinal[1] = std::stoi(ordinals[1]);
231     m_ordinal[2] = std::stoi(ordinals[2]);
232 
233     m_adam = this;
234   }
235 
236   // ========================================================================
237   // Split this StructuredZone along the largest ordinal
238   // into two children and return the created zones.
239   std::pair<StructuredZoneData *, StructuredZoneData *>
split(int zone_id,double avg_work,int rank,bool verbose)240   StructuredZoneData::split(int zone_id, double avg_work, int rank, bool verbose)
241   {
242     assert(is_active());
243     double ratio = avg_work / work();
244     if (ratio > 1.0) {
245       ratio = 1.0 / ratio;
246     }
247 
248     auto ord0 = llround((double)m_ordinal[0] * ratio);
249     auto ord1 = llround((double)m_ordinal[1] * ratio);
250     auto ord2 = llround((double)m_ordinal[2] * ratio);
251 
252     size_t work0 = ord0 * m_ordinal[1] * m_ordinal[2];
253     size_t work1 = ord1 * m_ordinal[0] * m_ordinal[2];
254     size_t work2 = ord2 * m_ordinal[0] * m_ordinal[1];
255 
256     // Don't decompose along m_lineOrdinal direction and Avoid decompositions 1-cell thick.
257     if (m_lineOrdinal & Ordinal::I || m_ordinal[0] == 1 || ord0 == 1 || m_ordinal[0] - ord0 == 1) {
258       work0 = 0;
259     }
260     if (m_lineOrdinal & Ordinal::J || m_ordinal[1] == 1 || ord1 == 1 || m_ordinal[1] - ord1 == 1) {
261       work1 = 0;
262     }
263     if (m_lineOrdinal & Ordinal::K || m_ordinal[2] == 1 || ord2 == 1 || m_ordinal[2] - ord2 == 1) {
264       work2 = 0;
265     }
266 
267     bool enforce_1cell_constraint = true;
268     if (work0 == 0 && work1 == 0 && work2 == 0) {
269       // Need to relax the "cells > 1" constraint...
270       work0 = ord0 * m_ordinal[1] * m_ordinal[2];
271       work1 = ord1 * m_ordinal[0] * m_ordinal[2];
272       work2 = ord2 * m_ordinal[0] * m_ordinal[1];
273       if (m_lineOrdinal & Ordinal::I || m_ordinal[0] == 1) {
274         work0 = 0;
275       }
276       if (m_lineOrdinal & Ordinal::J || m_ordinal[1] == 1) {
277         work1 = 0;
278       }
279       if (m_lineOrdinal & Ordinal::K || m_ordinal[2] == 1) {
280         work2 = 0;
281       }
282       enforce_1cell_constraint = false;
283     }
284 
285     auto delta0 = std::make_pair(std::abs((double)work0 - avg_work), -m_ordinal[0]);
286     auto delta1 = std::make_pair(std::abs((double)work1 - avg_work), -m_ordinal[1]);
287     auto delta2 = std::make_pair(std::abs((double)work2 - avg_work), -m_ordinal[2]);
288 
289     auto min_ordinal = 0;
290     auto min_delta   = delta0;
291     if (delta1 < min_delta) {
292       min_ordinal = 1;
293       min_delta   = delta1;
294     }
295     if (delta2 < min_delta) {
296       min_ordinal = 2;
297       min_delta   = delta2;
298     }
299 
300     unsigned int ordinal = min_ordinal;
301 
302     // One more check to try to produce more "squarish" decompositions.
303     // Check the ratio of max ordinal to selected min_ordinal and if > 1.5 (heuristic), choose the
304     // max ordinal instead.
305     int max_ordinal    = -1;
306     int max_ordinal_sz = 0;
307     for (int i = 0; i < 3; i++) {
308       unsigned ord = i == 0 ? 1 : 2 * i;
309       if (!(m_lineOrdinal & ord) && m_ordinal[i] > max_ordinal_sz) {
310         max_ordinal    = i;
311         max_ordinal_sz = m_ordinal[i];
312       }
313     }
314 
315     if (max_ordinal != -1 && (double)max_ordinal_sz / m_ordinal[ordinal] > 1.5) {
316       ordinal = max_ordinal;
317     }
318 
319     if ((m_ordinal[ordinal] <= (enforce_1cell_constraint ? 1 : 0)) ||
320         (work0 == 0 && work1 == 0 && work2 == 0)) {
321       return std::make_pair(nullptr, nullptr);
322     }
323 
324     //    SMART_ASSERT(!(ordinal & m_lineOrdinal))(ordinal)(m_lineOrdinal);
325 
326     m_child1 = new StructuredZoneData;
327     m_child2 = new StructuredZoneData;
328 
329     m_child1->m_name             = m_name + "_c1";
330     m_child1->m_ordinal          = m_ordinal;
331     m_child1->m_ordinal[ordinal] = llround(m_ordinal[ordinal] * ratio);
332     if (m_child1->m_ordinal[ordinal] == 0) {
333       m_child1->m_ordinal[ordinal] = 1;
334     }
335     SMART_ASSERT(!enforce_1cell_constraint || m_child1->m_ordinal[ordinal] != 1)
336     (!enforce_1cell_constraint)(m_child1->m_ordinal[ordinal] != 1);
337 
338     m_child1->m_offset = m_offset; // Child1 offsets the same as parent;
339 
340     m_child1->m_lineOrdinal  = m_lineOrdinal;
341     m_child1->m_zone         = zone_id++;
342     m_child1->m_adam         = m_adam;
343     m_child1->m_parent       = this;
344     m_child1->m_splitOrdinal = ordinal;
345     m_child1->m_sibling      = m_child2;
346 
347     m_child2->m_name             = m_name + "_c2";
348     m_child2->m_ordinal          = m_ordinal;
349     m_child2->m_ordinal[ordinal] = m_ordinal[ordinal] - m_child1->m_ordinal[ordinal];
350     assert(m_child2->m_ordinal[ordinal] > 0);
351     assert(!enforce_1cell_constraint || m_child2->m_ordinal[ordinal] != 1);
352     m_child2->m_offset = m_offset;
353     m_child2->m_offset[ordinal] += m_child1->m_ordinal[ordinal];
354 
355     m_child2->m_lineOrdinal  = m_lineOrdinal;
356     m_child2->m_zone         = zone_id++;
357     m_child2->m_adam         = m_adam;
358     m_child2->m_parent       = this;
359     m_child2->m_splitOrdinal = ordinal;
360     m_child2->m_sibling      = m_child1;
361 
362     if (rank == 0 && verbose) {
363       fmt::print(
364           Ioss::DEBUG(), "{}",
365           fmt::format(
366               fg(fmt::color::cyan),
367               "\nSplit Zone {} ({}) Adam {} ({}) with intervals {:>12},\twork = {:12L}, offset {} "
368               "{} {}, ordinal {}, ratio {:.3f}\n",
369               m_name, m_zone, m_adam->m_name, m_adam->m_zone,
370               fmt::format("{} {} {}", m_ordinal[0], m_ordinal[1], m_ordinal[2]), work(),
371               m_offset[0], m_offset[1], m_offset[2], ordinal, ratio));
372 
373       fmt::print(Ioss::DEBUG(),
374                  "\tChild 1: Zone {} ({}) with intervals {:>12},\twork = {:12L}, offset "
375                  "{} {} {}\n"
376                  "\tChild 2: Zone {} ({}) with intervals {:>12},\twork = {:12L}, offset "
377                  "{} {} {}\n",
378                  m_child1->m_name, m_child1->m_zone,
379                  fmt::format("{} {} {}", m_child1->m_ordinal[0], m_child1->m_ordinal[1],
380                              m_child1->m_ordinal[2]),
381                  m_child1->work(), m_child1->m_offset[0], m_child1->m_offset[1],
382                  m_child1->m_offset[2], m_child2->m_name, m_child2->m_zone,
383                  fmt::format("{} {} {}", m_child2->m_ordinal[0], m_child2->m_ordinal[1],
384                              m_child2->m_ordinal[2]),
385                  m_child2->work(), m_child2->m_offset[0], m_child2->m_offset[1],
386                  m_child2->m_offset[2]);
387     }
388 
389     // Add ZoneGridConnectivity instance to account for split...
390     add_proc_split_zgc(this, m_child1, m_child2, ordinal);
391 
392     // Propagate parent ZoneGridConnectivities to appropriate children.
393     // Split if needed...
394     propogate_zgc(this, m_child1);
395     propogate_zgc(this, m_child2);
396 
397     return std::make_pair(m_child1, m_child2);
398   }
399 
400   namespace {
update_zgc(Ioss::ZoneConnectivity & zgc,Iocgns::StructuredZoneData * child,std::vector<Ioss::ZoneConnectivity> & zgc_vec,bool new_zgc)401     void update_zgc(Ioss::ZoneConnectivity &zgc, Iocgns::StructuredZoneData *child,
402                     std::vector<Ioss::ZoneConnectivity> &zgc_vec, bool new_zgc)
403     {
404       zgc.m_donorZone = child->m_zone;
405       zgc_subset_donor_ranges(child, zgc);
406       // If `!new_zgc`, then the zgc is already in `zgc_vec`
407       if (new_zgc) {
408         zgc_vec.push_back(zgc);
409       }
410     }
411   } // namespace
412 
413   // If a zgc points to a donor zone which was split (has non-null children),
414   // then create two zgc that point to each child.  Update range and donor_range
resolve_zgc_split_donor(const std::vector<Iocgns::StructuredZoneData * > & zones)415   void StructuredZoneData::resolve_zgc_split_donor(
416       const std::vector<Iocgns::StructuredZoneData *> &zones)
417   {
418     // Updates m_zoneConnectivity in place, but in case a new zgc is created,
419     // need a place to store it to avoid invalidating any iterators...
420     // Guess at size to avoid as many reallocations as possible.
421     // At most 1 new zgc per split...
422     std::vector<Ioss::ZoneConnectivity> new_zgc;
423     new_zgc.reserve(m_zoneConnectivity.size());
424 
425     bool did_split = false;
426     do {
427       did_split = false;
428 
429       for (auto &zgc : m_zoneConnectivity) {
430         auto &donor_zone = zones[zgc.m_donorZone - 1];
431         if (!donor_zone->is_active()) {
432           did_split = true;
433 
434           bool overlap_1 = zgc_donor_overlaps(donor_zone->m_child1, zgc);
435           bool overlap_2 = zgc_donor_overlaps(donor_zone->m_child2, zgc);
436           bool overlap   = overlap_1 || overlap_2;
437 
438           // Child 1
439           if (overlap_1) {
440             if (!overlap_2) {
441               // Use `zgc` since don't need it anymore...
442               update_zgc(zgc, donor_zone->m_child1, new_zgc, false);
443             }
444             else {
445               auto c1_zgc(zgc);
446               update_zgc(c1_zgc, donor_zone->m_child1, new_zgc, true);
447             }
448           }
449 
450           // Child 2
451           if (overlap_2) {
452             // Use `zgc` since don't need it anymore...
453             update_zgc(zgc, donor_zone->m_child2, new_zgc, false);
454           }
455 
456           if (!overlap) {
457             // Need to add at least one copy of this zgc even if no overlap
458             // so can maintain the original (un parallel decomposed) ranges
459             // for use in output...
460             zgc.m_donorZone     = donor_zone->m_child1->m_zone;
461             zgc.m_ownerRangeBeg = zgc.m_ownerRangeEnd = {{0, 0, 0}};
462             zgc.m_donorRangeBeg = zgc.m_donorRangeEnd = {{0, 0, 0}};
463           }
464         }
465       }
466       if (did_split) {
467         if (!new_zgc.empty()) {
468           m_zoneConnectivity.insert(m_zoneConnectivity.end(), new_zgc.begin(), new_zgc.end());
469           new_zgc.clear();
470         }
471         // Filter out all zgc that do not contain any faces unless needed to maintain original zgc
472         // reconstruction...
473         m_zoneConnectivity.erase(
474             std::remove_if(m_zoneConnectivity.begin(), m_zoneConnectivity.end(),
475                            [](Ioss::ZoneConnectivity &zgc) {
476                              return zgc.get_shared_node_count() <= 2 && !zgc.retain_original();
477                            }),
478             m_zoneConnectivity.end());
479       }
480     } while (did_split);
481   }
482 
483   void
update_zgc_processor(const std::vector<Iocgns::StructuredZoneData * > & zones)484   StructuredZoneData::update_zgc_processor(const std::vector<Iocgns::StructuredZoneData *> &zones)
485   {
486     for (auto &zgc : m_zoneConnectivity) {
487       auto &donor_zone = zones[zgc.m_donorZone - 1];
488       assert(donor_zone->m_proc >= 0);
489       zgc.m_donorProcessor = donor_zone->m_proc;
490       auto &owner_zone     = zones[zgc.m_ownerZone - 1];
491       assert(owner_zone->m_proc >= 0);
492       zgc.m_ownerProcessor = owner_zone->m_proc;
493     }
494   }
495 } // namespace Iocgns
496