plot3d 1.7.0__tar.gz → 1.7.1__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.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: plot3d
3
- Version: 1.7.0
3
+ Version: 1.7.1
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,8 +8,10 @@ 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
11
12
  Requires-Dist: networkx
12
13
  Requires-Dist: numpy
13
14
  Requires-Dist: pandas
15
+ Requires-Dist: pymetis
14
16
  Requires-Dist: scipy
15
17
  Requires-Dist: tqdm
@@ -7,7 +7,7 @@ from .blockfunctions import rotate_block, get_outer_bounds, block_connection_mat
7
7
  from .block_merging_mixed_facepairs import combine_nxnxn_cubes_mixed_pairs
8
8
  from .connectivity import find_matching_blocks, get_face_intersection, connectivity_fast, face_matches_to_dict
9
9
  from .face import Face
10
- from .facefunctions import create_face_from_diagonals, get_outer_faces, find_connected_faces, find_bounding_faces,split_face,find_face_nearest_point,match_faces_dict_to_list,outer_face_dict_to_list,find_closest_block
10
+ from .facefunctions import create_face_from_diagonals, get_outer_faces, find_bounding_faces,split_face,find_face_nearest_point,match_faces_dict_to_list,outer_face_dict_to_list,find_closest_block
11
11
  from .read import read_plot3D, read_ap_nasa
12
12
  from .write import write_plot3D
13
13
  from .differencing import find_edges, find_face_edges
@@ -16,10 +16,4 @@ from .point_match import point_match
16
16
  from .split_block import split_blocks, Direction
17
17
  from .listfunctions import unique_pairs
18
18
 
19
- # Try importing metis
20
- if os.getenv('METIS_DLL') is not None:
21
- if import_module('metis') is not None:
22
- import metis
23
- from .graph import block_to_graph,get_face_vertex_indices,get_starting_vertex,add_connectivity_to_graph, block_connectivity_to_graph
24
- else:
25
- print("METIS_DLL is not set. metis may not be configured. plot3D will function without metis")
19
+ from .graph import write_ddcmp, build_weighted_graph_from_face_matches,csr_from_adj_and_weights,partition_from_face_matches
@@ -91,81 +91,124 @@ def get_outer_bounds(blocks:List[Block]):
91
91
 
92
92
  return tuple(xbounds),tuple(ybounds),tuple(zbounds)
93
93
 
94
- def block_connection_matrix(blocks:List[Block],outer_faces:List[Dict[str,int]]=[],tol:float=1E-8):
95
- """Creates a matrix representing how block edges are connected to each other
96
94
 
97
- Args:
98
- blocks (List[Block]): List of blocks that describe the Plot3D mesh
99
- outer_faces (List[Dict[str,int]], optional): List of outer faces remaining from connectivity. Useful if you are interested in finding faces that are exterior to the block. Also useful if you combine outerfaces with match faces, this will help identify connections by looking at split faces. Defaults to [].
100
- tol (float, optional): Matching tolerance to look for when comparing face centroids.
101
-
102
- Returns:
103
- (Tuple): containing
95
+ def block_connection_matrix(
96
+ blocks: List[Block],
97
+ outer_faces: List[Dict[str, int]] = [],
98
+ tol: float = 1e-8,
99
+ *,
100
+ node_tol_xyz: float = 1e-7,
101
+ min_shared_frac: float = 0.02,
102
+ min_shared_abs: int = 4,
103
+ stride_u: int = 1,
104
+ stride_v: int = 1,
105
+ use_area_fallback: bool = True,
106
+ area_min_overlap_frac: float = 0.01
107
+ ) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
108
+ """
109
+ Creates matrices representing how blocks are connected.
104
110
 
