1
1
import h5py
2
2
import numpy as np
3
3
from collections import defaultdict
4
+ from postprocessing import compute_rank2tensor_measures
4
5
5
6
def recursively_find_structure (group , current_path = "" ):
6
7
"""
@@ -106,118 +107,91 @@ def extract_and_organize_data(file_path, hierarchy, quantities_to_load, microstr
106
107
107
108
return organized_data
108
109
109
- import plotly .graph_objects as go
110
- from plotly .subplots import make_subplots
111
110
112
- def plot_subplots (data1 , data2 , labels , title = "Subplot Grid" , nrows = None , ncols = None ):
111
+
112
+
113
+
114
+
115
+ def write_processed_data_to_h5 (file_path , processed_data , overwrite = True ):
113
116
"""
114
- Plot a grid of subplots using Plotly, handling both single-component (scalar vs scalar) and multi-component data .
117
+ Writes processed data back into the HDF5 file at the correct locations .
115
118
116
119
Parameters:
117
- - data1: numpy array, first set of data to plot (e.g., strain, time)
118
- - data2: numpy array, second set of data to plot (e.g., stress)
119
- - labels: tuple of strings, labels for the x and y axes (e.g., ("Strain", "Stress"))
120
- - title: string, title of the overall plot
121
- - nrows: int, number of rows in the subplot grid (optional)
122
- - ncols: int, number of columns in the subplot grid (optional)
123
- """
124
- # Ensure data1 and data2 are 2D arrays
125
- if data1 .ndim == 1 :
126
- data1 = data1 [:, np .newaxis ]
127
- if data2 .ndim == 1 :
128
- data2 = data2 [:, np .newaxis ]
129
-
130
- # Set the number of components based on data shape
131
- n_components = data1 .shape [1 ]
132
-
133
- # If nrows or ncols is not specified, determine an optimal grid layout
134
- if nrows is None or ncols is None :
135
- nrows = int (np .ceil (np .sqrt (n_components )))
136
- ncols = int (np .ceil (n_components / nrows ))
120
+ - file_path: str, path to the HDF5 file.
121
+ - processed_data: dict, structured data with computed measures to write back.
122
+ - overwrite: bool, whether to overwrite existing datasets.
137
123
138
- # Create the subplot figure
139
- fig = make_subplots (rows = nrows , cols = ncols , subplot_titles = [f'Component { i + 1 } ' for i in range (n_components )])
140
-
141
- # Add traces for each component
142
- for i in range (n_components ):
143
- row = i // ncols + 1
144
- col = i % ncols + 1
145
- fig .add_trace (go .Scatter (
146
- x = data1 [:, i ],
147
- y = data2 [:, i ],
148
- mode = 'lines+markers' ,
149
- marker = dict (symbol = 'x' , size = 4 ),
150
- line = dict (width = 1 ),
151
- name = f'Component { i + 1 } '
152
- ), row = row , col = col )
153
-
154
- # Update layout with text labels and styling
155
- fig .update_layout (
156
- height = 600 ,
157
- width = 900 ,
158
- title_text = title ,
159
- showlegend = False ,
160
- template = "plotly_white" ,
161
- )
162
-
163
- # Update axes with text labels
164
- for i in range (n_components ):
165
- row = i // ncols + 1
166
- col = i % ncols + 1
167
- fig .update_xaxes (title_text = labels [0 ], row = row , col = col , showgrid = True )
168
- fig .update_yaxes (title_text = labels [1 ], row = row , col = col , showgrid = True )
169
-
170
- # Adjust layout for tight spacing
171
- fig .update_layout (margin = dict (l = 20 , r = 20 , t = 50 , b = 20 ), title_x = 0.5 )
172
-
173
- # Show the plot
174
- fig .show ()
175
-
176
- def compute_additional_stress_measures (data ):
124
+ Returns:
125
+ - None
126
+ """
127
+ with h5py .File (file_path , 'a' ) as h5file :
128
+ for microstructure , loads in processed_data .items ():
129
+ for load_case , data in loads .items ():
130
+ time_steps = data .pop ('time_steps' , [])
131
+ for measure_name , data_array in data .items ():
132
+ for time_step_idx , time_step in enumerate (time_steps ):
133
+ time_step_group = f"{ microstructure } /{ load_case } /time_step{ time_step } "
134
+ dataset_path = f"{ time_step_group } /{ measure_name } "
135
+
136
+ # Ensure the group exists
137
+ if time_step_group not in h5file :
138
+ raise ValueError (f"Group '{ time_step_group } ' does not exist in the HDF5 file." )
139
+
140
+ # Check if the dataset already exists
141
+ if dataset_path in h5file :
142
+ if overwrite :
143
+ del h5file [dataset_path ]
144
+ h5file .create_dataset (dataset_path , data = data_array [time_step_idx ])
145
+ else :
146
+ print (f"Dataset exists and overwrite=False: { dataset_path } " )
147
+ else :
148
+ h5file .create_dataset (dataset_path , data = data_array [time_step_idx ])
149
+
150
+
151
+ def postprocess_and_write_to_h5 (file_path , hierarchy , quantities_to_process , measures ,
152
+ microstructures_to_load = None , load_cases_to_load = None ):
177
153
"""
178
- Computes von Mises stress, hydrostatic stress, and deviatoric stress directly from the stress tensor in Mandel notation.
179
- Adds these measures to the `data` dictionary for all microstructures and load cases using vectorized operations .
154
+ A higher-level function that extracts specific data from an HDF5 file, processes it to compute various tensor measures,
155
+ and writes the results back into the HDF5 file using the naming convention 'quantity_measure' .
180
156
181
157
Parameters:
182
- - data: dictionary, contains strain and stress matrices as well as other simulation data
158
+ - file_path: str
159
+ Path to the HDF5 file containing the data.
160
+ - hierarchy: dict
161
+ The structure of the HDF5 file as identified by the `identify_hierarchy` function.
162
+ - quantities_to_process: list of str
163
+ List of quantities (e.g., 'stress_average', 'strain_average') to extract from the HDF5 file and process.
164
+ - measures: list of str
165
+ List of tensor measures to compute for the extracted quantities.
166
+ Available options can be found in the `compute_rank2tensor_measures` function in the `postprocessing` module.
167
+ - microstructures_to_load: list of str, optional
168
+ List of microstructures to process. If None, all microstructures found in the hierarchy will be processed.
169
+ - load_cases_to_load: list of str, optional
170
+ List of load cases to process. If None, all load cases found in the hierarchy will be processed.
183
171
184
172
Returns:
185
- - updated_data: dictionary, the original data dictionary with added von Mises, hydrostatic, and deviatoric stress
173
+ - processed_data: dict
174
+ A dictionary containing the processed data, organized by microstructure and load case.
175
+ The computed measures are stored under keys following the 'quantity_measure' naming convention.
176
+ Additionally, the processed data is also written back into the HDF5 file at the corresponding locations.
186
177
"""
178
+ # Extract the data (loads all time steps if time_steps_to_load is None or empty)
179
+ extracted_data = extract_and_organize_data (file_path , hierarchy , quantities_to_process ,
180
+ microstructures_to_load , load_cases_to_load )
181
+
182
+ # Process the data and prepare for writing
183
+ processed_data = defaultdict (lambda : defaultdict (dict ))
184
+ for microstructure , loads in extracted_data .items ():
185
+ for load_case , quantities in loads .items ():
186
+ time_steps = quantities .pop ('time_steps' , [])
187
+ for quantity_name , tensor_data in quantities .items ():
188
+ measures_results = compute_rank2tensor_measures (tensor_data , measures )
189
+ for measure_name , measure_data in measures_results .items ():
190
+ key = f"{ quantity_name } _{ measure_name } "
191
+ processed_data [microstructure ][load_case ][key ] = measure_data
192
+ processed_data [microstructure ][load_case ]['time_steps' ] = time_steps
193
+
194
+ # Write the processed data back to the HDF5 file
195
+ write_processed_data_to_h5 (file_path , processed_data )
187
196
188
- for microstructure , loads in data .items ():
189
- for load_case , measures in loads .items ():
190
- if 'stress_average' in measures :
191
- # Extract the stress matrix in Mandel notation
192
- stress_matrix = measures ['stress_average' ]
193
-
194
- # Compute the hydrostatic stress (mean of diagonal components)
195
- hydrostatic_stress = np .mean (stress_matrix [:, :3 ], axis = 1 )
196
-
197
- # Deviatoric stress components (s11, s22, s33)
198
- deviatoric_stress = stress_matrix [:, :3 ] - hydrostatic_stress [:, np .newaxis ]
199
-
200
- # Shear components (s12, s23, s13) from Mandel notation
201
- deviatoric_shear = stress_matrix [:, 3 :6 ]
202
-
203
- # Compute von Mises stress using vectorized operations
204
- von_mises_stress = np .sqrt (
205
- 0.5 * (
206
- (deviatoric_stress [:, 0 ] - deviatoric_stress [:, 1 ])** 2 +
207
- (deviatoric_stress [:, 1 ] - deviatoric_stress [:, 2 ])** 2 +
208
- (deviatoric_stress [:, 2 ] - deviatoric_stress [:, 0 ])** 2 +
209
- 6 * (deviatoric_shear [:, 0 ]** 2 + deviatoric_shear [:, 1 ]** 2 + deviatoric_shear [:, 2 ]** 2 )
210
- )
211
- )
212
-
213
- # Reconstruct the deviatoric stress matrix in Mandel notation
214
- deviatoric_stress_matrix = np .zeros_like (stress_matrix )
215
- deviatoric_stress_matrix [:, :3 ] = deviatoric_stress # Deviatoric normal stresses
216
- deviatoric_stress_matrix [:, 3 :6 ] = deviatoric_shear # Deviatoric shear stress components
217
-
218
- # Store the computed stresses in the data dictionary
219
- measures ['von_mises_stress' ] = von_mises_stress
220
- measures ['hydrostatic_stress' ] = hydrostatic_stress
221
- measures ['deviatoric_stress' ] = deviatoric_stress_matrix
222
-
223
- return data
197
+ return processed_data
0 commit comments