141 Generate a Tree object by reading from a YAML file created by generate_tree_yaml().
143 This function reconstructs the complete tree structure from a YAML file, including
144 all spatial data, metadata, and subtree assignments. It provides a way to reload
145 previously processed trees for further analysis or modification.
147 ## YAML Structure Processing
149 The function expects YAML files with the structure created by `generate_tree_yaml()`:
152 title: "Complete Tree Structure from [source]"
153 description: "Spatial representation of tree with (weight, subtree_id) tuples"
158 max_level: [number_of_levels]
159 domain_offset: [x, y] or [x, y, z]
160 domain_size: [width, height] or [width, height, depth]
161 total_leaf_count: [total_leaves]
162 subtree_count: [number_of_subtrees]
164 subtree_distribution: [count_per_subtree]
169 0: "(1,0), (1,0), (1,1)"
173 ## Data Reconstruction
175 The function rebuilds the tree arrays by:
176 1. Parsing metadata to determine tree structure
177 2. Creating numpy arrays for each refinement level
178 3. Parsing tuple strings to extract (weight, subtree_id) values
179 4. Populating arrays according to spatial indexing
180 5. Updating tree statistics and metadata
184 Comprehensive validation includes:
185 - File existence and readability
186 - YAML syntax validation
187 - Required field presence
188 - Data type consistency
189 - Tuple format validation
190 - Dimensional compatibility
193 name (str): Descriptive name for the loaded tree (overrides YAML name if desired)
194 yaml_file_path (str): Path to the YAML file containing tree structure
197 Tree: Reconstructed Tree object with complete structure
200 FileNotFoundError: If YAML file doesn't exist or isn't readable
201 yaml.YAMLError: If YAML file has syntax errors
202 KeyError: If required fields are missing from YAML structure
203 ValueError: If data formats are inconsistent or invalid
207 # Load a previously saved tree structure
208 tree = CustomGridGenerator(
210 yaml_file_path="TreeStructure_l4_s3_split.yaml"
213 print(f"Loaded tree: {tree._name}")
214 print(f"Dimensions: {tree._dimension}D")
215 print(f"Levels: {tree._max_level}")
216 print(f"Leaf count: {tree._leaf_count}")
217 print(f"Subtrees: {tree._subtree_count}")
219 # Apply additional processing
220 from strategies.SplitTrees import split_tree
221 split_tree(tree, number_of_subtrees=2)
225 - The function preserves all original spatial relationships and metadata
226 - Tree name can be overridden with the name parameter
227 - Large YAML files may require significant memory during reconstruction
228 - The reconstructed tree is functionally identical to the original
231 print(f
"Loading custom tree '{name}' from YAML file: {yaml_file_path}")
234 if not os.path.exists(yaml_file_path):
235 raise FileNotFoundError(f
"YAML file not found: {yaml_file_path}")
237 if not os.path.isfile(yaml_file_path):
238 raise ValueError(f
"Path is not a file: {yaml_file_path}")
242 with open(yaml_file_path,
'r')
as file:
243 yaml_data = yaml.safe_load(file)
244 print(f
"Successfully loaded YAML file")
245 except yaml.YAMLError
as e:
246 raise yaml.YAMLError(f
"Error parsing YAML file: {e}")
248 raise IOError(f
"Error reading YAML file: {e}")
251 required_fields = [
'tree_info',
'tree_structure']
252 for field
in required_fields:
253 if field
not in yaml_data:
254 raise KeyError(f
"Required field '{field}' missing from YAML file")
257 tree_info = yaml_data[
'tree_info']
258 required_tree_fields = [
'dimension',
'max_level',
'domain_offset',
'domain_size']
259 for field
in required_tree_fields:
260 if field
not in tree_info:
261 raise KeyError(f
"Required tree_info field '{field}' missing from YAML file")
263 dimension = tree_info[
'dimension']
264 max_level = tree_info[
'max_level']
265 domain_offset = tree_info[
'domain_offset']
266 domain_size = tree_info[
'domain_size']
269 if dimension
not in [2, 3]:
270 raise ValueError(f
"Invalid dimension: {dimension}. Must be 2 or 3")
273 expected_coords = dimension
274 if len(domain_offset) != expected_coords
or len(domain_size) != expected_coords:
275 raise ValueError(f
"Domain offset and size must have {expected_coords} coordinates for {dimension}D")
277 print(f
" Dimension: {dimension}D")
278 print(f
" Max level: {max_level}")
279 print(f
" Domain offset: {domain_offset}")
280 print(f
" Domain size: {domain_size}")
283 peano_tree = Tree(name, dimension, domain_offset, domain_size)
286 tree_structure = yaml_data[
'tree_structure']
288 if len(tree_structure) != max_level:
289 raise ValueError(f
"Expected {max_level} levels, got {len(tree_structure)} in tree_structure")
292 artificial_arrays = []
294 for level_data
in tree_structure:
296 if 'level' not in level_data
or 'size' not in level_data
or 'data' not in level_data:
297 raise KeyError(
"Each level must have 'level', 'size', and 'data' fields")
299 level_num = level_data[
'level']
300 size = level_data[
'size']
301 data = level_data[
'data']
304 expected_level = len(artificial_arrays) + 1
305 expected_size = 3 ** expected_level
307 if level_num != expected_level:
308 print(f
"Warning: Level number {level_num} doesn't match expected {expected_level}")
310 if size != expected_size:
311 raise ValueError(f
"Level {level_num}: expected size {expected_size}, got {size}")
313 print(f
" Processing level {level_num}: {size}^{dimension} cells")
317 array = np.empty((size, size), dtype=object)
320 for row_idx
in range(size):
321 if row_idx
not in data:
322 raise KeyError(f
"Level {level_num}: missing row {row_idx}")
324 row_str = data[row_idx]
326 tuple_strings = [s.strip()
for s
in row_str.split(
', ')]
328 if len(tuple_strings) != size:
329 raise ValueError(f
"Level {level_num}, row {row_idx}: expected {size} tuples, got {len(tuple_strings)}")
331 for col_idx, tuple_str
in enumerate(tuple_strings):
334 array[row_idx][col_idx] = (weight, subtree_id)
335 except ValueError
as e:
336 raise ValueError(f
"Level {level_num}, row {row_idx}, col {col_idx}: {e}")
339 array = np.empty((size, size, size), dtype=object)
344 for plane_idx
in range(size):
345 if plane_idx
not in data:
346 raise KeyError(f
"Level {level_num}: missing plane {plane_idx}")
348 plane_data = data[plane_idx]
349 if not isinstance(plane_data, dict):
350 raise ValueError(f
"Level {level_num}, plane {plane_idx}: expected dictionary, got {type(plane_data)}")
352 for row_idx
in range(size):
353 if row_idx
not in plane_data:
354 raise KeyError(f
"Level {level_num}, plane {plane_idx}: missing row {row_idx}")
356 row_str = plane_data[row_idx]
358 tuple_strings = [s.strip()
for s
in row_str.split(
', ')]
360 if len(tuple_strings) != size:
361 raise ValueError(f
"Level {level_num}, plane {plane_idx}, row {row_idx}: expected {size} tuples, got {len(tuple_strings)}")
363 for col_idx, tuple_str
in enumerate(tuple_strings):
367 array[plane_idx][row_idx][col_idx] = (weight, subtree_id)
368 except ValueError
as e:
369 raise ValueError(f
"Level {level_num}, plane {plane_idx}, row {row_idx}, col {col_idx}: {e}")
371 artificial_arrays.append(array)
375 for array
in artificial_arrays:
377 for i
in range(array.shape[0]):
378 for j
in range(array.shape[1]):
379 weight, subtree_id = array[i][j]
381 max_subtree_id = max(max_subtree_id, subtree_id)
383 for i
in range(array.shape[0]):
384 for j
in range(array.shape[1]):
385 for k
in range(array.shape[2]):
386 weight, subtree_id = array[i][j][k]
388 max_subtree_id = max(max_subtree_id, subtree_id)
391 peano_tree._subtree_count = max_subtree_id + 1
392 peano_tree._max_level = max_level
396 peano_tree._tree_arrays = artificial_arrays
397 peano_tree.update_split_pattern()
400 peano_tree.update_weight()
402 print(f
"Custom tree generation completed successfully")
403 print(f
" Tree: {peano_tree._name}")
404 print(f
" Leaf count: {peano_tree._leaf_count}")
405 print(f
" Subtree count: {peano_tree._subtree_count}")
408 if 'total_leaf_count' in tree_info:
409 expected_leaf_count = tree_info[
'total_leaf_count']
410 if peano_tree._leaf_count != expected_leaf_count:
411 print(f
"Warning: Reconstructed leaf count ({peano_tree._leaf_count}) doesn't match YAML metadata ({expected_leaf_count})")
413 if 'subtree_count' in tree_info:
414 expected_subtree_count = tree_info[
'subtree_count']
415 if peano_tree._subtree_count != expected_subtree_count:
416 print(f
"Warning: Reconstructed subtree count ({peano_tree._subtree_count}) doesn't match YAML metadata ({expected_subtree_count})")