plot3d 1.6.1__tar.gz → 1.6.3__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
1
  Metadata-Version: 2.1
2
2
  Name: plot3d
3
- Version: 1.6.1
3
+ Version: 1.6.3
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
@@ -10,6 +10,7 @@ Classifier: Programming Language :: Python :: 3.8
10
10
  Classifier: Programming Language :: Python :: 3.9
11
11
  Classifier: Programming Language :: Python :: 3.10
12
12
  Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
13
14
  Requires-Dist: numpy
14
15
  Requires-Dist: pandas
15
16
  Requires-Dist: scipy
@@ -10,3 +10,4 @@ from .periodicity import periodicity, periodicity_fast, create_rotation_matrix,
10
10
  from .point_match import point_match
11
11
  from .split_block import split_blocks, Direction
12
12
  from .listfunctions import unique_pairs
13
+ from .graph import block_to_graph,get_face_vertex_indices,get_starting_vertex,add_connectivity_to_graph
@@ -136,6 +136,7 @@ def find_connected_faces(face_to_search:Face,outer_faces:List[Face],connectivity
136
136
  Returns:
137
137
  List[Face]: list of all faces that connect with face_to_search and it's neighbors. Beware of duplicates.
138
138
  """
139
+ connectivity_matrix = connectivity_matrix.copy()
139
140
  all_matching_faces = [face_to_search]
140
141
  faces_to_search = [face_to_search]
141
142
  faces_searched = []
@@ -153,7 +154,8 @@ def find_connected_faces(face_to_search:Face,outer_faces:List[Face],connectivity
153
154
  angle = abs(math.degrees(math.acos(np.dot(n1,n2)/(np.linalg.norm(n1)*np.linalg.norm(n2)))))
154
155
  if angle>90:
155
156
  angle = 180-angle
156
- if angle<50:
157
+
158
+ if angle<30:
157
159
  connectivity_matrix[selected_block_indx, f.BlockIndex] = 0
158
160
  connectivity_matrix[f.BlockIndex, selected_block_indx] = 0
159
161
  matching_faces.append(f)
@@ -0,0 +1,154 @@
1
+ import numpy as np
2
+ import numpy.typing as npt
3
+ import networkx as nx
4
+ import itertools as it
5
+ from typing import Dict, Tuple, List
6
+
7
+
8
+ def block_to_graph(IMAX:int,JMAX:int,KMAX:int,offset:int = 0) -> nx.graph.Graph:
9
+ """Converts a block to a graph
10
+
11
+ Args:
12
+ IMAX (int): block.IMAX
13
+ JMAX (int): block.JMAX
14
+ KMAX (int): block.KMAX
15
+ offset (int): IMAX*JMAX*KMAX of previous block
16
+
17
+ Returns:
18
+ nx.graph.Graph: networkx graph object
19
+ """
20
+ G = nx.Graph()
21
+ irange = np.arange(IMAX)
22
+ jrange = np.arange(JMAX)
23
+ krange = np.arange(KMAX)
24
+ kshift = 0
25
+ for k in range(KMAX): # K slices
26
+ kshift = IMAX*JMAX*k
27
+
28
+ for j in range(JMAX):
29
+ nx.add_star(G, offset+ kshift + IMAX*j + irange,weight=1)
30
+
31
+ for i in range(IMAX):
32
+ nx.add_star(G, offset+kshift + i + IMAX*jrange,weight=1)
33
+
34
+ for p in range(IMAX*JMAX):
35
+ nx.add_star(G, offset + p + IMAX*JMAX*krange)
36
+ return G
37
+
38
+ def get_face_vertex_indices(IMIN:int,JMIN:int,KMIN:int,IMAX:int,JMAX:int,KMAX:int,block_size:Tuple[int,int,int]) -> npt.NDArray:
39
+ """Returns an array containing the vertex number of a given face
40
+
41
+ Args:
42
+ IMIN (int): starting I index
43
+ JMIN (int): starting J index
44
+ KMIN (int): starting K index
45
+ IMAX (int): ending I index
46
+ JMAX (int): ending J index
47
+ KMAX (int): ending K index
48
+ block_size (Tuple[int,int,int]): This is the actual IMAX,JMAX,KMAX of the block
49
+
50
+ Returns:
51
+ npt.NDArray: an array containing all the vertices
52
+ """
53
+
54
+ def create_range(indx1,indx2):
55
+ if indx1<indx2:
56
+ return np.arange(indx1,indx2)
57
+ else:
58
+ return np.arange(indx1-1,indx2-1,-1)
59
+
60
+ indices = list()
61
+ if IMIN==IMAX:
62
+ jrange = create_range(JMIN,JMAX)
63
+ krange = create_range(KMIN,KMAX)
64
+ if IMIN==block_size[0]: # IMIN is really IMAX
65
+ for j in jrange:
66
+ j_offset = j*block_size[0] # IMAX * JMAX
67
+ indices.append(j_offset + block_size[0]*block_size[1]*krange + IMAX-1)
68
+ else:
69
+ for j in jrange:
70
+ j_offset = j*block_size[0]
71
+ indices.append(j_offset + block_size[0]*block_size[1]*krange)
72
+
73
+ elif JMIN == JMAX:
74
+ irange = create_range(IMIN,IMAX)
75
+ krange = create_range(KMIN,KMAX)
76
+ if JMIN==block_size[1]: # JMIN is really JMAX
77
+ for k in krange:
78
+ k_offset = k*block_size[0]*block_size[1]
79
+ indices.append(k_offset + block_size[0]*(block_size[1]-1)+irange)
80
+ else: # JMIN
81
+ for k in krange:
82
+ k_offset = k*block_size[0]*block_size[1]
83
+ indices.append(k_offset + irange)
84
+ else:
85
+ irange = create_range(IMIN,IMAX)
86
+ jrange = create_range(JMIN,JMAX)
87
+ if KMIN == block_size[2]: # KMIN is really KMAX
88
+ offset = (KMIN-1)*block_size[0]*block_size[1]
89
+ else:
90
+ offset = 0
91
+ for j in jrange:
92
+ indices.append(offset+block_size[0]*j + irange)
93
+ return np.array(indices).flatten()
94
+
95
+ def get_starting_vertex(blockIndex:int,block_sizes:List[Tuple[int,int,int]]) -> int:
96
+ """Gets the starting vertex index of the block
97
+
98
+ Args:
99
+ blockIndex (int): index of block
100
+ block_sizes (List[Tuple[int,int,int]]): List of all the [[IMAX,JMAX,KMAX]]
101
+
102
+ Returns:
103
+ int: offset
104
+ """
105
+ offset = 0
106
+ for i in range(blockIndex):
107
+ offset+=block_sizes[i][0]*block_sizes[i][1]*block_sizes[i][2]
108
+ return offset
109
+
110
+ def add_connectivity_to_graph(G:nx.classes.graph.Graph,block_sizes:List[Tuple[int,int,int]],connectivities:List[Dict[str,int]]) -> nx.graph.Graph:
111
+ """Convert plot3d defined connectivity into additional graph edges
112
+
113
+ Args:
114
+ G (nx.classes.graph.Graph): Giant graph
115
+ block_sizes (List[Tuple[int,int,int]]): _description_
116
+ connectivity (List[Dict[str,int]]): _description_
117
+
118
+ Returns:
119
+ nx.graph.Graph: networkx graph object with added edges
120
+ """
121
+
122
+ for con in connectivities:
123
+ block1_index = con['block1']['index']
124
+ block2_index = con['block2']['index']
125
+ IMIN1,IMAX1 = con['block1']['IMIN'], con['block1']['IMAX']
126
+ JMIN1,JMAX1 = con['block1']['JMIN'], con['block1']['JMAX']
127
+ KMIN1,KMAX1 = con['block1']['KMIN'], con['block1']['KMAX']
128
+
129
+ IMIN2,IMAX2 = con['block2']['IMIN'], con['block2']['IMAX']
130
+ JMIN2,JMAX2 = con['block2']['JMIN'], con['block2']['JMAX']
131
+ KMIN2,KMAX2 = con['block2']['KMIN'], con['block2']['KMAX']
132
+
133
+ # Number of connectivities should match
134
+ face1 = get_face_vertex_indices(IMIN1,IMAX1,JMIN1,JMAX1,KMIN1,KMAX1,block_sizes[block1_index]) + get_starting_vertex(block1_index, block_sizes)
135
+ face2 = get_face_vertex_indices(IMIN2,IMAX2,JMIN2,JMAX2,KMIN2,KMAX2,block_sizes[block2_index]) + get_starting_vertex(block2_index, block_sizes)
136
+
137
+ if block1_index!= block2_index:
138
+ nodes_to_add = face1
139
+ nodes_to_replace = face2
140
+ for node_to_add,node_to_replace in zip(nodes_to_add,nodes_to_replace):
141
+ G.add_edges_from(
142
+ it.product(
143
+ G.neighbors(node_to_add),
144
+ G.neighbors(node_to_replace)
145
+ )
146
+ )
147
+ G.remove_node(node_to_replace)
148
+
149
+ assert len(face1) == len(face2), f"Number of connections from {block1_index} I[{IMIN1},{IMAX1}], J[{JMIN1},{JMAX1}], K[{KMIN1},{KMAX1}] to {block2_index} I[{IMIN2},{IMAX2}], J[{JMIN2},{JMAX2}], K[{KMIN2},{KMAX2}] should match."
150
+
151
+ for i in range(len(face1)):
152
+ G.add_edge(face1[i],face2[i])
153
+
154
+ return G
@@ -545,9 +545,18 @@ def translational_periodicity(blocks:List[Block], lower_connected_faces:List[Dic
545
545
  dx = xmax-xmin if not delta else delta
546
546
  [b.shift(sign*dx,translational_direction) for b in blocks_shifted]
547
547
  elif translational_direction.lower().strip() == "y":
548
- ymin = min([b.Y.min() for b in blocks])
549
- ymax = max([b.Y.max() for b in blocks])
550
- dy = ymax-ymin if not delta else delta
548
+ ymin = np.array([(b.X.min(), b.Y.min(), b.Z.min()) for b in blocks]) # Look at the front face
549
+ ymax = np.array([(b.X.min(), b.Y.max(), b.Z.min()) for b in blocks])
550
+ xmin = min([b.X.min() for b in blocks])
551
+ zmin = min([b.Z.min() for b in blocks])
552
+
553
+ ymin = ymin[ymin[:,0] == xmin,:]
554
+ ymax = ymax[ymax[:,0] == xmin,:]
555
+
556
+ ymin = ymin[ymin[:,2] == zmin,:]
557
+ ymax = ymax[ymax[:,2] == zmin,:]
558
+
559
+ dy = ymax[:,1].max() - ymin[:,1].min() if not delta else delta
551
560
  [b.shift(sign*dy,translational_direction) for b in blocks_shifted]
552
561
  else: # direction.lower().strip() == "z"
553
562
  zmin = min([b.Z.min() for b in blocks])
@@ -580,11 +589,14 @@ def translational_periodicity(blocks:List[Block], lower_connected_faces:List[Dic
580
589
  pbar.set_description(f"Checking connections block {face1.blockIndex} with {face2.blockIndex}")
581
590
  # Shift block 1 -> Check periodicity -> if not periodic -> shift Block 1 opposite direction -> Check periodicity
582
591
  # Rotate Block 1
592
+ block1 = blocks[face1.blockIndex]
583
593
  block1_shifted = blocks_shifted[face1.blockIndex]
584
594
  block2 = blocks[face2.blockIndex]
595
+ block2_shifted = blocks_shifted[face2.blockIndex]
596
+
585
597
  # Check periodicity
586
- _, periodic_faces_temp, split_faces_temp = __periodicity_check__(face1,face2,block1_shifted, block2,periodicity_tol)
587
-
598
+ _, periodic_faces_temp, split_faces_temp = __periodicity_check__(face1,face2,block1_shifted, block2,periodicity_tol)
599
+
588
600
  if len(periodic_faces_temp) > 0:
589
601
  lower_connected_faces.pop(0)
590
602
  upper_connected_faces.pop(indx)
@@ -597,6 +609,21 @@ def translational_periodicity(blocks:List[Block], lower_connected_faces:List[Dic
597
609
  periodic_found = True
598
610
  pbar.update(1)
599
611
  break
612
+ else:
613
+ # Try the other way
614
+ _, periodic_faces_temp, split_faces_temp = __periodicity_check__(face1,face2,block1,block2_shifted,periodicity_tol)
615
+ if len(periodic_faces_temp) > 0:
616
+ lower_connected_faces.pop(0)
617
+ upper_connected_faces.pop(indx)
618
+ periodic_faces.append(periodic_faces_temp)
619
+ periodic_faces_export.append(face_matches_to_dict(periodic_faces_temp[0],periodic_faces_temp[1],block1, block2_shifted))
620
+ lower_split_faces = [s for s in split_faces_temp if s.BlockIndex in lower_blocks]
621
+ upper_split_faces = [s for s in split_faces_temp if s.BlockIndex in upper_blocks]
622
+ lower_connected_faces.extend(lower_split_faces)
623
+ upper_connected_faces.extend(upper_split_faces)
624
+ periodic_found = True
625
+ pbar.update(1)
626
+ break
600
627
 
601
628
  if periodic_found == False:
602
629
  # Lets switch the order
@@ -609,8 +636,7 @@ def translational_periodicity(blocks:List[Block], lower_connected_faces:List[Dic
609
636
  print(f"\nNot periodic {translational_direction}")
610
637
  else:
611
638
  print(f"\nPeriodic {translational_direction}")
612
-
613
-
639
+
614
640
  # remove any duplicate periodic face pairs
615
641
  indx_to_remove = list()
616
642
  for i in range(len(periodic_faces)):
@@ -4,6 +4,7 @@ import struct
4
4
  from typing import List
5
5
  from .block import Block
6
6
  from scipy.io import FortranFile
7
+ from tqdm import trange
7
8
 
8
9
  def __read_plot3D_chunk_binary(f,IMAX:int,JMAX:int,KMAX:int, big_endian:bool=False,read_double:bool=True):
9
10
  """Reads and formats a binary chunk of data into a plot3D block
@@ -28,29 +29,45 @@ def __read_plot3D_chunk_binary(f,IMAX:int,JMAX:int,KMAX:int, big_endian:bool=Fal
28
29
  A[i,j,k] = struct.unpack(">f",f.read(4))[0] if big_endian else struct.unpack("<f",f.read(4))[0]
29
30
  return A
30
31
 
31
- def __read_plot3D_chunk_ASCII(tokenArray:List[str],offset:int,IMAX:int,JMAX:int,KMAX:int):
32
- """Reads an ascii chunk of plot3D data into a block
32
+ def read_word(f):
33
+ """Continously read a word from an ascii file
33
34
 
34
35
  Args:
35
- tokenArray (List[str]): this is a list of strings separated by a space, new line character removed ["12","22", ... etc]
36
- offset (int): how many entries to skip in the array based on block size (IMAX*JMAX*KMAX) of the previous block
36
+ f (io): file handle
37
+
38
+ Yields:
39
+ float: value from ascii file
40
+ """
41
+ for line in f:
42
+ line = line.strip().replace('\n','').split(' ')
43
+ tokenArray = [float(entry) for entry in line if entry]
44
+ for token in tokenArray:
45
+ yield token
46
+
47
+ def __read_plot3D_chunk_ASCII(f,IMAX:int,JMAX:int,KMAX:int):
48
+ """Reads and formats a binary chunk of data into a plot3D block
49
+
50
+ Args:
51
+ f (io): file handle
37
52
  IMAX (int): maximum I index
38
53
  JMAX (int): maximum J index
39
- KMAX (int): maximum K index
54
+ KMAX (int): maximum K index
55
+ big_endian (bool, Optional): Use big endian format for reading binary files. Defaults False.
40
56
 
41
57
  Returns:
42
- numpy.ndarray: Plot3D variable either X,Y, or Z
58
+ numpy.ndarray: Plot3D variable either X,Y, or Z
43
59
  """
44
- '''Works for ASCII files
45
- '''
46
- A = np.empty(shape=(IMAX, JMAX, KMAX))
47
- for k in range(KMAX):
48
- for j in range(JMAX):
49
- for i in range(IMAX):
50
- A[i,j,k] = tokenArray[offset]
51
- offset+=1
52
-
53
- return A, offset
60
+ tokenArray = np.zeros(shape=(IMAX*JMAX*KMAX))
61
+ i = 0
62
+ for w in read_word(f):
63
+ tokenArray[i] = w
64
+ i+=1
65
+ if i>len(tokenArray)-1:
66
+ break
67
+
68
+ A = np.reshape(tokenArray,newshape=(KMAX,JMAX,IMAX))
69
+ A = np.transpose(A,[2,1,0])
70
+ return A
54
71
 
55
72
  def read_ap_nasa(filename:str):
56
73
  """Reads an AP NASA File and converts it to Block format which can be exported to a plot3d file
@@ -151,17 +168,12 @@ def read_plot3D(filename:str, binary:bool=True,big_endian:bool=False,read_double
151
168
  tokens = [int(w) for w in IJK if w]
152
169
  IMAX.append(tokens[0])
153
170
  JMAX.append(tokens[1])
154
- KMAX.append(tokens[2])
155
-
156
- lines = [l.replace('\n','').split(' ') for l in f.readlines()] # Basically an array of strings representing numbers
157
- lines = [item for sublist in lines for item in sublist] # Flatten list of lists https://stackabuse.com/python-how-to-flatten-list-of-lists/
158
-
159
- tokenArray = [float(entry) for entry in lines if entry] # Convert everything to float
160
- offset = 0
161
- for b in range(nblocks):
162
- X, offset = __read_plot3D_chunk_ASCII(tokenArray,offset,IMAX[b],JMAX[b],KMAX[b])
163
- Y, offset = __read_plot3D_chunk_ASCII(tokenArray,offset,IMAX[b],JMAX[b],KMAX[b])
164
- Z, offset = __read_plot3D_chunk_ASCII(tokenArray,offset,IMAX[b],JMAX[b],KMAX[b])
171
+ KMAX.append(tokens[2])
172
+
173
+ for b in trange(nblocks):
174
+ X = __read_plot3D_chunk_ASCII(f,IMAX[b],JMAX[b],KMAX[b])
175
+ Y = __read_plot3D_chunk_ASCII(f,IMAX[b],JMAX[b],KMAX[b])
176
+ Z = __read_plot3D_chunk_ASCII(f,IMAX[b],JMAX[b],KMAX[b])
165
177
  b_temp = Block(X,Y,Z)
166
178
  blocks.append(b_temp)
167
179
  return blocks
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "plot3d"
3
- version = "1.6.1"
3
+ version = "1.6.3"
4
4
  description = "Plot3D python utilities for reading and writing and also finding connectivity between blocks"
5
5
  authors = ["Paht Juangphanich <paht.juangphanich@nasa.gov>"]
6
6
 
plot3d-1.6.1/setup.py DELETED
@@ -1,30 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- from setuptools import setup
3
-
4
- packages = \
5
- ['plot3d']
6
-
7
- package_data = \
8
- {'': ['*']}
9
-
10
- install_requires = \
11
- ['numpy', 'pandas', 'scipy', 'tqdm']
12
-
13
- setup_kwargs = {
14
- 'name': 'plot3d',
15
- 'version': '1.6.1',
16
- 'description': 'Plot3D python utilities for reading and writing and also finding connectivity between blocks',
17
- 'long_description': 'None',
18
- 'author': 'Paht Juangphanich',
19
- 'author_email': 'paht.juangphanich@nasa.gov',
20
- 'maintainer': 'None',
21
- 'maintainer_email': 'None',
22
- 'url': 'None',
23
- 'packages': packages,
24
- 'package_data': package_data,
25
- 'install_requires': install_requires,
26
- 'python_requires': '>=3.7.1,<4.0.0',
27
- }
28
-
29
-
30
- setup(**setup_kwargs)
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