[skip sc][skip ci] newer code structure to create DREAM3d files

This commit is contained in:
Shah Vitesh 2023-02-27 14:49:41 +01:00
parent d70a41a059
commit f15d1da169
2 changed files with 122 additions and 211 deletions

View File

@ -7,6 +7,8 @@ import h5py
import numpy as np import numpy as np
import damask import damask
from damask import Rotation
from damask import Orientation
class AttributeManagerNullterm(h5py.AttributeManager): class AttributeManagerNullterm(h5py.AttributeManager):
""" """
@ -43,93 +45,140 @@ Crystal_structures = {'fcc': 1,
'ort': 6} #TODO: is bct Tetragonal low/Tetragonal high? 'ort': 6} #TODO: is bct Tetragonal low/Tetragonal high?
Phase_types = {'Primary': 0} #further additions to these can be done by looking at 'Create Ensemble Info' filter Phase_types = {'Primary': 0} #further additions to these can be done by looking at 'Create Ensemble Info' filter
class DAMASKtoDREAM3D():
"""
This class can convert the DAMASK data to DREAM3D compatible data.
There can be various different types of ways DAMASK data can be represented.
Therefore, there are multiple functions available for different purposes.
"""
def __init__(self,simulation_folder,job_file,geom_file,load_file):
"""
Defining the common quantities for all the functions in this class.
# -------------------------------------------------------------------- Parameters
# MAIN ----------
# -------------------------------------------------------------------- simulation_folder: str
parser = argparse.ArgumentParser(description='Creating a file for DREAM3D from DAMASK data') Path of the simulation folder.
parser.add_argument('filenames', nargs='+', job_file: str
help='DADF5 files') Name of the job file (DADF5 file).
parser.add_argument('-d','--dir', dest='dir',default='postProc',metavar='string', geom_file : str
help='name of subdirectory relative to the location of the DADF5 file to hold output') name of the geom file.
parser.add_argument('--inc',nargs='+', load_file :
help='Increment for which DREAM3D to be used, eg. 25',type=int) name of the load file.
"""
self.simulation_folder = simulation_folder
self.job_file = job_file
self.geom_file = geom_file
self.load_file = load_file
options = parser.parse_args() def DAMASKtoDREAM3D(self,dx,inc):
"""
Creates a dream3D file from DAMASK output.
Without any regridding.
Considers the original grid from DAMASK.
for filename in options.filenames: Parameters:
f = damask.Result(filename) -----------
N_digits = int(np.floor(np.log10(int(f.increments[-1][3:]))))+1 dx : float
The grid spacing.
inc: int
increment of interest for DREAM3D processing.
"""
os.chdir(self.simulation_folder)
#--------------------------------------------------------------------------
#Build array of euler angles for each cell
#--------------------------------------------------------------------------
d = damask.Result(self.job_file)
inc_data = d.view(increments=inc) # selecting only relevant data to reduce overload
f.pick('increments',options.inc) f = h5py.File(self.job_file,'r')
for inc in damask.util.show_progress(f.iterate('increments'),len(f.selection['increments'])): cells = f['geometry'].attrs['cells']
dirname = os.path.abspath(os.path.join(os.path.dirname(filename),options.dir)) size = f['geometry'].attrs['size']
try: dx = size/cells
os.mkdir(dirname)
except FileExistsError:
pass
o = h5py.File(dirname + '/' + os.path.splitext(filename)[0] \ O_dict = inc_data.get('O')
+ '_inc_{}.dream3D'.format(inc[3:].zfill(N_digits)),'w')
cell_orientation_array = np.zeros((np.prod(cells),3))
phase_ID_array = np.zeros((np.prod(cells)),dtype=np.int32) #need to reshape it later
for count,p in enumerate(d.phases):
phase_index = np.where(f['cell_to/phase']['label'] == f'{p}'.encode())[0]
if len(d.phases) > 1:
cell_orientation_array[phase_index,:] = Rotation(O_dict[p]).as_Euler_angles()
else:
cell_orientation_array[phase_index,:] = Rotation(O_dict).as_Euler_angles()
phase_ID_array[phase_index] = count + 1
#--------------------------------------------------------------------------
job_file_no_ext = os.path.splitext(self.job_file)[0]
o = h5py.File(f'{job_file_no_ext}_increment{inc}.dream3D','w')
o.attrs['DADF5toDREAM3D'] = '1.0' o.attrs['DADF5toDREAM3D'] = '1.0'
o.attrs['FileVersion'] = '7.0' o.attrs['FileVersion'] = '7.0'
for g in ['DataContainerBundles','Pipeline']: # empty groups (needed) for g in ['DataContainerBundles','Pipeline']: # empty groups (needed)
o.create_group(g) o.create_group(g)
data_container_label = 'DataContainers/ImageDataContainer' data_container_label = 'DataContainers/SyntheticVolumeDataContainer'
cell_data_label = data_container_label + '/CellData' cell_data_label = data_container_label + '/CellData'
# Phase information of DREAM.3D is constituent ID in DAMASK # Data phases
o[cell_data_label + '/Phases'] = f.get_constituent_ID().reshape(tuple(f.grid)+(1,)) o[cell_data_label + '/Phases'] = np.reshape(phase_ID_array, \
DAMASK_quaternion = f.read_dataset(f.get_dataset_location('orientation')) tuple(np.flip(cells))+(1,))
# Convert: DAMASK uses P = -1, DREAM.3D uses P = +1. Also change position of imagninary part
DREAM_3D_quaternion = np.hstack((-DAMASK_quaternion['x'],-DAMASK_quaternion['y'],-DAMASK_quaternion['z'], # Data eulers
DAMASK_quaternion['w'])).astype(np.float32) orientation_data = cell_orientation_array.astype(np.float32)
o[cell_data_label + '/Quats'] = DREAM_3D_quaternion.reshape(tuple(f.grid)+(4,)) o[cell_data_label + '/Eulers'] = orientation_data.reshape(tuple(np.flip(cells))+(3,))
# Attributes to CellData group # Attributes to CellData group
o[cell_data_label].attrs['AttributeMatrixType'] = np.array([3],np.uint32) o[cell_data_label].attrs['AttributeMatrixType'] = np.array([3],np.uint32)
o[cell_data_label].attrs['TupleDimensions'] = f.grid.astype(np.uint64) o[cell_data_label].attrs['TupleDimensions'] = np.array(cells,np.uint64)
# Common Attributes for groups in CellData # Common Attributes for groups in CellData
for group in ['/Phases','/Quats']: for group in ['/Phases','/Eulers']:
o[cell_data_label + group].attrs['DataArrayVersion'] = np.array([2],np.int32) o[cell_data_label + group].attrs['DataArrayVersion'] = np.array([2],np.int32)
o[cell_data_label + group].attrs['Tuple Axis Dimensions'] = 'x={},y={},z={}'.format(*f.grid) o[cell_data_label + group].attrs['Tuple Axis Dimensions'] = 'x={},y={},z={}'.format(*np.array(cells))
# phase attributes
o[cell_data_label + '/Phases'].attrs['ComponentDimensions'] = np.array([1],np.uint64) o[cell_data_label + '/Phases'].attrs['ComponentDimensions'] = np.array([1],np.uint64)
o[cell_data_label + '/Phases'].attrs['ObjectType'] = 'DataArray<int32_t>' o[cell_data_label + '/Phases'].attrs['ObjectType'] = 'DataArray<int32_t>'
o[cell_data_label + '/Phases'].attrs['TupleDimensions'] = f.grid.astype(np.uint64) o[cell_data_label + '/Phases'].attrs['TupleDimensions'] = np.array(cells,np.uint64)
o[cell_data_label + '/Quats'].attrs['ComponentDimensions'] = np.array([4],np.uint64) # Eulers attributes
o[cell_data_label + '/Quats'].attrs['ObjectType'] = 'DataArray<float>' o[cell_data_label + '/Eulers'].attrs['ComponentDimensions'] = np.array([3],np.uint64)
o[cell_data_label + '/Quats'].attrs['TupleDimensions'] = f.grid.astype(np.uint64) o[cell_data_label + '/Eulers'].attrs['ObjectType'] = 'DataArray<float>'
o[cell_data_label + '/Eulers'].attrs['TupleDimensions'] = np.array(cells,np.uint64)
# Create EnsembleAttributeMatrix # Create EnsembleAttributeMatrix
ensemble_label = data_container_label + '/EnsembleAttributeMatrix' ensemble_label = data_container_label + '/CellEnsembleData'
# Data CrystalStructures # Data CrystalStructures
o[ensemble_label + '/CrystalStructures'] = np.uint32(np.array([999,\ #o[ensemble_label + '/CrystalStructures'] = np.uint32(np.array([999,1]))
Crystal_structures[f.get_crystal_structure()]])).reshape(2,1) o[ensemble_label + '/CrystalStructures'] = np.uint32(np.array([999] + [1]*len(d.phases)))
o[ensemble_label + '/PhaseTypes'] = np.uint32(np.array([999,Phase_types['Primary']])).reshape(2,1) # ToDo # assuming only cubic crystal structures
# Damask can give the crystal structure info but need to look into dream3d which crystal structure corresponds to which number
o[ensemble_label + '/PhaseTypes'] = np.uint32(np.array([999] + [Phase_types['Primary']]*len(d.phases))).reshape((len(d.phases)+1,1))
# also assuming Primary phases
# there can be precipitates etc as well
# Attributes Ensemble Matrix # Attributes Ensemble Matrix
o[ensemble_label].attrs['AttributeMatrixType'] = np.array([11],np.uint32) o[ensemble_label].attrs['AttributeMatrixType'] = np.array([11],np.uint32)
o[ensemble_label].attrs['TupleDimensions'] = np.array([2], np.uint64) o[ensemble_label].attrs['TupleDimensions'] = np.array([len(d.phases) + 1], np.uint64)
# Attributes for data in Ensemble matrix # Attributes for data in Ensemble matrix
for group in ['CrystalStructures','PhaseTypes']: # 'PhaseName' not required MD: But would be nice to take the phase name mapping for group in ['CrystalStructures','PhaseTypes']: # 'PhaseName' not required MD: But would be nice to take the phase name mapping
o[ensemble_label+'/'+group].attrs['ComponentDimensions'] = np.array([1],np.uint64) o[ensemble_label+'/'+group].attrs['ComponentDimensions'] = np.array([1],np.uint64)
o[ensemble_label+'/'+group].attrs['Tuple Axis Dimensions'] = 'x=2' o[ensemble_label+'/'+group].attrs['Tuple Axis Dimensions'] = f'x={len(d.phases)+1}'
o[ensemble_label+'/'+group].attrs['DataArrayVersion'] = np.array([2],np.int32) o[ensemble_label+'/'+group].attrs['DataArrayVersion'] = np.array([2],np.int32)
o[ensemble_label+'/'+group].attrs['ObjectType'] = 'DataArray<uint32_t>' o[ensemble_label+'/'+group].attrs['ObjectType'] = 'DataArray<uint32_t>'
o[ensemble_label+'/'+group].attrs['TupleDimensions'] = np.array([2],np.uint64) o[ensemble_label+'/'+group].attrs['TupleDimensions'] = np.array([len(d.phases) + 1],np.uint64)
# Create geometry info
geom_label = data_container_label + '/_SIMPL_GEOMETRY' geom_label = data_container_label + '/_SIMPL_GEOMETRY'
o[geom_label + '/DIMENSIONS'] = np.int64(f.grid) o[geom_label + '/DIMENSIONS'] = np.int64(np.array(cells))
o[geom_label + '/ORIGIN'] = np.float32(np.zeros(3)) o[geom_label + '/ORIGIN'] = np.float32(np.zeros(3))
o[geom_label + '/SPACING'] = np.float32(f.size) o[geom_label + '/SPACING'] = np.float32(dx)
o[geom_label].attrs['GeometryName'] = 'ImageGeometry' o[geom_label].attrs['GeometryName'] = 'ImageGeometry'
o[geom_label].attrs['GeometryTypeName'] = 'ImageGeometry' o[geom_label].attrs['GeometryTypeName'] = 'ImageGeometry'