105
- *connectivity* (np.ndarray): integer matrix defining how the blocks are connected to each other
106
- *connectivity_i* (np.ndarray): integer matrix defining connectivity of all blocks where IMAX=IMIN
107
- *connectivity_j* (np.ndarray): integer matrix defining connectivity of all blocks where JMAX=JMIN
108
- *connectivity_k* (np.ndarray): integer matrix defining connectivity of all blocks where KMAX=KMIN
109
-
111
+ Returns:
112
+ connectivity : (n,n) overall connectivity (1 = connected, -1 = not)
113
+ connectivity_i : (n,n) connections where both faces are I-constant
114
+ connectivity_j : (n,n) connections where both faces are J-constant
115
+ connectivity_k : (n,n) connections where both faces are K-constant
110
116
  """
111
- # Reduce the size of the blocks by the GCD
112
- gcd_array = list()
117
+ # Reduce the size of the blocks by the minimum GCD so index grids line up
118
+ gcd_array = []
113
119
  for block_indx in range(len(blocks)):
114
120
  block = blocks[block_indx]
115
- gcd_array.append(math.gcd(block.IMAX-1, math.gcd(block.JMAX-1, block.KMAX-1)))
116
- gcd_to_use = min(gcd_array) # You need to use the minimum gcd otherwise 1 block may not exactly match the next block. They all have to be scaled the same way.
117
- blocks = reduce_blocks(deepcopy(blocks),gcd_to_use)
121
+ gcd_array.append(math.gcd(block.IMAX - 1, math.gcd(block.JMAX - 1, block.KMAX - 1)))
122
+ gcd_to_use = min(gcd_array)
123
+ blocks = reduce_blocks(deepcopy(blocks), gcd_to_use)
118
124
 
119
- # Face to List
120
- outer_faces_all = list()
125
+ # Convert dict outer faces (if provided) to Face objects at the reduced resolution
126
+ outer_faces_all: List[Face] = []
121
127
  for o in outer_faces:
122
- face = create_face_from_diagonals(blocks[o['block_index']], int(o['IMIN']/gcd_to_use), int(o['JMIN']/gcd_to_use),
123
- int(o['KMIN']/gcd_to_use), int(o['IMAX']/gcd_to_use), int(o['JMAX']/gcd_to_use), int(o['KMAX']/gcd_to_use))
124
- face.set_block_index(o['block_index'])
128
+ face = create_face_from_diagonals(
129
+ blocks[o["block_index"]],
130
+ int(o["IMIN"] / gcd_to_use), int(o["JMIN"] / gcd_to_use), int(o["KMIN"] / gcd_to_use),
131
+ int(o["IMAX"] / gcd_to_use), int(o["JMAX"] / gcd_to_use), int(o["KMAX"] / gcd_to_use)
132
+ )
133
+ face.set_block_index(o["block_index"])
125
134
  if "id" in o:
126
- face.id = o['id']
135
+ face.id = o["id"]
127
136
  outer_faces_all.append(face)
128
-
129
- outer_faces = outer_faces_all
137
+ outer_faces = outer_faces_all # type: ignore
130
138
 
131
139
  n = len(blocks)
132
- connectivity = np.eye(n,dtype=np.int8)
133
- combos = list(combinations(range(n),2))
134
- for indx in (pbar:=trange(len(combos))):
135
- i,j = combos[indx]
136
- pbar.set_description(f"Building block to block connectivity matrix: checking {i}")
140
+ connectivity = np.eye(n, dtype=np.int8)
141
+ connectivity_i = np.eye(n, dtype=np.int8)
142
+ connectivity_j = np.eye(n, dtype=np.int8)
143
+ connectivity_k = np.eye(n, dtype=np.int8)
144
+
145
+ combos = list(combinations(range(n), 2))
146
+ for idx in (pbar := trange(len(combos))):
147
+ i, j = combos[idx]
148
+ pbar.set_description(f"Building connectivity: checking block {i}")
137
149
  b1 = blocks[i]
138
150
 
139
- if len(outer_faces)==0: # Get the outerfaces to search
140
- b1_outer_faces,_ = get_outer_faces(b1)
151
+ # Gather outer faces for block i
152
+ if len(outer_faces) == 0:
153
+ b1_outer_faces, _ = get_outer_faces(b1)
141
154
  else:
142
- b1_outer_faces = [o for o in outer_faces if o.BlockIndex == i] # type: ignore
143
-
144
- if i != j and connectivity[i,j]!=-1:
145
- b2 = blocks[j]
155
+ b1_outer_faces = [o for o in outer_faces if o.BlockIndex == i]
146
156
 
147
- if len(outer_faces)==0: # Get the outerfaces to search
148
- b2_outer_faces,_ = get_outer_faces(b2)
157
+ if i != j and connectivity[i, j] != -1:
158
+ b2 = blocks[j]
159
+ if len(outer_faces) == 0:
160
+ b2_outer_faces, _ = get_outer_faces(b2)
149
161
  else:
150
- b2_outer_faces = [o for o in outer_faces if o.BlockIndex == j] # type: ignore
162
+ b2_outer_faces = [o for o in outer_faces if o.BlockIndex == j]
163
+
164
+ connection_found = False
151
165
 
152
- # Check to see if any of the outer faces of the blocks match
153
- connection_found=False
154
166
  for f1 in b1_outer_faces:
155
167
  for f2 in b2_outer_faces:
156
- if (f1.is_connected(f2,tol)): # type: ignore # Check if face centroid is the same
157
- connectivity[i,j] = 1 # Default block to block connection matrix
158
- connectivity[j,i] = 1
159
- connection_found=True
160
- # c = np.sum(connectivity[i,:]==1)
161
- # print(f"block {i} connections {c}")
168
+ # 1) Primary: node-sharing (exact common grid nodes)
169
+ if f1.touches_by_nodes(
170
+ f2, b1, b2,
171
+ tol_xyz=node_tol_xyz,
172
+ min_shared_frac=min_shared_frac,
173
+ min_shared_abs=min_shared_abs,
174
+ stride_u=stride_u,
175
+ stride_v=stride_v
176
+ ):
177
+ connectivity[i, j] = connectivity[j, i] = 1
178
+ if f1.const_type == 0 and f2.const_type == 0:
179
+ connectivity_i[i, j] = connectivity_i[j, i] = 1
180
+ if f1.const_type == 1 and f2.const_type == 1:
181
+ connectivity_j[i, j] = connectivity_j[j, i] = 1
182
+ if f1.const_type == 2 and f2.const_type == 2:
183
+ connectivity_k[i, j] = connectivity_k[j, i] = 1
184
+
185
+ # Debug message to see what matched
186
+ print(f"[nodes] blocks {i} and {j} connected via {f1} <-> {f2}")
187
+ connection_found = True
162
188
  break
189
+
190
+ # 2) Optional fallback: polygon overlap for non-conforming interfaces
191
+ if (not connection_found) and use_area_fallback:
192
+ if f1.touches(f2, min_overlap_frac=area_min_overlap_frac):
193
+ connectivity[i, j] = connectivity[j, i] = 1
194
+ if f1.const_type == 0 and f2.const_type == 0:
195
+ connectivity_i[i, j] = connectivity_i[j, i] = 1
196
+ if f1.const_type == 1 and f2.const_type == 1:
197
+ connectivity_j[i, j] = connectivity_j[j, i] = 1
198
+ if f1.const_type == 2 and f2.const_type == 2:
199
+ connectivity_k[i, j] = connectivity_k[j, i] = 1
200
+
201
+ print(f"[area ] blocks {i} and {j} connected via {f1} <-> {f2}")
202
+ connection_found = True
203
+ break
204
+
163
205
  if connection_found:
164
206
  break
207
+
165
208
  if not connection_found:
166
- connectivity[i,j] = -1
167
- connectivity[j,i] = -1
168
- return connectivity
209
+ connectivity[i, j] = connectivity[j, i] = -1
210
+
211
+ return connectivity, connectivity_i, connectivity_j, connectivity_k
169
212
 
170
213
  def plot_blocks(blocks):
171
214
  gcd_array = list()