DNDSR 0.2.1
Distributed Numeric Data Structure for CFV
Loading...
Searching...
No Matches
test_basic_geom.py
Go to the documentation of this file.
1# import DNDS
2# import Geom
3
4
5from DNDSR import DNDS as DNDS
6from DNDSR import Geom as Geom
7from DNDSR.Geom.utils import create_mesh_from_CGNS, create_bnd_mesh, read_mesh, prepare_mesh
8import os
9import numpy as np
10
11print(DNDS.__file__)
12
13
15 mpi = DNDS.MPIInfo()
16 mpi.setWorld()
17
18 # mesh, reader, name2Id = create_mesh_from_CGNS(
19 # os.path.join(
20 # os.path.dirname(__file__), "..", "..", "data", "mesh", "NACA0012_H2.cgns"
21 # ),
22 # mpi,
23 # 2,
24 # )
25
26 mesh, reader, name2Id = create_mesh_from_CGNS(
27 os.path.join(
28 os.path.dirname(__file__),
29 "..",
30 "..",
31 "data",
32 "mesh",
33 "Uniform32_Periodic.cgns",
34 ),
35 mpi,
36 2,
37 inner_process_parts=4,
38 second_level_parts=4,
39 )
40
41 # mesh, reader, name2Id = create_mesh_from_CGNS(
42 # os.path.join(
43 # os.path.dirname(__file__), "..", "..", "data", "mesh", "UP3D_128.cgns"
44 # ),
45 # mpi,
46 # 3,
47 # )
48
49 n2idmap = name2Id.n2id_map
50 id2nmap = {k: v for v, k in n2idmap.items()}
51
52 def name_is_wall(name: str):
53 name = name.capitalize()
54 if "WALL" in name:
55 return True
56 if "bc-4".capitalize() in name:
57 return True
58
59 def id_is_wall(id: int):
60 # print(id2nmap[id])
61 if id in id2nmap and name_is_wall(id2nmap[id]):
62 return True
63 return False
64
65 wallDistOptions = Geom.UnstructuredMesh.WallDistOptions()
66 wallDistOptions.method = 1
67 wallDistOptions.subdivide_quad = 5
68 wallDistOptions.verbose = 10
69 wallDistOptions.wallDistExecution = 4
70 mesh.BuildNodeWallDist(id_is_wall, wallDistOptions)
71
72 meshBnd, readerBnd = create_bnd_mesh(mesh)
73
74 mesh_bytes = mesh.getArrayBytes()
75 mesh_nCell = mesh.NumCellGlobal()
76
77 if mpi.rank == 0:
78 print(f"mesh num cell: {mesh_nCell}")
79 print(f"mesh size total: {mesh_bytes / (1024 * 1024):.4g} MB")
80
81 try:
82 mesh.coords.to_device("CUDA")
83 mesh.to_device("CUDA")
84 except RuntimeError:
85 pass # CUDA not available, skip device transfer
86 # while True:
87 # pass
88
89 # fig, ax = plt.subplots(figsize=(16, 16), dpi=320)
90 # xymaxs = np.array([-1e100, -1e100], dtype=np.double)
91 # xymins = np.array([1e100, 1e100], dtype=np.double)
92 # for iCell in range(mesh.cell2node.Size()):
93 # c2n = mesh.cell2node[iCell].tolist()
94 # nodes = []
95 # # print(mesh.cell2node[iCell].tolist())
96
97 # for iNode in c2n:
98 # nodes.append(np.array(mesh.coords[iNode]))
99 # nodes = np.array(nodes)
100
101 # vertices = nodes[:, 0:2]
102 # xymaxs = np.maximum(vertices.max(axis=0), xymaxs)
103 # xymins = np.minimum(vertices.min(axis=0), xymins)
104 # # print(vertices.max(axis=0))
105 # polygon = patches.Polygon(
106 # vertices,
107 # closed=True,
108 # edgecolor="black",
109 # facecolor="blue" if iCell < mesh.cell2node.father.Size() else "red",
110 # lw=1,
111 # )
112 # ax.add_patch(polygon)
113
114 # # Maintain aspect ratio
115 # ax.set_aspect("equal")
116 # ax.set_xlim(xymins[0], xymaxs[0])
117 # ax.set_ylim(xymins[1], xymaxs[1])
118 # ax.set_title(f"part_{mpi.rank}")
119
120 # # Show the plot
121 # # plt.show(block=False)
122 # plt.figure(fig)
123 # plt.savefig(f"test_print_part_{mpi.rank}.png")
124
125
127 """Test multi-layer ghost cell creation with nGhostLayers=1,2,3.
128
129 Verifies:
130 1. Ghost cell/node/bnd counts increase monotonically with layer count.
131 2. For nLayers=2, every layer-1 ghost cell's cell2cell neighbors are
132 in the local+ghost set (the defining property of 2-layer ghosts).
133 3. The full pipeline (read + prepare) works for each layer count.
134 """
135 mpi = DNDS.MPIInfo()
136 mpi.setWorld()
137
138 mesh_file = os.path.join(
139 os.path.dirname(__file__), "..", "..", "data", "mesh",
140 "Uniform32_Periodic.cgns",
141 )
142
143 ghost_counts = {} # nLayers -> (nCellGhost, nNodeGhost, nBndGhost)
144
145 for nLayers in [1, 2, 3]:
146 mesh = Geom.UnstructuredMesh(mpi, 2)
147 reader = Geom.UnstructuredMeshSerialRW(mesh, 0)
148 reader.ReadFromCGNSSerial(mesh_file)
149 reader.Deduplicate1to1Periodic(1e-9)
150 reader.BuildCell2Cell()
151 reader.MeshPartitionCell2Cell({
152 "metisType": "KWAY",
153 "metisUfactor": 5,
154 "metisSeed": 0,
155 "metisNcuts": 3,
156 })
157 reader.PartitionReorderToMeshCell2Cell()
158
159 mesh.RecoverNode2CellAndNode2Bnd()
160 mesh.RecoverCell2CellAndBnd2Cell()
161 mesh.BuildGhostPrimary(nLayers)
162 mesh.AdjGlobal2LocalPrimary()
163 mesh.AdjGlobal2LocalN2CB()
164
165 nCellOwned = mesh.NumCell()
166 nCellGhost = mesh.NumCellGhost()
167 nNodeGhost = mesh.NumNodeGhost()
168 nBndGhost = mesh.NumBndGhost()
169
170 ghost_counts[nLayers] = (nCellGhost, nNodeGhost, nBndGhost)
171
172 if mpi.rank == 0:
173 print(f" nLayers={nLayers}: owned={nCellOwned}, "
174 f"cellGhost={nCellGhost}, nodeGhost={nNodeGhost}, "
175 f"bndGhost={nBndGhost}")
176
177 # For nLayers >= 2: verify that every owned cell's cell2cell
178 # neighbor's cell2cell neighbors are all in the local+ghost set.
179 # This is the defining property of 2-layer ghosts.
180 if nLayers >= 2:
181 nCellProc = mesh.NumCellProc() # father + son
182 for iCell in range(nCellOwned):
183 for iNeighbor in mesh.cell2cell[iCell].tolist():
184 if iNeighbor < 0:
185 continue # not-found encoded, skip
186 assert iNeighbor < nCellProc, (
187 f"Layer-1 neighbor {iNeighbor} of owned cell {iCell} "
188 f"is out of range [0, {nCellProc})")
189 # Check that iNeighbor's own neighbors are also resolvable
190 for iNeighbor2 in mesh.cell2cell[iNeighbor].tolist():
191 if iNeighbor2 < 0:
192 continue
193 assert iNeighbor2 < nCellProc, (
194 f"Layer-2 neighbor {iNeighbor2} of cell {iNeighbor} "
195 f"(neighbor of owned cell {iCell}) is out of range "
196 f"[0, {nCellProc})")
197
198 # Run the full prepare pipeline to make sure it doesn't crash.
199 prepare_mesh(mesh, reader, build_serial_out=False)
200
201 del mesh, reader
202
203 # Verify monotonic increase of ghost counts.
204 for key in ["cell", "node", "bnd"]:
205 idx = {"cell": 0, "node": 1, "bnd": 2}[key]
206 counts = [ghost_counts[n][idx] for n in [1, 2, 3]]
207 for i in range(len(counts) - 1):
208 assert counts[i + 1] >= counts[i], (
209 f"{key} ghost count should be monotonically non-decreasing: "
210 f"nLayers={i + 1}->{i + 2}: {counts[i]}->{counts[i + 1]}")
211 if mpi.rank == 0:
212 print(f" {key} ghost counts: {counts}")
213
214 # With mpi.size >= 2, ghost counts should strictly increase.
215 if mpi.size >= 2:
216 for key in ["cell", "node"]:
217 idx = {"cell": 0, "node": 1}[key]
218 assert ghost_counts[2][idx] > ghost_counts[1][idx], (
219 f"{key} ghost count should increase from 1 to 2 layers "
220 f"with {mpi.size} ranks: "
221 f"{ghost_counts[1][idx]} vs {ghost_counts[2][idx]}")
222
223
224if __name__ == "__main__":
225 test_mesh0()
Lightweight bundle of an MPI communicator and the calling rank's coordinates.
Definition MPI.hpp:231