DNDSR 0.1.0.dev1+gcd065ad
Distributed Numeric Data Structure for CFV
Loading...
Searching...
No Matches
OversetCart.py
Go to the documentation of this file.
1from DNDSR import DNDS, Geom
2import sys, os, math
3import mpi4py.MPI as pyMPI
4import numpy as np
5from GeomUtils import *
6from CartGridField import CartGridField
7import itertools
8
9
11 def __init__(self, mpi: DNDS.MPIInfo, transform=(np.eye(3, 3), np.zeros(3))):
12 self._mpi = mpi
13 self._MPI = get_mpi4py_comm_from_MPIInfo(mpi)
14 self._transform = transform
15 assert transform[0].shape == (3, 3)
16 assert transform[1].shape == (3,)
17
18 @property
19 def transform(self):
20 return self._transform
21
22 @transform.setter
23 def transform(self, value):
24 if not (value[0].shape == (3, 3) and value[1].shape == (3,)):
25 raise ValueError("value shape wrong " + f"{value}")
26 rotrott = value[0] @ value[0].T
27 err = np.linalg.norm(rotrott - np.eye(3), "fro")
28 if err > 1e-5:
29 raise ValueError(f"input not close to rotation: {value[0]}")
30 if np.linalg.norm(rotrott * np.array([[0, 0, 1], [0, 0, 1], [1, 1, 0]])) > 1e-5:
31 raise ValueError(f"input not 2d rotation: {value[0]}")
32 self._transform = value
33
34 # don't forget to update AABB!
35 coordsFatherData = np.array(self._mesh.coords.father.data())
36 coordsFatherData = coordsFatherData.reshape((3, -1), order="F")
37 coordsFatherData = self.coord_mesh_to_phy(coordsFatherData)
38 coordsMax = coordsFatherData.max(axis=1)
39 coordsMin = coordsFatherData.min(axis=1)
40 self._MPI.Allreduce(None, coordsMax, op=pyMPI.MAX)
41 self._MPI.Allreduce(None, coordsMin, op=pyMPI.MIN)
42 self._xyzMax = coordsMax
43 self._xyzMin = coordsMin
44
45 def coord_mesh_to_phy(self, coord_mesh):
46 if coord_mesh.ndim == 2:
47 return self._transform[0] @ coord_mesh + self._transform[1][:, np.newaxis]
48 elif coord_mesh.ndim == 1:
49 return self._transform[0] @ coord_mesh + self._transform[1]
50
51 def read_mesh(self, f_name: str):
52 self._mesh, self._meshReader, self._name2IDAutoAppend = get_mesh_2D(
53 f_name, self._mpi
54 )
55 self._name2ID = self._name2IDAutoAppend.n2id_map
56 self._id2name = {}
57 for name, id in self._name2ID.items():
58 self._id2name[id] = name
59 coordsFatherData = np.array(self._mesh.coords.father.data())
60 # print(coordsFatherData.shape)
61 coordsFatherData = coordsFatherData.reshape((3, -1), order="F")
62 coordsFatherData = self.coord_mesh_to_phy(coordsFatherData)
63 coordsMax = coordsFatherData.max(axis=1)
64 coordsMin = coordsFatherData.min(axis=1)
65 self._MPI.Allreduce(None, coordsMax, op=pyMPI.MAX)
66 self._MPI.Allreduce(None, coordsMin, op=pyMPI.MIN)
67 self._xyzMax = coordsMax
68 self._xyzMin = coordsMin
69
70 @property
71 def xyzMinMax(self):
72 return np.concatenate(
73 (self._xyzMin.transpose(), self._xyzMax.transpose())
74 ).reshape((2, 3), order="C")
75
76 def obtain_dist_node(self, bc_names=["WALL"]):
77 from OversetCartUtil import obtain_part_local_elem_dists
78
79 self.dist_node = obtain_part_local_elem_dists(self, bc_names)
80
81 def get_travelling_cell(self, iCell: int, in_phy: bool = True):
82 """Pack a cell's geometry into a travelling-cell tuple.
83
84 Args:
85 iCell (int): is local iCell
86 in_phy (bool, optional): in physical instead of mesh space. Defaults to True.
87
88 Returns:
89 tuple[ElemType, int, int, list[int], ndarray]: travelling cell
90 """
91 mesh = self._mesh
92 part = self
93
94 cell2node = np.array(mesh.cell2node[iCell], copy=False)
95 coords = []
96 for iNode in cell2node:
97 coords.append(np.array(mesh.coords[iNode], copy=True))
98 coords = np.array(coords).transpose()
99 if in_phy:
100 coords = part.coord_mesh_to_phy(coords)
101 elemInfo = mesh.cellElemInfo[iCell, 0]
102 assert elemInfo.getElemType() in {
103 Geom.Elem.ElemType.Tri3,
104 Geom.Elem.ElemType.Quad4,
105 } # coords is now polygon
106 travelling_cell = pack_travelling_cell(
107 cellType=elemInfo.getElemType(),
108 cellZone=elemInfo.zone,
109 iCell=mesh.cell2node.trans.LGhostMapping(-1, iCell),
110 cell2nodeRow=[
111 mesh.coords.trans.LGhostMapping(-1, iNode) for iNode in cell2node
112 ],
113 coords=coords,
114 )
115 return travelling_cell
116
117 def get_holed_faces_midpt(self, cell_type_arr: DNDS.ArrayEigenVectorPair_1_1_N):
118 """Return indices and midpoints of faces between cells of differing type.
119
120 Args:
121 cell_type_arr (DNDS.ArrayEigenVectorPair_1_1_N): per-cell type tag array.
122
123 Returns:
124 (array, array): iFaces (local), midpt coords
125 """
126 mesh = self._mesh
127 faces = []
128 coords_mid = []
129 for iFace in range(mesh.NumFaceProc()):
130 f2c = mesh.face2cell[iFace].tolist()
131 type_L = cell_type_arr[f2c[0]].tolist()[0]
132 type_R = type_L
133 if f2c[1] != DNDS.UnInitIndex:
134 type_R = cell_type_arr[f2c[1]].tolist()[0]
135 if type_L != type_R:
136 faces.append(iFace)
137 faceAtr = mesh.faceElemInfo[iFace, 0]
138 assert faceAtr.getElemType() == Geom.Elem.ElemType.Line2
139 f2n = mesh.face2node[iFace].tolist()
140 coord_mid = (
141 np.array(mesh.coords[f2n[0]]) + np.array(mesh.coords[f2n[1]])
142 ) * 0.5
143 coord_mid = self.coord_mesh_to_phy(coord_mid)
144 coords_mid.append(coord_mid[:2])
145 coords_mid = np.array(coords_mid).reshape(-1, 2).T
146
147 return (np.array(faces, dtype=np.int64), coords_mid)
148
150 self,
151 iPart,
152 cell_type_arr: DNDS.ArrayEigenVectorPair_1_1_N,
153 no_hole=False,
154 ax=None,
155 highlight_iCells=set(),
156 ):
157 mpi = self._mpi
158 MPI = self._MPI
159
160 highlight_iCells = set([int(v) for v in highlight_iCells])
161 highlight_iCells = MPI.gather(list(highlight_iCells), root=0)
162 if mpi.rank == 0:
163 highlight_iCells = set(itertools.chain(*highlight_iCells))
164
165 mesh = self._mesh
166 travelling_cell_type_list = []
167 for iCell in range(mesh.NumCell()):
168 cell_type = cell_type_arr[iCell].tolist()[0] # float
169 travelling_cell_type_list.append(
170 self.get_travelling_cell(iCell, in_phy=True) + (cell_type,)
171 )
172
173 all_cell_types = MPI.gather(travelling_cell_type_list, root=0)
174
175 if mpi.rank != 0:
176 return
177
178 import matplotlib.pyplot as plt
179 import matplotlib.patches as patches
180
181 self_ax = ax is None
182 if ax is None:
183 fig = plt.figure(figsize=(8, 6), dpi=320)
184 ax = plt.gca()
185 for cellType, _, iCell, _, coords, osType in itertools.chain(*all_cell_types):
186 assert cellType in {Geom.Elem.ElemType.Quad4, Geom.Elem.ElemType.Tri3}
187 if no_hole and not (osType == 0):
188 continue
189 # if int(iCell) in highlight_iCells:
190 # print(f"HIGHLIGHT {iPart},{iCell}")
191 polygon = patches.Polygon(
192 coords[:2, :].transpose(),
193 closed=True,
194 alpha=0.8 if int(iCell) in highlight_iCells else 0.2,
195 edgecolor="k",
196 facecolor=f"C{int(iPart)}",
197 ls="-" if osType == 0 else "none",
198 lw=0.3,
199 )
200 ax.add_patch(polygon)
201
202 if self_ax:
203 plt.axis("equal")
204 plt.savefig(f"part_{iPart}.png")
205 plt.close(fig)
206
207
209
211 self,
212 cell_bnds: dict[tuple[int], list[t_travelling_cell_pack]],
213 dist_field: CartGridField,
214 cell_cell_inds: dict[tuple[int], set[int]],
215 cell_cells_on_bnd: dict[tuple[int], list[t_travelling_cell_pack]],
216 cell_bnds_expanded: dict[tuple[int], list[t_travelling_cell_pack]],
217 ):
218 (
219 self.cell_bnds, # ijk (g) to travelling boundary elements on the current cell
220 self.dist_field, # scalar field representing interpolated distance
221 self.cell_cell_inds, # ijk (g) to global cells intersecting
222 self.cell_cells_on_bnd, # with all the cells on the bg cell having on zero cell_bnds
223 self.cell_bnds_expanded, # cell_bnds but with fringe records
224 ) = (
225 cell_bnds,
226 dist_field,
227 cell_cell_inds,
228 cell_cells_on_bnd,
229 cell_bnds_expanded,
230 )
231
232
234
235 def __init__(self, mpi: DNDS.MPIInfo):
236 self._mpi = mpi
237 self._MPI = get_mpi4py_comm_from_MPIInfo(mpi)
238
239 def set_bg(self, parts: list[OversetPart2D], h: float):
240 mpi = self._mpi
241 self.h = h
242 box = np.zeros((2, 3))
243 box[0, :] = 1e300
244 box[1, :] = -1e300
245 for part in parts:
246 partBox = part.xyzMinMax
247 box[0, :] = np.minimum(partBox[0, :], box[0, :])
248 box[1, :] = np.maximum(partBox[1, :], box[1, :])
249 xNodes = []
250 origins = np.zeros(3) * np.nan
251 for iax in range(3):
252 lower = box[0, iax]
253 upper = box[1, iax]
254 lower -= h / 2
255 upper += h / 2
256 nInterval = int(np.ceil((upper - lower) / h))
257 assert nInterval >= 0
258 upperV = lower + nInterval * h
259 assert upperV > box[1, iax]
260 xNodes.append(np.linspace(lower, upperV, nInterval + 1, dtype=np.float64))
261 origins[iax] = lower
262
263 self.xNodes = xNodes
264 self.grid_shape = tuple([v.size for v in xNodes]) # shape for global point grid
265 self.origins = origins
266
267 self.grid_shape = self._MPI.bcast(self.grid_shape, root=0)
268 self.origins = self._MPI.bcast(self.origins, root=0)
269 if self._mpi.rank == 0:
270
271 # print(self.grid_shape)
272 # print(self.origins)
273
274 # do BG grid partition
275 from ProductDecompose import int_factor_divide
276
277 nProcAx = int_factor_divide(self._mpi.size, 2) # 2d
278 nIntervals = [v - 1 for v in self.grid_shape]
279 nStarts = []
280 for iax in range(2):
281 nIntervalP = int(np.floor(nIntervals[iax] / nProcAx[iax]))
282 nIntervalPLast = nIntervals[iax] - (nProcAx[iax] - 1) * nIntervalP
283 assert nIntervalPLast >= 0
284 nStartsAx = [i * nIntervalP for i in range(nProcAx[iax])] + [
285 nIntervals[iax]
286 ]
287 assert nStartsAx[-1] - nStartsAx[-2] == nIntervalPLast
288 nStarts.append(np.array(nStartsAx, dtype=np.int64))
289 # print(nStarts)
290 self.nStarts = nStarts
291 self.nProcAx = nProcAx
292 self.nStarts, self.nProcAx = self._MPI.bcast(
293 (self.nStarts, self.nProcAx) if self._mpi.rank == 0 else None, root=0
294 )
295 self.procMap = np.arange(self._mpi.size, dtype=np.int64).reshape(
296 self.nProcAx, order="C"
297 )
298 self.nStarts4point = [np.copy(ar) for ar in self.nStarts]
299 for v in self.nStarts4point:
300 v[-1] += 1
301 if self._mpi.rank == 0:
302 print(self.procMap)
303 print(
304 f"{self._mpi.rank} nStarts: {self.nStarts4point}, nProcAx: {self.nProcAx}"
305 )
306
307 # for index conversion in DNDSR
308 nPointsPerRank = np.array(
309 [np.array(self.proc_grid_shape(rank)).prod() for rank in range(mpi.size)],
310 dtype=np.int64,
311 )
312 self.global_map_pointsStart = np.concat(([0], np.cumsum(nPointsPerRank)))
313 nCellsPerRank = np.array(
314 [
315 np.array(self.proc_cell_grid_shape(rank)).prod()
316 for rank in range(mpi.size)
317 ],
318 dtype=np.int64,
319 )
320 self.global_map_cellsStart = np.concat(([0], np.cumsum(nCellsPerRank)))
321 self.rank_to_ax_rank_map_i = np.array(
322 [self.rank_to_ax_rank(rank)[0] for rank in range(mpi.size)], dtype=np.int32
323 )
324 self.rank_to_ax_rank_map_j = np.array(
325 [self.rank_to_ax_rank(rank)[1] for rank in range(mpi.size)], dtype=np.int32
326 )
327
328 ## do borders
329 ijk_ranges_point = self.proc_grid_range_expanded(is_point=True)
330 # print(f"{mpi.rank}, { self.proc_grid_range(is_point=True)}, {ijk_ranges_point}")
331 ism_expanded, jsm_expanded = np.meshgrid(
332 *[
333 np.arange(
334 ijk_ranges_point[iax][0],
335 ijk_ranges_point[iax][1],
336 dtype=np.int64,
337 )
338 for iax in range(2)
339 ],
340 indexing="ij",
341 )
342 self.local_point_grid_shape_expanded = ism_expanded.shape
343 grid_point_expanded_idxs_g = self.ijk_to_idx(
344 np.array([ism_expanded, jsm_expanded]),
345 is_point=True,
346 ).flat
347 in_local = (
348 grid_point_expanded_idxs_g >= self.global_map_pointsStart[mpi.rank]
349 ) & (grid_point_expanded_idxs_g < self.global_map_pointsStart[mpi.rank + 1])
350 grid_point_expanded_idxs_g = grid_point_expanded_idxs_g[~in_local]
352 np.array(grid_point_expanded_idxs_g, dtype=np.int64)
353 )
355 self.grid_point_expanded_idxs_g, is_point=True, ret_global=True
356 ) - np.array(
357 [[self.proc_grid_range_expanded(is_point=True)[iax][0]] for iax in range(2)]
358 ) # base point # becomes in expanded array instead of in non-expanded
359
360 def rank_to_ax_rank(self, rank=None):
361 if rank is None:
362 rank = self._mpi.rank
363 return (rank // self.nProcAx[1], rank % self.nProcAx[1])
364
365 def proc_cell_grid_shape(self, rank=None):
366 ax_rank = self.rank_to_ax_rank(rank)
367 return tuple(
368 [
369 self.nStarts[i][ax_rank[i] + 1] - self.nStarts[i][ax_rank[i]]
370 for i in range(2)
371 ]
372 )
373
374 def proc_grid_range(self, rank=None, is_point=True):
375 nStarts_ax = self.nStarts4point if is_point else self.nStarts
376 ax_rank = self.rank_to_ax_rank(rank)
377 return [
378 (nStarts_ax[i][ax_rank[i]], nStarts_ax[i][ax_rank[i] + 1]) for i in range(2)
379 ]
380
381 def proc_grid_shape(self, rank=None, is_point=True):
382 ax_rank = self.rank_to_ax_rank(rank)
383 nStarts_ax = self.nStarts4point if is_point else self.nStarts
384 return tuple(
385 [
386 nStarts_ax[i][ax_rank[i] + 1] - nStarts_ax[i][ax_rank[i]]
387 for i in range(2)
388 ]
389 )
390
391 def proc_grid_range_expanded(self, rank=None, is_point=True):
392 ijk_ranges = self.proc_grid_range(rank=rank, is_point=is_point)
393 for iax in range(2):
394 ijk_ranges[iax] = (
395 max(ijk_ranges[iax][0] - 1, 0),
396 min(
397 ijk_ranges[iax][1] + (2 if is_point else 1),
398 self.grid_shape[iax]
399 - (0 if is_point else 1), # mind this parentheses
400 ),
401 )
402 return ijk_ranges
403
404 def proc_grid_shape_expanded(self, rank=None, is_point=True):
405 ijk_ranges = self.proc_grid_range_expanded(rank=rank, is_point=is_point)
406 return tuple([ijk_ranges[iax][1] - ijk_ranges[iax][0] for iax in range(2)])
407
408 def proc_grid_core_start_in_local_expanded(self, rank=None, is_point=True):
409 index_range = self.proc_grid_range(rank, is_point)
410 index_range_expanded = self.proc_grid_range_expanded(rank, is_point)
411 starts = [
412 int(index_range[iax][0] - index_range_expanded[iax][0]) for iax in range(2)
413 ]
414 return starts
415
416 def proc_grid_core_range_in_local_expanded(self, rank=None, is_point=True):
417 starts = self.proc_grid_core_start_in_local_expanded(rank, is_point)
418 return [
419 (starts[iax], starts[iax] + int(self.proc_grid_shape(rank, is_point)[iax]))
420 for iax in range(2)
421 ]
422
423 def global_idx_to_rank(self, idx, is_point=False):
424 ranks = (
425 np.searchsorted(
426 (
428 if is_point
429 else self.global_map_cellsStart
430 ),
431 idx,
432 side="right",
433 )
434 - 1
435 )
436 if np.any(ranks < 0) or np.any(ranks >= self._mpi.size):
437 raise ValueError(f"idx {idx} out of range")
438 return ranks
439
440 def global_ijk_to_rank(self, ijk, is_point=False):
441 if len(ijk) == 0:
442 return []
443 idxs = []
444 for iax in range(2):
445 # searchsorted gives the insertion point for target
446 indexAx = (
447 np.searchsorted(
448 self.nStarts4point[iax] if is_point else self.nStarts[iax],
449 ijk[iax],
450 side="right",
451 )
452 - 1
453 )
454 if np.any(indexAx < 0) or np.any(indexAx >= self.nProcAx[iax]):
455 raise ValueError(
456 f"ijk {ijk[iax]} {ijk} {self.nStarts4point[iax] if is_point else self.nStarts[iax]} {indexAx} out of range"
457 )
458 idxs.append(indexAx)
459 # print(idxs)
460 return self.procMap[tuple(idxs)]
461
463 self, ijk, is_point=False, in_global=True, ret_global=True, ranks=None
464 ):
465 if in_global and ranks is None:
466 ranks = self.global_ijk_to_rank(ijk, is_point)
467 if ranks is None and not in_global:
468 raise ValueError("if input is local, should provide ranks")
469 start_offsets = (
470 self.global_map_pointsStart[ranks]
471 if is_point
472 else self.global_map_cellsStart[ranks]
473 )
474 ax_rank_is = self.rank_to_ax_rank_map_i[ranks]
475 ax_rank_js = self.rank_to_ax_rank_map_j[ranks]
476 nStarts_ax = self.nStarts4point if is_point else self.nStarts
477 nStarts_ax_is = nStarts_ax[0][ax_rank_is]
478 nStarts_ax_js = nStarts_ax[1][ax_rank_js]
479 nStarts_ax_jlen = nStarts_ax[1][ax_rank_js + 1] - nStarts_ax_js
480 ## conversion
481 local_ijks = (
482 ijk - np.array([nStarts_ax_is, nStarts_ax_js]) if in_global else ijk
483 )
484 local_idxs = local_ijks[0] * nStarts_ax_jlen + local_ijks[1]
485 global_idxs = start_offsets + local_idxs
486 return global_idxs if ret_global else local_idxs
487
489 self, idx, is_point=False, in_global=True, ret_global=True, ranks=None
490 ):
491 if in_global and ranks is None:
492 ranks = self.global_idx_to_rank(idx, is_point)
493 if ranks is None and not in_global:
494 raise ValueError("if input is local, should provide ranks")
495 start_offsets = (
496 self.global_map_pointsStart[ranks]
497 if is_point
498 else self.global_map_cellsStart[ranks]
499 )
500 ax_rank_is = self.rank_to_ax_rank_map_i[ranks]
501 ax_rank_js = self.rank_to_ax_rank_map_j[ranks]
502 nStarts_ax = self.nStarts4point if is_point else self.nStarts
503 nStarts_ax_is = nStarts_ax[0][ax_rank_is]
504 nStarts_ax_js = nStarts_ax[1][ax_rank_js]
505 nStarts_ax_jlen = nStarts_ax[1][ax_rank_js + 1] - nStarts_ax_js
506 ## conversion
507 local_idxs = idx - start_offsets if in_global else idx
508 local_is = local_idxs // nStarts_ax_jlen
509 local_js = local_idxs % nStarts_ax_jlen
510 if ret_global:
511 ijks = np.array([nStarts_ax_is + local_is, nStarts_ax_js + local_js])
512 else:
513 ijks = np.array([local_is, local_js])
514 return ijks
515
517 self, rank=None, is_point=False, expanded=False, no_mesh=False
518 ):
519 grid_range_method = (
520 self.proc_grid_range_expanded if expanded else self.proc_grid_range
521 )
522 ijkRanges = grid_range_method(rank=rank, is_point=is_point)
523 iss = np.arange(ijkRanges[0][0], ijkRanges[0][1], dtype=np.int64)
524 jss = np.arange(ijkRanges[1][0], ijkRanges[1][1], dtype=np.int64)
525 if no_mesh:
526 return iss, jss
527 ism, jsm = np.meshgrid(iss, jss, indexing="ij")
528 ijks = np.array([ism, jsm])
529 return ijks
530
531 def proc_grid_point_coords(self, expanded=False):
532 return self.origins[
533 :2, np.newaxis, np.newaxis
534 ] + self.h * self.proc_grid_ijkarray(is_point=True, expanded=expanded)
535
536 @staticmethod
537 @property
539 return dict[tuple[int], list[tuple[Geom.Elem.ElemType, int, np.ndarray]]]
540
541 @staticmethod
542 @property
544 return DistMap
545
546 def obtain_dist_map(self, parts: list[OversetPart2D], bc_names=["WALL"]):
547 MPI = self._MPI
548 mpi = self._mpi
549 from OversetCartUtil import (
550 obtain_proc_local_bg_dists,
551 obtain_proc_local_bg_cell_bnd_elems,
552 expand_proc_local_bg_cell_bnd_elems,
553 obtain_proc_local_bg_cell_cell_elem_inds,
554 obtain_proc_local_bg_cell_elems_with_bnd,
555 get_mesh_bnd_elems,
556 dist_field_neg_hole_expansion,
557 )
558 from CartUtil import single_elem_get_box_intersection_2D
559
560 proc_dist_maps = []
561
562 for iPart, part in enumerate(parts):
563 proc_bg_mesh_dist = obtain_proc_local_bg_dists(self, part)
564 cell_bnds = obtain_proc_local_bg_cell_bnd_elems(self, part)
565 cell_bnds_expanded = expand_proc_local_bg_cell_bnd_elems(self, cell_bnds)
566 cell_cell_inds = obtain_proc_local_bg_cell_cell_elem_inds(self, part)
567
568 dist_field = CartGridField(1, self.proc_grid_shape(), mpi)
569 dist_field.set_main_data(proc_bg_mesh_dist[:, :, None])
570 dist_field.set_ghost_info(
571 self.proc_grid_shape_expanded(is_point=True),
572 self.proc_grid_core_range_in_local_expanded(is_point=True),
574 )
575 dist_field.set_ghost_global_pull(self.grid_point_expanded_idxs_g)
576 dist_field.trans.startPersistentPull()
577 dist_field.trans.waitPersistentPull()
578 # print(
579 # f"{mpi.rank} - {self.grid_point_expanded_idxs_g_ijks_local}, {dist_field.ghost.Size()}"
580 # )
581
582 dist_field_neg_hole_expansion(self, part, bc_names, cell_bnds, dist_field)
583
584 cell_cells_on_bnd = obtain_proc_local_bg_cell_elems_with_bnd(
585 self, part, cell_bnds, cell_cell_inds
586 )
587
588 new_dist_map = DistMap(
589 cell_bnds,
590 dist_field,
591 cell_cell_inds,
592 cell_cells_on_bnd,
593 cell_bnds_expanded,
594 )
595
596 proc_dist_maps.append(new_dist_map)
597
598 return proc_dist_maps
599
600 def query_dist_from_points(self, distMap: DistMap, points: np.ndarray):
601 from OversetCartUtil import query_dist_from_points
602
603 return query_dist_from_points(self, distMap, points)
604
606 self, osPart: OversetPart2D, distMap: DistMap, points: np.ndarray
607 ):
608 from OversetCartUtil import query_template_cell_from_points
609
610 return query_template_cell_from_points(self, osPart, distMap, points)
611
613 self, parts: list[OversetPart2D], proc_dist_maps: list[DistMap]
614 ):
615 from OversetCartUtil import decide_cell_types
616
617 return decide_cell_types(self, parts, proc_dist_maps)
618
620 self,
621 parts: list[OversetPart2D],
622 proc_dist_maps: list[DistMap],
623 points: np.ndarray,
624 ):
625 from OversetCartUtil import decide_point_templates
626
628 self, parts, proc_dist_maps, points
629 ) # iPartTemp,iCellTemp
630
631 def print_proc_dist_maps(self, proc_dist_maps: list[DistMap], cmin=-1, cmax=10):
632 mpi = self._mpi
633 for iPart, item in enumerate(proc_dist_maps):
634 import matplotlib.pyplot as plt
635 import matplotlib.patches as patches
636
637 cell_bnds, dist_field, cell_cell_inds = (
638 item.cell_bnds,
639 item.dist_field,
640 item.cell_cell_inds,
641 )
642 cell_cells_on_bnd = item.cell_cells_on_bnd
643
644 data = np.array(dist_field.get_expanded_array()[:, :, 0])
645 data = np.minimum(data, cmax)
646 data = np.maximum(data, cmin)
647 [xv, yv] = self.proc_grid_point_coords(expanded=True)
648 # Create the plot
649
650 fig = plt.figure(figsize=(8, 6), dpi=320)
651 print(f"{xv.shape}, {yv.shape}, {data.shape}")
652 qmesh = plt.pcolormesh(
653 xv,
654 yv,
655 data,
656 cmap="viridis",
657 shading="gouraud",
658 edgecolors="black",
659 linewidths=0.5,
660 ) # 'viridis' is a common colormap
661 lw = 0.5
662 for x in xv[:, 0]:
663 plt.plot([x, x], [yv[0, 0], yv[0, -1]], c="k", lw=lw)
664 for y in yv[0, :]:
665 plt.plot([xv[-1, 0], xv[0, 0]], [y, y], c="k", lw=lw)
666 for type, bcid, _, b2n, coords in itertools.chain(*cell_bnds.values()):
667 assert type == Geom.Elem.ElemType.Line2
668 plt.plot(coords[0], coords[1], c="r", marker=".", lw=0.1, ms=0.1)
669 for ijks in cell_bnds.keys():
670 center = (np.array(ijks) + 0.5) * self.h + self.origins[:2]
671 plt.plot(
672 center[0], center[1], marker="o", ms=5, mfc="none", mew=0.3, c="k"
673 )
674
675 for ijks in cell_cell_inds:
676 center = (np.array(ijks) + 0.5) * self.h + self.origins[:2]
677 number = len(cell_cell_inds[ijks])
678 plt.text(
679 center[0], center[1], f"{number}", va="center", ha="center", size=4
680 )
681
682 for ijks, elems in cell_cells_on_bnd.items():
683 for cellType, cellZone, iCell, cell2nodeRow, coords in elems:
684 assert cellType in {
685 Geom.Elem.ElemType.Quad4,
686 Geom.Elem.ElemType.Tri3,
687 }
688 for iN in range(coords.shape[1]):
689 # plt.plot(
690 # coords[0], coords[1], c="g", marker=".", lw=0.1, ms=0.1
691 # )
692 polygon = patches.Polygon(
693 coords[:2, :].transpose(),
694 closed=True,
695 edgecolor="none",
696 facecolor=(0.5, 0.2, 0, 0.3),
697 lw=1,
698 )
699 plt.gca().add_patch(polygon)
700
701 # Add a colorbar to show the mapping of values to colors
702 plt.colorbar()
703 plt.axis("equal")
704 plt.savefig(f"part_{iPart}_rank_{mpi.rank}.png")
705 plt.close(fig)
706
707
708if __name__ == "__main__":
709 mpiGlob = DNDS.MPIInfo()
710 mpiGlob.setWorld()
711 from OversetCartManager import OversetBG2DManager
712
713 translates = [[0, 0, 0], [1.5, 0, 0]]
714
715 transforms = [
716 (np.eye(3, 3), np.array(translates[i])) for i in range(len(translates))
717 ]
718 # translates = [[0, 0, 0]]
719
720 mesh_names = [
721 os.path.join(
722 os.path.dirname(__file__),
723 "..",
724 "..",
725 "..",
726 "data",
727 "mesh",
728 "CylinderNoFar.cgns",
729 )
730 ] * len(transforms)
731
732 osMan = OversetBG2DManager(mpiGlob)
733
734 osMan.read_meshes_and_init(mesh_names, transforms)
735
736 osMan.process_overset(1.0 / 10)
737
738 osMan.print_proc_dist_maps()
739
740 osMan.print_full_mesh_type(together=True)
741
742 osBG = osMan.osBG
743 if mpiGlob.rank == 0:
744 _ = osBG.global_ijk_to_rank(
745 np.random.randint(0, min(osBG.grid_shape[0:2]) - 1, size=(2, 32))
746 )
747
748 # print(osBG.global_ijk_to_rank([v - 1 for v in osBG.grid_shape], is_point=True))
749
750 try:
751 print(
752 osBG.global_ijk_to_rank(
753 [v - 1 for v in osBG.grid_shape], is_point=False
754 )
755 )
756 except ValueError as e:
757 print(e)
758 print("caught")
759 else:
760 raise RuntimeError("not raising value error")
__init__(self, dict[tuple[int], list[t_travelling_cell_pack]] cell_bnds, CartGridField dist_field, dict[tuple[int], set[int]] cell_cell_inds, dict[tuple[int], list[t_travelling_cell_pack]] cell_cells_on_bnd, dict[tuple[int], list[t_travelling_cell_pack]] cell_bnds_expanded)
query_template_cell_from_points(self, OversetPart2D osPart, DistMap distMap, np.ndarray points)
idx_to_ijk(self, idx, is_point=False, in_global=True, ret_global=True, ranks=None)
proc_grid_shape_expanded(self, rank=None, is_point=True)
proc_grid_shape(self, rank=None, is_point=True)
ijk_to_idx(self, ijk, is_point=False, in_global=True, ret_global=True, ranks=None)
__init__(self, DNDS.MPIInfo mpi)
proc_grid_core_start_in_local_expanded(self, rank=None, is_point=True)
global_idx_to_rank(self, idx, is_point=False)
decide_point_templates(self, list[OversetPart2D] parts, list[DistMap] proc_dist_maps, np.ndarray points)
print_proc_dist_maps(self, list[DistMap] proc_dist_maps, cmin=-1, cmax=10)
proc_grid_ijkarray(self, rank=None, is_point=False, expanded=False, no_mesh=False)
decide_cell_types(self, list[OversetPart2D] parts, list[DistMap] proc_dist_maps)
query_dist_from_points(self, DistMap distMap, np.ndarray points)
obtain_dist_map(self, list[OversetPart2D] parts, bc_names=["WALL"])
proc_grid_range(self, rank=None, is_point=True)
proc_grid_range_expanded(self, rank=None, is_point=True)
global_ijk_to_rank(self, ijk, is_point=False)
proc_grid_core_range_in_local_expanded(self, rank=None, is_point=True)
proc_grid_point_coords(self, expanded=False)
set_bg(self, list[OversetPart2D] parts, float h)
obtain_dist_node(self, bc_names=["WALL"])
get_holed_faces_midpt(self, DNDS.ArrayEigenVectorPair_1_1_N cell_type_arr)
get_travelling_cell(self, int iCell, bool in_phy=True)
print_full_mesh_type(self, iPart, DNDS.ArrayEigenVectorPair_1_1_N cell_type_arr, no_hole=False, ax=None, highlight_iCells=set())
__init__(self, DNDS.MPIInfo mpi, transform=(np.eye(3, 3), np.zeros(3)))
set(LIBNAME cfv) set(LINKS) set(LINKS_SHARED geom_shared dnds_shared $
Definition CMakeLists.txt:5
Lightweight bundle of an MPI communicator and the calling rank's coordinates.
Definition MPI.hpp:215