plot3d 1.7.7__tar.gz → 1.7.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {plot3d-1.7.7 → plot3d-1.7.9}/PKG-INFO +4 -2
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/glennht/class_definitions.py +5 -5
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/glennht/export_functions.py +40 -14
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/graph.py +7 -7
- plot3d-1.7.9/plot3d/gridpro/__init__.py +6 -0
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/gridpro/import_functions.py +195 -61
- {plot3d-1.7.7 → plot3d-1.7.9}/pyproject.toml +1 -1
- plot3d-1.7.7/plot3d/gridpro/__init__.py +0 -2
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/__init__.py +0 -0
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/block.py +0 -0
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/block_merging_mixed_facepairs.py +0 -0
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/blockfunctions.py +0 -0
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/connectivity.py +0 -0
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/differencing.py +0 -0
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/face.py +0 -0
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/facefunctions.py +0 -0
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/glennht/__init__.py +0 -0
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/glennht/import_functions.py +0 -0
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/listfunctions.py +0 -0
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/periodicity.py +0 -0
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/point_match.py +0 -0
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/read.py +0 -0
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/split_block.py +0 -0
- {plot3d-1.7.7 → plot3d-1.7.9}/plot3d/write.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: plot3d
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.9
|
|
4
4
|
Summary: Plot3D python utilities for reading and writing and also finding connectivity between blocks
|
|
5
5
|
Author: Paht Juangphanich
|
|
6
6
|
Author-email: paht.juangphanich@nasa.gov
|
|
@@ -8,6 +8,8 @@ Requires-Python: >=3.10.12,<4.0.0
|
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.11
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
11
13
|
Requires-Dist: matplotlib
|
|
12
14
|
Requires-Dist: networkx
|
|
13
15
|
Requires-Dist: numpy
|
|
@@ -8,11 +8,11 @@ from typing import Any, Dict, List, Optional
|
|
|
8
8
|
# Enums (mirror your C#)
|
|
9
9
|
# ----------------------------
|
|
10
10
|
class BoundaryConditionType(IntEnum):
|
|
11
|
-
Inlet =
|
|
12
|
-
Outlet =
|
|
13
|
-
SymmetryOrSlip =
|
|
14
|
-
Wall =
|
|
15
|
-
GIF =
|
|
11
|
+
Inlet = 1
|
|
12
|
+
Outlet = 2
|
|
13
|
+
SymmetryOrSlip = 3
|
|
14
|
+
Wall = 4
|
|
15
|
+
GIF = 1000
|
|
16
16
|
|
|
17
17
|
class InletBC_Subtype(IntEnum):
|
|
18
18
|
Normal = 0
|
|
@@ -331,29 +331,57 @@ def export_to_boundary_condition(
|
|
|
331
331
|
}
|
|
332
332
|
json_path.write_text(json.dumps(json_payload, indent=4))
|
|
333
333
|
|
|
334
|
-
# ---- Robust reference defaults (use inlet
|
|
334
|
+
# ---- Robust reference defaults (use highest-pressure inlet if available)
|
|
335
335
|
ref = job_settings.ReferenceCondFull
|
|
336
|
-
|
|
336
|
+
|
|
337
|
+
def _reference_inlet(inlets: List[Any]) -> Tuple[Any | None, float | None]:
|
|
338
|
+
best = None
|
|
339
|
+
best_pa: float | None = None
|
|
340
|
+
for inlet in inlets:
|
|
341
|
+
p0 = getattr(inlet, "P0_const", None)
|
|
342
|
+
if p0 is None:
|
|
343
|
+
continue
|
|
344
|
+
phys_pa = to_pa(p0, getattr(inlet, "P0_const_unit", "Pa"))
|
|
345
|
+
if phys_pa is None:
|
|
346
|
+
continue
|
|
347
|
+
if best_pa is None or phys_pa > best_pa:
|
|
348
|
+
best = inlet
|
|
349
|
+
best_pa = phys_pa
|
|
350
|
+
if best is None and inlets:
|
|
351
|
+
return inlets[0], None
|
|
352
|
+
return best, best_pa
|
|
353
|
+
|
|
354
|
+
ref_inlet, ref_inlet_pa = _reference_inlet(bc_group.Inlets)
|
|
337
355
|
|
|
338
356
|
# refLen: default 1.0 if missing
|
|
339
357
|
if getattr(ref, "reflen", None) in (None, 0):
|
|
340
358
|
ref.reflen = 1.0 # type: ignore
|
|
341
359
|
|
|
342
|
-
# refP0: from
|
|
360
|
+
# refP0: from highest-pressure inlet (convert to Pa) if missing
|
|
343
361
|
if getattr(ref, "refP0", None) in (None, 0):
|
|
344
|
-
if
|
|
345
|
-
|
|
346
|
-
ref.refP0 = phys_pa # type: ignore
|
|
362
|
+
if ref_inlet_pa not in (None, 0):
|
|
363
|
+
ref.refP0 = ref_inlet_pa # type: ignore
|
|
347
364
|
|
|
348
365
|
# refT0: from first inlet if missing
|
|
349
366
|
if getattr(ref, "refT0", None) in (None, 0):
|
|
350
|
-
if
|
|
351
|
-
ref.refT0 =
|
|
367
|
+
if ref_inlet and getattr(ref_inlet, "T0_const", None) is not None:
|
|
368
|
+
ref.refT0 = ref_inlet.T0_const # type: ignore
|
|
369
|
+
|
|
370
|
+
def _dedupe_by_bc_id(objs: Iterable[Any]) -> List[Any]:
|
|
371
|
+
seen: set[int] = set()
|
|
372
|
+
unique: List[Any] = []
|
|
373
|
+
for obj in objs:
|
|
374
|
+
sid = obj.get("id") if isinstance(obj, dict) else getattr(obj, "SurfaceID", None)
|
|
375
|
+
if sid is None or sid in seen:
|
|
376
|
+
continue
|
|
377
|
+
seen.add(sid)
|
|
378
|
+
unique.append(obj)
|
|
379
|
+
return unique
|
|
352
380
|
|
|
353
381
|
# ---- Write .bcs file
|
|
354
382
|
with path.open("w", encoding="utf-8") as w:
|
|
355
383
|
# INLETS (normalize to refP0, refT0, refLen)
|
|
356
|
-
for inlet in bc_group.Inlets:
|
|
384
|
+
for inlet in _dedupe_by_bc_id(bc_group.Inlets):
|
|
357
385
|
if getattr(inlet, "P0_const", None) is not None:
|
|
358
386
|
phys_pa = to_pa(inlet.P0_const, getattr(inlet, "P0_const_unit", "Pa"))
|
|
359
387
|
if phys_pa is not None and ref.refP0 not in (None, 0):
|
|
@@ -369,7 +397,7 @@ def export_to_boundary_condition(
|
|
|
369
397
|
_write_bsurf_spec(w, inlet)
|
|
370
398
|
|
|
371
399
|
# OUTLETS (normalize back-pressure by refP0)
|
|
372
|
-
for outlet in bc_group.Outlets:
|
|
400
|
+
for outlet in _dedupe_by_bc_id(bc_group.Outlets):
|
|
373
401
|
if getattr(outlet, "Pback_const", None) is not None and ref.refP0 not in (None, 0):
|
|
374
402
|
phys_pa = to_pa(outlet.Pback_const, getattr(outlet, "Pback_const_unit", "Pa"))
|
|
375
403
|
if phys_pa is not None:
|
|
@@ -377,9 +405,9 @@ def export_to_boundary_condition(
|
|
|
377
405
|
_write_bsurf_spec(w, outlet)
|
|
378
406
|
|
|
379
407
|
# SLIPS / WALLS
|
|
380
|
-
for slip in bc_group.SymmetricSlips:
|
|
408
|
+
for slip in _dedupe_by_bc_id(bc_group.SymmetricSlips):
|
|
381
409
|
_write_bsurf_spec(w, slip)
|
|
382
|
-
for wall in bc_group.Walls:
|
|
410
|
+
for wall in _dedupe_by_bc_id(bc_group.Walls):
|
|
383
411
|
_write_bsurf_spec(w, wall)
|
|
384
412
|
|
|
385
413
|
# GIFS (dicts or dataclasses)
|
|
@@ -636,5 +664,3 @@ if __name__ == "__main__":
|
|
|
636
664
|
|
|
637
665
|
export_to_boundary_condition("boundary_conditions.bcs", job, bcg, gifs=gifs, volume_zones=volume_zones)
|
|
638
666
|
export_to_job_file(job, "jobfile", title="SingleBlade")
|
|
639
|
-
|
|
640
|
-
|
|
@@ -182,7 +182,7 @@ def _metis_part_graph_compat(
|
|
|
182
182
|
# ---------------------------------------------------------------------------
|
|
183
183
|
def partition_from_face_matches(
|
|
184
184
|
face_matches: List[dict],
|
|
185
|
-
|
|
185
|
+
block_sizes: List[int],
|
|
186
186
|
nparts: int,
|
|
187
187
|
favor_blocksize: bool = True,
|
|
188
188
|
aggregate: str = "sum",
|
|
@@ -216,7 +216,7 @@ def partition_from_face_matches(
|
|
|
216
216
|
"Install pymetis (Linux/macOS) or run on a platform where it is supported."
|
|
217
217
|
)
|
|
218
218
|
|
|
219
|
-
n_blocks = len(
|
|
219
|
+
n_blocks = len(block_sizes)
|
|
220
220
|
adj_list, edge_w = build_weighted_graph_from_face_matches(
|
|
221
221
|
face_matches, n_blocks,
|
|
222
222
|
aggregate=aggregate,
|
|
@@ -226,7 +226,7 @@ def partition_from_face_matches(
|
|
|
226
226
|
|
|
227
227
|
vwgt: Optional[List[int]] = None
|
|
228
228
|
if favor_blocksize:
|
|
229
|
-
vwgt =
|
|
229
|
+
vwgt = block_sizes
|
|
230
230
|
|
|
231
231
|
_edgecut, parts = _metis_part_graph_compat(
|
|
232
232
|
nparts=nparts,
|
|
@@ -243,7 +243,7 @@ def partition_from_face_matches(
|
|
|
243
243
|
# ---------------------------------------------------------------------------
|
|
244
244
|
def write_ddcmp(
|
|
245
245
|
parts: Sequence[int],
|
|
246
|
-
|
|
246
|
+
blocksizes: List[int],
|
|
247
247
|
adj_list: Dict[int, List[int]],
|
|
248
248
|
edge_weights: Optional[Dict[int, Dict[int, int]]] = None,
|
|
249
249
|
filename: str = "ddcmp.dat",
|
|
@@ -272,12 +272,12 @@ def write_ddcmp(
|
|
|
272
272
|
partition_edge_weights = [0] * n_proc
|
|
273
273
|
volume_nodes = [0] * n_proc
|
|
274
274
|
|
|
275
|
-
for b,
|
|
275
|
+
for b, bsize in enumerate(blocksizes):
|
|
276
276
|
pid = parts[b]
|
|
277
|
-
volume_nodes[pid] +=
|
|
277
|
+
volume_nodes[pid] += bsize
|
|
278
278
|
|
|
279
279
|
ew = edge_weights or {}
|
|
280
|
-
for b in range(len(
|
|
280
|
+
for b in range(len(blocksizes)):
|
|
281
281
|
pid = parts[b]
|
|
282
282
|
for nbr in adj_list.get(b, []):
|
|
283
283
|
nbr_pid = parts[nbr]
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
from typing import Dict, List, Tuple, Optional
|
|
1
|
+
from typing import Dict, List, Tuple, Optional, Union, Set
|
|
2
2
|
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
3
4
|
from tqdm import tqdm
|
|
4
5
|
from typing import List, Tuple, Optional, Any
|
|
5
6
|
from plot3d import Block
|
|
@@ -15,19 +16,23 @@ def _parse_header(line: str) -> Optional[Tuple[int,int,int]]:
|
|
|
15
16
|
except ValueError:
|
|
16
17
|
return None
|
|
17
18
|
|
|
18
|
-
def read_gridpro_to_blocks(
|
|
19
|
-
|
|
19
|
+
def read_gridpro_to_blocks(
|
|
20
|
+
filename: str,
|
|
21
|
+
encoding: str = "utf-8",
|
|
22
|
+
comment_prefixes: Tuple[str, ...] = ("#", "//"),
|
|
23
|
+
) -> List[Block]:
|
|
24
|
+
"""Read a structured GridPro text grid into Plot3D Block objects.
|
|
20
25
|
|
|
21
26
|
Args:
|
|
22
|
-
filename
|
|
23
|
-
encoding
|
|
24
|
-
comment_prefixes
|
|
27
|
+
filename: Path to the GridPro grid file.
|
|
28
|
+
encoding: Encoding to use while reading the text file.
|
|
29
|
+
comment_prefixes: Line prefixes that should be treated as comments.
|
|
25
30
|
|
|
26
31
|
Raises:
|
|
27
|
-
ValueError:
|
|
32
|
+
ValueError: If a block does not contain the expected number of floats.
|
|
28
33
|
|
|
29
34
|
Returns:
|
|
30
|
-
List[Block]:
|
|
35
|
+
List[Block]: A list of Block objects representing each GridPro block.
|
|
31
36
|
"""
|
|
32
37
|
blocks = []
|
|
33
38
|
|
|
@@ -82,10 +87,10 @@ def read_gridpro_connectivity(
|
|
|
82
87
|
file_path: str,
|
|
83
88
|
sb_zero_based_in_file: bool = True, # False if sb1/sb2 are 1-based in file
|
|
84
89
|
index_zero_based_in_file: bool = True, # False if IMIN..KMAX are 1-based in file
|
|
85
|
-
inlet_ids: Optional[List[int]] = None,
|
|
86
|
-
outlet_ids: Optional[List[int]] = None,
|
|
87
|
-
wall_ids: Optional[List[int]] = None,
|
|
88
|
-
symm_slip_ids: Optional[List[int]] = None,
|
|
90
|
+
inlet_ids: Optional[Union[List[int], Dict[str, Any]]] = None,
|
|
91
|
+
outlet_ids: Optional[Union[List[int], Dict[str, Any]]] = None,
|
|
92
|
+
wall_ids: Optional[Union[List[int], Dict[str, Any]]] = None,
|
|
93
|
+
symm_slip_ids: Optional[Union[List[int], Dict[str, Any]]] = None,
|
|
89
94
|
custom_bc_ids: Optional[Dict[str, List[int]]] = None,
|
|
90
95
|
) -> Dict[str, object]:
|
|
91
96
|
"""Parse a GridPro connectivity file into plot3d.connectivity_fast-like structures.
|
|
@@ -94,33 +99,45 @@ def read_gridpro_connectivity(
|
|
|
94
99
|
``P pid sb1 sf1 sb2 sf2 fmap L1i L1j L1k H1i H1j H1k L2i L2j L2k H2i H2j H2k pty lbid``
|
|
95
100
|
|
|
96
101
|
Boundary-condition IDs default to GridPro's PTY values but can be overridden
|
|
97
|
-
via the provided *_ids
|
|
102
|
+
via the provided *_ids arguments (either a list of PTY ints or
|
|
103
|
+
``{"name": str, "ids": [...]}``) or extended via
|
|
104
|
+
``custom_bc_ids={"name": [ids]}``.
|
|
98
105
|
|
|
99
106
|
Args:
|
|
100
107
|
file_path: Path to the GridPro connectivity file.
|
|
101
108
|
sb_zero_based_in_file: Set False if ``sb1/sb2`` are 1-based in the file.
|
|
102
109
|
index_zero_based_in_file: Set False if IMIN..KMAX are 1-based in the file.
|
|
103
110
|
inlet_ids: PTY ids to treat as inlet; defaults to ``[5]``.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
111
|
+
Pass a dict ``{"name": "...", "ids": [...]}`` to rename the group, or
|
|
112
|
+
provide a mapping like ``{"Inlet": [...], "Pressure Inlet": [...]}``
|
|
113
|
+
to emit multiple named inlet groups. Each named group remembers its
|
|
114
|
+
base type in the returned ``bc_group`` mapping.
|
|
115
|
+
outlet_ids: PTY ids to treat as outlet; defaults to ``[6]`` (same dict
|
|
116
|
+
options as ``inlet_ids``).
|
|
117
|
+
wall_ids: PTY ids to treat as wall; defaults to ``[2]`` (same dict options).
|
|
118
|
+
symm_slip_ids: PTY ids to treat as symmetry/slip; defaults to ``[4]`` (same dict options).
|
|
107
119
|
custom_bc_ids: Optional mapping of ``group_name -> [pty ids]`` for any
|
|
108
120
|
additional boundary-condition groupings (e.g., ``{"cooling": [500]}``).
|
|
109
121
|
|
|
110
122
|
Returns:
|
|
111
123
|
Dict[str, object]: A dictionary with keys:
|
|
112
124
|
- ``face_matches``: list of face-pair dictionaries for connected blocks.
|
|
113
|
-
- ``
|
|
114
|
-
- ``
|
|
125
|
+
- ``all_surfaces``: list of faces on the exterior (no neighbor).
|
|
126
|
+
- ``ungrouped_surfaces``: exterior faces whose PTY does not belong to any BC group.
|
|
127
|
+
- ``bc_group``: mapping of bc name to a metadata dict
|
|
128
|
+
``{"type": base_type, "pty_ids": [...], "faces": [...]}``. Each
|
|
129
|
+
face dict also includes ``bc_name`` (group label) and ``bc_type``.
|
|
115
130
|
- ``gif_faces``: list of faces tagged as GIF (pty 12..21 or 1000).
|
|
116
131
|
- ``periodic_faces``: list of paired periodic faces (pty 3).
|
|
117
132
|
- ``volume_zones``: list describing volume zone type per superblock.
|
|
133
|
+
- ``blocksizes``: list of per-superblock cell counts (I*J*K).
|
|
134
|
+
- ``patches``: pandas DataFrame of raw patch records.
|
|
118
135
|
|
|
119
136
|
Examples:
|
|
120
137
|
Basic usage with defaults::
|
|
121
138
|
|
|
122
139
|
data = read_gridpro_connectivity("connectivity.dat")
|
|
123
|
-
inlet_faces = data["bc_group"]["inlet"]
|
|
140
|
+
inlet_faces = data["bc_group"]["inlet"]["faces"]
|
|
124
141
|
|
|
125
142
|
Override built-in BC ids and add a custom group::
|
|
126
143
|
|
|
@@ -129,11 +146,12 @@ def read_gridpro_connectivity(
|
|
|
129
146
|
inlet_ids=[5, 105],
|
|
130
147
|
custom_bc_ids={"cooling_hole1": [500]},
|
|
131
148
|
)
|
|
132
|
-
cooling_faces = data["bc_group"]["cooling_hole1"]
|
|
149
|
+
cooling_faces = data["bc_group"]["cooling_hole1"]["faces"]
|
|
133
150
|
"""
|
|
134
151
|
# ---------------------------- parsing ----------------------------
|
|
135
152
|
superblock_ptys: List[int] = []
|
|
136
|
-
|
|
153
|
+
superblock_sizes: List[int] = []
|
|
154
|
+
patch_rows: List[Dict[str, object]] = []
|
|
137
155
|
|
|
138
156
|
sb_offset = 0 if sb_zero_based_in_file else -1
|
|
139
157
|
idx_offset = 0 if index_zero_based_in_file else -1 # convert to 0-based if file is 1-based
|
|
@@ -175,7 +193,7 @@ def read_gridpro_connectivity(
|
|
|
175
193
|
except Exception as e:
|
|
176
194
|
raise ValueError(f"Failed to parse patch line: {' '.join(tokens)}") from e
|
|
177
195
|
|
|
178
|
-
|
|
196
|
+
patch_rows.append({
|
|
179
197
|
"pid": pid, "sb1": sb1, "sf1": sf1, "sb2": sb2, "sf2": sf2,
|
|
180
198
|
"fmap": fmap,
|
|
181
199
|
"L1i": L1i, "L1j": L1j, "L1k": L1k, "H1i": H1i, "H1j": H1j, "H1k": H1k,
|
|
@@ -192,9 +210,18 @@ def read_gridpro_connectivity(
|
|
|
192
210
|
tag = toks[0]
|
|
193
211
|
if tag == "SB":
|
|
194
212
|
superblock_ptys.append(int(toks[-2]))
|
|
213
|
+
try:
|
|
214
|
+
I_dim = int(toks[2])
|
|
215
|
+
J_dim = int(toks[3])
|
|
216
|
+
K_dim = int(toks[4])
|
|
217
|
+
superblock_sizes.append(I_dim * J_dim * K_dim)
|
|
218
|
+
except (IndexError, ValueError):
|
|
219
|
+
superblock_sizes.append(0)
|
|
195
220
|
elif tag == "P":
|
|
196
221
|
parse_patch(toks)
|
|
197
222
|
|
|
223
|
+
patches_df = pd.DataFrame(patch_rows)
|
|
224
|
+
|
|
198
225
|
# ---------------------------- helpers ----------------------------
|
|
199
226
|
def face_dict(sb: int, imin: int, jmin: int, kmin: int, imax: int, jmax: int, kmax: int, pty:int=-1) -> Dict[str, int]:
|
|
200
227
|
# Matches plot3d.connectivity_fast single-face dict shape
|
|
@@ -217,66 +244,123 @@ def read_gridpro_connectivity(
|
|
|
217
244
|
# ------------------------- build outputs -------------------------
|
|
218
245
|
# Connections: sb2 != -1 (i.e., not "none") and pty in {1,3}
|
|
219
246
|
connections: List[Dict[str, Dict[str, int]]] = []
|
|
220
|
-
for p in
|
|
221
|
-
if p
|
|
247
|
+
for p in patches_df.itertuples(index=False):
|
|
248
|
+
if p.sb2 != -1 and (p.pty in (1, 3)):
|
|
222
249
|
connections.append(
|
|
223
250
|
pair_dict(
|
|
224
|
-
p
|
|
225
|
-
p
|
|
251
|
+
p.sb1, (p.L1i, p.L1j, p.L1k, p.H1i, p.H1j, p.H1k),
|
|
252
|
+
p.sb2, (p.L2i, p.L2j, p.L2k, p.H2i, p.H2j, p.H2k),
|
|
226
253
|
)
|
|
227
254
|
)
|
|
228
255
|
|
|
229
|
-
# Outer faces: sb2 == -1 (means '0' in file when sb_zero_based_in_file=False; or literal 0 if already zero-based)
|
|
230
|
-
# To cover both cases, treat original token value 0 as "no neighbor".
|
|
231
|
-
# Since we already applied sb_offset, "no neighbor" now appears as -1.
|
|
232
|
-
outer_faces: List[Dict[str, int]] = []
|
|
233
|
-
pty_exclude = [6,5,4,2]
|
|
234
|
-
for p in patches:
|
|
235
|
-
if p["sb2"] == -1 and (p["pty"] not in pty_exclude):
|
|
236
|
-
outer_faces.append(
|
|
237
|
-
face_dict(p["sb1"], p["L1i"], p["L1j"], p["L1k"], p["H1i"], p["H1j"], p["H1k"], p["pty"]) # type: ignore
|
|
238
|
-
)
|
|
239
|
-
|
|
240
256
|
# Boundary-condition groups (by pty)
|
|
241
|
-
def bc_faces_for(pty_vals: List[int]) -> List[Dict[str, int]]:
|
|
257
|
+
def bc_faces_for(name: str, pty_vals: List[int], bc_type: str) -> List[Dict[str, int]]:
|
|
242
258
|
targets = set(pty_vals)
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
259
|
+
faces: List[Dict[str, int]] = []
|
|
260
|
+
for p in patches_df.itertuples(index=False):
|
|
261
|
+
if p.pty in targets:
|
|
262
|
+
face = face_dict(p.sb1, p.L1i, p.L1j, p.L1k, p.H1i, p.H1j, p.H1k, p.pty)
|
|
263
|
+
face["bc_name"] = name # type: ignore
|
|
264
|
+
face["bc_type"] = bc_type # type: ignore
|
|
265
|
+
faces.append(face)
|
|
266
|
+
return faces
|
|
267
|
+
|
|
268
|
+
def normalize_bc_arg(
|
|
269
|
+
arg: Optional[Union[List[int], Dict[str, Any]]],
|
|
270
|
+
bc_type: str,
|
|
271
|
+
default_ids: List[int],
|
|
272
|
+
) -> List[Tuple[str, List[int]]]:
|
|
273
|
+
def coerce_ids(values: Any) -> List[int]:
|
|
274
|
+
if values is None:
|
|
275
|
+
return []
|
|
276
|
+
# Allow nested {"ids": [...]} dictionaries to be passed directly
|
|
277
|
+
if isinstance(values, dict):
|
|
278
|
+
return coerce_ids(values.get("ids"))
|
|
279
|
+
if isinstance(values, (list, tuple, set)):
|
|
280
|
+
return [int(v) for v in values if v is not None]
|
|
281
|
+
return [int(values)]
|
|
282
|
+
|
|
283
|
+
if arg is None:
|
|
284
|
+
return [(bc_type, coerce_ids(default_ids))]
|
|
285
|
+
|
|
286
|
+
if isinstance(arg, dict):
|
|
287
|
+
# Support legacy {"name": str, "ids": [...]} as well as
|
|
288
|
+
# {"CustomName": [...], "AnotherName": [...]} mappings.
|
|
289
|
+
if "ids" in arg or "name" in arg:
|
|
290
|
+
ids = coerce_ids(arg.get("ids", default_ids))
|
|
291
|
+
name = str(arg.get("name", bc_type) or bc_type)
|
|
292
|
+
return [(name, ids)]
|
|
293
|
+
|
|
294
|
+
normalized: List[Tuple[str, List[int]]] = []
|
|
295
|
+
for name, ids_val in arg.items():
|
|
296
|
+
ids_list = coerce_ids(ids_val)
|
|
297
|
+
if ids_list:
|
|
298
|
+
normalized.append((str(name), ids_list))
|
|
299
|
+
return normalized
|
|
300
|
+
|
|
301
|
+
ids = coerce_ids(arg)
|
|
302
|
+
return [(bc_type, ids)]
|
|
303
|
+
|
|
304
|
+
bc_group: Dict[str, Dict[str, Any]] = {}
|
|
305
|
+
grouped_bc_ids: Set[int] = set()
|
|
306
|
+
|
|
307
|
+
def add_bc_group(name: str, ids: List[int], bc_type: str) -> None:
|
|
308
|
+
normalized_ids = sorted({int(i) for i in ids if i is not None})
|
|
309
|
+
faces = bc_faces_for(name, normalized_ids, bc_type) if normalized_ids else []
|
|
310
|
+
if not faces:
|
|
311
|
+
return
|
|
312
|
+
bc_group[name] = {
|
|
313
|
+
"type": bc_type,
|
|
314
|
+
"pty_ids": normalized_ids,
|
|
315
|
+
"faces": faces,
|
|
316
|
+
}
|
|
317
|
+
grouped_bc_ids.update(normalized_ids)
|
|
318
|
+
|
|
319
|
+
for bc_type, group in [
|
|
320
|
+
("inlet", normalize_bc_arg(inlet_ids, "inlet", [5])),
|
|
321
|
+
("outlet", normalize_bc_arg(outlet_ids, "outlet", [6])),
|
|
322
|
+
("symm_slip", normalize_bc_arg(symm_slip_ids, "symm_slip", [4])),
|
|
323
|
+
("wall", normalize_bc_arg(wall_ids, "wall", [2])),
|
|
324
|
+
]:
|
|
325
|
+
for name, ids in group:
|
|
326
|
+
add_bc_group(name, ids, bc_type)
|
|
254
327
|
|
|
255
328
|
# add any user-defined boundary groups keyed by name -> list of pty ids
|
|
256
329
|
if custom_bc_ids:
|
|
257
330
|
for name, ids in custom_bc_ids.items():
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
331
|
+
add_bc_group(name, ids or [], name)
|
|
332
|
+
|
|
333
|
+
# Outer faces: sb2 == -1 (means '0' in file when sb_zero_based_in_file=False; or literal 0 if already zero-based)
|
|
334
|
+
# To cover both cases, treat original token value 0 as "no neighbor".
|
|
335
|
+
# Since we already applied sb_offset, "no neighbor" now appears as -1.
|
|
336
|
+
all_surfaces: List[Dict[str, int]] = []
|
|
337
|
+
ungrouped_surfaces: List[Dict[str, int]] = []
|
|
338
|
+
for p in patches_df.itertuples(index=False):
|
|
339
|
+
if p.sb2 == -1:
|
|
340
|
+
face = face_dict(p.sb1, p.L1i, p.L1j, p.L1k, p.H1i, p.H1j, p.H1k, p.pty)
|
|
341
|
+
all_surfaces.append(face)
|
|
342
|
+
if p.pty not in grouped_bc_ids:
|
|
343
|
+
ungrouped_surfaces.append(face.copy())
|
|
344
|
+
|
|
261
345
|
# Periodic faces: pty == 3 (explicit pairs)
|
|
262
346
|
periodic_faces: List[Dict[str, Dict[str, int]]] = []
|
|
263
|
-
for p in
|
|
264
|
-
if p
|
|
347
|
+
for p in patches_df.itertuples(index=False):
|
|
348
|
+
if p.pty == 3 and p.sb2 != -1:
|
|
265
349
|
periodic_faces.append(
|
|
266
350
|
pair_dict(
|
|
267
|
-
p
|
|
268
|
-
p
|
|
351
|
+
p.sb1, (p.L1i, p.L1j, p.L1k, p.H1i, p.H1j, p.H1k),
|
|
352
|
+
p.sb2, (p.L2i, p.L2j, p.L2k, p.H2i, p.H2j, p.H2k),
|
|
269
353
|
)
|
|
270
354
|
)
|
|
271
355
|
|
|
272
356
|
# GIF faces grouped by sf1 for pty in 12..21 or == 1000
|
|
273
357
|
gif_faces: List[Dict[str, int]] = []
|
|
274
|
-
for p in
|
|
358
|
+
for p in patches_df.itertuples(index=False):
|
|
275
359
|
#? This is how we identify gifs inside of a grid pro connectivity file.
|
|
276
360
|
#? If the PTY is between 12 and 21 these are gifs
|
|
277
|
-
if (12 <= p
|
|
278
|
-
face_temp = face_dict(p
|
|
279
|
-
face_temp["id"] = p
|
|
361
|
+
if (12 <= p.pty <= 21) or (p.pty == 1000):
|
|
362
|
+
face_temp = face_dict(p.sb1, p.L1i, p.L1j, p.L1k, p.H1i, p.H1j, p.H1k)
|
|
363
|
+
face_temp["id"] = p.pty # type: ignore
|
|
280
364
|
gif_faces.append(face_temp)
|
|
281
365
|
|
|
282
366
|
# Volume zones (unique superblock ptys; odd→fluid, even→solid) with contiguous ids
|
|
@@ -293,9 +377,59 @@ def read_gridpro_connectivity(
|
|
|
293
377
|
|
|
294
378
|
return {
|
|
295
379
|
"face_matches": connections,
|
|
296
|
-
"
|
|
380
|
+
"all_surfaces": all_surfaces,
|
|
381
|
+
"ungrouped_surfaces": ungrouped_surfaces,
|
|
297
382
|
"bc_group": bc_group,
|
|
298
383
|
"gif_faces": gif_faces,
|
|
299
384
|
"periodic_faces": periodic_faces,
|
|
300
385
|
"volume_zones": volume_zones,
|
|
386
|
+
"blocksizes": superblock_sizes,
|
|
387
|
+
"patches": patches_df,
|
|
301
388
|
}
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def bc_faces_by_type(
|
|
392
|
+
bc_group: Dict[str, Any],
|
|
393
|
+
bc_type: str,
|
|
394
|
+
*,
|
|
395
|
+
unique: bool = False,
|
|
396
|
+
) -> List[Dict[str, int]]:
|
|
397
|
+
"""Collect faces for a specific boundary-condition type across all named groups.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
bc_group: Mapping returned by :func:`read_gridpro_connectivity` (supports
|
|
401
|
+
both the legacy ``{name: [faces]}`` and the new
|
|
402
|
+
``{name: {"type": ..., "faces": [...]}}`` shapes).
|
|
403
|
+
bc_type: Base BC type (``"inlet"``, ``"outlet"``, ``"wall"``, ``"symm_slip"``).
|
|
404
|
+
unique: When True, keep only the first face per unique ``id``.
|
|
405
|
+
|
|
406
|
+
Returns:
|
|
407
|
+
List of face dictionaries belonging to the requested base type.
|
|
408
|
+
"""
|
|
409
|
+
faces: List[Dict[str, int]] = []
|
|
410
|
+
seen: Set[int] = set()
|
|
411
|
+
target = (bc_type or "").lower()
|
|
412
|
+
|
|
413
|
+
for entry in bc_group.values():
|
|
414
|
+
entry_type = None
|
|
415
|
+
entry_faces: List[Dict[str, int]] = []
|
|
416
|
+
|
|
417
|
+
if isinstance(entry, dict) and "faces" in entry:
|
|
418
|
+
entry_faces = entry.get("faces", []) or []
|
|
419
|
+
entry_type = entry.get("type") or entry.get("bc_type")
|
|
420
|
+
elif isinstance(entry, list):
|
|
421
|
+
entry_faces = entry
|
|
422
|
+
else:
|
|
423
|
+
continue
|
|
424
|
+
|
|
425
|
+
for face in entry_faces:
|
|
426
|
+
ftype = entry_type or face.get("bc_type") or face.get("bc_name")
|
|
427
|
+
if not isinstance(ftype, str) or ftype.lower() != target:
|
|
428
|
+
continue
|
|
429
|
+
fid = face.get("id")
|
|
430
|
+
if unique and isinstance(fid, int):
|
|
431
|
+
if fid in seen:
|
|
432
|
+
continue
|
|
433
|
+
seen.add(fid)
|
|
434
|
+
faces.append(face)
|
|
435
|
+
return faces
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|