View File

@ -1,138 +0,0 @@
#!/usr/bin/env python3
import argparse
import os
import h5py
import numpy as np
import damask
class AttributeManagerNullterm(h5py.AttributeManager):
"""
Attribute management for DREAM.3D hdf5 files.
String attribute values are stored as fixed-length string with NULLTERM
References
----------
https://stackoverflow.com/questions/38267076
https://stackoverflow.com/questions/52750232
"""
def create(self, name, data, shape=None, dtype=None):
if isinstance(data,str):
tid = h5py.h5t.C_S1.copy()
tid.set_size(len(data + ' '))
super().create(name=name,data=data+' ',dtype = h5py.Datatype(tid))
else:
super().create(name=name,data=data,shape=shape,dtype=dtype)
h5py._hl.attrs.AttributeManager = AttributeManagerNullterm # 'Monkey patch'
# --------------------------------------------------------------------
# Crystal structure specifications
# --------------------------------------------------------------------
Crystal_structures = {'fcc': 1,
'bcc': 1,
'hcp': 0,
'bct': 7,
'ort': 6} #TODO: is bct Tetragonal low/Tetragonal high?
Phase_types = {'Primary': 0} #further additions to these can be done by looking at 'Create Ensemble Info' filter
# --------------------------------------------------------------------
# MAIN
# --------------------------------------------------------------------
parser = argparse.ArgumentParser(description='Creating a file for DREAM3D from DAMASK data')
parser.add_argument('filenames', nargs='+',
help='DADF5 files')
parser.add_argument('-d','--dir', dest='dir',default='postProc',metavar='string',
help='name of subdirectory relative to the location of the DADF5 file to hold output')
parser.add_argument('--inc',nargs='+',
help='Increment for which DREAM3D to be used, eg. 25',type=int)
options = parser.parse_args()
for filename in options.filenames:
f = damask.Result(filename)
N_digits = int(np.floor(np.log10(int(f.increments[-1][3:]))))+1
f.pick('increments',options.inc)
for inc in damask.util.show_progress(f.iterate('increments'),len(f.selection['increments'])):
dirname = os.path.abspath(os.path.join(os.path.dirname(filename),options.dir))
try:
os.mkdir(dirname)
except FileExistsError:
pass
o = h5py.File(dirname + '/' + os.path.splitext(filename)[0] \
+ '_inc_{}.dream3D'.format(inc[3:].zfill(N_digits)),'w')
o.attrs['DADF5toDREAM3D'] = '1.0'
o.attrs['FileVersion'] = '7.0'
for g in ['DataContainerBundles','Pipeline']: # empty groups (needed)
o.create_group(g)
data_container_label = 'DataContainers/ImageDataContainer'
cell_data_label = data_container_label + '/CellData'
# Phase information of DREAM.3D is constituent ID in DAMASK
o[cell_data_label + '/Phases'] = f.get_constituent_ID().reshape(tuple(f.grid)+(1,))
DAMASK_quaternion = f.read_dataset(f.get_dataset_location('orientation'))
# Convert: DAMASK uses P = -1, DREAM.3D uses P = +1. Also change position of imagninary part
DREAM_3D_quaternion = np.hstack((-DAMASK_quaternion['x'],-DAMASK_quaternion['y'],-DAMASK_quaternion['z'],
DAMASK_quaternion['w'])).astype(np.float32)
o[cell_data_label + '/Quats'] = DREAM_3D_quaternion.reshape(tuple(f.grid)+(4,))
# Attributes to CellData group
o[cell_data_label].attrs['AttributeMatrixType'] = np.array([3],np.uint32)
o[cell_data_label].attrs['TupleDimensions'] = f.grid.astype(np.uint64)
# Common Attributes for groups in CellData
for group in ['/Phases','/Quats']:
o[cell_data_label + group].attrs['DataArrayVersion'] = np.array([2],np.int32)
o[cell_data_label + group].attrs['Tuple Axis Dimensions'] = 'x={},y={},z={}'.format(*f.grid)
o[cell_data_label + '/Phases'].attrs['ComponentDimensions'] = np.array([1],np.uint64)
o[cell_data_label + '/Phases'].attrs['ObjectType'] = 'DataArray<int32_t>'
o[cell_data_label + '/Phases'].attrs['TupleDimensions'] = f.grid.astype(np.uint64)
o[cell_data_label + '/Quats'].attrs['ComponentDimensions'] = np.array([4],np.uint64)
o[cell_data_label + '/Quats'].attrs['ObjectType'] = 'DataArray<float>'
o[cell_data_label + '/Quats'].attrs['TupleDimensions'] = f.grid.astype(np.uint64)
# Create EnsembleAttributeMatrix
ensemble_label = data_container_label + '/EnsembleAttributeMatrix'
# Data CrystalStructures
o[ensemble_label + '/CrystalStructures'] = np.uint32(np.array([999,\
Crystal_structures[f.get_crystal_structure()]])).reshape(2,1)
o[ensemble_label + '/PhaseTypes'] = np.uint32(np.array([999,Phase_types['Primary']])).reshape(2,1) # ToDo
# Attributes Ensemble Matrix
o[ensemble_label].attrs['AttributeMatrixType'] = np.array([11],np.uint32)
o[ensemble_label].attrs['TupleDimensions'] = np.array([2], np.uint64)
# Attributes for data in Ensemble matrix
for group in ['CrystalStructures','PhaseTypes']: # 'PhaseName' not required MD: But would be nice to take the phase name mapping
o[ensemble_label+'/'+group].attrs['ComponentDimensions'] = np.array([1],np.uint64)
o[ensemble_label+'/'+group].attrs['Tuple Axis Dimensions'] = 'x=2'
o[ensemble_label+'/'+group].attrs['DataArrayVersion'] = np.array([2],np.int32)
o[ensemble_label+'/'+group].attrs['ObjectType'] = 'DataArray<uint32_t>'
o[ensemble_label+'/'+group].attrs['TupleDimensions'] = np.array([2],np.uint64)
geom_label = data_container_label + '/_SIMPL_GEOMETRY'
o[geom_label + '/DIMENSIONS'] = np.int64(f.grid)
o[geom_label + '/ORIGIN'] = np.float32(np.zeros(3))
o[geom_label + '/SPACING'] = np.float32(f.size)
o[geom_label].attrs['GeometryName'] = 'ImageGeometry'
o[geom_label].attrs['GeometryTypeName'] = 'ImageGeometry'
o[geom_label].attrs['GeometryType'] = np.array([0],np.uint32)
o[geom_label].attrs['SpatialDimensionality'] = np.array([3],np.uint32)
o[geom_label].attrs['UnitDimensionality'] = np.array([3],np.uint32)