Nektar++
Loading...
Searching...
No Matches
InterfaceMapDG.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File InterfaceMapDG.cpp
4//
5// For more information, please see: http://www.nektar.info
6//
7// The MIT License
8//
9// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10// Department of Aeronautics, Imperial College London (UK), and Scientific
11// Computing and Imaging Institute, University of Utah (USA).
12//
13// Permission is hereby granted, free of charge, to any person obtaining a
14// copy of this software and associated documentation files (the "Software"),
15// to deal in the Software without restriction, including without limitation
16// the rights to use, copy, modify, merge, publish, distribute, sublicense,
17// and/or sell copies of the Software, and to permit persons to whom the
18// Software is furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included
21// in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29// DEALINGS IN THE SOFTWARE.
30//
31// Description: MPI communication for interfaces
32//
33///////////////////////////////////////////////////////////////////////////////
34
37
39{
40
42 const ExpListSharedPtr &trace,
43 const SpatialDomains::InterfaceShPtr &interfaceShPtr,
45 : m_trace(trace), m_interface(interfaceShPtr), m_movement(movementShPtr)
46{
47}
48
49// Creates the objects that communicates trace values across the interface in
50// serial and parallel. Does this by checking all interfaces to see if they
51// are contained locally (on the current rank), and checks all other ranks for
52// the same interface ID and if contains the opposite interface side i.e.
53// 'left' or 'right'
56 const ExpListSharedPtr &trace)
57 : m_graph(meshGraph), m_movement(meshGraph->GetMovement()), m_trace(trace)
58{
59 auto comm = m_trace->GetComm()->GetSpaceComm();
60 auto interfaceCollection = m_movement->GetInterfaces();
61
62 // myIndxLR contains the info about what interface edges are present on
63 // current rank with each interface no, i, consisting of:
64 // [i] = indx
65 // [i + 1] = 0 (non), = 1 (left only), = 2 (right only), = 3 (both)
66 std::map<int, int> myIndxLRMap;
67
68 // localInterfaces contains a map of interface ID to a pair of interface
69 // traces, this pair is 'left' and 'right' interface and is used to
70 // construct and store the traces for interfaces present on the current rank
71 std::map<int, std::pair<InterfaceTraceSharedPtr, InterfaceTraceSharedPtr>>
72 localInterfaces;
73
74 // Map of m_localInterfaces vector to interface ID
75 Array<OneD, int> indxToInterfaceID(interfaceCollection.size());
76
77 // Loops over all interfaces and check if either side, 'left' or 'right'
78 // are empty on the current rank, if not empty then populate the local
79 // interface data structures and keep track using myIndxLRMap
80 size_t cnt = 0;
81 for (const auto &interface : interfaceCollection)
82 {
83 indxToInterfaceID[cnt] = interface.first.first;
84 myIndxLRMap[interface.first.first] = 0;
85
86 if (!interface.second->GetLeftInterface()->IsEmpty())
87 {
88 myIndxLRMap[interface.first.first] += 1;
89
90 localInterfaces[interface.first.first].first =
92 trace, interface.second->GetLeftInterface(), m_movement);
93 m_localInterfaces.emplace_back(
94 localInterfaces[interface.first.first].first);
95 }
96
97 if (!interface.second->GetRightInterface()->IsEmpty())
98 {
99 myIndxLRMap[interface.first.first] += 2;
100
101 localInterfaces[interface.first.first].second =
103 trace, interface.second->GetRightInterface(), m_movement);
104 m_localInterfaces.emplace_back(
105 localInterfaces[interface.first.first].second);
106 }
107
108 cnt++;
109 }
110
111 // Send num of interfaces size so all partitions can prepare buffers
112 int nRanks = comm->GetSize();
113
114 // Send all interface edges present to all partitions
115 Array<OneD, int> interfaceEdges(myIndxLRMap.size());
116 cnt = 0;
117 for (auto pres : myIndxLRMap)
118 {
119 interfaceEdges[cnt++] = pres.second;
120 }
121 Array<OneD, int> rankLocalInterfaceIds(myIndxLRMap.size() * nRanks, 0);
122 comm->AllGather(interfaceEdges, rankLocalInterfaceIds);
123
124 // Map of rank to vector of interface traces
125 std::map<int, std::vector<InterfaceTraceSharedPtr>> oppRankSharedInterface;
126
127 // Find what interface Ids match with other ranks, then check if opposite
128 // edge
129 size_t myRank = comm->GetRank();
130 size_t numInterfaces = interfaceCollection.size();
131 for (int i = 0; i < nRanks; ++i)
132 {
133 for (size_t j = 0; j < numInterfaces; ++j)
134 {
135 int otherId = indxToInterfaceID[j];
136
137 // otherCode represents for a specific interface ID what sides are
138 // present on rank i, 0=non, 1=left, 2=right, 3=both
139 int otherCode = rankLocalInterfaceIds[i * numInterfaces + j];
140
141 // myCode represents for a specific interface ID what sides are
142 // present on this rank/process 0=non, 1=left, 2=right, 3=both
143 int myCode = myIndxLRMap[otherId];
144
145 // Special case if checking current rank (this process)
146 if (i == myRank)
147 {
148 // If contains both edges locally then set check local to true
149 if (myCode == 3)
150 {
151 localInterfaces[otherId].first->SetCheckLocal(true);
152 localInterfaces[otherId].second->SetCheckLocal(true);
153 }
154
155 continue;
156 }
157
158 // Checks if this ranks 'left' matches any 'right' on other rank
159 if ((myCode == 1 && otherCode == 2) ||
160 (myCode == 1 && otherCode == 3) ||
161 (myCode == 3 && otherCode == 2))
162 {
163 oppRankSharedInterface[i].emplace_back(
164 localInterfaces[otherId].first);
165 }
166 // Checks if this ranks 'right' matches any 'left' on other rank
167 else if ((myCode == 2 && otherCode == 1) ||
168 (myCode == 2 && otherCode == 3) ||
169 (myCode == 3 && otherCode == 1))
170 {
171 oppRankSharedInterface[i].emplace_back(
172 localInterfaces[otherId].second);
173 }
174 // Checks if this ranks 'both' matches any 'both' on other rank
175 else if (myCode == 3 && otherCode == 3)
176 {
177 oppRankSharedInterface[i].emplace_back(
178 localInterfaces[otherId].first);
179 oppRankSharedInterface[i].emplace_back(
180 localInterfaces[otherId].second);
181 }
182 }
183 }
184
185 // Create individual interface exchange objects (each object is rank ->
186 // rank) and contains a vector of interfaceTrace objects
187 for (auto &rank : oppRankSharedInterface)
188 {
189 m_exchange.emplace_back(
191 m_movement, m_trace, comm, rank));
192 }
193
194 // Find missing coordinates on interface from other side
196}
197
199{
201 timer.Start();
202
203 auto comm = m_trace->GetComm();
204 auto zones = m_movement->GetZones();
205
206 LibUtilities::Timer timer2;
207 timer2.Start();
208 for (auto &interfaceTrace : m_localInterfaces)
209 {
210 interfaceTrace->CalcLocalMissing();
211 }
212 timer2.Stop();
213 timer2.AccumulateRegion("InterfaceMapDG::ExchangeCoords local", 1);
214
215 // If parallel communication is needed
216 if (!m_exchange.empty())
217 {
218 LibUtilities::Timer timer3;
219 timer3.Start();
220
221 auto requestSend = comm->CreateRequest(m_exchange.size());
222 auto requestRecv = comm->CreateRequest(m_exchange.size());
223
224 for (int i = 0; i < m_exchange.size(); ++i)
225 {
226 m_exchange[i]->RankFillSizes(requestSend, requestRecv, i);
227 }
228 comm->WaitAll(requestSend);
229 comm->WaitAll(requestRecv);
230
231 for (int i = 0; i < m_exchange.size(); ++i)
232 {
233 m_exchange[i]->SendMissing(requestSend, requestRecv, i);
234 }
235 comm->WaitAll(requestSend);
236 comm->WaitAll(requestRecv);
237
238 for (auto &i : m_exchange)
239 {
240 i->CalcRankDistances();
241 }
242
243 timer3.Stop();
244 timer3.AccumulateRegion("InterfaceMapDG::ExchangeCoords parallel", 1);
245 }
246
247 m_movement->GetCoordExchangeFlag() = false;
248
249 timer.Stop();
250 timer.AccumulateRegion("InterfaceMapDG::ExchangeCoords");
251}
252
253/**
254 * Calculates what coordinates on the interface are missing; i.e. aren't located
255 * in an edge from the other side of the interface on this partition. These are
256 * stored in the vector #m_missingCoords, and another vector of the same index
257 * layout contains the location of that coordinate in the trace i.e. the
258 * 'offset + i' value. It checks first if the interface is flagged local, which
259 * denotes whether the opposite side is also present on this rank, if not then
260 * all coordinates are missing. If local then the same structure as
261 * CalcRankDistances is used to minimise computational cost.
262 */
264{
265 // Nuke old missing/found
266 m_missingCoords.clear();
268
269 // Store copy of found coords to optimise search before clearing
270 auto foundLocalCoordsCopy = m_foundLocalCoords;
271 m_foundLocalCoords.clear();
272 // Zone id
273 int id = m_interface->GetId();
274 NekDouble angle = 0.0;
275
276 auto childEdge = m_interface->GetEdge();
277 // If not flagged 'check local' then all points are missing
278 if (!m_checkLocal)
279 {
280 int cnt = 0;
281 for (auto &childId : childEdge)
282 {
283 auto childElmt = m_trace->GetExpFromGeomId(childId.first);
284 size_t nq = childElmt->GetTotPoints();
285 Array<OneD, NekDouble> xc(nq, 0.0), yc(nq, 0.0), zc(nq, 0.0);
286 childElmt->GetCoords(xc, yc, zc);
287 int offset =
288 m_trace->GetPhys_Offset(m_trace->GetElmtToExpId(childId.first));
289 for (int i = 0; i < nq; ++i, ++cnt)
290 {
292 xs[0] = xc[i];
293 xs[1] = yc[i];
294 xs[2] = zc[i];
295
296 // If mesh has moved, need to update corresponding points on
297 // interface
298 if (m_movement->GetMovedFlag())
299 {
300 DomainCheck(xs, angle);
301
302 // Record the rotation angle for sector rotation
303 if (m_movement->GetSectorRotateFlag())
304 {
305 m_phyRotAngle[offset + i] = std::make_pair(id, angle);
306 }
307 }
308
309 m_missingCoords.emplace_back(xs);
310 m_mapMissingCoordToTrace.emplace_back(offset + i);
311 }
312 }
313 }
314 // Otherwise we need to check to see what points the other side of the
315 // interface on this rank contains. This is done by looping over each
316 // quadrature point in all the edges on this side of the interface, then
317 // check if this quadrature point is also in any edges on the other side
318 // of the interface. First use MinMaxCheck as a cheap method to eliminate
319 // far away edges. If it passes MinMaxCheck perform the FindDistance search
320 // which numerically searches for the closest local coordinate on the other
321 // interface side edge and returns the cartesian distance from the search
322 // coordinate to the found local coordinate. If determined to be close
323 // enough then add to found local coordinates.
324 else
325 {
326 for (auto &childId : childEdge)
327 {
328 auto childElmt = m_trace->GetExpFromGeomId(childId.first);
329 size_t nq = childElmt->GetTotPoints();
330 Array<OneD, NekDouble> xc(nq, 0.0), yc(nq, 0.0), zc(nq, 0.0);
331 childElmt->GetCoords(xc, yc, zc);
332 int offset =
333 m_trace->GetPhys_Offset(m_trace->GetElmtToExpId(childId.first));
334 for (int i = 0; i < nq; ++i)
335 {
336 bool found = false;
337 Array<OneD, NekDouble> foundLocCoord;
339 xs[0] = xc[i];
340 xs[1] = yc[i];
341 xs[2] = zc[i];
342
343 // If mesh has moved, need to update corresponding points on
344 // interface
345 if (m_movement->GetMovedFlag())
346 {
347 DomainCheck(xs, angle);
348
349 // Record the rotation angle for sector rotation
350 if (m_movement->GetSectorRotateFlag())
351 {
352 m_phyRotAngle[offset + i] = std::make_pair(id, angle);
353 }
354 }
355
356 // First search the edge the point was found in last timestep
357 if (foundLocalCoordsCopy.find(offset + i) !=
358 foundLocalCoordsCopy.end())
359 {
360 auto edge = m_interface->GetOppInterface()->GetEdge(
361 foundLocalCoordsCopy[offset + i].first);
362 NekDouble dist = edge->FindDistance(xs, foundLocCoord);
364 {
365 m_foundLocalCoords[offset + i] =
366 std::make_pair(edge->GetGlobalID(), foundLocCoord);
367 continue;
368 }
369 }
370
371 // If not in last timestep edge then loop over all interface
372 // edges
373 auto parentEdge = m_interface->GetOppInterface()->GetEdge();
374 for (auto &edge : parentEdge)
375 {
376 // First check if inside the edge bounding box
377 if (!edge.second->MinMaxCheck(xs))
378 {
379 continue;
380 }
381
382 NekDouble dist =
383 edge.second->FindDistance(xs, foundLocCoord);
385 {
386 found = true;
387 m_foundLocalCoords[offset + i] = std::make_pair(
388 edge.second->GetGlobalID(), foundLocCoord);
389 break;
390 }
391 }
392
393 if (!found)
394 {
395 m_missingCoords.emplace_back(xs);
396 m_mapMissingCoordToTrace.emplace_back(offset + i);
397 }
398 }
399 }
400 }
401
402 // If running in serial there shouldn't be any missing coordinates.
403 // Unless we flag to ignore this check for this specific interface.
404 if (m_trace->GetComm()->IsSerial() && !m_interface->GetSkipCoordCheck())
405 {
407 "Missing " + std::to_string(m_missingCoords.size()) +
408 " coordinates on interface ID " +
409 std::to_string(m_interface->GetId()) +
410 " linked to interface ID " +
411 std::to_string(m_interface->GetOppInterface()->GetId()) +
412 ". Check both sides of the interface line up.");
413 }
414}
415
416// This fills m_sendSize and m_recvSize with the correct sizes for the
417// communication lengths for each rank when communicating the traces. I.e.
418// however many quadrature points on the opposite side of the interface to
419// send, and however many on this side to receive.
422 LibUtilities::CommRequestSharedPtr &requestRecv, int requestNum)
423{
424
425 // Recalc size is the number of interfaces missing points that need to be
426 // communicated.
427 int recalcSize = 0;
428 for (auto &localInterface : m_interfaceTraces)
429 {
430 recalcSize +=
431 m_zones[localInterface->GetInterface()->GetId()]->GetMoved();
432 }
433
434 m_sendSize[m_rank] = Array<OneD, int>(recalcSize);
435 m_recvSize[m_rank] = Array<OneD, int>(recalcSize);
436
437 int cnt = 0;
438 for (auto &interface : m_interfaceTraces)
439 {
440 if (m_zones[interface->GetInterface()->GetId()]->GetMoved())
441 {
442 // Calculates the number of missing coordinates to be communicated
443 // This size is 3 times the number of missing coordinates to allow
444 // for 3D points
445 m_sendSize[m_rank][cnt++] =
446 interface->GetMissingCoords().size() * 3;
447 }
448 }
449
450 // Uses non-blocking send/recvs to send communication sizes to other ranks
452 requestSend, requestNum);
454 requestRecv, requestNum);
455}
456
457// Uses the calculated send/recv sizes to send all missing coordinates to
458// other ranks.
461 LibUtilities::CommRequestSharedPtr &requestRecv, int requestNum)
462{
463 m_totSendSize[m_rank] = std::accumulate(m_sendSize[m_rank].begin(),
464 m_sendSize[m_rank].end(), 0);
465 m_totRecvSize[m_rank] = std::accumulate(m_recvSize[m_rank].begin(),
466 m_recvSize[m_rank].end(), 0);
467
470
471 int cnt = 0;
472 for (auto &interface : m_interfaceTraces)
473 {
474 if (m_zones[interface->GetInterface()->GetId()]->GetMoved())
475 {
476 auto missing = interface->GetMissingCoords();
477 for (auto coord : missing)
478 {
479 // Points are organised in blocks of three
480 // x coord, then y coord, then z coord.
481 for (int k = 0; k < 3; ++k, ++cnt)
482 {
483 m_send[cnt] = coord[k];
484 }
485 }
486 }
487 }
488
489 // Uses non-blocking send/recvs to send missing points to other ranks
490 m_comm->Isend(m_rank, m_send, m_totSendSize[m_rank], requestSend,
491 requestNum);
492 m_comm->Irecv(m_rank, m_recv, m_totRecvSize[m_rank], requestRecv,
493 requestNum);
494}
495
496/**
497 * Performs the trace exchange across interfaces
498 * 1) Calculate and send the Fwd trace to other ranks with pairwise comms
499 * 2) While waiting for the send/recv to complete we fill the local interfaces
500 * Bwd trace
501 * 3) Fill the remaining Bwd trace with the received trace data from other ranks
502 */
505{
506 if (m_movement->GetCoordExchangeFlag())
507 {
509 }
510 auto comm = m_trace->GetComm();
511 // If no parallel exchange needed we only fill the local traces
512 if (m_exchange.empty())
513 {
514
515 // Fill local interface traces
516 for (auto &m_localInterface : m_localInterfaces)
517 {
518 m_localInterface->FillLocalBwdTrace(Fwd, Bwd);
519 }
520 }
521 else
522 {
523 auto requestSend = comm->CreateRequest(m_exchange.size());
524 auto requestRecv = comm->CreateRequest(m_exchange.size());
525 for (int i = 0; i < m_exchange.size(); ++i)
526 {
527 m_exchange[i]->SendFwdTrace(requestSend, requestRecv, i, Fwd);
528 }
529
530 // Fill local interface traces
531 for (auto &m_localInterface : m_localInterfaces)
532 {
533 m_localInterface->FillLocalBwdTrace(Fwd, Bwd);
534 }
535
536 comm->WaitAll(requestSend);
537 comm->WaitAll(requestRecv);
538
539 // Fill communicated interface traces
540 for (auto &i : m_exchange)
541 {
542 i->FillRankBwdTraceExchange(Bwd);
543 }
544 }
545}
546
549{
550 // If flagged then fill trace from local coords
551 if (m_checkLocal)
552 {
553 for (auto &foundLocCoord : m_foundLocalCoords)
554 {
555 int traceId = m_trace->GetElmtToExpId(foundLocCoord.second.first);
556 Array<OneD, NekDouble> locCoord = foundLocCoord.second.second;
557
558 Array<OneD, NekDouble> edgePhys =
559 Fwd + m_trace->GetPhys_Offset(traceId);
560
561 Bwd[foundLocCoord.first] =
562 m_trace->GetExp(traceId)->StdPhysEvaluate(locCoord, edgePhys);
563 }
564 }
565}
566
568 const int &dim)
569{
570
572 vecLocs[0] = Array<OneD, NekDouble>(dim);
573 for (int i = 0; i < dim; ++i)
574 {
575 vecLocs[0][i] = 1 + i;
576 }
577 int id = m_interface->GetId();
578 auto zones = m_movement->GetZones();
579 auto zone = std::static_pointer_cast<SpatialDomains::ZoneRotate>(zones[id]);
580 Array<OneD, NekDouble> tmpVelocity(dim);
581
582 // Rotates the interface velocity for sector rotation
583 // Loop over rotated search coordinates
584 for (auto &phyRotAngle : m_phyRotAngle)
585 {
586 int traceId = phyRotAngle.first;
587 NekDouble angle = m_phyRotAngle[traceId].second;
588 if (angle != 0)
589 {
590 for (int i = 0; i < dim; ++i)
591 {
592 tmpVelocity[i] = Bwd[vecLocs[0][i]][traceId];
593 }
594 zone->Rotate(tmpVelocity, -angle);
595 for (int i = 0; i < dim; ++i)
596 {
597 Bwd[vecLocs[0][i]][traceId] = tmpVelocity[i];
598 }
599 }
600 }
601}
602
604 const int &dim)
605{
606
607 // Get rotating information from zones
608 size_t nVariables = Bwd[0].size();
609 int id = m_interface->GetId();
610 auto zones = m_movement->GetZones();
611 auto zone = std::static_pointer_cast<SpatialDomains::ZoneRotate>(zones[id]);
612 Array<OneD, NekDouble> tmpVelocity(dim);
613 auto axis = zone->GetAxis();
614 int dir = 0;
615 if (axis[0] == 0 && axis[1] == 0 && axis[2] == 1)
616 {
617 dir = 2;
618 }
619 else if (axis[0] == 1 && axis[1] == 0 && axis[2] == 0)
620 {
621 dir = 0;
622 }
623 else if (axis[0] == 0 && axis[1] == 1 && axis[2] == 0)
624 {
625 dir = 1;
626 }
627 else
628 {
629 NEKERROR(ErrorUtil::efatal, "Invalid rotation axis.");
630 }
631
632 // Rotates the derivative velocity for sector rotation
633 for (auto &phyRotAngle : m_phyRotAngle)
634 {
635 int traceId = phyRotAngle.first;
636 NekDouble angle = m_phyRotAngle[traceId].second;
637 if (angle != 0)
638 {
639 TensorOfArray3D<NekDouble> tmpBwd(dim);
640
641 for (int j = 0; j < dim; ++j)
642 {
643 tmpBwd[j] = Array<OneD, Array<OneD, NekDouble>>{nVariables};
644 for (int i = 0; i < nVariables; ++i)
645 {
646 tmpBwd[j][i] = Array<OneD, NekDouble>{1, 0.0};
647 }
648 }
649
650 for (int j = 0; j < dim; ++j)
651 {
652 for (int i = 0; i < nVariables; ++i)
653 {
654 tmpBwd[j][i][0] = Bwd[j][i][traceId];
655 }
656 }
657
658 DeriveRotate(tmpBwd, dir, -angle);
659
660 for (int j = 0; j < dim; ++j)
661 {
662 for (int i = 0; i < dim; ++i)
663 {
664 Bwd[j][i][traceId] = tmpBwd[j][i][0];
665 }
666 }
667 }
668 }
669}
670
672{
673 int cnt = 0;
674 for (int i = 0; i < m_interfaceTraces.size(); ++i)
675 {
676 Array<OneD, NekDouble> traceTmp(m_sendSize[m_rank][i] / 3, 0.0);
677 for (int j = 0; j < m_sendSize[m_rank][i] / 3; ++j, ++cnt)
678 {
679 traceTmp[j] = m_recvTrace[cnt];
680 }
681
682 m_interfaceTraces[i]->FillRankBwdTrace(traceTmp, Bwd);
683 }
684}
685
688{
689 for (int i = 0; i < m_mapMissingCoordToTrace.size(); ++i)
690 {
691 if (!std::isnan(trace[i]))
692 {
693 Bwd[m_mapMissingCoordToTrace[i]] = trace[i];
694 }
695 }
696}
697
700 LibUtilities::CommRequestSharedPtr &requestRecv, int requestNum,
702{
704 Array<OneD, NekDouble>(m_totSendSize[m_rank] / 3, std::nan(""));
706 Array<OneD, NekDouble>(m_totRecvSize[m_rank] / 3, std::nan(""));
707
708 for (auto &i : m_foundRankCoords[m_rank])
709 {
710 int traceId = m_trace->GetElmtToExpId(i.second.first.second);
711 Array<OneD, NekDouble> locCoord = i.second.second;
712
713 Array<OneD, NekDouble> edgePhys =
714 Fwd + m_trace->GetPhys_Offset(traceId);
715
716 m_sendTrace[i.first] =
717 m_trace->GetExp(traceId)->StdPhysEvaluate(locCoord, edgePhys);
718 }
719
720 m_comm->Isend(m_rank, m_sendTrace, m_sendTrace.size(), requestSend,
721 requestNum);
722 m_comm->Irecv(m_rank, m_recvTrace, m_recvTrace.size(), requestRecv,
723 requestNum);
724}
725
726/**
727 * Check coords in m_recv from other rank to see if present on this rank, and
728 * then populates m_foundRankCoords if found.
729 * - Loops over all interface traces present in the exchange, i.e. every
730 * interface needed on this rank-to-rank communication object
731 * - Then loops over all the quadrature points missing from the other side in
732 * that interface using global coordinates
733 * - Loop over each local edge and use MinMaxCheck on the point as a fast
734 * comparison on whether the point lies on the edge
735 * - If true then use the more expensive FindDistance function to find the
736 * closest local coordinate of the global 'missing' point on the local edge
737 * and the distance from the edge that corresponds to
738 * - If distance is less than 5e-5 save found coordinate in m_foundRankCoords
739 */
740
742{
743 // Clear old found coordinates
744 auto foundRankCoordsCopy = m_foundRankCoords[m_rank];
746 .clear(); // @TODO: This may cause problems with 2 interfaces and one is
747 // fixed? With the skip below.
748
749 Array<OneD, int> disp(m_recvSize[m_rank].size() + 1, 0.0);
750 std::partial_sum(m_recvSize[m_rank].begin(), m_recvSize[m_rank].end(),
751 &disp[1]);
752
753 for (int i = 0; i < m_interfaceTraces.size(); ++i)
754 {
755 if (!m_zones[m_interfaceTraces[i]->GetInterface()->GetId()]->GetMoved())
756 {
757 // If zone is not moved then skip
758 continue;
759 }
760
761 auto localEdge = m_interfaceTraces[i]->GetInterface()->GetEdge();
762
763 for (int j = disp[i]; j < disp[i + 1]; j += 3)
764 {
765 Array<OneD, NekDouble> foundLocCoord;
767 xs[0] = m_recv[j];
768 xs[1] = m_recv[j + 1];
769 xs[2] = m_recv[j + 2];
770
771 // First search the edge the point was found in last timestep
772 if ((foundRankCoordsCopy.find(j / 3) !=
773 foundRankCoordsCopy.end()) &&
774 (foundRankCoordsCopy[j / 3].first.first == i))
775 {
776 auto edge = m_interfaceTraces[i]->GetInterface()->GetEdge(
777 foundRankCoordsCopy[j / 3].first.second);
778 NekDouble dist = edge->FindDistance(xs, foundLocCoord);
779
781 {
782 m_foundRankCoords[m_rank][j / 3] = std::make_pair(
783 std::make_pair(i, edge->GetGlobalID()), foundLocCoord);
784 continue;
785 }
786 }
787
788 for (auto &edge : localEdge)
789 {
790 // First check if inside the edge bounding box
791 if (!edge.second->MinMaxCheck(xs))
792 {
793 continue;
794 }
795
796 NekDouble dist = edge.second->FindDistance(xs, foundLocCoord);
797
799 {
800 m_foundRankCoords[m_rank][j / 3] = std::make_pair(
801 std::make_pair(i, edge.second->GetGlobalID()),
802 foundLocCoord);
803 break;
804 }
805 }
806 }
807 }
808}
809
811 NekDouble &RotateAngle)
812{
813 // Get current interface id and opposite interface id
814 int id = m_interface->GetId();
815 int oppid = m_interface->GetOppInterface()->GetId();
816
817 // Get displacement of the current interface and the opposite interface
818 auto zones = m_movement->GetZones();
819
820 if (m_movement->GetTranslateFlag())
821 {
822 auto disp = zones[id]->v_GetDisp();
823 auto oppdisp = zones[oppid]->v_GetDisp();
825 Array<OneD, NekDouble> length(3);
826
827 // Get zone length and domian box from the translate zone
828 if (zones[id]->GetMovementType() ==
830 {
831 auto zone = std::static_pointer_cast<SpatialDomains::ZoneTranslate>(
832 zones[id]);
833 box = zone->GetZoneBox();
834 length = zone->GetZoneLength();
835 }
836 else if (zones[oppid]->GetMovementType() ==
838 {
839 auto zone = std::static_pointer_cast<SpatialDomains::ZoneTranslate>(
840 zones[oppid]);
841 box = zone->GetZoneBox();
842 length = zone->GetZoneLength();
843 }
844
845 NekDouble ExactMove = 0.0;
846 NekDouble ExactOppMove = 0.0;
847 int tmp = 0;
848
849 // update coordinates by add or minus domian length
850 for (int i = 0; i < disp.size(); ++i)
851 {
852
853 // map coordinate back to the original mesh
854 ExactMove = std::fmod(disp[i], length[i]);
855 ExactOppMove = std::fmod(oppdisp[i], length[i]);
856 tmp = oppdisp[i] / length[i];
857 gloCoord[i] = gloCoord[i] - disp[i] + ExactMove;
858
859 // update coordinates for perioidic interface
860 if (oppdisp[i] - disp[i] < 0)
861 {
862 if (gloCoord[i] > box[i + 3] + ExactOppMove + 1e-8)
863 {
864 gloCoord[i] = gloCoord[i] - length[i] + tmp * length[i];
865 }
866 else
867 {
868 gloCoord[i] = gloCoord[i] + tmp * length[i];
869 }
870 }
871 else
872 {
873 if (gloCoord[i] < box[i] + ExactOppMove - 1e-8)
874 {
875 gloCoord[i] = gloCoord[i] + length[i] + tmp * length[i];
876 }
877 else
878 {
879 gloCoord[i] = gloCoord[i] + tmp * length[i];
880 }
881 }
882 }
883 }
884
885 if (m_movement->GetSectorRotateFlag())
886 {
887 // Using rotation zone information to update the coordinates
888 auto zone =
889 std::static_pointer_cast<SpatialDomains::ZoneRotate>(zones[id]);
890 auto zone2 =
891 std::static_pointer_cast<SpatialDomains::ZoneRotate>(zones[oppid]);
892
893 // Get the rotation angle of the current interface and the opposite
894 // interface
895 auto angle = zone->v_GetAngle();
896 auto oppAngle = zone2->v_GetAngle();
897
898 // Get the sector angle
899 NekDouble SectorAngle = zone->GetSectorAngle();
900
901 NekDouble ExactOppMove = 0.0;
902 RotateAngle = 0.0;
903
904 int tmp1 = 0;
905 int tmp2 = 0;
906 int tmp3 = 0;
907
908 // Get the base coordinate of the sector
909 Array<OneD, NekDouble> base = zone->GetSectorBase();
910
911 // Normalize angles to [0, 2*PI)
912 angle = std::fmod(angle, 2 * M_PI);
913 if (angle < 0)
914 {
915 angle += 2 * M_PI;
916 }
917 oppAngle = std::fmod(oppAngle, 2 * M_PI);
918 if (oppAngle < 0)
919 {
920 oppAngle += 2 * M_PI;
921 }
922
923 // Map coordinate back to the original mesh
924 ExactOppMove = std::fmod(oppAngle, SectorAngle);
925 tmp1 = static_cast<int>(std::floor(angle / SectorAngle));
926 tmp2 = static_cast<int>(std::floor(oppAngle / SectorAngle));
927
928 // Compute the angle between the current coordinate and the base
929 // coordinate
930 auto axis = zone->GetAxis();
931 NekDouble diff = 0.0;
932 if (axis[0] == 0 && axis[1] == 0 && axis[2] == 1)
933 {
934 diff = atan2(gloCoord[1], gloCoord[0]) - atan2(base[1], base[0]);
935 }
936 else if (axis[0] == 1 && axis[1] == 0 && axis[2] == 0)
937 {
938 diff = atan2(gloCoord[2], gloCoord[1]) - atan2(base[2], base[1]);
939 }
940 else if (axis[0] == 0 && axis[1] == 1 && axis[2] == 0)
941 {
942 diff = atan2(gloCoord[0], gloCoord[2]) - atan2(base[0], base[2]);
943 }
944 else
945 {
946 NEKERROR(ErrorUtil::efatal, "Invalid rotation axis.");
947 }
948
949 // Normalize angles to [0, 2*PI)
950 if (diff < 0)
951 {
952 diff += 2 * M_PI;
953 }
954 tmp3 = static_cast<int>(std::floor(diff / SectorAngle));
955
956 // Compute the exact difference after mapping the angle back to the
957 // original mesh
958 NekDouble Exactdiff = std::fmod(diff, SectorAngle);
959
960 // update coordinates for perioidic interface
961 if (oppAngle - angle < 0)
962 {
963 if (ExactOppMove < Exactdiff && tmp3 != tmp1)
964 {
965 RotateAngle =
966 -SectorAngle + tmp2 * SectorAngle - tmp1 * SectorAngle;
967
968 zone->Rotate(gloCoord, RotateAngle);
969 }
970 else
971 {
972 RotateAngle = -tmp1 * SectorAngle + tmp2 * SectorAngle;
973 zone->Rotate(gloCoord, RotateAngle);
974 }
975 }
976 else
977 {
978 if (Exactdiff < ExactOppMove && tmp3 == tmp1)
979 {
980 RotateAngle =
981 SectorAngle + tmp2 * SectorAngle - tmp1 * SectorAngle;
982 zone->Rotate(gloCoord, RotateAngle);
983 }
984 else
985 {
986 RotateAngle = -tmp1 * SectorAngle + tmp2 * SectorAngle;
987 zone->Rotate(gloCoord, RotateAngle);
988 }
989 }
990 }
991}
992
994 const int dir, const NekDouble angle)
995{
996 // Precompute trigonometric values using correct angle input
997 const NekDouble cosA = cos(angle);
998 const NekDouble sinA = sin(angle);
999
1000 // Define rotation matrix R (3x3) using Array<OneD, NekDouble> in
1001 // **column-major order**
1002 Array<OneD, NekDouble> R(9, 0.0);
1003
1004 if (dir == 0) // Rotation around X-axis
1005 {
1006 R[0] = 1.0;
1007 R[3] = 0.0;
1008 R[6] = 0.0;
1009 R[1] = 0.0;
1010 R[4] = cosA;
1011 R[7] = -sinA;
1012 R[2] = 0.0;
1013 R[5] = sinA;
1014 R[8] = cosA;
1015 }
1016 else if (dir == 1) // Rotation around Y-axis
1017 {
1018 R[0] = cosA;
1019 R[3] = 0.0;
1020 R[6] = sinA;
1021 R[1] = 0.0;
1022 R[4] = 1.0;
1023 R[7] = 0.0;
1024 R[2] = -sinA;
1025 R[5] = 0.0;
1026 R[8] = cosA;
1027 }
1028 else if (dir == 2) // Rotation around Z-axis
1029 {
1030 R[0] = cosA;
1031 R[3] = -sinA;
1032 R[6] = 0.0;
1033 R[1] = sinA;
1034 R[4] = cosA;
1035 R[7] = 0.0;
1036 R[2] = 0.0;
1037 R[5] = 0.0;
1038 R[8] = 1.0;
1039 }
1040 else
1041 {
1042 NEKERROR(
1044 "Invalid rotation axis for first-order derivative transformation.");
1045 }
1046
1047 // Allocate gradient tensor (3x3) in **column-major order**
1048 Array<OneD, NekDouble> gradU(9, 0.0);
1049
1050 // Load original velocity gradient tensor into **column-major** format
1051 gradU[0] = Bwd[0][1][0]; // ∂u_x/∂x
1052 gradU[3] = Bwd[1][1][0]; // ∂u_x/∂y
1053 gradU[6] = Bwd[2][1][0]; // ∂u_x/∂z
1054
1055 gradU[1] = Bwd[0][2][0]; // ∂u_y/∂x
1056 gradU[4] = Bwd[1][2][0]; // ∂u_y/∂y
1057 gradU[7] = Bwd[2][2][0]; // ∂u_y/∂z
1058
1059 gradU[2] = Bwd[0][3][0]; // ∂u_z/∂x
1060 gradU[5] = Bwd[1][3][0]; // ∂u_z/∂y
1061 gradU[8] = Bwd[2][3][0]; // ∂u_z/∂z
1062
1063 // Allocate intermediate matrix R * gradU in **column-major order**
1064 Array<OneD, NekDouble> R_gradU(9, 0.0);
1065
1066 // Perform matrix multiplication R * gradU using Blas::Dgemm in
1067 // **column-major order**
1068 Blas::Dgemm('N', 'N', 3, 3, 3, 1.0, R.data(), 3, gradU.data(), 3, 0.0,
1069 R_gradU.data(), 3);
1070
1071 // Allocate final rotated gradient (R * gradU) * R^T in **column-major
1072 // order**
1073 Array<OneD, NekDouble> gradU_rotated(9, 0.0);
1074
1075 // Perform matrix multiplication (R * gradU) * R^T using Blas::Dgemm in
1076 // **column-major order**
1077 Blas::Dgemm('N', 'T', 3, 3, 3, 1.0, R_gradU.data(), 3, R.data(), 3, 0.0,
1078 gradU_rotated.data(), 3);
1079
1080 // Store rotated gradients back in Bwd in **column-major order**
1081 Bwd[0][1][0] = gradU_rotated[0]; // ∂u_x'/∂x'
1082 Bwd[1][1][0] = gradU_rotated[3]; // ∂u_x'/∂y'
1083 Bwd[2][1][0] = gradU_rotated[6]; // ∂u_x'/∂z'
1084
1085 Bwd[0][2][0] = gradU_rotated[1]; // ∂u_y'/∂x'
1086 Bwd[1][2][0] = gradU_rotated[4]; // ∂u_y'/∂y'
1087 Bwd[2][2][0] = gradU_rotated[7]; // ∂u_y'/∂z'
1088
1089 Bwd[0][3][0] = gradU_rotated[2]; // ∂u_z'/∂x'
1090 Bwd[1][3][0] = gradU_rotated[5]; // ∂u_z'/∂y'
1091 Bwd[2][3][0] = gradU_rotated[8]; // ∂u_z'/∂z'
1092}
1093
1094} // namespace Nektar::MultiRegions
#define ASSERTL0(condition, msg)
#define NEKERROR(type, msg)
Assert Level 0 – Fundamental assert which is used whether in FULLDEBUG, DEBUG or OPT compilation mode...
void AccumulateRegion(std::string, int iolevel=0)
Accumulate elapsed time for a region.
Definition Timer.cpp:70
General purpose memory allocation routines with the ability to allocate from thread specific memory p...
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
std::map< int, Array< OneD, int > > m_sendSize
Map of rank to array of size of send buffer for each interface.
void RankFillSizes(LibUtilities::CommRequestSharedPtr &requestSend, LibUtilities::CommRequestSharedPtr &requestRecv, int requestNum)
std::map< int, SpatialDomains::ZoneBaseShPtr > m_zones
Map of zone IDs to zone bases.
const ExpListSharedPtr m_trace
Trace expansion list.
void FillRankBwdTraceExchange(Array< OneD, NekDouble > &Bwd)
const std::vector< InterfaceTraceSharedPtr > m_interfaceTraces
Vector of interface traces i.e. every interface side present on m_rank.
const LibUtilities::CommSharedPtr m_comm
Communicator.
void SendFwdTrace(LibUtilities::CommRequestSharedPtr &requestSend, LibUtilities::CommRequestSharedPtr &requestRecv, int requestNum, Array< OneD, NekDouble > &Fwd)
Array< OneD, NekDouble > m_send
Send buffer for coord exchange.
std::map< int, int > m_totSendSize
Map of rank to total size of send buffer for all interfaces.
void CalcRankDistances()
Populates m_foundRankCoords using the FindDistance function.
std::map< int, int > m_totRecvSize
Map of rank to total size of receive buffer for all interfaces.
std::map< int, Array< OneD, int > > m_recvSize
Map of rank to array of size of receive buffer for each interface.
Array< OneD, NekDouble > m_recv
Receive buffer for coord exchange.
void SendMissing(LibUtilities::CommRequestSharedPtr &requestSend, LibUtilities::CommRequestSharedPtr &requestRecv, int requestNum)
Array< OneD, NekDouble > m_recvTrace
Receive buffer for trace exchange.
std::map< int, std::map< int, std::pair< std::pair< int, int >, Array< OneD, NekDouble > > > > m_foundRankCoords
Array< OneD, NekDouble > m_sendTrace
Send buffer for trace exchange.
const ExpListSharedPtr m_trace
Trace expansion list.
void ExchangeTrace(Array< OneD, NekDouble > &Fwd, Array< OneD, NekDouble > &Bwd)
Perform the trace exchange between processors, given the forwards and backwards spaces.
std::vector< InterfaceExchangeSharedPtr > m_exchange
Vector of interface exchanges, i.e. every rank-to-rank comm needed.
void ExchangeCoords()
Perform the coordinate exchange between processors. This is where the missing coordinates on the inte...
SpatialDomains::MovementSharedPtr m_movement
Movement object associated with the non-conformal interfaces.
InterfaceMapDG(const SpatialDomains::MeshGraphSharedPtr &graph, const ExpListSharedPtr &trace)
std::vector< InterfaceTraceSharedPtr > m_localInterfaces
Interface sides present on current process.
ExpListSharedPtr m_trace
Trace expansion list.
InterfaceTrace(const ExpListSharedPtr &trace, const SpatialDomains::InterfaceShPtr &interfaceShPtr, SpatialDomains::MovementSharedPtr movementShPtr)
Constructor.
void RotLocalBwdTrace(Array< OneD, Array< OneD, NekDouble > > &Bwd, const int &dim)
Rotates the interface velocity for sector rotation.
std::map< int, std::pair< int, Array< OneD, NekDouble > > > m_foundLocalCoords
Map of found coordinates present locally.
std::vector< int > m_mapMissingCoordToTrace
Vector of indices corresponding to m_missingCoord locations in trace.
SpatialDomains::InterfaceShPtr m_interface
Local interface object.
std::vector< Array< OneD, NekDouble > > m_missingCoords
Vector of coordinates on interface missing from the other side locally.
void DomainCheck(Array< OneD, NekDouble > &gloCoord, NekDouble &angle)
Update the coordiniates after movement.
void FillRankBwdTrace(Array< OneD, NekDouble > &trace, Array< OneD, NekDouble > &Bwd)
Fills the Bwd trace from partitioned trace.
void CalcLocalMissing()
Calculates what coordinates on the interface are missing locally.
void RotLocalBwdDeriveTrace(TensorOfArray3D< NekDouble > &Bwd, const int &dim)
Rotates the interface derivative velocity for sector rotation.
SpatialDomains::MovementSharedPtr m_movement
Movement object associated with the non-conformal interfaces.
std::map< int, std::pair< int, NekDouble > > m_phyRotAngle
Map of phy rotate angle for sector rotate interfaces.
void FillLocalBwdTrace(Array< OneD, NekDouble > &Fwd, Array< OneD, NekDouble > &Bwd)
Fills the Bwd trace by interpolating from the Fwd for local interfaces.
bool m_checkLocal
Flag whether the opposite side of the interface is present locally.
void DeriveRotate(TensorOfArray3D< NekDouble > &Bwd, const int dir, const NekDouble angle)
Rotate the 3D vector field in Bwd around the axis 'dir' by 'angle'.
static void Dgemm(const char &transa, const char &transb, const int &m, const int &n, const int &k, const double &alpha, const double *a, const int &lda, const double *b, const int &ldb, const double &beta, double *c, const int &ldc)
BLAS level 3: Matrix-matrix multiply C = A x B where op(A)[m x k], op(B)[k x n], C[m x n] DGEMM perfo...
Definition Blas.hpp:383
std::shared_ptr< CommRequest > CommRequestSharedPtr
Definition Comm.h:84
std::shared_ptr< ExpList > ExpListSharedPtr
Shared pointer to an ExpList object.
static const NekDouble kFindDistanceMin
std::shared_ptr< Interface > InterfaceShPtr
std::shared_ptr< Movement > MovementSharedPtr
Definition MeshGraph.h:220
std::shared_ptr< MeshGraph > MeshGraphSharedPtr
Definition MeshGraph.h:217