95 Parse a single Peano grid patch file and extract spatial information.
97 This function reads a grid patch file and extracts offset and size information
98 for each patch using regular expressions. Patches are grouped by size to
99 determine refinement levels.
102 file_path (str): Path to the grid patch file
103 size_group (defaultdict): Dictionary to store {size: [offsets]} mapping
104 dimensions (int): Spatial dimensions count (2 or 3)
107 defaultdict: Updated size_group with patch information
110 The regular expression patterns match the standard Peano patch format:
111 - 'begin patch' blocks with offset and size specifications
112 - Floating point numbers in scientific notation
113 - Space-separated coordinate values
117 with open(file_path,
"r", encoding=
"latin-1")
as f:
122 patch_pattern = re.compile(
123 r'begin patch.*?offset ([\d.eE+\-]+ [\d.eE+\-]+ [\d.eE+\-]+).*?'
124 r'size ([\d.eE+\-]+ [\d.eE+\-]+ [\d.eE+\-]+)',
128 patch_pattern = re.compile(
129 r'begin patch.*?offset ([\d.eE+\-]+ [\d.eE+\-]+).*?'
130 r'size ([\d.eE+\-]+ [\d.eE+\-]+)',
135 for match
in patch_pattern.findall(content):
137 size_group[size].append(offset)
143 Main function to read Peano grid patch files and convert to Tree structure.
145 This function processes a directory of Peano grid patch files and constructs
146 a Tree object suitable for load balancing analysis. It handles multiple subtrees,
147 determines domain boundaries, and creates the hierarchical array structure.
151 1. **File Discovery**: Scans directory for files with 'grid-' prefix
152 2. **Patch Extraction**: Parses each file to extract spatial information
153 3. **Level Determination**: Groups patches by size to identify refinement levels
154 4. **Domain Analysis**: Determines overall domain boundaries from largest patches
155 5. **Tree Construction**: Builds hierarchical array structure
156 6. **Subtree Assignment**: Assigns patches to subtrees based on source files
158 ## Multi-subtree Handling
160 Each 'grid-' file represents a different subtree in the domain decomposition.
161 The function automatically:
162 - Processes all subtree files in the directory
163 - Assigns appropriate subtree IDs based on file order
164 - Merges spatial information into unified tree structure
165 - Reports distribution statistics for analysis
168 name (str, optional): Descriptive name for the resulting tree.
169 If not provided, a deterministic default name is generated.
170 file_path (str): Path to directory containing grid patch files
171 dimensions (int): Spatial dimensions count (2 for 2D, 3 for 3D)
174 Tree: Tree object with complete spatial structure and subtree assignments
178 # Read 3D grid data from checkpoint
179 tree = GridPatchFileReader(
180 name="CCZ4_Simulation_T100",
181 file_path="/path/to/checkpoint/grid/files",
185 print(f"Loaded tree with {tree._leaf_count} leaves")
186 print(f"Domain: {tree._domain_offset} to {tree._domain_size}")
187 print(f"Subtrees: {tree._subtree_count}")
190 if file_path
is None:
191 raise ValueError(
"file_path must be provided")
193 if dimensions
is None:
194 raise ValueError(
"dimensions must be provided")
196 if dimensions
not in [2, 3]:
197 raise ValueError(f
"dimensions must be 2 or 3, got {dimensions}")
200 name = f
"grid-patch-reader-{dimensions}d"
202 folder_path=file_path
204 if folder_path[-1] !=
'/':
209 datafiles = [f
for f
in os.listdir(folder_path)
if f.startswith(prefix)]
213 for file_path
in datafiles:
214 size_group = defaultdict(list)
215 read_patches(folder_path+
'/'+file_path, size_group, dimensions)
216 size_groups.append(size_group)
219 for size_group
in size_groups:
220 total_level=max(total_level, len(size_group.keys()))
222 patch_number_for_each_level = defaultdict(list)
224 for size_group
in size_groups:
225 print(
"Subtree", subtree_NO,
"from File")
226 for size, offsets
in size_group.items():
227 print(f
"Size: {size} -> Count: {len(offsets)}")
228 patch_number_for_each_level[size].append(len(offsets))
231 print(
"Total Cell Count:")
232 for size, count
in patch_number_for_each_level.items():
233 print(f
"Size: {size} -> Count: {sum(count)}")
239 for size_group
in size_groups:
240 for size
in size_group.keys():
241 size_tuple = tuple(map(float, size.split()))
242 if size_tuple[0]>max_size:
243 max_size=size_tuple[0]
248 leftmost_offset=[999,999,999]
250 leftmost_offset=[999,999]
251 for size_group
in size_groups:
252 for offset
in size_group[max_size_key]:
253 offset_tuple = tuple(map(float, offset.split()))
254 for m
in range(dimensions):
255 if offset_tuple[m]<leftmost_offset[m]:
256 leftmost_offset[m]=offset_tuple[m]
257 leftmost_offset_key=offset
259 domain_offset = tuple(leftmost_offset)
260 domain_size = tuple(map(float, max_size_key.split()))
261 domain_size = tuple(
round_sig(x * 3,5)
for x
in domain_size)
267 for size_group
in size_groups:
268 tree_matrix = [
None]*total_level
269 for size, offsets
in size_group.items():
270 size_tuple = tuple(map(float, size.split()))
271 level=int( math.log( int(
round_sig(domain_size[0]/size_tuple[0],1)), 3) )
272 matrix_size=(3**level)
276 matrix = np.zeros((matrix_size, matrix_size, matrix_size), dtype=int)
277 elif dimensions == 2:
278 matrix = np.zeros((matrix_size, matrix_size), dtype=int)
280 raise ValueError(f
"Unsupported dimensions: {dimensions}")
282 for offset
in offsets:
283 offset_tuple = tuple(map(float, offset.split()))
288 for i
in range(len(offset_tuple)):
289 index[i]=round((offset_tuple[i]-domain_offset[i])/size_tuple[i])
290 matrix[tuple(index)]=1
293 tree_matrix[level-1]=matrix
294 tree_matrices.append(tree_matrix)
297 peano_tree = Tree(name, dimensions, domain_offset, domain_size)
298 peano_tree.read_in_tree_from_arrays(input_tree_arrays=tree_matrices, max_level=total_level, weighted=
False)